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