]> git.saurik.com Git - wxWidgets.git/blob - src/generic/logg.cpp
f03ba5cc39b6bf067e7796680029c9feb2788db2
[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 #include "wx/wxcrtvararg.h"
42 #endif // WX_PRECOMP
43
44 #if wxUSE_LOGGUI || wxUSE_LOGWINDOW
45
46 #include "wx/file.h"
47 #include "wx/clipbrd.h"
48 #include "wx/dataobj.h"
49 #include "wx/textfile.h"
50 #include "wx/statline.h"
51 #include "wx/artprov.h"
52 #include "wx/collpane.h"
53 #include "wx/arrstr.h"
54 #include "wx/msgout.h"
55
56 #ifdef __WXMSW__
57 // for OutputDebugString()
58 #include "wx/msw/private.h"
59 #endif // Windows
60
61
62 #ifdef __WXPM__
63 #include <time.h>
64 #endif
65
66 #if wxUSE_LOG_DIALOG
67 #include "wx/listctrl.h"
68 #include "wx/imaglist.h"
69 #include "wx/image.h"
70 #endif // wxUSE_LOG_DIALOG/!wxUSE_LOG_DIALOG
71
72 #if defined(__MWERKS__) && wxUSE_UNICODE
73 #include <wtime.h>
74 #endif
75
76 #include "wx/time.h"
77
78 // the suffix we add to the button to show that the dialog can be expanded
79 #define EXPAND_SUFFIX wxT(" >>")
80
81 #define CAN_SAVE_FILES (wxUSE_FILE && wxUSE_FILEDLG)
82
83 // ----------------------------------------------------------------------------
84 // private classes
85 // ----------------------------------------------------------------------------
86
87 #if wxUSE_LOG_DIALOG
88
89 // this function is a wrapper around strftime(3)
90 // allows to exclude the usage of wxDateTime
91 static wxString TimeStamp(const wxString& format, time_t t)
92 {
93 wxChar buf[4096];
94 struct tm tm;
95 if ( !wxStrftime(buf, WXSIZEOF(buf), format, wxLocaltime_r(&t, &tm)) )
96 {
97 // buffer is too small?
98 wxFAIL_MSG(wxT("strftime() failed"));
99 }
100 return wxString(buf);
101 }
102
103
104 class wxLogDialog : public wxDialog
105 {
106 public:
107 wxLogDialog(wxWindow *parent,
108 const wxArrayString& messages,
109 const wxArrayInt& severity,
110 const wxArrayLong& timess,
111 const wxString& caption,
112 long style);
113 virtual ~wxLogDialog();
114
115 // event handlers
116 void OnOk(wxCommandEvent& event);
117 #if wxUSE_CLIPBOARD
118 void OnCopy(wxCommandEvent& event);
119 #endif // wxUSE_CLIPBOARD
120 #if CAN_SAVE_FILES
121 void OnSave(wxCommandEvent& event);
122 #endif // CAN_SAVE_FILES
123 void OnListItemActivated(wxListEvent& event);
124
125 private:
126 // create controls needed for the details display
127 void CreateDetailsControls(wxWindow *);
128
129 // if necessary truncates the given string and adds an ellipsis
130 wxString EllipsizeString(const wxString &text)
131 {
132 if (ms_maxLength > 0 &&
133 text.length() > ms_maxLength)
134 {
135 wxString ret(text);
136 ret.Truncate(ms_maxLength);
137 ret << "...";
138 return ret;
139 }
140
141 return text;
142 }
143
144 #if CAN_SAVE_FILES || wxUSE_CLIPBOARD
145 // return the contents of the dialog as a multiline string
146 wxString GetLogMessages() const;
147 #endif // CAN_SAVE_FILES || wxUSE_CLIPBOARD
148
149
150 // the data for the listctrl
151 wxArrayString m_messages;
152 wxArrayInt m_severity;
153 wxArrayLong m_times;
154
155 // the controls which are not shown initially (but only when details
156 // button is pressed)
157 wxListCtrl *m_listctrl;
158
159 // the translated "Details" string
160 static wxString ms_details;
161
162 // the maximum length of the log message
163 static size_t ms_maxLength;
164
165 DECLARE_EVENT_TABLE()
166 wxDECLARE_NO_COPY_CLASS(wxLogDialog);
167 };
168
169 BEGIN_EVENT_TABLE(wxLogDialog, wxDialog)
170 EVT_BUTTON(wxID_OK, wxLogDialog::OnOk)
171 #if wxUSE_CLIPBOARD
172 EVT_BUTTON(wxID_COPY, wxLogDialog::OnCopy)
173 #endif // wxUSE_CLIPBOARD
174 #if CAN_SAVE_FILES
175 EVT_BUTTON(wxID_SAVE, wxLogDialog::OnSave)
176 #endif // CAN_SAVE_FILES
177 EVT_LIST_ITEM_ACTIVATED(wxID_ANY, wxLogDialog::OnListItemActivated)
178 END_EVENT_TABLE()
179
180 #endif // wxUSE_LOG_DIALOG
181
182 // ----------------------------------------------------------------------------
183 // private functions
184 // ----------------------------------------------------------------------------
185
186 #if CAN_SAVE_FILES
187
188 // pass an uninitialized file object, the function will ask the user for the
189 // filename and try to open it, returns true on success (file was opened),
190 // false if file couldn't be opened/created and -1 if the file selection
191 // dialog was cancelled
192 static int OpenLogFile(wxFile& file, wxString *filename = NULL, wxWindow *parent = NULL);
193
194 #endif // CAN_SAVE_FILES
195
196 // ============================================================================
197 // implementation
198 // ============================================================================
199
200 // ----------------------------------------------------------------------------
201 // wxLogGui implementation (FIXME MT-unsafe)
202 // ----------------------------------------------------------------------------
203
204 #if wxUSE_LOGGUI
205
206 wxLogGui::wxLogGui()
207 {
208 Clear();
209 }
210
211 void wxLogGui::Clear()
212 {
213 m_bErrors =
214 m_bWarnings =
215 m_bHasMessages = false;
216
217 m_aMessages.Empty();
218 m_aSeverity.Empty();
219 m_aTimes.Empty();
220 }
221
222 int wxLogGui::GetSeverityIcon() const
223 {
224 return m_bErrors ? wxICON_STOP
225 : m_bWarnings ? wxICON_EXCLAMATION
226 : wxICON_INFORMATION;
227 }
228
229 wxString wxLogGui::GetTitle() const
230 {
231 wxString titleFormat;
232 switch ( GetSeverityIcon() )
233 {
234 case wxICON_STOP:
235 titleFormat = _("%s Error");
236 break;
237
238 case wxICON_EXCLAMATION:
239 titleFormat = _("%s Warning");
240 break;
241
242 default:
243 wxFAIL_MSG( "unexpected icon severity" );
244 // fall through
245
246 case wxICON_INFORMATION:
247 titleFormat = _("%s Information");
248 }
249
250 return wxString::Format(titleFormat, wxTheApp->GetAppDisplayName());
251 }
252
253 void
254 wxLogGui::DoShowSingleLogMessage(const wxString& message,
255 const wxString& title,
256 int style)
257 {
258 wxMessageBox(message, title, wxOK | style);
259 }
260
261 void
262 wxLogGui::DoShowMultipleLogMessages(const wxArrayString& messages,
263 const wxArrayInt& severities,
264 const wxArrayLong& times,
265 const wxString& title,
266 int style)
267 {
268 #if wxUSE_LOG_DIALOG
269 wxLogDialog dlg(NULL,
270 messages, severities, times,
271 title, style);
272
273 // clear the message list before showing the dialog because while it's
274 // shown some new messages may appear
275 Clear();
276
277 (void)dlg.ShowModal();
278 #else // !wxUSE_LOG_DIALOG
279 // start from the most recent message
280 wxString message;
281 const size_t nMsgCount = messages.size();
282 message.reserve(nMsgCount*100);
283 for ( size_t n = nMsgCount; n > 0; n-- ) {
284 message << m_aMessages[n - 1] << wxT("\n");
285 }
286
287 DoShowSingleLogMessage(message, title, style);
288 #endif // wxUSE_LOG_DIALOG/!wxUSE_LOG_DIALOG
289 }
290
291 void wxLogGui::Flush()
292 {
293 wxLog::Flush();
294
295 if ( !m_bHasMessages )
296 return;
297
298 // do it right now to block any new calls to Flush() while we're here
299 m_bHasMessages = false;
300
301 // note that this must be done before examining m_aMessages as it may log
302 // yet another message
303 const unsigned repeatCount = LogLastRepeatIfNeeded();
304
305 const size_t nMsgCount = m_aMessages.size();
306
307 if ( repeatCount > 0 )
308 {
309 m_aMessages[nMsgCount - 1] << " (" << m_aMessages[nMsgCount - 2] << ")";
310 }
311
312 const wxString title = GetTitle();
313 const int style = GetSeverityIcon();
314
315 // avoid showing other log dialogs until we're done with the dialog we're
316 // showing right now: nested modal dialogs make for really bad UI!
317 Suspend();
318
319 if ( nMsgCount == 1 )
320 {
321 // make a copy before calling Clear()
322 const wxString message(m_aMessages[0]);
323 Clear();
324
325 DoShowSingleLogMessage(message, title, style);
326 }
327 else // more than one message
328 {
329 wxArrayString messages;
330 wxArrayInt severities;
331 wxArrayLong times;
332
333 messages.swap(m_aMessages);
334 severities.swap(m_aSeverity);
335 times.swap(m_aTimes);
336
337 Clear();
338
339 DoShowMultipleLogMessages(messages, severities, times, title, style);
340 }
341
342 // allow flushing the logs again
343 Resume();
344 }
345
346 // log all kinds of messages
347 void wxLogGui::DoLogRecord(wxLogLevel level,
348 const wxString& msg,
349 const wxLogRecordInfo& info)
350 {
351 switch ( level )
352 {
353 case wxLOG_Info:
354 if ( GetVerbose() )
355 case wxLOG_Message:
356 {
357 m_aMessages.Add(msg);
358 m_aSeverity.Add(wxLOG_Message);
359 m_aTimes.Add((long)info.timestamp);
360 m_bHasMessages = true;
361 }
362 break;
363
364 case wxLOG_Status:
365 #if wxUSE_STATUSBAR
366 {
367 wxFrame *pFrame = NULL;
368
369 // check if the frame was passed to us explicitly
370 wxUIntPtr ptr = 0;
371 if ( info.GetNumValue(wxLOG_KEY_FRAME, &ptr) )
372 {
373 pFrame = static_cast<wxFrame *>(wxUIntToPtr(ptr));
374 }
375
376 // find the top window and set it's status text if it has any
377 if ( pFrame == NULL ) {
378 wxWindow *pWin = wxTheApp->GetTopWindow();
379 if ( pWin != NULL && pWin->IsKindOf(CLASSINFO(wxFrame)) ) {
380 pFrame = (wxFrame *)pWin;
381 }
382 }
383
384 if ( pFrame && pFrame->GetStatusBar() )
385 pFrame->SetStatusText(msg);
386 }
387 #endif // wxUSE_STATUSBAR
388 break;
389
390 case wxLOG_Error:
391 if ( !m_bErrors ) {
392 #if !wxUSE_LOG_DIALOG
393 // discard earlier informational messages if this is the 1st
394 // error because they might not make sense any more and showing
395 // them in a message box might be confusing
396 m_aMessages.Empty();
397 m_aSeverity.Empty();
398 m_aTimes.Empty();
399 #endif // wxUSE_LOG_DIALOG
400 m_bErrors = true;
401 }
402 // fall through
403
404 case wxLOG_Warning:
405 if ( !m_bErrors ) {
406 // for the warning we don't discard the info messages
407 m_bWarnings = true;
408 }
409
410 m_aMessages.Add(msg);
411 m_aSeverity.Add((int)level);
412 m_aTimes.Add((long)info.timestamp);
413 m_bHasMessages = true;
414 break;
415
416 case wxLOG_Debug:
417 case wxLOG_Trace:
418 // let the base class deal with debug/trace messages
419 wxLog::DoLogRecord(level, msg, info);
420 break;
421
422 case wxLOG_FatalError:
423 case wxLOG_Max:
424 // fatal errors are shown immediately and terminate the program so
425 // we should never see them here
426 wxFAIL_MSG("unexpected log level");
427 break;
428
429 case wxLOG_Progress:
430 case wxLOG_User:
431 // just ignore those: passing them to the base class would result
432 // in asserts from DoLogText() because DoLogTextAtLevel() would
433 // call it as it doesn't know how to handle these levels otherwise
434 break;
435 }
436 }
437
438 #endif // wxUSE_LOGGUI
439
440 // ----------------------------------------------------------------------------
441 // wxLogWindow and wxLogFrame implementation
442 // ----------------------------------------------------------------------------
443
444 #if wxUSE_LOGWINDOW
445
446 // log frame class
447 // ---------------
448 class wxLogFrame : public wxFrame
449 {
450 public:
451 // ctor & dtor
452 wxLogFrame(wxWindow *pParent, wxLogWindow *log, const wxString& szTitle);
453 virtual ~wxLogFrame();
454
455 // menu callbacks
456 void OnClose(wxCommandEvent& event);
457 void OnCloseWindow(wxCloseEvent& event);
458 #if CAN_SAVE_FILES
459 void OnSave(wxCommandEvent& event);
460 #endif // CAN_SAVE_FILES
461 void OnClear(wxCommandEvent& event);
462
463 // do show the message in the text control
464 void ShowLogMessage(const wxString& message)
465 {
466 m_pTextCtrl->AppendText(message + wxS('\n'));
467 }
468
469 private:
470 // use standard ids for our commands!
471 enum
472 {
473 Menu_Close = wxID_CLOSE,
474 Menu_Save = wxID_SAVE,
475 Menu_Clear = wxID_CLEAR
476 };
477
478 // common part of OnClose() and OnCloseWindow()
479 void DoClose();
480
481 wxTextCtrl *m_pTextCtrl;
482 wxLogWindow *m_log;
483
484 DECLARE_EVENT_TABLE()
485 wxDECLARE_NO_COPY_CLASS(wxLogFrame);
486 };
487
488 BEGIN_EVENT_TABLE(wxLogFrame, wxFrame)
489 // wxLogWindow menu events
490 EVT_MENU(Menu_Close, wxLogFrame::OnClose)
491 #if CAN_SAVE_FILES
492 EVT_MENU(Menu_Save, wxLogFrame::OnSave)
493 #endif // CAN_SAVE_FILES
494 EVT_MENU(Menu_Clear, wxLogFrame::OnClear)
495
496 EVT_CLOSE(wxLogFrame::OnCloseWindow)
497 END_EVENT_TABLE()
498
499 wxLogFrame::wxLogFrame(wxWindow *pParent, wxLogWindow *log, const wxString& szTitle)
500 : wxFrame(pParent, wxID_ANY, szTitle)
501 {
502 m_log = log;
503
504 m_pTextCtrl = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition,
505 wxDefaultSize,
506 wxTE_MULTILINE |
507 wxHSCROLL |
508 // needed for Win32 to avoid 65Kb limit but it doesn't work well
509 // when using RichEdit 2.0 which we always do in the Unicode build
510 #if !wxUSE_UNICODE
511 wxTE_RICH |
512 #endif // !wxUSE_UNICODE
513 wxTE_READONLY);
514
515 #if wxUSE_MENUS
516 // create menu
517 wxMenuBar *pMenuBar = new wxMenuBar;
518 wxMenu *pMenu = new wxMenu;
519 #if CAN_SAVE_FILES
520 pMenu->Append(Menu_Save, _("Save &As..."), _("Save log contents to file"));
521 #endif // CAN_SAVE_FILES
522 pMenu->Append(Menu_Clear, _("C&lear"), _("Clear the log contents"));
523 pMenu->AppendSeparator();
524 pMenu->Append(Menu_Close, _("&Close"), _("Close this window"));
525 pMenuBar->Append(pMenu, _("&Log"));
526 SetMenuBar(pMenuBar);
527 #endif // wxUSE_MENUS
528
529 #if wxUSE_STATUSBAR
530 // status bar for menu prompts
531 CreateStatusBar();
532 #endif // wxUSE_STATUSBAR
533
534 m_log->OnFrameCreate(this);
535 }
536
537 void wxLogFrame::DoClose()
538 {
539 if ( m_log->OnFrameClose(this) )
540 {
541 // instead of closing just hide the window to be able to Show() it
542 // later
543 Show(false);
544 }
545 }
546
547 void wxLogFrame::OnClose(wxCommandEvent& WXUNUSED(event))
548 {
549 DoClose();
550 }
551
552 void wxLogFrame::OnCloseWindow(wxCloseEvent& WXUNUSED(event))
553 {
554 DoClose();
555 }
556
557 #if CAN_SAVE_FILES
558 void wxLogFrame::OnSave(wxCommandEvent& WXUNUSED(event))
559 {
560 wxString filename;
561 wxFile file;
562 int rc = OpenLogFile(file, &filename, this);
563 if ( rc == -1 )
564 {
565 // cancelled
566 return;
567 }
568
569 bool bOk = rc != 0;
570
571 // retrieve text and save it
572 // -------------------------
573 int nLines = m_pTextCtrl->GetNumberOfLines();
574 for ( int nLine = 0; bOk && nLine < nLines; nLine++ ) {
575 bOk = file.Write(m_pTextCtrl->GetLineText(nLine) +
576 wxTextFile::GetEOL());
577 }
578
579 if ( bOk )
580 bOk = file.Close();
581
582 if ( !bOk ) {
583 wxLogError(_("Can't save log contents to file."));
584 }
585 else {
586 wxLogStatus((wxFrame*)this, _("Log saved to the file '%s'."), filename.c_str());
587 }
588 }
589 #endif // CAN_SAVE_FILES
590
591 void wxLogFrame::OnClear(wxCommandEvent& WXUNUSED(event))
592 {
593 m_pTextCtrl->Clear();
594 }
595
596 wxLogFrame::~wxLogFrame()
597 {
598 m_log->OnFrameDelete(this);
599 }
600
601 // wxLogWindow
602 // -----------
603
604 wxLogWindow::wxLogWindow(wxWindow *pParent,
605 const wxString& szTitle,
606 bool bShow,
607 bool bDoPass)
608 {
609 // Initialize it to NULL to ensure that we don't crash if any log messages
610 // are generated before the frame is fully created (while this doesn't
611 // happen normally, it might, in principle).
612 m_pLogFrame = NULL;
613
614 PassMessages(bDoPass);
615
616 m_pLogFrame = new wxLogFrame(pParent, this, szTitle);
617
618 if ( bShow )
619 m_pLogFrame->Show();
620 }
621
622 void wxLogWindow::Show(bool bShow)
623 {
624 m_pLogFrame->Show(bShow);
625 }
626
627 void wxLogWindow::DoLogTextAtLevel(wxLogLevel level, const wxString& msg)
628 {
629 if ( !m_pLogFrame )
630 return;
631
632 // don't put trace messages in the text window for 2 reasons:
633 // 1) there are too many of them
634 // 2) they may provoke other trace messages (e.g. wxMSW code uses
635 // wxLogTrace to log Windows messages and adding text to the control
636 // sends more of them) thus sending a program into an infinite loop
637 if ( level == wxLOG_Trace )
638 return;
639
640 m_pLogFrame->ShowLogMessage(msg);
641 }
642
643 wxFrame *wxLogWindow::GetFrame() const
644 {
645 return m_pLogFrame;
646 }
647
648 void wxLogWindow::OnFrameCreate(wxFrame * WXUNUSED(frame))
649 {
650 }
651
652 bool wxLogWindow::OnFrameClose(wxFrame * WXUNUSED(frame))
653 {
654 // allow to close
655 return true;
656 }
657
658 void wxLogWindow::OnFrameDelete(wxFrame * WXUNUSED(frame))
659 {
660 m_pLogFrame = NULL;
661 }
662
663 wxLogWindow::~wxLogWindow()
664 {
665 // may be NULL if log frame already auto destroyed itself
666 delete m_pLogFrame;
667 }
668
669 #endif // wxUSE_LOGWINDOW
670
671 // ----------------------------------------------------------------------------
672 // wxLogDialog
673 // ----------------------------------------------------------------------------
674
675 #if wxUSE_LOG_DIALOG
676
677 wxString wxLogDialog::ms_details;
678 size_t wxLogDialog::ms_maxLength = 0;
679
680 wxLogDialog::wxLogDialog(wxWindow *parent,
681 const wxArrayString& messages,
682 const wxArrayInt& severity,
683 const wxArrayLong& times,
684 const wxString& caption,
685 long style)
686 : wxDialog(parent, wxID_ANY, caption,
687 wxDefaultPosition, wxDefaultSize,
688 wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
689 {
690 // init the static variables:
691
692 if ( ms_details.empty() )
693 {
694 // ensure that we won't loop here if wxGetTranslation()
695 // happens to pop up a Log message while translating this :-)
696 ms_details = wxTRANSLATE("&Details");
697 ms_details = wxGetTranslation(ms_details);
698 #ifdef __SMARTPHONE__
699 ms_details = wxStripMenuCodes(ms_details);
700 #endif
701 }
702
703 if ( ms_maxLength == 0 )
704 {
705 ms_maxLength = (2 * wxGetDisplaySize().x/3) / GetCharWidth();
706 }
707
708 size_t count = messages.GetCount();
709 m_messages.Alloc(count);
710 m_severity.Alloc(count);
711 m_times.Alloc(count);
712
713 for ( size_t n = 0; n < count; n++ )
714 {
715 m_messages.Add(messages[n]);
716 m_severity.Add(severity[n]);
717 m_times.Add(times[n]);
718 }
719
720 m_listctrl = NULL;
721
722 bool isPda = (wxSystemSettings::GetScreenType() <= wxSYS_SCREEN_PDA);
723
724 // create the controls which are always shown and layout them: we use
725 // sizers even though our window is not resizable to calculate the size of
726 // the dialog properly
727 wxBoxSizer *sizerTop = new wxBoxSizer(wxVERTICAL);
728 wxBoxSizer *sizerAll = new wxBoxSizer(isPda ? wxVERTICAL : wxHORIZONTAL);
729
730 if (!isPda)
731 {
732 wxStaticBitmap *icon = new wxStaticBitmap
733 (
734 this,
735 wxID_ANY,
736 wxArtProvider::GetMessageBoxIcon(style)
737 );
738 sizerAll->Add(icon, wxSizerFlags().Centre());
739 }
740
741 // create the text sizer with a minimal size so that we are sure it won't be too small
742 wxString message = EllipsizeString(messages.Last());
743 wxSizer *szText = CreateTextSizer(message);
744 szText->SetMinSize(wxMin(300, wxGetDisplaySize().x / 3), -1);
745
746 sizerAll->Add(szText, wxSizerFlags(1).Centre().Border(wxLEFT | wxRIGHT));
747
748 wxButton *btnOk = new wxButton(this, wxID_OK);
749 sizerAll->Add(btnOk, wxSizerFlags().Centre());
750
751 sizerTop->Add(sizerAll, wxSizerFlags().Expand().Border());
752
753
754 // add the details pane
755 #ifndef __SMARTPHONE__
756 wxCollapsiblePane * const
757 collpane = new wxCollapsiblePane(this, wxID_ANY, ms_details);
758 sizerTop->Add(collpane, wxSizerFlags(1).Expand().Border());
759
760 wxWindow *win = collpane->GetPane();
761 wxSizer * const paneSz = new wxBoxSizer(wxVERTICAL);
762
763 CreateDetailsControls(win);
764
765 paneSz->Add(m_listctrl, wxSizerFlags(1).Expand().Border(wxTOP));
766
767 #if wxUSE_CLIPBOARD || CAN_SAVE_FILES
768 wxBoxSizer * const btnSizer = new wxBoxSizer(wxHORIZONTAL);
769
770 wxSizerFlags flagsBtn;
771 flagsBtn.Border(wxLEFT);
772
773 #if wxUSE_CLIPBOARD
774 btnSizer->Add(new wxButton(win, wxID_COPY), flagsBtn);
775 #endif // wxUSE_CLIPBOARD
776
777 #if CAN_SAVE_FILES
778 btnSizer->Add(new wxButton(win, wxID_SAVE), flagsBtn);
779 #endif // CAN_SAVE_FILES
780
781 paneSz->Add(btnSizer, wxSizerFlags().Right().Border(wxTOP));
782 #endif // wxUSE_CLIPBOARD || CAN_SAVE_FILES
783
784 win->SetSizer(paneSz);
785 paneSz->SetSizeHints(win);
786 #else // __SMARTPHONE__
787 SetLeftMenu(wxID_OK);
788 SetRightMenu(wxID_MORE, ms_details + EXPAND_SUFFIX);
789 #endif // __SMARTPHONE__/!__SMARTPHONE__
790
791 SetSizerAndFit(sizerTop);
792
793 Centre();
794
795 if (isPda)
796 {
797 // Move up the screen so that when we expand the dialog,
798 // there's enough space.
799 Move(wxPoint(GetPosition().x, GetPosition().y / 2));
800 }
801 }
802
803 void wxLogDialog::CreateDetailsControls(wxWindow *parent)
804 {
805 wxString fmt = wxLog::GetTimestamp();
806 bool hasTimeStamp = !fmt.IsEmpty();
807
808 // create the list ctrl now
809 m_listctrl = new wxListCtrl(parent, wxID_ANY,
810 wxDefaultPosition, wxDefaultSize,
811 wxBORDER_SIMPLE |
812 wxLC_REPORT |
813 wxLC_NO_HEADER |
814 wxLC_SINGLE_SEL);
815 #ifdef __WXWINCE__
816 // This makes a big aesthetic difference on WinCE but I
817 // don't want to risk problems on other platforms
818 m_listctrl->Hide();
819 #endif
820
821 // no need to translate these strings as they're not shown to the
822 // user anyhow (we use wxLC_NO_HEADER style)
823 m_listctrl->InsertColumn(0, wxT("Message"));
824
825 if (hasTimeStamp)
826 m_listctrl->InsertColumn(1, wxT("Time"));
827
828 // prepare the imagelist
829 static const int ICON_SIZE = 16;
830 wxImageList *imageList = new wxImageList(ICON_SIZE, ICON_SIZE);
831
832 // order should be the same as in the switch below!
833 static const char* const icons[] =
834 {
835 wxART_ERROR,
836 wxART_WARNING,
837 wxART_INFORMATION
838 };
839
840 bool loadedIcons = true;
841
842 for ( size_t icon = 0; icon < WXSIZEOF(icons); icon++ )
843 {
844 wxBitmap bmp = wxArtProvider::GetBitmap(icons[icon], wxART_MESSAGE_BOX,
845 wxSize(ICON_SIZE, ICON_SIZE));
846
847 // This may very well fail if there are insufficient colours available.
848 // Degrade gracefully.
849 if ( !bmp.IsOk() )
850 {
851 loadedIcons = false;
852
853 break;
854 }
855
856 imageList->Add(bmp);
857 }
858
859 m_listctrl->SetImageList(imageList, wxIMAGE_LIST_SMALL);
860
861 // fill the listctrl
862 size_t count = m_messages.GetCount();
863 for ( size_t n = 0; n < count; n++ )
864 {
865 int image;
866
867 if ( loadedIcons )
868 {
869 switch ( m_severity[n] )
870 {
871 case wxLOG_Error:
872 image = 0;
873 break;
874
875 case wxLOG_Warning:
876 image = 1;
877 break;
878
879 default:
880 image = 2;
881 }
882 }
883 else // failed to load images
884 {
885 image = -1;
886 }
887
888 wxString msg = m_messages[n];
889 msg.Replace(wxT("\n"), wxT(" "));
890 msg = EllipsizeString(msg);
891
892 m_listctrl->InsertItem(n, msg, image);
893
894 if (hasTimeStamp)
895 m_listctrl->SetItem(n, 1, TimeStamp(fmt, (time_t)m_times[n]));
896 }
897
898 // let the columns size themselves
899 m_listctrl->SetColumnWidth(0, wxLIST_AUTOSIZE);
900 if (hasTimeStamp)
901 m_listctrl->SetColumnWidth(1, wxLIST_AUTOSIZE);
902
903 // calculate an approximately nice height for the listctrl
904 int height = GetCharHeight()*(count + 4);
905
906 // but check that the dialog won't fall fown from the screen
907 //
908 // we use GetMinHeight() to get the height of the dialog part without the
909 // details and we consider that the "Save" button below and the separator
910 // line (and the margins around it) take about as much, hence double it
911 int heightMax = wxGetDisplaySize().y - GetPosition().y - 2*GetMinHeight();
912
913 // we should leave a margin
914 heightMax *= 9;
915 heightMax /= 10;
916
917 m_listctrl->SetSize(wxDefaultCoord, wxMin(height, heightMax));
918 }
919
920 void wxLogDialog::OnListItemActivated(wxListEvent& event)
921 {
922 // show the activated item in a message box
923 // This allow the user to correctly display the logs which are longer
924 // than the listctrl and thus gets truncated or those which contains
925 // newlines.
926
927 // NB: don't do:
928 // wxString str = m_listctrl->GetItemText(event.GetIndex());
929 // as there's a 260 chars limit on the items inside a wxListCtrl in wxMSW.
930 wxString str = m_messages[event.GetIndex()];
931
932 // wxMessageBox will nicely handle the '\n' in the string (if any)
933 // and supports long strings
934 wxMessageBox(str, wxT("Log message"), wxOK, this);
935 }
936
937 void wxLogDialog::OnOk(wxCommandEvent& WXUNUSED(event))
938 {
939 EndModal(wxID_OK);
940 }
941
942 #if CAN_SAVE_FILES || wxUSE_CLIPBOARD
943
944 wxString wxLogDialog::GetLogMessages() const
945 {
946 wxString fmt = wxLog::GetTimestamp();
947 if ( fmt.empty() )
948 {
949 // use the default format
950 fmt = "%c";
951 }
952
953 const size_t count = m_messages.GetCount();
954
955 wxString text;
956 text.reserve(count*m_messages[0].length());
957 for ( size_t n = 0; n < count; n++ )
958 {
959 text << TimeStamp(fmt, (time_t)m_times[n])
960 << ": "
961 << m_messages[n]
962 << wxTextFile::GetEOL();
963 }
964
965 return text;
966 }
967
968 #endif // CAN_SAVE_FILES || wxUSE_CLIPBOARD
969
970 #if wxUSE_CLIPBOARD
971
972 void wxLogDialog::OnCopy(wxCommandEvent& WXUNUSED(event))
973 {
974 wxClipboardLocker clip;
975 if ( !clip ||
976 !wxTheClipboard->AddData(new wxTextDataObject(GetLogMessages())) )
977 {
978 wxLogError(_("Failed to copy dialog contents to the clipboard."));
979 }
980 }
981
982 #endif // wxUSE_CLIPBOARD
983
984 #if CAN_SAVE_FILES
985
986 void wxLogDialog::OnSave(wxCommandEvent& WXUNUSED(event))
987 {
988 wxFile file;
989 int rc = OpenLogFile(file, NULL, this);
990 if ( rc == -1 )
991 {
992 // cancelled
993 return;
994 }
995
996 if ( !rc || !file.Write(GetLogMessages()) || !file.Close() )
997 {
998 wxLogError(_("Can't save log contents to file."));
999 }
1000 }
1001
1002 #endif // CAN_SAVE_FILES
1003
1004 wxLogDialog::~wxLogDialog()
1005 {
1006 if ( m_listctrl )
1007 {
1008 delete m_listctrl->GetImageList(wxIMAGE_LIST_SMALL);
1009 }
1010 }
1011
1012 #endif // wxUSE_LOG_DIALOG
1013
1014 #if CAN_SAVE_FILES
1015
1016 // pass an uninitialized file object, the function will ask the user for the
1017 // filename and try to open it, returns true on success (file was opened),
1018 // false if file couldn't be opened/created and -1 if the file selection
1019 // dialog was cancelled
1020 static int OpenLogFile(wxFile& file, wxString *pFilename, wxWindow *parent)
1021 {
1022 // get the file name
1023 // -----------------
1024 wxString filename = wxSaveFileSelector(wxT("log"), wxT("txt"), wxT("log.txt"), parent);
1025 if ( !filename ) {
1026 // cancelled
1027 return -1;
1028 }
1029
1030 // open file
1031 // ---------
1032 bool bOk = true; // suppress warning about it being possible uninitialized
1033 if ( wxFile::Exists(filename) ) {
1034 bool bAppend = false;
1035 wxString strMsg;
1036 strMsg.Printf(_("Append log to file '%s' (choosing [No] will overwrite it)?"),
1037 filename.c_str());
1038 switch ( wxMessageBox(strMsg, _("Question"),
1039 wxICON_QUESTION | wxYES_NO | wxCANCEL) ) {
1040 case wxYES:
1041 bAppend = true;
1042 break;
1043
1044 case wxNO:
1045 bAppend = false;
1046 break;
1047
1048 case wxCANCEL:
1049 return -1;
1050
1051 default:
1052 wxFAIL_MSG(_("invalid message box return value"));
1053 }
1054
1055 if ( bAppend ) {
1056 bOk = file.Open(filename, wxFile::write_append);
1057 }
1058 else {
1059 bOk = file.Create(filename, true /* overwrite */);
1060 }
1061 }
1062 else {
1063 bOk = file.Create(filename);
1064 }
1065
1066 if ( pFilename )
1067 *pFilename = filename;
1068
1069 return bOk;
1070 }
1071
1072 #endif // CAN_SAVE_FILES
1073
1074 #endif // !(wxUSE_LOGGUI || wxUSE_LOGWINDOW)
1075
1076 #if wxUSE_LOG && wxUSE_TEXTCTRL
1077
1078 // ----------------------------------------------------------------------------
1079 // wxLogTextCtrl implementation
1080 // ----------------------------------------------------------------------------
1081
1082 wxLogTextCtrl::wxLogTextCtrl(wxTextCtrl *pTextCtrl)
1083 {
1084 m_pTextCtrl = pTextCtrl;
1085 }
1086
1087 void wxLogTextCtrl::DoLogText(const wxString& msg)
1088 {
1089 m_pTextCtrl->AppendText(msg + wxS('\n'));
1090 }
1091
1092 #endif // wxUSE_LOG && wxUSE_TEXTCTRL