]> git.saurik.com Git - wxWidgets.git/blob - src/common/log.cpp
New virtual key defines (NUMPAD_XXX).
[wxWidgets.git] / src / common / log.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        log.cpp
3 // Purpose:     Assorted wxLogXXX functions, and wxLog (sink for logs)
4 // Author:      Vadim Zeitlin
5 // Modified by:
6 // Created:     29/01/98
7 // RCS-ID:      $Id$
8 // Copyright:   (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
9 // Licence:     wxWindows license
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19 #ifdef __GNUG__
20   #pragma implementation "log.h"
21 #endif
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 // wxWindows
31 #ifndef WX_PRECOMP
32   #include  <wx/event.h>
33   #include  <wx/app.h>
34   #include  <wx/string.h>
35   #include  <wx/intl.h>
36   #include  <wx/menu.h>
37   #include  <wx/frame.h>
38   #include  <wx/msgdlg.h>
39   #include  <wx/filedlg.h>
40   #include  <wx/textctrl.h>
41 #endif //WX_PRECOMP
42
43 #include  <wx/file.h>
44 #include  <wx/textfile.h>
45 #include  <wx/utils.h>
46 #include  <wx/log.h>
47
48 // other standard headers
49 #include  <errno.h>
50 #include  <stdlib.h>
51 #include  <time.h>
52
53 #ifdef  __WXMSW__
54   #include  <windows.h>
55   // Redefines OutputDebugString if necessary
56   #include  "wx/msw/private.h"
57 #else   //Unix
58   #include  <signal.h>
59 #endif  //Win/Unix
60
61 // ----------------------------------------------------------------------------
62 // non member functions
63 // ----------------------------------------------------------------------------
64
65 // define this to enable wrapping of log messages
66 //#define LOG_PRETTY_WRAP
67
68 #ifdef  LOG_PRETTY_WRAP
69   static void wxLogWrap(FILE *f, const char *pszPrefix, const char *psz);
70 #endif
71
72 // ----------------------------------------------------------------------------
73 // global variables
74 // ----------------------------------------------------------------------------
75
76 // we use a global variable to store the frame pointer for wxLogStatus - bad,
77 // but it's he easiest way
78 static wxFrame *gs_pFrame; // FIXME MT-unsafe
79
80 // ============================================================================
81 // implementation
82 // ============================================================================
83
84 // ----------------------------------------------------------------------------
85 // implementation of Log functions
86 //
87 // NB: unfortunately we need all these distinct functions, we can't make them
88 //     macros and not all compilers inline vararg functions.
89 // ----------------------------------------------------------------------------
90
91 // log functions can't allocate memory (LogError("out of memory...") should
92 // work!), so we use a static buffer for all log messages
93 #define LOG_BUFFER_SIZE   (4096)
94
95 // static buffer for error messages (FIXME MT-unsafe)
96 static wxChar s_szBuf[LOG_BUFFER_SIZE];
97
98 // generic log function
99 void wxLogGeneric(wxLogLevel level, const wxChar *szFormat, ...)
100 {
101   if ( wxLog::GetActiveTarget() != NULL ) {
102     va_list argptr;
103     va_start(argptr, szFormat);
104     wxVsprintf(s_szBuf, szFormat, argptr);
105     va_end(argptr);
106
107     wxLog::OnLog(level, s_szBuf, time(NULL));
108   }
109 }
110
111 #define IMPLEMENT_LOG_FUNCTION(level)                             \
112   void wxLog##level(const wxChar *szFormat, ...)                  \
113   {                                                               \
114     if ( wxLog::GetActiveTarget() != NULL ) {                     \
115       va_list argptr;                                             \
116       va_start(argptr, szFormat);                                 \
117       wxVsprintf(s_szBuf, szFormat, argptr);                      \
118       va_end(argptr);                                             \
119                                                                   \
120       wxLog::OnLog(wxLOG_##level, s_szBuf, time(NULL));           \
121     }                                                             \
122   }
123
124 IMPLEMENT_LOG_FUNCTION(FatalError)
125 IMPLEMENT_LOG_FUNCTION(Error)
126 IMPLEMENT_LOG_FUNCTION(Warning)
127 IMPLEMENT_LOG_FUNCTION(Message)
128 IMPLEMENT_LOG_FUNCTION(Info)
129 IMPLEMENT_LOG_FUNCTION(Status)
130
131 // accepts an additional argument which tells to which frame the output should
132 // be directed
133 void wxLogStatus(wxFrame *pFrame, const wxChar *szFormat, ...)
134 {
135   wxLog *pLog = wxLog::GetActiveTarget();
136   if ( pLog != NULL ) {
137     va_list argptr;
138     va_start(argptr, szFormat);
139     wxVsprintf(s_szBuf, szFormat, argptr);
140     va_end(argptr);
141
142     wxASSERT( gs_pFrame == NULL ); // should be reset!
143     gs_pFrame = pFrame;
144     wxLog::OnLog(wxLOG_Status, s_szBuf, time(NULL));
145     gs_pFrame = (wxFrame *) NULL;
146   }
147 }
148
149 // same as info, but only if 'verbose' mode is on
150 void wxLogVerbose(const wxChar *szFormat, ...)
151 {
152   wxLog *pLog = wxLog::GetActiveTarget();
153   if ( pLog != NULL && pLog->GetVerbose() ) {
154     va_list argptr;
155     va_start(argptr, szFormat);
156     wxVsprintf(s_szBuf, szFormat, argptr);
157     va_end(argptr);
158
159     wxLog::OnLog(wxLOG_Info, s_szBuf, time(NULL));
160   }
161 }
162
163 // debug functions
164 #ifdef __WXDEBUG__
165 #define IMPLEMENT_LOG_DEBUG_FUNCTION(level)                       \
166   void wxLog##level(const wxChar *szFormat, ...)                  \
167   {                                                               \
168     if ( wxLog::GetActiveTarget() != NULL ) {                     \
169       va_list argptr;                                             \
170       va_start(argptr, szFormat);                                 \
171       wxVsprintf(s_szBuf, szFormat, argptr);                      \
172       va_end(argptr);                                             \
173                                                                   \
174       wxLog::OnLog(wxLOG_##level, s_szBuf, time(NULL));           \
175     }                                                             \
176   }
177
178   void wxLogTrace(const wxChar *mask, const wxChar *szFormat, ...)
179   {
180     wxLog *pLog = wxLog::GetActiveTarget();
181
182     if ( pLog != NULL && wxLog::IsAllowedTraceMask(mask) ) {
183       va_list argptr;
184       va_start(argptr, szFormat);
185       wxVsprintf(s_szBuf, szFormat, argptr);
186       va_end(argptr);
187
188       wxLog::OnLog(wxLOG_Trace, s_szBuf, time(NULL));
189     }
190   }
191
192   void wxLogTrace(wxTraceMask mask, const wxChar *szFormat, ...)
193   {
194     wxLog *pLog = wxLog::GetActiveTarget();
195
196     // we check that all of mask bits are set in the current mask, so
197     // that wxLogTrace(wxTraceRefCount | wxTraceOle) will only do something
198     // if both bits are set.
199     if ( pLog != NULL && ((pLog->GetTraceMask() & mask) == mask) ) {
200       va_list argptr;
201       va_start(argptr, szFormat);
202       wxVsprintf(s_szBuf, szFormat, argptr);
203       va_end(argptr);
204
205       wxLog::OnLog(wxLOG_Trace, s_szBuf, time(NULL));
206     }
207   }
208
209 #else // release
210   #define IMPLEMENT_LOG_DEBUG_FUNCTION(level)
211 #endif
212
213 IMPLEMENT_LOG_DEBUG_FUNCTION(Debug)
214 IMPLEMENT_LOG_DEBUG_FUNCTION(Trace)
215
216 // wxLogSysError: one uses the last error code, for other  you must give it
217 // explicitly
218
219 // common part of both wxLogSysError
220 void wxLogSysErrorHelper(long lErrCode)
221 {
222     wxChar szErrMsg[LOG_BUFFER_SIZE / 2];
223     wxSprintf(szErrMsg, _(" (error %ld: %s)"), lErrCode, wxSysErrorMsg(lErrCode));
224     wxStrncat(s_szBuf, szErrMsg, WXSIZEOF(s_szBuf) - wxStrlen(s_szBuf));
225
226     wxLog::OnLog(wxLOG_Error, s_szBuf, time(NULL));
227 }
228
229 void WXDLLEXPORT wxLogSysError(const wxChar *szFormat, ...)
230 {
231     va_list argptr;
232     va_start(argptr, szFormat);
233     wxVsprintf(s_szBuf, szFormat, argptr);
234     va_end(argptr);
235
236     wxLogSysErrorHelper(wxSysErrorCode());
237 }
238
239 void WXDLLEXPORT wxLogSysError(long lErrCode, const wxChar *szFormat, ...)
240 {
241     va_list argptr;
242     va_start(argptr, szFormat);
243     wxVsprintf(s_szBuf, szFormat, argptr);
244     va_end(argptr);
245
246     wxLogSysErrorHelper(lErrCode);
247 }
248
249 // ----------------------------------------------------------------------------
250 // wxLog class implementation
251 // ----------------------------------------------------------------------------
252
253 wxLog::wxLog()
254 {
255     m_bHasMessages = FALSE;
256
257     // enable verbose messages by default in the debug builds
258 #ifdef __WXDEBUG__
259     m_bVerbose = TRUE;
260 #else // release
261     m_bVerbose = FALSE;
262 #endif // debug/release
263 }
264
265 wxLog *wxLog::GetActiveTarget()
266 {
267     if ( ms_bAutoCreate && ms_pLogger == NULL ) {
268         // prevent infinite recursion if someone calls wxLogXXX() from
269         // wxApp::CreateLogTarget()
270         static bool s_bInGetActiveTarget = FALSE;
271         if ( !s_bInGetActiveTarget ) {
272             s_bInGetActiveTarget = TRUE;
273
274 #ifdef  wxUSE_NOGUI
275             ms_pLogger = new wxLogStderr;
276 #else
277             // ask the application to create a log target for us
278             if ( wxTheApp != NULL )
279                 ms_pLogger = wxTheApp->CreateLogTarget();
280             else
281                 ms_pLogger = new wxLogStderr;
282 #endif
283
284             s_bInGetActiveTarget = FALSE;
285
286             // do nothing if it fails - what can we do?
287         }
288     }
289
290     return ms_pLogger;
291 }
292
293 wxLog *wxLog::SetActiveTarget(wxLog *pLogger)
294 {
295     if ( ms_pLogger != NULL ) {
296         // flush the old messages before changing because otherwise they might
297         // get lost later if this target is not restored
298         ms_pLogger->Flush();
299     }
300
301     wxLog *pOldLogger = ms_pLogger;
302     ms_pLogger = pLogger;
303
304     return pOldLogger;
305 }
306
307 void wxLog::RemoveTraceMask(const wxString& str)
308 {
309     int index = ms_aTraceMasks.Index(str);
310     if ( index != wxNOT_FOUND )
311         ms_aTraceMasks.Remove((size_t)index);
312 }
313
314 void wxLog::DoLog(wxLogLevel level, const wxChar *szString, time_t t)
315 {
316     switch ( level ) {
317         case wxLOG_FatalError:
318             DoLogString(wxString(_("Fatal error: ")) + szString, t);
319             DoLogString(_("Program aborted."), t);
320             Flush();
321             abort();
322             break;
323
324         case wxLOG_Error:
325             DoLogString(wxString(_("Error: ")) + szString, t);
326             break;
327
328         case wxLOG_Warning:
329             DoLogString(wxString(_("Warning: ")) + szString, t);
330             break;
331
332         case wxLOG_Info:
333             if ( GetVerbose() )
334         case wxLOG_Message:
335         default:    // log unknown log levels too
336                 DoLogString(szString, t);
337             // fall through
338
339         case wxLOG_Status:
340             // nothing to do
341             break;
342
343         case wxLOG_Trace:
344         case wxLOG_Debug:
345 #ifdef __WXDEBUG__
346             DoLogString(szString, t);
347 #endif
348             break;
349     }
350 }
351
352 void wxLog::DoLogString(const wxChar *WXUNUSED(szString), time_t WXUNUSED(t))
353 {
354     wxFAIL_MSG(_T("DoLogString must be overriden if it's called."));
355 }
356
357 void wxLog::Flush()
358 {
359     // do nothing
360 }
361
362 // ----------------------------------------------------------------------------
363 // wxLogStderr class implementation
364 // ----------------------------------------------------------------------------
365
366 wxLogStderr::wxLogStderr(FILE *fp)
367 {
368     if ( fp == NULL )
369         m_fp = stderr;
370     else
371         m_fp = fp;
372 }
373
374 void wxLogStderr::DoLogString(const wxChar *szString, time_t WXUNUSED(t))
375 {
376     wxString str(szString);
377     str << _T('\n');
378
379     fputs(str.mb_str(), m_fp);
380     fflush(m_fp);
381
382     // under Windows, programs usually don't have stderr at all, so make show the
383     // messages also under debugger
384 #ifdef __WXMSW__
385     OutputDebugString(str + _T('\r'));
386 #endif // MSW
387 }
388
389 // ----------------------------------------------------------------------------
390 // wxLogStream implementation
391 // ----------------------------------------------------------------------------
392
393 #if wxUSE_STD_IOSTREAM
394 wxLogStream::wxLogStream(ostream *ostr)
395 {
396     if ( ostr == NULL )
397         m_ostr = &cerr;
398     else
399         m_ostr = ostr;
400 }
401
402 void wxLogStream::DoLogString(const wxChar *szString, time_t WXUNUSED(t))
403 {
404     (*m_ostr) << wxConv_libc.cWX2MB(szString) << endl << flush;
405 }
406 #endif // wxUSE_STD_IOSTREAM
407
408 #ifndef wxUSE_NOGUI
409
410 // ----------------------------------------------------------------------------
411 // wxLogTextCtrl implementation
412 // ----------------------------------------------------------------------------
413
414 #if wxUSE_STD_IOSTREAM
415 wxLogTextCtrl::wxLogTextCtrl(wxTextCtrl *pTextCtrl)
416 #if !defined(NO_TEXT_WINDOW_STREAM)
417 : wxLogStream(new ostream(pTextCtrl))
418 #endif
419 {
420 }
421
422 wxLogTextCtrl::~wxLogTextCtrl()
423 {
424     delete m_ostr;
425 }
426 #endif // wxUSE_STD_IOSTREAM
427
428 // ----------------------------------------------------------------------------
429 // wxLogGui implementation (FIXME MT-unsafe)
430 // ----------------------------------------------------------------------------
431
432 wxLogGui::wxLogGui()
433 {
434     Clear();
435 }
436
437 void wxLogGui::Clear()
438 {
439     m_bErrors = m_bWarnings = FALSE;
440     m_aMessages.Empty();
441     m_aTimes.Empty();
442 }
443
444 void wxLogGui::Flush()
445 {
446     if ( !m_bHasMessages )
447         return;
448
449     // do it right now to block any new calls to Flush() while we're here
450     m_bHasMessages = FALSE;
451
452     // concatenate all strings (but not too many to not overfill the msg box)
453     wxString str;
454     size_t nLines = 0,
455     nMsgCount = m_aMessages.Count();
456
457     // start from the most recent message
458     for ( size_t n = nMsgCount; n > 0; n-- ) {
459         // for Windows strings longer than this value are wrapped (NT 4.0)
460         const size_t nMsgLineWidth = 156;
461
462         nLines += (m_aMessages[n - 1].Len() + nMsgLineWidth - 1) / nMsgLineWidth;
463
464         if ( nLines > 25 )  // don't put too many lines in message box
465             break;
466
467         str << m_aMessages[n - 1] << _T("\n");
468     }
469
470     const wxChar *title;
471     long style;
472
473     if ( m_bErrors ) {
474         title = _("Error");
475         style = wxICON_STOP;
476     }
477     else if ( m_bWarnings ) {
478         title = _("Warning");
479         style = wxICON_EXCLAMATION;
480     }
481     else {
482         title = _("Information");
483         style = wxICON_INFORMATION;
484     }
485
486     wxMessageBox(str, title, wxOK | style);
487
488     // no undisplayed messages whatsoever
489     Clear();
490 }
491
492 // the default behaviour is to discard all informational messages if there
493 // are any errors/warnings.
494 void wxLogGui::DoLog(wxLogLevel level, const wxChar *szString, time_t t)
495 {
496     switch ( level ) {
497         case wxLOG_Info:
498             if ( GetVerbose() )
499         case wxLOG_Message:
500                 if ( !m_bErrors ) {
501                     m_aMessages.Add(szString);
502                     m_aTimes.Add((long)t);
503                     m_bHasMessages = TRUE;
504                 }
505                 break;
506
507         case wxLOG_Status:
508                 {
509                     // find the top window and set it's status text if it has any
510                     wxFrame *pFrame = gs_pFrame;
511                     if ( pFrame == NULL ) {
512                         wxWindow *pWin = wxTheApp->GetTopWindow();
513                         if ( pWin != NULL && pWin->IsKindOf(CLASSINFO(wxFrame)) ) {
514                             pFrame = (wxFrame *)pWin;
515                         }
516                     }
517
518                     if ( pFrame != NULL )
519                         pFrame->SetStatusText(szString);
520                 }
521                 break;
522
523         case wxLOG_Trace:
524         case wxLOG_Debug:
525                 #ifdef __WXDEBUG__
526                 {
527                     #ifdef __WXMSW__
528                         // don't prepend debug/trace here: it goes to the
529                         // debug window anyhow, but do put a timestamp
530                         OutputDebugString(wxString(szString) + _T("\n\r"));
531                     #else
532                         // send them to stderr
533                         wxFprintf(stderr, _T("%s: %s\n"),
534                                 level == wxLOG_Trace ? _T("Trace") : _T("Debug"),
535                                 szString);
536                         fflush(stderr);
537                     #endif
538                 }
539                 #endif // __WXDEBUG__
540
541                 break;
542
543         case wxLOG_FatalError:
544                 // show this one immediately
545                 wxMessageBox(szString, _("Fatal error"), wxICON_HAND);
546                 break;
547
548         case wxLOG_Error:
549                 // discard earlier informational messages if this is the 1st
550                 // error because they might not make sense any more
551                 if ( !m_bErrors ) {
552                     m_aMessages.Empty();
553                     m_aTimes.Empty();
554                     m_bHasMessages = TRUE;
555                     m_bErrors = TRUE;
556                 }
557                 // fall through
558
559         case wxLOG_Warning:
560                 if ( !m_bErrors ) {
561                     // for the warning we don't discard the info messages
562                     m_bWarnings = TRUE;
563                 }
564
565                 m_aMessages.Add(szString);
566                 m_aTimes.Add((long)t);
567                 break;
568     }
569 }
570
571 // ----------------------------------------------------------------------------
572 // wxLogWindow and wxLogFrame implementation
573 // ----------------------------------------------------------------------------
574
575 // log frame class
576 // ---------------
577 class wxLogFrame : public wxFrame
578 {
579 public:
580     // ctor & dtor
581     wxLogFrame(wxFrame *pParent, wxLogWindow *log, const wxChar *szTitle);
582     virtual ~wxLogFrame();
583
584     // menu callbacks
585     void OnClose(wxCommandEvent& event);
586     void OnCloseWindow(wxCloseEvent& event);
587     void OnSave (wxCommandEvent& event);
588     void OnClear(wxCommandEvent& event);
589
590     void OnIdle(wxIdleEvent&);
591
592     // accessors
593     wxTextCtrl *TextCtrl() const { return m_pTextCtrl; }
594
595 private:
596     enum
597     {
598         Menu_Close = 100,
599         Menu_Save,
600         Menu_Clear
601     };
602
603     // instead of closing just hide the window to be able to Show() it later
604     void DoClose() { Show(FALSE); }
605
606     wxTextCtrl  *m_pTextCtrl;
607     wxLogWindow *m_log;
608
609     DECLARE_EVENT_TABLE()
610 };
611
612 BEGIN_EVENT_TABLE(wxLogFrame, wxFrame)
613     // wxLogWindow menu events
614     EVT_MENU(Menu_Close, wxLogFrame::OnClose)
615     EVT_MENU(Menu_Save,  wxLogFrame::OnSave)
616     EVT_MENU(Menu_Clear, wxLogFrame::OnClear)
617
618     EVT_CLOSE(wxLogFrame::OnCloseWindow)
619 END_EVENT_TABLE()
620
621 wxLogFrame::wxLogFrame(wxFrame *pParent, wxLogWindow *log, const wxChar *szTitle)
622           : wxFrame(pParent, -1, szTitle)
623 {
624     m_log = log;
625
626     m_pTextCtrl = new wxTextCtrl(this, -1, wxEmptyString, wxDefaultPosition,
627             wxDefaultSize,
628             wxTE_MULTILINE  |
629             wxHSCROLL       |
630             wxTE_READONLY);
631
632     // create menu
633     wxMenuBar *pMenuBar = new wxMenuBar;
634     wxMenu *pMenu = new wxMenu;
635     pMenu->Append(Menu_Save,  _("&Save..."), _("Save log contents to file"));
636     pMenu->Append(Menu_Clear, _("C&lear"), _("Clear the log contents"));
637     pMenu->AppendSeparator();
638     pMenu->Append(Menu_Close, _("&Close"), _("Close this window"));
639     pMenuBar->Append(pMenu, _("&Log"));
640     SetMenuBar(pMenuBar);
641
642     // status bar for menu prompts
643     CreateStatusBar();
644
645     m_log->OnFrameCreate(this);
646 }
647
648 void wxLogFrame::OnClose(wxCommandEvent& WXUNUSED(event))
649 {
650     DoClose();
651 }
652
653 void wxLogFrame::OnCloseWindow(wxCloseEvent& WXUNUSED(event))
654 {
655     DoClose();
656 }
657
658 void wxLogFrame::OnSave(wxCommandEvent& WXUNUSED(event))
659 {
660     // get the file name
661     // -----------------
662     const wxChar *szFileName = wxSaveFileSelector(_T("log"), _T("txt"), _T("log.txt"));
663     if ( szFileName == NULL ) {
664         // cancelled
665         return;
666     }
667
668     // open file
669     // ---------
670     wxFile file;
671     bool bOk = FALSE;
672     if ( wxFile::Exists(szFileName) ) {
673         bool bAppend = FALSE;
674         wxString strMsg;
675         strMsg.Printf(_("Append log to file '%s' "
676                     "(choosing [No] will overwrite it)?"), szFileName);
677         switch ( wxMessageBox(strMsg, _("Question"), wxYES_NO | wxCANCEL) ) {
678             case wxYES:
679                 bAppend = TRUE;
680                 break;
681
682             case wxNO:
683                 bAppend = FALSE;
684                 break;
685
686             case wxCANCEL:
687                 return;
688
689             default:
690                 wxFAIL_MSG(_("invalid message box return value"));
691         }
692
693         if ( bAppend ) {
694             bOk = file.Open(szFileName, wxFile::write_append);
695         }
696         else {
697             bOk = file.Create(szFileName, TRUE /* overwrite */);
698         }
699     }
700     else {
701         bOk = file.Create(szFileName);
702     }
703
704     // retrieve text and save it
705     // -------------------------
706     int nLines = m_pTextCtrl->GetNumberOfLines();
707     for ( int nLine = 0; bOk && nLine < nLines; nLine++ ) {
708         bOk = file.Write(m_pTextCtrl->GetLineText(nLine) +
709                 // we're not going to pull in the whole wxTextFile if all we need is this...
710 #if wxUSE_TEXTFILE
711                 wxTextFile::GetEOL()
712 #else // !wxUSE_TEXTFILE
713                 '\n'
714 #endif // wxUSE_TEXTFILE
715                         );
716     }
717
718     if ( bOk )
719         bOk = file.Close();
720
721     if ( !bOk ) {
722         wxLogError(_("Can't save log contents to file."));
723     }
724     else {
725         wxLogStatus(this, _("Log saved to the file '%s'."), szFileName);
726     }
727 }
728
729 void wxLogFrame::OnClear(wxCommandEvent& WXUNUSED(event))
730 {
731     m_pTextCtrl->Clear();
732 }
733
734 wxLogFrame::~wxLogFrame()
735 {
736     m_log->OnFrameDelete(this);
737 }
738
739 // wxLogWindow
740 // -----------
741 wxLogWindow::wxLogWindow(wxFrame *pParent,
742         const wxChar *szTitle,
743         bool bShow,
744         bool bDoPass)
745 {
746     m_bPassMessages = bDoPass;
747
748     m_pLogFrame = new wxLogFrame(pParent, this, szTitle);
749     m_pOldLog = wxLog::SetActiveTarget(this);
750
751     if ( bShow )
752         m_pLogFrame->Show(TRUE);
753 }
754
755 void wxLogWindow::Show(bool bShow)
756 {
757     m_pLogFrame->Show(bShow);
758 }
759
760 void wxLogWindow::Flush()
761 {
762     if ( m_pOldLog != NULL )
763         m_pOldLog->Flush();
764
765     m_bHasMessages = FALSE;
766 }
767
768 void wxLogWindow::DoLog(wxLogLevel level, const wxChar *szString, time_t t)
769 {
770     // first let the previous logger show it
771     if ( m_pOldLog != NULL && m_bPassMessages ) {
772         // FIXME why can't we access protected wxLog method from here (we derive
773         //       from wxLog)? gcc gives "DoLog is protected in this context", what
774         //       does this mean? Anyhow, the cast is harmless and let's us do what
775         //       we want.
776         ((wxLogWindow *)m_pOldLog)->DoLog(level, szString, t);
777     }
778
779     if ( m_pLogFrame ) {
780         switch ( level ) {
781             case wxLOG_Status:
782                 // by default, these messages are ignored by wxLog, so process
783                 // them ourselves
784                 if ( !wxIsEmpty(szString) )
785                 {
786                     wxString str;
787                     str << _("Status: ") << szString;
788                     DoLogString(str, t);
789                 }
790                 break;
791
792                 // don't put trace messages in the text window for 2 reasons:
793                 // 1) there are too many of them
794                 // 2) they may provoke other trace messages thus sending a program
795                 //    into an infinite loop
796             case wxLOG_Trace:
797                 break;
798
799             default:
800                 // and this will format it nicely and call our DoLogString()
801                 wxLog::DoLog(level, szString, t);
802         }
803     }
804
805     m_bHasMessages = TRUE;
806 }
807
808 void wxLogWindow::DoLogString(const wxChar *szString, time_t WXUNUSED(t))
809 {
810     // put the text into our window
811     wxTextCtrl *pText = m_pLogFrame->TextCtrl();
812
813     // remove selection (WriteText is in fact ReplaceSelection)
814 #ifdef __WXMSW__
815     long nLen = pText->GetLastPosition();
816     pText->SetSelection(nLen, nLen);
817 #endif // Windows
818
819     pText->WriteText(szString);
820     pText->WriteText(_T("\n")); // "\n" ok here (_not_ "\r\n")
821
822     // TODO ensure that the line can be seen
823 }
824
825 wxFrame *wxLogWindow::GetFrame() const
826 {
827     return m_pLogFrame;
828 }
829
830 void wxLogWindow::OnFrameCreate(wxFrame * WXUNUSED(frame))
831 {
832 }
833
834 void wxLogWindow::OnFrameDelete(wxFrame * WXUNUSED(frame))
835 {
836     m_pLogFrame = (wxLogFrame *)NULL;
837 }
838
839 wxLogWindow::~wxLogWindow()
840 {
841     delete m_pOldLog;
842
843     // may be NULL if log frame already auto destroyed itself
844     delete m_pLogFrame;
845 }
846
847 #endif  //wxUSE_NOGUI
848
849 // ============================================================================
850 // Global functions/variables
851 // ============================================================================
852
853 // ----------------------------------------------------------------------------
854 // static variables
855 // ----------------------------------------------------------------------------
856
857 wxLog          *wxLog::ms_pLogger      = (wxLog *)NULL;
858 bool            wxLog::ms_doLog        = TRUE;
859 bool            wxLog::ms_bAutoCreate  = TRUE;
860 wxTraceMask     wxLog::ms_ulTraceMask  = (wxTraceMask)0;
861 wxArrayString   wxLog::ms_aTraceMasks;
862
863 // ----------------------------------------------------------------------------
864 // stdout error logging helper
865 // ----------------------------------------------------------------------------
866
867 // helper function: wraps the message and justifies it under given position
868 // (looks more pretty on the terminal). Also adds newline at the end.
869 //
870 // TODO this is now disabled until I find a portable way of determining the
871 //      terminal window size (ok, I found it but does anybody really cares?)
872 #ifdef LOG_PRETTY_WRAP
873 static void wxLogWrap(FILE *f, const char *pszPrefix, const char *psz)
874 {
875     size_t nMax = 80; // FIXME
876     size_t nStart = strlen(pszPrefix);
877     fputs(pszPrefix, f);
878
879     size_t n;
880     while ( *psz != '\0' ) {
881         for ( n = nStart; (n < nMax) && (*psz != '\0'); n++ )
882             putc(*psz++, f);
883
884         // wrapped?
885         if ( *psz != '\0' ) {
886             /*putc('\n', f);*/
887             for ( n = 0; n < nStart; n++ )
888                 putc(' ', f);
889
890             // as we wrapped, squeeze all white space
891             while ( isspace(*psz) )
892                 psz++;
893         }
894     }
895
896     putc('\n', f);
897 }
898 #endif  //LOG_PRETTY_WRAP
899
900 // ----------------------------------------------------------------------------
901 // error code/error message retrieval functions
902 // ----------------------------------------------------------------------------
903
904 // get error code from syste
905 unsigned long wxSysErrorCode()
906 {
907 #ifdef  __WXMSW__
908 #ifdef  __WIN32__
909     return ::GetLastError();
910 #else   //WIN16
911     // TODO what to do on Windows 3.1?
912     return 0;
913 #endif  //WIN16/32
914 #else   //Unix
915     return errno;
916 #endif  //Win/Unix
917 }
918
919 // get error message from system
920 const wxChar *wxSysErrorMsg(unsigned long nErrCode)
921 {
922     if ( nErrCode == 0 )
923         nErrCode = wxSysErrorCode();
924
925 #ifdef  __WXMSW__
926 #ifdef  __WIN32__
927     static wxChar s_szBuf[LOG_BUFFER_SIZE / 2];
928
929     // get error message from system
930     LPVOID lpMsgBuf;
931     FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
932             NULL, nErrCode,
933             MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
934             (LPTSTR)&lpMsgBuf,
935             0, NULL);
936
937     // copy it to our buffer and free memory
938     wxStrncpy(s_szBuf, (const wxChar *)lpMsgBuf, WXSIZEOF(s_szBuf) - 1);
939     s_szBuf[WXSIZEOF(s_szBuf) - 1] = _T('\0');
940     LocalFree(lpMsgBuf);
941
942     // returned string is capitalized and ended with '\r\n' - bad
943     s_szBuf[0] = (wxChar)wxTolower(s_szBuf[0]);
944     size_t len = wxStrlen(s_szBuf);
945     if ( len > 0 ) {
946         // truncate string
947         if ( s_szBuf[len - 2] == _T('\r') )
948             s_szBuf[len - 2] = _T('\0');
949     }
950
951     return s_szBuf;
952 #else   //Win16
953     // TODO
954     return NULL;
955 #endif // Win16/32
956 #else   // Unix
957 #if wxUSE_UNICODE
958     static wxChar s_szBuf[LOG_BUFFER_SIZE / 2];
959     wxConv_libc.MB2WC(s_szBuf, strerror(nErrCode), WXSIZEOF(s_szBuf) -1);
960     return s_szBuf;
961 #else
962     return strerror(nErrCode);
963 #endif
964 #endif  // Win/Unix
965 }
966
967 // ----------------------------------------------------------------------------
968 // debug helper
969 // ----------------------------------------------------------------------------
970
971 #ifdef  __WXDEBUG__
972
973 // break into the debugger
974 void Trap()
975 {
976 #ifdef __WXMSW__
977     DebugBreak();
978 #elif defined(__WXMAC__)
979 #if __powerc
980     Debugger();
981 #else
982     SysBreak();
983 #endif
984 #elif defined(__UNIX__)
985     raise(SIGTRAP);
986 #else
987     // TODO
988 #endif // Win/Unix
989 }
990
991 // this function is called when an assert fails
992 void wxOnAssert(const wxChar *szFile, int nLine, const wxChar *szMsg)
993 {
994     // this variable can be set to true to suppress "assert failure" messages
995     static bool s_bNoAsserts = FALSE;
996     static bool s_bInAssert = FALSE;        // FIXME MT-unsafe
997
998     if ( s_bInAssert ) {
999         // He-e-e-e-elp!! we're trapped in endless loop
1000         Trap();
1001
1002         s_bInAssert = FALSE;
1003
1004         return;
1005     }
1006
1007     s_bInAssert = TRUE;
1008
1009     wxChar szBuf[LOG_BUFFER_SIZE];
1010
1011     // make life easier for people using VC++ IDE: clicking on the message
1012     // will take us immediately to the place of the failed assert
1013 #ifdef __VISUALC__
1014     wxSprintf(szBuf, _T("%s(%d): assert failed"), szFile, nLine);
1015 #else  // !VC++
1016     // make the error message more clear for all the others
1017     wxSprintf(szBuf, _T("Assert failed in file %s at line %d"), szFile, nLine);
1018 #endif // VC/!VC
1019
1020     if ( szMsg != NULL ) {
1021         wxStrcat(szBuf, _T(": "));
1022         wxStrcat(szBuf, szMsg);
1023     }
1024     else {
1025         wxStrcat(szBuf, _T("."));
1026     }
1027
1028     if ( !s_bNoAsserts ) {
1029         // send it to the normal log destination
1030         wxLogDebug(szBuf);
1031
1032 #if wxUSE_NOGUI
1033         Trap();
1034 #else
1035         // this message is intentionally not translated - it is for
1036         // developpers only
1037         wxStrcat(szBuf, _T("\nDo you want to stop the program?"
1038                 "\nYou can also choose [Cancel] to suppress "
1039                 "further warnings."));
1040
1041         switch ( wxMessageBox(szBuf, _("Debug"),
1042                     wxYES_NO | wxCANCEL | wxICON_STOP ) ) {
1043             case wxYES:
1044                 Trap();
1045                 break;
1046
1047             case wxCANCEL:
1048                 s_bNoAsserts = TRUE;
1049                 break;
1050
1051                 //case wxNO: nothing to do
1052         }
1053 #endif // USE_NOGUI
1054     }
1055
1056     s_bInAssert = FALSE;
1057 }
1058
1059 #endif  //WXDEBUG
1060