diff --git a/clientgui/DlgExclusiveApps.cpp b/clientgui/DlgExclusiveApps.cpp new file mode 100644 index 0000000000..36b695ec2c --- /dev/null +++ b/clientgui/DlgExclusiveApps.cpp @@ -0,0 +1,471 @@ +// 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 . +// +#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; iAppend(appName); + } + + for (unsigned int i=0; iAppend(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; iGetStrings(); + config.exclusive_gpu_apps.clear(); + for (unsigned int i=0; irpc.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; jGetString(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); + } +} diff --git a/clientgui/DlgExclusiveApps.h b/clientgui/DlgExclusiveApps.h new file mode 100644 index 0000000000..ac74a57c34 --- /dev/null +++ b/clientgui/DlgExclusiveApps.h @@ -0,0 +1,73 @@ +// 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 . +// +#ifndef _DLGEXCLUSIVEAPPS_H_ +#define _DLGEXCLUSIVEAPPS_H_ + +#if defined(__GNUG__) && !defined(__APPLE__) +#pragma interface "DlgExclusiveApps.cpp" +#endif + + +#include "cc_config.h" + +#define ID_LISTBOX_EXCLAPPS 20061 +#define ID_ADDEXCLUSIVEAPPBUTTON 20062 +#define ID_REMOVEEXCLUSIVEAPPBUTTON 20063 +#define ID_LISTBOX_EXCLGPUAPPS 20064 +#define ID_ADDEXCLUSIVEGPUAPPBUTTON 20065 +#define ID_REMOVEEXCLUSIVEGPUAPPBUTTON 20066 + +class CDlgExclusiveApps : public wxDialog { + DECLARE_DYNAMIC_CLASS( CDlgExclusiveApps ) + DECLARE_EVENT_TABLE() + void ReadPreferenceSettings(); + bool SavePreferencesSettings(); +public: + CDlgExclusiveApps(wxWindow* parent=NULL); + virtual ~CDlgExclusiveApps(); + void OnExclusiveAppListEvent(wxCommandEvent& event); + void OnExclusiveGPUAppListEvent(wxCommandEvent& event); + void OnAddExclusiveApp(wxCommandEvent& event); + void OnAddExclusiveGPUApp(wxCommandEvent& event); + void AddToListBox(wxListBox * theListBox); + void OnRemoveExclusiveApp(wxCommandEvent& event); + void OnRemoveExclusiveGPUApp(wxCommandEvent& event); + void OnOK(wxCommandEvent& event); + void OnHelp(wxCommandEvent& event); +private: + LOG_FLAGS log_flags; + CC_CONFIG config; + bool m_bExclusiveAppsDataChanged; + bool m_bInInit; + + wxListBox* m_exclusiveApsListBox; + wxListBox* m_exclusiveGPUApsListBox; + wxButton* m_addExclusiveAppButton; + wxButton* m_removeExclusiveAppButton; + wxButton* m_addExclusiveGPUAppButton; + wxButton* m_removeExclusiveGPUAppButton; + + wxPanel* m_panelButtons; + wxButton* m_btnOK; + wxButton* m_btnCancel; + wxButton* m_btnHelp; + +}; + +#endif // _DLGEXCLUSIVEAPPS_H_ +