Files
wxWidgets/src/generic/logg.cpp
Vadim Zeitlin af58844636 Add information about the log message generation location to wxLog.
This means that wxLog::DoLogRecord() can now retrieve the file name, line
number and the function where the message was logged.

An unfortunate consequence of this change is that now

	if ( condition )
		wxLogError("Whatever");

results in a warning from g++ 4.x with -Wparentehses, so extra parentheses had
to be added in many places.

Finally, also allow storing arbitrary attributes in wxLogRecordInfo. This had
to be added to implement our own overloaded wxLogStatus() and wxLogSysError()
and will probably be useful for the others as well.


git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@61363 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
2009-07-09 20:26:34 +00:00

1137 lines
30 KiB
C++

/////////////////////////////////////////////////////////////////////////////
// Name: src/generic/logg.cpp
// Purpose: wxLog-derived classes which need GUI support (the rest is in
// src/common/log.cpp)
// Author: Vadim Zeitlin
// Modified by:
// Created: 20.09.99 (extracted from src/common/log.cpp)
// RCS-ID: $Id$
// Copyright: (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
// ============================================================================
// declarations
// ============================================================================
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#ifndef WX_PRECOMP
#include "wx/app.h"
#include "wx/button.h"
#include "wx/intl.h"
#include "wx/log.h"
#include "wx/menu.h"
#include "wx/frame.h"
#include "wx/filedlg.h"
#include "wx/msgdlg.h"
#include "wx/textctrl.h"
#include "wx/sizer.h"
#include "wx/statbmp.h"
#include "wx/settings.h"
#include "wx/wxcrtvararg.h"
#endif // WX_PRECOMP
#if wxUSE_LOGGUI || wxUSE_LOGWINDOW
#include "wx/file.h"
#include "wx/clipbrd.h"
#include "wx/dataobj.h"
#include "wx/textfile.h"
#include "wx/statline.h"
#include "wx/artprov.h"
#include "wx/collpane.h"
#include "wx/arrstr.h"
#include "wx/msgout.h"
#if wxUSE_THREADS
#include "wx/thread.h"
#endif // wxUSE_THREADS
#ifdef __WXMSW__
// for OutputDebugString()
#include "wx/msw/private.h"
#endif // Windows
#ifdef __WXPM__
#include <time.h>
#endif
#if wxUSE_LOG_DIALOG
#include "wx/listctrl.h"
#include "wx/imaglist.h"
#include "wx/image.h"
#endif // wxUSE_LOG_DIALOG/!wxUSE_LOG_DIALOG
#if defined(__MWERKS__) && wxUSE_UNICODE
#include <wtime.h>
#endif
#include "wx/datetime.h"
// the suffix we add to the button to show that the dialog can be expanded
#define EXPAND_SUFFIX _T(" >>")
#define CAN_SAVE_FILES (wxUSE_FILE && wxUSE_FILEDLG)
// ----------------------------------------------------------------------------
// private classes
// ----------------------------------------------------------------------------
#if wxUSE_LOG_DIALOG
// this function is a wrapper around strftime(3)
// allows to exclude the usage of wxDateTime
static wxString TimeStamp(const wxString& format, time_t t)
{
#if wxUSE_DATETIME
wxChar buf[4096];
struct tm tm;
if ( !wxStrftime(buf, WXSIZEOF(buf), format, wxLocaltime_r(&t, &tm)) )
{
// buffer is too small?
wxFAIL_MSG(_T("strftime() failed"));
}
return wxString(buf);
#else // !wxUSE_DATETIME
return wxEmptyString;
#endif // wxUSE_DATETIME/!wxUSE_DATETIME
}
class wxLogDialog : public wxDialog
{
public:
wxLogDialog(wxWindow *parent,
const wxArrayString& messages,
const wxArrayInt& severity,
const wxArrayLong& timess,
const wxString& caption,
long style);
virtual ~wxLogDialog();
// event handlers
void OnOk(wxCommandEvent& event);
#if wxUSE_CLIPBOARD
void OnCopy(wxCommandEvent& event);
#endif // wxUSE_CLIPBOARD
#if CAN_SAVE_FILES
void OnSave(wxCommandEvent& event);
#endif // CAN_SAVE_FILES
void OnListItemActivated(wxListEvent& event);
private:
// create controls needed for the details display
void CreateDetailsControls(wxWindow *);
// if necessary truncates the given string and adds an ellipsis
wxString EllipsizeString(const wxString &text)
{
if (ms_maxLength > 0 &&
text.length() > ms_maxLength)
{
wxString ret(text);
ret.Truncate(ms_maxLength);
ret << "...";
return ret;
}
return text;
}
#if CAN_SAVE_FILES || wxUSE_CLIPBOARD
// return the contents of the dialog as a multiline string
wxString GetLogMessages() const;
#endif // CAN_SAVE_FILES || wxUSE_CLIPBOARD
// the data for the listctrl
wxArrayString m_messages;
wxArrayInt m_severity;
wxArrayLong m_times;
// the controls which are not shown initially (but only when details
// button is pressed)
wxListCtrl *m_listctrl;
// the translated "Details" string
static wxString ms_details;
// the maximum length of the log message
static size_t ms_maxLength;
DECLARE_EVENT_TABLE()
wxDECLARE_NO_COPY_CLASS(wxLogDialog);
};
BEGIN_EVENT_TABLE(wxLogDialog, wxDialog)
EVT_BUTTON(wxID_OK, wxLogDialog::OnOk)
#if wxUSE_CLIPBOARD
EVT_BUTTON(wxID_COPY, wxLogDialog::OnCopy)
#endif // wxUSE_CLIPBOARD
#if CAN_SAVE_FILES
EVT_BUTTON(wxID_SAVE, wxLogDialog::OnSave)
#endif // CAN_SAVE_FILES
EVT_LIST_ITEM_ACTIVATED(wxID_ANY, wxLogDialog::OnListItemActivated)
END_EVENT_TABLE()
#endif // wxUSE_LOG_DIALOG
// ----------------------------------------------------------------------------
// private functions
// ----------------------------------------------------------------------------
#if CAN_SAVE_FILES
// pass an uninitialized file object, the function will ask the user for the
// filename and try to open it, returns true on success (file was opened),
// false if file couldn't be opened/created and -1 if the file selection
// dialog was cancelled
static int OpenLogFile(wxFile& file, wxString *filename = NULL, wxWindow *parent = NULL);
#endif // CAN_SAVE_FILES
// ============================================================================
// implementation
// ============================================================================
// ----------------------------------------------------------------------------
// wxLogGui implementation (FIXME MT-unsafe)
// ----------------------------------------------------------------------------
#if wxUSE_LOGGUI
wxLogGui::wxLogGui()
{
Clear();
}
void wxLogGui::Clear()
{
m_bErrors =
m_bWarnings =
m_bHasMessages = false;
m_aMessages.Empty();
m_aSeverity.Empty();
m_aTimes.Empty();
}
int wxLogGui::GetSeverityIcon() const
{
return m_bErrors ? wxICON_STOP
: m_bWarnings ? wxICON_EXCLAMATION
: wxICON_INFORMATION;
}
wxString wxLogGui::GetTitle() const
{
wxString titleFormat;
switch ( GetSeverityIcon() )
{
case wxICON_STOP:
titleFormat = _("%s Error");
break;
case wxICON_EXCLAMATION:
titleFormat = _("%s Warning");
break;
default:
wxFAIL_MSG( "unexpected icon severity" );
// fall through
case wxICON_INFORMATION:
titleFormat = _("%s Information");
}
return wxString::Format(titleFormat, wxTheApp->GetAppDisplayName());
}
void
wxLogGui::DoShowSingleLogMessage(const wxString& message,
const wxString& title,
int style)
{
wxMessageBox(message, title, wxOK | style);
}
void
wxLogGui::DoShowMultipleLogMessages(const wxArrayString& messages,
const wxArrayInt& severities,
const wxArrayLong& times,
const wxString& title,
int style)
{
#if wxUSE_LOG_DIALOG
wxLogDialog dlg(NULL,
messages, severities, times,
title, style);
// clear the message list before showing the dialog because while it's
// shown some new messages may appear
Clear();
(void)dlg.ShowModal();
#else // !wxUSE_LOG_DIALOG
// start from the most recent message
wxString message;
const size_t nMsgCount = messages.size();
message.reserve(nMsgCount*100);
for ( size_t n = nMsgCount; n > 0; n-- ) {
message << m_aMessages[n - 1] << wxT("\n");
}
DoShowSingleLogMessage(message, title, style);
#endif // wxUSE_LOG_DIALOG/!wxUSE_LOG_DIALOG
}
void wxLogGui::Flush()
{
if ( !m_bHasMessages )
return;
// do it right now to block any new calls to Flush() while we're here
m_bHasMessages = false;
// note that this must be done before examining m_aMessages as it may log
// yet another message
const unsigned repeatCount = LogLastRepeatIfNeeded();
const size_t nMsgCount = m_aMessages.size();
if ( repeatCount > 0 )
{
m_aMessages[nMsgCount - 1] << " (" << m_aMessages[nMsgCount - 2] << ")";
}
const wxString title = GetTitle();
const int style = GetSeverityIcon();
// avoid showing other log dialogs until we're done with the dialog we're
// showing right now: nested modal dialogs make for really bad UI!
Suspend();
if ( nMsgCount == 1 )
{
// make a copy before calling Clear()
const wxString message(m_aMessages[0]);
Clear();
DoShowSingleLogMessage(message, title, style);
}
else // more than one message
{
wxArrayString messages;
wxArrayInt severities;
wxArrayLong times;
messages.swap(m_aMessages);
severities.swap(m_aSeverity);
times.swap(m_aTimes);
Clear();
DoShowMultipleLogMessages(messages, severities, times, title, style);
}
// allow flushing the logs again
Resume();
}
// log all kinds of messages
void wxLogGui::DoLogRecord(wxLogLevel level,
const wxString& msg,
const wxLogRecordInfo& info)
{
switch ( level )
{
case wxLOG_Info:
if ( GetVerbose() )
case wxLOG_Message:
{
m_aMessages.Add(msg);
m_aSeverity.Add(wxLOG_Message);
m_aTimes.Add((long)info.timestamp);
m_bHasMessages = true;
}
break;
case wxLOG_Status:
#if wxUSE_STATUSBAR
{
wxFrame *pFrame = NULL;
// check if the frame was passed to us explicitly
wxUIntPtr ptr;
if ( info.GetNumValue(wxLOG_KEY_FRAME, &ptr) )
{
pFrame = static_cast<wxFrame *>(wxUIntToPtr(ptr));
}
// find the top window and set it's status text if it has any
if ( pFrame == NULL ) {
wxWindow *pWin = wxTheApp->GetTopWindow();
if ( pWin != NULL && pWin->IsKindOf(CLASSINFO(wxFrame)) ) {
pFrame = (wxFrame *)pWin;
}
}
if ( pFrame && pFrame->GetStatusBar() )
pFrame->SetStatusText(msg);
}
#endif // wxUSE_STATUSBAR
break;
case wxLOG_Error:
if ( !m_bErrors ) {
#if !wxUSE_LOG_DIALOG
// discard earlier informational messages if this is the 1st
// error because they might not make sense any more and showing
// them in a message box might be confusing
m_aMessages.Empty();
m_aSeverity.Empty();
m_aTimes.Empty();
#endif // wxUSE_LOG_DIALOG
m_bErrors = true;
}
// fall through
case wxLOG_Warning:
if ( !m_bErrors ) {
// for the warning we don't discard the info messages
m_bWarnings = true;
}
m_aMessages.Add(msg);
m_aSeverity.Add((int)level);
m_aTimes.Add((long)info.timestamp);
m_bHasMessages = true;
break;
default:
// let the base class deal with debug/trace messages as well as any
// custom levels
wxLog::DoLogRecord(level, msg, info);
}
}
#endif // wxUSE_LOGGUI
// ----------------------------------------------------------------------------
// wxLogWindow and wxLogFrame implementation
// ----------------------------------------------------------------------------
#if wxUSE_LOGWINDOW
// log frame class
// ---------------
class wxLogFrame : public wxFrame
{
public:
// ctor & dtor
wxLogFrame(wxWindow *pParent, wxLogWindow *log, const wxString& szTitle);
virtual ~wxLogFrame();
// menu callbacks
void OnClose(wxCommandEvent& event);
void OnCloseWindow(wxCloseEvent& event);
#if CAN_SAVE_FILES
void OnSave(wxCommandEvent& event);
#endif // CAN_SAVE_FILES
void OnClear(wxCommandEvent& event);
// this function is safe to call from any thread (notice that it should be
// also called from the main thread to ensure that the messages logged from
// it appear in correct order with the messages from the other threads)
void AddLogMessage(const wxString& message);
// actually append the messages logged from secondary threads to the text
// control during idle time in the main thread
virtual void OnInternalIdle();
private:
// use standard ids for our commands!
enum
{
Menu_Close = wxID_CLOSE,
Menu_Save = wxID_SAVE,
Menu_Clear = wxID_CLEAR
};
// common part of OnClose() and OnCloseWindow()
void DoClose();
// do show the message in the text control
void DoShowLogMessage(const wxString& message)
{
m_pTextCtrl->AppendText(message + wxS('\n'));
}
wxTextCtrl *m_pTextCtrl;
wxLogWindow *m_log;
// queue of messages logged from other threads which need to be displayed
wxArrayString m_pendingMessages;
#if wxUSE_THREADS
// critical section to protect access to m_pendingMessages
wxCriticalSection m_critSection;
#endif // wxUSE_THREADS
DECLARE_EVENT_TABLE()
wxDECLARE_NO_COPY_CLASS(wxLogFrame);
};
BEGIN_EVENT_TABLE(wxLogFrame, wxFrame)
// wxLogWindow menu events
EVT_MENU(Menu_Close, wxLogFrame::OnClose)
#if CAN_SAVE_FILES
EVT_MENU(Menu_Save, wxLogFrame::OnSave)
#endif // CAN_SAVE_FILES
EVT_MENU(Menu_Clear, wxLogFrame::OnClear)
EVT_CLOSE(wxLogFrame::OnCloseWindow)
END_EVENT_TABLE()
wxLogFrame::wxLogFrame(wxWindow *pParent, wxLogWindow *log, const wxString& szTitle)
: wxFrame(pParent, wxID_ANY, szTitle)
{
m_log = log;
m_pTextCtrl = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition,
wxDefaultSize,
wxTE_MULTILINE |
wxHSCROLL |
// needed for Win32 to avoid 65Kb limit but it doesn't work well
// when using RichEdit 2.0 which we always do in the Unicode build
#if !wxUSE_UNICODE
wxTE_RICH |
#endif // !wxUSE_UNICODE
wxTE_READONLY);
#if wxUSE_MENUS
// create menu
wxMenuBar *pMenuBar = new wxMenuBar;
wxMenu *pMenu = new wxMenu;
#if CAN_SAVE_FILES
pMenu->Append(Menu_Save, _("&Save..."), _("Save log contents to file"));
#endif // CAN_SAVE_FILES
pMenu->Append(Menu_Clear, _("C&lear"), _("Clear the log contents"));
pMenu->AppendSeparator();
pMenu->Append(Menu_Close, _("&Close"), _("Close this window"));
pMenuBar->Append(pMenu, _("&Log"));
SetMenuBar(pMenuBar);
#endif // wxUSE_MENUS
#if wxUSE_STATUSBAR
// status bar for menu prompts
CreateStatusBar();
#endif // wxUSE_STATUSBAR
m_log->OnFrameCreate(this);
}
void wxLogFrame::DoClose()
{
if ( m_log->OnFrameClose(this) )
{
// instead of closing just hide the window to be able to Show() it
// later
Show(false);
}
}
void wxLogFrame::OnClose(wxCommandEvent& WXUNUSED(event))
{
DoClose();
}
void wxLogFrame::OnCloseWindow(wxCloseEvent& WXUNUSED(event))
{
DoClose();
}
#if CAN_SAVE_FILES
void wxLogFrame::OnSave(wxCommandEvent& WXUNUSED(event))
{
wxString filename;
wxFile file;
int rc = OpenLogFile(file, &filename, this);
if ( rc == -1 )
{
// cancelled
return;
}
bool bOk = rc != 0;
// retrieve text and save it
// -------------------------
int nLines = m_pTextCtrl->GetNumberOfLines();
for ( int nLine = 0; bOk && nLine < nLines; nLine++ ) {
bOk = file.Write(m_pTextCtrl->GetLineText(nLine) +
wxTextFile::GetEOL());
}
if ( bOk )
bOk = file.Close();
if ( !bOk ) {
wxLogError(_("Can't save log contents to file."));
}
else {
wxLogStatus((wxFrame*)this, _("Log saved to the file '%s'."), filename.c_str());
}
}
#endif // CAN_SAVE_FILES
void wxLogFrame::OnClear(wxCommandEvent& WXUNUSED(event))
{
m_pTextCtrl->Clear();
}
void wxLogFrame::OnInternalIdle()
{
{
wxCRIT_SECT_LOCKER(locker, m_critSection);
const size_t count = m_pendingMessages.size();
for ( size_t n = 0; n < count; n++ )
{
DoShowLogMessage(m_pendingMessages[n]);
}
m_pendingMessages.clear();
} // release m_critSection
wxFrame::OnInternalIdle();
}
void wxLogFrame::AddLogMessage(const wxString& message)
{
wxCRIT_SECT_LOCKER(locker, m_critSection);
#if wxUSE_THREADS
if ( !wxThread::IsMain() || !m_pendingMessages.empty() )
{
// message needs to be queued for later showing
m_pendingMessages.Add(message);
wxWakeUpIdle();
}
else // we are the main thread and no messages are queued, so we can
// log the message directly
#endif // wxUSE_THREADS
{
DoShowLogMessage(message);
}
}
wxLogFrame::~wxLogFrame()
{
m_log->OnFrameDelete(this);
}
// wxLogWindow
// -----------
wxLogWindow::wxLogWindow(wxWindow *pParent,
const wxString& szTitle,
bool bShow,
bool bDoPass)
{
PassMessages(bDoPass);
m_pLogFrame = new wxLogFrame(pParent, this, szTitle);
if ( bShow )
m_pLogFrame->Show();
}
void wxLogWindow::Show(bool bShow)
{
m_pLogFrame->Show(bShow);
}
void wxLogWindow::DoLogTextAtLevel(wxLogLevel level, const wxString& msg)
{
// first let the previous logger show it
wxLogPassThrough::DoLogTextAtLevel(level, msg);
if ( !m_pLogFrame )
return;
// don't put trace messages in the text window for 2 reasons:
// 1) there are too many of them
// 2) they may provoke other trace messages (e.g. wxMSW code uses
// wxLogTrace to log Windows messages and adding text to the control
// sends more of them) thus sending a program into an infinite loop
if ( level == wxLOG_Trace )
return;
m_pLogFrame->AddLogMessage(msg);
}
wxFrame *wxLogWindow::GetFrame() const
{
return m_pLogFrame;
}
void wxLogWindow::OnFrameCreate(wxFrame * WXUNUSED(frame))
{
}
bool wxLogWindow::OnFrameClose(wxFrame * WXUNUSED(frame))
{
// allow to close
return true;
}
void wxLogWindow::OnFrameDelete(wxFrame * WXUNUSED(frame))
{
m_pLogFrame = NULL;
}
wxLogWindow::~wxLogWindow()
{
// may be NULL if log frame already auto destroyed itself
delete m_pLogFrame;
}
#endif // wxUSE_LOGWINDOW
// ----------------------------------------------------------------------------
// wxLogDialog
// ----------------------------------------------------------------------------
#if wxUSE_LOG_DIALOG
wxString wxLogDialog::ms_details;
size_t wxLogDialog::ms_maxLength = 0;
wxLogDialog::wxLogDialog(wxWindow *parent,
const wxArrayString& messages,
const wxArrayInt& severity,
const wxArrayLong& times,
const wxString& caption,
long style)
: wxDialog(parent, wxID_ANY, caption,
wxDefaultPosition, wxDefaultSize,
wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
{
// init the static variables:
if ( ms_details.empty() )
{
// ensure that we won't loop here if wxGetTranslation()
// happens to pop up a Log message while translating this :-)
ms_details = wxTRANSLATE("&Details");
ms_details = wxGetTranslation(ms_details);
#ifdef __SMARTPHONE__
ms_details = wxStripMenuCodes(ms_details);
#endif
}
if ( ms_maxLength == 0 )
{
ms_maxLength = (2 * wxGetDisplaySize().x/3) / GetCharWidth();
}
size_t count = messages.GetCount();
m_messages.Alloc(count);
m_severity.Alloc(count);
m_times.Alloc(count);
for ( size_t n = 0; n < count; n++ )
{
m_messages.Add(messages[n]);
m_severity.Add(severity[n]);
m_times.Add(times[n]);
}
m_listctrl = NULL;
bool isPda = (wxSystemSettings::GetScreenType() <= wxSYS_SCREEN_PDA);
// create the controls which are always shown and layout them: we use
// sizers even though our window is not resizeable to calculate the size of
// the dialog properly
wxBoxSizer *sizerTop = new wxBoxSizer(wxVERTICAL);
wxBoxSizer *sizerAll = new wxBoxSizer(isPda ? wxVERTICAL : wxHORIZONTAL);
if (!isPda)
{
wxStaticBitmap *icon = new wxStaticBitmap
(
this,
wxID_ANY,
wxArtProvider::GetMessageBoxIcon(style)
);
sizerAll->Add(icon, wxSizerFlags().Centre());
}
// create the text sizer with a minimal size so that we are sure it won't be too small
wxString message = EllipsizeString(messages.Last());
wxSizer *szText = CreateTextSizer(message);
szText->SetMinSize(wxMin(300, wxGetDisplaySize().x / 3), -1);
sizerAll->Add(szText, wxSizerFlags(1).Centre().Border(wxLEFT | wxRIGHT));
wxButton *btnOk = new wxButton(this, wxID_OK);
sizerAll->Add(btnOk, wxSizerFlags().Centre());
sizerTop->Add(sizerAll, wxSizerFlags().Expand().Border());
// add the details pane
#ifndef __SMARTPHONE__
wxCollapsiblePane * const
collpane = new wxCollapsiblePane(this, wxID_ANY, ms_details);
sizerTop->Add(collpane, wxSizerFlags(1).Expand().Border());
wxWindow *win = collpane->GetPane();
wxSizer * const paneSz = new wxBoxSizer(wxVERTICAL);
CreateDetailsControls(win);
paneSz->Add(m_listctrl, wxSizerFlags(1).Expand().Border(wxTOP));
#if wxUSE_CLIPBOARD || CAN_SAVE_FILES
wxBoxSizer * const btnSizer = new wxBoxSizer(wxHORIZONTAL);
wxSizerFlags flagsBtn;
flagsBtn.Border(wxLEFT);
#if wxUSE_CLIPBOARD
btnSizer->Add(new wxButton(win, wxID_COPY), flagsBtn);
#endif // wxUSE_CLIPBOARD
#if CAN_SAVE_FILES
btnSizer->Add(new wxButton(win, wxID_SAVE), flagsBtn);
#endif // CAN_SAVE_FILES
paneSz->Add(btnSizer, wxSizerFlags().Right().Border(wxTOP));
#endif // wxUSE_CLIPBOARD || CAN_SAVE_FILES
win->SetSizer(paneSz);
paneSz->SetSizeHints(win);
#else // __SMARTPHONE__
SetLeftMenu(wxID_OK);
SetRightMenu(wxID_MORE, ms_details + EXPAND_SUFFIX);
#endif // __SMARTPHONE__/!__SMARTPHONE__
SetSizerAndFit(sizerTop);
Centre();
if (isPda)
{
// Move up the screen so that when we expand the dialog,
// there's enough space.
Move(wxPoint(GetPosition().x, GetPosition().y / 2));
}
}
void wxLogDialog::CreateDetailsControls(wxWindow *parent)
{
wxString fmt = wxLog::GetTimestamp();
bool hasTimeStamp = !fmt.IsEmpty();
// create the list ctrl now
m_listctrl = new wxListCtrl(parent, wxID_ANY,
wxDefaultPosition, wxDefaultSize,
wxSUNKEN_BORDER |
wxLC_REPORT |
wxLC_NO_HEADER |
wxLC_SINGLE_SEL);
#ifdef __WXWINCE__
// This makes a big aesthetic difference on WinCE but I
// don't want to risk problems on other platforms
m_listctrl->Hide();
#endif
// no need to translate these strings as they're not shown to the
// user anyhow (we use wxLC_NO_HEADER style)
m_listctrl->InsertColumn(0, _T("Message"));
if (hasTimeStamp)
m_listctrl->InsertColumn(1, _T("Time"));
// prepare the imagelist
static const int ICON_SIZE = 16;
wxImageList *imageList = new wxImageList(ICON_SIZE, ICON_SIZE);
// order should be the same as in the switch below!
static const wxChar* icons[] =
{
wxART_ERROR,
wxART_WARNING,
wxART_INFORMATION
};
bool loadedIcons = true;
for ( size_t icon = 0; icon < WXSIZEOF(icons); icon++ )
{
wxBitmap bmp = wxArtProvider::GetBitmap(icons[icon], wxART_MESSAGE_BOX,
wxSize(ICON_SIZE, ICON_SIZE));
// This may very well fail if there are insufficient colours available.
// Degrade gracefully.
if ( !bmp.Ok() )
{
loadedIcons = false;
break;
}
imageList->Add(bmp);
}
m_listctrl->SetImageList(imageList, wxIMAGE_LIST_SMALL);
// fill the listctrl
size_t count = m_messages.GetCount();
for ( size_t n = 0; n < count; n++ )
{
int image;
if ( loadedIcons )
{
switch ( m_severity[n] )
{
case wxLOG_Error:
image = 0;
break;
case wxLOG_Warning:
image = 1;
break;
default:
image = 2;
}
}
else // failed to load images
{
image = -1;
}
wxString msg = m_messages[n];
msg.Replace(wxT("\n"), wxT(" "));
msg = EllipsizeString(msg);
m_listctrl->InsertItem(n, msg, image);
if (hasTimeStamp)
m_listctrl->SetItem(n, 1, TimeStamp(fmt, (time_t)m_times[n]));
}
// let the columns size themselves
m_listctrl->SetColumnWidth(0, wxLIST_AUTOSIZE);
if (hasTimeStamp)
m_listctrl->SetColumnWidth(1, wxLIST_AUTOSIZE);
// calculate an approximately nice height for the listctrl
int height = GetCharHeight()*(count + 4);
// but check that the dialog won't fall fown from the screen
//
// we use GetMinHeight() to get the height of the dialog part without the
// details and we consider that the "Save" button below and the separator
// line (and the margins around it) take about as much, hence double it
int heightMax = wxGetDisplaySize().y - GetPosition().y - 2*GetMinHeight();
// we should leave a margin
heightMax *= 9;
heightMax /= 10;
m_listctrl->SetSize(wxDefaultCoord, wxMin(height, heightMax));
}
void wxLogDialog::OnListItemActivated(wxListEvent& event)
{
// show the activated item in a message box
// This allow the user to correctly display the logs which are longer
// than the listctrl and thus gets truncated or those which contains
// newlines.
// NB: don't do:
// wxString str = m_listctrl->GetItemText(event.GetIndex());
// as there's a 260 chars limit on the items inside a wxListCtrl in wxMSW.
wxString str = m_messages[event.GetIndex()];
// wxMessageBox will nicely handle the '\n' in the string (if any)
// and supports long strings
wxMessageBox(str, wxT("Log message"), wxOK, this);
}
void wxLogDialog::OnOk(wxCommandEvent& WXUNUSED(event))
{
EndModal(wxID_OK);
}
#if CAN_SAVE_FILES || wxUSE_CLIPBOARD
wxString wxLogDialog::GetLogMessages() const
{
wxString fmt = wxLog::GetTimestamp();
if ( fmt.empty() )
{
// use the default format
fmt = "%c";
}
const size_t count = m_messages.GetCount();
wxString text;
text.reserve(count*m_messages[0].length());
for ( size_t n = 0; n < count; n++ )
{
text << TimeStamp(fmt, (time_t)m_times[n])
<< ": "
<< m_messages[n]
<< wxTextFile::GetEOL();
}
return text;
}
#endif // CAN_SAVE_FILES || wxUSE_CLIPBOARD
#if wxUSE_CLIPBOARD
void wxLogDialog::OnCopy(wxCommandEvent& WXUNUSED(event))
{
wxClipboardLocker clip;
if ( !clip ||
!wxTheClipboard->AddData(new wxTextDataObject(GetLogMessages())) )
{
wxLogError(_("Failed to copy dialog contents to the clipboard."));
}
}
#endif // wxUSE_CLIPBOARD
#if CAN_SAVE_FILES
void wxLogDialog::OnSave(wxCommandEvent& WXUNUSED(event))
{
wxFile file;
int rc = OpenLogFile(file, NULL, this);
if ( rc == -1 )
{
// cancelled
return;
}
if ( !rc || !file.Write(GetLogMessages()) || !file.Close() )
{
wxLogError(_("Can't save log contents to file."));
}
}
#endif // CAN_SAVE_FILES
wxLogDialog::~wxLogDialog()
{
if ( m_listctrl )
{
delete m_listctrl->GetImageList(wxIMAGE_LIST_SMALL);
}
}
#endif // wxUSE_LOG_DIALOG
#if CAN_SAVE_FILES
// pass an uninitialized file object, the function will ask the user for the
// filename and try to open it, returns true on success (file was opened),
// false if file couldn't be opened/created and -1 if the file selection
// dialog was cancelled
static int OpenLogFile(wxFile& file, wxString *pFilename, wxWindow *parent)
{
// get the file name
// -----------------
wxString filename = wxSaveFileSelector(wxT("log"), wxT("txt"), wxT("log.txt"), parent);
if ( !filename ) {
// cancelled
return -1;
}
// open file
// ---------
bool bOk = true; // suppress warning about it being possible uninitialized
if ( wxFile::Exists(filename) ) {
bool bAppend = false;
wxString strMsg;
strMsg.Printf(_("Append log to file '%s' (choosing [No] will overwrite it)?"),
filename.c_str());
switch ( wxMessageBox(strMsg, _("Question"),
wxICON_QUESTION | wxYES_NO | wxCANCEL) ) {
case wxYES:
bAppend = true;
break;
case wxNO:
bAppend = false;
break;
case wxCANCEL:
return -1;
default:
wxFAIL_MSG(_("invalid message box return value"));
}
if ( bAppend ) {
bOk = file.Open(filename, wxFile::write_append);
}
else {
bOk = file.Create(filename, true /* overwrite */);
}
}
else {
bOk = file.Create(filename);
}
if ( pFilename )
*pFilename = filename;
return bOk;
}
#endif // CAN_SAVE_FILES
#endif // !(wxUSE_LOGGUI || wxUSE_LOGWINDOW)
#if wxUSE_LOG && wxUSE_TEXTCTRL
// ----------------------------------------------------------------------------
// wxLogTextCtrl implementation
// ----------------------------------------------------------------------------
wxLogTextCtrl::wxLogTextCtrl(wxTextCtrl *pTextCtrl)
{
m_pTextCtrl = pTextCtrl;
}
void wxLogTextCtrl::DoLogText(const wxString& msg)
{
m_pTextCtrl->AppendText(msg + wxS('\n'));
}
#endif // wxUSE_LOG && wxUSE_TEXTCTRL