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