]> git.saurik.com Git - wxWidgets.git/blame - src/generic/logg.cpp
wxControl and wxDialog coded and supporting module def file.
[wxWidgets.git] / src / generic / logg.cpp
CommitLineData
dd85fc6b
VZ
1/////////////////////////////////////////////////////////////////////////////
2// Name: logg.cpp
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>
10// Licence: wxWindows license
11/////////////////////////////////////////////////////////////////////////////
12
13// ============================================================================
14// declarations
15// ============================================================================
16
17// ----------------------------------------------------------------------------
18// headers
19// ----------------------------------------------------------------------------
20
21// no #pragma implementation "log.h" because it's already in src/common/log.cpp
22
23// For compilers that support precompilation, includes "wx.h".
24#include "wx/wxprec.h"
25
26#ifdef __BORLANDC__
27 #pragma hdrstop
28#endif
29
e90c1d2a
VZ
30#if !wxUSE_GUI
31 #error "This file can't be compiled without GUI!"
dd85fc6b
VZ
32#endif
33
34#ifndef WX_PRECOMP
35 #include "wx/app.h"
f6bcfd97 36 #include "wx/button.h"
dd85fc6b
VZ
37 #include "wx/intl.h"
38 #include "wx/log.h"
39 #include "wx/menu.h"
40 #include "wx/frame.h"
41 #include "wx/filedlg.h"
8ca2f11c 42 #include "wx/msgdlg.h"
dd85fc6b 43 #include "wx/textctrl.h"
f1df0927
VZ
44 #include "wx/sizer.h"
45 #include "wx/statbmp.h"
f6bcfd97 46 #include "wx/button.h"
dd85fc6b
VZ
47#endif // WX_PRECOMP
48
49#include "wx/file.h"
50#include "wx/textfile.h"
f6bcfd97 51#include "wx/statline.h"
dd85fc6b 52
f94dfb38
VS
53#if wxUSE_LOG
54
39a7c7e1
VZ
55#ifdef __WXMSW__
56 // for OutputDebugString()
57 #include "wx/msw/private.h"
58#endif // Windows
59
f1df0927
VZ
60// may be defined to 0 for old behavior (using wxMessageBox) - shouldn't be
61// changed normally (that's why it's here and not in setup.h)
62#define wxUSE_LOG_DIALOG 1
63
64#if wxUSE_LOG_DIALOG
f1df0927 65 #include "wx/listctrl.h"
de8e98f1 66 #include "wx/imaglist.h"
f1df0927
VZ
67 #include "wx/image.h"
68#else // !wxUSE_TEXTFILE
69 #include "wx/msgdlg.h"
70#endif // wxUSE_LOG_DIALOG/!wxUSE_LOG_DIALOG
71
72// ----------------------------------------------------------------------------
73// private classes
74// ----------------------------------------------------------------------------
75
76#if wxUSE_LOG_DIALOG
77
f94dfb38
VS
78// this function is a wrapper around strftime(3)
79// allows to exclude the usage of wxDateTime
80static wxString TimeStamp(const wxChar *format, time_t t)
81{
82 wxChar buf[4096];
83 if ( !wxStrftime(buf, WXSIZEOF(buf), format, localtime(&t)) )
84 {
85 // buffer is too small?
86 wxFAIL_MSG(_T("strftime() failed"));
87 }
88 return wxString(buf);
89}
90
91
f1df0927
VZ
92class wxLogDialog : public wxDialog
93{
94public:
95 wxLogDialog(wxWindow *parent,
96 const wxArrayString& messages,
97 const wxArrayInt& severity,
98 const wxArrayLong& timess,
99 const wxString& caption,
100 long style);
101 virtual ~wxLogDialog();
102
103 // event handlers
104 void OnOk(wxCommandEvent& event);
105 void OnDetails(wxCommandEvent& event);
f6bcfd97
BP
106#if wxUSE_FILE
107 void OnSave(wxCommandEvent& event);
108#endif // wxUSE_FILE
4aff28fc 109 void OnListSelect(wxListEvent& event);
f1df0927
VZ
110
111private:
f6bcfd97
BP
112 // create controls needed for the details display
113 void CreateDetailsControls();
114
f1df0927 115 // the data for the listctrl
224ff7a5
VZ
116 wxArrayString m_messages;
117 wxArrayInt m_severity;
118 wxArrayLong m_times;
f1df0927
VZ
119
120 // the "toggle" button and its state
121 wxButton *m_btnDetails;
122 bool m_showingDetails;
123
f6bcfd97
BP
124 // the controls which are not shown initially (but only when details
125 // button is pressed)
f1df0927 126 wxListCtrl *m_listctrl;
f6bcfd97
BP
127#if wxUSE_STATLINE
128 wxStaticLine *m_statline;
129#endif // wxUSE_STATLINE
130#if wxUSE_FILE
131 wxButton *m_btnSave;
132#endif // wxUSE_FILE
f1df0927 133
5f170f33
VZ
134 // the translated "Details" string
135 static wxString ms_details;
136
f1df0927
VZ
137 DECLARE_EVENT_TABLE()
138};
139
140BEGIN_EVENT_TABLE(wxLogDialog, wxDialog)
f6bcfd97 141 EVT_BUTTON(wxID_CANCEL, wxLogDialog::OnOk)
f1df0927 142 EVT_BUTTON(wxID_MORE, wxLogDialog::OnDetails)
f6bcfd97
BP
143#if wxUSE_FILE
144 EVT_BUTTON(wxID_SAVE, wxLogDialog::OnSave)
145#endif // wxUSE_FILE
4aff28fc 146 EVT_LIST_ITEM_SELECTED(-1, wxLogDialog::OnListSelect)
f1df0927
VZ
147END_EVENT_TABLE()
148
149#endif // wxUSE_LOG_DIALOG
150
f6bcfd97
BP
151// ----------------------------------------------------------------------------
152// private functions
153// ----------------------------------------------------------------------------
154
155#if wxUSE_FILE
156
157// pass an uninitialized file object, the function will ask the user for the
158// filename and try to open it, returns TRUE on success (file was opened),
159// FALSE if file couldn't be opened/created and -1 if the file selection
160// dialog was cancelled
161static int OpenLogFile(wxFile& file, wxString *filename = NULL);
162
163#endif // wxUSE_FILE
164
dd85fc6b
VZ
165// ----------------------------------------------------------------------------
166// global variables
167// ----------------------------------------------------------------------------
168
169// we use a global variable to store the frame pointer for wxLogStatus - bad,
170// but it's he easiest way
171static wxFrame *gs_pFrame; // FIXME MT-unsafe
172
173// ============================================================================
174// implementation
175// ============================================================================
176
177// ----------------------------------------------------------------------------
178// global functions
179// ----------------------------------------------------------------------------
180
181// accepts an additional argument which tells to which frame the output should
182// be directed
183void wxLogStatus(wxFrame *pFrame, const wxChar *szFormat, ...)
184{
185 wxString msg;
186
187 wxLog *pLog = wxLog::GetActiveTarget();
188 if ( pLog != NULL ) {
189 va_list argptr;
190 va_start(argptr, szFormat);
191 msg.PrintfV(szFormat, argptr);
192 va_end(argptr);
193
194 wxASSERT( gs_pFrame == NULL ); // should be reset!
195 gs_pFrame = pFrame;
196 wxLog::OnLog(wxLOG_Status, msg, time(NULL));
197 gs_pFrame = (wxFrame *) NULL;
198 }
199}
200
201// ----------------------------------------------------------------------------
202// wxLogTextCtrl implementation
203// ----------------------------------------------------------------------------
204
205wxLogTextCtrl::wxLogTextCtrl(wxTextCtrl *pTextCtrl)
206{
207 m_pTextCtrl = pTextCtrl;
208}
209
5e0201ea 210void wxLogTextCtrl::DoLogString(const wxChar *szString, time_t WXUNUSED(t))
dd85fc6b
VZ
211{
212 wxString msg;
213 TimeStamp(&msg);
b0f1bf1c
RR
214#ifdef __WXMAC__
215 msg << szString << wxT('\r');
216#else
223d09f6 217 msg << szString << wxT('\n');
b0f1bf1c 218#endif
dd85fc6b
VZ
219
220 m_pTextCtrl->AppendText(msg);
221}
222
223// ----------------------------------------------------------------------------
224// wxLogGui implementation (FIXME MT-unsafe)
225// ----------------------------------------------------------------------------
226
227wxLogGui::wxLogGui()
228{
229 Clear();
230}
231
232void wxLogGui::Clear()
233{
5f170f33
VZ
234 m_bErrors =
235 m_bWarnings =
236 m_bHasMessages = FALSE;
237
dd85fc6b 238 m_aMessages.Empty();
f1df0927 239 m_aSeverity.Empty();
dd85fc6b
VZ
240 m_aTimes.Empty();
241}
242
243void wxLogGui::Flush()
244{
245 if ( !m_bHasMessages )
246 return;
247
248 // do it right now to block any new calls to Flush() while we're here
249 m_bHasMessages = FALSE;
250
f6bcfd97
BP
251 wxString appName = wxTheApp->GetAppName();
252 if ( !!appName )
253 appName[0u] = wxToupper(appName[0u]);
f1df0927
VZ
254
255 long style;
f6bcfd97 256 wxString titleFormat;
f1df0927 257 if ( m_bErrors ) {
f6bcfd97 258 titleFormat = _("%s Error");
f1df0927
VZ
259 style = wxICON_STOP;
260 }
261 else if ( m_bWarnings ) {
f6bcfd97 262 titleFormat = _("%s Warning");
f1df0927
VZ
263 style = wxICON_EXCLAMATION;
264 }
265 else {
f6bcfd97 266 titleFormat = _("%s Information");
f1df0927
VZ
267 style = wxICON_INFORMATION;
268 }
f6bcfd97
BP
269
270 wxString title;
271 title.Printf(titleFormat, appName.c_str());
f1df0927 272
23f681ec
VZ
273 // this is the best we can do here
274 wxWindow *parent = wxTheApp->GetTopWindow();
224ff7a5 275
23f681ec 276 size_t nMsgCount = m_aMessages.Count();
224ff7a5 277
23f681ec
VZ
278 wxString str;
279 if ( nMsgCount == 1 )
280 {
281 str = m_aMessages[0];
282 }
283 else // more than one message
284 {
285#if wxUSE_LOG_DIALOG
286 wxLogDialog dlg(parent,
287 m_aMessages, m_aSeverity, m_aTimes,
288 title, style);
f1df0927 289
23f681ec
VZ
290 // clear the message list before showing the dialog because while it's
291 // shown some new messages may appear
292 Clear();
293
294 (void)dlg.ShowModal();
f1df0927 295#else // !wxUSE_LOG_DIALOG
23f681ec
VZ
296 // concatenate all strings (but not too many to not overfill the msg box)
297 size_t nLines = 0;
dd85fc6b 298
23f681ec
VZ
299 // start from the most recent message
300 for ( size_t n = nMsgCount; n > 0; n-- ) {
301 // for Windows strings longer than this value are wrapped (NT 4.0)
302 const size_t nMsgLineWidth = 156;
dd85fc6b 303
23f681ec 304 nLines += (m_aMessages[n - 1].Len() + nMsgLineWidth - 1) / nMsgLineWidth;
dd85fc6b 305
23f681ec
VZ
306 if ( nLines > 25 ) // don't put too many lines in message box
307 break;
dd85fc6b 308
23f681ec
VZ
309 str << m_aMessages[n - 1] << wxT("\n");
310 }
311#endif // wxUSE_LOG_DIALOG/!wxUSE_LOG_DIALOG
dd85fc6b
VZ
312 }
313
23f681ec
VZ
314 // this catches both cases of 1 message with wxUSE_LOG_DIALOG and any
315 // situation without it
316 if ( !!str )
317 {
318 wxMessageBox(str, title, wxOK | style, parent);
dd85fc6b 319
23f681ec
VZ
320 // no undisplayed messages whatsoever
321 Clear();
322 }
dd85fc6b
VZ
323}
324
23f681ec 325// log all kinds of messages
dd85fc6b
VZ
326void wxLogGui::DoLog(wxLogLevel level, const wxChar *szString, time_t t)
327{
328 switch ( level ) {
329 case wxLOG_Info:
330 if ( GetVerbose() )
331 case wxLOG_Message:
f1df0927 332 {
dd85fc6b
VZ
333 if ( !m_bErrors ) {
334 m_aMessages.Add(szString);
f1df0927 335 m_aSeverity.Add(wxLOG_Message);
dd85fc6b
VZ
336 m_aTimes.Add((long)t);
337 m_bHasMessages = TRUE;
338 }
f1df0927
VZ
339 }
340 break;
dd85fc6b
VZ
341
342 case wxLOG_Status:
343#if wxUSE_STATUSBAR
f1df0927
VZ
344 {
345 // find the top window and set it's status text if it has any
346 wxFrame *pFrame = gs_pFrame;
347 if ( pFrame == NULL ) {
348 wxWindow *pWin = wxTheApp->GetTopWindow();
349 if ( pWin != NULL && pWin->IsKindOf(CLASSINFO(wxFrame)) ) {
350 pFrame = (wxFrame *)pWin;
dd85fc6b 351 }
dd85fc6b 352 }
f1df0927
VZ
353
354 if ( pFrame && pFrame->GetStatusBar() )
355 pFrame->SetStatusText(szString);
356 }
dd85fc6b 357#endif // wxUSE_STATUSBAR
f1df0927 358 break;
dd85fc6b
VZ
359
360 case wxLOG_Trace:
361 case wxLOG_Debug:
f1df0927
VZ
362 #ifdef __WXDEBUG__
363 {
364 #ifdef __WXMSW__
365 // don't prepend debug/trace here: it goes to the
366 // debug window anyhow, but do put a timestamp
367 wxString str;
368 TimeStamp(&str);
df4b7d98 369 str << szString << wxT("\r\n");
f1df0927
VZ
370 OutputDebugString(str);
371 #else
372 // send them to stderr
373 wxFprintf(stderr, wxT("%s: %s\n"),
374 level == wxLOG_Trace ? wxT("Trace")
375 : wxT("Debug"),
376 szString);
377 fflush(stderr);
378 #endif
379 }
380 #endif // __WXDEBUG__
dd85fc6b 381
f1df0927 382 break;
dd85fc6b
VZ
383
384 case wxLOG_FatalError:
f1df0927
VZ
385 // show this one immediately
386 wxMessageBox(szString, _("Fatal error"), wxICON_HAND);
a2a0d40d 387 wxExit();
f1df0927 388 break;
dd85fc6b
VZ
389
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
400 m_bErrors = TRUE;
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
407 m_bWarnings = TRUE;
408 }
409
410 m_aMessages.Add(szString);
06b466c7 411 m_aSeverity.Add((int)level);
f1df0927
VZ
412 m_aTimes.Add((long)t);
413 m_bHasMessages = TRUE;
414 break;
dd85fc6b
VZ
415 }
416}
417
418// ----------------------------------------------------------------------------
419// wxLogWindow and wxLogFrame implementation
420// ----------------------------------------------------------------------------
421
422// log frame class
423// ---------------
424class wxLogFrame : public wxFrame
425{
426public:
427 // ctor & dtor
428 wxLogFrame(wxFrame *pParent, wxLogWindow *log, const wxChar *szTitle);
429 virtual ~wxLogFrame();
430
431 // menu callbacks
432 void OnClose(wxCommandEvent& event);
433 void OnCloseWindow(wxCloseEvent& event);
434#if wxUSE_FILE
435 void OnSave (wxCommandEvent& event);
436#endif // wxUSE_FILE
437 void OnClear(wxCommandEvent& event);
438
439 void OnIdle(wxIdleEvent&);
440
441 // accessors
442 wxTextCtrl *TextCtrl() const { return m_pTextCtrl; }
443
444private:
857eb081 445 // use standard ids for our commands!
dd85fc6b
VZ
446 enum
447 {
857eb081
VZ
448 Menu_Close = wxID_CLOSE,
449 Menu_Save = wxID_SAVE,
450 Menu_Clear = wxID_CLEAR
dd85fc6b
VZ
451 };
452
f6bcfd97
BP
453 // common part of OnClose() and OnCloseWindow()
454 void DoClose();
dd85fc6b
VZ
455
456 wxTextCtrl *m_pTextCtrl;
457 wxLogWindow *m_log;
458
459 DECLARE_EVENT_TABLE()
460};
461
462BEGIN_EVENT_TABLE(wxLogFrame, wxFrame)
463 // wxLogWindow menu events
464 EVT_MENU(Menu_Close, wxLogFrame::OnClose)
465#if wxUSE_FILE
466 EVT_MENU(Menu_Save, wxLogFrame::OnSave)
467#endif // wxUSE_FILE
468 EVT_MENU(Menu_Clear, wxLogFrame::OnClear)
469
470 EVT_CLOSE(wxLogFrame::OnCloseWindow)
471END_EVENT_TABLE()
472
473wxLogFrame::wxLogFrame(wxFrame *pParent, wxLogWindow *log, const wxChar *szTitle)
474 : wxFrame(pParent, -1, szTitle)
475{
476 m_log = log;
477
478 m_pTextCtrl = new wxTextCtrl(this, -1, wxEmptyString, wxDefaultPosition,
479 wxDefaultSize,
480 wxTE_MULTILINE |
481 wxHSCROLL |
482 wxTE_READONLY);
483
484 // create menu
485 wxMenuBar *pMenuBar = new wxMenuBar;
486 wxMenu *pMenu = new wxMenu;
487#if wxUSE_FILE
488 pMenu->Append(Menu_Save, _("&Save..."), _("Save log contents to file"));
489#endif // wxUSE_FILE
490 pMenu->Append(Menu_Clear, _("C&lear"), _("Clear the log contents"));
491 pMenu->AppendSeparator();
492 pMenu->Append(Menu_Close, _("&Close"), _("Close this window"));
493 pMenuBar->Append(pMenu, _("&Log"));
494 SetMenuBar(pMenuBar);
495
496#if wxUSE_STATUSBAR
497 // status bar for menu prompts
498 CreateStatusBar();
499#endif // wxUSE_STATUSBAR
500
501 m_log->OnFrameCreate(this);
502}
503
f6bcfd97
BP
504void wxLogFrame::DoClose()
505{
506 if ( m_log->OnFrameClose(this) )
507 {
508 // instead of closing just hide the window to be able to Show() it
509 // later
510 Show(FALSE);
511 }
512}
513
dd85fc6b
VZ
514void wxLogFrame::OnClose(wxCommandEvent& WXUNUSED(event))
515{
516 DoClose();
517}
518
519void wxLogFrame::OnCloseWindow(wxCloseEvent& WXUNUSED(event))
520{
521 DoClose();
522}
523
524#if wxUSE_FILE
525void wxLogFrame::OnSave(wxCommandEvent& WXUNUSED(event))
526{
f6bcfd97
BP
527 wxString filename;
528 wxFile file;
529 int rc = OpenLogFile(file, &filename);
530 if ( rc == -1 )
531 {
dd85fc6b
VZ
532 // cancelled
533 return;
534 }
535
f6bcfd97 536 bool bOk = rc != 0;
dd85fc6b
VZ
537
538 // retrieve text and save it
539 // -------------------------
540 int nLines = m_pTextCtrl->GetNumberOfLines();
541 for ( int nLine = 0; bOk && nLine < nLines; nLine++ ) {
542 bOk = file.Write(m_pTextCtrl->GetLineText(nLine) +
f6bcfd97 543 wxTextFile::GetEOL());
dd85fc6b
VZ
544 }
545
546 if ( bOk )
547 bOk = file.Close();
548
549 if ( !bOk ) {
550 wxLogError(_("Can't save log contents to file."));
551 }
552 else {
f6bcfd97 553 wxLogStatus(this, _("Log saved to the file '%s'."), filename.c_str());
dd85fc6b
VZ
554 }
555}
556#endif // wxUSE_FILE
557
558void wxLogFrame::OnClear(wxCommandEvent& WXUNUSED(event))
559{
560 m_pTextCtrl->Clear();
561}
562
563wxLogFrame::~wxLogFrame()
564{
565 m_log->OnFrameDelete(this);
566}
567
568// wxLogWindow
569// -----------
570wxLogWindow::wxLogWindow(wxFrame *pParent,
571 const wxChar *szTitle,
572 bool bShow,
573 bool bDoPass)
574{
575 m_bPassMessages = bDoPass;
576
577 m_pLogFrame = new wxLogFrame(pParent, this, szTitle);
578 m_pOldLog = wxLog::SetActiveTarget(this);
579
580 if ( bShow )
581 m_pLogFrame->Show(TRUE);
582}
583
584void wxLogWindow::Show(bool bShow)
585{
586 m_pLogFrame->Show(bShow);
587}
588
589void wxLogWindow::Flush()
590{
591 if ( m_pOldLog != NULL )
592 m_pOldLog->Flush();
593
594 m_bHasMessages = FALSE;
595}
596
597void wxLogWindow::DoLog(wxLogLevel level, const wxChar *szString, time_t t)
598{
599 // first let the previous logger show it
600 if ( m_pOldLog != NULL && m_bPassMessages ) {
f1df0927 601 // bogus cast just to access protected DoLog
dd85fc6b
VZ
602 ((wxLogWindow *)m_pOldLog)->DoLog(level, szString, t);
603 }
604
605 if ( m_pLogFrame ) {
606 switch ( level ) {
607 case wxLOG_Status:
608 // by default, these messages are ignored by wxLog, so process
609 // them ourselves
610 if ( !wxIsEmpty(szString) )
611 {
612 wxString str;
613 str << _("Status: ") << szString;
614 DoLogString(str, t);
615 }
616 break;
617
618 // don't put trace messages in the text window for 2 reasons:
619 // 1) there are too many of them
620 // 2) they may provoke other trace messages thus sending a program
621 // into an infinite loop
622 case wxLOG_Trace:
623 break;
624
625 default:
626 // and this will format it nicely and call our DoLogString()
627 wxLog::DoLog(level, szString, t);
628 }
629 }
630
631 m_bHasMessages = TRUE;
632}
633
634void wxLogWindow::DoLogString(const wxChar *szString, time_t WXUNUSED(t))
635{
636 // put the text into our window
637 wxTextCtrl *pText = m_pLogFrame->TextCtrl();
638
639 // remove selection (WriteText is in fact ReplaceSelection)
640#ifdef __WXMSW__
641 long nLen = pText->GetLastPosition();
642 pText->SetSelection(nLen, nLen);
643#endif // Windows
644
645 wxString msg;
646 TimeStamp(&msg);
223d09f6 647 msg << szString << wxT('\n');
dd85fc6b
VZ
648
649 pText->AppendText(msg);
650
651 // TODO ensure that the line can be seen
652}
653
654wxFrame *wxLogWindow::GetFrame() const
655{
656 return m_pLogFrame;
657}
658
659void wxLogWindow::OnFrameCreate(wxFrame * WXUNUSED(frame))
660{
661}
662
f6bcfd97
BP
663bool wxLogWindow::OnFrameClose(wxFrame * WXUNUSED(frame))
664{
665 // allow to close
666 return TRUE;
667}
668
dd85fc6b
VZ
669void wxLogWindow::OnFrameDelete(wxFrame * WXUNUSED(frame))
670{
671 m_pLogFrame = (wxLogFrame *)NULL;
672}
673
674wxLogWindow::~wxLogWindow()
675{
676 delete m_pOldLog;
677
678 // may be NULL if log frame already auto destroyed itself
679 delete m_pLogFrame;
680}
681
f1df0927
VZ
682// ----------------------------------------------------------------------------
683// wxLogDialog
684// ----------------------------------------------------------------------------
685
686#if wxUSE_LOG_DIALOG
687
688static const size_t MARGIN = 10;
689
5f170f33
VZ
690wxString wxLogDialog::ms_details;
691
f1df0927
VZ
692wxLogDialog::wxLogDialog(wxWindow *parent,
693 const wxArrayString& messages,
694 const wxArrayInt& severity,
695 const wxArrayLong& times,
696 const wxString& caption,
697 long style)
f6bcfd97 698 : wxDialog(parent, -1, caption)
f1df0927 699{
669f7a11 700 if ( ms_details.IsEmpty() )
5f170f33 701 {
f6bcfd97
BP
702 // ensure that we won't loop here if wxGetTranslation()
703 // happens to pop up a Log message while translating this :-)
704 ms_details = wxTRANSLATE("&Details");
5f170f33
VZ
705 ms_details = wxGetTranslation(ms_details);
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 {
715 wxString msg = messages[n];
716 do
717 {
718 m_messages.Add(msg.BeforeFirst(_T('\n')));
719 msg = msg.AfterFirst(_T('\n'));
720
721 m_severity.Add(severity[n]);
722 m_times.Add(times[n]);
723 }
724 while ( !!msg );
725 }
726
f1df0927
VZ
727 m_showingDetails = FALSE; // not initially
728 m_listctrl = (wxListCtrl *)NULL;
729
f6bcfd97
BP
730#if wxUSE_STATLINE
731 m_statline = (wxStaticLine *)NULL;
732#endif // wxUSE_STATLINE
733
734#if wxUSE_FILE
735 m_btnSave = (wxButton *)NULL;
736#endif // wxUSE_FILE
737
f1df0927
VZ
738 // create the controls which are always shown and layout them: we use
739 // sizers even though our window is not resizeable to calculate the size of
740 // the dialog properly
741 wxBoxSizer *sizerTop = new wxBoxSizer(wxVERTICAL);
742 wxBoxSizer *sizerButtons = new wxBoxSizer(wxVERTICAL);
743 wxBoxSizer *sizerAll = new wxBoxSizer(wxHORIZONTAL);
744
f6bcfd97
BP
745 // this "Ok" button has wxID_CANCEL id - not very logical, but this allows
746 // to close the log dialog with <Esc> which wouldn't work otherwise (as it
747 // translates into click on cancel button)
748 wxButton *btnOk = new wxButton(this, wxID_CANCEL, _("OK"));
f1df0927 749 sizerButtons->Add(btnOk, 0, wxCENTRE|wxBOTTOM, MARGIN/2);
5f170f33 750 m_btnDetails = new wxButton(this, wxID_MORE, ms_details + _T(" >>"));
f1df0927
VZ
751 sizerButtons->Add(m_btnDetails, 0, wxCENTRE|wxTOP, MARGIN/2 - 1);
752
669f7a11 753#ifndef __WIN16__
06b466c7 754 wxIcon icon = wxTheApp->GetStdIcon((int)(style & wxICON_MASK));
f1df0927 755 sizerAll->Add(new wxStaticBitmap(this, -1, icon), 0, wxCENTRE);
f6bcfd97 756#endif // !Win16
669f7a11 757
f1df0927
VZ
758 const wxString& message = messages.Last();
759 sizerAll->Add(CreateTextSizer(message), 0, wxCENTRE|wxLEFT|wxRIGHT, MARGIN);
760 sizerAll->Add(sizerButtons, 0, wxALIGN_RIGHT|wxLEFT, MARGIN);
761
762 sizerTop->Add(sizerAll, 0, wxCENTRE|wxALL, MARGIN);
763
764 SetAutoLayout(TRUE);
765 SetSizer(sizerTop);
766
767 sizerTop->SetSizeHints(this);
768 sizerTop->Fit(this);
769
770 btnOk->SetFocus();
771
23f681ec
VZ
772 // this can't happen any more as we don't use this dialog in this case
773#if 0
4aff28fc 774 if ( count == 1 )
50af7fa9
VZ
775 {
776 // no details... it's easier to disable a button than to change the
777 // dialog layout depending on whether we have details or not
778 m_btnDetails->Disable();
779 }
23f681ec 780#endif // 0
50af7fa9 781
f1df0927
VZ
782 Centre();
783}
784
f6bcfd97
BP
785void wxLogDialog::CreateDetailsControls()
786{
787 // create the save button and separator line if possible
788#if wxUSE_FILE
789 m_btnSave = new wxButton(this, wxID_SAVE, _("&Save..."));
790#endif // wxUSE_FILE
791
792#if wxUSE_STATLINE
793 m_statline = new wxStaticLine(this, -1);
794#endif // wxUSE_STATLINE
795
796 // create the list ctrl now
797 m_listctrl = new wxListCtrl(this, -1,
798 wxDefaultPosition, wxDefaultSize,
799 wxSUNKEN_BORDER |
800 wxLC_REPORT |
801 wxLC_NO_HEADER |
802 wxLC_SINGLE_SEL);
803
804 // no need to translate these strings as they're not shown to the
805 // user anyhow (we use wxLC_NO_HEADER style)
806 m_listctrl->InsertColumn(0, _T("Message"));
807 m_listctrl->InsertColumn(1, _T("Time"));
808
809 // prepare the imagelist
810 static const int ICON_SIZE = 16;
811 wxImageList *imageList = new wxImageList(ICON_SIZE, ICON_SIZE);
812
813 // order should be the same as in the switch below!
814 static const int icons[] =
815 {
816 wxICON_ERROR,
817 wxICON_EXCLAMATION,
818 wxICON_INFORMATION
819 };
820
821 bool loadedIcons = TRUE;
822
823#ifndef __WIN16__
824 for ( size_t icon = 0; icon < WXSIZEOF(icons); icon++ )
825 {
826 wxBitmap bmp = wxTheApp->GetStdIcon(icons[icon]);
827
828 // This may very well fail if there are insufficient
829 // colours available. Degrade gracefully.
830
831 if (!bmp.Ok())
832 loadedIcons = FALSE;
833 else
834 imageList->Add(wxImage(bmp).
835 Rescale(ICON_SIZE, ICON_SIZE).
836 ConvertToBitmap());
837 }
838
839 m_listctrl->SetImageList(imageList, wxIMAGE_LIST_SMALL);
840#endif // !Win16
841
842 // and fill it
843 wxString fmt = wxLog::GetTimestamp();
844 if ( !fmt )
845 {
846 // default format
847 fmt = _T("%c");
848 }
849
850 size_t count = m_messages.GetCount();
851 for ( size_t n = 0; n < count; n++ )
852 {
853 int image = -1;
854#ifndef __WIN16__
855 switch ( m_severity[n] )
856 {
857 case wxLOG_Error:
858 image = 0;
859 break;
860
861 case wxLOG_Warning:
862 image = 1;
863 break;
864
865 default:
866 image = 2;
867 }
868#endif // !Win16
869
870 if (!loadedIcons)
871 image = -1;
872
873 if (image > -1)
874 m_listctrl->InsertItem(n, m_messages[n], image);
875 else
876 m_listctrl->InsertItem(n, m_messages[n]);
877
878 m_listctrl->SetItem(n, 1,
f94dfb38 879 TimeStamp(fmt, (time_t)m_times[n]));
f6bcfd97
BP
880 }
881
882 // let the columns size themselves
883 m_listctrl->SetColumnWidth(0, wxLIST_AUTOSIZE);
884 m_listctrl->SetColumnWidth(1, wxLIST_AUTOSIZE);
885
886 // get the approx height of the listctrl
887 wxFont font = GetFont();
888 if ( !font.Ok() )
889 font = *wxSWISS_FONT;
890
891 int y;
892 GetTextExtent(_T("H"), (int*)NULL, &y, (int*)NULL, (int*)NULL, &font);
893 int height = wxMax(y*(count + 3), 100);
894 m_listctrl->SetSize(-1, height);
895}
896
4aff28fc
VZ
897void wxLogDialog::OnListSelect(wxListEvent& event)
898{
899 // we can't just disable the control because this looks ugly under Windows
900 // (wrong bg colour, no scrolling...), but we still want to disable
901 // selecting items - it makes no sense here
225fe9d6 902 m_listctrl->SetItemState(event.GetIndex(), 0, wxLIST_STATE_SELECTED);
4aff28fc
VZ
903}
904
f1df0927
VZ
905void wxLogDialog::OnOk(wxCommandEvent& WXUNUSED(event))
906{
907 EndModal(wxID_OK);
908}
909
f6bcfd97
BP
910#if wxUSE_FILE
911
912void wxLogDialog::OnSave(wxCommandEvent& WXUNUSED(event))
913{
914 wxFile file;
915 int rc = OpenLogFile(file);
916 if ( rc == -1 )
917 {
918 // cancelled
919 return;
920 }
921
922 bool ok = rc != 0;
923
924 wxString fmt = wxLog::GetTimestamp();
925 if ( !fmt )
926 {
927 // default format
928 fmt = _T("%c");
929 }
930
931 size_t count = m_messages.GetCount();
932 for ( size_t n = 0; ok && (n < count); n++ )
933 {
934 wxString line;
f94dfb38 935 line << TimeStamp(fmt, (time_t)m_times[n])
f6bcfd97
BP
936 << _T(": ")
937 << m_messages[n]
938 << wxTextFile::GetEOL();
939
940 ok = file.Write(line);
941 }
942
943 if ( ok )
944 ok = file.Close();
945
946 if ( !ok )
947 wxLogError(_("Can't save log contents to file."));
948}
949
950#endif // wxUSE_FILE
951
f1df0927
VZ
952void wxLogDialog::OnDetails(wxCommandEvent& WXUNUSED(event))
953{
954 wxSizer *sizer = GetSizer();
955
956 if ( m_showingDetails )
957 {
5f170f33 958 m_btnDetails->SetLabel(ms_details + _T(">>"));
f1df0927
VZ
959
960 sizer->Remove(m_listctrl);
f6bcfd97
BP
961
962#if wxUSE_STATLINE
963 sizer->Remove(m_statline);
964#endif // wxUSE_STATLINE
965
966#if wxUSE_FILE
967 sizer->Remove(m_btnSave);
968#endif // wxUSE_FILE
f1df0927
VZ
969 }
970 else // show details now
971 {
5f170f33 972 m_btnDetails->SetLabel(wxString(_T("<< ")) + ms_details);
f1df0927
VZ
973
974 if ( !m_listctrl )
975 {
f6bcfd97
BP
976 CreateDetailsControls();
977 }
f1df0927 978
f6bcfd97
BP
979#if wxUSE_STATLINE
980 sizer->Add(m_statline, 0, wxEXPAND | (wxALL & ~wxTOP), MARGIN);
981#endif // wxUSE_STATLINE
f1df0927 982
f6bcfd97 983 sizer->Add(m_listctrl, 1, wxEXPAND | (wxALL & ~wxTOP), MARGIN);
f1df0927 984
f6bcfd97
BP
985#if wxUSE_FILE
986 sizer->Add(m_btnSave, 0, wxALIGN_RIGHT | (wxALL & ~wxTOP), MARGIN);
987#endif // wxUSE_FILE
f1df0927
VZ
988 }
989
990 m_showingDetails = !m_showingDetails;
991
992 // in any case, our size changed - update
ea451729 993 sizer->SetSizeHints(this);
f1df0927 994 sizer->Fit(this);
f6bcfd97
BP
995
996#ifdef __WXGTK__
997 // VS: this is neccessary in order to force frame redraw under
998 // WindowMaker or fvwm2 (and probably other broken WMs).
999 // Otherwise, detailed list wouldn't be displayed.
1000 Show(TRUE);
1001#endif // wxGTK
f1df0927
VZ
1002}
1003
1004wxLogDialog::~wxLogDialog()
1005{
1006 if ( m_listctrl )
1007 {
1008 delete m_listctrl->GetImageList(wxIMAGE_LIST_SMALL);
1009 }
1010}
1011
1012#endif // wxUSE_LOG_DIALOG
f6bcfd97
BP
1013
1014#if wxUSE_FILE
1015
1016// pass an uninitialized file object, the function will ask the user for the
1017// filename and try to open it, returns TRUE on success (file was opened),
1018// FALSE if file couldn't be opened/created and -1 if the file selection
1019// dialog was cancelled
1020static int OpenLogFile(wxFile& file, wxString *pFilename)
1021{
1022 // get the file name
1023 // -----------------
1024 wxString filename = wxSaveFileSelector(wxT("log"), wxT("txt"), wxT("log.txt"));
1025 if ( !filename ) {
1026 // cancelled
1027 return -1;
1028 }
1029
1030 // open file
1031 // ---------
1032 bool bOk = FALSE;
1033 if ( wxFile::Exists(filename) ) {
1034 bool bAppend = FALSE;
1035 wxString strMsg;
1036 strMsg.Printf(_("Append log to file '%s' (choosing [No] will overwrite it)?"),
1037 filename.c_str());
1038 switch ( wxMessageBox(strMsg, _("Question"),
1039 wxICON_QUESTION | wxYES_NO | wxCANCEL) ) {
1040 case wxYES:
1041 bAppend = TRUE;
1042 break;
1043
1044 case wxNO:
1045 bAppend = FALSE;
1046 break;
1047
1048 case wxCANCEL:
1049 return -1;
1050
1051 default:
1052 wxFAIL_MSG(_("invalid message box return value"));
1053 }
1054
1055 if ( bAppend ) {
1056 bOk = file.Open(filename, wxFile::write_append);
1057 }
1058 else {
1059 bOk = file.Create(filename, TRUE /* overwrite */);
1060 }
1061 }
1062 else {
1063 bOk = file.Create(filename);
1064 }
1065
1066 if ( pFilename )
1067 *pFilename = filename;
1068
1069 return bOk;
1070}
1071
1072#endif // wxUSE_FILE
1073
f94dfb38 1074#endif // wxUSE_LOG