]> git.saurik.com Git - wxWidgets.git/blame - src/generic/logg.cpp
Use string column identifiers with NSTableColumns in wxDataViewCtrl.
[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
2abc54ca 76#include "wx/time.h"
1aaf88d2 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
VS
92{
93 wxChar buf[4096];
83e8b44c
VZ
94 struct tm tm;
95 if ( !wxStrftime(buf, WXSIZEOF(buf), format, wxLocaltime_r(&t, &tm)) )
f94dfb38
VS
96 {
97 // buffer is too small?
9a83f860 98 wxFAIL_MSG(wxT("strftime() failed"));
f94dfb38
VS
99 }
100 return wxString(buf);
101}
102
103
f1df0927
VZ
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);
b6d2b072
VZ
117#if wxUSE_CLIPBOARD
118 void OnCopy(wxCommandEvent& event);
119#endif // wxUSE_CLIPBOARD
120#if CAN_SAVE_FILES
f6bcfd97 121 void OnSave(wxCommandEvent& event);
b6d2b072 122#endif // CAN_SAVE_FILES
a14670e2 123 void OnListItemActivated(wxListEvent& event);
f1df0927
VZ
124
125private:
f6bcfd97 126 // create controls needed for the details display
a14670e2
VZ
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 }
f6bcfd97 143
b6d2b072
VZ
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
f1df0927 150 // the data for the listctrl
224ff7a5
VZ
151 wxArrayString m_messages;
152 wxArrayInt m_severity;
153 wxArrayLong m_times;
f1df0927 154
f6bcfd97
BP
155 // the controls which are not shown initially (but only when details
156 // button is pressed)
f1df0927
VZ
157 wxListCtrl *m_listctrl;
158
5f170f33
VZ
159 // the translated "Details" string
160 static wxString ms_details;
161
a14670e2
VZ
162 // the maximum length of the log message
163 static size_t ms_maxLength;
164
f1df0927 165 DECLARE_EVENT_TABLE()
c0c133e1 166 wxDECLARE_NO_COPY_CLASS(wxLogDialog);
f1df0927
VZ
167};
168
169BEGIN_EVENT_TABLE(wxLogDialog, wxDialog)
eb3e6de3 170 EVT_BUTTON(wxID_OK, wxLogDialog::OnOk)
b6d2b072
VZ
171#if wxUSE_CLIPBOARD
172 EVT_BUTTON(wxID_COPY, wxLogDialog::OnCopy)
173#endif // wxUSE_CLIPBOARD
174#if CAN_SAVE_FILES
f6bcfd97 175 EVT_BUTTON(wxID_SAVE, wxLogDialog::OnSave)
b6d2b072 176#endif // CAN_SAVE_FILES
a14670e2 177 EVT_LIST_ITEM_ACTIVATED(wxID_ANY, wxLogDialog::OnListItemActivated)
f1df0927
VZ
178END_EVENT_TABLE()
179
180#endif // wxUSE_LOG_DIALOG
181
f6bcfd97
BP
182// ----------------------------------------------------------------------------
183// private functions
184// ----------------------------------------------------------------------------
185
b6d2b072 186#if CAN_SAVE_FILES
f6bcfd97
BP
187
188// pass an uninitialized file object, the function will ask the user for the
dabbc6a5
DS
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
f6bcfd97 191// dialog was cancelled
73ff84ff 192static int OpenLogFile(wxFile& file, wxString *filename = NULL, wxWindow *parent = NULL);
f6bcfd97 193
b6d2b072 194#endif // CAN_SAVE_FILES
f6bcfd97 195
dd85fc6b
VZ
196// ============================================================================
197// implementation
198// ============================================================================
199
dd85fc6b
VZ
200// ----------------------------------------------------------------------------
201// wxLogGui implementation (FIXME MT-unsafe)
202// ----------------------------------------------------------------------------
203
461dae94
VZ
204#if wxUSE_LOGGUI
205
dd85fc6b
VZ
206wxLogGui::wxLogGui()
207{
208 Clear();
209}
210
211void wxLogGui::Clear()
212{
5f170f33
VZ
213 m_bErrors =
214 m_bWarnings =
dabbc6a5 215 m_bHasMessages = false;
5f170f33 216
dd85fc6b 217 m_aMessages.Empty();
f1df0927 218 m_aSeverity.Empty();
dd85fc6b
VZ
219 m_aTimes.Empty();
220}
221
9ad2fe62
VZ
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;
f946f8bb 281 const size_t nMsgCount = messages.size();
14fd5e96 282 message.reserve(nMsgCount*100);
9ad2fe62
VZ
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
dd85fc6b
VZ
291void wxLogGui::Flush()
292{
232addd1
VZ
293 wxLog::Flush();
294
dd85fc6b
VZ
295 if ( !m_bHasMessages )
296 return;
297
298 // do it right now to block any new calls to Flush() while we're here
dabbc6a5 299 m_bHasMessages = false;
dd85fc6b 300
9ad2fe62
VZ
301 // note that this must be done before examining m_aMessages as it may log
302 // yet another message
dbaa16de 303 const unsigned repeatCount = LogLastRepeatIfNeeded();
f9837791 304
9ad2fe62 305 const size_t nMsgCount = m_aMessages.size();
f1df0927 306
9ad2fe62
VZ
307 if ( repeatCount > 0 )
308 {
309 m_aMessages[nMsgCount - 1] << " (" << m_aMessages[nMsgCount - 2] << ")";
f1df0927 310 }
1e6feb95 311
9ad2fe62
VZ
312 const wxString title = GetTitle();
313 const int style = GetSeverityIcon();
224ff7a5 314
69c916e9
VZ
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
23f681ec
VZ
319 if ( nMsgCount == 1 )
320 {
9ad2fe62
VZ
321 // make a copy before calling Clear()
322 const wxString message(m_aMessages[0]);
323 Clear();
324
325 DoShowSingleLogMessage(message, title, style);
23f681ec
VZ
326 }
327 else // more than one message
328 {
9ad2fe62
VZ
329 wxArrayString messages;
330 wxArrayInt severities;
331 wxArrayLong times;
0250efd6 332
9ad2fe62
VZ
333 messages.swap(m_aMessages);
334 severities.swap(m_aSeverity);
335 times.swap(m_aTimes);
f1df0927 336
23f681ec
VZ
337 Clear();
338
9ad2fe62 339 DoShowMultipleLogMessages(messages, severities, times, title, style);
23f681ec 340 }
69c916e9
VZ
341
342 // allow flushing the logs again
343 Resume();
dd85fc6b
VZ
344}
345
23f681ec 346// log all kinds of messages
bc73d5ae
VZ
347void wxLogGui::DoLogRecord(wxLogLevel level,
348 const wxString& msg,
349 const wxLogRecordInfo& info)
dd85fc6b 350{
bc73d5ae
VZ
351 switch ( level )
352 {
dd85fc6b
VZ
353 case wxLOG_Info:
354 if ( GetVerbose() )
355 case wxLOG_Message:
f1df0927 356 {
bc73d5ae 357 m_aMessages.Add(msg);
a250392c 358 m_aSeverity.Add(wxLOG_Message);
bc73d5ae 359 m_aTimes.Add((long)info.timestamp);
dabbc6a5 360 m_bHasMessages = true;
f1df0927
VZ
361 }
362 break;
dd85fc6b
VZ
363
364 case wxLOG_Status:
365#if wxUSE_STATUSBAR
f1df0927 366 {
af588446
VZ
367 wxFrame *pFrame = NULL;
368
369 // check if the frame was passed to us explicitly
958f8790 370 wxUIntPtr ptr = 0;
af588446
VZ
371 if ( info.GetNumValue(wxLOG_KEY_FRAME, &ptr) )
372 {
373 pFrame = static_cast<wxFrame *>(wxUIntToPtr(ptr));
374 }
375
f1df0927 376 // find the top window and set it's status text if it has any
f1df0927
VZ
377 if ( pFrame == NULL ) {
378 wxWindow *pWin = wxTheApp->GetTopWindow();
379 if ( pWin != NULL && pWin->IsKindOf(CLASSINFO(wxFrame)) ) {
380 pFrame = (wxFrame *)pWin;
dd85fc6b 381 }
dd85fc6b 382 }
f1df0927
VZ
383
384 if ( pFrame && pFrame->GetStatusBar() )
bc73d5ae 385 pFrame->SetStatusText(msg);
f1df0927 386 }
dd85fc6b 387#endif // wxUSE_STATUSBAR
f1df0927 388 break;
dd85fc6b 389
dd85fc6b 390 case wxLOG_Error:
f1df0927
VZ
391 if ( !m_bErrors ) {
392#if !wxUSE_LOG_DIALOG
dd85fc6b 393 // discard earlier informational messages if this is the 1st
f1df0927
VZ
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
dabbc6a5 400 m_bErrors = true;
f1df0927
VZ
401 }
402 // fall through
dd85fc6b
VZ
403
404 case wxLOG_Warning:
f1df0927
VZ
405 if ( !m_bErrors ) {
406 // for the warning we don't discard the info messages
dabbc6a5 407 m_bWarnings = true;
f1df0927
VZ
408 }
409
bc73d5ae 410 m_aMessages.Add(msg);
06b466c7 411 m_aSeverity.Add((int)level);
bc73d5ae 412 m_aTimes.Add((long)info.timestamp);
dabbc6a5 413 m_bHasMessages = true;
f1df0927 414 break;
711f12ef 415
6900a897
VZ
416 case wxLOG_Debug:
417 case wxLOG_Trace:
418 // let the base class deal with debug/trace messages
bc73d5ae 419 wxLog::DoLogRecord(level, msg, info);
6900a897
VZ
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;
dd85fc6b
VZ
435 }
436}
437
461dae94
VZ
438#endif // wxUSE_LOGGUI
439
dd85fc6b
VZ
440// ----------------------------------------------------------------------------
441// wxLogWindow and wxLogFrame implementation
442// ----------------------------------------------------------------------------
443
c54a8b64
VZ
444#if wxUSE_LOGWINDOW
445
dd85fc6b
VZ
446// log frame class
447// ---------------
448class wxLogFrame : public wxFrame
449{
450public:
451 // ctor & dtor
46ff9bd5 452 wxLogFrame(wxWindow *pParent, wxLogWindow *log, const wxString& szTitle);
dd85fc6b
VZ
453 virtual ~wxLogFrame();
454
455 // menu callbacks
456 void OnClose(wxCommandEvent& event);
457 void OnCloseWindow(wxCloseEvent& event);
b6d2b072
VZ
458#if CAN_SAVE_FILES
459 void OnSave(wxCommandEvent& event);
460#endif // CAN_SAVE_FILES
dd85fc6b
VZ
461 void OnClear(wxCommandEvent& event);
462
232addd1
VZ
463 // do show the message in the text control
464 void ShowLogMessage(const wxString& message)
465 {
466 m_pTextCtrl->AppendText(message + wxS('\n'));
467 }
dd85fc6b
VZ
468
469private:
857eb081 470 // use standard ids for our commands!
dd85fc6b
VZ
471 enum
472 {
857eb081
VZ
473 Menu_Close = wxID_CLOSE,
474 Menu_Save = wxID_SAVE,
475 Menu_Clear = wxID_CLEAR
dd85fc6b
VZ
476 };
477
f6bcfd97
BP
478 // common part of OnClose() and OnCloseWindow()
479 void DoClose();
dd85fc6b
VZ
480
481 wxTextCtrl *m_pTextCtrl;
482 wxLogWindow *m_log;
483
484 DECLARE_EVENT_TABLE()
c0c133e1 485 wxDECLARE_NO_COPY_CLASS(wxLogFrame);
dd85fc6b
VZ
486};
487
488BEGIN_EVENT_TABLE(wxLogFrame, wxFrame)
489 // wxLogWindow menu events
490 EVT_MENU(Menu_Close, wxLogFrame::OnClose)
b6d2b072 491#if CAN_SAVE_FILES
dd85fc6b 492 EVT_MENU(Menu_Save, wxLogFrame::OnSave)
b6d2b072 493#endif // CAN_SAVE_FILES
dd85fc6b
VZ
494 EVT_MENU(Menu_Clear, wxLogFrame::OnClear)
495
496 EVT_CLOSE(wxLogFrame::OnCloseWindow)
497END_EVENT_TABLE()
498
46ff9bd5 499wxLogFrame::wxLogFrame(wxWindow *pParent, wxLogWindow *log, const wxString& szTitle)
dabbc6a5 500 : wxFrame(pParent, wxID_ANY, szTitle)
dd85fc6b
VZ
501{
502 m_log = log;
503
dabbc6a5 504 m_pTextCtrl = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition,
dd85fc6b
VZ
505 wxDefaultSize,
506 wxTE_MULTILINE |
507 wxHSCROLL |
46d52925
VZ
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
dd85fc6b
VZ
513 wxTE_READONLY);
514
3a8c693a 515#if wxUSE_MENUS
dd85fc6b
VZ
516 // create menu
517 wxMenuBar *pMenuBar = new wxMenuBar;
518 wxMenu *pMenu = new wxMenu;
b6d2b072 519#if CAN_SAVE_FILES
b82f92a8 520 pMenu->Append(Menu_Save, _("Save &As..."), _("Save log contents to file"));
b6d2b072 521#endif // CAN_SAVE_FILES
dd85fc6b
VZ
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);
3a8c693a 527#endif // wxUSE_MENUS
dd85fc6b
VZ
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
f6bcfd97
BP
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
dabbc6a5 543 Show(false);
f6bcfd97
BP
544 }
545}
546
dd85fc6b
VZ
547void wxLogFrame::OnClose(wxCommandEvent& WXUNUSED(event))
548{
549 DoClose();
550}
551
552void wxLogFrame::OnCloseWindow(wxCloseEvent& WXUNUSED(event))
553{
554 DoClose();
555}
556
b6d2b072 557#if CAN_SAVE_FILES
dd85fc6b
VZ
558void wxLogFrame::OnSave(wxCommandEvent& WXUNUSED(event))
559{
f6bcfd97
BP
560 wxString filename;
561 wxFile file;
73ff84ff 562 int rc = OpenLogFile(file, &filename, this);
f6bcfd97
BP
563 if ( rc == -1 )
564 {
dd85fc6b
VZ
565 // cancelled
566 return;
567 }
568
f6bcfd97 569 bool bOk = rc != 0;
dd85fc6b
VZ
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) +
f6bcfd97 576 wxTextFile::GetEOL());
dd85fc6b
VZ
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 {
2523e9b7 586 wxLogStatus((wxFrame*)this, _("Log saved to the file '%s'."), filename.c_str());
dd85fc6b
VZ
587 }
588}
b6d2b072 589#endif // CAN_SAVE_FILES
dd85fc6b
VZ
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// -----------
03147cd0 603
55c9a186 604wxLogWindow::wxLogWindow(wxWindow *pParent,
46ff9bd5 605 const wxString& szTitle,
dd85fc6b
VZ
606 bool bShow,
607 bool bDoPass)
608{
0538419f
VZ
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
03147cd0 614 PassMessages(bDoPass);
dd85fc6b
VZ
615
616 m_pLogFrame = new wxLogFrame(pParent, this, szTitle);
dd85fc6b
VZ
617
618 if ( bShow )
dabbc6a5 619 m_pLogFrame->Show();
dd85fc6b
VZ
620}
621
622void wxLogWindow::Show(bool bShow)
623{
624 m_pLogFrame->Show(bShow);
625}
626
bc73d5ae 627void wxLogWindow::DoLogTextAtLevel(wxLogLevel level, const wxString& msg)
dd85fc6b 628{
bc73d5ae
VZ
629 if ( !m_pLogFrame )
630 return;
cd1ff792 631
bc73d5ae
VZ
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;
dd85fc6b 639
232addd1 640 m_pLogFrame->ShowLogMessage(msg);
dd85fc6b
VZ
641}
642
643wxFrame *wxLogWindow::GetFrame() const
644{
645 return m_pLogFrame;
646}
647
648void wxLogWindow::OnFrameCreate(wxFrame * WXUNUSED(frame))
649{
650}
651
f6bcfd97
BP
652bool wxLogWindow::OnFrameClose(wxFrame * WXUNUSED(frame))
653{
654 // allow to close
dabbc6a5 655 return true;
f6bcfd97
BP
656}
657
dd85fc6b
VZ
658void wxLogWindow::OnFrameDelete(wxFrame * WXUNUSED(frame))
659{
d3b9f782 660 m_pLogFrame = NULL;
dd85fc6b
VZ
661}
662
663wxLogWindow::~wxLogWindow()
664{
dd85fc6b
VZ
665 // may be NULL if log frame already auto destroyed itself
666 delete m_pLogFrame;
667}
668
c54a8b64
VZ
669#endif // wxUSE_LOGWINDOW
670
f1df0927
VZ
671// ----------------------------------------------------------------------------
672// wxLogDialog
673// ----------------------------------------------------------------------------
674
675#if wxUSE_LOG_DIALOG
676
5f170f33 677wxString wxLogDialog::ms_details;
a14670e2 678size_t wxLogDialog::ms_maxLength = 0;
5f170f33 679
f1df0927
VZ
680wxLogDialog::wxLogDialog(wxWindow *parent,
681 const wxArrayString& messages,
682 const wxArrayInt& severity,
683 const wxArrayLong& times,
684 const wxString& caption,
685 long style)
dabbc6a5 686 : wxDialog(parent, wxID_ANY, caption,
0b165ed6
VZ
687 wxDefaultPosition, wxDefaultSize,
688 wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
f1df0927 689{
a14670e2
VZ
690 // init the static variables:
691
32b13913 692 if ( ms_details.empty() )
5f170f33 693 {
f6bcfd97
BP
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");
5f170f33 697 ms_details = wxGetTranslation(ms_details);
5e63b67d
WS
698#ifdef __SMARTPHONE__
699 ms_details = wxStripMenuCodes(ms_details);
700#endif
5f170f33
VZ
701 }
702
a14670e2
VZ
703 if ( ms_maxLength == 0 )
704 {
705 ms_maxLength = (2 * wxGetDisplaySize().x/3) / GetCharWidth();
706 }
707
4aff28fc
VZ
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 {
a14670e2 715 m_messages.Add(messages[n]);
786c4e23
VZ
716 m_severity.Add(severity[n]);
717 m_times.Add(times[n]);
4aff28fc
VZ
718 }
719
b6d2b072 720 m_listctrl = NULL;
5e63b67d 721
94f53923 722 bool isPda = (wxSystemSettings::GetScreenType() <= wxSYS_SCREEN_PDA);
5e63b67d 723
f1df0927 724 // create the controls which are always shown and layout them: we use
d13b34d3 725 // sizers even though our window is not resizable to calculate the size of
f1df0927
VZ
726 // the dialog properly
727 wxBoxSizer *sizerTop = new wxBoxSizer(wxVERTICAL);
94f53923 728 wxBoxSizer *sizerAll = new wxBoxSizer(isPda ? wxVERTICAL : wxHORIZONTAL);
f1df0927 729
1970409e 730 if (!isPda)
389d906b 731 {
1970409e
VZ
732 wxStaticBitmap *icon = new wxStaticBitmap
733 (
734 this,
735 wxID_ANY,
736 wxArtProvider::GetMessageBoxIcon(style)
737 );
b6d2b072 738 sizerAll->Add(icon, wxSizerFlags().Centre());
389d906b 739 }
5e63b67d 740
a14670e2
VZ
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
b6d2b072 746 sizerAll->Add(szText, wxSizerFlags(1).Centre().Border(wxLEFT | wxRIGHT));
a14670e2
VZ
747
748 wxButton *btnOk = new wxButton(this, wxID_OK);
b6d2b072 749 sizerAll->Add(btnOk, wxSizerFlags().Centre());
f1df0927 750
b6d2b072 751 sizerTop->Add(sizerAll, wxSizerFlags().Expand().Border());
f1df0927 752
f1df0927 753
a14670e2 754 // add the details pane
5e63b67d 755#ifndef __SMARTPHONE__
b6d2b072
VZ
756 wxCollapsiblePane * const
757 collpane = new wxCollapsiblePane(this, wxID_ANY, ms_details);
758 sizerTop->Add(collpane, wxSizerFlags(1).Expand().Border());
a14670e2
VZ
759
760 wxWindow *win = collpane->GetPane();
b6d2b072 761 wxSizer * const paneSz = new wxBoxSizer(wxVERTICAL);
a14670e2
VZ
762
763 CreateDetailsControls(win);
764
b6d2b072
VZ
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
a14670e2 776
b6d2b072
VZ
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
a14670e2
VZ
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);
f1df0927
VZ
792
793 Centre();
5e63b67d 794
94f53923
JS
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 }
f1df0927
VZ
801}
802
a14670e2 803void wxLogDialog::CreateDetailsControls(wxWindow *parent)
f6bcfd97 804{
4ccc2a07
FM
805 wxString fmt = wxLog::GetTimestamp();
806 bool hasTimeStamp = !fmt.IsEmpty();
807
f6bcfd97 808 // create the list ctrl now
a14670e2 809 m_listctrl = new wxListCtrl(parent, wxID_ANY,
f6bcfd97 810 wxDefaultPosition, wxDefaultSize,
3d35ec88 811 wxBORDER_SIMPLE |
f6bcfd97
BP
812 wxLC_REPORT |
813 wxLC_NO_HEADER |
814 wxLC_SINGLE_SEL);
94f53923 815#ifdef __WXWINCE__
a14670e2 816 // This makes a big aesthetic difference on WinCE but I
94f53923
JS
817 // don't want to risk problems on other platforms
818 m_listctrl->Hide();
5e63b67d 819#endif
f6bcfd97
BP
820
821 // no need to translate these strings as they're not shown to the
822 // user anyhow (we use wxLC_NO_HEADER style)
9a83f860 823 m_listctrl->InsertColumn(0, wxT("Message"));
4ccc2a07
FM
824
825 if (hasTimeStamp)
9a83f860 826 m_listctrl->InsertColumn(1, wxT("Time"));
f6bcfd97
BP
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!
19be42b7 833 static const char* const icons[] =
f6bcfd97 834 {
389d906b
VS
835 wxART_ERROR,
836 wxART_WARNING,
837 wxART_INFORMATION
f6bcfd97
BP
838 };
839
dabbc6a5 840 bool loadedIcons = true;
f6bcfd97 841
f6bcfd97
BP
842 for ( size_t icon = 0; icon < WXSIZEOF(icons); icon++ )
843 {
389d906b
VS
844 wxBitmap bmp = wxArtProvider::GetBitmap(icons[icon], wxART_MESSAGE_BOX,
845 wxSize(ICON_SIZE, ICON_SIZE));
f6bcfd97 846
9f14a818
VZ
847 // This may very well fail if there are insufficient colours available.
848 // Degrade gracefully.
a1b806b9 849 if ( !bmp.IsOk() )
9f14a818 850 {
dabbc6a5 851 loadedIcons = false;
9f14a818
VZ
852
853 break;
854 }
855
389d906b 856 imageList->Add(bmp);
f6bcfd97
BP
857 }
858
859 m_listctrl->SetImageList(imageList, wxIMAGE_LIST_SMALL);
f6bcfd97 860
4ccc2a07 861 // fill the listctrl
f6bcfd97
BP
862 size_t count = m_messages.GetCount();
863 for ( size_t n = 0; n < count; n++ )
864 {
9f14a818
VZ
865 int image;
866
9f14a818 867 if ( loadedIcons )
f6bcfd97 868 {
9f14a818
VZ
869 switch ( m_severity[n] )
870 {
871 case wxLOG_Error:
872 image = 0;
873 break;
f6bcfd97 874
9f14a818
VZ
875 case wxLOG_Warning:
876 image = 1;
877 break;
f6bcfd97 878
9f14a818
VZ
879 default:
880 image = 2;
881 }
f6bcfd97 882 }
9f14a818 883 else // failed to load images
9f14a818 884 {
f6bcfd97 885 image = -1;
9f14a818 886 }
f6bcfd97 887
a14670e2
VZ
888 wxString msg = m_messages[n];
889 msg.Replace(wxT("\n"), wxT(" "));
890 msg = EllipsizeString(msg);
891
892 m_listctrl->InsertItem(n, msg, image);
4ccc2a07
FM
893
894 if (hasTimeStamp)
895 m_listctrl->SetItem(n, 1, TimeStamp(fmt, (time_t)m_times[n]));
f6bcfd97
BP
896 }
897
898 // let the columns size themselves
899 m_listctrl->SetColumnWidth(0, wxLIST_AUTOSIZE);
4ccc2a07
FM
900 if (hasTimeStamp)
901 m_listctrl->SetColumnWidth(1, wxLIST_AUTOSIZE);
f6bcfd97 902
9f14a818 903 // calculate an approximately nice height for the listctrl
7b82d782 904 int height = GetCharHeight()*(count + 4);
f6bcfd97 905
9f14a818
VZ
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();
4d509295 912
9f14a818
VZ
913 // we should leave a margin
914 heightMax *= 9;
915 heightMax /= 10;
4d509295 916
422d0ff0 917 m_listctrl->SetSize(wxDefaultCoord, wxMin(height, heightMax));
f6bcfd97
BP
918}
919
a14670e2
VZ
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
cd1ff792 932 // wxMessageBox will nicely handle the '\n' in the string (if any)
a14670e2
VZ
933 // and supports long strings
934 wxMessageBox(str, wxT("Log message"), wxOK, this);
935}
936
f1df0927
VZ
937void wxLogDialog::OnOk(wxCommandEvent& WXUNUSED(event))
938{
939 EndModal(wxID_OK);
940}
941
b6d2b072 942#if CAN_SAVE_FILES || wxUSE_CLIPBOARD
f6bcfd97 943
b6d2b072 944wxString wxLogDialog::GetLogMessages() const
f6bcfd97 945{
f6bcfd97 946 wxString fmt = wxLog::GetTimestamp();
b6d2b072 947 if ( fmt.empty() )
f6bcfd97 948 {
b6d2b072
VZ
949 // use the default format
950 fmt = "%c";
f6bcfd97
BP
951 }
952
b6d2b072
VZ
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++ )
f6bcfd97 958 {
b6d2b072
VZ
959 text << TimeStamp(fmt, (time_t)m_times[n])
960 << ": "
f6bcfd97
BP
961 << m_messages[n]
962 << wxTextFile::GetEOL();
b6d2b072
VZ
963 }
964
965 return text;
966}
967
968#endif // CAN_SAVE_FILES || wxUSE_CLIPBOARD
f6bcfd97 969
b6d2b072
VZ
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."));
f6bcfd97 979 }
b6d2b072 980}
f6bcfd97 981
b6d2b072
VZ
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 }
f6bcfd97 995
b6d2b072 996 if ( !rc || !file.Write(GetLogMessages()) || !file.Close() )
af588446 997 {
f6bcfd97 998 wxLogError(_("Can't save log contents to file."));
af588446 999 }
f6bcfd97
BP
1000}
1001
b6d2b072 1002#endif // CAN_SAVE_FILES
f6bcfd97 1003
f1df0927
VZ
1004wxLogDialog::~wxLogDialog()
1005{
1006 if ( m_listctrl )
1007 {
1008 delete m_listctrl->GetImageList(wxIMAGE_LIST_SMALL);
1009 }
1010}
1011
1012#endif // wxUSE_LOG_DIALOG
f6bcfd97 1013
b6d2b072 1014#if CAN_SAVE_FILES
f6bcfd97
BP
1015
1016// pass an uninitialized file object, the function will ask the user for the
dabbc6a5
DS
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
f6bcfd97 1019// dialog was cancelled
73ff84ff 1020static int OpenLogFile(wxFile& file, wxString *pFilename, wxWindow *parent)
f6bcfd97
BP
1021{
1022 // get the file name
1023 // -----------------
73ff84ff 1024 wxString filename = wxSaveFileSelector(wxT("log"), wxT("txt"), wxT("log.txt"), parent);
f6bcfd97
BP
1025 if ( !filename ) {
1026 // cancelled
1027 return -1;
1028 }
1029
1030 // open file
1031 // ---------
c2f7cc71 1032 bool bOk = true; // suppress warning about it being possible uninitialized
f6bcfd97 1033 if ( wxFile::Exists(filename) ) {
dabbc6a5 1034 bool bAppend = false;
f6bcfd97
BP
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:
dabbc6a5 1041 bAppend = true;
f6bcfd97
BP
1042 break;
1043
1044 case wxNO:
dabbc6a5 1045 bAppend = false;
f6bcfd97
BP
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 {
dabbc6a5 1059 bOk = file.Create(filename, true /* overwrite */);
f6bcfd97
BP
1060 }
1061 }
1062 else {
1063 bOk = file.Create(filename);
1064 }
1065
1066 if ( pFilename )
1067 *pFilename = filename;
1068
1069 return bOk;
1070}
1071
b6d2b072 1072#endif // CAN_SAVE_FILES
f6bcfd97 1073
1e6feb95
VZ
1074#endif // !(wxUSE_LOGGUI || wxUSE_LOGWINDOW)
1075
f561a4e0 1076#if wxUSE_LOG && wxUSE_TEXTCTRL
1e6feb95
VZ
1077
1078// ----------------------------------------------------------------------------
1079// wxLogTextCtrl implementation
1080// ----------------------------------------------------------------------------
1081
1082wxLogTextCtrl::wxLogTextCtrl(wxTextCtrl *pTextCtrl)
1083{
1084 m_pTextCtrl = pTextCtrl;
1085}
1086
bc73d5ae 1087void wxLogTextCtrl::DoLogText(const wxString& msg)
1e6feb95 1088{
bc73d5ae 1089 m_pTextCtrl->AppendText(msg + wxS('\n'));
1e6feb95
VZ
1090}
1091
f561a4e0 1092#endif // wxUSE_LOG && wxUSE_TEXTCTRL