boinc/clientgui/DlgExclusiveApps.cpp

472 lines
17 KiB
C++

// This file is part of BOINC.
// http://boinc.berkeley.edu
// Copyright (C) 2014 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 <http://www.gnu.org/licenses/>.
//
#if defined(__GNUG__) && !defined(__APPLE__)
#pragma implementation "DlgExclusiveApps.h"
#endif
#include "stdwx.h"
#include "BOINCGUIApp.h"
#include "MainDocument.h"
#include "BOINCBaseFrame.h"
#include "SkinManager.h"
#include "Events.h"
#include "error_numbers.h"
#include "version.h"
#include "DlgExclusiveApps.h"
using std::string;
IMPLEMENT_DYNAMIC_CLASS(CDlgExclusiveApps, wxDialog)
BEGIN_EVENT_TABLE(CDlgExclusiveApps, wxDialog)
EVT_COMMAND(ID_LISTBOX_EXCLAPPS,wxEVT_COMMAND_LISTBOX_SELECTED,CDlgExclusiveApps::OnExclusiveAppListEvent)
EVT_COMMAND(ID_LISTBOX_EXCLGPUAPPS,wxEVT_COMMAND_LISTBOX_SELECTED,CDlgExclusiveApps::OnExclusiveGPUAppListEvent)
//buttons
EVT_BUTTON(ID_ADDEXCLUSIVEAPPBUTTON,CDlgExclusiveApps::OnAddExclusiveApp)
EVT_BUTTON(ID_REMOVEEXCLUSIVEAPPBUTTON,CDlgExclusiveApps::OnRemoveExclusiveApp)
EVT_BUTTON(ID_ADDEXCLUSIVEGPUAPPBUTTON,CDlgExclusiveApps::OnAddExclusiveGPUApp)
EVT_BUTTON(ID_REMOVEEXCLUSIVEGPUAPPBUTTON,CDlgExclusiveApps::OnRemoveExclusiveGPUApp)
EVT_BUTTON(wxID_OK,CDlgExclusiveApps::OnOK)
EVT_BUTTON(ID_HELPBOINC,CDlgExclusiveApps::OnHelp)
END_EVENT_TABLE()
/* Constructor */
CDlgExclusiveApps::CDlgExclusiveApps(wxWindow* parent) :
wxDialog( parent, ID_ANYDIALOG, wxEmptyString, wxDefaultPosition,
wxDefaultSize, wxDEFAULT_DIALOG_STYLE
) {
CSkinAdvanced* pSkinAdvanced = wxGetApp().GetSkinManager()->GetAdvanced();
wxASSERT(pSkinAdvanced);
wxASSERT(wxDynamicCast(pSkinAdvanced, CSkinAdvanced));
wxString title;
title.Printf(
_("%s - Exclusive Applications"),
pSkinAdvanced->GetApplicationShortName().c_str()
);
SetTitle(title);
m_bInInit=false;
m_bExclusiveAppsDataChanged=false;
wxBoxSizer* dialogSizer = new wxBoxSizer( wxVERTICAL );
dialogSizer->AddSpacer(10);
wxStaticBox* exclusiveAppsListStaticBox = new wxStaticBox( this, -1, _("Suspend processor and network usage when these applications are running:") );
wxStaticBoxSizer* exclusiveAppsListBoxSizer = new wxStaticBoxSizer( exclusiveAppsListStaticBox, wxVERTICAL );
m_exclusiveApsListBox = new wxListBox(exclusiveAppsListStaticBox, ID_LISTBOX_EXCLAPPS, wxDefaultPosition, wxSize(-1, 145), 0, NULL, wxLB_EXTENDED|wxLB_NEEDED_SB|wxLB_SORT);
exclusiveAppsListBoxSizer->Add(m_exclusiveApsListBox, 1, wxALL|wxEXPAND, 5);
wxBoxSizer* exclusiveAppsButtonSizer = new wxBoxSizer( wxHORIZONTAL );
m_addExclusiveAppButton = new wxButton( exclusiveAppsListStaticBox, ID_ADDEXCLUSIVEAPPBUTTON, _("Add..."), wxDefaultPosition, wxDefaultSize, 0 );
m_addExclusiveAppButton->SetToolTip( _("Add an application to this list"));
exclusiveAppsButtonSizer->Add( m_addExclusiveAppButton, 0, wxRIGHT, 5 );
exclusiveAppsButtonSizer->AddStretchSpacer();
m_removeExclusiveAppButton = new wxButton( exclusiveAppsListStaticBox, ID_REMOVEEXCLUSIVEAPPBUTTON, _("Remove"), wxDefaultPosition, wxDefaultSize, 0 );
m_removeExclusiveAppButton->SetToolTip( _("Remove an application from this list"));
exclusiveAppsButtonSizer->Add( m_removeExclusiveAppButton, 0, wxLEFT, 5 );
exclusiveAppsListBoxSizer->Add(exclusiveAppsButtonSizer, 0, wxEXPAND|wxLEFT|wxRIGHT, 25 );
dialogSizer->Add( exclusiveAppsListBoxSizer, 0, wxALL|wxEXPAND, 1 );
dialogSizer->AddSpacer(25);
wxStaticBox* exclusiveGPUAppsListStaticBox = new wxStaticBox( this, -1, _("Suspend GPU usage when these applications are running:") );
wxStaticBoxSizer* exclusiveGPUAppsListBoxSizer = new wxStaticBoxSizer( exclusiveGPUAppsListStaticBox, wxVERTICAL );
m_exclusiveGPUApsListBox = new wxListBox(exclusiveGPUAppsListStaticBox, ID_LISTBOX_EXCLGPUAPPS, wxDefaultPosition, wxSize(-1, 145), 0, NULL, wxLB_EXTENDED|wxLB_NEEDED_SB|wxLB_SORT);
exclusiveGPUAppsListBoxSizer->Add(m_exclusiveGPUApsListBox, 1, wxALL|wxEXPAND, 5);
wxBoxSizer* exclusiveGPUAppsButtonSizer = new wxBoxSizer( wxHORIZONTAL );
m_addExclusiveGPUAppButton = new wxButton( exclusiveGPUAppsListStaticBox, ID_ADDEXCLUSIVEGPUAPPBUTTON, _("Add..."), wxDefaultPosition, wxDefaultSize, 0 );
m_addExclusiveGPUAppButton->SetToolTip( _("Add an application to this list"));
exclusiveGPUAppsButtonSizer->Add( m_addExclusiveGPUAppButton, 0, wxRIGHT, 5 );
exclusiveGPUAppsButtonSizer->AddStretchSpacer();
m_removeExclusiveGPUAppButton = new wxButton( exclusiveGPUAppsListStaticBox, ID_REMOVEEXCLUSIVEGPUAPPBUTTON, _("Remove"), wxDefaultPosition, wxDefaultSize, 0 );
m_removeExclusiveGPUAppButton->SetToolTip( _("Remove an application from this list"));
exclusiveGPUAppsButtonSizer->Add( m_removeExclusiveGPUAppButton, 0, wxLEFT, 5 );
exclusiveGPUAppsListBoxSizer->Add(exclusiveGPUAppsButtonSizer, 0, wxEXPAND|wxLEFT|wxRIGHT, 25 );
dialogSizer->Add( exclusiveGPUAppsListBoxSizer, 0, wxALL|wxEXPAND, 1 );
wxBoxSizer* moreOptionsLinkSizer = new wxBoxSizer( wxHORIZONTAL );
moreOptionsLinkSizer->Add(
new wxStaticText(
this, wxID_ANY, _("For advanced options, refer to "),
wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT
),
0, wxLEFT, 5
);
moreOptionsLinkSizer->Add(
new wxHyperlinkCtrl(
this, wxID_ANY, wxT("http://boinc.berkeley.edu/wiki/Client_configuration"),
wxT("http://boinc.berkeley.edu/wiki/Client_configuration"),
wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE
),
#ifdef __WXMAC__
0, wxLEFT, 5
#else
0, wxLEFT, 3
#endif
);
dialogSizer->Add(moreOptionsLinkSizer, 0, wxALL, 10);
m_panelButtons = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 );
wxBoxSizer* buttonSizer = new wxBoxSizer( wxHORIZONTAL );
m_btnOK = new wxButton( m_panelButtons, wxID_OK, _("OK"), wxDefaultPosition, wxDefaultSize, 0 );
m_btnOK->SetToolTip( _("save all values and close the dialog") );
m_btnOK->SetDefault();
buttonSizer->Add( m_btnOK, 0, wxALL, 5 );
m_btnCancel = new wxButton( m_panelButtons, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxDefaultSize, 0 );
m_btnCancel->SetToolTip( _("close the dialog without saving") );
buttonSizer->Add( m_btnCancel, 0, wxALL, 5 );
m_btnHelp = new wxButton( m_panelButtons, ID_HELPBOINC, _("Help"), wxDefaultPosition, wxDefaultSize, 0 );
m_btnHelp->SetToolTip( _("shows the preferences web page") );
buttonSizer->Add( m_btnHelp, 0, wxALL, 5 );
m_panelButtons->SetSizer( buttonSizer );
m_panelButtons->Layout();
buttonSizer->Fit( m_panelButtons );
dialogSizer->Add( m_panelButtons, 0, wxALIGN_BOTTOM|wxALIGN_CENTER_HORIZONTAL|wxALL, 1 );
m_removeExclusiveAppButton->Disable();
m_removeExclusiveGPUAppButton->Disable();
ReadPreferenceSettings();
// CAF SetSizerAndFit(dialogSizer);
Layout();
dialogSizer->Fit( this );
SetSizer( dialogSizer );
// CAF Fit();
Centre();
}
/* destructor */
CDlgExclusiveApps::~CDlgExclusiveApps() {
}
/* read preferences from core client and initialize control values */
void CDlgExclusiveApps::ReadPreferenceSettings() {
m_bInInit=true;//prevent dialog handlers from doing anything
CMainDocument* pDoc = wxGetApp().GetDocument();
int retval;
wxASSERT(pDoc);
wxASSERT(wxDynamicCast(pDoc, CMainDocument));
// Get cc_config.xml file flags
log_flags.init();
config.defaults();
retval = pDoc->rpc.get_cc_config(config, log_flags);
if (!retval) {
for (unsigned int i=0; i<config.exclusive_apps.size(); ++i) {
wxString appName = wxString(config.exclusive_apps[i].c_str(), wxConvUTF8);
m_exclusiveApsListBox->Append(appName);
}
for (unsigned int i=0; i<config.exclusive_gpu_apps.size(); ++i) {
wxString appName = wxString(config.exclusive_gpu_apps[i].c_str(), wxConvUTF8);
m_exclusiveGPUApsListBox->Append(appName);
}
}
m_bInInit=false;
}
bool CDlgExclusiveApps::SavePreferencesSettings() {
if (m_bExclusiveAppsDataChanged) {
CMainDocument* pDoc = wxGetApp().GetDocument();
wxASSERT(pDoc);
wxASSERT(wxDynamicCast(pDoc, CMainDocument));
wxArrayString appNames = m_exclusiveApsListBox->GetStrings();
config.exclusive_apps.clear();
for (unsigned int i=0; i<appNames.size(); ++i) {
std::string s = (const char*)appNames[i].mb_str();
config.exclusive_apps.push_back(s);
}
wxArrayString gpuAppNames = m_exclusiveGPUApsListBox->GetStrings();
config.exclusive_gpu_apps.clear();
for (unsigned int i=0; i<gpuAppNames.size(); ++i) {
std::string s = (const char*)gpuAppNames[i].mb_str();
config.exclusive_gpu_apps.push_back(s);
}
int retval = pDoc->rpc.set_cc_config(config, log_flags);
if (!retval) {
pDoc->rpc.read_cc_config();
}
return true;
}
return false;
}
// ------------ Event handlers starts here
// ---- Exclusive Apps list box handler
void CDlgExclusiveApps::OnExclusiveAppListEvent(wxCommandEvent& ev) {
wxArrayInt selections;
int numSelected;
if(!m_bInInit) {
numSelected = m_exclusiveApsListBox->GetSelections(selections);
m_removeExclusiveAppButton->Enable(numSelected > 0);
}
ev.Skip();
}
void CDlgExclusiveApps::OnExclusiveGPUAppListEvent(wxCommandEvent& ev) {
wxArrayInt selections;
int numSelected;
if(!m_bInInit) {
numSelected = m_exclusiveGPUApsListBox->GetSelections(selections);
m_removeExclusiveGPUAppButton->Enable(numSelected > 0);
}
ev.Skip();
}
// ---- command buttons handlers
// handles Add button clicked
void CDlgExclusiveApps::OnAddExclusiveApp(wxCommandEvent&) {
AddToListBox(m_exclusiveApsListBox);
}
void CDlgExclusiveApps::OnAddExclusiveGPUApp(wxCommandEvent&) {
AddToListBox(m_exclusiveGPUApsListBox);
}
void CDlgExclusiveApps::AddToListBox(wxListBox * theListBox) {
wxString strMachineName;
int i, j, n;
bool hostIsMac = false;
bool hostIsWin = false;
bool isDuplicate;
wxArrayString appNames;
wxChar *extension = wxT("");
wxString errmsg;
CMainDocument* pDoc = wxGetApp().GetDocument();
wxASSERT(pDoc);
wxASSERT(wxDynamicCast(pDoc, CMainDocument));
if (strstr(pDoc->state.host_info.os_name, "Darwin")) {
hostIsMac = true;
extension = wxT(".app");
} else if (strstr(pDoc->state.host_info.os_name, "Microsoft")) {
hostIsWin = true;
extension = wxT(".exe");
}
pDoc->GetConnectedComputerName(strMachineName);
if (pDoc->IsComputerNameLocal(strMachineName)) {
#ifdef __WXMAC__
wxFileDialog picker(this, _("Applications to add"),
wxT("/Applications"), wxT(""), wxT("*.app"),
wxFD_OPEN|wxFD_FILE_MUST_EXIST|wxFD_MULTIPLE|wxFD_CHANGE_DIR
);
#elif defined(__WXMSW__)
//TODO: fill in the default directory for MSW
wxFileDialog picker(this, _("Applications to add"),
wxT("C:/Program Files"), wxT(""), wxT("*.exe"),
wxFD_OPEN|wxFD_FILE_MUST_EXIST|wxFD_MULTIPLE|wxFD_CHANGE_DIR
);
#else
//TODO: fill in the default directory for Linux
wxFileDialog picker(this, _("Applications to add"),
wxT("/usr/bin"), wxT(""), wxT("*"),
wxFD_OPEN|wxFD_FILE_MUST_EXIST|wxFD_MULTIPLE|wxFD_CHANGE_DIR
);
#endif
if (picker.ShowModal() != wxID_OK) return;
picker.GetFilenames(appNames);
for (i=appNames.Count()-1; i>=0; --i) {
#ifdef __WXMSW__
// Under Windows, filename may include paths if a shortcut selected
wxString appNameOnly = appNames[i].AfterLast('\\');
appNames[i] = appNameOnly;
#endif
wxString directory = picker.GetDirectory();
wxFileName fn(directory, appNames[i]);
if (!fn.IsOk() || !fn.IsFileExecutable()) {
errmsg.Printf(_("'%s' is not an executable application."), appNames[i].c_str());
wxGetApp().SafeMessageBox(errmsg, _("Add Exclusive App"),
wxOK | wxICON_EXCLAMATION, this
);
appNames.RemoveAt(i);
continue;
}
}
} else {
// We can't use file picker if connected to a remote computer,
// so show a dialog with textedit field so user can type app name
wxChar path_separator = wxT('/');
wxTextEntryDialog dlg(this, _("Name of application to add?"), _("Add exclusive app"));
if (hostIsMac) {
dlg.SetValue(extension);
} else if (hostIsWin) {
dlg.SetValue(extension);
path_separator = wxT('\\');
}
if (dlg.ShowModal() != wxID_OK) return;
wxString theAppName = dlg.GetValue();
// Strip off path if present
appNames.Add(theAppName.AfterLast(path_separator));
}
for (i=0; i<(int)appNames.Count(); ++i) {
// wxFileName::IsFileExecutable() doesn't seem to work on Windows,
// and we can only perform minimal validation on remote hosts, so
// check filename extension on Mac and Win
bool bad_name = false;
if (hostIsMac) {
bad_name = !appNames[i].EndsWith(extension);
} else if (hostIsWin) {
size_t len = appNames[i].Len();
size_t xl = 4;
if (len < xl) {
bad_name = true;
} else {
wxString x = appNames[i].Mid(len-xl);
if (x.CmpNoCase(extension) != 0) {
bad_name = true;
}
}
}
if (bad_name) {
errmsg.Printf(_("Application names must end with '%s'"), extension);
wxGetApp().SafeMessageBox(errmsg, _("Add Exclusive App"),
wxOK | wxICON_EXCLAMATION, this
);
return;
}
if (hostIsMac) {
int suffix = appNames[i].Find('.', true);
if (suffix != wxNOT_FOUND) {
appNames[i].Truncate(suffix);
}
}
// Skip requests for duplicate entries
isDuplicate = false;
n = theListBox->GetCount();
for (j=0; j<n; ++j) {
if ((theListBox->GetString(j)).Cmp(appNames[i]) == 0) {
isDuplicate = true;
break;
}
}
if (isDuplicate) {
errmsg.Printf(_("'%s' is already in the list."), appNames[i].c_str());
wxGetApp().SafeMessageBox(errmsg, _("Add Exclusive App"),
wxOK | wxICON_EXCLAMATION, this
);
continue;
}
theListBox->Append(appNames[i]);
m_bExclusiveAppsDataChanged = true;
}
}
typedef int (*sortcomparefunc)(int*, int*);
static int myCompareInts(int *first, int *second) {
return *first - *second;
}
// handles Remove button clicked
void CDlgExclusiveApps::OnRemoveExclusiveApp(wxCommandEvent& ev) {
wxArrayInt selections;
int numSelected = m_exclusiveApsListBox->GetSelections(selections);
// The selection indices are returned in random order.
// We must sort them to ensure deleting the correct items.
selections.Sort((sortcomparefunc)&myCompareInts);
for (int i=numSelected-1; i>=0; --i) {
m_exclusiveApsListBox->Delete(selections[i]);
m_bExclusiveAppsDataChanged = true;
}
ev.Skip();
}
void CDlgExclusiveApps::OnRemoveExclusiveGPUApp(wxCommandEvent& ev) {
wxArrayInt selections;
int numSelected = m_exclusiveGPUApsListBox->GetSelections(selections);
// The selection indices are returned in random order.
// We must sort them to ensure deleting the correct items.
selections.Sort((sortcomparefunc)&myCompareInts);
for (int i=numSelected-1; i>=0; --i) {
m_exclusiveGPUApsListBox->Delete(selections[i]);
m_bExclusiveAppsDataChanged = true;
}
ev.Skip();
}
// handles OK button clicked
void CDlgExclusiveApps::OnOK(wxCommandEvent& ev) {
SavePreferencesSettings();
ev.Skip();
}
// handles Help button clicked
void CDlgExclusiveApps::OnHelp(wxCommandEvent& ev) {
if (IsShown()) {
wxString strURL = wxGetApp().GetSkinManager()->GetAdvanced()->GetOrganizationHelpUrl();
wxString wxurl;
wxurl.Printf(
wxT("%s?target=exclusive_apps&version=%s&controlid=%d"),
strURL.c_str(),
wxString(BOINC_VERSION_STRING, wxConvUTF8).c_str(),
ev.GetId()
);
wxLaunchDefaultBrowser(wxurl);
}
}