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