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