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