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