]> git.saurik.com Git - wxWidgets.git/blame - src/generic/logg.cpp
remove miniframe stuff from GtkOnSize(), it's handled by wxFrame
[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
VZ
46#include "wx/file.h"
47#include "wx/textfile.h"
f6bcfd97 48#include "wx/statline.h"
389d906b 49#include "wx/artprov.h"
a14670e2 50#include "wx/collpane.h"
dd85fc6b 51
39a7c7e1 52#ifdef __WXMSW__
f1e01716
WS
53 // for OutputDebugString()
54 #include "wx/msw/private.h"
39a7c7e1
VZ
55#endif // Windows
56
2eb96d03 57#ifdef __WXPM__
f1e01716 58 #include <time.h>
2eb96d03
DW
59#endif
60
f1df0927 61#if wxUSE_LOG_DIALOG
f1df0927 62 #include "wx/listctrl.h"
de8e98f1 63 #include "wx/imaglist.h"
f1df0927 64 #include "wx/image.h"
f1df0927
VZ
65#endif // wxUSE_LOG_DIALOG/!wxUSE_LOG_DIALOG
66
ed4e170a
SC
67#if defined(__MWERKS__) && wxUSE_UNICODE
68 #include <wtime.h>
69#endif
70
1aaf88d2
WS
71#include "wx/datetime.h"
72
8ff41136
VZ
73// the suffix we add to the button to show that the dialog can be expanded
74#define EXPAND_SUFFIX _T(" >>")
75
f1df0927
VZ
76// ----------------------------------------------------------------------------
77// private classes
78// ----------------------------------------------------------------------------
79
80#if wxUSE_LOG_DIALOG
81
f94dfb38
VS
82// this function is a wrapper around strftime(3)
83// allows to exclude the usage of wxDateTime
84static wxString TimeStamp(const wxChar *format, time_t t)
85{
7b2d1c74 86#if wxUSE_DATETIME
f94dfb38 87 wxChar buf[4096];
83e8b44c
VZ
88 struct tm tm;
89 if ( !wxStrftime(buf, WXSIZEOF(buf), format, wxLocaltime_r(&t, &tm)) )
f94dfb38
VS
90 {
91 // buffer is too small?
92 wxFAIL_MSG(_T("strftime() failed"));
93 }
94 return wxString(buf);
7b2d1c74
VZ
95#else // !wxUSE_DATETIME
96 return wxEmptyString;
97#endif // wxUSE_DATETIME/!wxUSE_DATETIME
f94dfb38
VS
98}
99
100
f1df0927
VZ
101class wxLogDialog : public wxDialog
102{
103public:
104 wxLogDialog(wxWindow *parent,
105 const wxArrayString& messages,
106 const wxArrayInt& severity,
107 const wxArrayLong& timess,
108 const wxString& caption,
109 long style);
110 virtual ~wxLogDialog();
111
112 // event handlers
113 void OnOk(wxCommandEvent& event);
f6bcfd97
BP
114#if wxUSE_FILE
115 void OnSave(wxCommandEvent& event);
116#endif // wxUSE_FILE
4aff28fc 117 void OnListSelect(wxListEvent& event);
a14670e2 118 void OnListItemActivated(wxListEvent& event);
f1df0927
VZ
119
120private:
f6bcfd97 121 // create controls needed for the details display
a14670e2
VZ
122 void CreateDetailsControls(wxWindow *);
123
124 // if necessary truncates the given string and adds an ellipsis
125 wxString EllipsizeString(const wxString &text)
126 {
127 if (ms_maxLength > 0 &&
128 text.length() > ms_maxLength)
129 {
130 wxString ret(text);
131 ret.Truncate(ms_maxLength);
132 ret << "...";
133 return ret;
134 }
135
136 return text;
137 }
f6bcfd97 138
f1df0927 139 // the data for the listctrl
224ff7a5
VZ
140 wxArrayString m_messages;
141 wxArrayInt m_severity;
142 wxArrayLong m_times;
f1df0927 143
f6bcfd97
BP
144 // the controls which are not shown initially (but only when details
145 // button is pressed)
f1df0927 146 wxListCtrl *m_listctrl;
5e63b67d 147#ifndef __SMARTPHONE__
f6bcfd97
BP
148#if wxUSE_STATLINE
149 wxStaticLine *m_statline;
150#endif // wxUSE_STATLINE
151#if wxUSE_FILE
152 wxButton *m_btnSave;
153#endif // wxUSE_FILE
5e63b67d 154#endif // __SMARTPHONE__
f1df0927 155
5f170f33
VZ
156 // the translated "Details" string
157 static wxString ms_details;
158
a14670e2
VZ
159 // the maximum length of the log message
160 static size_t ms_maxLength;
161
f1df0927 162 DECLARE_EVENT_TABLE()
22f3361e 163 DECLARE_NO_COPY_CLASS(wxLogDialog)
f1df0927
VZ
164};
165
166BEGIN_EVENT_TABLE(wxLogDialog, wxDialog)
eb3e6de3 167 EVT_BUTTON(wxID_OK, wxLogDialog::OnOk)
f6bcfd97
BP
168#if wxUSE_FILE
169 EVT_BUTTON(wxID_SAVE, wxLogDialog::OnSave)
170#endif // wxUSE_FILE
dabbc6a5 171 EVT_LIST_ITEM_SELECTED(wxID_ANY, wxLogDialog::OnListSelect)
a14670e2 172 EVT_LIST_ITEM_ACTIVATED(wxID_ANY, wxLogDialog::OnListItemActivated)
f1df0927
VZ
173END_EVENT_TABLE()
174
175#endif // wxUSE_LOG_DIALOG
176
f6bcfd97
BP
177// ----------------------------------------------------------------------------
178// private functions
179// ----------------------------------------------------------------------------
180
3e6e2754 181#if wxUSE_FILE && wxUSE_FILEDLG
f6bcfd97
BP
182
183// pass an uninitialized file object, the function will ask the user for the
dabbc6a5
DS
184// filename and try to open it, returns true on success (file was opened),
185// false if file couldn't be opened/created and -1 if the file selection
f6bcfd97 186// dialog was cancelled
73ff84ff 187static int OpenLogFile(wxFile& file, wxString *filename = NULL, wxWindow *parent = NULL);
f6bcfd97
BP
188
189#endif // wxUSE_FILE
190
dd85fc6b
VZ
191// ----------------------------------------------------------------------------
192// global variables
193// ----------------------------------------------------------------------------
194
195// we use a global variable to store the frame pointer for wxLogStatus - bad,
3e6e2754 196// but it's the easiest way
63617fb5 197static wxFrame *gs_pFrame = NULL; // FIXME MT-unsafe
dd85fc6b
VZ
198
199// ============================================================================
200// implementation
201// ============================================================================
202
203// ----------------------------------------------------------------------------
204// global functions
205// ----------------------------------------------------------------------------
206
207// accepts an additional argument which tells to which frame the output should
208// be directed
2523e9b7 209void wxVLogStatus(wxFrame *pFrame, const wxString& format, va_list argptr)
dd85fc6b
VZ
210{
211 wxString msg;
212
213 wxLog *pLog = wxLog::GetActiveTarget();
214 if ( pLog != NULL ) {
2523e9b7 215 msg.PrintfV(format, argptr);
dd85fc6b
VZ
216
217 wxASSERT( gs_pFrame == NULL ); // should be reset!
218 gs_pFrame = pFrame;
4676948b
JS
219#ifdef __WXWINCE__
220 wxLog::OnLog(wxLOG_Status, msg, 0);
221#else
dd85fc6b 222 wxLog::OnLog(wxLOG_Status, msg, time(NULL));
4676948b 223#endif
dd85fc6b
VZ
224 gs_pFrame = (wxFrame *) NULL;
225 }
226}
227
2523e9b7 228void wxDoLogStatus(wxFrame *pFrame, const wxString& format, ...)
42fa962e
GD
229{
230 va_list argptr;
2523e9b7
VS
231 va_start(argptr, format);
232 wxVLogStatus(pFrame, format, argptr);
42fa962e
GD
233 va_end(argptr);
234}
235
dd85fc6b
VZ
236// ----------------------------------------------------------------------------
237// wxLogGui implementation (FIXME MT-unsafe)
238// ----------------------------------------------------------------------------
239
461dae94
VZ
240#if wxUSE_LOGGUI
241
dd85fc6b
VZ
242wxLogGui::wxLogGui()
243{
244 Clear();
245}
246
247void wxLogGui::Clear()
248{
5f170f33
VZ
249 m_bErrors =
250 m_bWarnings =
dabbc6a5 251 m_bHasMessages = false;
5f170f33 252
dd85fc6b 253 m_aMessages.Empty();
f1df0927 254 m_aSeverity.Empty();
dd85fc6b
VZ
255 m_aTimes.Empty();
256}
257
258void wxLogGui::Flush()
259{
260 if ( !m_bHasMessages )
261 return;
262
263 // do it right now to block any new calls to Flush() while we're here
dabbc6a5 264 m_bHasMessages = false;
dd85fc6b 265
f9837791
VZ
266 unsigned repeatCount = 0;
267 if ( wxLog::GetRepetitionCounting() )
268 {
269 repeatCount = wxLog::DoLogNumberOfRepeats();
270 }
271
f6bcfd97 272 wxString appName = wxTheApp->GetAppName();
32b13913 273 if ( !appName.empty() )
8253f2e0 274 appName[0u] = (wxChar)wxToupper(appName[0u]);
f1df0927
VZ
275
276 long style;
f6bcfd97 277 wxString titleFormat;
f1df0927 278 if ( m_bErrors ) {
f6bcfd97 279 titleFormat = _("%s Error");
f1df0927
VZ
280 style = wxICON_STOP;
281 }
282 else if ( m_bWarnings ) {
f6bcfd97 283 titleFormat = _("%s Warning");
f1df0927
VZ
284 style = wxICON_EXCLAMATION;
285 }
286 else {
f6bcfd97 287 titleFormat = _("%s Information");
f1df0927
VZ
288 style = wxICON_INFORMATION;
289 }
1e6feb95 290
f6bcfd97
BP
291 wxString title;
292 title.Printf(titleFormat, appName.c_str());
f1df0927 293
b4a980f4 294 size_t nMsgCount = m_aMessages.GetCount();
224ff7a5 295
69c916e9
VZ
296 // avoid showing other log dialogs until we're done with the dialog we're
297 // showing right now: nested modal dialogs make for really bad UI!
298 Suspend();
299
23f681ec
VZ
300 wxString str;
301 if ( nMsgCount == 1 )
302 {
303 str = m_aMessages[0];
304 }
305 else // more than one message
306 {
307#if wxUSE_LOG_DIALOG
3e6e2754 308
f9837791
VZ
309 if ( repeatCount > 0 )
310 m_aMessages[nMsgCount-1] += wxString::Format(wxT(" (%s)"), m_aMessages[nMsgCount-2].c_str());
39cc7a0b 311 wxLogDialog dlg(NULL,
23f681ec
VZ
312 m_aMessages, m_aSeverity, m_aTimes,
313 title, style);
f1df0927 314
23f681ec
VZ
315 // clear the message list before showing the dialog because while it's
316 // shown some new messages may appear
317 Clear();
318
319 (void)dlg.ShowModal();
f1df0927 320#else // !wxUSE_LOG_DIALOG
23f681ec
VZ
321 // concatenate all strings (but not too many to not overfill the msg box)
322 size_t nLines = 0;
dd85fc6b 323
23f681ec
VZ
324 // start from the most recent message
325 for ( size_t n = nMsgCount; n > 0; n-- ) {
326 // for Windows strings longer than this value are wrapped (NT 4.0)
327 const size_t nMsgLineWidth = 156;
dd85fc6b 328
23f681ec 329 nLines += (m_aMessages[n - 1].Len() + nMsgLineWidth - 1) / nMsgLineWidth;
dd85fc6b 330
23f681ec
VZ
331 if ( nLines > 25 ) // don't put too many lines in message box
332 break;
dd85fc6b 333
23f681ec
VZ
334 str << m_aMessages[n - 1] << wxT("\n");
335 }
336#endif // wxUSE_LOG_DIALOG/!wxUSE_LOG_DIALOG
dd85fc6b
VZ
337 }
338
23f681ec
VZ
339 // this catches both cases of 1 message with wxUSE_LOG_DIALOG and any
340 // situation without it
32b13913 341 if ( !str.empty() )
23f681ec 342 {
39cc7a0b 343 wxMessageBox(str, title, wxOK | style);
dd85fc6b 344
23f681ec
VZ
345 // no undisplayed messages whatsoever
346 Clear();
347 }
69c916e9
VZ
348
349 // allow flushing the logs again
350 Resume();
dd85fc6b
VZ
351}
352
23f681ec 353// log all kinds of messages
dd85fc6b
VZ
354void wxLogGui::DoLog(wxLogLevel level, const wxChar *szString, time_t t)
355{
356 switch ( level ) {
357 case wxLOG_Info:
358 if ( GetVerbose() )
359 case wxLOG_Message:
f1df0927 360 {
a250392c
VZ
361 m_aMessages.Add(szString);
362 m_aSeverity.Add(wxLOG_Message);
363 m_aTimes.Add((long)t);
dabbc6a5 364 m_bHasMessages = true;
f1df0927
VZ
365 }
366 break;
dd85fc6b
VZ
367
368 case wxLOG_Status:
369#if wxUSE_STATUSBAR
f1df0927
VZ
370 {
371 // find the top window and set it's status text if it has any
372 wxFrame *pFrame = gs_pFrame;
373 if ( pFrame == NULL ) {
374 wxWindow *pWin = wxTheApp->GetTopWindow();
375 if ( pWin != NULL && pWin->IsKindOf(CLASSINFO(wxFrame)) ) {
376 pFrame = (wxFrame *)pWin;
dd85fc6b 377 }
dd85fc6b 378 }
f1df0927
VZ
379
380 if ( pFrame && pFrame->GetStatusBar() )
381 pFrame->SetStatusText(szString);
382 }
dd85fc6b 383#endif // wxUSE_STATUSBAR
f1df0927 384 break;
dd85fc6b
VZ
385
386 case wxLOG_Trace:
387 case wxLOG_Debug:
f1df0927
VZ
388 #ifdef __WXDEBUG__
389 {
3a57b93d
VZ
390 wxString str;
391 TimeStamp(&str);
392 str += szString;
393
8a46f9b1 394 #if defined(__WXMSW__) && !defined(__WXMICROWIN__)
f1df0927 395 // don't prepend debug/trace here: it goes to the
3a57b93d
VZ
396 // debug window anyhow
397 str += wxT("\r\n");
f1df0927
VZ
398 OutputDebugString(str);
399 #else
400 // send them to stderr
3a57b93d 401 wxFprintf(stderr, wxT("[%s] %s\n"),
f1df0927
VZ
402 level == wxLOG_Trace ? wxT("Trace")
403 : wxT("Debug"),
3a57b93d 404 str.c_str());
f1df0927
VZ
405 fflush(stderr);
406 #endif
407 }
408 #endif // __WXDEBUG__
dd85fc6b 409
f1df0927 410 break;
dd85fc6b
VZ
411
412 case wxLOG_FatalError:
f1df0927
VZ
413 // show this one immediately
414 wxMessageBox(szString, _("Fatal error"), wxICON_HAND);
a2a0d40d 415 wxExit();
f1df0927 416 break;
dd85fc6b
VZ
417
418 case wxLOG_Error:
f1df0927
VZ
419 if ( !m_bErrors ) {
420#if !wxUSE_LOG_DIALOG
dd85fc6b 421 // discard earlier informational messages if this is the 1st
f1df0927
VZ
422 // error because they might not make sense any more and showing
423 // them in a message box might be confusing
424 m_aMessages.Empty();
425 m_aSeverity.Empty();
426 m_aTimes.Empty();
427#endif // wxUSE_LOG_DIALOG
dabbc6a5 428 m_bErrors = true;
f1df0927
VZ
429 }
430 // fall through
dd85fc6b
VZ
431
432 case wxLOG_Warning:
f1df0927
VZ
433 if ( !m_bErrors ) {
434 // for the warning we don't discard the info messages
dabbc6a5 435 m_bWarnings = true;
f1df0927
VZ
436 }
437
438 m_aMessages.Add(szString);
06b466c7 439 m_aSeverity.Add((int)level);
f1df0927 440 m_aTimes.Add((long)t);
dabbc6a5 441 m_bHasMessages = true;
f1df0927 442 break;
dd85fc6b
VZ
443 }
444}
445
461dae94
VZ
446#endif // wxUSE_LOGGUI
447
dd85fc6b
VZ
448// ----------------------------------------------------------------------------
449// wxLogWindow and wxLogFrame implementation
450// ----------------------------------------------------------------------------
451
c54a8b64
VZ
452#if wxUSE_LOGWINDOW
453
dd85fc6b
VZ
454// log frame class
455// ---------------
456class wxLogFrame : public wxFrame
457{
458public:
459 // ctor & dtor
55c9a186 460 wxLogFrame(wxWindow *pParent, wxLogWindow *log, const wxChar *szTitle);
dd85fc6b
VZ
461 virtual ~wxLogFrame();
462
463 // menu callbacks
464 void OnClose(wxCommandEvent& event);
465 void OnCloseWindow(wxCloseEvent& event);
466#if wxUSE_FILE
467 void OnSave (wxCommandEvent& event);
468#endif // wxUSE_FILE
469 void OnClear(wxCommandEvent& event);
470
dd85fc6b
VZ
471 // accessors
472 wxTextCtrl *TextCtrl() const { return m_pTextCtrl; }
473
474private:
857eb081 475 // use standard ids for our commands!
dd85fc6b
VZ
476 enum
477 {
857eb081
VZ
478 Menu_Close = wxID_CLOSE,
479 Menu_Save = wxID_SAVE,
480 Menu_Clear = wxID_CLEAR
dd85fc6b
VZ
481 };
482
f6bcfd97
BP
483 // common part of OnClose() and OnCloseWindow()
484 void DoClose();
dd85fc6b
VZ
485
486 wxTextCtrl *m_pTextCtrl;
487 wxLogWindow *m_log;
488
489 DECLARE_EVENT_TABLE()
22f3361e 490 DECLARE_NO_COPY_CLASS(wxLogFrame)
dd85fc6b
VZ
491};
492
493BEGIN_EVENT_TABLE(wxLogFrame, wxFrame)
494 // wxLogWindow menu events
495 EVT_MENU(Menu_Close, wxLogFrame::OnClose)
496#if wxUSE_FILE
497 EVT_MENU(Menu_Save, wxLogFrame::OnSave)
498#endif // wxUSE_FILE
499 EVT_MENU(Menu_Clear, wxLogFrame::OnClear)
500
501 EVT_CLOSE(wxLogFrame::OnCloseWindow)
502END_EVENT_TABLE()
503
55c9a186 504wxLogFrame::wxLogFrame(wxWindow *pParent, wxLogWindow *log, const wxChar *szTitle)
dabbc6a5 505 : wxFrame(pParent, wxID_ANY, szTitle)
dd85fc6b
VZ
506{
507 m_log = log;
508
dabbc6a5 509 m_pTextCtrl = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition,
dd85fc6b
VZ
510 wxDefaultSize,
511 wxTE_MULTILINE |
512 wxHSCROLL |
46d52925
VZ
513 // needed for Win32 to avoid 65Kb limit but it doesn't work well
514 // when using RichEdit 2.0 which we always do in the Unicode build
515#if !wxUSE_UNICODE
516 wxTE_RICH |
517#endif // !wxUSE_UNICODE
dd85fc6b
VZ
518 wxTE_READONLY);
519
3a8c693a 520#if wxUSE_MENUS
dd85fc6b
VZ
521 // create menu
522 wxMenuBar *pMenuBar = new wxMenuBar;
523 wxMenu *pMenu = new wxMenu;
524#if wxUSE_FILE
525 pMenu->Append(Menu_Save, _("&Save..."), _("Save log contents to file"));
526#endif // wxUSE_FILE
527 pMenu->Append(Menu_Clear, _("C&lear"), _("Clear the log contents"));
528 pMenu->AppendSeparator();
529 pMenu->Append(Menu_Close, _("&Close"), _("Close this window"));
530 pMenuBar->Append(pMenu, _("&Log"));
531 SetMenuBar(pMenuBar);
3a8c693a 532#endif // wxUSE_MENUS
dd85fc6b
VZ
533
534#if wxUSE_STATUSBAR
535 // status bar for menu prompts
536 CreateStatusBar();
537#endif // wxUSE_STATUSBAR
538
539 m_log->OnFrameCreate(this);
540}
541
f6bcfd97
BP
542void wxLogFrame::DoClose()
543{
544 if ( m_log->OnFrameClose(this) )
545 {
546 // instead of closing just hide the window to be able to Show() it
547 // later
dabbc6a5 548 Show(false);
f6bcfd97
BP
549 }
550}
551
dd85fc6b
VZ
552void wxLogFrame::OnClose(wxCommandEvent& WXUNUSED(event))
553{
554 DoClose();
555}
556
557void wxLogFrame::OnCloseWindow(wxCloseEvent& WXUNUSED(event))
558{
559 DoClose();
560}
561
562#if wxUSE_FILE
563void wxLogFrame::OnSave(wxCommandEvent& WXUNUSED(event))
564{
3e6e2754 565#if wxUSE_FILEDLG
f6bcfd97
BP
566 wxString filename;
567 wxFile file;
73ff84ff 568 int rc = OpenLogFile(file, &filename, this);
f6bcfd97
BP
569 if ( rc == -1 )
570 {
dd85fc6b
VZ
571 // cancelled
572 return;
573 }
574
f6bcfd97 575 bool bOk = rc != 0;
dd85fc6b
VZ
576
577 // retrieve text and save it
578 // -------------------------
579 int nLines = m_pTextCtrl->GetNumberOfLines();
580 for ( int nLine = 0; bOk && nLine < nLines; nLine++ ) {
581 bOk = file.Write(m_pTextCtrl->GetLineText(nLine) +
f6bcfd97 582 wxTextFile::GetEOL());
dd85fc6b
VZ
583 }
584
585 if ( bOk )
586 bOk = file.Close();
587
588 if ( !bOk ) {
589 wxLogError(_("Can't save log contents to file."));
590 }
591 else {
2523e9b7 592 wxLogStatus((wxFrame*)this, _("Log saved to the file '%s'."), filename.c_str());
dd85fc6b 593 }
3e6e2754 594#endif
dd85fc6b
VZ
595}
596#endif // wxUSE_FILE
597
598void wxLogFrame::OnClear(wxCommandEvent& WXUNUSED(event))
599{
600 m_pTextCtrl->Clear();
601}
602
603wxLogFrame::~wxLogFrame()
604{
605 m_log->OnFrameDelete(this);
606}
607
608// wxLogWindow
609// -----------
03147cd0 610
55c9a186 611wxLogWindow::wxLogWindow(wxWindow *pParent,
dd85fc6b
VZ
612 const wxChar *szTitle,
613 bool bShow,
614 bool bDoPass)
615{
03147cd0 616 PassMessages(bDoPass);
dd85fc6b
VZ
617
618 m_pLogFrame = new wxLogFrame(pParent, this, szTitle);
dd85fc6b
VZ
619
620 if ( bShow )
dabbc6a5 621 m_pLogFrame->Show();
dd85fc6b
VZ
622}
623
624void wxLogWindow::Show(bool bShow)
625{
626 m_pLogFrame->Show(bShow);
627}
628
dd85fc6b
VZ
629void wxLogWindow::DoLog(wxLogLevel level, const wxChar *szString, time_t t)
630{
631 // first let the previous logger show it
03147cd0 632 wxLogPassThrough::DoLog(level, szString, t);
dd85fc6b
VZ
633
634 if ( m_pLogFrame ) {
635 switch ( level ) {
636 case wxLOG_Status:
637 // by default, these messages are ignored by wxLog, so process
638 // them ourselves
639 if ( !wxIsEmpty(szString) )
640 {
641 wxString str;
642 str << _("Status: ") << szString;
643 DoLogString(str, t);
644 }
645 break;
646
647 // don't put trace messages in the text window for 2 reasons:
648 // 1) there are too many of them
649 // 2) they may provoke other trace messages thus sending a program
650 // into an infinite loop
651 case wxLOG_Trace:
652 break;
653
654 default:
655 // and this will format it nicely and call our DoLogString()
656 wxLog::DoLog(level, szString, t);
657 }
658 }
dd85fc6b
VZ
659}
660
661void wxLogWindow::DoLogString(const wxChar *szString, time_t WXUNUSED(t))
662{
663 // put the text into our window
664 wxTextCtrl *pText = m_pLogFrame->TextCtrl();
665
666 // remove selection (WriteText is in fact ReplaceSelection)
667#ifdef __WXMSW__
7d8268a1 668 wxTextPos nLen = pText->GetLastPosition();
dd85fc6b
VZ
669 pText->SetSelection(nLen, nLen);
670#endif // Windows
671
672 wxString msg;
673 TimeStamp(&msg);
223d09f6 674 msg << szString << wxT('\n');
dd85fc6b
VZ
675
676 pText->AppendText(msg);
677
678 // TODO ensure that the line can be seen
679}
680
681wxFrame *wxLogWindow::GetFrame() const
682{
683 return m_pLogFrame;
684}
685
686void wxLogWindow::OnFrameCreate(wxFrame * WXUNUSED(frame))
687{
688}
689
f6bcfd97
BP
690bool wxLogWindow::OnFrameClose(wxFrame * WXUNUSED(frame))
691{
692 // allow to close
dabbc6a5 693 return true;
f6bcfd97
BP
694}
695
dd85fc6b
VZ
696void wxLogWindow::OnFrameDelete(wxFrame * WXUNUSED(frame))
697{
698 m_pLogFrame = (wxLogFrame *)NULL;
699}
700
701wxLogWindow::~wxLogWindow()
702{
dd85fc6b
VZ
703 // may be NULL if log frame already auto destroyed itself
704 delete m_pLogFrame;
705}
706
c54a8b64
VZ
707#endif // wxUSE_LOGWINDOW
708
f1df0927
VZ
709// ----------------------------------------------------------------------------
710// wxLogDialog
711// ----------------------------------------------------------------------------
712
713#if wxUSE_LOG_DIALOG
714
5e63b67d 715#ifndef __SMARTPHONE__
f1df0927 716static const size_t MARGIN = 10;
5e63b67d
WS
717#else
718static const size_t MARGIN = 0;
719#endif
f1df0927 720
5f170f33 721wxString wxLogDialog::ms_details;
a14670e2 722size_t wxLogDialog::ms_maxLength = 0;
5f170f33 723
f1df0927
VZ
724wxLogDialog::wxLogDialog(wxWindow *parent,
725 const wxArrayString& messages,
726 const wxArrayInt& severity,
727 const wxArrayLong& times,
728 const wxString& caption,
729 long style)
dabbc6a5 730 : wxDialog(parent, wxID_ANY, caption,
0b165ed6
VZ
731 wxDefaultPosition, wxDefaultSize,
732 wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
f1df0927 733{
a14670e2
VZ
734 // init the static variables:
735
32b13913 736 if ( ms_details.empty() )
5f170f33 737 {
f6bcfd97
BP
738 // ensure that we won't loop here if wxGetTranslation()
739 // happens to pop up a Log message while translating this :-)
740 ms_details = wxTRANSLATE("&Details");
5f170f33 741 ms_details = wxGetTranslation(ms_details);
5e63b67d
WS
742#ifdef __SMARTPHONE__
743 ms_details = wxStripMenuCodes(ms_details);
744#endif
5f170f33
VZ
745 }
746
a14670e2
VZ
747 if ( ms_maxLength == 0 )
748 {
749 ms_maxLength = (2 * wxGetDisplaySize().x/3) / GetCharWidth();
750 }
751
4aff28fc
VZ
752 size_t count = messages.GetCount();
753 m_messages.Alloc(count);
754 m_severity.Alloc(count);
755 m_times.Alloc(count);
756
757 for ( size_t n = 0; n < count; n++ )
758 {
a14670e2 759 m_messages.Add(messages[n]);
786c4e23
VZ
760 m_severity.Add(severity[n]);
761 m_times.Add(times[n]);
4aff28fc
VZ
762 }
763
f1df0927
VZ
764 m_listctrl = (wxListCtrl *)NULL;
765
5e63b67d
WS
766#ifndef __SMARTPHONE__
767
f6bcfd97
BP
768#if wxUSE_STATLINE
769 m_statline = (wxStaticLine *)NULL;
770#endif // wxUSE_STATLINE
771
772#if wxUSE_FILE
773 m_btnSave = (wxButton *)NULL;
774#endif // wxUSE_FILE
775
5e63b67d
WS
776#endif // __SMARTPHONE__
777
94f53923 778 bool isPda = (wxSystemSettings::GetScreenType() <= wxSYS_SCREEN_PDA);
5e63b67d 779
f1df0927
VZ
780 // create the controls which are always shown and layout them: we use
781 // sizers even though our window is not resizeable to calculate the size of
782 // the dialog properly
783 wxBoxSizer *sizerTop = new wxBoxSizer(wxVERTICAL);
94f53923 784 wxBoxSizer *sizerAll = new wxBoxSizer(isPda ? wxVERTICAL : wxHORIZONTAL);
f1df0927 785
389d906b
VS
786 wxBitmap bitmap;
787 switch ( style & wxICON_MASK )
788 {
789 case wxICON_ERROR:
5854bb83 790 bitmap = wxArtProvider::GetBitmap(wxART_ERROR, wxART_MESSAGE_BOX);
f1ecc5db
DW
791#ifdef __WXPM__
792 bitmap.SetId(wxICON_SMALL_ERROR);
793#endif
794 break;
795
389d906b 796 case wxICON_INFORMATION:
5854bb83 797 bitmap = wxArtProvider::GetBitmap(wxART_INFORMATION, wxART_MESSAGE_BOX);
f1ecc5db
DW
798#ifdef __WXPM__
799 bitmap.SetId(wxICON_SMALL_INFO);
800#endif
801 break;
802
389d906b 803 case wxICON_WARNING:
5854bb83 804 bitmap = wxArtProvider::GetBitmap(wxART_WARNING, wxART_MESSAGE_BOX);
f1ecc5db
DW
805#ifdef __WXPM__
806 bitmap.SetId(wxICON_SMALL_WARNING);
807#endif
808 break;
809
389d906b
VS
810 default:
811 wxFAIL_MSG(_T("incorrect log style"));
812 }
5e63b67d 813
94f53923
JS
814 if (!isPda)
815 sizerAll->Add(new wxStaticBitmap(this, wxID_ANY, bitmap), 0,
c9ecfbe4 816 wxALIGN_CENTRE_VERTICAL);
669f7a11 817
a14670e2
VZ
818 // create the text sizer with a minimal size so that we are sure it won't be too small
819 wxString message = EllipsizeString(messages.Last());
820 wxSizer *szText = CreateTextSizer(message);
821 szText->SetMinSize(wxMin(300, wxGetDisplaySize().x / 3), -1);
822
823 sizerAll->Add(szText, 1,
0b165ed6 824 wxALIGN_CENTRE_VERTICAL | wxLEFT | wxRIGHT, MARGIN);
a14670e2
VZ
825
826 wxButton *btnOk = new wxButton(this, wxID_OK);
827 sizerAll->Add(btnOk, 0, isPda ? wxCENTRE : wxCENTRE|wxBOTTOM, MARGIN/2);
f1df0927 828
0b165ed6 829 sizerTop->Add(sizerAll, 0, wxALL | wxEXPAND, MARGIN);
f1df0927 830
f1df0927 831
a14670e2 832 // add the details pane
f1df0927 833
5e63b67d 834#ifndef __SMARTPHONE__
a14670e2
VZ
835 wxCollapsiblePane *collpane = new wxCollapsiblePane(this, wxID_ANY, ms_details);
836 sizerTop->Add(collpane, 1, wxGROW|wxALL, MARGIN);
837
838 wxWindow *win = collpane->GetPane();
839 wxSizer *paneSz = new wxBoxSizer(wxVERTICAL);
840
841 CreateDetailsControls(win);
842
843 paneSz->Add(m_listctrl, 1, wxEXPAND | wxTOP, MARGIN);
844
845#if wxUSE_FILE && !defined(__SMARTPHONE__)
846 paneSz->Add(m_btnSave, 0, wxALIGN_RIGHT | wxTOP, MARGIN);
847#endif // wxUSE_FILE
848
849 win->SetSizer(paneSz);
850 paneSz->SetSizeHints(win);
851#else // __SMARTPHONE__
852 SetLeftMenu(wxID_OK);
853 SetRightMenu(wxID_MORE, ms_details + EXPAND_SUFFIX);
854#endif // __SMARTPHONE__/!__SMARTPHONE__
855
856 SetSizerAndFit(sizerTop);
f1df0927
VZ
857
858 Centre();
5e63b67d 859
94f53923
JS
860 if (isPda)
861 {
862 // Move up the screen so that when we expand the dialog,
863 // there's enough space.
864 Move(wxPoint(GetPosition().x, GetPosition().y / 2));
865 }
f1df0927
VZ
866}
867
a14670e2 868void wxLogDialog::CreateDetailsControls(wxWindow *parent)
f6bcfd97 869{
5e63b67d 870#ifndef __SMARTPHONE__
f6bcfd97
BP
871 // create the save button and separator line if possible
872#if wxUSE_FILE
a14670e2 873 m_btnSave = new wxButton(parent, wxID_SAVE);
f6bcfd97
BP
874#endif // wxUSE_FILE
875
5e63b67d
WS
876#endif // __SMARTPHONE__
877
f6bcfd97 878 // create the list ctrl now
a14670e2 879 m_listctrl = new wxListCtrl(parent, wxID_ANY,
f6bcfd97
BP
880 wxDefaultPosition, wxDefaultSize,
881 wxSUNKEN_BORDER |
882 wxLC_REPORT |
883 wxLC_NO_HEADER |
884 wxLC_SINGLE_SEL);
94f53923 885#ifdef __WXWINCE__
a14670e2 886 // This makes a big aesthetic difference on WinCE but I
94f53923
JS
887 // don't want to risk problems on other platforms
888 m_listctrl->Hide();
5e63b67d 889#endif
f6bcfd97
BP
890
891 // no need to translate these strings as they're not shown to the
892 // user anyhow (we use wxLC_NO_HEADER style)
893 m_listctrl->InsertColumn(0, _T("Message"));
894 m_listctrl->InsertColumn(1, _T("Time"));
895
896 // prepare the imagelist
897 static const int ICON_SIZE = 16;
898 wxImageList *imageList = new wxImageList(ICON_SIZE, ICON_SIZE);
899
900 // order should be the same as in the switch below!
389d906b 901 static const wxChar* icons[] =
f6bcfd97 902 {
389d906b
VS
903 wxART_ERROR,
904 wxART_WARNING,
905 wxART_INFORMATION
f6bcfd97
BP
906 };
907
dabbc6a5 908 bool loadedIcons = true;
f6bcfd97 909
f6bcfd97
BP
910 for ( size_t icon = 0; icon < WXSIZEOF(icons); icon++ )
911 {
389d906b
VS
912 wxBitmap bmp = wxArtProvider::GetBitmap(icons[icon], wxART_MESSAGE_BOX,
913 wxSize(ICON_SIZE, ICON_SIZE));
f6bcfd97 914
9f14a818
VZ
915 // This may very well fail if there are insufficient colours available.
916 // Degrade gracefully.
917 if ( !bmp.Ok() )
918 {
dabbc6a5 919 loadedIcons = false;
9f14a818
VZ
920
921 break;
922 }
923
389d906b 924 imageList->Add(bmp);
f6bcfd97
BP
925 }
926
927 m_listctrl->SetImageList(imageList, wxIMAGE_LIST_SMALL);
f6bcfd97
BP
928
929 // and fill it
930 wxString fmt = wxLog::GetTimestamp();
931 if ( !fmt )
932 {
933 // default format
c54a8b64 934 fmt = _T("%c");
f6bcfd97
BP
935 }
936
937 size_t count = m_messages.GetCount();
938 for ( size_t n = 0; n < count; n++ )
939 {
9f14a818
VZ
940 int image;
941
9f14a818 942 if ( loadedIcons )
f6bcfd97 943 {
9f14a818
VZ
944 switch ( m_severity[n] )
945 {
946 case wxLOG_Error:
947 image = 0;
948 break;
f6bcfd97 949
9f14a818
VZ
950 case wxLOG_Warning:
951 image = 1;
952 break;
f6bcfd97 953
9f14a818
VZ
954 default:
955 image = 2;
956 }
f6bcfd97 957 }
9f14a818 958 else // failed to load images
9f14a818 959 {
f6bcfd97 960 image = -1;
9f14a818 961 }
f6bcfd97 962
a14670e2
VZ
963 wxString msg = m_messages[n];
964 msg.Replace(wxT("\n"), wxT(" "));
965 msg = EllipsizeString(msg);
966
967 m_listctrl->InsertItem(n, msg, image);
9f14a818 968 m_listctrl->SetItem(n, 1, TimeStamp(fmt, (time_t)m_times[n]));
f6bcfd97
BP
969 }
970
971 // let the columns size themselves
972 m_listctrl->SetColumnWidth(0, wxLIST_AUTOSIZE);
973 m_listctrl->SetColumnWidth(1, wxLIST_AUTOSIZE);
974
9f14a818 975 // calculate an approximately nice height for the listctrl
7b82d782 976 int height = GetCharHeight()*(count + 4);
f6bcfd97 977
9f14a818
VZ
978 // but check that the dialog won't fall fown from the screen
979 //
980 // we use GetMinHeight() to get the height of the dialog part without the
981 // details and we consider that the "Save" button below and the separator
982 // line (and the margins around it) take about as much, hence double it
983 int heightMax = wxGetDisplaySize().y - GetPosition().y - 2*GetMinHeight();
4d509295 984
9f14a818
VZ
985 // we should leave a margin
986 heightMax *= 9;
987 heightMax /= 10;
4d509295 988
422d0ff0 989 m_listctrl->SetSize(wxDefaultCoord, wxMin(height, heightMax));
f6bcfd97
BP
990}
991
4aff28fc
VZ
992void wxLogDialog::OnListSelect(wxListEvent& event)
993{
994 // we can't just disable the control because this looks ugly under Windows
995 // (wrong bg colour, no scrolling...), but we still want to disable
996 // selecting items - it makes no sense here
225fe9d6 997 m_listctrl->SetItemState(event.GetIndex(), 0, wxLIST_STATE_SELECTED);
4aff28fc
VZ
998}
999
a14670e2
VZ
1000void wxLogDialog::OnListItemActivated(wxListEvent& event)
1001{
1002 // show the activated item in a message box
1003 // This allow the user to correctly display the logs which are longer
1004 // than the listctrl and thus gets truncated or those which contains
1005 // newlines.
1006
1007 // NB: don't do:
1008 // wxString str = m_listctrl->GetItemText(event.GetIndex());
1009 // as there's a 260 chars limit on the items inside a wxListCtrl in wxMSW.
1010 wxString str = m_messages[event.GetIndex()];
1011
1012 // wxMessageBox will nicely handle the '\n' in the string (if any)
1013 // and supports long strings
1014 wxMessageBox(str, wxT("Log message"), wxOK, this);
1015}
1016
f1df0927
VZ
1017void wxLogDialog::OnOk(wxCommandEvent& WXUNUSED(event))
1018{
1019 EndModal(wxID_OK);
1020}
1021
f6bcfd97
BP
1022#if wxUSE_FILE
1023
1024void wxLogDialog::OnSave(wxCommandEvent& WXUNUSED(event))
1025{
3e6e2754 1026#if wxUSE_FILEDLG
f6bcfd97 1027 wxFile file;
73ff84ff 1028 int rc = OpenLogFile(file, NULL, this);
f6bcfd97
BP
1029 if ( rc == -1 )
1030 {
1031 // cancelled
1032 return;
1033 }
1034
1035 bool ok = rc != 0;
1036
1037 wxString fmt = wxLog::GetTimestamp();
1038 if ( !fmt )
1039 {
1040 // default format
c54a8b64 1041 fmt = _T("%c");
f6bcfd97
BP
1042 }
1043
1044 size_t count = m_messages.GetCount();
1045 for ( size_t n = 0; ok && (n < count); n++ )
1046 {
1047 wxString line;
f94dfb38 1048 line << TimeStamp(fmt, (time_t)m_times[n])
f6bcfd97
BP
1049 << _T(": ")
1050 << m_messages[n]
1051 << wxTextFile::GetEOL();
1052
1053 ok = file.Write(line);
1054 }
1055
1056 if ( ok )
1057 ok = file.Close();
1058
1059 if ( !ok )
1060 wxLogError(_("Can't save log contents to file."));
9f14a818 1061#endif // wxUSE_FILEDLG
f6bcfd97
BP
1062}
1063
1064#endif // wxUSE_FILE
1065
f1df0927
VZ
1066wxLogDialog::~wxLogDialog()
1067{
1068 if ( m_listctrl )
1069 {
1070 delete m_listctrl->GetImageList(wxIMAGE_LIST_SMALL);
1071 }
1072}
1073
1074#endif // wxUSE_LOG_DIALOG
f6bcfd97 1075
3e6e2754 1076#if wxUSE_FILE && wxUSE_FILEDLG
f6bcfd97
BP
1077
1078// pass an uninitialized file object, the function will ask the user for the
dabbc6a5
DS
1079// filename and try to open it, returns true on success (file was opened),
1080// false if file couldn't be opened/created and -1 if the file selection
f6bcfd97 1081// dialog was cancelled
73ff84ff 1082static int OpenLogFile(wxFile& file, wxString *pFilename, wxWindow *parent)
f6bcfd97
BP
1083{
1084 // get the file name
1085 // -----------------
73ff84ff 1086 wxString filename = wxSaveFileSelector(wxT("log"), wxT("txt"), wxT("log.txt"), parent);
f6bcfd97
BP
1087 if ( !filename ) {
1088 // cancelled
1089 return -1;
1090 }
1091
1092 // open file
1093 // ---------
ba9bbf13 1094 bool bOk wxDUMMY_INITIALIZE(false);
f6bcfd97 1095 if ( wxFile::Exists(filename) ) {
dabbc6a5 1096 bool bAppend = false;
f6bcfd97
BP
1097 wxString strMsg;
1098 strMsg.Printf(_("Append log to file '%s' (choosing [No] will overwrite it)?"),
1099 filename.c_str());
1100 switch ( wxMessageBox(strMsg, _("Question"),
1101 wxICON_QUESTION | wxYES_NO | wxCANCEL) ) {
1102 case wxYES:
dabbc6a5 1103 bAppend = true;
f6bcfd97
BP
1104 break;
1105
1106 case wxNO:
dabbc6a5 1107 bAppend = false;
f6bcfd97
BP
1108 break;
1109
1110 case wxCANCEL:
1111 return -1;
1112
1113 default:
1114 wxFAIL_MSG(_("invalid message box return value"));
1115 }
1116
1117 if ( bAppend ) {
1118 bOk = file.Open(filename, wxFile::write_append);
1119 }
1120 else {
dabbc6a5 1121 bOk = file.Create(filename, true /* overwrite */);
f6bcfd97
BP
1122 }
1123 }
1124 else {
1125 bOk = file.Create(filename);
1126 }
1127
1128 if ( pFilename )
1129 *pFilename = filename;
1130
1131 return bOk;
1132}
1133
1134#endif // wxUSE_FILE
1135
1e6feb95
VZ
1136#endif // !(wxUSE_LOGGUI || wxUSE_LOGWINDOW)
1137
f561a4e0 1138#if wxUSE_LOG && wxUSE_TEXTCTRL
1e6feb95
VZ
1139
1140// ----------------------------------------------------------------------------
1141// wxLogTextCtrl implementation
1142// ----------------------------------------------------------------------------
1143
1144wxLogTextCtrl::wxLogTextCtrl(wxTextCtrl *pTextCtrl)
1145{
1146 m_pTextCtrl = pTextCtrl;
1147}
1148
1149void wxLogTextCtrl::DoLogString(const wxChar *szString, time_t WXUNUSED(t))
1150{
1151 wxString msg;
1152 TimeStamp(&msg);
1153
1e6feb95 1154 msg << szString << wxT('\n');
1e6feb95
VZ
1155 m_pTextCtrl->AppendText(msg);
1156}
1157
f561a4e0 1158#endif // wxUSE_LOG && wxUSE_TEXTCTRL