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