Compile fixes for wxUSE_COLLPANE=0
[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
757 #if wxUSE_COLLPANE
758 wxCollapsiblePane * const
759 collpane = new wxCollapsiblePane(this, wxID_ANY, ms_details);
760 sizerTop->Add(collpane, wxSizerFlags(1).Expand().Border());
761
762 wxWindow *win = collpane->GetPane();
763 #else
764 wxPanel* win = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize,
765 wxBORDER_NONE);
766 #endif
767 wxSizer * const paneSz = new wxBoxSizer(wxVERTICAL);
768
769 CreateDetailsControls(win);
770
771 paneSz->Add(m_listctrl, wxSizerFlags(1).Expand().Border(wxTOP));
772
773 #if wxUSE_CLIPBOARD || CAN_SAVE_FILES
774 wxBoxSizer * const btnSizer = new wxBoxSizer(wxHORIZONTAL);
775
776 wxSizerFlags flagsBtn;
777 flagsBtn.Border(wxLEFT);
778
779 #if wxUSE_CLIPBOARD
780 btnSizer->Add(new wxButton(win, wxID_COPY), flagsBtn);
781 #endif // wxUSE_CLIPBOARD
782
783 #if CAN_SAVE_FILES
784 btnSizer->Add(new wxButton(win, wxID_SAVE), flagsBtn);
785 #endif // CAN_SAVE_FILES
786
787 paneSz->Add(btnSizer, wxSizerFlags().Right().Border(wxTOP));
788 #endif // wxUSE_CLIPBOARD || CAN_SAVE_FILES
789
790 win->SetSizer(paneSz);
791 paneSz->SetSizeHints(win);
792 #else // __SMARTPHONE__
793 SetLeftMenu(wxID_OK);
794 SetRightMenu(wxID_MORE, ms_details + EXPAND_SUFFIX);
795 #endif // __SMARTPHONE__/!__SMARTPHONE__
796
797 SetSizerAndFit(sizerTop);
798
799 Centre();
800
801 if (isPda)
802 {
803 // Move up the screen so that when we expand the dialog,
804 // there's enough space.
805 Move(wxPoint(GetPosition().x, GetPosition().y / 2));
806 }
807 }
808
809 void wxLogDialog::CreateDetailsControls(wxWindow *parent)
810 {
811 wxString fmt = wxLog::GetTimestamp();
812 bool hasTimeStamp = !fmt.IsEmpty();
813
814 // create the list ctrl now
815 m_listctrl = new wxListCtrl(parent, wxID_ANY,
816 wxDefaultPosition, wxDefaultSize,
817 wxBORDER_SIMPLE |
818 wxLC_REPORT |
819 wxLC_NO_HEADER |
820 wxLC_SINGLE_SEL);
821 #ifdef __WXWINCE__
822 // This makes a big aesthetic difference on WinCE but I
823 // don't want to risk problems on other platforms
824 m_listctrl->Hide();
825 #endif
826
827 // no need to translate these strings as they're not shown to the
828 // user anyhow (we use wxLC_NO_HEADER style)
829 m_listctrl->InsertColumn(0, wxT("Message"));
830
831 if (hasTimeStamp)
832 m_listctrl->InsertColumn(1, wxT("Time"));
833
834 // prepare the imagelist
835 static const int ICON_SIZE = 16;
836 wxImageList *imageList = new wxImageList(ICON_SIZE, ICON_SIZE);
837
838 // order should be the same as in the switch below!
839 static const char* const icons[] =
840 {
841 wxART_ERROR,
842 wxART_WARNING,
843 wxART_INFORMATION
844 };
845
846 bool loadedIcons = true;
847
848 for ( size_t icon = 0; icon < WXSIZEOF(icons); icon++ )
849 {
850 wxBitmap bmp = wxArtProvider::GetBitmap(icons[icon], wxART_MESSAGE_BOX,
851 wxSize(ICON_SIZE, ICON_SIZE));
852
853 // This may very well fail if there are insufficient colours available.
854 // Degrade gracefully.
855 if ( !bmp.IsOk() )
856 {
857 loadedIcons = false;
858
859 break;
860 }
861
862 imageList->Add(bmp);
863 }
864
865 m_listctrl->SetImageList(imageList, wxIMAGE_LIST_SMALL);
866
867 // fill the listctrl
868 size_t count = m_messages.GetCount();
869 for ( size_t n = 0; n < count; n++ )
870 {
871 int image;
872
873 if ( loadedIcons )
874 {
875 switch ( m_severity[n] )
876 {
877 case wxLOG_Error:
878 image = 0;
879 break;
880
881 case wxLOG_Warning:
882 image = 1;
883 break;
884
885 default:
886 image = 2;
887 }
888 }
889 else // failed to load images
890 {
891 image = -1;
892 }
893
894 wxString msg = m_messages[n];
895 msg.Replace(wxT("\n"), wxT(" "));
896 msg = EllipsizeString(msg);
897
898 m_listctrl->InsertItem(n, msg, image);
899
900 if (hasTimeStamp)
901 m_listctrl->SetItem(n, 1, TimeStamp(fmt, (time_t)m_times[n]));
902 }
903
904 // let the columns size themselves
905 m_listctrl->SetColumnWidth(0, wxLIST_AUTOSIZE);
906 if (hasTimeStamp)
907 m_listctrl->SetColumnWidth(1, wxLIST_AUTOSIZE);
908
909 // calculate an approximately nice height for the listctrl
910 int height = GetCharHeight()*(count + 4);
911
912 // but check that the dialog won't fall fown from the screen
913 //
914 // we use GetMinHeight() to get the height of the dialog part without the
915 // details and we consider that the "Save" button below and the separator
916 // line (and the margins around it) take about as much, hence double it
917 int heightMax = wxGetDisplaySize().y - GetPosition().y - 2*GetMinHeight();
918
919 // we should leave a margin
920 heightMax *= 9;
921 heightMax /= 10;
922
923 m_listctrl->SetSize(wxDefaultCoord, wxMin(height, heightMax));
924 }
925
926 void wxLogDialog::OnListItemActivated(wxListEvent& event)
927 {
928 // show the activated item in a message box
929 // This allow the user to correctly display the logs which are longer
930 // than the listctrl and thus gets truncated or those which contains
931 // newlines.
932
933 // NB: don't do:
934 // wxString str = m_listctrl->GetItemText(event.GetIndex());
935 // as there's a 260 chars limit on the items inside a wxListCtrl in wxMSW.
936 wxString str = m_messages[event.GetIndex()];
937
938 // wxMessageBox will nicely handle the '\n' in the string (if any)
939 // and supports long strings
940 wxMessageBox(str, wxT("Log message"), wxOK, this);
941 }
942
943 void wxLogDialog::OnOk(wxCommandEvent& WXUNUSED(event))
944 {
945 EndModal(wxID_OK);
946 }
947
948 #if CAN_SAVE_FILES || wxUSE_CLIPBOARD
949
950 wxString wxLogDialog::GetLogMessages() const
951 {
952 wxString fmt = wxLog::GetTimestamp();
953 if ( fmt.empty() )
954 {
955 // use the default format
956 fmt = "%c";
957 }
958
959 const size_t count = m_messages.GetCount();
960
961 wxString text;
962 text.reserve(count*m_messages[0].length());
963 for ( size_t n = 0; n < count; n++ )
964 {
965 text << TimeStamp(fmt, (time_t)m_times[n])
966 << ": "
967 << m_messages[n]
968 << wxTextFile::GetEOL();
969 }
970
971 return text;
972 }
973
974 #endif // CAN_SAVE_FILES || wxUSE_CLIPBOARD
975
976 #if wxUSE_CLIPBOARD
977
978 void wxLogDialog::OnCopy(wxCommandEvent& WXUNUSED(event))
979 {
980 wxClipboardLocker clip;
981 if ( !clip ||
982 !wxTheClipboard->AddData(new wxTextDataObject(GetLogMessages())) )
983 {
984 wxLogError(_("Failed to copy dialog contents to the clipboard."));
985 }
986 }
987
988 #endif // wxUSE_CLIPBOARD
989
990 #if CAN_SAVE_FILES
991
992 void wxLogDialog::OnSave(wxCommandEvent& WXUNUSED(event))
993 {
994 wxFile file;
995 int rc = OpenLogFile(file, NULL, this);
996 if ( rc == -1 )
997 {
998 // cancelled
999 return;
1000 }
1001
1002 if ( !rc || !file.Write(GetLogMessages()) || !file.Close() )
1003 {
1004 wxLogError(_("Can't save log contents to file."));
1005 }
1006 }
1007
1008 #endif // CAN_SAVE_FILES
1009
1010 wxLogDialog::~wxLogDialog()
1011 {
1012 if ( m_listctrl )
1013 {
1014 delete m_listctrl->GetImageList(wxIMAGE_LIST_SMALL);
1015 }
1016 }
1017
1018 #endif // wxUSE_LOG_DIALOG
1019
1020 #if CAN_SAVE_FILES
1021
1022 // pass an uninitialized file object, the function will ask the user for the
1023 // filename and try to open it, returns true on success (file was opened),
1024 // false if file couldn't be opened/created and -1 if the file selection
1025 // dialog was cancelled
1026 static int OpenLogFile(wxFile& file, wxString *pFilename, wxWindow *parent)
1027 {
1028 // get the file name
1029 // -----------------
1030 wxString filename = wxSaveFileSelector(wxT("log"), wxT("txt"), wxT("log.txt"), parent);
1031 if ( !filename ) {
1032 // cancelled
1033 return -1;
1034 }
1035
1036 // open file
1037 // ---------
1038 bool bOk = true; // suppress warning about it being possible uninitialized
1039 if ( wxFile::Exists(filename) ) {
1040 bool bAppend = false;
1041 wxString strMsg;
1042 strMsg.Printf(_("Append log to file '%s' (choosing [No] will overwrite it)?"),
1043 filename.c_str());
1044 switch ( wxMessageBox(strMsg, _("Question"),
1045 wxICON_QUESTION | wxYES_NO | wxCANCEL) ) {
1046 case wxYES:
1047 bAppend = true;
1048 break;
1049
1050 case wxNO:
1051 bAppend = false;
1052 break;
1053
1054 case wxCANCEL:
1055 return -1;
1056
1057 default:
1058 wxFAIL_MSG(_("invalid message box return value"));
1059 }
1060
1061 if ( bAppend ) {
1062 bOk = file.Open(filename, wxFile::write_append);
1063 }
1064 else {
1065 bOk = file.Create(filename, true /* overwrite */);
1066 }
1067 }
1068 else {
1069 bOk = file.Create(filename);
1070 }
1071
1072 if ( pFilename )
1073 *pFilename = filename;
1074
1075 return bOk;
1076 }
1077
1078 #endif // CAN_SAVE_FILES
1079
1080 #endif // !(wxUSE_LOGGUI || wxUSE_LOGWINDOW)
1081
1082 #if wxUSE_LOG && wxUSE_TEXTCTRL
1083
1084 // ----------------------------------------------------------------------------
1085 // wxLogTextCtrl implementation
1086 // ----------------------------------------------------------------------------
1087
1088 wxLogTextCtrl::wxLogTextCtrl(wxTextCtrl *pTextCtrl)
1089 {
1090 m_pTextCtrl = pTextCtrl;
1091 }
1092
1093 void wxLogTextCtrl::DoLogText(const wxString& msg)
1094 {
1095 m_pTextCtrl->AppendText(msg + wxS('\n'));
1096 }
1097
1098 #endif // wxUSE_LOG && wxUSE_TEXTCTRL