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