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