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