]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/generic/logg.cpp
Document wxDateTime::GregorianAdoption enum and TimeZone class.
[wxWidgets.git] / src / generic / logg.cpp
... / ...
CommitLineData
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
91static 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
104class wxLogDialog : public wxDialog
105{
106public:
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
125private:
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
169BEGIN_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)
178END_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
192static 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
206wxLogGui::wxLogGui()
207{
208 Clear();
209}
210
211void 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
222int wxLogGui::GetSeverityIcon() const
223{
224 return m_bErrors ? wxICON_STOP
225 : m_bWarnings ? wxICON_EXCLAMATION
226 : wxICON_INFORMATION;
227}
228
229wxString 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
253void
254wxLogGui::DoShowSingleLogMessage(const wxString& message,
255 const wxString& title,
256 int style)
257{
258 wxMessageBox(message, title, wxOK | style);
259}
260
261void
262wxLogGui::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
291void 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
347void 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// ---------------
448class wxLogFrame : public wxFrame
449{
450public:
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
469private:
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
488BEGIN_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)
497END_EVENT_TABLE()
498
499wxLogFrame::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
537void 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
547void wxLogFrame::OnClose(wxCommandEvent& WXUNUSED(event))
548{
549 DoClose();
550}
551
552void wxLogFrame::OnCloseWindow(wxCloseEvent& WXUNUSED(event))
553{
554 DoClose();
555}
556
557#if CAN_SAVE_FILES
558void 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
591void wxLogFrame::OnClear(wxCommandEvent& WXUNUSED(event))
592{
593 m_pTextCtrl->Clear();
594}
595
596wxLogFrame::~wxLogFrame()
597{
598 m_log->OnFrameDelete(this);
599}
600
601// wxLogWindow
602// -----------
603
604wxLogWindow::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
622void wxLogWindow::Show(bool bShow)
623{
624 m_pLogFrame->Show(bShow);
625}
626
627void 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
643wxFrame *wxLogWindow::GetFrame() const
644{
645 return m_pLogFrame;
646}
647
648void wxLogWindow::OnFrameCreate(wxFrame * WXUNUSED(frame))
649{
650}
651
652bool wxLogWindow::OnFrameClose(wxFrame * WXUNUSED(frame))
653{
654 // allow to close
655 return true;
656}
657
658void wxLogWindow::OnFrameDelete(wxFrame * WXUNUSED(frame))
659{
660 m_pLogFrame = NULL;
661}
662
663wxLogWindow::~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
677wxString wxLogDialog::ms_details;
678size_t wxLogDialog::ms_maxLength = 0;
679
680wxLogDialog::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
803void 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
920void 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
937void wxLogDialog::OnOk(wxCommandEvent& WXUNUSED(event))
938{
939 EndModal(wxID_OK);
940}
941
942#if CAN_SAVE_FILES || wxUSE_CLIPBOARD
943
944wxString 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
972void 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
986void 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
1004wxLogDialog::~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
1020static 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
1082wxLogTextCtrl::wxLogTextCtrl(wxTextCtrl *pTextCtrl)
1083{
1084 m_pTextCtrl = pTextCtrl;
1085}
1086
1087void wxLogTextCtrl::DoLogText(const wxString& msg)
1088{
1089 m_pTextCtrl->AppendText(msg + wxS('\n'));
1090}
1091
1092#endif // wxUSE_LOG && wxUSE_TEXTCTRL