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