boinc/clientgui/BOINCBaseFrame.cpp

712 lines
22 KiB
C++

// Berkeley Open Infrastructure for Network Computing
// http://boinc.berkeley.edu
// Copyright (C) 2005 University of California
//
// This 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 2.1 of the License, or (at your option) any later version.
//
// This software 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.
//
// To view the GNU Lesser General Public License visit
// http://www.gnu.org/copyleft/lesser.html
// or write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#if defined(__GNUG__) && !defined(__APPLE__)
#pragma implementation "BOINCBaseFrame.h"
#endif
#include "stdwx.h"
#include "diagnostics.h"
#include "util.h"
#include "mfile.h"
#include "miofile.h"
#include "parse.h"
#include "error_numbers.h"
#include "hyperlink.h"
#include "BOINCGUIApp.h"
#include "SkinManager.h"
#include "MainDocument.h"
#include "BOINCClientManager.h"
#include "BOINCTaskBar.h"
#include "BOINCBaseFrame.h"
#include "BOINCDialupManager.h"
#include "Events.h"
DEFINE_EVENT_TYPE(wxEVT_FRAME_ALERT)
DEFINE_EVENT_TYPE(wxEVT_FRAME_CONNECT)
DEFINE_EVENT_TYPE(wxEVT_FRAME_INITIALIZED)
DEFINE_EVENT_TYPE(wxEVT_FRAME_REFRESHVIEW)
DEFINE_EVENT_TYPE(wxEVT_FRAME_UPDATESTATUS)
DEFINE_EVENT_TYPE(wxEVT_FRAME_RELOADSKIN)
IMPLEMENT_DYNAMIC_CLASS(CBOINCBaseFrame, wxFrame)
BEGIN_EVENT_TABLE (CBOINCBaseFrame, wxFrame)
EVT_TIMER(ID_DOCUMENTPOLLTIMER, CBOINCBaseFrame::OnDocumentPoll)
EVT_TIMER(ID_ALERTPOLLTIMER, CBOINCBaseFrame::OnAlertPoll)
EVT_FRAME_INITIALIZED(CBOINCBaseFrame::OnInitialized)
EVT_FRAME_ALERT(CBOINCBaseFrame::OnAlert)
EVT_CLOSE(CBOINCBaseFrame::OnClose)
EVT_MENU(ID_FILECLOSEWINDOW, CBOINCBaseFrame::OnCloseWindow)
END_EVENT_TABLE ()
CBOINCBaseFrame::CBOINCBaseFrame()
{
wxLogTrace(wxT("Function Start/End"), wxT("CBOINCBaseFrame::CBOINCBaseFrame - Default Constructor Function Begin"));
wxLogTrace(wxT("Function Start/End"), wxT("CBOINCBaseFrame::CBOINCBaseFrame - Default Constructor Function End"));
}
CBOINCBaseFrame::CBOINCBaseFrame(wxWindow* parent, const wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, const long style) :
wxFrame(parent, id, title, pos, size, style)
{
wxLogTrace(wxT("Function Start/End"), wxT("CBOINCBaseFrame::CBOINCBaseFrame - Function Begin"));
// Configuration Settings
m_iSelectedLanguage = 0;
m_iReminderFrequency = 0;
wxGetApp().SetDisplayExitWarning(1);
m_strNetworkDialupConnectionName = wxEmptyString;
m_aSelectedComputerMRU.Clear();
m_pDialupManager = new CBOINCDialUpManager();
wxASSERT(m_pDialupManager->IsOk());
m_pDocumentPollTimer = new wxTimer(this, ID_DOCUMENTPOLLTIMER);
wxASSERT(m_pDocumentPollTimer);
m_pDocumentPollTimer->Start(250); // Send event every 250 milliseconds
m_pAlertPollTimer = new wxTimer(this, ID_ALERTPOLLTIMER);
wxASSERT(m_pAlertPollTimer);
m_pAlertPollTimer->Start(1000); // Send event every 1000 milliseconds
// Limit the number of times the UI can update itself to two times a second
// NOTE: Linux and Mac were updating several times a second and eating
// CPU time
wxUpdateUIEvent::SetUpdateInterval(500);
// The second half of the initialization process picks up in the OnFrameRender()
// routine since the menus' and status bars' are drawn in the frameworks
// on idle routines, on idle events are sent in between the end of the
// constructor and the first call to OnFrameRender
//
// Look for the 'if (!bAlreadyRunOnce) {' statement
wxLogTrace(wxT("Function Start/End"), wxT("CBOINCBaseFrame::CBOINCBaseFrame - Function End"));
}
CBOINCBaseFrame::~CBOINCBaseFrame() {
wxLogTrace(wxT("Function Start/End"), wxT("CBOINCBaseFrame::~CBOINCBaseFrame - Function Begin"));
wxASSERT(m_pAlertPollTimer);
wxASSERT(m_pDocumentPollTimer);
if (m_pAlertPollTimer) {
m_pAlertPollTimer->Stop();
delete m_pAlertPollTimer;
}
if (m_pDocumentPollTimer) {
m_pDocumentPollTimer->Stop();
delete m_pDocumentPollTimer;
}
if (m_pDialupManager)
delete m_pDialupManager;
wxLogTrace(wxT("Function Start/End"), wxT("CBOINCBaseFrame::~CBOINCBaseFrame - Function End"));
}
void CBOINCBaseFrame::OnDocumentPoll(wxTimerEvent& WXUNUSED(event)) {
static bool bAlreadyRunOnce = false;
CMainDocument* pDoc = wxGetApp().GetDocument();
wxASSERT(pDoc);
wxASSERT(wxDynamicCast(pDoc, CMainDocument));
if (!bAlreadyRunOnce && m_pDocumentPollTimer->IsRunning()) {
// Complete any remaining initialization that has to happen after we are up
// and running
FireInitialize();
bAlreadyRunOnce = true;
}
pDoc->OnPoll();
}
void CBOINCBaseFrame::OnAlertPoll(wxTimerEvent& WXUNUSED(event)) {
static bool bAlreadyRunningLoop = false;
CMainDocument* pDoc = wxGetApp().GetDocument();
if (!bAlreadyRunningLoop && m_pAlertPollTimer->IsRunning()) {
bAlreadyRunningLoop = true;
// Update idle detection if needed.
wxGetApp().UpdateSystemIdleDetection();
// Check to see if there is anything that we need to do from the
// dial up user perspective.
if (pDoc && m_pDialupManager) {
if (pDoc->IsConnected()) {
m_pDialupManager->OnPoll();
}
}
bAlreadyRunningLoop = false;
}
}
void CBOINCBaseFrame::OnInitialized(CFrameEvent& WXUNUSED(event)) {
wxLogTrace(wxT("Function Start/End"), wxT("CBOINCBaseFrame::OnInitialized - Function Begin"));
wxLogTrace(wxT("Function Start/End"), wxT("CBOINCBaseFrame::OnInitialized - Function End"));
}
void CBOINCBaseFrame::OnAlert(CFrameAlertEvent& event) {
wxLogTrace(wxT("Function Start/End"), wxT("CBOINCBaseFrame::OnAlert - Function Begin"));
static bool bAlreadyRunningLoop = false;
if (!bAlreadyRunningLoop) {
bAlreadyRunningLoop = true;
#ifdef __WXMSW__
CTaskBarIcon* pTaskbar = wxGetApp().GetTaskBarIcon();
wxASSERT(pTaskbar);
if ((IsShown() && !event.m_notification_only) || (IsShown() && !pTaskbar->IsBalloonsSupported())) {
if (!event.m_notification_only) {
int retval = 0;
if (!IsShown()) {
Show();
}
retval = ::wxMessageBox(event.m_message, event.m_title, event.m_style, this);
if (event.m_alert_event_type == AlertProcessResponse) {
event.ProcessResponse(retval);
}
}
} else {
// If the main window is hidden or minimzed use the system tray ballon
// to notify the user instead. This keeps dialogs from interfering
// with people typing email messages or any other activity where they
// do not want keyboard focus changed to another window while typing.
unsigned int icon_type;
if (wxICON_ERROR & event.m_style) {
icon_type = NIIF_ERROR;
} else if (wxICON_WARNING & event.m_style) {
icon_type = NIIF_WARNING;
} else if (wxICON_INFORMATION & event.m_style) {
icon_type = NIIF_INFO;
} else {
icon_type = NIIF_NONE;
}
pTaskbar->SetBalloon(
pTaskbar->m_iconTaskBarNormal,
event.m_title,
event.m_message,
5000,
icon_type
);
}
#else
// Notification only events on platforms other than Windows are currently
// discarded. On the Mac a model dialog box for a hidden window causes
// the menus to be locked and the application to become unresponsive. On
// Linux the application is restored and input focus is set on the
// notification which interrupts whatever the user was up to.
if (IsShown() && !event.m_notification_only) {
if (!event.m_notification_only) {
int retval = 0;
if (!IsShown()) {
Show();
}
retval = ::wxMessageBox(event.m_message, event.m_title, event.m_style, this);
if (event.m_alert_event_type == AlertProcessResponse) {
event.ProcessResponse(retval);
}
}
}
#endif
bAlreadyRunningLoop = false;
}
wxLogTrace(wxT("Function Start/End"), wxT("CBOINCBaseFrame::OnAlert - Function End"));
}
void CBOINCBaseFrame::OnClose(wxCloseEvent& event) {
wxLogTrace(wxT("Function Start/End"), wxT("CBOINCBaseFrame::OnClose - Function Begin"));
#if defined(__WXMSW__) || defined(__WXMAC__)
if (!event.CanVeto()) {
Destroy();
} else {
Hide();
}
#else
Destroy();
#endif
wxLogTrace(wxT("Function Start/End"), wxT("CBOINCBaseFrame::OnClose - Function End"));
}
void CBOINCBaseFrame::OnCloseWindow(wxCommandEvent& WXUNUSED(event)) {
wxLogTrace(wxT("Function Start/End"), wxT("CBOINCBaseFrame::OnCloseWindow - Function Begin"));
Close();
wxLogTrace(wxT("Function Start/End"), wxT("CBOINCBaseFrame::OnCloseWindow - Function End"));
}
void CBOINCBaseFrame::OnExit(wxCommandEvent& WXUNUSED(event)) {
wxLogTrace(wxT("Function Start/End"), wxT("CAdvancedFrame::OnExit - Function Begin"));
if (wxGetApp().ConfirmExit()) {
// Under wxWidgets 2.8.0, the task bar icons must be deleted for app to exit its main loop
#ifdef __WXMAC__
CMacSystemMenu* pMSM = wxGetApp().GetMacSystemMenu();
if (pMSM)
delete pMSM;
#endif
// TaskBarIcon isn't used in Linux
#if defined(__WXMSW__) || defined(__WXMAC__)
CTaskBarIcon* pTBI = wxGetApp().GetTaskBarIcon();
if (pTBI && !pTBI->m_bTaskbarInitiatedShutdown) {
delete pTBI;
}
#endif
Close(true);
}
wxLogTrace(wxT("Function Start/End"), wxT("CAdvancedFrame::OnExit - Function End"));
}
void CBOINCBaseFrame::FireInitialize() {
CFrameEvent event(wxEVT_FRAME_INITIALIZED, this);
AddPendingEvent(event);
}
void CBOINCBaseFrame::FireRefreshView() {
CFrameEvent event(wxEVT_FRAME_REFRESHVIEW, this);
AddPendingEvent(event);
}
void CBOINCBaseFrame::FireConnect() {
CFrameEvent event(wxEVT_FRAME_CONNECT, this);
AddPendingEvent(event);
}
void CBOINCBaseFrame::FireReloadSkin() {
CFrameEvent event(wxEVT_FRAME_RELOADSKIN, this);
AddPendingEvent(event);
}
void CBOINCBaseFrame::ShowConnectionBadPasswordAlert( bool bUsedDefaultPassword, int m_iReadGUIRPCAuthFailure ) {
CSkinAdvanced* pSkinAdvanced = wxGetApp().GetSkinManager()->GetAdvanced();
wxString strDialogTitle = wxEmptyString;
wxASSERT(pSkinAdvanced);
wxASSERT(wxDynamicCast(pSkinAdvanced, CSkinAdvanced));
wxLogTrace(wxT("Function Start/End"), wxT("CBOINCBaseFrame::ShowConnectionBadPasswordAlert - Function Begin"));
// %s is the application name
// i.e. 'BOINC Manager', 'GridRepublic Manager'
strDialogTitle.Printf(
_("%s - Connection Error"),
pSkinAdvanced->GetApplicationName().c_str()
);
if ( bUsedDefaultPassword ) {
#ifdef __WXMSW__
if ( EACCES == m_iReadGUIRPCAuthFailure || ENOENT == m_iReadGUIRPCAuthFailure ) {
ShowAlert(
strDialogTitle,
_("You currently are not authorized to manage the client.\n"
"Please contact your administrator to add you to the 'boinc_users' local user group."),
wxOK | wxICON_ERROR
);
} else
#endif
{
ShowAlert(
strDialogTitle,
_("Authorization failed connecting to running client.\n"
"Make sure you start this program in the same directory as the client."),
wxOK | wxICON_ERROR
);
}
} else {
ShowAlert(
strDialogTitle,
_("The password you have provided is incorrect, please try again."),
wxOK | wxICON_ERROR
);
}
wxLogTrace(wxT("Function Start/End"), wxT("CBOINCBaseFrame::ShowConnectionBadPasswordAlert - Function End"));
}
void CBOINCBaseFrame::ShowConnectionFailedAlert() {
CSkinAdvanced* pSkinAdvanced = wxGetApp().GetSkinManager()->GetAdvanced();
wxString strDialogTitle = wxEmptyString;
wxString strDialogMessage = wxEmptyString;
wxASSERT(pSkinAdvanced);
wxASSERT(wxDynamicCast(pSkinAdvanced, CSkinAdvanced));
wxLogTrace(wxT("Function Start/End"), wxT("CBOINCBaseFrame::ShowConnectionFailedAlert - Function Begin"));
// Did BOINC crash? If so restart it.
wxGetApp().GetDocument()->m_pClientManager->AutoRestart();
// %s is the application name
// i.e. 'BOINC Manager', 'GridRepublic Manager'
strDialogTitle.Printf(
_("%s - Connection Failed"),
pSkinAdvanced->GetApplicationName().c_str()
);
// 1st %s is the application name
// i.e. 'BOINC Manager', 'GridRepublic Manager'
// 2st %s is the project name
// i.e. 'BOINC', 'GridRepublic'
strDialogMessage.Printf(
_("%s is not able to connect to a %s client.\n"
"Would you like to try to connect again?"),
pSkinAdvanced->GetApplicationName().c_str(),
pSkinAdvanced->GetApplicationShortName().c_str()
);
ShowAlert(
strDialogTitle,
strDialogMessage,
wxYES_NO | wxICON_QUESTION,
false,
AlertProcessResponse
);
wxLogTrace(wxT("Function Start/End"), wxT("CBOINCBaseFrame::ShowConnectionFailedAlert - Function End"));
}
void CBOINCBaseFrame::ShowDaemonStartFailedAlert() {
CSkinAdvanced* pSkinAdvanced = wxGetApp().GetSkinManager()->GetAdvanced();
wxString strDialogTitle = wxEmptyString;
wxString strDialogMessage = wxEmptyString;
wxASSERT(pSkinAdvanced);
wxASSERT(wxDynamicCast(pSkinAdvanced, CSkinAdvanced));
wxLogTrace(wxT("Function Start/End"), wxT("CBOINCBaseFrame::ShowDaemonStartFailedAlert - Function Begin"));
// %s is the application name
// i.e. 'BOINC Manager', 'GridRepublic Manager'
strDialogTitle.Printf(
_("%s - Daemon Start Failed"),
pSkinAdvanced->GetApplicationName().c_str()
);
// 1st %s is the application name
// i.e. 'BOINC Manager', 'GridRepublic Manager'
// 2st %s is the project name
// i.e. 'BOINC', 'GridRepublic'
#ifdef __WXMSW__
strDialogMessage.Printf(
_("%s is not able to start a %s client.\n"
"Please launch the Control Panel->Administative Tools->Services "
"applet and start the BOINC service."),
pSkinAdvanced->GetApplicationName().c_str(),
pSkinAdvanced->GetApplicationShortName().c_str()
);
#else
strDialogMessage.Printf(
_("%s is not able to start a %s client.\n"
"Please start the daemon and try again."),
pSkinAdvanced->GetApplicationName().c_str(),
pSkinAdvanced->GetApplicationShortName().c_str()
);
#endif
ShowAlert(
strDialogTitle,
strDialogMessage,
wxOK | wxICON_ERROR
);
wxLogTrace(wxT("Function Start/End"), wxT("CBOINCBaseFrame::ShowDaemonStartFailedAlert - Function End"));
}
void CBOINCBaseFrame::ShowNotCurrentlyConnectedAlert() {
CSkinAdvanced* pSkinAdvanced = wxGetApp().GetSkinManager()->GetAdvanced();
wxString strDialogTitle = wxEmptyString;
wxString strDialogMessage = wxEmptyString;
wxASSERT(pSkinAdvanced);
wxASSERT(wxDynamicCast(pSkinAdvanced, CSkinAdvanced));
wxLogTrace(wxT("Function Start/End"), wxT("CBOINCBaseFrame::ShowNotCurrentlyConnectedAlert - Function Begin"));
// Did BOINC crash? If so restart it.
wxGetApp().GetDocument()->m_pClientManager->AutoRestart();
// %s is the application name
// i.e. 'BOINC Manager', 'GridRepublic Manager'
strDialogTitle.Printf(
_("%s - Connection Status"),
pSkinAdvanced->GetApplicationName().c_str()
);
// 1st %s is the application name
// i.e. 'BOINC Manager', 'GridRepublic Manager'
// 2nd %s is the project name
// i.e. 'BOINC', 'GridRepublic'
// 3nd %s is the project name
// i.e. 'BOINC', 'GridRepublic'
strDialogMessage.Printf(
_("%s is not currently connected to a %s client.\n"
"Please use the 'Advanced\\Select Computer...' menu option to connect up to a %s client.\n"
"To connect up to your local computer please use 'localhost' as the host name."),
pSkinAdvanced->GetApplicationName().c_str(),
pSkinAdvanced->GetApplicationShortName().c_str(),
pSkinAdvanced->GetApplicationShortName().c_str()
);
ShowAlert(
strDialogTitle,
strDialogMessage,
wxOK | wxICON_ERROR
);
wxLogTrace(wxT("Function Start/End"), wxT("CBOINCBaseFrame::ShowNotCurrentlyConnectedAlert - Function End"));
}
void CBOINCBaseFrame::StartTimers() {
wxASSERT(m_pAlertPollTimer);
m_pAlertPollTimer->Start();
}
void CBOINCBaseFrame::StopTimers() {
wxASSERT(m_pAlertPollTimer);
m_pAlertPollTimer->Stop();
}
void CBOINCBaseFrame::UpdateStatusText(const wxChar* szStatus) {
CFrameEvent event(wxEVT_FRAME_UPDATESTATUS, this, szStatus);
ProcessEvent(event);
}
void CBOINCBaseFrame::ShowAlert( const wxString title, const wxString message, const int style, const bool notification_only, const FrameAlertEventType alert_event_type ) {
CFrameAlertEvent event(wxEVT_FRAME_ALERT, this, title, message, style, notification_only, alert_event_type);
AddPendingEvent(event);
}
void CBOINCBaseFrame::ExecuteBrowserLink(const wxString &strLink) {
wxHyperLink::ExecuteLink(strLink);
}
bool CBOINCBaseFrame::SaveState() {
wxLogTrace(wxT("Function Start/End"), wxT("CBOINCBaseFrame::SaveState - Function Begin"));
wxString strBaseConfigLocation = wxString(wxT("/"));
wxConfigBase* pConfig = wxConfigBase::Get(FALSE);
wxString strConfigLocation;
wxString strPreviousLocation;
wxString strBuffer;
int iIndex;
int iItemCount;
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);
pConfig->Write(wxT("Language"), m_iSelectedLanguage);
pConfig->Write(wxT("ReminderFrequency"), m_iReminderFrequency);
pConfig->Write(wxT("DisplayExitWarning"), wxGetApp().GetDisplayExitWarning());
pConfig->Write(wxT("NetworkDialupConnectionName"), m_strNetworkDialupConnectionName);
//
// Save Computer MRU list
//
strPreviousLocation = pConfig->GetPath();
strConfigLocation = strPreviousLocation + wxT("ComputerMRU");
pConfig->SetPath(strConfigLocation);
iItemCount = (int)m_aSelectedComputerMRU.GetCount() - 1;
for (iIndex = 0; iIndex <= iItemCount; iIndex++) {
strBuffer.Printf(wxT("%d"), iIndex);
pConfig->Write(
strBuffer,
m_aSelectedComputerMRU.Item(iIndex)
);
}
pConfig->SetPath(strPreviousLocation);
wxLogTrace(wxT("Function Start/End"), wxT("CBOINCBaseFrame::SaveState - Function End"));
return true;
}
bool CBOINCBaseFrame::RestoreState() {
wxLogTrace(wxT("Function Start/End"), wxT("CBOINCBaseFrame::RestoreState - Function Begin"));
wxString strBaseConfigLocation = wxString(wxT("/"));
wxConfigBase* pConfig = wxConfigBase::Get(FALSE);
wxString strConfigLocation;
wxString strPreviousLocation;
wxString strBuffer;
wxString strValue;
long iIndex;
bool bKeepEnumerating = false;
int iDisplayExitWarning;
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);
pConfig->Read(wxT("Language"), &m_iSelectedLanguage, 0L);
pConfig->Read(wxT("ReminderFrequency"), &m_iReminderFrequency, 60L);
pConfig->Read(wxT("DisplayExitWarning"), &iDisplayExitWarning, 1L);
wxGetApp().SetDisplayExitWarning(iDisplayExitWarning);
pConfig->Read(wxT("NetworkDialupConnectionName"), &m_strNetworkDialupConnectionName, wxEmptyString);
//
// Restore Computer MRU list
//
strPreviousLocation = pConfig->GetPath();
strConfigLocation = strPreviousLocation + wxT("ComputerMRU");
pConfig->SetPath(strConfigLocation);
m_aSelectedComputerMRU.Clear();
bKeepEnumerating = pConfig->GetFirstEntry(strBuffer, iIndex);
while (bKeepEnumerating) {
pConfig->Read(strBuffer, &strValue);
m_aSelectedComputerMRU.Add(strValue);
bKeepEnumerating = pConfig->GetNextEntry(strBuffer, iIndex);
}
pConfig->SetPath(strPreviousLocation);
wxLogTrace(wxT("Function Start/End"), wxT("CBOINCBaseFrame::RestoreState - Function End"));
return true;
}
#ifdef __WXMAC__
bool CBOINCBaseFrame::Show(bool show) {
ProcessSerialNumber psn;
GetCurrentProcess(&psn);
if (show) {
SetFrontProcess(&psn); // Shows process if hidden
} else {
// GetWindowDimensions();
if (wxGetApp().GetCurrentGUISelection() == m_iWindowType)
if (IsProcessVisible(&psn))
ShowHideProcess(&psn, false);
}
return wxFrame::Show(show);
}
#endif // __WXMAC__
void CFrameAlertEvent::ProcessResponse(const int response) const {
CMainDocument* pDoc = wxGetApp().GetDocument();
wxASSERT(pDoc);
wxASSERT(wxDynamicCast(pDoc, CMainDocument));
if ((AlertProcessResponse == m_alert_event_type) && (wxYES == response)) {
pDoc->Reconnect();
}
}
const char *BOINC_RCSID_0a1bd38a5b = "$Id$";