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