]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/generic/logg.cpp
removed superfluous ;
[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/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
91static 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
108class wxLogDialog : public wxDialog
109{
110public:
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
129private:
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
173BEGIN_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)
182END_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
196static 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
210wxLogGui::wxLogGui()
211{
212 Clear();
213}
214
215void 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
226int wxLogGui::GetSeverityIcon() const
227{
228 return m_bErrors ? wxICON_STOP
229 : m_bWarnings ? wxICON_EXCLAMATION
230 : wxICON_INFORMATION;
231}
232
233wxString 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
257void
258wxLogGui::DoShowSingleLogMessage(const wxString& message,
259 const wxString& title,
260 int style)
261{
262 wxMessageBox(message, title, wxOK | style);
263}
264
265void
266wxLogGui::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
295void 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
351void 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 default:
421 // let the base class deal with debug/trace messages as well as any
422 // custom levels
423 wxLog::DoLogRecord(level, msg, info);
424 }
425}
426
427#endif // wxUSE_LOGGUI
428
429// ----------------------------------------------------------------------------
430// wxLogWindow and wxLogFrame implementation
431// ----------------------------------------------------------------------------
432
433#if wxUSE_LOGWINDOW
434
435// log frame class
436// ---------------
437class wxLogFrame : public wxFrame
438{
439public:
440 // ctor & dtor
441 wxLogFrame(wxWindow *pParent, wxLogWindow *log, const wxString& szTitle);
442 virtual ~wxLogFrame();
443
444 // menu callbacks
445 void OnClose(wxCommandEvent& event);
446 void OnCloseWindow(wxCloseEvent& event);
447#if CAN_SAVE_FILES
448 void OnSave(wxCommandEvent& event);
449#endif // CAN_SAVE_FILES
450 void OnClear(wxCommandEvent& event);
451
452 // do show the message in the text control
453 void ShowLogMessage(const wxString& message)
454 {
455 m_pTextCtrl->AppendText(message + wxS('\n'));
456 }
457
458private:
459 // use standard ids for our commands!
460 enum
461 {
462 Menu_Close = wxID_CLOSE,
463 Menu_Save = wxID_SAVE,
464 Menu_Clear = wxID_CLEAR
465 };
466
467 // common part of OnClose() and OnCloseWindow()
468 void DoClose();
469
470 wxTextCtrl *m_pTextCtrl;
471 wxLogWindow *m_log;
472
473 DECLARE_EVENT_TABLE()
474 wxDECLARE_NO_COPY_CLASS(wxLogFrame);
475};
476
477BEGIN_EVENT_TABLE(wxLogFrame, wxFrame)
478 // wxLogWindow menu events
479 EVT_MENU(Menu_Close, wxLogFrame::OnClose)
480#if CAN_SAVE_FILES
481 EVT_MENU(Menu_Save, wxLogFrame::OnSave)
482#endif // CAN_SAVE_FILES
483 EVT_MENU(Menu_Clear, wxLogFrame::OnClear)
484
485 EVT_CLOSE(wxLogFrame::OnCloseWindow)
486END_EVENT_TABLE()
487
488wxLogFrame::wxLogFrame(wxWindow *pParent, wxLogWindow *log, const wxString& szTitle)
489 : wxFrame(pParent, wxID_ANY, szTitle)
490{
491 m_log = log;
492
493 m_pTextCtrl = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition,
494 wxDefaultSize,
495 wxTE_MULTILINE |
496 wxHSCROLL |
497 // needed for Win32 to avoid 65Kb limit but it doesn't work well
498 // when using RichEdit 2.0 which we always do in the Unicode build
499#if !wxUSE_UNICODE
500 wxTE_RICH |
501#endif // !wxUSE_UNICODE
502 wxTE_READONLY);
503
504#if wxUSE_MENUS
505 // create menu
506 wxMenuBar *pMenuBar = new wxMenuBar;
507 wxMenu *pMenu = new wxMenu;
508#if CAN_SAVE_FILES
509 pMenu->Append(Menu_Save, _("&Save..."), _("Save log contents to file"));
510#endif // CAN_SAVE_FILES
511 pMenu->Append(Menu_Clear, _("C&lear"), _("Clear the log contents"));
512 pMenu->AppendSeparator();
513 pMenu->Append(Menu_Close, _("&Close"), _("Close this window"));
514 pMenuBar->Append(pMenu, _("&Log"));
515 SetMenuBar(pMenuBar);
516#endif // wxUSE_MENUS
517
518#if wxUSE_STATUSBAR
519 // status bar for menu prompts
520 CreateStatusBar();
521#endif // wxUSE_STATUSBAR
522
523 m_log->OnFrameCreate(this);
524}
525
526void wxLogFrame::DoClose()
527{
528 if ( m_log->OnFrameClose(this) )
529 {
530 // instead of closing just hide the window to be able to Show() it
531 // later
532 Show(false);
533 }
534}
535
536void wxLogFrame::OnClose(wxCommandEvent& WXUNUSED(event))
537{
538 DoClose();
539}
540
541void wxLogFrame::OnCloseWindow(wxCloseEvent& WXUNUSED(event))
542{
543 DoClose();
544}
545
546#if CAN_SAVE_FILES
547void wxLogFrame::OnSave(wxCommandEvent& WXUNUSED(event))
548{
549 wxString filename;
550 wxFile file;
551 int rc = OpenLogFile(file, &filename, this);
552 if ( rc == -1 )
553 {
554 // cancelled
555 return;
556 }
557
558 bool bOk = rc != 0;
559
560 // retrieve text and save it
561 // -------------------------
562 int nLines = m_pTextCtrl->GetNumberOfLines();
563 for ( int nLine = 0; bOk && nLine < nLines; nLine++ ) {
564 bOk = file.Write(m_pTextCtrl->GetLineText(nLine) +
565 wxTextFile::GetEOL());
566 }
567
568 if ( bOk )
569 bOk = file.Close();
570
571 if ( !bOk ) {
572 wxLogError(_("Can't save log contents to file."));
573 }
574 else {
575 wxLogStatus((wxFrame*)this, _("Log saved to the file '%s'."), filename.c_str());
576 }
577}
578#endif // CAN_SAVE_FILES
579
580void wxLogFrame::OnClear(wxCommandEvent& WXUNUSED(event))
581{
582 m_pTextCtrl->Clear();
583}
584
585wxLogFrame::~wxLogFrame()
586{
587 m_log->OnFrameDelete(this);
588}
589
590// wxLogWindow
591// -----------
592
593wxLogWindow::wxLogWindow(wxWindow *pParent,
594 const wxString& szTitle,
595 bool bShow,
596 bool bDoPass)
597{
598 PassMessages(bDoPass);
599
600 m_pLogFrame = new wxLogFrame(pParent, this, szTitle);
601
602 if ( bShow )
603 m_pLogFrame->Show();
604}
605
606void wxLogWindow::Show(bool bShow)
607{
608 m_pLogFrame->Show(bShow);
609}
610
611void wxLogWindow::DoLogTextAtLevel(wxLogLevel level, const wxString& msg)
612{
613 // first let the previous logger show it
614 wxLogPassThrough::DoLogTextAtLevel(level, msg);
615
616 if ( !m_pLogFrame )
617 return;
618
619 // don't put trace messages in the text window for 2 reasons:
620 // 1) there are too many of them
621 // 2) they may provoke other trace messages (e.g. wxMSW code uses
622 // wxLogTrace to log Windows messages and adding text to the control
623 // sends more of them) thus sending a program into an infinite loop
624 if ( level == wxLOG_Trace )
625 return;
626
627 m_pLogFrame->ShowLogMessage(msg);
628}
629
630wxFrame *wxLogWindow::GetFrame() const
631{
632 return m_pLogFrame;
633}
634
635void wxLogWindow::OnFrameCreate(wxFrame * WXUNUSED(frame))
636{
637}
638
639bool wxLogWindow::OnFrameClose(wxFrame * WXUNUSED(frame))
640{
641 // allow to close
642 return true;
643}
644
645void wxLogWindow::OnFrameDelete(wxFrame * WXUNUSED(frame))
646{
647 m_pLogFrame = NULL;
648}
649
650wxLogWindow::~wxLogWindow()
651{
652 // may be NULL if log frame already auto destroyed itself
653 delete m_pLogFrame;
654}
655
656#endif // wxUSE_LOGWINDOW
657
658// ----------------------------------------------------------------------------
659// wxLogDialog
660// ----------------------------------------------------------------------------
661
662#if wxUSE_LOG_DIALOG
663
664wxString wxLogDialog::ms_details;
665size_t wxLogDialog::ms_maxLength = 0;
666
667wxLogDialog::wxLogDialog(wxWindow *parent,
668 const wxArrayString& messages,
669 const wxArrayInt& severity,
670 const wxArrayLong& times,
671 const wxString& caption,
672 long style)
673 : wxDialog(parent, wxID_ANY, caption,
674 wxDefaultPosition, wxDefaultSize,
675 wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
676{
677 // init the static variables:
678
679 if ( ms_details.empty() )
680 {
681 // ensure that we won't loop here if wxGetTranslation()
682 // happens to pop up a Log message while translating this :-)
683 ms_details = wxTRANSLATE("&Details");
684 ms_details = wxGetTranslation(ms_details);
685#ifdef __SMARTPHONE__
686 ms_details = wxStripMenuCodes(ms_details);
687#endif
688 }
689
690 if ( ms_maxLength == 0 )
691 {
692 ms_maxLength = (2 * wxGetDisplaySize().x/3) / GetCharWidth();
693 }
694
695 size_t count = messages.GetCount();
696 m_messages.Alloc(count);
697 m_severity.Alloc(count);
698 m_times.Alloc(count);
699
700 for ( size_t n = 0; n < count; n++ )
701 {
702 m_messages.Add(messages[n]);
703 m_severity.Add(severity[n]);
704 m_times.Add(times[n]);
705 }
706
707 m_listctrl = NULL;
708
709 bool isPda = (wxSystemSettings::GetScreenType() <= wxSYS_SCREEN_PDA);
710
711 // create the controls which are always shown and layout them: we use
712 // sizers even though our window is not resizeable to calculate the size of
713 // the dialog properly
714 wxBoxSizer *sizerTop = new wxBoxSizer(wxVERTICAL);
715 wxBoxSizer *sizerAll = new wxBoxSizer(isPda ? wxVERTICAL : wxHORIZONTAL);
716
717 if (!isPda)
718 {
719 wxStaticBitmap *icon = new wxStaticBitmap
720 (
721 this,
722 wxID_ANY,
723 wxArtProvider::GetMessageBoxIcon(style)
724 );
725 sizerAll->Add(icon, wxSizerFlags().Centre());
726 }
727
728 // create the text sizer with a minimal size so that we are sure it won't be too small
729 wxString message = EllipsizeString(messages.Last());
730 wxSizer *szText = CreateTextSizer(message);
731 szText->SetMinSize(wxMin(300, wxGetDisplaySize().x / 3), -1);
732
733 sizerAll->Add(szText, wxSizerFlags(1).Centre().Border(wxLEFT | wxRIGHT));
734
735 wxButton *btnOk = new wxButton(this, wxID_OK);
736 sizerAll->Add(btnOk, wxSizerFlags().Centre());
737
738 sizerTop->Add(sizerAll, wxSizerFlags().Expand().Border());
739
740
741 // add the details pane
742#ifndef __SMARTPHONE__
743 wxCollapsiblePane * const
744 collpane = new wxCollapsiblePane(this, wxID_ANY, ms_details);
745 sizerTop->Add(collpane, wxSizerFlags(1).Expand().Border());
746
747 wxWindow *win = collpane->GetPane();
748 wxSizer * const paneSz = new wxBoxSizer(wxVERTICAL);
749
750 CreateDetailsControls(win);
751
752 paneSz->Add(m_listctrl, wxSizerFlags(1).Expand().Border(wxTOP));
753
754#if wxUSE_CLIPBOARD || CAN_SAVE_FILES
755 wxBoxSizer * const btnSizer = new wxBoxSizer(wxHORIZONTAL);
756
757 wxSizerFlags flagsBtn;
758 flagsBtn.Border(wxLEFT);
759
760#if wxUSE_CLIPBOARD
761 btnSizer->Add(new wxButton(win, wxID_COPY), flagsBtn);
762#endif // wxUSE_CLIPBOARD
763
764#if CAN_SAVE_FILES
765 btnSizer->Add(new wxButton(win, wxID_SAVE), flagsBtn);
766#endif // CAN_SAVE_FILES
767
768 paneSz->Add(btnSizer, wxSizerFlags().Right().Border(wxTOP));
769#endif // wxUSE_CLIPBOARD || CAN_SAVE_FILES
770
771 win->SetSizer(paneSz);
772 paneSz->SetSizeHints(win);
773#else // __SMARTPHONE__
774 SetLeftMenu(wxID_OK);
775 SetRightMenu(wxID_MORE, ms_details + EXPAND_SUFFIX);
776#endif // __SMARTPHONE__/!__SMARTPHONE__
777
778 SetSizerAndFit(sizerTop);
779
780 Centre();
781
782 if (isPda)
783 {
784 // Move up the screen so that when we expand the dialog,
785 // there's enough space.
786 Move(wxPoint(GetPosition().x, GetPosition().y / 2));
787 }
788}
789
790void wxLogDialog::CreateDetailsControls(wxWindow *parent)
791{
792 wxString fmt = wxLog::GetTimestamp();
793 bool hasTimeStamp = !fmt.IsEmpty();
794
795 // create the list ctrl now
796 m_listctrl = new wxListCtrl(parent, wxID_ANY,
797 wxDefaultPosition, wxDefaultSize,
798 wxSUNKEN_BORDER |
799 wxLC_REPORT |
800 wxLC_NO_HEADER |
801 wxLC_SINGLE_SEL);
802#ifdef __WXWINCE__
803 // This makes a big aesthetic difference on WinCE but I
804 // don't want to risk problems on other platforms
805 m_listctrl->Hide();
806#endif
807
808 // no need to translate these strings as they're not shown to the
809 // user anyhow (we use wxLC_NO_HEADER style)
810 m_listctrl->InsertColumn(0, wxT("Message"));
811
812 if (hasTimeStamp)
813 m_listctrl->InsertColumn(1, wxT("Time"));
814
815 // prepare the imagelist
816 static const int ICON_SIZE = 16;
817 wxImageList *imageList = new wxImageList(ICON_SIZE, ICON_SIZE);
818
819 // order should be the same as in the switch below!
820 static const wxChar* icons[] =
821 {
822 wxART_ERROR,
823 wxART_WARNING,
824 wxART_INFORMATION
825 };
826
827 bool loadedIcons = true;
828
829 for ( size_t icon = 0; icon < WXSIZEOF(icons); icon++ )
830 {
831 wxBitmap bmp = wxArtProvider::GetBitmap(icons[icon], wxART_MESSAGE_BOX,
832 wxSize(ICON_SIZE, ICON_SIZE));
833
834 // This may very well fail if there are insufficient colours available.
835 // Degrade gracefully.
836 if ( !bmp.Ok() )
837 {
838 loadedIcons = false;
839
840 break;
841 }
842
843 imageList->Add(bmp);
844 }
845
846 m_listctrl->SetImageList(imageList, wxIMAGE_LIST_SMALL);
847
848 // fill the listctrl
849 size_t count = m_messages.GetCount();
850 for ( size_t n = 0; n < count; n++ )
851 {
852 int image;
853
854 if ( loadedIcons )
855 {
856 switch ( m_severity[n] )
857 {
858 case wxLOG_Error:
859 image = 0;
860 break;
861
862 case wxLOG_Warning:
863 image = 1;
864 break;
865
866 default:
867 image = 2;
868 }
869 }
870 else // failed to load images
871 {
872 image = -1;
873 }
874
875 wxString msg = m_messages[n];
876 msg.Replace(wxT("\n"), wxT(" "));
877 msg = EllipsizeString(msg);
878
879 m_listctrl->InsertItem(n, msg, image);
880
881 if (hasTimeStamp)
882 m_listctrl->SetItem(n, 1, TimeStamp(fmt, (time_t)m_times[n]));
883 }
884
885 // let the columns size themselves
886 m_listctrl->SetColumnWidth(0, wxLIST_AUTOSIZE);
887 if (hasTimeStamp)
888 m_listctrl->SetColumnWidth(1, wxLIST_AUTOSIZE);
889
890 // calculate an approximately nice height for the listctrl
891 int height = GetCharHeight()*(count + 4);
892
893 // but check that the dialog won't fall fown from the screen
894 //
895 // we use GetMinHeight() to get the height of the dialog part without the
896 // details and we consider that the "Save" button below and the separator
897 // line (and the margins around it) take about as much, hence double it
898 int heightMax = wxGetDisplaySize().y - GetPosition().y - 2*GetMinHeight();
899
900 // we should leave a margin
901 heightMax *= 9;
902 heightMax /= 10;
903
904 m_listctrl->SetSize(wxDefaultCoord, wxMin(height, heightMax));
905}
906
907void wxLogDialog::OnListItemActivated(wxListEvent& event)
908{
909 // show the activated item in a message box
910 // This allow the user to correctly display the logs which are longer
911 // than the listctrl and thus gets truncated or those which contains
912 // newlines.
913
914 // NB: don't do:
915 // wxString str = m_listctrl->GetItemText(event.GetIndex());
916 // as there's a 260 chars limit on the items inside a wxListCtrl in wxMSW.
917 wxString str = m_messages[event.GetIndex()];
918
919 // wxMessageBox will nicely handle the '\n' in the string (if any)
920 // and supports long strings
921 wxMessageBox(str, wxT("Log message"), wxOK, this);
922}
923
924void wxLogDialog::OnOk(wxCommandEvent& WXUNUSED(event))
925{
926 EndModal(wxID_OK);
927}
928
929#if CAN_SAVE_FILES || wxUSE_CLIPBOARD
930
931wxString wxLogDialog::GetLogMessages() const
932{
933 wxString fmt = wxLog::GetTimestamp();
934 if ( fmt.empty() )
935 {
936 // use the default format
937 fmt = "%c";
938 }
939
940 const size_t count = m_messages.GetCount();
941
942 wxString text;
943 text.reserve(count*m_messages[0].length());
944 for ( size_t n = 0; n < count; n++ )
945 {
946 text << TimeStamp(fmt, (time_t)m_times[n])
947 << ": "
948 << m_messages[n]
949 << wxTextFile::GetEOL();
950 }
951
952 return text;
953}
954
955#endif // CAN_SAVE_FILES || wxUSE_CLIPBOARD
956
957#if wxUSE_CLIPBOARD
958
959void wxLogDialog::OnCopy(wxCommandEvent& WXUNUSED(event))
960{
961 wxClipboardLocker clip;
962 if ( !clip ||
963 !wxTheClipboard->AddData(new wxTextDataObject(GetLogMessages())) )
964 {
965 wxLogError(_("Failed to copy dialog contents to the clipboard."));
966 }
967}
968
969#endif // wxUSE_CLIPBOARD
970
971#if CAN_SAVE_FILES
972
973void wxLogDialog::OnSave(wxCommandEvent& WXUNUSED(event))
974{
975 wxFile file;
976 int rc = OpenLogFile(file, NULL, this);
977 if ( rc == -1 )
978 {
979 // cancelled
980 return;
981 }
982
983 if ( !rc || !file.Write(GetLogMessages()) || !file.Close() )
984 {
985 wxLogError(_("Can't save log contents to file."));
986 }
987}
988
989#endif // CAN_SAVE_FILES
990
991wxLogDialog::~wxLogDialog()
992{
993 if ( m_listctrl )
994 {
995 delete m_listctrl->GetImageList(wxIMAGE_LIST_SMALL);
996 }
997}
998
999#endif // wxUSE_LOG_DIALOG
1000
1001#if CAN_SAVE_FILES
1002
1003// pass an uninitialized file object, the function will ask the user for the
1004// filename and try to open it, returns true on success (file was opened),
1005// false if file couldn't be opened/created and -1 if the file selection
1006// dialog was cancelled
1007static int OpenLogFile(wxFile& file, wxString *pFilename, wxWindow *parent)
1008{
1009 // get the file name
1010 // -----------------
1011 wxString filename = wxSaveFileSelector(wxT("log"), wxT("txt"), wxT("log.txt"), parent);
1012 if ( !filename ) {
1013 // cancelled
1014 return -1;
1015 }
1016
1017 // open file
1018 // ---------
1019 bool bOk = true; // suppress warning about it being possible uninitialized
1020 if ( wxFile::Exists(filename) ) {
1021 bool bAppend = false;
1022 wxString strMsg;
1023 strMsg.Printf(_("Append log to file '%s' (choosing [No] will overwrite it)?"),
1024 filename.c_str());
1025 switch ( wxMessageBox(strMsg, _("Question"),
1026 wxICON_QUESTION | wxYES_NO | wxCANCEL) ) {
1027 case wxYES:
1028 bAppend = true;
1029 break;
1030
1031 case wxNO:
1032 bAppend = false;
1033 break;
1034
1035 case wxCANCEL:
1036 return -1;
1037
1038 default:
1039 wxFAIL_MSG(_("invalid message box return value"));
1040 }
1041
1042 if ( bAppend ) {
1043 bOk = file.Open(filename, wxFile::write_append);
1044 }
1045 else {
1046 bOk = file.Create(filename, true /* overwrite */);
1047 }
1048 }
1049 else {
1050 bOk = file.Create(filename);
1051 }
1052
1053 if ( pFilename )
1054 *pFilename = filename;
1055
1056 return bOk;
1057}
1058
1059#endif // CAN_SAVE_FILES
1060
1061#endif // !(wxUSE_LOGGUI || wxUSE_LOGWINDOW)
1062
1063#if wxUSE_LOG && wxUSE_TEXTCTRL
1064
1065// ----------------------------------------------------------------------------
1066// wxLogTextCtrl implementation
1067// ----------------------------------------------------------------------------
1068
1069wxLogTextCtrl::wxLogTextCtrl(wxTextCtrl *pTextCtrl)
1070{
1071 m_pTextCtrl = pTextCtrl;
1072}
1073
1074void wxLogTextCtrl::DoLogText(const wxString& msg)
1075{
1076 m_pTextCtrl->AppendText(msg + wxS('\n'));
1077}
1078
1079#endif // wxUSE_LOG && wxUSE_TEXTCTRL