Ok -> OK; status bar typo/cast
[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/intl.h"
37 #include "wx/log.h"
38 #include "wx/menu.h"
39 #include "wx/frame.h"
40 #include "wx/filedlg.h"
41 #include "wx/msgdlg.h"
42 #include "wx/textctrl.h"
43 #include "wx/sizer.h"
44 #include "wx/statbmp.h"
45 #endif // WX_PRECOMP
46
47 #include "wx/file.h"
48 #include "wx/textfile.h"
49
50 #ifdef __WXMSW__
51 // for OutputDebugString()
52 #include "wx/msw/private.h"
53 #endif // Windows
54
55 // may be defined to 0 for old behavior (using wxMessageBox) - shouldn't be
56 // changed normally (that's why it's here and not in setup.h)
57 #define wxUSE_LOG_DIALOG 1
58
59 #if wxUSE_LOG_DIALOG
60 #include "wx/datetime.h"
61 #include "wx/listctrl.h"
62 #include "wx/image.h"
63 #else // !wxUSE_TEXTFILE
64 #include "wx/msgdlg.h"
65 #endif // wxUSE_LOG_DIALOG/!wxUSE_LOG_DIALOG
66
67 // ----------------------------------------------------------------------------
68 // private classes
69 // ----------------------------------------------------------------------------
70
71 #if wxUSE_LOG_DIALOG
72
73 class wxLogDialog : public wxDialog
74 {
75 public:
76 wxLogDialog(wxWindow *parent,
77 const wxArrayString& messages,
78 const wxArrayInt& severity,
79 const wxArrayLong& timess,
80 const wxString& caption,
81 long style);
82 virtual ~wxLogDialog();
83
84 // event handlers
85 void OnOk(wxCommandEvent& event);
86 void OnDetails(wxCommandEvent& event);
87
88 private:
89 // the data for the listctrl
90 wxArrayString m_messages;
91 wxArrayInt m_severity;
92 wxArrayLong m_times;
93
94 // the "toggle" button and its state
95 wxButton *m_btnDetails;
96 bool m_showingDetails;
97
98 // the listctrl (not shown initially)
99 wxListCtrl *m_listctrl;
100
101 DECLARE_EVENT_TABLE()
102 };
103
104 BEGIN_EVENT_TABLE(wxLogDialog, wxDialog)
105 EVT_BUTTON(wxID_OK, wxLogDialog::OnOk)
106 EVT_BUTTON(wxID_MORE, wxLogDialog::OnDetails)
107 END_EVENT_TABLE()
108
109 #endif // wxUSE_LOG_DIALOG
110
111 // ----------------------------------------------------------------------------
112 // global variables
113 // ----------------------------------------------------------------------------
114
115 // we use a global variable to store the frame pointer for wxLogStatus - bad,
116 // but it's he easiest way
117 static wxFrame *gs_pFrame; // FIXME MT-unsafe
118
119 // ============================================================================
120 // implementation
121 // ============================================================================
122
123 // ----------------------------------------------------------------------------
124 // global functions
125 // ----------------------------------------------------------------------------
126
127 // accepts an additional argument which tells to which frame the output should
128 // be directed
129 void wxLogStatus(wxFrame *pFrame, const wxChar *szFormat, ...)
130 {
131 wxString msg;
132
133 wxLog *pLog = wxLog::GetActiveTarget();
134 if ( pLog != NULL ) {
135 va_list argptr;
136 va_start(argptr, szFormat);
137 msg.PrintfV(szFormat, argptr);
138 va_end(argptr);
139
140 wxASSERT( gs_pFrame == NULL ); // should be reset!
141 gs_pFrame = pFrame;
142 wxLog::OnLog(wxLOG_Status, msg, time(NULL));
143 gs_pFrame = (wxFrame *) NULL;
144 }
145 }
146
147 // ----------------------------------------------------------------------------
148 // wxLogTextCtrl implementation
149 // ----------------------------------------------------------------------------
150
151 wxLogTextCtrl::wxLogTextCtrl(wxTextCtrl *pTextCtrl)
152 {
153 m_pTextCtrl = pTextCtrl;
154 }
155
156 void wxLogTextCtrl::DoLogString(const wxChar *szString, time_t WXUNUSED(t))
157 {
158 wxString msg;
159 TimeStamp(&msg);
160 msg << szString << wxT('\n');
161
162 m_pTextCtrl->AppendText(msg);
163 }
164
165 // ----------------------------------------------------------------------------
166 // wxLogGui implementation (FIXME MT-unsafe)
167 // ----------------------------------------------------------------------------
168
169 wxLogGui::wxLogGui()
170 {
171 Clear();
172 }
173
174 void wxLogGui::Clear()
175 {
176 m_bErrors = m_bWarnings = FALSE;
177 m_aMessages.Empty();
178 m_aSeverity.Empty();
179 m_aTimes.Empty();
180 }
181
182 void wxLogGui::Flush()
183 {
184 if ( !m_bHasMessages )
185 return;
186
187 // do it right now to block any new calls to Flush() while we're here
188 m_bHasMessages = FALSE;
189
190 wxString title = wxTheApp->GetAppName();
191 if ( !!title )
192 {
193 title[0u] = wxToupper(title[0u]);
194 title += _T(' ');
195 }
196
197 long style;
198 if ( m_bErrors ) {
199 title += _("Error");
200 style = wxICON_STOP;
201 }
202 else if ( m_bWarnings ) {
203 title += _("Warning");
204 style = wxICON_EXCLAMATION;
205 }
206 else {
207 title += _("Information");
208 style = wxICON_INFORMATION;
209 }
210
211 #if wxUSE_LOG_DIALOG
212 wxLogDialog dlg(wxTheApp->GetTopWindow(),
213 m_aMessages, m_aSeverity, m_aTimes,
214 title, style);
215
216 // clear the message list before showing the dialog because while it's
217 // shown some new messages may appear
218 Clear();
219
220 (void)dlg.ShowModal();
221
222 #else // !wxUSE_LOG_DIALOG
223 // concatenate all strings (but not too many to not overfill the msg box)
224 wxString str;
225 size_t nLines = 0,
226 nMsgCount = m_aMessages.Count();
227
228 // start from the most recent message
229 for ( size_t n = nMsgCount; n > 0; n-- ) {
230 // for Windows strings longer than this value are wrapped (NT 4.0)
231 const size_t nMsgLineWidth = 156;
232
233 nLines += (m_aMessages[n - 1].Len() + nMsgLineWidth - 1) / nMsgLineWidth;
234
235 if ( nLines > 25 ) // don't put too many lines in message box
236 break;
237
238 str << m_aMessages[n - 1] << wxT("\n");
239 }
240
241 wxMessageBox(str, title, wxOK | style);
242
243 // no undisplayed messages whatsoever
244 Clear();
245 #endif // wxUSE_LOG_DIALOG/!wxUSE_LOG_DIALOG
246
247 // do it here again
248 m_bHasMessages = FALSE;
249 }
250
251 // the default behaviour is to discard all informational messages if there
252 // are any errors/warnings.
253 void wxLogGui::DoLog(wxLogLevel level, const wxChar *szString, time_t t)
254 {
255 switch ( level ) {
256 case wxLOG_Info:
257 if ( GetVerbose() )
258 case wxLOG_Message:
259 {
260 if ( !m_bErrors ) {
261 m_aMessages.Add(szString);
262 m_aSeverity.Add(wxLOG_Message);
263 m_aTimes.Add((long)t);
264 m_bHasMessages = TRUE;
265 }
266 }
267 break;
268
269 case wxLOG_Status:
270 #if wxUSE_STATUSBAR
271 {
272 // find the top window and set it's status text if it has any
273 wxFrame *pFrame = gs_pFrame;
274 if ( pFrame == NULL ) {
275 wxWindow *pWin = wxTheApp->GetTopWindow();
276 if ( pWin != NULL && pWin->IsKindOf(CLASSINFO(wxFrame)) ) {
277 pFrame = (wxFrame *)pWin;
278 }
279 }
280
281 if ( pFrame && pFrame->GetStatusBar() )
282 pFrame->SetStatusText(szString);
283 }
284 #endif // wxUSE_STATUSBAR
285 break;
286
287 case wxLOG_Trace:
288 case wxLOG_Debug:
289 #ifdef __WXDEBUG__
290 {
291 #ifdef __WXMSW__
292 // don't prepend debug/trace here: it goes to the
293 // debug window anyhow, but do put a timestamp
294 wxString str;
295 TimeStamp(&str);
296 str << szString << wxT("\n\r");
297 OutputDebugString(str);
298 #else
299 // send them to stderr
300 wxFprintf(stderr, wxT("%s: %s\n"),
301 level == wxLOG_Trace ? wxT("Trace")
302 : wxT("Debug"),
303 szString);
304 fflush(stderr);
305 #endif
306 }
307 #endif // __WXDEBUG__
308
309 break;
310
311 case wxLOG_FatalError:
312 // show this one immediately
313 wxMessageBox(szString, _("Fatal error"), wxICON_HAND);
314 break;
315
316 case wxLOG_Error:
317 if ( !m_bErrors ) {
318 #if !wxUSE_LOG_DIALOG
319 // discard earlier informational messages if this is the 1st
320 // error because they might not make sense any more and showing
321 // them in a message box might be confusing
322 m_aMessages.Empty();
323 m_aSeverity.Empty();
324 m_aTimes.Empty();
325 #endif // wxUSE_LOG_DIALOG
326 m_bErrors = TRUE;
327 }
328 // fall through
329
330 case wxLOG_Warning:
331 if ( !m_bErrors ) {
332 // for the warning we don't discard the info messages
333 m_bWarnings = TRUE;
334 }
335
336 m_aMessages.Add(szString);
337 m_aSeverity.Add(level);
338 m_aTimes.Add((long)t);
339 m_bHasMessages = TRUE;
340 break;
341 }
342 }
343
344 // ----------------------------------------------------------------------------
345 // wxLogWindow and wxLogFrame implementation
346 // ----------------------------------------------------------------------------
347
348 // log frame class
349 // ---------------
350 class wxLogFrame : public wxFrame
351 {
352 public:
353 // ctor & dtor
354 wxLogFrame(wxFrame *pParent, wxLogWindow *log, const wxChar *szTitle);
355 virtual ~wxLogFrame();
356
357 // menu callbacks
358 void OnClose(wxCommandEvent& event);
359 void OnCloseWindow(wxCloseEvent& event);
360 #if wxUSE_FILE
361 void OnSave (wxCommandEvent& event);
362 #endif // wxUSE_FILE
363 void OnClear(wxCommandEvent& event);
364
365 void OnIdle(wxIdleEvent&);
366
367 // accessors
368 wxTextCtrl *TextCtrl() const { return m_pTextCtrl; }
369
370 private:
371 enum
372 {
373 Menu_Close = 100,
374 Menu_Save,
375 Menu_Clear
376 };
377
378 // instead of closing just hide the window to be able to Show() it later
379 void DoClose() { Show(FALSE); }
380
381 wxTextCtrl *m_pTextCtrl;
382 wxLogWindow *m_log;
383
384 DECLARE_EVENT_TABLE()
385 };
386
387 BEGIN_EVENT_TABLE(wxLogFrame, wxFrame)
388 // wxLogWindow menu events
389 EVT_MENU(Menu_Close, wxLogFrame::OnClose)
390 #if wxUSE_FILE
391 EVT_MENU(Menu_Save, wxLogFrame::OnSave)
392 #endif // wxUSE_FILE
393 EVT_MENU(Menu_Clear, wxLogFrame::OnClear)
394
395 EVT_CLOSE(wxLogFrame::OnCloseWindow)
396 END_EVENT_TABLE()
397
398 wxLogFrame::wxLogFrame(wxFrame *pParent, wxLogWindow *log, const wxChar *szTitle)
399 : wxFrame(pParent, -1, szTitle)
400 {
401 m_log = log;
402
403 m_pTextCtrl = new wxTextCtrl(this, -1, wxEmptyString, wxDefaultPosition,
404 wxDefaultSize,
405 wxTE_MULTILINE |
406 wxHSCROLL |
407 wxTE_READONLY);
408
409 // create menu
410 wxMenuBar *pMenuBar = new wxMenuBar;
411 wxMenu *pMenu = new wxMenu;
412 #if wxUSE_FILE
413 pMenu->Append(Menu_Save, _("&Save..."), _("Save log contents to file"));
414 #endif // wxUSE_FILE
415 pMenu->Append(Menu_Clear, _("C&lear"), _("Clear the log contents"));
416 pMenu->AppendSeparator();
417 pMenu->Append(Menu_Close, _("&Close"), _("Close this window"));
418 pMenuBar->Append(pMenu, _("&Log"));
419 SetMenuBar(pMenuBar);
420
421 #if wxUSE_STATUSBAR
422 // status bar for menu prompts
423 CreateStatusBar();
424 #endif // wxUSE_STATUSBAR
425
426 m_log->OnFrameCreate(this);
427 }
428
429 void wxLogFrame::OnClose(wxCommandEvent& WXUNUSED(event))
430 {
431 DoClose();
432 }
433
434 void wxLogFrame::OnCloseWindow(wxCloseEvent& WXUNUSED(event))
435 {
436 DoClose();
437 }
438
439 #if wxUSE_FILE
440 void wxLogFrame::OnSave(wxCommandEvent& WXUNUSED(event))
441 {
442 // get the file name
443 // -----------------
444 const wxChar *szFileName = wxSaveFileSelector(wxT("log"), wxT("txt"), wxT("log.txt"));
445 if ( szFileName == NULL ) {
446 // cancelled
447 return;
448 }
449
450 // open file
451 // ---------
452 wxFile file;
453 bool bOk = FALSE;
454 if ( wxFile::Exists(szFileName) ) {
455 bool bAppend = FALSE;
456 wxString strMsg;
457 strMsg.Printf(_("Append log to file '%s' "
458 "(choosing [No] will overwrite it)?"), szFileName);
459 switch ( wxMessageBox(strMsg, _("Question"), wxYES_NO | wxCANCEL) ) {
460 case wxYES:
461 bAppend = TRUE;
462 break;
463
464 case wxNO:
465 bAppend = FALSE;
466 break;
467
468 case wxCANCEL:
469 return;
470
471 default:
472 wxFAIL_MSG(_("invalid message box return value"));
473 }
474
475 if ( bAppend ) {
476 bOk = file.Open(szFileName, wxFile::write_append);
477 }
478 else {
479 bOk = file.Create(szFileName, TRUE /* overwrite */);
480 }
481 }
482 else {
483 bOk = file.Create(szFileName);
484 }
485
486 // retrieve text and save it
487 // -------------------------
488 int nLines = m_pTextCtrl->GetNumberOfLines();
489 for ( int nLine = 0; bOk && nLine < nLines; nLine++ ) {
490 bOk = file.Write(m_pTextCtrl->GetLineText(nLine) +
491 // we're not going to pull in the whole wxTextFile if all we need is this...
492 #if wxUSE_TEXTFILE
493 wxTextFile::GetEOL()
494 #else // !wxUSE_TEXTFILE
495 '\n'
496 #endif // wxUSE_TEXTFILE
497 );
498 }
499
500 if ( bOk )
501 bOk = file.Close();
502
503 if ( !bOk ) {
504 wxLogError(_("Can't save log contents to file."));
505 }
506 else {
507 wxLogStatus(this, _("Log saved to the file '%s'."), szFileName);
508 }
509 }
510 #endif // wxUSE_FILE
511
512 void wxLogFrame::OnClear(wxCommandEvent& WXUNUSED(event))
513 {
514 m_pTextCtrl->Clear();
515 }
516
517 wxLogFrame::~wxLogFrame()
518 {
519 m_log->OnFrameDelete(this);
520 }
521
522 // wxLogWindow
523 // -----------
524 wxLogWindow::wxLogWindow(wxFrame *pParent,
525 const wxChar *szTitle,
526 bool bShow,
527 bool bDoPass)
528 {
529 m_bPassMessages = bDoPass;
530
531 m_pLogFrame = new wxLogFrame(pParent, this, szTitle);
532 m_pOldLog = wxLog::SetActiveTarget(this);
533
534 if ( bShow )
535 m_pLogFrame->Show(TRUE);
536 }
537
538 void wxLogWindow::Show(bool bShow)
539 {
540 m_pLogFrame->Show(bShow);
541 }
542
543 void wxLogWindow::Flush()
544 {
545 if ( m_pOldLog != NULL )
546 m_pOldLog->Flush();
547
548 m_bHasMessages = FALSE;
549 }
550
551 void wxLogWindow::DoLog(wxLogLevel level, const wxChar *szString, time_t t)
552 {
553 // first let the previous logger show it
554 if ( m_pOldLog != NULL && m_bPassMessages ) {
555 // bogus cast just to access protected DoLog
556 ((wxLogWindow *)m_pOldLog)->DoLog(level, szString, t);
557 }
558
559 if ( m_pLogFrame ) {
560 switch ( level ) {
561 case wxLOG_Status:
562 // by default, these messages are ignored by wxLog, so process
563 // them ourselves
564 if ( !wxIsEmpty(szString) )
565 {
566 wxString str;
567 str << _("Status: ") << szString;
568 DoLogString(str, t);
569 }
570 break;
571
572 // don't put trace messages in the text window for 2 reasons:
573 // 1) there are too many of them
574 // 2) they may provoke other trace messages thus sending a program
575 // into an infinite loop
576 case wxLOG_Trace:
577 break;
578
579 default:
580 // and this will format it nicely and call our DoLogString()
581 wxLog::DoLog(level, szString, t);
582 }
583 }
584
585 m_bHasMessages = TRUE;
586 }
587
588 void wxLogWindow::DoLogString(const wxChar *szString, time_t WXUNUSED(t))
589 {
590 // put the text into our window
591 wxTextCtrl *pText = m_pLogFrame->TextCtrl();
592
593 // remove selection (WriteText is in fact ReplaceSelection)
594 #ifdef __WXMSW__
595 long nLen = pText->GetLastPosition();
596 pText->SetSelection(nLen, nLen);
597 #endif // Windows
598
599 wxString msg;
600 TimeStamp(&msg);
601 msg << szString << wxT('\n');
602
603 pText->AppendText(msg);
604
605 // TODO ensure that the line can be seen
606 }
607
608 wxFrame *wxLogWindow::GetFrame() const
609 {
610 return m_pLogFrame;
611 }
612
613 void wxLogWindow::OnFrameCreate(wxFrame * WXUNUSED(frame))
614 {
615 }
616
617 void wxLogWindow::OnFrameDelete(wxFrame * WXUNUSED(frame))
618 {
619 m_pLogFrame = (wxLogFrame *)NULL;
620 }
621
622 wxLogWindow::~wxLogWindow()
623 {
624 delete m_pOldLog;
625
626 // may be NULL if log frame already auto destroyed itself
627 delete m_pLogFrame;
628 }
629
630 // ----------------------------------------------------------------------------
631 // wxLogDialog
632 // ----------------------------------------------------------------------------
633
634 #if wxUSE_LOG_DIALOG
635
636 static const size_t MARGIN = 10;
637
638 wxLogDialog::wxLogDialog(wxWindow *parent,
639 const wxArrayString& messages,
640 const wxArrayInt& severity,
641 const wxArrayLong& times,
642 const wxString& caption,
643 long style)
644 : wxDialog(parent, -1, caption ),
645 m_messages(messages), m_severity(severity), m_times(times)
646 {
647 m_showingDetails = FALSE; // not initially
648 m_listctrl = (wxListCtrl *)NULL;
649
650 // create the controls which are always shown and layout them: we use
651 // sizers even though our window is not resizeable to calculate the size of
652 // the dialog properly
653 wxBoxSizer *sizerTop = new wxBoxSizer(wxVERTICAL);
654 wxBoxSizer *sizerButtons = new wxBoxSizer(wxVERTICAL);
655 wxBoxSizer *sizerAll = new wxBoxSizer(wxHORIZONTAL);
656
657 wxButton *btnOk = new wxButton(this, wxID_OK, _T("OK"));
658 sizerButtons->Add(btnOk, 0, wxCENTRE|wxBOTTOM, MARGIN/2);
659 m_btnDetails = new wxButton(this, wxID_MORE, _T("&Details >>"));
660 sizerButtons->Add(m_btnDetails, 0, wxCENTRE|wxTOP, MARGIN/2 - 1);
661
662 wxIcon icon = wxTheApp->GetStdIcon(style & wxICON_MASK);
663 sizerAll->Add(new wxStaticBitmap(this, -1, icon), 0, wxCENTRE);
664 const wxString& message = messages.Last();
665 sizerAll->Add(CreateTextSizer(message), 0, wxCENTRE|wxLEFT|wxRIGHT, MARGIN);
666 sizerAll->Add(sizerButtons, 0, wxALIGN_RIGHT|wxLEFT, MARGIN);
667
668 sizerTop->Add(sizerAll, 0, wxCENTRE|wxALL, MARGIN);
669
670 SetAutoLayout(TRUE);
671 SetSizer(sizerTop);
672
673 sizerTop->SetSizeHints(this);
674 sizerTop->Fit(this);
675
676 btnOk->SetFocus();
677
678 if ( m_messages.GetCount() == 1 )
679 {
680 // no details... it's easier to disable a button than to change the
681 // dialog layout depending on whether we have details or not
682 m_btnDetails->Disable();
683 }
684
685 Centre();
686 }
687
688 void wxLogDialog::OnOk(wxCommandEvent& WXUNUSED(event))
689 {
690 EndModal(wxID_OK);
691 }
692
693 void wxLogDialog::OnDetails(wxCommandEvent& WXUNUSED(event))
694 {
695 wxSizer *sizer = GetSizer();
696
697 if ( m_showingDetails )
698 {
699 m_btnDetails->SetLabel(_T("&Details >>"));
700
701 sizer->Remove(m_listctrl);
702 }
703 else // show details now
704 {
705 m_btnDetails->SetLabel(_T("<< &Details"));
706
707 if ( !m_listctrl )
708 {
709 // create it now
710 m_listctrl = new wxListCtrl(this, -1,
711 wxDefaultPosition, wxDefaultSize,
712 wxSUNKEN_BORDER |
713 wxLC_REPORT |
714 wxLC_NO_HEADER );
715 m_listctrl->InsertColumn(0, _("Message"));
716 m_listctrl->InsertColumn(1, _("Time"));
717
718 // prepare the imagelist
719 static const int ICON_SIZE = 16;
720 wxImageList *imageList = new wxImageList(ICON_SIZE, ICON_SIZE);
721
722 // order should be the same as in the switch below!
723 static const int icons[] =
724 {
725 wxICON_ERROR,
726 wxICON_EXCLAMATION,
727 wxICON_INFORMATION
728 };
729
730 for ( size_t icon = 0; icon < WXSIZEOF(icons); icon++ )
731 {
732 wxBitmap bmp = wxTheApp->GetStdIcon(icons[icon]);
733 imageList->Add(wxImage(bmp).
734 Rescale(ICON_SIZE, ICON_SIZE).
735 ConvertToBitmap());
736 }
737
738 m_listctrl->SetImageList(imageList, wxIMAGE_LIST_SMALL);
739
740 // and fill it
741 wxString fmt = wxLog::GetTimestamp();
742 if ( !fmt )
743 {
744 // default format
745 fmt = _T("%X");
746 }
747
748 size_t count = m_messages.GetCount();
749 for ( size_t n = 0; n < count; n++ )
750 {
751 int image;
752 switch ( m_severity[n] )
753 {
754 case wxLOG_Error:
755 image = 0;
756 break;
757
758 case wxLOG_Warning:
759 image = 1;
760 break;
761
762 default:
763 image = 2;
764 }
765
766 m_listctrl->InsertItem(n, m_messages[n], image);
767 m_listctrl->SetItem(n, 1,
768 wxDateTime((time_t)m_times[n]).Format(fmt));
769 }
770
771 // let the columns size themselves
772 m_listctrl->SetColumnWidth(0, wxLIST_AUTOSIZE);
773 m_listctrl->SetColumnWidth(1, wxLIST_AUTOSIZE);
774
775 // get the approx height of the listctrl
776 wxFont font = GetFont();
777 if ( !font.Ok() )
778 font = *wxSWISS_FONT;
779
780 int y;
781 GetTextExtent(_T("H"), (int*)NULL, &y, (int*)NULL, (int*)NULL, &font);
782 int height = wxMin(y*(count + 3), 100);
783 m_listctrl->SetSize(-1, height);
784 }
785
786 sizer->Add(m_listctrl, 1, wxEXPAND|(wxALL & ~wxTOP), MARGIN);
787 }
788
789 m_showingDetails = !m_showingDetails;
790
791 // in any case, our size changed - update
792 sizer->SetSizeHints(this);
793 sizer->Fit(this);
794 }
795
796 wxLogDialog::~wxLogDialog()
797 {
798 if ( m_listctrl )
799 {
800 delete m_listctrl->GetImageList(wxIMAGE_LIST_SMALL);
801 }
802 }
803
804 #endif // wxUSE_LOG_DIALOG