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