home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / wxos2240.zip / wxWindows-2.4.0 / src / generic / logg.cpp < prev    next >
C/C++ Source or Header  |  2002-08-05  |  34KB  |  1,179 lines

  1. /////////////////////////////////////////////////////////////////////////////
  2. // Name:        src/generic/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: logg.cpp,v 1.55 2002/08/05 15:53:21 DW Exp $
  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.  
  30. #if !wxUSE_GUI
  31.     #error "This file can't be compiled without GUI!"
  32. #endif
  33.  
  34. #ifndef WX_PRECOMP
  35.     #include "wx/app.h"
  36.     #include "wx/button.h"
  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"
  42.     #include "wx/msgdlg.h"
  43.     #include "wx/textctrl.h"
  44.     #include "wx/sizer.h"
  45.     #include "wx/statbmp.h"
  46.     #include "wx/button.h"
  47.     #include "wx/settings.h"
  48. #endif // WX_PRECOMP
  49.  
  50. #if wxUSE_LOGGUI || wxUSE_LOGWINDOW
  51.  
  52. #include "wx/file.h"
  53. #include "wx/textfile.h"
  54. #include "wx/statline.h"
  55. #include "wx/artprov.h"
  56.  
  57. #ifdef  __WXMSW__
  58.   // for OutputDebugString()
  59.   #include  "wx/msw/private.h"
  60. #endif // Windows
  61.  
  62. #ifdef  __WXPM__
  63.   #include <time.h>
  64. #endif
  65.  
  66. #if wxUSE_LOG_DIALOG
  67.     #include "wx/listctrl.h"
  68.     #include "wx/imaglist.h"
  69.     #include "wx/image.h"
  70. #else // !wxUSE_LOG_DIALOG
  71.     #include "wx/msgdlg.h"
  72. #endif // wxUSE_LOG_DIALOG/!wxUSE_LOG_DIALOG
  73.  
  74. // the suffix we add to the button to show that the dialog can be expanded
  75. #define EXPAND_SUFFIX _T(" >>")
  76.  
  77. // ----------------------------------------------------------------------------
  78. // private classes
  79. // ----------------------------------------------------------------------------
  80.  
  81. #if wxUSE_LOG_DIALOG
  82.  
  83. // this function is a wrapper around strftime(3)
  84. // allows to exclude the usage of wxDateTime
  85. static wxString TimeStamp(const wxChar *format, time_t t)
  86. {
  87.     wxChar buf[4096];
  88.     if ( !wxStrftime(buf, WXSIZEOF(buf), format, localtime(&t)) )
  89.     {
  90.         // buffer is too small?
  91.         wxFAIL_MSG(_T("strftime() failed"));
  92.     }
  93.     return wxString(buf);
  94. }
  95.  
  96.  
  97. class wxLogDialog : public wxDialog
  98. {
  99. public:
  100.     wxLogDialog(wxWindow *parent,
  101.                 const wxArrayString& messages,
  102.                 const wxArrayInt& severity,
  103.                 const wxArrayLong& timess,
  104.                 const wxString& caption,
  105.                 long style);
  106.     virtual ~wxLogDialog();
  107.  
  108.     // event handlers
  109.     void OnOk(wxCommandEvent& event);
  110.     void OnDetails(wxCommandEvent& event);
  111. #if wxUSE_FILE
  112.     void OnSave(wxCommandEvent& event);
  113. #endif // wxUSE_FILE
  114.     void OnListSelect(wxListEvent& event);
  115.  
  116. private:
  117.     // create controls needed for the details display
  118.     void CreateDetailsControls();
  119.  
  120.     // the data for the listctrl
  121.     wxArrayString m_messages;
  122.     wxArrayInt m_severity;
  123.     wxArrayLong m_times;
  124.  
  125.     // the "toggle" button and its state
  126.     wxButton *m_btnDetails;
  127.     bool      m_showingDetails;
  128.  
  129.     // the controls which are not shown initially (but only when details
  130.     // button is pressed)
  131.     wxListCtrl *m_listctrl;
  132. #if wxUSE_STATLINE
  133.     wxStaticLine *m_statline;
  134. #endif // wxUSE_STATLINE
  135. #if wxUSE_FILE
  136.     wxButton *m_btnSave;
  137. #endif // wxUSE_FILE
  138.  
  139.     // the translated "Details" string
  140.     static wxString ms_details;
  141.  
  142.     DECLARE_EVENT_TABLE()
  143. };
  144.  
  145. BEGIN_EVENT_TABLE(wxLogDialog, wxDialog)
  146.     EVT_BUTTON(wxID_CANCEL, wxLogDialog::OnOk)
  147.     EVT_BUTTON(wxID_MORE,   wxLogDialog::OnDetails)
  148. #if wxUSE_FILE
  149.     EVT_BUTTON(wxID_SAVE,   wxLogDialog::OnSave)
  150. #endif // wxUSE_FILE
  151.     EVT_LIST_ITEM_SELECTED(-1, wxLogDialog::OnListSelect)
  152. END_EVENT_TABLE()
  153.  
  154. #endif // wxUSE_LOG_DIALOG
  155.  
  156. // ----------------------------------------------------------------------------
  157. // private functions
  158. // ----------------------------------------------------------------------------
  159.  
  160. #if wxUSE_FILE && wxUSE_FILEDLG
  161.  
  162. // pass an uninitialized file object, the function will ask the user for the
  163. // filename and try to open it, returns TRUE on success (file was opened),
  164. // FALSE if file couldn't be opened/created and -1 if the file selection
  165. // dialog was cancelled
  166. static int OpenLogFile(wxFile& file, wxString *filename = NULL);
  167.  
  168. #endif // wxUSE_FILE
  169.  
  170. // ----------------------------------------------------------------------------
  171. // global variables
  172. // ----------------------------------------------------------------------------
  173.  
  174. // we use a global variable to store the frame pointer for wxLogStatus - bad,
  175. // but it's the easiest way
  176. static wxFrame *gs_pFrame = NULL; // FIXME MT-unsafe
  177.  
  178. // ============================================================================
  179. // implementation
  180. // ============================================================================
  181.  
  182. // ----------------------------------------------------------------------------
  183. // global functions
  184. // ----------------------------------------------------------------------------
  185.  
  186. // accepts an additional argument which tells to which frame the output should
  187. // be directed
  188. void wxVLogStatus(wxFrame *pFrame, const wxChar *szFormat, va_list argptr)
  189. {
  190.   wxString msg;
  191.  
  192.   wxLog *pLog = wxLog::GetActiveTarget();
  193.   if ( pLog != NULL ) {
  194.     msg.PrintfV(szFormat, argptr);
  195.  
  196.     wxASSERT( gs_pFrame == NULL ); // should be reset!
  197.     gs_pFrame = pFrame;
  198.     wxLog::OnLog(wxLOG_Status, msg, time(NULL));
  199.     gs_pFrame = (wxFrame *) NULL;
  200.   }
  201. }
  202.  
  203. void wxLogStatus(wxFrame *pFrame, const wxChar *szFormat, ...)
  204. {
  205.     va_list argptr;
  206.     va_start(argptr, szFormat);
  207.     wxVLogStatus(pFrame, szFormat, argptr);
  208.     va_end(argptr);
  209. }
  210.  
  211. // ----------------------------------------------------------------------------
  212. // wxLogGui implementation (FIXME MT-unsafe)
  213. // ----------------------------------------------------------------------------
  214.  
  215. wxLogGui::wxLogGui()
  216. {
  217.     Clear();
  218. }
  219.  
  220. void wxLogGui::Clear()
  221. {
  222.     m_bErrors =
  223.     m_bWarnings =
  224.     m_bHasMessages = FALSE;
  225.  
  226.     m_aMessages.Empty();
  227.     m_aSeverity.Empty();
  228.     m_aTimes.Empty();
  229. }
  230.  
  231. void wxLogGui::Flush()
  232. {
  233.     if ( !m_bHasMessages )
  234.         return;
  235.  
  236.     // do it right now to block any new calls to Flush() while we're here
  237.     m_bHasMessages = FALSE;
  238.  
  239.     wxString appName = wxTheApp->GetAppName();
  240.     if ( !!appName )
  241.         appName[0u] = wxToupper(appName[0u]);
  242.  
  243.     long style;
  244.     wxString titleFormat;
  245.     if ( m_bErrors ) {
  246.         titleFormat = _("%s Error");
  247.         style = wxICON_STOP;
  248.     }
  249.     else if ( m_bWarnings ) {
  250.         titleFormat = _("%s Warning");
  251.         style = wxICON_EXCLAMATION;
  252.     }
  253.     else {
  254.         titleFormat = _("%s Information");
  255.         style = wxICON_INFORMATION;
  256.     }
  257.  
  258.     wxString title;
  259.     title.Printf(titleFormat, appName.c_str());
  260.  
  261.     size_t nMsgCount = m_aMessages.Count();
  262.  
  263.     // avoid showing other log dialogs until we're done with the dialog we're
  264.     // showing right now: nested modal dialogs make for really bad UI!
  265.     Suspend();
  266.  
  267.     wxString str;
  268.     if ( nMsgCount == 1 )
  269.     {
  270.         str = m_aMessages[0];
  271.     }
  272.     else // more than one message
  273.     {
  274. #if wxUSE_LOG_DIALOG
  275.  
  276.         wxLogDialog dlg(NULL,
  277.                         m_aMessages, m_aSeverity, m_aTimes,
  278.                         title, style);
  279.  
  280.         // clear the message list before showing the dialog because while it's
  281.         // shown some new messages may appear
  282.         Clear();
  283.  
  284.         (void)dlg.ShowModal();
  285. #else // !wxUSE_LOG_DIALOG
  286.         // concatenate all strings (but not too many to not overfill the msg box)
  287.         size_t nLines = 0;
  288.  
  289.         // start from the most recent message
  290.         for ( size_t n = nMsgCount; n > 0; n-- ) {
  291.             // for Windows strings longer than this value are wrapped (NT 4.0)
  292.             const size_t nMsgLineWidth = 156;
  293.  
  294.             nLines += (m_aMessages[n - 1].Len() + nMsgLineWidth - 1) / nMsgLineWidth;
  295.  
  296.             if ( nLines > 25 )  // don't put too many lines in message box
  297.                 break;
  298.  
  299.             str << m_aMessages[n - 1] << wxT("\n");
  300.         }
  301. #endif // wxUSE_LOG_DIALOG/!wxUSE_LOG_DIALOG
  302.     }
  303.  
  304.     // this catches both cases of 1 message with wxUSE_LOG_DIALOG and any
  305.     // situation without it
  306.     if ( !!str )
  307.     {
  308.         wxMessageBox(str, title, wxOK | style);
  309.  
  310.         // no undisplayed messages whatsoever
  311.         Clear();
  312.     }
  313.  
  314.     // allow flushing the logs again
  315.     Resume();
  316. }
  317.  
  318. // log all kinds of messages
  319. void wxLogGui::DoLog(wxLogLevel level, const wxChar *szString, time_t t)
  320. {
  321.     switch ( level ) {
  322.         case wxLOG_Info:
  323.             if ( GetVerbose() )
  324.         case wxLOG_Message:
  325.             {
  326.                 m_aMessages.Add(szString);
  327.                 m_aSeverity.Add(wxLOG_Message);
  328.                 m_aTimes.Add((long)t);
  329.                 m_bHasMessages = TRUE;
  330.             }
  331.             break;
  332.  
  333.         case wxLOG_Status:
  334. #if wxUSE_STATUSBAR
  335.             {
  336.                 // find the top window and set it's status text if it has any
  337.                 wxFrame *pFrame = gs_pFrame;
  338.                 if ( pFrame == NULL ) {
  339.                     wxWindow *pWin = wxTheApp->GetTopWindow();
  340.                     if ( pWin != NULL && pWin->IsKindOf(CLASSINFO(wxFrame)) ) {
  341.                         pFrame = (wxFrame *)pWin;
  342.                     }
  343.                 }
  344.  
  345.                 if ( pFrame && pFrame->GetStatusBar() )
  346.                     pFrame->SetStatusText(szString);
  347.             }
  348. #endif // wxUSE_STATUSBAR
  349.             break;
  350.  
  351.         case wxLOG_Trace:
  352.         case wxLOG_Debug:
  353.             #ifdef __WXDEBUG__
  354.             {
  355.                 wxString str;
  356.                 TimeStamp(&str);
  357.                 str += szString;
  358.  
  359.                 #if defined(__WXMSW__) && !defined(__WXMICROWIN__)
  360.                     // don't prepend debug/trace here: it goes to the
  361.                     // debug window anyhow
  362.                     str += wxT("\r\n");
  363.                     OutputDebugString(str);
  364.                 #else
  365.                     // send them to stderr
  366.                     wxFprintf(stderr, wxT("[%s] %s\n"),
  367.                               level == wxLOG_Trace ? wxT("Trace")
  368.                                                    : wxT("Debug"),
  369.                               str.c_str());
  370.                     fflush(stderr);
  371.                 #endif
  372.             }
  373.             #endif // __WXDEBUG__
  374.  
  375.             break;
  376.  
  377.         case wxLOG_FatalError:
  378.             // show this one immediately
  379.             wxMessageBox(szString, _("Fatal error"), wxICON_HAND);
  380.             wxExit();
  381.             break;
  382.  
  383.         case wxLOG_Error:
  384.             if ( !m_bErrors ) {
  385. #if !wxUSE_LOG_DIALOG
  386.                 // discard earlier informational messages if this is the 1st
  387.                 // error because they might not make sense any more and showing
  388.                 // them in a message box might be confusing
  389.                 m_aMessages.Empty();
  390.                 m_aSeverity.Empty();
  391.                 m_aTimes.Empty();
  392. #endif // wxUSE_LOG_DIALOG
  393.                 m_bErrors = TRUE;
  394.             }
  395.             // fall through
  396.  
  397.         case wxLOG_Warning:
  398.             if ( !m_bErrors ) {
  399.                 // for the warning we don't discard the info messages
  400.                 m_bWarnings = TRUE;
  401.             }
  402.  
  403.             m_aMessages.Add(szString);
  404.             m_aSeverity.Add((int)level);
  405.             m_aTimes.Add((long)t);
  406.             m_bHasMessages = TRUE;
  407.             break;
  408.     }
  409. }
  410.  
  411. // ----------------------------------------------------------------------------
  412. // wxLogWindow and wxLogFrame implementation
  413. // ----------------------------------------------------------------------------
  414.  
  415. // log frame class
  416. // ---------------
  417. class wxLogFrame : public wxFrame
  418. {
  419. public:
  420.     // ctor & dtor
  421.     wxLogFrame(wxFrame *pParent, wxLogWindow *log, const wxChar *szTitle);
  422.     virtual ~wxLogFrame();
  423.  
  424.     // menu callbacks
  425.     void OnClose(wxCommandEvent& event);
  426.     void OnCloseWindow(wxCloseEvent& event);
  427. #if wxUSE_FILE
  428.     void OnSave (wxCommandEvent& event);
  429. #endif // wxUSE_FILE
  430.     void OnClear(wxCommandEvent& event);
  431.  
  432.     void OnIdle(wxIdleEvent&);
  433.  
  434.     // accessors
  435.     wxTextCtrl *TextCtrl() const { return m_pTextCtrl; }
  436.  
  437. private:
  438.     // use standard ids for our commands!
  439.     enum
  440.     {
  441.         Menu_Close = wxID_CLOSE,
  442.         Menu_Save  = wxID_SAVE,
  443.         Menu_Clear = wxID_CLEAR
  444.     };
  445.  
  446.     // common part of OnClose() and OnCloseWindow()
  447.     void DoClose();
  448.  
  449.     wxTextCtrl  *m_pTextCtrl;
  450.     wxLogWindow *m_log;
  451.  
  452.     DECLARE_EVENT_TABLE()
  453. };
  454.  
  455. BEGIN_EVENT_TABLE(wxLogFrame, wxFrame)
  456.     // wxLogWindow menu events
  457.     EVT_MENU(Menu_Close, wxLogFrame::OnClose)
  458. #if wxUSE_FILE
  459.     EVT_MENU(Menu_Save,  wxLogFrame::OnSave)
  460. #endif // wxUSE_FILE
  461.     EVT_MENU(Menu_Clear, wxLogFrame::OnClear)
  462.  
  463.     EVT_CLOSE(wxLogFrame::OnCloseWindow)
  464. END_EVENT_TABLE()
  465.  
  466. wxLogFrame::wxLogFrame(wxFrame *pParent, wxLogWindow *log, const wxChar *szTitle)
  467.           : wxFrame(pParent, -1, szTitle)
  468. {
  469.     m_log = log;
  470.  
  471.     m_pTextCtrl = new wxTextCtrl(this, -1, wxEmptyString, wxDefaultPosition,
  472.             wxDefaultSize,
  473.             wxTE_MULTILINE  |
  474.             wxHSCROLL       |
  475.             // needed for Win32 to avoid 65Kb limit but it doesn't work well
  476.             // when using RichEdit 2.0 which we always do in the Unicode build
  477. #if !wxUSE_UNICODE
  478.             wxTE_RICH       |
  479. #endif // !wxUSE_UNICODE
  480.             wxTE_READONLY);
  481.  
  482. #if wxUSE_MENUS
  483.     // create menu
  484.     wxMenuBar *pMenuBar = new wxMenuBar;
  485.     wxMenu *pMenu = new wxMenu;
  486. #if wxUSE_FILE
  487.     pMenu->Append(Menu_Save,  _("&Save..."), _("Save log contents to file"));
  488. #endif // wxUSE_FILE
  489.     pMenu->Append(Menu_Clear, _("C&lear"), _("Clear the log contents"));
  490.     pMenu->AppendSeparator();
  491.     pMenu->Append(Menu_Close, _("&Close"), _("Close this window"));
  492.     pMenuBar->Append(pMenu, _("&Log"));
  493.     SetMenuBar(pMenuBar);
  494. #endif // wxUSE_MENUS
  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.  
  504. void 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.  
  514. void wxLogFrame::OnClose(wxCommandEvent& WXUNUSED(event))
  515. {
  516.     DoClose();
  517. }
  518.  
  519. void wxLogFrame::OnCloseWindow(wxCloseEvent& WXUNUSED(event))
  520. {
  521.     DoClose();
  522. }
  523.  
  524. #if wxUSE_FILE
  525. void wxLogFrame::OnSave(wxCommandEvent& WXUNUSED(event))
  526. {
  527. #if wxUSE_FILEDLG
  528.     wxString filename;
  529.     wxFile file;
  530.     int rc = OpenLogFile(file, &filename);
  531.     if ( rc == -1 )
  532.     {
  533.         // cancelled
  534.         return;
  535.     }
  536.  
  537.     bool bOk = rc != 0;
  538.  
  539.     // retrieve text and save it
  540.     // -------------------------
  541.     int nLines = m_pTextCtrl->GetNumberOfLines();
  542.     for ( int nLine = 0; bOk && nLine < nLines; nLine++ ) {
  543.         bOk = file.Write(m_pTextCtrl->GetLineText(nLine) +
  544.                          wxTextFile::GetEOL());
  545.     }
  546.  
  547.     if ( bOk )
  548.         bOk = file.Close();
  549.  
  550.     if ( !bOk ) {
  551.         wxLogError(_("Can't save log contents to file."));
  552.     }
  553.     else {
  554.         wxLogStatus(this, _("Log saved to the file '%s'."), filename.c_str());
  555.     }
  556. #endif
  557. }
  558. #endif // wxUSE_FILE
  559.  
  560. void wxLogFrame::OnClear(wxCommandEvent& WXUNUSED(event))
  561. {
  562.     m_pTextCtrl->Clear();
  563. }
  564.  
  565. wxLogFrame::~wxLogFrame()
  566. {
  567.     m_log->OnFrameDelete(this);
  568. }
  569.  
  570. // wxLogWindow
  571. // -----------
  572.  
  573. wxLogWindow::wxLogWindow(wxFrame *pParent,
  574.                          const wxChar *szTitle,
  575.                          bool bShow,
  576.                          bool bDoPass)
  577. {
  578.     PassMessages(bDoPass);
  579.  
  580.     m_pLogFrame = new wxLogFrame(pParent, this, szTitle);
  581.  
  582.     if ( bShow )
  583.         m_pLogFrame->Show(TRUE);
  584. }
  585.  
  586. void wxLogWindow::Show(bool bShow)
  587. {
  588.     m_pLogFrame->Show(bShow);
  589. }
  590.  
  591. void wxLogWindow::DoLog(wxLogLevel level, const wxChar *szString, time_t t)
  592. {
  593.     // first let the previous logger show it
  594.     wxLogPassThrough::DoLog(level, szString, t);
  595.  
  596.     if ( m_pLogFrame ) {
  597.         switch ( level ) {
  598.             case wxLOG_Status:
  599.                 // by default, these messages are ignored by wxLog, so process
  600.                 // them ourselves
  601.                 if ( !wxIsEmpty(szString) )
  602.                 {
  603.                     wxString str;
  604.                     str << _("Status: ") << szString;
  605.                     DoLogString(str, t);
  606.                 }
  607.                 break;
  608.  
  609.                 // don't put trace messages in the text window for 2 reasons:
  610.                 // 1) there are too many of them
  611.                 // 2) they may provoke other trace messages thus sending a program
  612.                 //    into an infinite loop
  613.             case wxLOG_Trace:
  614.                 break;
  615.  
  616.             default:
  617.                 // and this will format it nicely and call our DoLogString()
  618.                 wxLog::DoLog(level, szString, t);
  619.         }
  620.     }
  621.  
  622.     m_bHasMessages = TRUE;
  623. }
  624.  
  625. void wxLogWindow::DoLogString(const wxChar *szString, time_t WXUNUSED(t))
  626. {
  627.     // put the text into our window
  628.     wxTextCtrl *pText = m_pLogFrame->TextCtrl();
  629.  
  630.     // remove selection (WriteText is in fact ReplaceSelection)
  631. #ifdef __WXMSW__
  632.     long nLen = pText->GetLastPosition();
  633.     pText->SetSelection(nLen, nLen);
  634. #endif // Windows
  635.  
  636.     wxString msg;
  637.     TimeStamp(&msg);
  638.     msg << szString << wxT('\n');
  639.  
  640.     pText->AppendText(msg);
  641.  
  642.     // TODO ensure that the line can be seen
  643. }
  644.  
  645. wxFrame *wxLogWindow::GetFrame() const
  646. {
  647.     return m_pLogFrame;
  648. }
  649.  
  650. void wxLogWindow::OnFrameCreate(wxFrame * WXUNUSED(frame))
  651. {
  652. }
  653.  
  654. bool wxLogWindow::OnFrameClose(wxFrame * WXUNUSED(frame))
  655. {
  656.     // allow to close
  657.     return TRUE;
  658. }
  659.  
  660. void wxLogWindow::OnFrameDelete(wxFrame * WXUNUSED(frame))
  661. {
  662.     m_pLogFrame = (wxLogFrame *)NULL;
  663. }
  664.  
  665. wxLogWindow::~wxLogWindow()
  666. {
  667.     // may be NULL if log frame already auto destroyed itself
  668.     delete m_pLogFrame;
  669. }
  670.  
  671. // ----------------------------------------------------------------------------
  672. // wxLogDialog
  673. // ----------------------------------------------------------------------------
  674.  
  675. #if wxUSE_LOG_DIALOG
  676.  
  677. static const size_t MARGIN = 10;
  678.  
  679. wxString wxLogDialog::ms_details;
  680.  
  681. wxLogDialog::wxLogDialog(wxWindow *parent,
  682.                          const wxArrayString& messages,
  683.                          const wxArrayInt& severity,
  684.                          const wxArrayLong& times,
  685.                          const wxString& caption,
  686.                          long style)
  687.            : wxDialog(parent, -1, caption,
  688.                       wxDefaultPosition, wxDefaultSize,
  689.                       wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
  690. {
  691.     if ( ms_details.IsEmpty() )
  692.     {
  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");
  696.         ms_details = wxGetTranslation(ms_details);
  697.     }
  698.  
  699.     size_t count = messages.GetCount();
  700.     m_messages.Alloc(count);
  701.     m_severity.Alloc(count);
  702.     m_times.Alloc(count);
  703.  
  704.     for ( size_t n = 0; n < count; n++ )
  705.     {
  706.         wxString msg = messages[n];
  707.         do
  708.         {
  709.             m_messages.Add(msg.BeforeFirst(_T('\n')));
  710.             msg = msg.AfterFirst(_T('\n'));
  711.  
  712.             m_severity.Add(severity[n]);
  713.             m_times.Add(times[n]);
  714.         }
  715.         while ( !!msg );
  716.     }
  717.  
  718.     m_showingDetails = FALSE; // not initially
  719.     m_listctrl = (wxListCtrl *)NULL;
  720.  
  721. #if wxUSE_STATLINE
  722.     m_statline = (wxStaticLine *)NULL;
  723. #endif // wxUSE_STATLINE
  724.  
  725. #if wxUSE_FILE
  726.     m_btnSave = (wxButton *)NULL;
  727. #endif // wxUSE_FILE
  728.  
  729.     // create the controls which are always shown and layout them: we use
  730.     // sizers even though our window is not resizeable to calculate the size of
  731.     // the dialog properly
  732.     wxBoxSizer *sizerTop = new wxBoxSizer(wxVERTICAL);
  733.     wxBoxSizer *sizerButtons = new wxBoxSizer(wxVERTICAL);
  734.     wxBoxSizer *sizerAll = new wxBoxSizer(wxHORIZONTAL);
  735.  
  736.     // this "Ok" button has wxID_CANCEL id - not very logical, but this allows
  737.     // to close the log dialog with <Esc> which wouldn't work otherwise (as it
  738.     // translates into click on cancel button)
  739.     wxButton *btnOk = new wxButton(this, wxID_CANCEL, _("OK"));
  740.     sizerButtons->Add(btnOk, 0, wxCENTRE | wxBOTTOM, MARGIN/2);
  741.     m_btnDetails = new wxButton(this, wxID_MORE, ms_details + EXPAND_SUFFIX);
  742.     sizerButtons->Add(m_btnDetails, 0, wxCENTRE | wxTOP, MARGIN/2 - 1);
  743.  
  744. #ifndef __WIN16__
  745.     wxBitmap bitmap;
  746.     switch ( style & wxICON_MASK )
  747.     {
  748.         case wxICON_ERROR:
  749.             bitmap = wxArtProvider::GetIcon(wxART_ERROR, wxART_MESSAGE_BOX);
  750. #ifdef __WXPM__
  751.             bitmap.SetId(wxICON_SMALL_ERROR);
  752. #endif
  753.             break;
  754.  
  755.         case wxICON_INFORMATION:
  756.             bitmap = wxArtProvider::GetIcon(wxART_INFORMATION, wxART_MESSAGE_BOX);
  757. #ifdef __WXPM__
  758.             bitmap.SetId(wxICON_SMALL_INFO);
  759. #endif
  760.             break;
  761.  
  762.         case wxICON_WARNING:
  763.             bitmap = wxArtProvider::GetIcon(wxART_WARNING, wxART_MESSAGE_BOX);
  764. #ifdef __WXPM__
  765.             bitmap.SetId(wxICON_SMALL_WARNING);
  766. #endif
  767.             break;
  768.  
  769.         default:
  770.             wxFAIL_MSG(_T("incorrect log style"));
  771.     }
  772.     sizerAll->Add(new wxStaticBitmap(this, -1, bitmap), 0);
  773. #endif // !Win16
  774.  
  775.     const wxString& message = messages.Last();
  776.     sizerAll->Add(CreateTextSizer(message), 1,
  777.                   wxALIGN_CENTRE_VERTICAL | wxLEFT | wxRIGHT, MARGIN);
  778.     sizerAll->Add(sizerButtons, 0, wxALIGN_RIGHT | wxLEFT, MARGIN);
  779.  
  780.     sizerTop->Add(sizerAll, 0, wxALL | wxEXPAND, MARGIN);
  781.  
  782.     SetAutoLayout(TRUE);
  783.     SetSizer(sizerTop);
  784.  
  785.     // see comments in OnDetails()
  786.     //
  787.     // Note: Doing this, this way, triggered a nasty bug in
  788.     //       wxTopLevelWindowGTK::GtkOnSize which took -1 literally once
  789.     //       either of maxWidth or maxHeight was set.  This symptom has been
  790.     //       fixed there, but it is a problem that remains as long as we allow
  791.     //       unchecked access to the internal size members.  We really need to
  792.     //       encapuslate window sizes more cleanly and make it clear when -1 will
  793.     //       be substituted and when it will not.
  794.  
  795.     wxSize size = sizerTop->Fit(this);
  796.     m_maxHeight = size.y;
  797.     SetSizeHints(size.x, size.y, m_maxWidth, m_maxHeight);
  798.  
  799.     btnOk->SetFocus();
  800.  
  801.     // this can't happen any more as we don't use this dialog in this case
  802. #if 0
  803.     if ( count == 1 )
  804.     {
  805.         // no details... it's easier to disable a button than to change the
  806.         // dialog layout depending on whether we have details or not
  807.         m_btnDetails->Disable();
  808.     }
  809. #endif // 0
  810.  
  811.     Centre();
  812. }
  813.  
  814. void wxLogDialog::CreateDetailsControls()
  815. {
  816.     // create the save button and separator line if possible
  817. #if wxUSE_FILE
  818.     m_btnSave = new wxButton(this, wxID_SAVE, _("&Save..."));
  819. #endif // wxUSE_FILE
  820.  
  821. #if wxUSE_STATLINE
  822.     m_statline = new wxStaticLine(this, -1);
  823. #endif // wxUSE_STATLINE
  824.  
  825.     // create the list ctrl now
  826.     m_listctrl = new wxListCtrl(this, -1,
  827.                                 wxDefaultPosition, wxDefaultSize,
  828.                                 wxSUNKEN_BORDER |
  829.                                 wxLC_REPORT |
  830.                                 wxLC_NO_HEADER |
  831.                                 wxLC_SINGLE_SEL);
  832.  
  833.     // no need to translate these strings as they're not shown to the
  834.     // user anyhow (we use wxLC_NO_HEADER style)
  835.     m_listctrl->InsertColumn(0, _T("Message"));
  836.     m_listctrl->InsertColumn(1, _T("Time"));
  837.  
  838.     // prepare the imagelist
  839.     static const int ICON_SIZE = 16;
  840.     wxImageList *imageList = new wxImageList(ICON_SIZE, ICON_SIZE);
  841.  
  842.     // order should be the same as in the switch below!
  843.     static const wxChar* icons[] =
  844.     {
  845.         wxART_ERROR,
  846.         wxART_WARNING,
  847.         wxART_INFORMATION
  848.     };
  849.  
  850.     bool loadedIcons = TRUE;
  851.  
  852. #ifndef __WIN16__
  853.     for ( size_t icon = 0; icon < WXSIZEOF(icons); icon++ )
  854.     {
  855.         wxBitmap bmp = wxArtProvider::GetBitmap(icons[icon], wxART_MESSAGE_BOX,
  856.                                                 wxSize(ICON_SIZE, ICON_SIZE));
  857.  
  858.         // This may very well fail if there are insufficient colours available.
  859.         // Degrade gracefully.
  860.         if ( !bmp.Ok() )
  861.         {
  862.             loadedIcons = FALSE;
  863.  
  864.             break;
  865.         }
  866.  
  867.         imageList->Add(bmp);
  868.     }
  869.  
  870.     m_listctrl->SetImageList(imageList, wxIMAGE_LIST_SMALL);
  871. #endif // !Win16
  872.  
  873.     // and fill it
  874.     wxString fmt = wxLog::GetTimestamp();
  875.     if ( !fmt )
  876.     {
  877.         // default format
  878.         fmt = _T("%c");
  879.     }
  880.  
  881.     size_t count = m_messages.GetCount();
  882.     for ( size_t n = 0; n < count; n++ )
  883.     {
  884.         int image;
  885.  
  886. #ifndef __WIN16__
  887.         if ( loadedIcons )
  888.         {
  889.             switch ( m_severity[n] )
  890.             {
  891.                 case wxLOG_Error:
  892.                     image = 0;
  893.                     break;
  894.  
  895.                 case wxLOG_Warning:
  896.                     image = 1;
  897.                     break;
  898.  
  899.                 default:
  900.                     image = 2;
  901.             }
  902.         }
  903.         else // failed to load images
  904. #endif // !Win16
  905.         {
  906.             image = -1;
  907.         }
  908.  
  909.         m_listctrl->InsertItem(n, m_messages[n], image);
  910.         m_listctrl->SetItem(n, 1, TimeStamp(fmt, (time_t)m_times[n]));
  911.     }
  912.  
  913.     // let the columns size themselves
  914.     m_listctrl->SetColumnWidth(0, wxLIST_AUTOSIZE);
  915.     m_listctrl->SetColumnWidth(1, wxLIST_AUTOSIZE);
  916.  
  917.     // calculate an approximately nice height for the listctrl
  918.     int height = GetCharHeight()*(count + 4);
  919.  
  920.     // but check that the dialog won't fall fown from the screen
  921.     //
  922.     // we use GetMinHeight() to get the height of the dialog part without the
  923.     // details and we consider that the "Save" button below and the separator
  924.     // line (and the margins around it) take about as much, hence double it
  925.     int heightMax = wxGetDisplaySize().y - GetPosition().y - 2*GetMinHeight();
  926.  
  927.     // we should leave a margin
  928.     heightMax *= 9;
  929.     heightMax /= 10;
  930.  
  931.     m_listctrl->SetSize(-1, wxMin(height, heightMax));
  932. }
  933.  
  934. void wxLogDialog::OnListSelect(wxListEvent& event)
  935. {
  936.     // we can't just disable the control because this looks ugly under Windows
  937.     // (wrong bg colour, no scrolling...), but we still want to disable
  938.     // selecting items - it makes no sense here
  939.     m_listctrl->SetItemState(event.GetIndex(), 0, wxLIST_STATE_SELECTED);
  940. }
  941.  
  942. void wxLogDialog::OnOk(wxCommandEvent& WXUNUSED(event))
  943. {
  944.     EndModal(wxID_OK);
  945. }
  946.  
  947. #if wxUSE_FILE
  948.  
  949. void wxLogDialog::OnSave(wxCommandEvent& WXUNUSED(event))
  950. {
  951. #if wxUSE_FILEDLG
  952.     wxFile file;
  953.     int rc = OpenLogFile(file);
  954.     if ( rc == -1 )
  955.     {
  956.         // cancelled
  957.         return;
  958.     }
  959.  
  960.     bool ok = rc != 0;
  961.  
  962.     wxString fmt = wxLog::GetTimestamp();
  963.     if ( !fmt )
  964.     {
  965.         // default format
  966.         fmt = _T("%c");
  967.     }
  968.  
  969.     size_t count = m_messages.GetCount();
  970.     for ( size_t n = 0; ok && (n < count); n++ )
  971.     {
  972.         wxString line;
  973.         line << TimeStamp(fmt, (time_t)m_times[n])
  974.              << _T(": ")
  975.              << m_messages[n]
  976.              << wxTextFile::GetEOL();
  977.  
  978.         ok = file.Write(line);
  979.     }
  980.  
  981.     if ( ok )
  982.         ok = file.Close();
  983.  
  984.     if ( !ok )
  985.         wxLogError(_("Can't save log contents to file."));
  986. #endif // wxUSE_FILEDLG
  987. }
  988.  
  989. #endif // wxUSE_FILE
  990.  
  991. void wxLogDialog::OnDetails(wxCommandEvent& WXUNUSED(event))
  992. {
  993.     wxSizer *sizer = GetSizer();
  994.  
  995.     if ( m_showingDetails )
  996.     {
  997.         m_btnDetails->SetLabel(ms_details + EXPAND_SUFFIX);
  998.  
  999.         sizer->Remove(m_listctrl);
  1000.  
  1001. #if wxUSE_STATLINE
  1002.         sizer->Remove(m_statline);
  1003. #endif // wxUSE_STATLINE
  1004.  
  1005. #if wxUSE_FILE
  1006.         sizer->Remove(m_btnSave);
  1007. #endif // wxUSE_FILE
  1008.     }
  1009.     else // show details now
  1010.     {
  1011.         m_btnDetails->SetLabel(wxString(_T("<< ")) + ms_details);
  1012.  
  1013.         if ( !m_listctrl )
  1014.         {
  1015.             CreateDetailsControls();
  1016.         }
  1017.  
  1018. #if wxUSE_STATLINE
  1019.         sizer->Add(m_statline, 0, wxEXPAND | (wxALL & ~wxTOP), MARGIN);
  1020. #endif // wxUSE_STATLINE
  1021.  
  1022.         sizer->Add(m_listctrl, 1, wxEXPAND | (wxALL & ~wxTOP), MARGIN);
  1023.  
  1024.         // VZ: this doesn't work as this becomes the initial (and not only
  1025.         //     minimal) listctrl height as well - why?
  1026. #if 0
  1027.         // allow the user to make the dialog shorter than its initial height -
  1028.         // without this it wouldn't work as the list ctrl would have been
  1029.         // incompressible
  1030.         sizer->SetItemMinSize(m_listctrl, 100, 3*GetCharHeight());
  1031. #endif // 0
  1032.  
  1033. #if wxUSE_FILE
  1034.         sizer->Add(m_btnSave, 0, wxALIGN_RIGHT | (wxALL & ~wxTOP), MARGIN);
  1035. #endif // wxUSE_FILE
  1036.     }
  1037.  
  1038.     m_showingDetails = !m_showingDetails;
  1039.  
  1040.     // in any case, our size changed - relayout everything and set new hints
  1041.     // ---------------------------------------------------------------------
  1042.  
  1043.     // we have to reset min size constraints or Fit() would never reduce the
  1044.     // dialog size when collapsing it and we have to reset max constraint
  1045.     // because it wouldn't expand it otherwise
  1046.  
  1047.     m_minHeight =
  1048.     m_maxHeight = -1;
  1049.  
  1050.     // wxSizer::FitSize() is private, otherwise we might use it directly...
  1051.     wxSize sizeTotal = GetSize(),
  1052.            sizeClient = GetClientSize();
  1053.  
  1054.     wxSize size = sizer->GetMinSize();
  1055.     size.x += sizeTotal.x - sizeClient.x;
  1056.     size.y += sizeTotal.y - sizeClient.y;
  1057.  
  1058.     // we don't want to allow expanding the dialog in vertical direction as
  1059.     // this would show the "hidden" details but we can resize the dialog
  1060.     // vertically while the details are shown
  1061.     if ( !m_showingDetails )
  1062.         m_maxHeight = size.y;
  1063.  
  1064.     SetSizeHints(size.x, size.y, m_maxWidth, m_maxHeight);
  1065.  
  1066.     // don't change the width when expanding/collapsing
  1067.     SetSize(-1, size.y);
  1068.  
  1069. #ifdef __WXGTK__
  1070.     // VS: this is neccessary in order to force frame redraw under
  1071.     // WindowMaker or fvwm2 (and probably other broken WMs).
  1072.     // Otherwise, detailed list wouldn't be displayed.
  1073.     Show(TRUE);
  1074. #endif // wxGTK
  1075. }
  1076.  
  1077. wxLogDialog::~wxLogDialog()
  1078. {
  1079.     if ( m_listctrl )
  1080.     {
  1081.         delete m_listctrl->GetImageList(wxIMAGE_LIST_SMALL);
  1082.     }
  1083. }
  1084.  
  1085. #endif // wxUSE_LOG_DIALOG
  1086.  
  1087. #if wxUSE_FILE && wxUSE_FILEDLG
  1088.  
  1089. // pass an uninitialized file object, the function will ask the user for the
  1090. // filename and try to open it, returns TRUE on success (file was opened),
  1091. // FALSE if file couldn't be opened/created and -1 if the file selection
  1092. // dialog was cancelled
  1093. static int OpenLogFile(wxFile& file, wxString *pFilename)
  1094. {
  1095.     // get the file name
  1096.     // -----------------
  1097.     wxString filename = wxSaveFileSelector(wxT("log"), wxT("txt"), wxT("log.txt"));
  1098.     if ( !filename ) {
  1099.         // cancelled
  1100.         return -1;
  1101.     }
  1102.  
  1103.     // open file
  1104.     // ---------
  1105.     bool bOk = FALSE;
  1106.     if ( wxFile::Exists(filename) ) {
  1107.         bool bAppend = FALSE;
  1108.         wxString strMsg;
  1109.         strMsg.Printf(_("Append log to file '%s' (choosing [No] will overwrite it)?"),
  1110.                       filename.c_str());
  1111.         switch ( wxMessageBox(strMsg, _("Question"),
  1112.                               wxICON_QUESTION | wxYES_NO | wxCANCEL) ) {
  1113.             case wxYES:
  1114.                 bAppend = TRUE;
  1115.                 break;
  1116.  
  1117.             case wxNO:
  1118.                 bAppend = FALSE;
  1119.                 break;
  1120.  
  1121.             case wxCANCEL:
  1122.                 return -1;
  1123.  
  1124.             default:
  1125.                 wxFAIL_MSG(_("invalid message box return value"));
  1126.         }
  1127.  
  1128.         if ( bAppend ) {
  1129.             bOk = file.Open(filename, wxFile::write_append);
  1130.         }
  1131.         else {
  1132.             bOk = file.Create(filename, TRUE /* overwrite */);
  1133.         }
  1134.     }
  1135.     else {
  1136.         bOk = file.Create(filename);
  1137.     }
  1138.  
  1139.     if ( pFilename )
  1140.         *pFilename = filename;
  1141.  
  1142.     return bOk;
  1143. }
  1144.  
  1145. #endif // wxUSE_FILE
  1146.  
  1147. #endif // !(wxUSE_LOGGUI || wxUSE_LOGWINDOW)
  1148.  
  1149. #if wxUSE_TEXTCTRL
  1150.  
  1151. // ----------------------------------------------------------------------------
  1152. // wxLogTextCtrl implementation
  1153. // ----------------------------------------------------------------------------
  1154.  
  1155. wxLogTextCtrl::wxLogTextCtrl(wxTextCtrl *pTextCtrl)
  1156. {
  1157.     m_pTextCtrl = pTextCtrl;
  1158. }
  1159.  
  1160. void wxLogTextCtrl::DoLogString(const wxChar *szString, time_t WXUNUSED(t))
  1161. {
  1162.     wxString msg;
  1163.     TimeStamp(&msg);
  1164.  
  1165. #if defined(__WXMAC__)
  1166.     // VZ: this is a bug in wxMac, it *must* accept '\n' as new line, the
  1167.     //     translation must be done in wxTextCtrl, not here! (FIXME)
  1168.     msg << szString << wxT('\r');
  1169. #else
  1170.     msg << szString << wxT('\n');
  1171. #endif
  1172.  
  1173.     m_pTextCtrl->AppendText(msg);
  1174. }
  1175.  
  1176. #endif // wxUSE_TEXTCTRL
  1177.  
  1178. // vi:sts=4:sw=4:et
  1179.