]> git.saurik.com Git - wxWidgets.git/blob - src/generic/logg.cpp
build fix
[wxWidgets.git] / src / generic / logg.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/generic/logg.cpp
3 // Purpose: wxLog-derived classes which need GUI support (the rest is in
4 // src/common/log.cpp)
5 // Author: Vadim Zeitlin
6 // Modified by:
7 // Created: 20.09.99 (extracted from src/common/log.cpp)
8 // RCS-ID: $Id$
9 // Copyright: (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
10 // Licence: wxWindows licence
11 /////////////////////////////////////////////////////////////////////////////
12
13 // ============================================================================
14 // declarations
15 // ============================================================================
16
17 // ----------------------------------------------------------------------------
18 // headers
19 // ----------------------------------------------------------------------------
20
21 // For compilers that support precompilation, includes "wx.h".
22 #include "wx/wxprec.h"
23
24 #ifdef __BORLANDC__
25 #pragma hdrstop
26 #endif
27
28 #ifndef WX_PRECOMP
29 #include "wx/app.h"
30 #include "wx/button.h"
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"
36 #include "wx/msgdlg.h"
37 #include "wx/textctrl.h"
38 #include "wx/sizer.h"
39 #include "wx/statbmp.h"
40 #include "wx/settings.h"
41 #endif // WX_PRECOMP
42
43 #if wxUSE_LOGGUI || wxUSE_LOGWINDOW
44
45 #include "wx/file.h"
46 #include "wx/textfile.h"
47 #include "wx/statline.h"
48 #include "wx/artprov.h"
49
50 #ifdef __WXMSW__
51 // for OutputDebugString()
52 #include "wx/msw/private.h"
53 #endif // Windows
54
55 #ifdef __WXPM__
56 #include <time.h>
57 #endif
58
59 #if wxUSE_LOG_DIALOG
60 #include "wx/listctrl.h"
61 #include "wx/imaglist.h"
62 #include "wx/image.h"
63 #else // !wxUSE_LOG_DIALOG
64 #include "wx/msgdlg.h"
65 #endif // wxUSE_LOG_DIALOG/!wxUSE_LOG_DIALOG
66
67 #if defined(__MWERKS__) && wxUSE_UNICODE
68 #include <wtime.h>
69 #endif
70
71 #include "wx/datetime.h"
72
73 // the suffix we add to the button to show that the dialog can be expanded
74 #define EXPAND_SUFFIX _T(" >>")
75
76 // ----------------------------------------------------------------------------
77 // private classes
78 // ----------------------------------------------------------------------------
79
80 #if wxUSE_LOG_DIALOG
81
82 // this function is a wrapper around strftime(3)
83 // allows to exclude the usage of wxDateTime
84 static 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
96 class wxLogDialog : public wxDialog
97 {
98 public:
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);
110 #if wxUSE_FILE
111 void OnSave(wxCommandEvent& event);
112 #endif // wxUSE_FILE
113 void OnListSelect(wxListEvent& event);
114
115 private:
116 // create controls needed for the details display
117 void CreateDetailsControls();
118
119 // the data for the listctrl
120 wxArrayString m_messages;
121 wxArrayInt m_severity;
122 wxArrayLong m_times;
123
124 // the "toggle" button and its state
125 #ifndef __SMARTPHONE__
126 wxButton *m_btnDetails;
127 #endif
128 bool m_showingDetails;
129
130 // the controls which are not shown initially (but only when details
131 // button is pressed)
132 wxListCtrl *m_listctrl;
133 #ifndef __SMARTPHONE__
134 #if wxUSE_STATLINE
135 wxStaticLine *m_statline;
136 #endif // wxUSE_STATLINE
137 #if wxUSE_FILE
138 wxButton *m_btnSave;
139 #endif // wxUSE_FILE
140 #endif // __SMARTPHONE__
141
142 // the translated "Details" string
143 static wxString ms_details;
144
145 DECLARE_EVENT_TABLE()
146 DECLARE_NO_COPY_CLASS(wxLogDialog)
147 };
148
149 BEGIN_EVENT_TABLE(wxLogDialog, wxDialog)
150 EVT_BUTTON(wxID_OK, wxLogDialog::OnOk)
151 EVT_BUTTON(wxID_MORE, wxLogDialog::OnDetails)
152 #if wxUSE_FILE
153 EVT_BUTTON(wxID_SAVE, wxLogDialog::OnSave)
154 #endif // wxUSE_FILE
155 EVT_LIST_ITEM_SELECTED(wxID_ANY, wxLogDialog::OnListSelect)
156 END_EVENT_TABLE()
157
158 #endif // wxUSE_LOG_DIALOG
159
160 // ----------------------------------------------------------------------------
161 // private functions
162 // ----------------------------------------------------------------------------
163
164 #if wxUSE_FILE && wxUSE_FILEDLG
165
166 // pass an uninitialized file object, the function will ask the user for the
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
169 // dialog was cancelled
170 static int OpenLogFile(wxFile& file, wxString *filename = NULL, wxWindow *parent = NULL);
171
172 #endif // wxUSE_FILE
173
174 // ----------------------------------------------------------------------------
175 // global variables
176 // ----------------------------------------------------------------------------
177
178 // we use a global variable to store the frame pointer for wxLogStatus - bad,
179 // but it's the easiest way
180 static wxFrame *gs_pFrame = NULL; // FIXME MT-unsafe
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
192 void wxVLogStatus(wxFrame *pFrame, const wxChar *szFormat, va_list argptr)
193 {
194 wxString msg;
195
196 wxLog *pLog = wxLog::GetActiveTarget();
197 if ( pLog != NULL ) {
198 msg.PrintfV(szFormat, argptr);
199
200 wxASSERT( gs_pFrame == NULL ); // should be reset!
201 gs_pFrame = pFrame;
202 #ifdef __WXWINCE__
203 wxLog::OnLog(wxLOG_Status, msg, 0);
204 #else
205 wxLog::OnLog(wxLOG_Status, msg, time(NULL));
206 #endif
207 gs_pFrame = (wxFrame *) NULL;
208 }
209 }
210
211 void wxLogStatus(wxFrame *pFrame, const wxChar *szFormat, ...)
212 {
213 va_list argptr;
214 va_start(argptr, szFormat);
215 wxVLogStatus(pFrame, szFormat, argptr);
216 va_end(argptr);
217 }
218
219 // ----------------------------------------------------------------------------
220 // wxLogGui implementation (FIXME MT-unsafe)
221 // ----------------------------------------------------------------------------
222
223 #if wxUSE_LOGGUI
224
225 wxLogGui::wxLogGui()
226 {
227 Clear();
228 }
229
230 void wxLogGui::Clear()
231 {
232 m_bErrors =
233 m_bWarnings =
234 m_bHasMessages = false;
235
236 m_aMessages.Empty();
237 m_aSeverity.Empty();
238 m_aTimes.Empty();
239 }
240
241 void 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
247 m_bHasMessages = false;
248
249 wxString appName = wxTheApp->GetAppName();
250 if ( !appName.empty() )
251 appName[0u] = (wxChar)wxToupper(appName[0u]);
252
253 long style;
254 wxString titleFormat;
255 if ( m_bErrors ) {
256 titleFormat = _("%s Error");
257 style = wxICON_STOP;
258 }
259 else if ( m_bWarnings ) {
260 titleFormat = _("%s Warning");
261 style = wxICON_EXCLAMATION;
262 }
263 else {
264 titleFormat = _("%s Information");
265 style = wxICON_INFORMATION;
266 }
267
268 wxString title;
269 title.Printf(titleFormat, appName.c_str());
270
271 size_t nMsgCount = m_aMessages.Count();
272
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
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
285
286 wxLogDialog dlg(NULL,
287 m_aMessages, m_aSeverity, m_aTimes,
288 title, style);
289
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();
295 #else // !wxUSE_LOG_DIALOG
296 // concatenate all strings (but not too many to not overfill the msg box)
297 size_t nLines = 0;
298
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;
303
304 nLines += (m_aMessages[n - 1].Len() + nMsgLineWidth - 1) / nMsgLineWidth;
305
306 if ( nLines > 25 ) // don't put too many lines in message box
307 break;
308
309 str << m_aMessages[n - 1] << wxT("\n");
310 }
311 #endif // wxUSE_LOG_DIALOG/!wxUSE_LOG_DIALOG
312 }
313
314 // this catches both cases of 1 message with wxUSE_LOG_DIALOG and any
315 // situation without it
316 if ( !str.empty() )
317 {
318 wxMessageBox(str, title, wxOK | style);
319
320 // no undisplayed messages whatsoever
321 Clear();
322 }
323
324 // allow flushing the logs again
325 Resume();
326 }
327
328 // log all kinds of messages
329 void 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:
335 {
336 m_aMessages.Add(szString);
337 m_aSeverity.Add(wxLOG_Message);
338 m_aTimes.Add((long)t);
339 m_bHasMessages = true;
340 }
341 break;
342
343 case wxLOG_Status:
344 #if wxUSE_STATUSBAR
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;
352 }
353 }
354
355 if ( pFrame && pFrame->GetStatusBar() )
356 pFrame->SetStatusText(szString);
357 }
358 #endif // wxUSE_STATUSBAR
359 break;
360
361 case wxLOG_Trace:
362 case wxLOG_Debug:
363 #ifdef __WXDEBUG__
364 {
365 wxString str;
366 TimeStamp(&str);
367 str += szString;
368
369 #if defined(__WXMSW__) && !defined(__WXMICROWIN__)
370 // don't prepend debug/trace here: it goes to the
371 // debug window anyhow
372 str += wxT("\r\n");
373 OutputDebugString(str);
374 #else
375 // send them to stderr
376 wxFprintf(stderr, wxT("[%s] %s\n"),
377 level == wxLOG_Trace ? wxT("Trace")
378 : wxT("Debug"),
379 str.c_str());
380 fflush(stderr);
381 #endif
382 }
383 #endif // __WXDEBUG__
384
385 break;
386
387 case wxLOG_FatalError:
388 // show this one immediately
389 wxMessageBox(szString, _("Fatal error"), wxICON_HAND);
390 wxExit();
391 break;
392
393 case wxLOG_Error:
394 if ( !m_bErrors ) {
395 #if !wxUSE_LOG_DIALOG
396 // discard earlier informational messages if this is the 1st
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
403 m_bErrors = true;
404 }
405 // fall through
406
407 case wxLOG_Warning:
408 if ( !m_bErrors ) {
409 // for the warning we don't discard the info messages
410 m_bWarnings = true;
411 }
412
413 m_aMessages.Add(szString);
414 m_aSeverity.Add((int)level);
415 m_aTimes.Add((long)t);
416 m_bHasMessages = true;
417 break;
418 }
419 }
420
421 #endif // wxUSE_LOGGUI
422
423 // ----------------------------------------------------------------------------
424 // wxLogWindow and wxLogFrame implementation
425 // ----------------------------------------------------------------------------
426
427 #if wxUSE_LOGWINDOW
428
429 // log frame class
430 // ---------------
431 class wxLogFrame : public wxFrame
432 {
433 public:
434 // ctor & dtor
435 wxLogFrame(wxWindow *pParent, wxLogWindow *log, const wxChar *szTitle);
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
446 // accessors
447 wxTextCtrl *TextCtrl() const { return m_pTextCtrl; }
448
449 private:
450 // use standard ids for our commands!
451 enum
452 {
453 Menu_Close = wxID_CLOSE,
454 Menu_Save = wxID_SAVE,
455 Menu_Clear = wxID_CLEAR
456 };
457
458 // common part of OnClose() and OnCloseWindow()
459 void DoClose();
460
461 wxTextCtrl *m_pTextCtrl;
462 wxLogWindow *m_log;
463
464 DECLARE_EVENT_TABLE()
465 DECLARE_NO_COPY_CLASS(wxLogFrame)
466 };
467
468 BEGIN_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)
477 END_EVENT_TABLE()
478
479 wxLogFrame::wxLogFrame(wxWindow *pParent, wxLogWindow *log, const wxChar *szTitle)
480 : wxFrame(pParent, wxID_ANY, szTitle)
481 {
482 m_log = log;
483
484 m_pTextCtrl = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition,
485 wxDefaultSize,
486 wxTE_MULTILINE |
487 wxHSCROLL |
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
493 wxTE_READONLY);
494
495 #if wxUSE_MENUS
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);
507 #endif // wxUSE_MENUS
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
517 void 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
523 Show(false);
524 }
525 }
526
527 void wxLogFrame::OnClose(wxCommandEvent& WXUNUSED(event))
528 {
529 DoClose();
530 }
531
532 void wxLogFrame::OnCloseWindow(wxCloseEvent& WXUNUSED(event))
533 {
534 DoClose();
535 }
536
537 #if wxUSE_FILE
538 void wxLogFrame::OnSave(wxCommandEvent& WXUNUSED(event))
539 {
540 #if wxUSE_FILEDLG
541 wxString filename;
542 wxFile file;
543 int rc = OpenLogFile(file, &filename, this);
544 if ( rc == -1 )
545 {
546 // cancelled
547 return;
548 }
549
550 bool bOk = rc != 0;
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) +
557 wxTextFile::GetEOL());
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 {
567 wxLogStatus(this, _("Log saved to the file '%s'."), filename.c_str());
568 }
569 #endif
570 }
571 #endif // wxUSE_FILE
572
573 void wxLogFrame::OnClear(wxCommandEvent& WXUNUSED(event))
574 {
575 m_pTextCtrl->Clear();
576 }
577
578 wxLogFrame::~wxLogFrame()
579 {
580 m_log->OnFrameDelete(this);
581 }
582
583 // wxLogWindow
584 // -----------
585
586 wxLogWindow::wxLogWindow(wxWindow *pParent,
587 const wxChar *szTitle,
588 bool bShow,
589 bool bDoPass)
590 {
591 PassMessages(bDoPass);
592
593 m_pLogFrame = new wxLogFrame(pParent, this, szTitle);
594
595 if ( bShow )
596 m_pLogFrame->Show();
597 }
598
599 void wxLogWindow::Show(bool bShow)
600 {
601 m_pLogFrame->Show(bShow);
602 }
603
604 void wxLogWindow::DoLog(wxLogLevel level, const wxChar *szString, time_t t)
605 {
606 // first let the previous logger show it
607 wxLogPassThrough::DoLog(level, szString, t);
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 }
634 }
635
636 void 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__
643 wxTextPos nLen = pText->GetLastPosition();
644 pText->SetSelection(nLen, nLen);
645 #endif // Windows
646
647 wxString msg;
648 TimeStamp(&msg);
649 msg << szString << wxT('\n');
650
651 pText->AppendText(msg);
652
653 // TODO ensure that the line can be seen
654 }
655
656 wxFrame *wxLogWindow::GetFrame() const
657 {
658 return m_pLogFrame;
659 }
660
661 void wxLogWindow::OnFrameCreate(wxFrame * WXUNUSED(frame))
662 {
663 }
664
665 bool wxLogWindow::OnFrameClose(wxFrame * WXUNUSED(frame))
666 {
667 // allow to close
668 return true;
669 }
670
671 void wxLogWindow::OnFrameDelete(wxFrame * WXUNUSED(frame))
672 {
673 m_pLogFrame = (wxLogFrame *)NULL;
674 }
675
676 wxLogWindow::~wxLogWindow()
677 {
678 // may be NULL if log frame already auto destroyed itself
679 delete m_pLogFrame;
680 }
681
682 #endif // wxUSE_LOGWINDOW
683
684 // ----------------------------------------------------------------------------
685 // wxLogDialog
686 // ----------------------------------------------------------------------------
687
688 #if wxUSE_LOG_DIALOG
689
690 #ifndef __SMARTPHONE__
691 static const size_t MARGIN = 10;
692 #else
693 static const size_t MARGIN = 0;
694 #endif
695
696 wxString wxLogDialog::ms_details;
697
698 wxLogDialog::wxLogDialog(wxWindow *parent,
699 const wxArrayString& messages,
700 const wxArrayInt& severity,
701 const wxArrayLong& times,
702 const wxString& caption,
703 long style)
704 : wxDialog(parent, wxID_ANY, caption,
705 wxDefaultPosition, wxDefaultSize,
706 wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
707 {
708 if ( ms_details.empty() )
709 {
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");
713 ms_details = wxGetTranslation(ms_details);
714 #ifdef __SMARTPHONE__
715 ms_details = wxStripMenuCodes(ms_details);
716 #endif
717 }
718
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];
727 msg.Replace(wxT("\n"), wxT(" "));
728 m_messages.Add(msg);
729 m_severity.Add(severity[n]);
730 m_times.Add(times[n]);
731 }
732
733 m_showingDetails = false; // not initially
734 m_listctrl = (wxListCtrl *)NULL;
735
736 #ifndef __SMARTPHONE__
737
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
746 #endif // __SMARTPHONE__
747
748 bool isPda = (wxSystemSettings::GetScreenType() <= wxSYS_SCREEN_PDA);
749
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);
754 #ifndef __SMARTPHONE__
755 wxBoxSizer *sizerButtons = new wxBoxSizer(isPda ? wxHORIZONTAL : wxVERTICAL);
756 #endif
757 wxBoxSizer *sizerAll = new wxBoxSizer(isPda ? wxVERTICAL : wxHORIZONTAL);
758
759 #ifdef __SMARTPHONE__
760 SetLeftMenu(wxID_OK);
761 SetRightMenu(wxID_MORE, ms_details + EXPAND_SUFFIX);
762 #else
763 wxButton *btnOk = new wxButton(this, wxID_OK);
764 sizerButtons->Add(btnOk, 0, isPda ? wxCENTRE : wxCENTRE|wxBOTTOM, MARGIN/2);
765 m_btnDetails = new wxButton(this, wxID_MORE, ms_details + EXPAND_SUFFIX);
766 sizerButtons->Add(m_btnDetails, 0, isPda ? wxCENTRE|wxLEFT : wxCENTRE | wxTOP, MARGIN/2 - 1);
767 #endif
768
769 wxBitmap bitmap;
770 switch ( style & wxICON_MASK )
771 {
772 case wxICON_ERROR:
773 bitmap = wxArtProvider::GetBitmap(wxART_ERROR, wxART_MESSAGE_BOX);
774 #ifdef __WXPM__
775 bitmap.SetId(wxICON_SMALL_ERROR);
776 #endif
777 break;
778
779 case wxICON_INFORMATION:
780 bitmap = wxArtProvider::GetBitmap(wxART_INFORMATION, wxART_MESSAGE_BOX);
781 #ifdef __WXPM__
782 bitmap.SetId(wxICON_SMALL_INFO);
783 #endif
784 break;
785
786 case wxICON_WARNING:
787 bitmap = wxArtProvider::GetBitmap(wxART_WARNING, wxART_MESSAGE_BOX);
788 #ifdef __WXPM__
789 bitmap.SetId(wxICON_SMALL_WARNING);
790 #endif
791 break;
792
793 default:
794 wxFAIL_MSG(_T("incorrect log style"));
795 }
796
797 if (!isPda)
798 sizerAll->Add(new wxStaticBitmap(this, wxID_ANY, bitmap), 0,
799 wxALIGN_CENTRE_VERTICAL);
800
801 const wxString& message = messages.Last();
802 sizerAll->Add(CreateTextSizer(message), 1,
803 wxALIGN_CENTRE_VERTICAL | wxLEFT | wxRIGHT, MARGIN);
804 #ifndef __SMARTPHONE__
805 sizerAll->Add(sizerButtons, 0, isPda ? wxCENTRE|wxTOP|wxBOTTOM : (wxALIGN_RIGHT | wxLEFT), MARGIN);
806 #endif
807
808 sizerTop->Add(sizerAll, 0, wxALL | wxEXPAND, MARGIN);
809
810 SetSizer(sizerTop);
811
812 // see comments in OnDetails()
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
822 wxSize size = sizerTop->Fit(this);
823 m_maxHeight = size.y;
824 SetSizeHints(size.x, size.y, m_maxWidth, m_maxHeight);
825
826 #ifndef __SMARTPHONE__
827 btnOk->SetFocus();
828 #endif
829
830 Centre();
831
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 }
838 }
839
840 void wxLogDialog::CreateDetailsControls()
841 {
842 #ifndef __SMARTPHONE__
843 // create the save button and separator line if possible
844 #if wxUSE_FILE
845 m_btnSave = new wxButton(this, wxID_SAVE);
846 #endif // wxUSE_FILE
847
848 #if wxUSE_STATLINE
849 m_statline = new wxStaticLine(this, wxID_ANY);
850 #endif // wxUSE_STATLINE
851
852 #endif // __SMARTPHONE__
853
854 // create the list ctrl now
855 m_listctrl = new wxListCtrl(this, wxID_ANY,
856 wxDefaultPosition, wxDefaultSize,
857 wxSUNKEN_BORDER |
858 wxLC_REPORT |
859 wxLC_NO_HEADER |
860 wxLC_SINGLE_SEL);
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();
865 #endif
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!
877 static const wxChar* icons[] =
878 {
879 wxART_ERROR,
880 wxART_WARNING,
881 wxART_INFORMATION
882 };
883
884 bool loadedIcons = true;
885
886 for ( size_t icon = 0; icon < WXSIZEOF(icons); icon++ )
887 {
888 wxBitmap bmp = wxArtProvider::GetBitmap(icons[icon], wxART_MESSAGE_BOX,
889 wxSize(ICON_SIZE, ICON_SIZE));
890
891 // This may very well fail if there are insufficient colours available.
892 // Degrade gracefully.
893 if ( !bmp.Ok() )
894 {
895 loadedIcons = false;
896
897 break;
898 }
899
900 imageList->Add(bmp);
901 }
902
903 m_listctrl->SetImageList(imageList, wxIMAGE_LIST_SMALL);
904
905 // and fill it
906 wxString fmt = wxLog::GetTimestamp();
907 if ( !fmt )
908 {
909 // default format
910 fmt = _T("%c");
911 }
912
913 size_t count = m_messages.GetCount();
914 for ( size_t n = 0; n < count; n++ )
915 {
916 int image;
917
918 if ( loadedIcons )
919 {
920 switch ( m_severity[n] )
921 {
922 case wxLOG_Error:
923 image = 0;
924 break;
925
926 case wxLOG_Warning:
927 image = 1;
928 break;
929
930 default:
931 image = 2;
932 }
933 }
934 else // failed to load images
935 {
936 image = -1;
937 }
938
939 m_listctrl->InsertItem(n, m_messages[n], image);
940 m_listctrl->SetItem(n, 1, TimeStamp(fmt, (time_t)m_times[n]));
941 }
942
943 // let the columns size themselves
944 m_listctrl->SetColumnWidth(0, wxLIST_AUTOSIZE);
945 m_listctrl->SetColumnWidth(1, wxLIST_AUTOSIZE);
946
947 // calculate an approximately nice height for the listctrl
948 int height = GetCharHeight()*(count + 4);
949
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();
956
957 // we should leave a margin
958 heightMax *= 9;
959 heightMax /= 10;
960
961 m_listctrl->SetSize(wxDefaultCoord, wxMin(height, heightMax));
962 }
963
964 void 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
969 m_listctrl->SetItemState(event.GetIndex(), 0, wxLIST_STATE_SELECTED);
970 }
971
972 void wxLogDialog::OnOk(wxCommandEvent& WXUNUSED(event))
973 {
974 EndModal(wxID_OK);
975 }
976
977 #if wxUSE_FILE
978
979 void wxLogDialog::OnSave(wxCommandEvent& WXUNUSED(event))
980 {
981 #if wxUSE_FILEDLG
982 wxFile file;
983 int rc = OpenLogFile(file, NULL, this);
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
996 fmt = _T("%c");
997 }
998
999 size_t count = m_messages.GetCount();
1000 for ( size_t n = 0; ok && (n < count); n++ )
1001 {
1002 wxString line;
1003 line << TimeStamp(fmt, (time_t)m_times[n])
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."));
1016 #endif // wxUSE_FILEDLG
1017 }
1018
1019 #endif // wxUSE_FILE
1020
1021 void wxLogDialog::OnDetails(wxCommandEvent& WXUNUSED(event))
1022 {
1023 wxSizer *sizer = GetSizer();
1024
1025 if ( m_showingDetails )
1026 {
1027 #ifdef __SMARTPHONE__
1028 SetRightMenu(wxID_MORE, ms_details + EXPAND_SUFFIX);
1029 #else
1030 m_btnDetails->SetLabel(ms_details + EXPAND_SUFFIX);
1031 #endif
1032
1033 sizer->Detach( m_listctrl );
1034
1035 #ifndef __SMARTPHONE__
1036
1037 #if wxUSE_STATLINE
1038 sizer->Detach( m_statline );
1039 #endif // wxUSE_STATLINE
1040
1041 #if wxUSE_FILE
1042 sizer->Detach( m_btnSave );
1043 #endif // wxUSE_FILE
1044
1045 #endif // __SMARTPHONE__
1046 }
1047 else // show details now
1048 {
1049 #ifdef __SMARTPHONE__
1050 SetRightMenu(wxID_MORE, wxString(_T("<< ")) + ms_details);
1051 #else
1052 m_btnDetails->SetLabel(wxString(_T("<< ")) + ms_details);
1053 #endif
1054
1055 if ( !m_listctrl )
1056 {
1057 CreateDetailsControls();
1058 }
1059
1060 #if wxUSE_STATLINE && !defined(__SMARTPHONE__)
1061 bool isPda = (wxSystemSettings::GetScreenType() <= wxSYS_SCREEN_PDA);
1062 if (!isPda)
1063 sizer->Add(m_statline, 0, wxEXPAND | (wxALL & ~wxTOP), MARGIN);
1064 #endif // wxUSE_STATLINE
1065
1066 sizer->Add(m_listctrl, 1, wxEXPAND | (wxALL & ~wxTOP), MARGIN);
1067
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
1077 #if wxUSE_FILE && !defined(__SMARTPHONE__)
1078 sizer->Add(m_btnSave, 0, wxALIGN_RIGHT | (wxALL & ~wxTOP), MARGIN);
1079 #endif // wxUSE_FILE
1080 }
1081
1082 m_showingDetails = !m_showingDetails;
1083
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
1090
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
1110 #ifdef __WXWINCE__
1111 if (m_showingDetails)
1112 m_listctrl->Show();
1113 #endif
1114
1115 // don't change the width when expanding/collapsing
1116 SetSize(wxDefaultCoord, size.y);
1117
1118 #ifdef __WXGTK__
1119 // VS: this is necessary in order to force frame redraw under
1120 // WindowMaker or fvwm2 (and probably other broken WMs).
1121 // Otherwise, detailed list wouldn't be displayed.
1122 Show();
1123 #endif // wxGTK
1124 }
1125
1126 wxLogDialog::~wxLogDialog()
1127 {
1128 if ( m_listctrl )
1129 {
1130 delete m_listctrl->GetImageList(wxIMAGE_LIST_SMALL);
1131 }
1132 }
1133
1134 #endif // wxUSE_LOG_DIALOG
1135
1136 #if wxUSE_FILE && wxUSE_FILEDLG
1137
1138 // pass an uninitialized file object, the function will ask the user for the
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
1141 // dialog was cancelled
1142 static int OpenLogFile(wxFile& file, wxString *pFilename, wxWindow *parent)
1143 {
1144 // get the file name
1145 // -----------------
1146 wxString filename = wxSaveFileSelector(wxT("log"), wxT("txt"), wxT("log.txt"), parent);
1147 if ( !filename ) {
1148 // cancelled
1149 return -1;
1150 }
1151
1152 // open file
1153 // ---------
1154 bool bOk wxDUMMY_INITIALIZE(false);
1155 if ( wxFile::Exists(filename) ) {
1156 bool bAppend = false;
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:
1163 bAppend = true;
1164 break;
1165
1166 case wxNO:
1167 bAppend = false;
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 {
1181 bOk = file.Create(filename, true /* overwrite */);
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
1196 #endif // !(wxUSE_LOGGUI || wxUSE_LOGWINDOW)
1197
1198 #if wxUSE_LOG && wxUSE_TEXTCTRL
1199
1200 // ----------------------------------------------------------------------------
1201 // wxLogTextCtrl implementation
1202 // ----------------------------------------------------------------------------
1203
1204 wxLogTextCtrl::wxLogTextCtrl(wxTextCtrl *pTextCtrl)
1205 {
1206 m_pTextCtrl = pTextCtrl;
1207 }
1208
1209 void wxLogTextCtrl::DoLogString(const wxChar *szString, time_t WXUNUSED(t))
1210 {
1211 wxString msg;
1212 TimeStamp(&msg);
1213
1214 msg << szString << wxT('\n');
1215 m_pTextCtrl->AppendText(msg);
1216 }
1217
1218 #endif // wxUSE_LOG && wxUSE_TEXTCTRL