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