]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/generic/logg.cpp
recognize schemes other than http and ftp in wxLaunchDefaultBrowser()
[wxWidgets.git] / src / generic / logg.cpp
... / ...
CommitLineData
1/////////////////////////////////////////////////////////////////////////////
2// Name: src/generic/logg.cpp
3// Purpose: wxLog-derived classes which need GUI support (the rest is in
4// src/common/log.cpp)
5// Author: Vadim Zeitlin
6// Modified by:
7// Created: 20.09.99 (extracted from src/common/log.cpp)
8// RCS-ID: $Id$
9// Copyright: (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
10// Licence: wxWindows licence
11/////////////////////////////////////////////////////////////////////////////
12
13// ============================================================================
14// declarations
15// ============================================================================
16
17// ----------------------------------------------------------------------------
18// headers
19// ----------------------------------------------------------------------------
20
21// For compilers that support precompilation, includes "wx.h".
22#include "wx/wxprec.h"
23
24#ifdef __BORLANDC__
25 #pragma hdrstop
26#endif
27
28#ifndef WX_PRECOMP
29 #include "wx/app.h"
30 #include "wx/button.h"
31 #include "wx/intl.h"
32 #include "wx/log.h"
33 #include "wx/menu.h"
34 #include "wx/frame.h"
35 #include "wx/filedlg.h"
36 #include "wx/msgdlg.h"
37 #include "wx/textctrl.h"
38 #include "wx/sizer.h"
39 #include "wx/statbmp.h"
40 #include "wx/settings.h"
41 #include "wx/wxcrtvararg.h"
42#endif // WX_PRECOMP
43
44#if wxUSE_LOGGUI || wxUSE_LOGWINDOW
45
46#include "wx/file.h"
47#include "wx/clipbrd.h"
48#include "wx/dataobj.h"
49#include "wx/textfile.h"
50#include "wx/statline.h"
51#include "wx/artprov.h"
52#include "wx/collpane.h"
53#include "wx/arrstr.h"
54
55#if wxUSE_THREADS
56 #include "wx/thread.h"
57#endif // wxUSE_THREADS
58
59#ifdef __WXMSW__
60 // for OutputDebugString()
61 #include "wx/msw/private.h"
62#endif // Windows
63
64
65#ifdef __WXPM__
66 #include <time.h>
67#endif
68
69#if wxUSE_LOG_DIALOG
70 #include "wx/listctrl.h"
71 #include "wx/imaglist.h"
72 #include "wx/image.h"
73#endif // wxUSE_LOG_DIALOG/!wxUSE_LOG_DIALOG
74
75#if defined(__MWERKS__) && wxUSE_UNICODE
76 #include <wtime.h>
77#endif
78
79#include "wx/datetime.h"
80
81// the suffix we add to the button to show that the dialog can be expanded
82#define EXPAND_SUFFIX _T(" >>")
83
84#define CAN_SAVE_FILES (wxUSE_FILE && wxUSE_FILEDLG)
85
86// ----------------------------------------------------------------------------
87// private classes
88// ----------------------------------------------------------------------------
89
90#if wxUSE_LOG_DIALOG
91
92// this function is a wrapper around strftime(3)
93// allows to exclude the usage of wxDateTime
94static wxString TimeStamp(const wxString& format, time_t t)
95{
96#if wxUSE_DATETIME
97 wxChar buf[4096];
98 struct tm tm;
99 if ( !wxStrftime(buf, WXSIZEOF(buf), format, wxLocaltime_r(&t, &tm)) )
100 {
101 // buffer is too small?
102 wxFAIL_MSG(_T("strftime() failed"));
103 }
104 return wxString(buf);
105#else // !wxUSE_DATETIME
106 return wxEmptyString;
107#endif // wxUSE_DATETIME/!wxUSE_DATETIME
108}
109
110
111class wxLogDialog : public wxDialog
112{
113public:
114 wxLogDialog(wxWindow *parent,
115 const wxArrayString& messages,
116 const wxArrayInt& severity,
117 const wxArrayLong& timess,
118 const wxString& caption,
119 long style);
120 virtual ~wxLogDialog();
121
122 // event handlers
123 void OnOk(wxCommandEvent& event);
124#if wxUSE_CLIPBOARD
125 void OnCopy(wxCommandEvent& event);
126#endif // wxUSE_CLIPBOARD
127#if CAN_SAVE_FILES
128 void OnSave(wxCommandEvent& event);
129#endif // CAN_SAVE_FILES
130 void OnListItemActivated(wxListEvent& event);
131
132private:
133 // create controls needed for the details display
134 void CreateDetailsControls(wxWindow *);
135
136 // if necessary truncates the given string and adds an ellipsis
137 wxString EllipsizeString(const wxString &text)
138 {
139 if (ms_maxLength > 0 &&
140 text.length() > ms_maxLength)
141 {
142 wxString ret(text);
143 ret.Truncate(ms_maxLength);
144 ret << "...";
145 return ret;
146 }
147
148 return text;
149 }
150
151#if CAN_SAVE_FILES || wxUSE_CLIPBOARD
152 // return the contents of the dialog as a multiline string
153 wxString GetLogMessages() const;
154#endif // CAN_SAVE_FILES || wxUSE_CLIPBOARD
155
156
157 // the data for the listctrl
158 wxArrayString m_messages;
159 wxArrayInt m_severity;
160 wxArrayLong m_times;
161
162 // the controls which are not shown initially (but only when details
163 // button is pressed)
164 wxListCtrl *m_listctrl;
165
166 // the translated "Details" string
167 static wxString ms_details;
168
169 // the maximum length of the log message
170 static size_t ms_maxLength;
171
172 DECLARE_EVENT_TABLE()
173 wxDECLARE_NO_COPY_CLASS(wxLogDialog);
174};
175
176BEGIN_EVENT_TABLE(wxLogDialog, wxDialog)
177 EVT_BUTTON(wxID_OK, wxLogDialog::OnOk)
178#if wxUSE_CLIPBOARD
179 EVT_BUTTON(wxID_COPY, wxLogDialog::OnCopy)
180#endif // wxUSE_CLIPBOARD
181#if CAN_SAVE_FILES
182 EVT_BUTTON(wxID_SAVE, wxLogDialog::OnSave)
183#endif // CAN_SAVE_FILES
184 EVT_LIST_ITEM_ACTIVATED(wxID_ANY, wxLogDialog::OnListItemActivated)
185END_EVENT_TABLE()
186
187#endif // wxUSE_LOG_DIALOG
188
189// ----------------------------------------------------------------------------
190// private functions
191// ----------------------------------------------------------------------------
192
193#if CAN_SAVE_FILES
194
195// pass an uninitialized file object, the function will ask the user for the
196// filename and try to open it, returns true on success (file was opened),
197// false if file couldn't be opened/created and -1 if the file selection
198// dialog was cancelled
199static int OpenLogFile(wxFile& file, wxString *filename = NULL, wxWindow *parent = NULL);
200
201#endif // CAN_SAVE_FILES
202
203// ----------------------------------------------------------------------------
204// global variables
205// ----------------------------------------------------------------------------
206
207// we use a global variable to store the frame pointer for wxLogStatus - bad,
208// but it's the easiest way
209static wxFrame *gs_pFrame = NULL; // FIXME MT-unsafe
210
211// ============================================================================
212// implementation
213// ============================================================================
214
215// ----------------------------------------------------------------------------
216// global functions
217// ----------------------------------------------------------------------------
218
219// accepts an additional argument which tells to which frame the output should
220// be directed
221void wxVLogStatus(wxFrame *pFrame, const wxString& format, va_list argptr)
222{
223 wxString msg;
224
225 wxLog *pLog = wxLog::GetActiveTarget();
226 if ( pLog != NULL ) {
227 msg.PrintfV(format, argptr);
228
229 wxASSERT( gs_pFrame == NULL ); // should be reset!
230 gs_pFrame = pFrame;
231#ifdef __WXWINCE__
232 wxLog::OnLog(wxLOG_Status, msg, 0);
233#else
234 wxLog::OnLog(wxLOG_Status, msg, time(NULL));
235#endif
236 gs_pFrame = NULL;
237 }
238}
239
240#if !wxUSE_UTF8_LOCALE_ONLY
241void wxDoLogStatusWchar(wxFrame *pFrame, const wxChar *format, ...)
242{
243 va_list argptr;
244 va_start(argptr, format);
245 wxVLogStatus(pFrame, format, argptr);
246 va_end(argptr);
247}
248#endif // !wxUSE_UTF8_LOCALE_ONLY
249
250#if wxUSE_UNICODE_UTF8
251void wxDoLogStatusUtf8(wxFrame *pFrame, const char *format, ...)
252{
253 va_list argptr;
254 va_start(argptr, format);
255 wxVLogStatus(pFrame, format, argptr);
256 va_end(argptr);
257}
258#endif // wxUSE_UNICODE_UTF8
259
260// ----------------------------------------------------------------------------
261// wxLogGui implementation (FIXME MT-unsafe)
262// ----------------------------------------------------------------------------
263
264#if wxUSE_LOGGUI
265
266wxLogGui::wxLogGui()
267{
268 Clear();
269}
270
271void wxLogGui::Clear()
272{
273 m_bErrors =
274 m_bWarnings =
275 m_bHasMessages = false;
276
277 m_aMessages.Empty();
278 m_aSeverity.Empty();
279 m_aTimes.Empty();
280}
281
282int wxLogGui::GetSeverityIcon() const
283{
284 return m_bErrors ? wxICON_STOP
285 : m_bWarnings ? wxICON_EXCLAMATION
286 : wxICON_INFORMATION;
287}
288
289wxString wxLogGui::GetTitle() const
290{
291 wxString titleFormat;
292 switch ( GetSeverityIcon() )
293 {
294 case wxICON_STOP:
295 titleFormat = _("%s Error");
296 break;
297
298 case wxICON_EXCLAMATION:
299 titleFormat = _("%s Warning");
300 break;
301
302 default:
303 wxFAIL_MSG( "unexpected icon severity" );
304 // fall through
305
306 case wxICON_INFORMATION:
307 titleFormat = _("%s Information");
308 }
309
310 return wxString::Format(titleFormat, wxTheApp->GetAppDisplayName());
311}
312
313void
314wxLogGui::DoShowSingleLogMessage(const wxString& message,
315 const wxString& title,
316 int style)
317{
318 wxMessageBox(message, title, wxOK | style);
319}
320
321void
322wxLogGui::DoShowMultipleLogMessages(const wxArrayString& messages,
323 const wxArrayInt& severities,
324 const wxArrayLong& times,
325 const wxString& title,
326 int style)
327{
328#if wxUSE_LOG_DIALOG
329 wxLogDialog dlg(NULL,
330 messages, severities, times,
331 title, style);
332
333 // clear the message list before showing the dialog because while it's
334 // shown some new messages may appear
335 Clear();
336
337 (void)dlg.ShowModal();
338#else // !wxUSE_LOG_DIALOG
339 // start from the most recent message
340 wxString message;
341 const size_t nMsgCount = messages.size();
342 message.reserve(nMsgCount*100);
343 for ( size_t n = nMsgCount; n > 0; n-- ) {
344 message << m_aMessages[n - 1] << wxT("\n");
345 }
346
347 DoShowSingleLogMessage(message, title, style);
348#endif // wxUSE_LOG_DIALOG/!wxUSE_LOG_DIALOG
349}
350
351void wxLogGui::Flush()
352{
353 if ( !m_bHasMessages )
354 return;
355
356 // do it right now to block any new calls to Flush() while we're here
357 m_bHasMessages = false;
358
359 // note that this must be done before examining m_aMessages as it may log
360 // yet another message
361 const unsigned repeatCount = LogLastRepeatIfNeeded();
362
363 const size_t nMsgCount = m_aMessages.size();
364
365 if ( repeatCount > 0 )
366 {
367 m_aMessages[nMsgCount - 1] << " (" << m_aMessages[nMsgCount - 2] << ")";
368 }
369
370 const wxString title = GetTitle();
371 const int style = GetSeverityIcon();
372
373 // avoid showing other log dialogs until we're done with the dialog we're
374 // showing right now: nested modal dialogs make for really bad UI!
375 Suspend();
376
377 if ( nMsgCount == 1 )
378 {
379 // make a copy before calling Clear()
380 const wxString message(m_aMessages[0]);
381 Clear();
382
383 DoShowSingleLogMessage(message, title, style);
384 }
385 else // more than one message
386 {
387 wxArrayString messages;
388 wxArrayInt severities;
389 wxArrayLong times;
390
391 messages.swap(m_aMessages);
392 severities.swap(m_aSeverity);
393 times.swap(m_aTimes);
394
395 Clear();
396
397 DoShowMultipleLogMessages(messages, severities, times, title, style);
398 }
399
400 // allow flushing the logs again
401 Resume();
402}
403
404// log all kinds of messages
405void wxLogGui::DoLog(wxLogLevel level, const wxString& szString, time_t t)
406{
407 switch ( level ) {
408 case wxLOG_Info:
409 if ( GetVerbose() )
410 case wxLOG_Message:
411 {
412 m_aMessages.Add(szString);
413 m_aSeverity.Add(wxLOG_Message);
414 m_aTimes.Add((long)t);
415 m_bHasMessages = true;
416 }
417 break;
418
419 case wxLOG_Status:
420#if wxUSE_STATUSBAR
421 {
422 // find the top window and set it's status text if it has any
423 wxFrame *pFrame = gs_pFrame;
424 if ( pFrame == NULL ) {
425 wxWindow *pWin = wxTheApp->GetTopWindow();
426 if ( pWin != NULL && pWin->IsKindOf(CLASSINFO(wxFrame)) ) {
427 pFrame = (wxFrame *)pWin;
428 }
429 }
430
431 if ( pFrame && pFrame->GetStatusBar() )
432 pFrame->SetStatusText(szString);
433 }
434#endif // wxUSE_STATUSBAR
435 break;
436
437 case wxLOG_Trace:
438 case wxLOG_Debug:
439 #ifdef __WXDEBUG__
440 {
441 wxString str;
442 TimeStamp(&str);
443 str += szString;
444
445 #if defined(__WXMSW__) && !defined(__WXMICROWIN__)
446 // don't prepend debug/trace here: it goes to the
447 // debug window anyhow
448 str += wxT("\r\n");
449 OutputDebugString(str.wx_str());
450 #else
451 // send them to stderr
452 wxFprintf(stderr, wxT("[%s] %s\n"),
453 level == wxLOG_Trace ? wxT("Trace")
454 : wxT("Debug"),
455 str.c_str());
456 fflush(stderr);
457 #endif
458 }
459 #endif // __WXDEBUG__
460
461 break;
462
463 case wxLOG_FatalError:
464 // show this one immediately
465 wxMessageBox(szString, _("Fatal error"), wxICON_HAND);
466 wxExit();
467 break;
468
469 case wxLOG_Error:
470 if ( !m_bErrors ) {
471#if !wxUSE_LOG_DIALOG
472 // discard earlier informational messages if this is the 1st
473 // error because they might not make sense any more and showing
474 // them in a message box might be confusing
475 m_aMessages.Empty();
476 m_aSeverity.Empty();
477 m_aTimes.Empty();
478#endif // wxUSE_LOG_DIALOG
479 m_bErrors = true;
480 }
481 // fall through
482
483 case wxLOG_Warning:
484 if ( !m_bErrors ) {
485 // for the warning we don't discard the info messages
486 m_bWarnings = true;
487 }
488
489 m_aMessages.Add(szString);
490 m_aSeverity.Add((int)level);
491 m_aTimes.Add((long)t);
492 m_bHasMessages = true;
493 break;
494 }
495}
496
497#endif // wxUSE_LOGGUI
498
499// ----------------------------------------------------------------------------
500// wxLogWindow and wxLogFrame implementation
501// ----------------------------------------------------------------------------
502
503#if wxUSE_LOGWINDOW
504
505// log frame class
506// ---------------
507class wxLogFrame : public wxFrame
508{
509public:
510 // ctor & dtor
511 wxLogFrame(wxWindow *pParent, wxLogWindow *log, const wxString& szTitle);
512 virtual ~wxLogFrame();
513
514 // menu callbacks
515 void OnClose(wxCommandEvent& event);
516 void OnCloseWindow(wxCloseEvent& event);
517#if CAN_SAVE_FILES
518 void OnSave(wxCommandEvent& event);
519#endif // CAN_SAVE_FILES
520 void OnClear(wxCommandEvent& event);
521
522 // this function is safe to call from any thread (notice that it should be
523 // also called from the main thread to ensure that the messages logged from
524 // it appear in correct order with the messages from the other threads)
525 void AddLogMessage(const wxString& message);
526
527 // actually append the messages logged from secondary threads to the text
528 // control during idle time in the main thread
529 virtual void OnInternalIdle();
530
531private:
532 // use standard ids for our commands!
533 enum
534 {
535 Menu_Close = wxID_CLOSE,
536 Menu_Save = wxID_SAVE,
537 Menu_Clear = wxID_CLEAR
538 };
539
540 // common part of OnClose() and OnCloseWindow()
541 void DoClose();
542
543 // do show the message in the text control
544 void DoShowLogMessage(const wxString& message)
545 {
546 m_pTextCtrl->AppendText(message);
547 }
548
549 wxTextCtrl *m_pTextCtrl;
550 wxLogWindow *m_log;
551
552 // queue of messages logged from other threads which need to be displayed
553 wxArrayString m_pendingMessages;
554
555#if wxUSE_THREADS
556 // critical section to protect access to m_pendingMessages
557 wxCriticalSection m_critSection;
558#endif // wxUSE_THREADS
559
560
561 DECLARE_EVENT_TABLE()
562 wxDECLARE_NO_COPY_CLASS(wxLogFrame);
563};
564
565BEGIN_EVENT_TABLE(wxLogFrame, wxFrame)
566 // wxLogWindow menu events
567 EVT_MENU(Menu_Close, wxLogFrame::OnClose)
568#if CAN_SAVE_FILES
569 EVT_MENU(Menu_Save, wxLogFrame::OnSave)
570#endif // CAN_SAVE_FILES
571 EVT_MENU(Menu_Clear, wxLogFrame::OnClear)
572
573 EVT_CLOSE(wxLogFrame::OnCloseWindow)
574END_EVENT_TABLE()
575
576wxLogFrame::wxLogFrame(wxWindow *pParent, wxLogWindow *log, const wxString& szTitle)
577 : wxFrame(pParent, wxID_ANY, szTitle)
578{
579 m_log = log;
580
581 m_pTextCtrl = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition,
582 wxDefaultSize,
583 wxTE_MULTILINE |
584 wxHSCROLL |
585 // needed for Win32 to avoid 65Kb limit but it doesn't work well
586 // when using RichEdit 2.0 which we always do in the Unicode build
587#if !wxUSE_UNICODE
588 wxTE_RICH |
589#endif // !wxUSE_UNICODE
590 wxTE_READONLY);
591
592#if wxUSE_MENUS
593 // create menu
594 wxMenuBar *pMenuBar = new wxMenuBar;
595 wxMenu *pMenu = new wxMenu;
596#if CAN_SAVE_FILES
597 pMenu->Append(Menu_Save, _("&Save..."), _("Save log contents to file"));
598#endif // CAN_SAVE_FILES
599 pMenu->Append(Menu_Clear, _("C&lear"), _("Clear the log contents"));
600 pMenu->AppendSeparator();
601 pMenu->Append(Menu_Close, _("&Close"), _("Close this window"));
602 pMenuBar->Append(pMenu, _("&Log"));
603 SetMenuBar(pMenuBar);
604#endif // wxUSE_MENUS
605
606#if wxUSE_STATUSBAR
607 // status bar for menu prompts
608 CreateStatusBar();
609#endif // wxUSE_STATUSBAR
610
611 m_log->OnFrameCreate(this);
612}
613
614void wxLogFrame::DoClose()
615{
616 if ( m_log->OnFrameClose(this) )
617 {
618 // instead of closing just hide the window to be able to Show() it
619 // later
620 Show(false);
621 }
622}
623
624void wxLogFrame::OnClose(wxCommandEvent& WXUNUSED(event))
625{
626 DoClose();
627}
628
629void wxLogFrame::OnCloseWindow(wxCloseEvent& WXUNUSED(event))
630{
631 DoClose();
632}
633
634#if CAN_SAVE_FILES
635void wxLogFrame::OnSave(wxCommandEvent& WXUNUSED(event))
636{
637 wxString filename;
638 wxFile file;
639 int rc = OpenLogFile(file, &filename, this);
640 if ( rc == -1 )
641 {
642 // cancelled
643 return;
644 }
645
646 bool bOk = rc != 0;
647
648 // retrieve text and save it
649 // -------------------------
650 int nLines = m_pTextCtrl->GetNumberOfLines();
651 for ( int nLine = 0; bOk && nLine < nLines; nLine++ ) {
652 bOk = file.Write(m_pTextCtrl->GetLineText(nLine) +
653 wxTextFile::GetEOL());
654 }
655
656 if ( bOk )
657 bOk = file.Close();
658
659 if ( !bOk ) {
660 wxLogError(_("Can't save log contents to file."));
661 }
662 else {
663 wxLogStatus((wxFrame*)this, _("Log saved to the file '%s'."), filename.c_str());
664 }
665}
666#endif // CAN_SAVE_FILES
667
668void wxLogFrame::OnClear(wxCommandEvent& WXUNUSED(event))
669{
670 m_pTextCtrl->Clear();
671}
672
673void wxLogFrame::OnInternalIdle()
674{
675 {
676 wxCRIT_SECT_LOCKER(locker, m_critSection);
677
678 const size_t count = m_pendingMessages.size();
679 for ( size_t n = 0; n < count; n++ )
680 {
681 DoShowLogMessage(m_pendingMessages[n]);
682 }
683
684 m_pendingMessages.clear();
685 } // release m_critSection
686
687 wxFrame::OnInternalIdle();
688}
689
690void wxLogFrame::AddLogMessage(const wxString& message)
691{
692 wxCRIT_SECT_LOCKER(locker, m_critSection);
693
694#if wxUSE_THREADS
695 if ( !wxThread::IsMain() || !m_pendingMessages.empty() )
696 {
697 // message needs to be queued for later showing
698 m_pendingMessages.Add(message);
699
700 wxWakeUpIdle();
701 }
702 else // we are the main thread and no messages are queued, so we can
703 // log the message directly
704#endif // wxUSE_THREADS
705 {
706 DoShowLogMessage(message);
707 }
708}
709
710wxLogFrame::~wxLogFrame()
711{
712 m_log->OnFrameDelete(this);
713}
714
715// wxLogWindow
716// -----------
717
718wxLogWindow::wxLogWindow(wxWindow *pParent,
719 const wxString& szTitle,
720 bool bShow,
721 bool bDoPass)
722{
723 PassMessages(bDoPass);
724
725 m_pLogFrame = new wxLogFrame(pParent, this, szTitle);
726
727 if ( bShow )
728 m_pLogFrame->Show();
729}
730
731void wxLogWindow::Show(bool bShow)
732{
733 m_pLogFrame->Show(bShow);
734}
735
736void wxLogWindow::DoLog(wxLogLevel level, const wxString& szString, time_t t)
737{
738 // first let the previous logger show it
739 wxLogPassThrough::DoLog(level, szString, t);
740
741 if ( m_pLogFrame ) {
742 switch ( level ) {
743 case wxLOG_Status:
744 // by default, these messages are ignored by wxLog, so process
745 // them ourselves
746 if ( !szString.empty() )
747 {
748 wxString str;
749 str << _("Status: ") << szString;
750 LogString(str, t);
751 }
752 break;
753
754 // don't put trace messages in the text window for 2 reasons:
755 // 1) there are too many of them
756 // 2) they may provoke other trace messages thus sending a program
757 // into an infinite loop
758 case wxLOG_Trace:
759 break;
760
761 default:
762 // and this will format it nicely and call our DoLogString()
763 wxLog::DoLog(level, szString, t);
764 }
765 }
766}
767
768void wxLogWindow::DoLogString(const wxString& szString, time_t WXUNUSED(t))
769{
770 wxString msg;
771
772 TimeStamp(&msg);
773 msg << szString << wxT('\n');
774
775 m_pLogFrame->AddLogMessage(msg);
776}
777
778wxFrame *wxLogWindow::GetFrame() const
779{
780 return m_pLogFrame;
781}
782
783void wxLogWindow::OnFrameCreate(wxFrame * WXUNUSED(frame))
784{
785}
786
787bool wxLogWindow::OnFrameClose(wxFrame * WXUNUSED(frame))
788{
789 // allow to close
790 return true;
791}
792
793void wxLogWindow::OnFrameDelete(wxFrame * WXUNUSED(frame))
794{
795 m_pLogFrame = NULL;
796}
797
798wxLogWindow::~wxLogWindow()
799{
800 // may be NULL if log frame already auto destroyed itself
801 delete m_pLogFrame;
802}
803
804#endif // wxUSE_LOGWINDOW
805
806// ----------------------------------------------------------------------------
807// wxLogDialog
808// ----------------------------------------------------------------------------
809
810#if wxUSE_LOG_DIALOG
811
812wxString wxLogDialog::ms_details;
813size_t wxLogDialog::ms_maxLength = 0;
814
815wxLogDialog::wxLogDialog(wxWindow *parent,
816 const wxArrayString& messages,
817 const wxArrayInt& severity,
818 const wxArrayLong& times,
819 const wxString& caption,
820 long style)
821 : wxDialog(parent, wxID_ANY, caption,
822 wxDefaultPosition, wxDefaultSize,
823 wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
824{
825 // init the static variables:
826
827 if ( ms_details.empty() )
828 {
829 // ensure that we won't loop here if wxGetTranslation()
830 // happens to pop up a Log message while translating this :-)
831 ms_details = wxTRANSLATE("&Details");
832 ms_details = wxGetTranslation(ms_details);
833#ifdef __SMARTPHONE__
834 ms_details = wxStripMenuCodes(ms_details);
835#endif
836 }
837
838 if ( ms_maxLength == 0 )
839 {
840 ms_maxLength = (2 * wxGetDisplaySize().x/3) / GetCharWidth();
841 }
842
843 size_t count = messages.GetCount();
844 m_messages.Alloc(count);
845 m_severity.Alloc(count);
846 m_times.Alloc(count);
847
848 for ( size_t n = 0; n < count; n++ )
849 {
850 m_messages.Add(messages[n]);
851 m_severity.Add(severity[n]);
852 m_times.Add(times[n]);
853 }
854
855 m_listctrl = NULL;
856
857 bool isPda = (wxSystemSettings::GetScreenType() <= wxSYS_SCREEN_PDA);
858
859 // create the controls which are always shown and layout them: we use
860 // sizers even though our window is not resizeable to calculate the size of
861 // the dialog properly
862 wxBoxSizer *sizerTop = new wxBoxSizer(wxVERTICAL);
863 wxBoxSizer *sizerAll = new wxBoxSizer(isPda ? wxVERTICAL : wxHORIZONTAL);
864
865 if (!isPda)
866 {
867 wxStaticBitmap *icon = new wxStaticBitmap
868 (
869 this,
870 wxID_ANY,
871 wxArtProvider::GetMessageBoxIcon(style)
872 );
873 sizerAll->Add(icon, wxSizerFlags().Centre());
874 }
875
876 // create the text sizer with a minimal size so that we are sure it won't be too small
877 wxString message = EllipsizeString(messages.Last());
878 wxSizer *szText = CreateTextSizer(message);
879 szText->SetMinSize(wxMin(300, wxGetDisplaySize().x / 3), -1);
880
881 sizerAll->Add(szText, wxSizerFlags(1).Centre().Border(wxLEFT | wxRIGHT));
882
883 wxButton *btnOk = new wxButton(this, wxID_OK);
884 sizerAll->Add(btnOk, wxSizerFlags().Centre());
885
886 sizerTop->Add(sizerAll, wxSizerFlags().Expand().Border());
887
888
889 // add the details pane
890#ifndef __SMARTPHONE__
891 wxCollapsiblePane * const
892 collpane = new wxCollapsiblePane(this, wxID_ANY, ms_details);
893 sizerTop->Add(collpane, wxSizerFlags(1).Expand().Border());
894
895 wxWindow *win = collpane->GetPane();
896 wxSizer * const paneSz = new wxBoxSizer(wxVERTICAL);
897
898 CreateDetailsControls(win);
899
900 paneSz->Add(m_listctrl, wxSizerFlags(1).Expand().Border(wxTOP));
901
902#if wxUSE_CLIPBOARD || CAN_SAVE_FILES
903 wxBoxSizer * const btnSizer = new wxBoxSizer(wxHORIZONTAL);
904
905 wxSizerFlags flagsBtn;
906 flagsBtn.Border(wxLEFT);
907
908#if wxUSE_CLIPBOARD
909 btnSizer->Add(new wxButton(win, wxID_COPY), flagsBtn);
910#endif // wxUSE_CLIPBOARD
911
912#if CAN_SAVE_FILES
913 btnSizer->Add(new wxButton(win, wxID_SAVE), flagsBtn);
914#endif // CAN_SAVE_FILES
915
916 paneSz->Add(btnSizer, wxSizerFlags().Right().Border(wxTOP));
917#endif // wxUSE_CLIPBOARD || CAN_SAVE_FILES
918
919 win->SetSizer(paneSz);
920 paneSz->SetSizeHints(win);
921#else // __SMARTPHONE__
922 SetLeftMenu(wxID_OK);
923 SetRightMenu(wxID_MORE, ms_details + EXPAND_SUFFIX);
924#endif // __SMARTPHONE__/!__SMARTPHONE__
925
926 SetSizerAndFit(sizerTop);
927
928 Centre();
929
930 if (isPda)
931 {
932 // Move up the screen so that when we expand the dialog,
933 // there's enough space.
934 Move(wxPoint(GetPosition().x, GetPosition().y / 2));
935 }
936}
937
938void wxLogDialog::CreateDetailsControls(wxWindow *parent)
939{
940 // create the list ctrl now
941 m_listctrl = new wxListCtrl(parent, wxID_ANY,
942 wxDefaultPosition, wxDefaultSize,
943 wxSUNKEN_BORDER |
944 wxLC_REPORT |
945 wxLC_NO_HEADER |
946 wxLC_SINGLE_SEL);
947#ifdef __WXWINCE__
948 // This makes a big aesthetic difference on WinCE but I
949 // don't want to risk problems on other platforms
950 m_listctrl->Hide();
951#endif
952
953 // no need to translate these strings as they're not shown to the
954 // user anyhow (we use wxLC_NO_HEADER style)
955 m_listctrl->InsertColumn(0, _T("Message"));
956 m_listctrl->InsertColumn(1, _T("Time"));
957
958 // prepare the imagelist
959 static const int ICON_SIZE = 16;
960 wxImageList *imageList = new wxImageList(ICON_SIZE, ICON_SIZE);
961
962 // order should be the same as in the switch below!
963 static const wxChar* icons[] =
964 {
965 wxART_ERROR,
966 wxART_WARNING,
967 wxART_INFORMATION
968 };
969
970 bool loadedIcons = true;
971
972 for ( size_t icon = 0; icon < WXSIZEOF(icons); icon++ )
973 {
974 wxBitmap bmp = wxArtProvider::GetBitmap(icons[icon], wxART_MESSAGE_BOX,
975 wxSize(ICON_SIZE, ICON_SIZE));
976
977 // This may very well fail if there are insufficient colours available.
978 // Degrade gracefully.
979 if ( !bmp.Ok() )
980 {
981 loadedIcons = false;
982
983 break;
984 }
985
986 imageList->Add(bmp);
987 }
988
989 m_listctrl->SetImageList(imageList, wxIMAGE_LIST_SMALL);
990
991 // and fill it
992 wxString fmt = wxLog::GetTimestamp();
993 if ( !fmt )
994 {
995 // default format
996 fmt = _T("%c");
997 }
998
999 size_t count = m_messages.GetCount();
1000 for ( size_t n = 0; n < count; n++ )
1001 {
1002 int image;
1003
1004 if ( loadedIcons )
1005 {
1006 switch ( m_severity[n] )
1007 {
1008 case wxLOG_Error:
1009 image = 0;
1010 break;
1011
1012 case wxLOG_Warning:
1013 image = 1;
1014 break;
1015
1016 default:
1017 image = 2;
1018 }
1019 }
1020 else // failed to load images
1021 {
1022 image = -1;
1023 }
1024
1025 wxString msg = m_messages[n];
1026 msg.Replace(wxT("\n"), wxT(" "));
1027 msg = EllipsizeString(msg);
1028
1029 m_listctrl->InsertItem(n, msg, image);
1030 m_listctrl->SetItem(n, 1, TimeStamp(fmt, (time_t)m_times[n]));
1031 }
1032
1033 // let the columns size themselves
1034 m_listctrl->SetColumnWidth(0, wxLIST_AUTOSIZE);
1035 m_listctrl->SetColumnWidth(1, wxLIST_AUTOSIZE);
1036
1037 // calculate an approximately nice height for the listctrl
1038 int height = GetCharHeight()*(count + 4);
1039
1040 // but check that the dialog won't fall fown from the screen
1041 //
1042 // we use GetMinHeight() to get the height of the dialog part without the
1043 // details and we consider that the "Save" button below and the separator
1044 // line (and the margins around it) take about as much, hence double it
1045 int heightMax = wxGetDisplaySize().y - GetPosition().y - 2*GetMinHeight();
1046
1047 // we should leave a margin
1048 heightMax *= 9;
1049 heightMax /= 10;
1050
1051 m_listctrl->SetSize(wxDefaultCoord, wxMin(height, heightMax));
1052}
1053
1054void wxLogDialog::OnListItemActivated(wxListEvent& event)
1055{
1056 // show the activated item in a message box
1057 // This allow the user to correctly display the logs which are longer
1058 // than the listctrl and thus gets truncated or those which contains
1059 // newlines.
1060
1061 // NB: don't do:
1062 // wxString str = m_listctrl->GetItemText(event.GetIndex());
1063 // as there's a 260 chars limit on the items inside a wxListCtrl in wxMSW.
1064 wxString str = m_messages[event.GetIndex()];
1065
1066 // wxMessageBox will nicely handle the '\n' in the string (if any)
1067 // and supports long strings
1068 wxMessageBox(str, wxT("Log message"), wxOK, this);
1069}
1070
1071void wxLogDialog::OnOk(wxCommandEvent& WXUNUSED(event))
1072{
1073 EndModal(wxID_OK);
1074}
1075
1076#if CAN_SAVE_FILES || wxUSE_CLIPBOARD
1077
1078wxString wxLogDialog::GetLogMessages() const
1079{
1080 wxString fmt = wxLog::GetTimestamp();
1081 if ( fmt.empty() )
1082 {
1083 // use the default format
1084 fmt = "%c";
1085 }
1086
1087 const size_t count = m_messages.GetCount();
1088
1089 wxString text;
1090 text.reserve(count*m_messages[0].length());
1091 for ( size_t n = 0; n < count; n++ )
1092 {
1093 text << TimeStamp(fmt, (time_t)m_times[n])
1094 << ": "
1095 << m_messages[n]
1096 << wxTextFile::GetEOL();
1097 }
1098
1099 return text;
1100}
1101
1102#endif // CAN_SAVE_FILES || wxUSE_CLIPBOARD
1103
1104#if wxUSE_CLIPBOARD
1105
1106void wxLogDialog::OnCopy(wxCommandEvent& WXUNUSED(event))
1107{
1108 wxClipboardLocker clip;
1109 if ( !clip ||
1110 !wxTheClipboard->AddData(new wxTextDataObject(GetLogMessages())) )
1111 {
1112 wxLogError(_("Failed to copy dialog contents to the clipboard."));
1113 }
1114}
1115
1116#endif // wxUSE_CLIPBOARD
1117
1118#if CAN_SAVE_FILES
1119
1120void wxLogDialog::OnSave(wxCommandEvent& WXUNUSED(event))
1121{
1122 wxFile file;
1123 int rc = OpenLogFile(file, NULL, this);
1124 if ( rc == -1 )
1125 {
1126 // cancelled
1127 return;
1128 }
1129
1130 if ( !rc || !file.Write(GetLogMessages()) || !file.Close() )
1131 wxLogError(_("Can't save log contents to file."));
1132}
1133
1134#endif // CAN_SAVE_FILES
1135
1136wxLogDialog::~wxLogDialog()
1137{
1138 if ( m_listctrl )
1139 {
1140 delete m_listctrl->GetImageList(wxIMAGE_LIST_SMALL);
1141 }
1142}
1143
1144#endif // wxUSE_LOG_DIALOG
1145
1146#if CAN_SAVE_FILES
1147
1148// pass an uninitialized file object, the function will ask the user for the
1149// filename and try to open it, returns true on success (file was opened),
1150// false if file couldn't be opened/created and -1 if the file selection
1151// dialog was cancelled
1152static int OpenLogFile(wxFile& file, wxString *pFilename, wxWindow *parent)
1153{
1154 // get the file name
1155 // -----------------
1156 wxString filename = wxSaveFileSelector(wxT("log"), wxT("txt"), wxT("log.txt"), parent);
1157 if ( !filename ) {
1158 // cancelled
1159 return -1;
1160 }
1161
1162 // open file
1163 // ---------
1164 bool bOk;
1165 if ( wxFile::Exists(filename) ) {
1166 bool bAppend = false;
1167 wxString strMsg;
1168 strMsg.Printf(_("Append log to file '%s' (choosing [No] will overwrite it)?"),
1169 filename.c_str());
1170 switch ( wxMessageBox(strMsg, _("Question"),
1171 wxICON_QUESTION | wxYES_NO | wxCANCEL) ) {
1172 case wxYES:
1173 bAppend = true;
1174 break;
1175
1176 case wxNO:
1177 bAppend = false;
1178 break;
1179
1180 case wxCANCEL:
1181 return -1;
1182
1183 default:
1184 wxFAIL_MSG(_("invalid message box return value"));
1185 }
1186
1187 if ( bAppend ) {
1188 bOk = file.Open(filename, wxFile::write_append);
1189 }
1190 else {
1191 bOk = file.Create(filename, true /* overwrite */);
1192 }
1193 }
1194 else {
1195 bOk = file.Create(filename);
1196 }
1197
1198 if ( pFilename )
1199 *pFilename = filename;
1200
1201 return bOk;
1202}
1203
1204#endif // CAN_SAVE_FILES
1205
1206#endif // !(wxUSE_LOGGUI || wxUSE_LOGWINDOW)
1207
1208#if wxUSE_LOG && wxUSE_TEXTCTRL
1209
1210// ----------------------------------------------------------------------------
1211// wxLogTextCtrl implementation
1212// ----------------------------------------------------------------------------
1213
1214wxLogTextCtrl::wxLogTextCtrl(wxTextCtrl *pTextCtrl)
1215{
1216 m_pTextCtrl = pTextCtrl;
1217}
1218
1219void wxLogTextCtrl::DoLogString(const wxString& szString, time_t WXUNUSED(t))
1220{
1221 wxString msg;
1222 TimeStamp(&msg);
1223
1224 msg << szString << wxT('\n');
1225 m_pTextCtrl->AppendText(msg);
1226}
1227
1228#endif // wxUSE_LOG && wxUSE_TEXTCTRL