boinc/clientgui/sg_DlgPreferences.cpp

1191 lines
40 KiB
C++

// 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 <http://www.gnu.org/licenses/>.
#if defined(__GNUG__) && !defined(__APPLE__)
#pragma implementation "sg_DlgPreferences.h"
#endif
#include "stdwx.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 "sg_CustomControls.h"
#include "sg_DlgPreferences.h"
#include "res/warning.xpm"
#define TEST_BACKGROUND_WITH_MAGENTA_FILL 0
using std::string;
/*!
* CPanelPreferences type definition
*/
IMPLEMENT_DYNAMIC_CLASS( CPanelPreferences, wxPanel )
/*!
* CPanelPreferences event table definition
*/
BEGIN_EVENT_TABLE( CPanelPreferences, wxPanel )
////@begin CPanelPreferences event table entries
EVT_COMMAND_RANGE(ID_SG_PREFS_START,ID_SG_PREFS_LAST,
wxEVT_COMMAND_CHECKBOX_CLICKED,
CPanelPreferences::OnHandleCheckboxEvent
)
EVT_ERASE_BACKGROUND( CPanelPreferences::OnEraseBackground )
EVT_BUTTON( ID_SIMPLE_HELP, CPanelPreferences::OnButtonHelp )
////@end CPanelPreferences event table entries
END_EVENT_TABLE()
/*!
* CPanelPreferences constructors
*/
CPanelPreferences::CPanelPreferences( )
{
}
CPanelPreferences::CPanelPreferences( wxWindow* parent ) :
wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNO_BORDER)
{
Create();
}
CPanelPreferences::~CPanelPreferences( )
{
if (m_backgroundBitmap) {
delete m_backgroundBitmap;
m_backgroundBitmap = NULL;
}
}
/*!
* CPanelPreferences creator
*/
bool CPanelPreferences::Create()
{
m_backgroundBitmap = NULL;
lastErrorCtrl = NULL;
stdTextBkgdColor = *wxWHITE;
CreateControls();
defaultPrefs.enabled_defaults();
ReadPreferenceSettings();
GetSizer()->Fit(this);
GetSizer()->SetSizeHints(this);
return true;
}
/*!
* Control creation for CPanelPreferences
*/
void CPanelPreferences::CreateControls()
{
CSkinSimple* pSkinSimple = wxGetApp().GetSkinManager()->GetSimple();
CSkinAdvanced* pSkinAdvanced = wxGetApp().GetSkinManager()->GetAdvanced();
wxASSERT(pSkinSimple);
wxASSERT(wxDynamicCast(pSkinSimple, CSkinSimple));
wxASSERT(pSkinAdvanced);
wxASSERT(wxDynamicCast(pSkinAdvanced, CSkinAdvanced));
wxSize textCtrlSize = getTextCtrlSize(wxT("999.99"));
wxSize timeCtrlSize = getTextCtrlSize(wxT("23:59 "));
CPanelPreferences* itemDialog1 = this;
wxBoxSizer* itemBoxSizer2 = new wxBoxSizer(wxVERTICAL);
itemDialog1->SetSizer(itemBoxSizer2);
bool usingLocalPrefs = doesLocalPrefsFileExist();
if (! web_prefs_url->IsEmpty()) {
wxStaticBox* topSectionStaticBox = new wxStaticBox();
topSectionStaticBox->SetBackgroundStyle(wxBG_STYLE_TRANSPARENT);
topSectionStaticBox->Create(this, -1, wxEmptyString);
wxStaticBoxSizer* topSectionSizer = new wxStaticBoxSizer( topSectionStaticBox, wxVERTICAL );
wxBoxSizer* topControlsSizer = new wxBoxSizer( wxHORIZONTAL );
topSectionSizer->Add(topControlsSizer);
wxBitmap warningBmp = GetScaledBitmapFromXPMData(warning_xpm);
CTransparentStaticBitmap* bmpWarning = new CTransparentStaticBitmap(
topSectionStaticBox, wxID_ANY,
warningBmp,
wxDefaultPosition, wxDefaultSize, 0
);
bmpWarning->SetMinSize( warningBmp.GetSize() );
topControlsSizer->Add( bmpWarning, 0, wxALIGN_CENTER_VERTICAL|wxALL, 0 );
wxBoxSizer* legendSizer = new wxBoxSizer( wxVERTICAL );
if (usingLocalPrefs) {
legendSizer->Add(
new CTransparentStaticText( topSectionStaticBox, wxID_ANY,
_("Using local preferences.\n"
"Click \"Use web prefs\" to use web-based preferences from"
), wxDefaultPosition, wxDefaultSize, 0 ),
0, wxALL, 1
);
} else {
legendSizer->Add(
new CTransparentStaticText( topSectionStaticBox, wxID_ANY,
_("Using web-based preferences from"),
wxDefaultPosition, wxDefaultSize, 0 ),
0, wxALL, 1
);
}
legendSizer->Add(
new CTransparentHyperlinkCtrl(
topSectionStaticBox, wxID_ANY, *web_prefs_url, *web_prefs_url,
wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE,
wxHyperlinkCtrlNameStr, &m_backgroundBitmap
),
0, wxLEFT, 5
);
if (!usingLocalPrefs) {
legendSizer->Add(
new CTransparentStaticText( topSectionStaticBox, wxID_ANY,
_("Set values and click OK to use local preferences instead."),
wxDefaultPosition, wxDefaultSize, 0 ),
0, wxALL, 1
);
}
#if 1
topSectionSizer->AddSpacer( 10 );
CTransparentStaticLine* itemStaticLine8 = new CTransparentStaticLine( topSectionStaticBox, wxID_ANY,
wxDefaultPosition,
wxSize(ADJUSTFORXDPI(300), 1),
wxLI_HORIZONTAL|wxNO_BORDER
);
itemStaticLine8->SetLineColor(pSkinSimple->GetStaticLineColor());
topSectionSizer->Add(itemStaticLine8, 0, wxALIGN_CENTER_HORIZONTAL|wxLEFT|wxRIGHT, ADJUSTFORXDPI(20));
topSectionSizer->AddSpacer( 10 );
CTransparentStaticText* itemStaticText7 = new CTransparentStaticText( topSectionStaticBox, wxID_ANY, _("For additional settings, select Computing Preferences in the Advanced View."), wxDefaultPosition, wxDefaultSize, 0 );
topSectionSizer->Add(itemStaticText7, 0, wxALL, 0);
topSectionSizer->AddSpacer( 10 );
#endif
topControlsSizer->Add( legendSizer, 1, wxALL, 1 );
m_btnClear = new wxButton( topSectionStaticBox, ID_SGPREFERENCESCLEAR, _("Use web prefs"), wxDefaultPosition, wxDefaultSize, 0 );
m_btnClear->SetToolTip( _("Restore web-based preferences and close the dialog.") );
if (!usingLocalPrefs) {
m_btnClear->Hide();
}
topControlsSizer->Add( m_btnClear, 0, wxALIGN_BOTTOM|wxALL, 4 );
#ifdef __WXMAC__
itemBoxSizer2->Add( topSectionSizer, 0, wxTOP|wxLEFT|wxRIGHT|wxEXPAND, 10 );
#else
itemBoxSizer2->Add( topSectionSizer, 0, wxALL|wxEXPAND, 5 );
#endif
}
wxBoxSizer* itemBoxSizer11 = new wxBoxSizer(wxVERTICAL);
itemBoxSizer2->Add(itemBoxSizer11, 0, wxLEFT, ADJUSTFORXDPI(20));
wxString ProcOnBatteriesTT(_("Check this to suspend computing on portables when running on battery power."));
m_chkProcOnBatteries = new CTransparentCheckBox(
itemDialog1, ID_CHKPROCONBATTERIES,
_("Suspend when computer is on battery"),
wxDefaultPosition, wxDefaultSize, 0,
wxDefaultValidator, wxCheckBoxNameStr,
&m_backgroundBitmap
);
m_chkProcOnBatteries->SetToolTip(ProcOnBatteriesTT);
itemBoxSizer11->Add(m_chkProcOnBatteries, 0, wxALL, 5 );
wxString ProcInUseTT(_("Check this to suspend computing and file transfers when you're using the computer."));
m_chkProcInUse = new CTransparentCheckBox(
itemDialog1, ID_CHKPROCINUSE,
_("Suspend when computer is in use"),
wxDefaultPosition, wxDefaultSize, 0,
wxDefaultValidator, wxCheckBoxNameStr,
&m_backgroundBitmap
);
m_chkProcInUse->SetToolTip(ProcInUseTT);
itemBoxSizer11->Add(m_chkProcInUse, 0, wxALL, 5 );
// min idle time
wxString ProcIdleForTT(_("This determines when the computer is considered 'in use'."));
CTransparentStaticText* staticText24 = new CTransparentStaticText(
itemDialog1, wxID_ANY,
_("'In use' means mouse/keyboard input in last"),
wxDefaultPosition, wxDefaultSize, 0
);
m_txtProcIdleFor = new wxTextCtrl(
itemDialog1, ID_TXTPROCIDLEFOR, wxEmptyString, wxDefaultPosition, textCtrlSize, wxTE_RIGHT
);
CTransparentStaticText* staticText25 = new CTransparentStaticText(itemDialog1, wxID_ANY, _("minutes"),
wxDefaultPosition, wxDefaultSize, 0 );
addNewRowToSizer(itemBoxSizer11, ProcIdleForTT, staticText24, m_txtProcIdleFor, staticText25);
/*xgettext:no-c-format*/
wxString MaxCPUTimeTT(_("Suspend/resume computing every few seconds to reduce CPU temperature and energy usage. Example: 75% means compute for 3 seconds, wait for 1 second, and repeat."));
CTransparentStaticText* staticText22 = new CTransparentStaticText(
itemDialog1, wxID_ANY, _("Use at most"), wxDefaultPosition, wxDefaultSize, 0 );
m_txtProcUseCPUTime = new wxTextCtrl( itemDialog1, ID_TXTPOCUSECPUTIME, wxEmptyString, wxDefaultPosition, textCtrlSize, wxTE_RIGHT );
/*xgettext:no-c-format*/
CTransparentStaticText* staticText23 = new CTransparentStaticText( itemDialog1, wxID_ANY, _("% of CPU time"), wxDefaultPosition, wxDefaultSize, 0 );
addNewRowToSizer(itemBoxSizer11, MaxCPUTimeTT, staticText22, m_txtProcUseCPUTime, staticText23);
wxString andString(_("and"));
wxString ProcEveryDayTT(_("Compute only during a particular period each day."));
m_chkProcEveryDay = new CTransparentCheckBox(
itemDialog1, ID_CHKPROCEVERYDAY,
_("Compute only between"),
wxDefaultPosition, wxDefaultSize, 0,
wxDefaultValidator, wxCheckBoxNameStr,
&m_backgroundBitmap
);
m_txtProcEveryDayStart = new wxTextCtrl(
itemDialog1, ID_TXTPROCEVERYDAYSTART, wxEmptyString, wxDefaultPosition, timeCtrlSize, wxTE_RIGHT
);
CTransparentStaticText* staticText26 = new CTransparentStaticText(
itemDialog1, wxID_ANY, andString, wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE
);
m_txtProcEveryDayStop = new wxTextCtrl(
itemDialog1, ID_TXTPROCEVERYDAYSTOP, wxEmptyString, wxDefaultPosition, timeCtrlSize, wxTE_RIGHT
);
addNewRowToSizer(
itemBoxSizer11, ProcEveryDayTT, m_chkProcEveryDay, m_txtProcEveryDayStart, staticText26, m_txtProcEveryDayStop
);
wxString NetEveryDayTT(_("Transfer files only during a particular period each day."));
m_chkNetEveryDay = new CTransparentCheckBox(
itemDialog1, ID_CHKNETEVERYDAY, _("Transfer files only between"),
wxDefaultPosition, wxDefaultSize, 0,
wxDefaultValidator, wxCheckBoxNameStr,
&m_backgroundBitmap
);
m_txtNetEveryDayStart = new wxTextCtrl( itemDialog1, ID_TXTNETEVERYDAYSTART, wxEmptyString, wxDefaultPosition, timeCtrlSize, 0 );
CTransparentStaticText* staticText37 = new CTransparentStaticText( itemDialog1, wxID_ANY, andString, wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE );
m_txtNetEveryDayStop = new wxTextCtrl( itemDialog1, ID_TXTNETEVERYDAYSTOP, wxEmptyString, wxDefaultPosition, timeCtrlSize, 0 );
addNewRowToSizer(itemBoxSizer11, NetEveryDayTT, m_chkNetEveryDay, m_txtNetEveryDayStart, staticText37, m_txtNetEveryDayStop);
wxString DiskMaxSpaceTT = wxEmptyString;
DiskMaxSpaceTT.Printf(_("Limit the total amount of disk space used by %s."), pSkinAdvanced->GetApplicationShortName().c_str());
m_chkDiskMaxSpace = new CTransparentCheckBox (
itemDialog1, ID_CHKDISKMAXSPACE, _("Use no more than"),
wxDefaultPosition, wxDefaultSize, 0,
wxDefaultValidator, wxCheckBoxNameStr,
&m_backgroundBitmap
);
m_txtDiskMaxSpace = new wxTextCtrl( itemDialog1, ID_TXTDISKMAXSPACE,wxEmptyString, wxDefaultPosition, getTextCtrlSize(wxT("9999.99")), wxTE_RIGHT );
CTransparentStaticText* staticText41 = new CTransparentStaticText( itemDialog1, wxID_ANY, _("GB of disk space"), wxDefaultPosition, wxDefaultSize, 0 );
addNewRowToSizer(itemBoxSizer11, DiskMaxSpaceTT, m_chkDiskMaxSpace, m_txtDiskMaxSpace, staticText41);
wxBoxSizer* itemBoxSizer44 = new wxBoxSizer(wxHORIZONTAL);
itemBoxSizer2->Add(itemBoxSizer44, 0, wxALIGN_RIGHT|wxALL, ADJUSTFORXDPI(5));
wxButton* itemButton44 = new wxButton( itemDialog1, wxID_OK, _("OK"), wxDefaultPosition, wxDefaultSize, 0 );
itemBoxSizer44->Add(itemButton44, 0, wxALIGN_CENTER_VERTICAL|wxALL, ADJUSTFORXDPI(5));
wxButton* itemButton45 = new wxButton( itemDialog1, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxDefaultSize, 0 );
itemBoxSizer44->Add(itemButton45, 0, wxALIGN_CENTER_VERTICAL|wxALL, ADJUSTFORXDPI(5));
#ifndef __WXMSW__
#ifdef __WXMAC__
wxButton* itemButton46 = new wxButton( this, ID_SIMPLE_HELP, _("Help"), wxDefaultPosition, wxDefaultSize, 0 );
#ifdef wxUSE_TOOLTIPS
wxString helpTip;
helpTip.Printf(_("Get help with %s"), pSkinAdvanced->GetApplicationShortName().c_str());
itemButton46->SetToolTip(helpTip);
#endif
#else
wxContextHelpButton* itemButton46 = new wxContextHelpButton(this);
#endif
itemBoxSizer44->Add(itemButton46, 0, wxALIGN_CENTER_VERTICAL|wxALL, ADJUSTFORXDPI(5));
#endif
// Set validators
m_vTimeValidator = new wxTextValidator(wxFILTER_INCLUDE_CHAR_LIST);
m_vTimeValidator->SetCharIncludes(wxT("0123456789:"));
m_txtProcIdleFor->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
m_txtProcEveryDayStart->SetValidator(*m_vTimeValidator);
m_txtProcEveryDayStop->SetValidator(*m_vTimeValidator);
m_txtProcUseCPUTime->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
m_txtNetEveryDayStart->SetValidator(*m_vTimeValidator);
m_txtNetEveryDayStop->SetValidator(*m_vTimeValidator);
m_txtDiskMaxSpace->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
////@end CPanelPreferences content construction
}
/*!
* wxEVT_COMMAND_BUTTON_CLICKED event handler for ID_SIMPLE_HELP
*/
void CPanelPreferences::OnButtonHelp( wxCommandEvent& event ) {
wxLogTrace(wxT("Function Start/End"), wxT("CPanelPreferences::OnHelp - Function Begin"));
if (IsShown()) {
wxString strURL = wxGetApp().GetSkinManager()->GetAdvanced()->GetOrganizationHelpUrl();
wxString wxurl;
wxurl.Printf(
wxT("%s?target=simple_preferences&version=%s&controlid=%d"),
strURL.c_str(),
wxString(BOINC_VERSION_STRING, wxConvUTF8).c_str(),
event.GetId()
);
wxLaunchDefaultBrowser(wxurl);
}
wxLogTrace(wxT("Function Start/End"), wxT("CPanelPreferences::OnHelp - Function End"));
}
void CPanelPreferences::MakeBackgroundBitmap() {
CSkinSimple* pSkinSimple = wxGetApp().GetSkinManager()->GetSimple();
wxASSERT(pSkinSimple);
wxASSERT(wxDynamicCast(pSkinSimple, CSkinSimple));
wxMemoryDC memDC;
wxCoord w, h, x, y;
// Get the desired background bitmap
wxBitmap bmp(*pSkinSimple->GetDialogBackgroundImage()->GetBitmap());
// Dialog dimensions
wxSize sz = GetClientSize();
m_backgroundBitmap = new wxBitmap(sz);
wxMemoryDC dc(*m_backgroundBitmap);
// bitmap dimensions
w = bmp.GetWidth();
h = bmp.GetHeight();
// Is the bitmap smaller than the window?
if ( (w < sz.x) || (h < sz.y) ) {
// Check to see if they need to be rescaled to fit in the window
wxImage img = bmp.ConvertToImage();
img.Rescale((int) sz.x, (int) sz.y);
// Draw our cool background (enlarged and centered)
dc.DrawBitmap(wxBitmap(img), 0, 0);
} else {
switch(pSkinSimple->GetDialogBackgroundImage()->GetHorizontalAnchor()) {
case BKGD_ANCHOR_HORIZ_LEFT:
default:
x = 0;
break;
case BKGD_ANCHOR_HORIZ_CENTER:
x = (w - sz.x) / 2;
break;
case BKGD_ANCHOR_HORIZ_RIGHT:
x = w - sz.x;
break;
}
switch(pSkinSimple->GetDialogBackgroundImage()->GetVerticalAnchor()) {
case BKGD_ANCHOR_VERT_TOP:
default:
y = 0;
break;
case BKGD_ANCHOR_VERT_CENTER:
y = (h - sz.y) /2;
break;
case BKGD_ANCHOR_VERT_BOTTOM:
y = h - sz.y;
break;
}
// Select the desired bitmap into the memory DC so we can take
// the desired chunk of it.
memDC.SelectObject(bmp);
// Draw the desired chunk on the window
dc.Blit(0, 0, sz.x, sz.y, &memDC, x, y, wxCOPY);
// Drop the bitmap
memDC.SelectObject(wxNullBitmap);
}
}
/*!
* wxEVT_ERASE_BACKGROUND event handler for ID_DLGPREFERENCES
*/
void CPanelPreferences::OnEraseBackground( wxEraseEvent& event ) {
if (!m_backgroundBitmap) {
MakeBackgroundBitmap();
}
// Create a buffered device context to reduce flicker
wxSize sz = GetClientSize();
wxBufferedDC dc(event.GetDC(), sz, wxBUFFER_CLIENT_AREA);
#if TEST_BACKGROUND_WITH_MAGENTA_FILL
// Fill the dialog with a magenta color so people can detect when something
// is wrong
dc.SetBrush(wxBrush(wxColour(255,0,255)));
dc.SetPen(wxPen(wxColour(255,0,255)));
dc.DrawRectangle(0, 0, sz.GetWidth(), sz.GetHeight());
#else
CSkinSimple* pSkinSimple = wxGetApp().GetSkinManager()->GetSimple();
wxASSERT(pSkinSimple);
wxASSERT(wxDynamicCast(pSkinSimple, CSkinSimple));
wxColour bgColor(*pSkinSimple->GetDialogBackgroundImage()->GetBackgroundColor());
SetBackgroundColour(bgColor);
#endif
if (m_backgroundBitmap->IsOk()) {
dc.DrawBitmap(*m_backgroundBitmap, 0, 0);
}
}
void CPanelPreferences::OnButtonClear() {
CMainDocument* pDoc = wxGetApp().GetDocument();
wxASSERT(pDoc);
wxASSERT(wxDynamicCast(pDoc, CMainDocument));
#if 1
// Delete all local prefs (delete global_prefs_override.xml file)
global_preferences_override_mask.clear();
#else
// Delete only those settings controlled by this dialog
ClearPreferenceSettings();
#endif
pDoc->rpc.set_global_prefs_override_struct(global_preferences_working, global_preferences_override_mask);
pDoc->rpc.read_global_prefs_override();
}
bool CPanelPreferences::OnOK() {
CMainDocument* pDoc = wxGetApp().GetDocument();
wxASSERT(pDoc);
wxASSERT(wxDynamicCast(pDoc, CMainDocument));
if(!ValidateInput()) {
return false;
}
SavePreferenceSettings();
pDoc->rpc.set_global_prefs_override_struct(global_preferences_working, global_preferences_override_mask);
pDoc->rpc.read_global_prefs_override();
return true;
}
bool CPanelPreferences::UpdateControlStates() {
m_txtProcIdleFor->Enable(m_chkProcInUse->IsChecked());
m_txtProcEveryDayStart->Enable(m_chkProcEveryDay->IsChecked());
m_txtProcEveryDayStop->Enable(m_chkProcEveryDay->IsChecked());
m_txtNetEveryDayStart->Enable(m_chkNetEveryDay->IsChecked());
m_txtNetEveryDayStop->Enable(m_chkNetEveryDay->IsChecked());
m_txtDiskMaxSpace->Enable(m_chkDiskMaxSpace->IsChecked());
return true;
}
bool CPanelPreferences::ClearPreferenceSettings() {
global_preferences_override_mask.start_hour = false;
global_preferences_override_mask.end_hour = false;
global_preferences_override_mask.net_start_hour = false;
global_preferences_override_mask.net_end_hour = false;
global_preferences_override_mask.disk_max_used_gb = false;
global_preferences_override_mask.cpu_usage_limit = false;
global_preferences_override_mask.run_if_user_active = false;
global_preferences_override_mask.run_on_batteries = false;
global_preferences_override_mask.idle_time_to_run = false;
return true;
}
// convert a Timestring HH:MM into a double
double CPanelPreferences::TimeStringToDouble(wxString timeStr) {
double hour;
double minutes;
timeStr.SubString(0,timeStr.First(':')).ToDouble(&hour);
timeStr.SubString(timeStr.First(':')+1,timeStr.Length()).ToDouble(&minutes);
minutes = minutes/60.0;
return hour + minutes;
}
// convert a double into a timestring HH:MM
wxString CPanelPreferences::DoubleToTimeString(double dt) {
int hour = (int)dt;
int minutes = (int)(60.0 * (dt - hour)+.5);
return wxString::Format(wxT("%02d:%02d"),hour,minutes);
}
// We only display 2 places past the decimal, so restrict the
// precision of saved values to .01. This prevents unexpected
// behavior when, for example, a zero value means no restriction
// and the value is displayed as 0.00 but is actually 0.001.
double CPanelPreferences::RoundToHundredths(double td) {
int i = (int)((td + .005) * 100.);
return ((double)(i) / 100.);
}
void CPanelPreferences::DisplayValue(double value, wxTextCtrl* textCtrl, wxCheckBox* checkBox) {
wxString buffer;
wxASSERT(textCtrl);
if (checkBox) {
if (! checkBox->IsChecked()) {
textCtrl->Clear();
textCtrl->Disable();
return;
}
}
buffer.Printf(wxT("%g"), value);
textCtrl->ChangeValue(buffer);
textCtrl->Enable();
}
/* read preferences from core client and initialize control values */
bool CPanelPreferences::ReadPreferenceSettings() {
CMainDocument* pDoc = wxGetApp().GetDocument();
int retval;
wxASSERT(pDoc);
wxASSERT(wxDynamicCast(pDoc, CMainDocument));
// Populate values and arrays from preferences
// Get override mask from client
retval = pDoc->rpc.get_global_prefs_override_struct(global_preferences_working, global_preferences_override_mask);
// Get current working preferences (including any overrides) from client
retval = pDoc->rpc.get_global_prefs_working_struct(global_preferences_working, global_preferences_mask);
if (retval == ERR_NOT_FOUND) {
// Older clients don't support get_global_prefs_working_struct RPC
global_preferences_working = pDoc->state.global_prefs;
retval = pDoc->rpc.get_global_prefs_override_struct(global_preferences_working, global_preferences_mask);
}
if (retval) {
m_bOKToShow = false;
return true;
}
m_bOKToShow = true;
#if 0 // We might use this to tell user whether local prefs exist
if (!retval && global_preferences_override_mask.are_simple_prefs_set()) {
m_bCustomizedPreferences = true;
} else {
m_bCustomizedPreferences = false;
}
#endif
// on batteries
m_chkProcOnBatteries->SetValue(! global_preferences_working.run_on_batteries);
// in use
m_chkProcInUse->SetValue(! global_preferences_working.run_if_user_active);
if (m_chkProcInUse->IsChecked()) {
m_txtProcIdleFor->Enable();
DisplayValue(global_preferences_working.idle_time_to_run, m_txtProcIdleFor);
} else {
m_txtProcIdleFor->Clear();
m_txtProcIdleFor->Disable();
}
// do work between
m_chkProcEveryDay->SetValue(global_preferences_working.cpu_times.start_hour != global_preferences_working.cpu_times.end_hour);
if (m_chkProcEveryDay->IsChecked()) {
m_txtProcEveryDayStart->ChangeValue(DoubleToTimeString(global_preferences_working.cpu_times.start_hour));
m_txtProcEveryDayStop->ChangeValue(DoubleToTimeString(global_preferences_working.cpu_times.end_hour));
}
//cpu limit
// 0 means "no retriction" but we don't use a checkbox here
if (global_preferences_working.cpu_usage_limit == 0.0) global_preferences_working.cpu_usage_limit = 100.0;
DisplayValue(global_preferences_working.cpu_usage_limit, m_txtProcUseCPUTime);
// use network between
m_chkNetEveryDay->SetValue(global_preferences_working.net_times.start_hour != global_preferences_working.net_times.end_hour);
if (m_chkNetEveryDay->IsChecked()) {
m_txtNetEveryDayStart->ChangeValue(DoubleToTimeString(global_preferences_working.net_times.start_hour));
m_txtNetEveryDayStop->ChangeValue(DoubleToTimeString(global_preferences_working.net_times.end_hour));
}
//max disk space used
m_chkDiskMaxSpace->SetValue(global_preferences_working.disk_max_used_gb > 0.0);
DisplayValue(global_preferences_working.disk_max_used_gb, m_txtDiskMaxSpace, m_chkDiskMaxSpace);
// Now make sure the UI is in sync with the settings
UpdateControlStates();
return true;
}
/* write overridden preferences to disk (global_prefs_override.xml) */
/* IMPORTANT: Any items added here must be checked in ValidateInput()! */
bool CPanelPreferences::SavePreferenceSettings() {
double td;
// on batteries
global_preferences_working.run_on_batteries = ! (m_chkProcOnBatteries->GetValue());
global_preferences_override_mask.run_on_batteries=true;
// in use
global_preferences_working.run_if_user_active = (! m_chkProcInUse->GetValue());
global_preferences_override_mask.run_if_user_active=true;
if(m_txtProcIdleFor->IsEnabled()) {
m_txtProcIdleFor->GetValue().ToDouble(&td);
global_preferences_working.idle_time_to_run=RoundToHundredths(td);
global_preferences_override_mask.idle_time_to_run=true;
}
// else leave idle_time_to_run value and mask unchanged (in case run_gpu_if_user_active is false)
// do work between
if (m_chkProcEveryDay->IsChecked()) {
global_preferences_working.cpu_times.start_hour = TimeStringToDouble(m_txtProcEveryDayStart->GetValue());
global_preferences_working.cpu_times.end_hour = TimeStringToDouble(m_txtProcEveryDayStop->GetValue());
} else {
global_preferences_working.cpu_times.start_hour = global_preferences_working.cpu_times.end_hour = 0.0;
}
global_preferences_override_mask.start_hour = global_preferences_override_mask.end_hour = true;
//cpu limit
m_txtProcUseCPUTime->GetValue().ToDouble(&td);
global_preferences_working.cpu_usage_limit=RoundToHundredths(td);
global_preferences_override_mask.cpu_usage_limit=true;
if (m_chkDiskMaxSpace->IsChecked()) {
m_txtDiskMaxSpace->GetValue().ToDouble(&td);
global_preferences_working.disk_max_used_gb=RoundToHundredths(td);
} else {
global_preferences_working.disk_max_used_gb = 0.0;
}
global_preferences_override_mask.disk_max_used_gb=true;
// use network between
if (m_chkNetEveryDay->IsChecked()) {
global_preferences_working.net_times.start_hour = TimeStringToDouble(m_txtNetEveryDayStart->GetValue());
global_preferences_working.net_times.end_hour = TimeStringToDouble(m_txtNetEveryDayStop->GetValue());
} else {
global_preferences_working.net_times.start_hour = global_preferences_working.net_times.end_hour = 0.0;
}
global_preferences_override_mask.net_start_hour = global_preferences_override_mask.net_end_hour = true;
//max disk space used
// BOINC uses the most restrictive of these 3 settings:
// disk_max_used_gb, disk_min_free_gb, disk_max_used_pct.
// Since the Simple Prefs dialog allows the user to set only
// disk_max_used_gb, we set the other two to "no restriction"
global_preferences_working.disk_min_free_gb = 0.0;
global_preferences_override_mask.disk_min_free_gb=true;
global_preferences_working.disk_max_used_pct = 100.0;
global_preferences_override_mask.disk_max_used_pct=true;
return true;
}
/* validates the entered informations */
bool CPanelPreferences::ValidateInput() {
wxString invMsgFloat = _("Invalid number");
wxString invMsgTime = _("Invalid time, value must be between 0:00 and 24:00, format is HH:MM");
wxString invMsgTimeSpan = _("Start time must be different from end time");
wxString invMsgLimit100 = _("Number must be between 0 and 100");
double startTime, endTime;
wxString buffer;
if(m_txtProcIdleFor->IsEnabled()) {
buffer = m_txtProcIdleFor->GetValue();
if(!IsValidFloatValue(buffer)) {
ShowErrorMessage(invMsgFloat,m_txtProcIdleFor);
return false;
}
}
if (m_chkProcEveryDay->IsChecked()) {
buffer = m_txtProcEveryDayStart->GetValue();
if(!IsValidTimeValue(buffer)) {
ShowErrorMessage(invMsgTime,m_txtProcEveryDayStart);
return false;
}
buffer = m_txtProcEveryDayStop->GetValue();
if(!IsValidTimeValue(buffer)) {
ShowErrorMessage(invMsgTime,m_txtProcEveryDayStop);
return false;
}
startTime = TimeStringToDouble(m_txtProcEveryDayStart->GetValue());
endTime = TimeStringToDouble(m_txtProcEveryDayStop->GetValue());
if (startTime == endTime) {
ShowErrorMessage(invMsgTimeSpan,m_txtProcEveryDayStop);
return false;
}
}
buffer = m_txtProcUseCPUTime->GetValue();
if(!IsValidFloatValueBetween(buffer, 0.0, 100.0)) {
ShowErrorMessage(invMsgLimit100, m_txtProcUseCPUTime);
return false;
}
if (m_chkNetEveryDay->IsChecked()) {
buffer = m_txtNetEveryDayStart->GetValue();
if(!IsValidTimeValue(buffer)) {
ShowErrorMessage(invMsgTime,m_txtNetEveryDayStart);
return false;
}
buffer = m_txtNetEveryDayStop->GetValue();
if(!IsValidTimeValue(buffer)) {
ShowErrorMessage(invMsgTime,m_txtNetEveryDayStop);
return false;
}
startTime = TimeStringToDouble(m_txtNetEveryDayStart->GetValue());
endTime = TimeStringToDouble(m_txtNetEveryDayStop->GetValue());
if (startTime == endTime) {
ShowErrorMessage(invMsgTimeSpan,m_txtNetEveryDayStop);
return false;
}
}
if (m_chkDiskMaxSpace->IsChecked()) {
buffer = m_txtDiskMaxSpace->GetValue();
if(!IsValidFloatValue(buffer)) {
ShowErrorMessage(invMsgFloat, m_txtDiskMaxSpace);
return false;
}
}
return true;
}
/* show an error message and set the focus to the control that caused the error */
void CPanelPreferences::ShowErrorMessage(wxString& message,wxTextCtrl* errorCtrl) {
if(message.IsEmpty()){
message = _("invalid input value detected");
}
if (lastErrorCtrl) {
lastErrorCtrl->SetBackgroundColour(stdTextBkgdColor);
lastErrorCtrl->Refresh();
}
if (lastErrorCtrl != errorCtrl) {
stdTextBkgdColor = errorCtrl->GetBackgroundColour();
}
errorCtrl->SetBackgroundColour(wxColour(255, 192, 192));
errorCtrl->Refresh();
lastErrorCtrl = errorCtrl;
wxGetApp().SafeMessageBox(message,_("Validation Error"),wxOK | wxCENTRE | wxICON_ERROR,this);
errorCtrl->SetFocus();
}
/* checks if ch is a valid character for float values */
bool CPanelPreferences::IsValidFloatChar(const wxChar& ch) {
//don't accept the e
return wxIsdigit(ch) || ch=='.' || ch==',' || ch=='+' || ch=='-';}
/* checks if ch is a valid character for time values */
bool CPanelPreferences::IsValidTimeChar(const wxChar& ch) {
return wxIsdigit(ch) || ch==':';
}
/* checks if the value contains a valid float */
bool CPanelPreferences::IsValidFloatValue(const wxString& value, bool allowNegative) {
for(unsigned int i=0; i < value.Length();i++) {
if(!IsValidFloatChar(value[i])) {
return false;
}
}
//all chars are valid, now what is with the value as a whole ?
double td;
if(!value.ToDouble(&td)) {
return false;
}
if (!allowNegative) {
if (td < 0.0) return false;
}
return true;
}
bool CPanelPreferences::IsValidFloatValueBetween(const wxString& value, double minVal, double maxVal){
for(unsigned int i=0; i < value.Length();i++) {
if(!IsValidFloatChar(value[i])) {
return false;
}
}
//all chars are valid, now what is with the value as a whole ?
double td;
if(!value.ToDouble(&td)) {
return false;
}
if ((td < minVal) || (td > maxVal)) return false;
return true;
}
/* checks if the value is a valid time */
bool CPanelPreferences::IsValidTimeValue(const wxString& value) {
for(unsigned int i=0; i < value.Length();i++) {
if(!IsValidTimeChar(value[i])) {
return false;
}
}
//all chars are valid, now what is with the value as a whole ?
wxDateTime dt;
const wxChar* stopChar = dt.ParseFormat(value,wxT("%H:%M"));
if(stopChar==NULL && value != wxT("24:00")) {
// conversion failed
return false;
}
return true;
}
void CPanelPreferences::OnHandleCheckboxEvent(wxCommandEvent& ev) {
ev.Skip();
// If user has just set the checkbox, set textedit field to default value.
// Note: use ChangeValue() here to avoid generating extra events.
// m_txtProcIdleFor depends on 2 checkboxes, set it in UpdateControlStates().
switch (ev.GetId()) {
case ID_CHKPROCINUSE:
DisplayValue(defaultPrefs.idle_time_to_run, m_txtProcIdleFor, m_chkProcInUse);
break;
case ID_CHKDISKMAXSPACE:
DisplayValue(defaultPrefs.disk_max_used_gb, m_txtDiskMaxSpace, m_chkDiskMaxSpace);
break;
case ID_CHKPROCEVERYDAY:
if (ev.IsChecked()) {
m_txtProcEveryDayStart->ChangeValue(DoubleToTimeString(defaultPrefs.cpu_times.start_hour));
m_txtProcEveryDayStop->ChangeValue(DoubleToTimeString(defaultPrefs.cpu_times.end_hour));
} else {
m_txtProcEveryDayStart->Clear();
m_txtProcEveryDayStop->Clear();
}
break;
case ID_CHKNETEVERYDAY:
if (ev.IsChecked()) {
m_txtNetEveryDayStart->ChangeValue(DoubleToTimeString(defaultPrefs.net_times.start_hour));
m_txtNetEveryDayStop->ChangeValue(DoubleToTimeString(defaultPrefs.net_times.end_hour));
} else {
m_txtNetEveryDayStart->Clear();
m_txtNetEveryDayStop->Clear();
}
break;
default:
break;
}
UpdateControlStates();
}
void CPanelPreferences::addNewRowToSizer(
wxSizer* toSizer, wxString& toolTipText,
wxWindow* first, wxWindow* second, wxWindow* third,
wxWindow* fourth, wxWindow* fifth)
{
wxBoxSizer* rowSizer = new wxBoxSizer( wxHORIZONTAL );
#ifdef __WXMSW__
// MSW adds space to the right of checkbox label
if (first->IsKindOf(CLASSINFO(CTransparentCheckBox))) {
rowSizer->Add(first, 0, wxTOP | wxBOTTOM |wxLEFT, 5 );
} else
#endif
rowSizer->Add(first, 0, wxALL, 5 );
first->SetToolTip(toolTipText);
rowSizer->Add(second, 0, wxALL, 2 );
second->SetToolTip(toolTipText);
rowSizer->Add(third, 0, wxALL, 5 );
third->SetToolTip(toolTipText);
if (fourth) {
rowSizer->Add(fourth, 0, wxALL, 2 );
fourth->SetToolTip(toolTipText);
}
if (fifth) {
rowSizer->Add(fifth, 0, wxALL, 5 );
fifth->SetToolTip(toolTipText);
}
toSizer->Add( rowSizer, 0, 0, 1 );
}
wxSize CPanelPreferences::getTextCtrlSize(wxString maxText) {
int w, h, margin;
wxSize sz;
wxFont f = GetParent()->GetFont();
GetTextExtent(maxText, &w, &h, NULL, NULL, &f);
margin = w/3;
if (margin < 9) margin = 9;
sz.x = w + margin;
sz.y = wxDefaultCoord;
return sz;
}
bool CPanelPreferences::doesLocalPrefsFileExist() {
std::string s;
int retval;
bool local_prefs_found = false;
MIOFILE mf;
bool found_venue;
GLOBAL_PREFS web_prefs;
GLOBAL_PREFS_MASK mask;
CMainDocument* pDoc = wxGetApp().GetDocument();
wxASSERT(pDoc);
wxASSERT(wxDynamicCast(pDoc, CMainDocument));
retval = pDoc->rpc.get_global_prefs_override(s);
local_prefs_found = (retval == BOINC_SUCCESS);
s.clear();
web_prefs.init();
retval = pDoc->rpc.get_global_prefs_file(s);
if (retval) {
web_prefs_url = new wxString(wxEmptyString);
} else {
mf.init_buf_read(s.c_str());
XML_PARSER xp(&mf);
web_prefs.parse(xp, "", found_venue, mask);
web_prefs_url = new wxString(web_prefs.source_project);
}
return local_prefs_found;
}
/*!
* CDlgPreferences type definition
*/
IMPLEMENT_DYNAMIC_CLASS( CDlgPreferences, wxDialog )
/*!
* CDlgPreferences event table definition
*/
BEGIN_EVENT_TABLE( CDlgPreferences, wxDialog )
////@begin CDlgPreferences event table entries
EVT_HELP(wxID_ANY, CDlgPreferences::OnHelp)
EVT_BUTTON( ID_SGPREFERENCESCLEAR, CDlgPreferences::OnButtonClear )
EVT_BUTTON( wxID_OK, CDlgPreferences::OnOK )
////@end CDlgPreferences event table entries
END_EVENT_TABLE()
/*!
* CDlgPreferences constructors
*/
CDlgPreferences::CDlgPreferences( )
{
}
CDlgPreferences::CDlgPreferences( wxWindow* parent, wxWindowID id, const wxString& caption, const wxPoint& pos, const wxSize& size, long style )
{
Create(parent, id, caption, pos, size, style);
}
/*!
* CDlgPreferences creator
*/
bool CDlgPreferences::Create( wxWindow* parent, wxWindowID id, const wxString& caption, const wxPoint& pos, const wxSize& size, long style )
{
CSkinAdvanced* pSkinAdvanced = wxGetApp().GetSkinManager()->GetAdvanced();
wxASSERT(pSkinAdvanced);
wxASSERT(wxDynamicCast(pSkinAdvanced, CSkinAdvanced));
SetExtraStyle(GetExtraStyle()|wxDIALOG_EX_CONTEXTHELP|wxWS_EX_BLOCK_EVENTS);
wxDialog::Create( parent, id, caption, pos, size, style );
// Initialize Application Title
wxString strCaption = caption;
if (strCaption.IsEmpty()) {
strCaption.Printf(_("%s - Computing Preferences"), pSkinAdvanced->GetApplicationShortName().c_str());
}
SetTitle(strCaption);
// Initialize Application Icon
SetIcons(*pSkinAdvanced->GetApplicationIcon());
Freeze();
SetBackgroundStyle(wxBG_STYLE_CUSTOM);
SetForegroundColour(*wxBLACK);
#if TEST_BACKGROUND_WITH_MAGENTA_FILL
SetBackgroundColour(wxColour(255, 0, 255));
#endif
wxBoxSizer* dialogSizer = new wxBoxSizer( wxVERTICAL );
SetSizer(dialogSizer);
m_pBackgroundPanel = new CPanelPreferences(this);
dialogSizer->Add(m_pBackgroundPanel, 0, wxGROW, 0);
GetSizer()->Fit(this);
GetSizer()->SetSizeHints(this);
Centre();
SetEscapeId(wxID_CANCEL);
Thaw();
return true;
}
/*!
* wxEVT_HELP event handler for ID_DLGPREFERENCES
*/
void CDlgPreferences::OnHelp(wxHelpEvent& event) {
wxLogTrace(wxT("Function Start/End"), wxT("CDlgPreferences::OnHelp - Function Begin"));
if (IsShown()) {
wxString strURL = wxGetApp().GetSkinManager()->GetAdvanced()->GetOrganizationHelpUrl();
wxString wxurl;
wxurl.Printf(
wxT("%s?target=simple_preferences&version=%s&controlid=%d"),
strURL.c_str(),
wxString(BOINC_VERSION_STRING, wxConvUTF8).c_str(),
event.GetId()
);
wxLaunchDefaultBrowser(wxurl);
}
wxLogTrace(wxT("Function Start/End"), wxT("CDlgPreferences::OnHelp - Function End"));
}
/*!
* wxEVT_COMMAND_BUTTON_CLICKED event handler for ID_SGPREFERENCESCLEAR
*/
void CDlgPreferences::OnButtonClear( wxCommandEvent& WXUNUSED(event) ) {
if(ConfirmClear()) {
m_pBackgroundPanel->OnButtonClear();
EndModal(wxID_OK);
}
}
/*!
* wxEVT_COMMAND_BUTTON_CLICKED event handler for wxID_OK
*/
void CDlgPreferences::OnOK( wxCommandEvent& WXUNUSED(event) ) {
if (m_pBackgroundPanel->OnOK()) {
EndModal(wxID_OK);
}
}
bool CDlgPreferences::ConfirmClear() {
int res = wxGetApp().SafeMessageBox(_(
"Discard all local preferences and use web-based preferences?"),
_("Confirmation"),wxCENTER | wxICON_QUESTION | wxYES_NO | wxNO_DEFAULT, this);
return res==wxYES;
}