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