// This file is part of BOINC. // http://boinc.berkeley.edu // Copyright (C) 2008 University of California // // BOINC is free software; you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License // as published by the Free Software Foundation, // either version 3 of the License, or (at your option) any later version. // // BOINC is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // See the GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with BOINC. If not, see . #if defined(__GNUG__) && !defined(__APPLE__) #pragma implementation "DlgEventLog.h" #endif #include "stdwx.h" #include "common_defs.h" #include "diagnostics.h" #include "str_util.h" #include "mfile.h" #include "miofile.h" #include "parse.h" #include "error_numbers.h" #include "Events.h" #include "BOINCGUIApp.h" #include "SkinManager.h" #include "MainDocument.h" #include "BOINCBaseFrame.h" #include "version.h" #include "DlgEventLogListCtrl.h" #include "DlgEventLog.h" #include "AdvancedFrame.h" ////@begin includes ////@end includes ////@begin XPM images ////@end XPM images #define COLUMN_PROJECT 0 #define COLUMN_TIME 1 #define COLUMN_MESSAGE 2 /*! * CDlgEventLog type definition */ IMPLEMENT_DYNAMIC_CLASS( CDlgEventLog, wxDialog ) /*! * CDlgEventLog event table definition */ BEGIN_EVENT_TABLE( CDlgEventLog, wxDialog ) ////@begin CDlgEventLog event table entries EVT_SHOW(CDlgEventLog::OnShow) EVT_HELP(wxID_ANY, CDlgEventLog::OnHelp) EVT_BUTTON(wxID_OK, CDlgEventLog::OnOK) EVT_BUTTON(ID_COPYAll, CDlgEventLog::OnMessagesCopyAll) EVT_BUTTON(ID_COPYSELECTED, CDlgEventLog::OnMessagesCopySelected) EVT_BUTTON(ID_SIMPLE_HELP, CDlgEventLog::OnButtonHelp) EVT_CLOSE(CDlgEventLog::OnClose) ////@end CDlgEventLog event table entries END_EVENT_TABLE() /*! * CDlgEventLog constructors */ CDlgEventLog::CDlgEventLog( ) { } CDlgEventLog::CDlgEventLog( wxWindow* parent, wxWindowID id, const wxString& caption, const wxPoint& pos, const wxSize& size, long style ) { wxLogTrace(wxT("Function Start/End"), wxT("CDlgEventLog::CDlgEventLog - Constructor Function Begin")); Create(parent, id, caption, pos, size, style); wxLogTrace(wxT("Function Start/End"), wxT("CDlgEventLog::CDlgEventLog - Constructor Function End")); } CDlgEventLog::~CDlgEventLog() { wxLogTrace(wxT("Function Start/End"), wxT("CDlgEventLog::CDlgEventLog - Destructor Function Begin")); SaveState(); // Save state if close box on window frame clicked if (m_pMessageInfoAttr) { delete m_pMessageInfoAttr; m_pMessageInfoAttr = NULL; } if (m_pMessageErrorAttr) { delete m_pMessageErrorAttr; m_pMessageErrorAttr = NULL; } wxGetApp().CloseEventLog(); wxLogTrace(wxT("Function Start/End"), wxT("CDlgEventLog::CDlgEventLog - Destructor Function End")); } /*! * CDlgEventLog creator */ bool CDlgEventLog::Create( wxWindow* WXUNUSED(parent), wxWindowID id, const wxString& caption, const wxPoint& pos, const wxSize& size, long style ) { ////@begin CDlgEventLog member initialisation m_iPreviousDocCount = 0; m_bProcessingRefreshEvent = false; ////@end CDlgEventLog member initialisation wxString strCaption = caption; if (strCaption.IsEmpty()) { CSkinAdvanced* pSkinAdvanced = wxGetApp().GetSkinManager()->GetAdvanced(); wxASSERT(pSkinAdvanced); wxASSERT(wxDynamicCast(pSkinAdvanced, CSkinAdvanced)); strCaption.Printf(_("%s - Event Log"), pSkinAdvanced->GetApplicationName().c_str()); } SetExtraStyle(GetExtraStyle()|wxDIALOG_EX_CONTEXTHELP|wxWS_EX_BLOCK_EVENTS); wxDialog::Create( NULL, id, strCaption, pos, size, style ); CreateControls(); // Create List Pane Items m_pList->InsertColumn(COLUMN_PROJECT, _("Project"), wxLIST_FORMAT_LEFT, 109); m_pList->InsertColumn(COLUMN_TIME, _("Time"), wxLIST_FORMAT_LEFT, 130); m_pList->InsertColumn(COLUMN_MESSAGE, _("Message"), wxLIST_FORMAT_LEFT, 378); m_pMessageInfoAttr = new wxListItemAttr(*wxBLACK, *wxWHITE, wxNullFont); m_pMessageErrorAttr = new wxListItemAttr(*wxRED, *wxWHITE, wxNullFont); GetSizer()->Fit(this); GetSizer()->SetSizeHints(this); Center(); // To work properly on Mac, RestoreState() must be called _after_ // calling GetSizer()->Fit(), GetSizer()->SetSizeHints() and Center() RestoreState(); return true; } /*! * Control creation for CDlgEventLog */ void CDlgEventLog::CreateControls() { wxFlexGridSizer* itemFlexGridSizer2 = new wxFlexGridSizer(2, 1, 0, 0); itemFlexGridSizer2->AddGrowableRow(0); itemFlexGridSizer2->AddGrowableCol(0); SetSizer(itemFlexGridSizer2); m_pList = new CDlgEventLogListCtrl(this, ID_SIMPLE_MESSAGESVIEW, DEFAULT_LIST_MULTI_SEL_FLAGS); itemFlexGridSizer2->Add(m_pList, 0, wxGROW|wxALL, 5); wxBoxSizer* itemBoxSizer4 = new wxBoxSizer(wxHORIZONTAL); itemFlexGridSizer2->Add(itemBoxSizer4, 0, wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL|wxALL, 12); #ifdef wxUSE_CLIPBOARD wxButton* itemButton1 = new wxButton(this, ID_COPYAll, _("Copy All"), wxDefaultPosition, wxDefaultSize ); itemButton1->SetHelpText( _("Copy all the messages to the clipboard.") ); #if wxUSE_TOOLTIPS itemButton1->SetToolTip( _("Copy all the messages to the clipboard.") ); #endif itemBoxSizer4->Add(itemButton1, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5); wxButton* itemButton2 = new wxButton(this, ID_COPYSELECTED, _("Copy Selected"), wxDefaultPosition, wxDefaultSize ); itemButton2->SetHelpText( #ifdef __WXMAC__ _("Copy the selected messages to the clipboard. You can select multiple messages by holding down the shift or command key while clicking on messages.") #else _("Copy the selected messages to the clipboard. You can select multiple messages by holding down the shift or control key while clicking on messages.") #endif ); #if wxUSE_TOOLTIPS itemButton2->SetToolTip( #ifdef __WXMAC__ _("Copy the selected messages to the clipboard. You can select multiple messages by holding down the shift or command key while clicking on messages.") #else _("Copy the selected messages to the clipboard. You can select multiple messages by holding down the shift or control key while clicking on messages.") #endif ); #endif itemBoxSizer4->Add(itemButton2, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5); #endif wxButton* itemButton44 = new wxButton(this, wxID_OK, _("Close"), wxDefaultPosition, wxDefaultSize); itemBoxSizer4->Add(itemButton44, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5); #ifndef __WXMSW__ #ifdef __WXMAC__ wxButton* itemButton45 = new wxButton(this, ID_SIMPLE_HELP, _("Help"), wxDefaultPosition, wxDefaultSize); itemButton45->SetHelpText( _("Get help with BOINC") ); #ifdef wxUSE_TOOLTIPS itemButton45->SetToolTip(_("Get help with BOINC")); #endif itemBoxSizer4->Add(itemButton45, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5); #else wxContextHelpButton* itemButton45 = new wxContextHelpButton(this); itemBoxSizer4->Add(itemButton45, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5); #endif #endif } /*! * wxEVT_SHOW event handler for ID_DLGMESSAGES */ void CDlgEventLog::OnShow(wxShowEvent& event) { wxLogTrace(wxT("Function Start/End"), wxT("CDlgEventLog::OnShow - Function Begin")); static bool bAlreadyRunning = false; if ((event.GetEventObject() == this) && !bAlreadyRunning) { bAlreadyRunning = true; wxLogTrace(wxT("Function Status"), wxT("CDlgEventLog::OnShow - Show/Hide Event for CAdvancedFrame detected")); if (event.GetShow()) { RestoreWindowDimensions(); } else { SaveWindowDimensions(); } bAlreadyRunning = false; } else { event.Skip(); } wxLogTrace(wxT("Function Start/End"), wxT("CDlgEventLog::OnShow - Function End")); } /*! * wxEVT_HELP event handler for ID_DLGMESSAGES */ void CDlgEventLog::OnHelp(wxHelpEvent& event) { wxLogTrace(wxT("Function Start/End"), wxT("CDlgEventLog::OnHelp - Function Begin")); if (IsShown()) { wxString strURL = wxGetApp().GetSkinManager()->GetAdvanced()->GetOrganizationHelpUrl(); wxString wxurl; wxurl.Printf( wxT("%s?target=simple_messages&version=%s&controlid=%d"), strURL.c_str(), wxString(BOINC_VERSION_STRING, wxConvUTF8).c_str(), event.GetId() ); wxLaunchDefaultBrowser(wxurl); } wxLogTrace(wxT("Function Start/End"), wxT("CDlgEventLog::OnHelp - Function End")); } /*! * wxEVT_COMMAND_BUTTON_CLICKED event handler for wxID_OK */ void CDlgEventLog::OnOK( wxCommandEvent& WXUNUSED(event) ) { Destroy(); } /*! * wxEVT_CLOSE event handler for CDlgEventLog (window close control clicked) */ void CDlgEventLog::OnClose(wxCloseEvent& WXUNUSED(event)) { Destroy(); } /*! * called from CAdvancedFrame::OnRefreshView() */ void CDlgEventLog::OnRefresh( wxTimerEvent& WXUNUSED(event) ) { bool isConnected; static bool was_connected = false; if (!m_bProcessingRefreshEvent) { m_bProcessingRefreshEvent = true; wxASSERT(m_pList); wxInt32 iDocCount = wxGetApp().GetDocument()->GetMessageCount(); if (0 >= iDocCount) { m_pList->DeleteAllItems(); } else { // If connection status changed, adjust color of messages display isConnected = wxGetApp().GetDocument()->IsConnected(); if (was_connected != isConnected) { was_connected = isConnected; if (isConnected) { m_pMessageInfoAttr->SetTextColour(*wxBLACK); m_pMessageErrorAttr->SetTextColour(*wxRED); } else { wxColourDatabase colorBase; m_pMessageInfoAttr->SetTextColour(wxColour(128, 128, 128)); m_pMessageErrorAttr->SetTextColour(wxColour(255, 128, 128)); } // Force a complete update m_pList->DeleteAllItems(); m_pList->SetItemCount(iDocCount); m_iPreviousDocCount = 0; // Force scrolling to bottom } else { // Connection status didn't change if (m_iPreviousDocCount != iDocCount) { m_pList->SetItemCount(iDocCount); } } } if ((iDocCount > 1) && (EnsureLastItemVisible()) && (m_iPreviousDocCount != iDocCount)) { m_pList->EnsureVisible(iDocCount - 1); } if (m_iPreviousDocCount != iDocCount) { m_iPreviousDocCount = iDocCount; } m_bProcessingRefreshEvent = false; } } bool CDlgEventLog::SaveState() { wxLogTrace(wxT("Function Start/End"), wxT("CDlgEventLog::SaveState - Function Begin")); wxString strBaseConfigLocation = wxString(wxT("/Messages")); wxConfigBase* pConfig = wxConfigBase::Get(FALSE); wxListItem liColumnInfo; wxInt32 iIndex = 0; wxInt32 iColumnCount = 0; wxASSERT(pConfig); // An odd case happens every once and awhile where wxWidgets looses // the pointer to the config object, or it is cleaned up before // the window has finished it's cleanup duty. If we detect a NULL // pointer, return false. if (!pConfig) return false; // // Save Frame State // pConfig->SetPath(strBaseConfigLocation); // Reterieve and store the latest window dimensions. SaveWindowDimensions(); // Convert to a zero based index iColumnCount = m_pList->GetColumnCount() - 1; // Which fields are we interested in? liColumnInfo.SetMask( wxLIST_MASK_TEXT | wxLIST_MASK_WIDTH | wxLIST_MASK_FORMAT ); // Cycle through the columns recording anything interesting for (iIndex = 0; iIndex <= iColumnCount; iIndex++) { m_pList->GetColumn(iIndex, liColumnInfo); pConfig->SetPath(strBaseConfigLocation + liColumnInfo.GetText()); pConfig->Write(wxT("Width"), m_pList->GetColumnWidth(iIndex)); // Work around bug in wxMac-2.8.0 wxListCtrl::SetColumn() } wxLogTrace(wxT("Function Start/End"), wxT("CDlgEventLog::SaveState - Function End")); return true; } void CDlgEventLog::SaveWindowDimensions() { wxString strBaseConfigLocation = wxString(wxT("/Simple/Messages")); wxConfigBase* pConfig = wxConfigBase::Get(FALSE); wxASSERT(pConfig); pConfig->SetPath(strBaseConfigLocation); pConfig->Write(wxT("WindowIconized"), IsIconized()); pConfig->Write(wxT("WindowMaximized"), IsMaximized()); pConfig->Write(wxT("Width"), GetSize().GetWidth()); pConfig->Write(wxT("Height"), GetSize().GetHeight()); #ifdef __WXMAC__ pConfig->Write(wxT("XPos"), GetPosition().x); pConfig->Write(wxT("YPos"), GetPosition().y); #endif // ! __WXMAC__ } bool CDlgEventLog::RestoreState() { wxLogTrace(wxT("Function Start/End"), wxT("CDlgEventLog::RestoreState - Function Begin")); wxString strBaseConfigLocation = wxString(wxT("/Messages")); wxConfigBase* pConfig = wxConfigBase::Get(FALSE); wxListItem liColumnInfo; wxInt32 iIndex = 0; wxInt32 iColumnCount = 0; wxInt32 iTempValue = 0; wxASSERT(pConfig); // An odd case happens every once and awhile where wxWidgets looses // the pointer to the config object, or it is cleaned up before // the window has finished it's cleanup duty. If we detect a NULL // pointer, return false. if (!pConfig) return false; // // Restore Frame State // pConfig->SetPath(strBaseConfigLocation); // Restore the windows properties RestoreWindowDimensions(); // Convert to a zero based index iColumnCount = m_pList->GetColumnCount() - 1; // Which fields are we interested in? liColumnInfo.SetMask( wxLIST_MASK_TEXT | wxLIST_MASK_WIDTH | wxLIST_MASK_FORMAT ); // Cycle through the columns recording anything interesting for (iIndex = 0; iIndex <= iColumnCount; iIndex++) { m_pList->GetColumn(iIndex, liColumnInfo); pConfig->SetPath(strBaseConfigLocation + liColumnInfo.GetText()); pConfig->Read(wxT("Width"), &iTempValue, -1); if (-1 != iTempValue) { m_pList->SetColumnWidth(iIndex,iTempValue); // Work around bug in wxMac-2.8.0 wxListCtrl::SetColumn() } m_pList->SetColumn(iIndex, liColumnInfo); } wxLogTrace(wxT("Function Start/End"), wxT("CDlgEventLog::RestoreState - Function End")); return true; } void CDlgEventLog::RestoreWindowDimensions() { wxString strBaseConfigLocation = wxString(wxT("/Simple/Messages")); wxConfigBase* pConfig = wxConfigBase::Get(FALSE); bool bWindowIconized = false; bool bWindowMaximized = false; int iHeight = 0; int iWidth = 0; int iTop = 0; int iLeft = 0; wxASSERT(pConfig); pConfig->SetPath(strBaseConfigLocation); pConfig->Read(wxT("YPos"), &iTop, 30); pConfig->Read(wxT("XPos"), &iLeft, 30); pConfig->Read(wxT("Width"), &iWidth, 640); pConfig->Read(wxT("Height"), &iHeight, 480); pConfig->Read(wxT("WindowIconized"), &bWindowIconized, false); pConfig->Read(wxT("WindowMaximized"), &bWindowMaximized, false); #ifndef __WXMAC__ Iconize(bWindowIconized); Maximize(bWindowMaximized); if (!IsIconized() && !IsMaximized()) { SetSize(-1, -1, iWidth, iHeight); } #else // ! __WXMAC__ // If the user has changed the arrangement of multiple // displays, make sure the window title bar is still on-screen. Rect titleRect = {iTop, iLeft, iTop+22, iLeft+iWidth }; InsetRect(&titleRect, 5, 5); // Make sure at least a 5X5 piece visible RgnHandle displayRgn = NewRgn(); CopyRgn(GetGrayRgn(), displayRgn); // Region encompassing all displays Rect menuRect = ((**GetMainDevice())).gdRect; menuRect.bottom = GetMBarHeight() + menuRect.top; RgnHandle menuRgn = NewRgn(); RectRgn(menuRgn, &menuRect); // Region hidden by menu bar DiffRgn(displayRgn, menuRgn, displayRgn); // Subtract menu bar retion if (!RectInRgn(&titleRect, displayRgn)) iTop = iLeft = 30; DisposeRgn(menuRgn); DisposeRgn(displayRgn); SetSize(iLeft, iTop, iWidth, iHeight); #endif // ! __WXMAC__ } /*! * wxEVT_COMMAND_BUTTON_CLICKED event handler for ID_COPYAll */ void CDlgEventLog::OnMessagesCopyAll( wxCommandEvent& WXUNUSED(event) ) { wxLogTrace(wxT("Function Start/End"), wxT("CDlgEventLog::OnMessagesCopyAll - Function Begin")); #ifdef wxUSE_CLIPBOARD wxInt32 iIndex = -1; wxInt32 iRowCount = 0; iRowCount = m_pList->GetItemCount(); OpenClipboard( iRowCount * 1024 ); iRowCount = m_pList->GetItemCount(); for (iIndex = 0; iIndex < iRowCount; iIndex++) { CopyToClipboard(iIndex); } CloseClipboard(); #endif wxLogTrace(wxT("Function Start/End"), wxT("CDlgEventLog::OnMessagesCopyAll - Function End")); } /*! * wxEVT_COMMAND_BUTTON_CLICKED event handler for ID_COPYSELECTED */ void CDlgEventLog::OnMessagesCopySelected( wxCommandEvent& WXUNUSED(event) ) { wxLogTrace(wxT("Function Start/End"), wxT("CDlgEventLog::OnMessagesCopySelected - Function Begin")); #ifdef wxUSE_CLIPBOARD wxInt32 iIndex = -1; wxInt32 iRowCount = 0; // Count the number of items selected for (;;) { iIndex = m_pList->GetNextItem( iIndex, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED ); if (iIndex == -1) break; iRowCount++; } OpenClipboard( iRowCount * 1024 ); // Reset the position indicator iIndex = -1; for (;;) { iIndex = m_pList->GetNextItem( iIndex, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED ); if (iIndex == -1) break; CopyToClipboard(iIndex); } CloseClipboard(); #endif wxLogTrace(wxT("Function Start/End"), wxT("CDlgEventLog::OnMessagesCopySelected - Function End")); } /*! * wxEVT_COMMAND_BUTTON_CLICKED event handler for ID_SIMPLE_HELP */ void CDlgEventLog::OnButtonHelp( wxCommandEvent& event ) { wxLogTrace(wxT("Function Start/End"), wxT("CDlgEventLog::OnHelp - Function Begin")); if (IsShown()) { wxString strURL = wxGetApp().GetSkinManager()->GetAdvanced()->GetOrganizationHelpUrl(); wxString wxurl; wxurl.Printf( wxT("%s?target=simple_messages&version=%s&controlid=%d"), strURL.c_str(), wxString(BOINC_VERSION_STRING, wxConvUTF8).c_str(), event.GetId() ); wxLaunchDefaultBrowser(wxurl); } wxLogTrace(wxT("Function Start/End"), wxT("CDlgEventLog::OnHelp - Function End")); } wxString CDlgEventLog::OnListGetItemText(long item, long column) const { wxString strBuffer = wxEmptyString; switch(column) { case COLUMN_PROJECT: FormatProjectName(item, strBuffer); break; case COLUMN_TIME: FormatTime(item, strBuffer); break; case COLUMN_MESSAGE: FormatMessage(item, strBuffer); break; } return strBuffer; } wxListItemAttr* CDlgEventLog::OnListGetItemAttr(long item) const { wxListItemAttr* pAttribute = NULL; MESSAGE* message = wxGetApp().GetDocument()->message(item); if (message) { switch(message->priority) { case MSG_USER_ERROR: pAttribute = m_pMessageErrorAttr; break; default: pAttribute = m_pMessageInfoAttr; break; } } return pAttribute; } bool CDlgEventLog::EnsureLastItemVisible() { int numVisible = m_pList->GetCountPerPage(); // Auto-scroll only if already at bottom of list if ((m_iPreviousDocCount > numVisible) && ((m_pList->GetTopItem() + numVisible) < (m_iPreviousDocCount-1)) ) { return false; } return true; } wxInt32 CDlgEventLog::FormatProjectName(wxInt32 item, wxString& strBuffer) const { MESSAGE* message = wxGetApp().GetDocument()->message(item); if (message) { strBuffer = wxString(message->project.c_str(), wxConvUTF8); } return 0; } wxInt32 CDlgEventLog::FormatTime(wxInt32 item, wxString& strBuffer) const { wxDateTime dtBuffer; MESSAGE* message = wxGetApp().GetDocument()->message(item); if (message) { dtBuffer.Set((time_t)message->timestamp); strBuffer = dtBuffer.Format(); } return 0; } wxInt32 CDlgEventLog::FormatMessage(wxInt32 item, wxString& strBuffer) const { MESSAGE* message = wxGetApp().GetDocument()->message(item); if (message) { strBuffer = wxString(message->body.c_str(), wxConvUTF8); } strBuffer.Replace(wxT("\n"), wxT(""), true); return 0; } #ifdef wxUSE_CLIPBOARD bool CDlgEventLog::OpenClipboard( wxInt32 size ) { bool bRetVal = false; bRetVal = wxTheClipboard->Open(); if (bRetVal) { m_bClipboardOpen = true; m_strClipboardData = wxEmptyString; m_strClipboardData.Alloc( size ); wxTheClipboard->Clear(); } return bRetVal; } wxInt32 CDlgEventLog::CopyToClipboard(wxInt32 item) { wxInt32 iRetVal = -1; if (m_bClipboardOpen) { wxString strBuffer = wxEmptyString; wxString strTimeStamp = wxEmptyString; wxString strProject = wxEmptyString; wxString strMessage = wxEmptyString; FormatTime(item, strTimeStamp); FormatProjectName(item, strProject); FormatMessage(item, strMessage); #ifdef __WXMSW__ strBuffer.Printf(wxT("%s|%s|%s\r\n"), strTimeStamp.c_str(), strProject.c_str(), strMessage.c_str()); #else strBuffer.Printf(wxT("%s|%s|%s\n"), strTimeStamp.c_str(), strProject.c_str(), strMessage.c_str()); #endif m_strClipboardData += strBuffer; iRetVal = 0; } return iRetVal; } bool CDlgEventLog::CloseClipboard() { bool bRetVal = false; if (m_bClipboardOpen) { wxTheClipboard->SetData(new wxTextDataObject(m_strClipboardData)); wxTheClipboard->Close(); m_bClipboardOpen = false; m_strClipboardData = wxEmptyString; } return bRetVal; } #endif