1 // This file is part of BOINC.
2 // http://boinc.berkeley.edu
3 // Copyright (C) 2014 University of California
4 //
5 // BOINC is free software; you can redistribute it and/or modify it
6 // under the terms of the GNU Lesser General Public License
7 // as published by the Free Software Foundation,
8 // either version 3 of the License, or (at your option) any later version.
9 //
10 // BOINC is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 // See the GNU Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public License
16 // along with BOINC. If not, see <http://www.gnu.org/licenses/>.
17 //
18 #if defined(__GNUG__) && !defined(__APPLE__)
19 #pragma implementation "DlgExclusiveApps.h"
20 #endif
21
22 #include "stdwx.h"
23 #include "BOINCGUIApp.h"
24 #include "MainDocument.h"
25 #include "BOINCBaseFrame.h"
26 #include "SkinManager.h"
27 #include "Events.h"
28 #include "error_numbers.h"
29 #include "version.h"
30 #include "DlgExclusiveApps.h"
31
32
33 using std::string;
34
IMPLEMENT_DYNAMIC_CLASS(CDlgExclusiveApps,wxDialog)35 IMPLEMENT_DYNAMIC_CLASS(CDlgExclusiveApps, wxDialog)
36
37 BEGIN_EVENT_TABLE(CDlgExclusiveApps, wxDialog)
38 EVT_COMMAND(ID_LISTBOX_EXCLAPPS,wxEVT_COMMAND_LISTBOX_SELECTED,CDlgExclusiveApps::OnExclusiveAppListEvent)
39 EVT_COMMAND(ID_LISTBOX_EXCLGPUAPPS,wxEVT_COMMAND_LISTBOX_SELECTED,CDlgExclusiveApps::OnExclusiveGPUAppListEvent)
40 //buttons
41 EVT_BUTTON(ID_ADDEXCLUSIVEAPPBUTTON,CDlgExclusiveApps::OnAddExclusiveApp)
42 EVT_BUTTON(ID_REMOVEEXCLUSIVEAPPBUTTON,CDlgExclusiveApps::OnRemoveExclusiveApp)
43 EVT_BUTTON(ID_ADDEXCLUSIVEGPUAPPBUTTON,CDlgExclusiveApps::OnAddExclusiveGPUApp)
44 EVT_BUTTON(ID_REMOVEEXCLUSIVEGPUAPPBUTTON,CDlgExclusiveApps::OnRemoveExclusiveGPUApp)
45 EVT_BUTTON(wxID_OK,CDlgExclusiveApps::OnOK)
46 EVT_BUTTON(ID_HELPBOINC,CDlgExclusiveApps::OnHelp)
47 END_EVENT_TABLE()
48
49 /* Constructor */
50 CDlgExclusiveApps::CDlgExclusiveApps(wxWindow* parent) :
51 wxDialog( parent, ID_ANYDIALOG, wxEmptyString, wxDefaultPosition,
52 wxDefaultSize, wxDEFAULT_DIALOG_STYLE
53 ) {
54 CSkinAdvanced* pSkinAdvanced = wxGetApp().GetSkinManager()->GetAdvanced();
55 wxASSERT(pSkinAdvanced);
56 wxASSERT(wxDynamicCast(pSkinAdvanced, CSkinAdvanced));
57
58 wxString title;
59 title.Printf(
60 _("%s - Exclusive Applications"),
61 pSkinAdvanced->GetApplicationShortName().c_str()
62 );
63
64 SetTitle(title);
65
66 m_bInInit=false;
67 m_bExclusiveAppsDataChanged=false;
68
69 wxBoxSizer* dialogSizer = new wxBoxSizer( wxVERTICAL );
70 dialogSizer->AddSpacer(10);
71
72 wxStaticBox* exclusiveAppsListStaticBox = new wxStaticBox( this, -1, _("Suspend processor and network usage when these applications are running:") );
73 wxStaticBoxSizer* exclusiveAppsListBoxSizer = new wxStaticBoxSizer( exclusiveAppsListStaticBox, wxVERTICAL );
74
75 m_exclusiveApsListBox = new wxListBox(exclusiveAppsListStaticBox, ID_LISTBOX_EXCLAPPS, wxDefaultPosition, wxSize(-1, 145), 0, NULL, wxLB_EXTENDED|wxLB_NEEDED_SB|wxLB_SORT);
76 exclusiveAppsListBoxSizer->Add(m_exclusiveApsListBox, 1, wxALL|wxEXPAND, 5);
77
78 wxBoxSizer* exclusiveAppsButtonSizer = new wxBoxSizer( wxHORIZONTAL );
79
80 m_addExclusiveAppButton = new wxButton( exclusiveAppsListStaticBox, ID_ADDEXCLUSIVEAPPBUTTON, _("Add..."), wxDefaultPosition, wxDefaultSize, 0 );
81 m_addExclusiveAppButton->SetToolTip( _("Add an application to this list"));
82 exclusiveAppsButtonSizer->Add( m_addExclusiveAppButton, 0, wxRIGHT, 5 );
83
84 exclusiveAppsButtonSizer->AddStretchSpacer();
85
86 m_removeExclusiveAppButton = new wxButton( exclusiveAppsListStaticBox, ID_REMOVEEXCLUSIVEAPPBUTTON, _("Remove"), wxDefaultPosition, wxDefaultSize, 0 );
87 m_removeExclusiveAppButton->SetToolTip( _("Remove an application from this list"));
88 exclusiveAppsButtonSizer->Add( m_removeExclusiveAppButton, 0, wxLEFT, 5 );
89
90 exclusiveAppsListBoxSizer->Add(exclusiveAppsButtonSizer, 0, wxEXPAND|wxLEFT|wxRIGHT, 25 );
91
92 dialogSizer->Add( exclusiveAppsListBoxSizer, 0, wxALL|wxEXPAND, 1 );
93
94 dialogSizer->AddSpacer(25);
95
96 wxStaticBox* exclusiveGPUAppsListStaticBox = new wxStaticBox( this, -1, _("Suspend GPU usage when these applications are running:") );
97 wxStaticBoxSizer* exclusiveGPUAppsListBoxSizer = new wxStaticBoxSizer( exclusiveGPUAppsListStaticBox, wxVERTICAL );
98
99 m_exclusiveGPUApsListBox = new wxListBox(exclusiveGPUAppsListStaticBox, ID_LISTBOX_EXCLGPUAPPS, wxDefaultPosition, wxSize(-1, 145), 0, NULL, wxLB_EXTENDED|wxLB_NEEDED_SB|wxLB_SORT);
100 exclusiveGPUAppsListBoxSizer->Add(m_exclusiveGPUApsListBox, 1, wxALL|wxEXPAND, 5);
101
102 wxBoxSizer* exclusiveGPUAppsButtonSizer = new wxBoxSizer( wxHORIZONTAL );
103
104 m_addExclusiveGPUAppButton = new wxButton( exclusiveGPUAppsListStaticBox, ID_ADDEXCLUSIVEGPUAPPBUTTON, _("Add..."), wxDefaultPosition, wxDefaultSize, 0 );
105 m_addExclusiveGPUAppButton->SetToolTip( _("Add an application to this list"));
106 exclusiveGPUAppsButtonSizer->Add( m_addExclusiveGPUAppButton, 0, wxRIGHT, 5 );
107
108 exclusiveGPUAppsButtonSizer->AddStretchSpacer();
109
110 m_removeExclusiveGPUAppButton = new wxButton( exclusiveGPUAppsListStaticBox, ID_REMOVEEXCLUSIVEGPUAPPBUTTON, _("Remove"), wxDefaultPosition, wxDefaultSize, 0 );
111 m_removeExclusiveGPUAppButton->SetToolTip( _("Remove an application from this list"));
112 exclusiveGPUAppsButtonSizer->Add( m_removeExclusiveGPUAppButton, 0, wxLEFT, 5 );
113
114 exclusiveGPUAppsListBoxSizer->Add(exclusiveGPUAppsButtonSizer, 0, wxEXPAND|wxLEFT|wxRIGHT, 25 );
115
116 dialogSizer->Add( exclusiveGPUAppsListBoxSizer, 0, wxALL|wxEXPAND, 1 );
117
118 wxBoxSizer* moreOptionsLinkSizer = new wxBoxSizer( wxHORIZONTAL );
119
120 moreOptionsLinkSizer->Add(
121 new wxStaticText(
122 this, wxID_ANY, _("For advanced options, refer to "),
123 wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT
124 ),
125 0, wxLEFT, 5
126 );
127
128 moreOptionsLinkSizer->Add(
129 new wxHyperlinkCtrl(
130 this, wxID_ANY, wxT("http://boinc.berkeley.edu/wiki/Client_configuration"),
131 wxT("http://boinc.berkeley.edu/wiki/Client_configuration"),
132 wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE
133 ),
134 #ifdef __WXMAC__
135 0, wxLEFT, 5
136 #else
137 0, wxLEFT, 3
138 #endif
139 );
140
141 dialogSizer->Add(moreOptionsLinkSizer, 0, wxALL, 10);
142
143 m_panelButtons = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 );
144 wxBoxSizer* buttonSizer = new wxBoxSizer( wxHORIZONTAL );
145
146 m_btnOK = new wxButton( m_panelButtons, wxID_OK, _("OK"), wxDefaultPosition, wxDefaultSize, 0 );
147 m_btnOK->SetToolTip( _("save all values and close the dialog") );
148 m_btnOK->SetDefault();
149
150 buttonSizer->Add( m_btnOK, 0, wxALL, 5 );
151
152 m_btnCancel = new wxButton( m_panelButtons, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxDefaultSize, 0 );
153 m_btnCancel->SetToolTip( _("close the dialog without saving") );
154
155 buttonSizer->Add( m_btnCancel, 0, wxALL, 5 );
156
157 m_btnHelp = new wxButton( m_panelButtons, ID_HELPBOINC, _("Help"), wxDefaultPosition, wxDefaultSize, 0 );
158 m_btnHelp->SetToolTip( _("shows the preferences web page") );
159
160 buttonSizer->Add( m_btnHelp, 0, wxALL, 5 );
161
162 m_panelButtons->SetSizer( buttonSizer );
163 m_panelButtons->Layout();
164 buttonSizer->Fit( m_panelButtons );
165 dialogSizer->Add( m_panelButtons, 0, wxALIGN_BOTTOM|wxALIGN_CENTER_HORIZONTAL|wxALL, 1 );
166
167 m_removeExclusiveAppButton->Disable();
168 m_removeExclusiveGPUAppButton->Disable();
169
170 ReadPreferenceSettings();
171
172 // CAF SetSizerAndFit(dialogSizer);
173 Layout();
174 dialogSizer->Fit( this );
175 SetSizer( dialogSizer );
176
177 // CAF Fit();
178 Centre();
179 }
180
181 /* destructor */
~CDlgExclusiveApps()182 CDlgExclusiveApps::~CDlgExclusiveApps() {
183 }
184
185 /* read preferences from core client and initialize control values */
ReadPreferenceSettings()186 void CDlgExclusiveApps::ReadPreferenceSettings() {
187 m_bInInit=true;//prevent dialog handlers from doing anything
188 CMainDocument* pDoc = wxGetApp().GetDocument();
189 int retval;
190
191 wxASSERT(pDoc);
192 wxASSERT(wxDynamicCast(pDoc, CMainDocument));
193
194 // Get cc_config.xml file flags
195 log_flags.init();
196 config.defaults();
197 retval = pDoc->rpc.get_cc_config(config, log_flags);
198 if (!retval) {
199 for (unsigned int i=0; i<config.exclusive_apps.size(); ++i) {
200 wxString appName = wxString(config.exclusive_apps[i].c_str(), wxConvUTF8);
201 m_exclusiveApsListBox->Append(appName);
202 }
203
204 for (unsigned int i=0; i<config.exclusive_gpu_apps.size(); ++i) {
205 wxString appName = wxString(config.exclusive_gpu_apps[i].c_str(), wxConvUTF8);
206 m_exclusiveGPUApsListBox->Append(appName);
207 }
208 }
209
210 m_bInInit=false;
211 }
212
213
SavePreferencesSettings()214 bool CDlgExclusiveApps::SavePreferencesSettings() {
215 if (m_bExclusiveAppsDataChanged) {
216 CMainDocument* pDoc = wxGetApp().GetDocument();
217
218 wxASSERT(pDoc);
219 wxASSERT(wxDynamicCast(pDoc, CMainDocument));
220
221 wxArrayString appNames = m_exclusiveApsListBox->GetStrings();
222
223 config.exclusive_apps.clear();
224 for (unsigned int i=0; i<appNames.size(); ++i) {
225 std::string s = (const char*)appNames[i].mb_str();
226 config.exclusive_apps.push_back(s);
227 }
228
229 wxArrayString gpuAppNames = m_exclusiveGPUApsListBox->GetStrings();
230 config.exclusive_gpu_apps.clear();
231 for (unsigned int i=0; i<gpuAppNames.size(); ++i) {
232 std::string s = (const char*)gpuAppNames[i].mb_str();
233 config.exclusive_gpu_apps.push_back(s);
234 }
235 int retval = pDoc->rpc.set_cc_config(config, log_flags);
236 if (!retval) {
237 pDoc->rpc.read_cc_config();
238 }
239 return true;
240 }
241 return false;
242 }
243
244 // ------------ Event handlers starts here
245 // ---- Exclusive Apps list box handler
OnExclusiveAppListEvent(wxCommandEvent & ev)246 void CDlgExclusiveApps::OnExclusiveAppListEvent(wxCommandEvent& ev) {
247 wxArrayInt selections;
248 int numSelected;
249
250 if(!m_bInInit) {
251 numSelected = m_exclusiveApsListBox->GetSelections(selections);
252 m_removeExclusiveAppButton->Enable(numSelected > 0);
253 }
254 ev.Skip();
255 }
256
OnExclusiveGPUAppListEvent(wxCommandEvent & ev)257 void CDlgExclusiveApps::OnExclusiveGPUAppListEvent(wxCommandEvent& ev) {
258 wxArrayInt selections;
259 int numSelected;
260
261 if(!m_bInInit) {
262 numSelected = m_exclusiveGPUApsListBox->GetSelections(selections);
263 m_removeExclusiveGPUAppButton->Enable(numSelected > 0);
264 }
265 ev.Skip();
266 }
267
268 // ---- command buttons handlers
269 // handles Add button clicked
OnAddExclusiveApp(wxCommandEvent &)270 void CDlgExclusiveApps::OnAddExclusiveApp(wxCommandEvent&) {
271 AddToListBox(m_exclusiveApsListBox);
272 }
273
OnAddExclusiveGPUApp(wxCommandEvent &)274 void CDlgExclusiveApps::OnAddExclusiveGPUApp(wxCommandEvent&) {
275 AddToListBox(m_exclusiveGPUApsListBox);
276 }
277
AddToListBox(wxListBox * theListBox)278 void CDlgExclusiveApps::AddToListBox(wxListBox * theListBox) {
279 wxString strMachineName;
280 int i, j, n;
281 bool hostIsMac = false;
282 bool hostIsWin = false;
283 bool isDuplicate;
284 wxArrayString appNames;
285 wxChar *extension = wxT("");
286 wxString errmsg;
287 CMainDocument* pDoc = wxGetApp().GetDocument();
288
289 wxASSERT(pDoc);
290 wxASSERT(wxDynamicCast(pDoc, CMainDocument));
291
292 if (strstr(pDoc->state.host_info.os_name, "Darwin")) {
293 hostIsMac = true;
294 extension = wxT(".app");
295 } else if (strstr(pDoc->state.host_info.os_name, "Microsoft")) {
296 hostIsWin = true;
297 extension = wxT(".exe");
298 }
299
300 pDoc->GetConnectedComputerName(strMachineName);
301 if (pDoc->IsComputerNameLocal(strMachineName)) {
302 #ifdef __WXMAC__
303 wxFileDialog picker(this, _("Applications to add"),
304 wxT("/Applications"), wxT(""), wxT("*.app"),
305 wxFD_OPEN|wxFD_FILE_MUST_EXIST|wxFD_MULTIPLE|wxFD_CHANGE_DIR
306 );
307 #elif defined(__WXMSW__)
308 //TODO: fill in the default directory for MSW
309 wxFileDialog picker(this, _("Applications to add"),
310 wxT("C:/Program Files"), wxT(""), wxT("*.exe"),
311 wxFD_OPEN|wxFD_FILE_MUST_EXIST|wxFD_MULTIPLE|wxFD_CHANGE_DIR
312 );
313 #else
314 //TODO: fill in the default directory for Linux
315 wxFileDialog picker(this, _("Applications to add"),
316 wxT("/usr/bin"), wxT(""), wxT("*"),
317 wxFD_OPEN|wxFD_FILE_MUST_EXIST|wxFD_MULTIPLE|wxFD_CHANGE_DIR
318 );
319 #endif
320 if (picker.ShowModal() != wxID_OK) return;
321 picker.GetFilenames(appNames);
322
323 for (i=appNames.Count()-1; i>=0; --i) {
324 #ifdef __WXMSW__
325 // Under Windows, filename may include paths if a shortcut selected
326 wxString appNameOnly = appNames[i].AfterLast('\\');
327 appNames[i] = appNameOnly;
328 #endif
329 wxString directory = picker.GetDirectory();
330 wxFileName fn(directory, appNames[i]);
331 if (!fn.IsOk() || !fn.IsFileExecutable()) {
332 errmsg.Printf(_("'%s' is not an executable application."), appNames[i].c_str());
333 wxGetApp().SafeMessageBox(errmsg, _("Add Exclusive App"),
334 wxOK | wxICON_EXCLAMATION, this
335 );
336 appNames.RemoveAt(i);
337 continue;
338 }
339 }
340 } else {
341 // We can't use file picker if connected to a remote computer,
342 // so show a dialog with textedit field so user can type app name
343 wxChar path_separator = wxT('/');
344
345 wxTextEntryDialog dlg(this, _("Name of application to add?"), _("Add exclusive app"));
346 if (hostIsMac) {
347 dlg.SetValue(extension);
348 } else if (hostIsWin) {
349 dlg.SetValue(extension);
350 path_separator = wxT('\\');
351 }
352 if (dlg.ShowModal() != wxID_OK) return;
353
354 wxString theAppName = dlg.GetValue();
355 // Strip off path if present
356 appNames.Add(theAppName.AfterLast(path_separator));
357 }
358
359 for (i=0; i<(int)appNames.Count(); ++i) {
360 // wxFileName::IsFileExecutable() doesn't seem to work on Windows,
361 // and we can only perform minimal validation on remote hosts, so
362 // check filename extension on Mac and Win
363 bool bad_name = false;
364 if (hostIsMac) {
365 bad_name = !appNames[i].EndsWith(extension);
366 } else if (hostIsWin) {
367 size_t len = appNames[i].Len();
368 size_t xl = 4;
369 if (len < xl) {
370 bad_name = true;
371 } else {
372 wxString x = appNames[i].Mid(len-xl);
373 if (x.CmpNoCase(extension) != 0) {
374 bad_name = true;
375 }
376 }
377 }
378 if (bad_name) {
379 errmsg.Printf(_("Application names must end with '%s'"), extension);
380 wxGetApp().SafeMessageBox(errmsg, _("Add Exclusive App"),
381 wxOK | wxICON_EXCLAMATION, this
382 );
383 return;
384 }
385
386 if (hostIsMac) {
387 int suffix = appNames[i].Find('.', true);
388 if (suffix != wxNOT_FOUND) {
389 appNames[i].Truncate(suffix);
390 }
391 }
392
393 // Skip requests for duplicate entries
394 isDuplicate = false;
395 n = theListBox->GetCount();
396 for (j=0; j<n; ++j) {
397 if ((theListBox->GetString(j)).Cmp(appNames[i]) == 0) {
398 isDuplicate = true;
399 break;
400 }
401 }
402 if (isDuplicate) {
403 errmsg.Printf(_("'%s' is already in the list."), appNames[i].c_str());
404 wxGetApp().SafeMessageBox(errmsg, _("Add Exclusive App"),
405 wxOK | wxICON_EXCLAMATION, this
406 );
407 continue;
408 }
409
410 theListBox->Append(appNames[i]);
411 m_bExclusiveAppsDataChanged = true;
412 }
413 }
414
415 typedef int (*sortcomparefunc)(int*, int*);
416
myCompareInts(int * first,int * second)417 static int myCompareInts(int *first, int *second) {
418 return *first - *second;
419 }
420 // handles Remove button clicked
OnRemoveExclusiveApp(wxCommandEvent & ev)421 void CDlgExclusiveApps::OnRemoveExclusiveApp(wxCommandEvent& ev) {
422 wxArrayInt selections;
423 int numSelected = m_exclusiveApsListBox->GetSelections(selections);
424
425 // The selection indices are returned in random order.
426 // We must sort them to ensure deleting the correct items.
427 selections.Sort((sortcomparefunc)&myCompareInts);
428 for (int i=numSelected-1; i>=0; --i) {
429 m_exclusiveApsListBox->Delete(selections[i]);
430 m_bExclusiveAppsDataChanged = true;
431 }
432 ev.Skip();
433 }
434
OnRemoveExclusiveGPUApp(wxCommandEvent & ev)435 void CDlgExclusiveApps::OnRemoveExclusiveGPUApp(wxCommandEvent& ev) {
436 wxArrayInt selections;
437 int numSelected = m_exclusiveGPUApsListBox->GetSelections(selections);
438
439 // The selection indices are returned in random order.
440 // We must sort them to ensure deleting the correct items.
441 selections.Sort((sortcomparefunc)&myCompareInts);
442 for (int i=numSelected-1; i>=0; --i) {
443 m_exclusiveGPUApsListBox->Delete(selections[i]);
444 m_bExclusiveAppsDataChanged = true;
445 }
446 ev.Skip();
447 }
448
449 // handles OK button clicked
OnOK(wxCommandEvent & ev)450 void CDlgExclusiveApps::OnOK(wxCommandEvent& ev) {
451 SavePreferencesSettings();
452
453 ev.Skip();
454 }
455
456 // handles Help button clicked
OnHelp(wxCommandEvent & ev)457 void CDlgExclusiveApps::OnHelp(wxCommandEvent& ev) {
458 if (IsShown()) {
459
460 wxString strURL = wxGetApp().GetSkinManager()->GetAdvanced()->GetOrganizationHelpUrl();
461
462 wxString wxurl;
463 wxurl.Printf(
464 wxT("%s?target=exclusive_apps&version=%s&controlid=%d"),
465 strURL.c_str(),
466 wxString(BOINC_VERSION_STRING, wxConvUTF8).c_str(),
467 ev.GetId()
468 );
469 wxLaunchDefaultBrowser(wxurl);
470 }
471 }
472