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