mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-02-14 16:39:57 +00:00
... add Directory to Configuration Page ... set Default similar to Workout Directory in Main General ... start Directory Browsing on Config Page with the configured directory (if is existing/valid)
5893 lines
197 KiB
C++
5893 lines
197 KiB
C++
/*
|
|
* Copyright (c) 2012 Mark Liversedge (liversedge@gmail.com)
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the Free
|
|
* Software Foundation; either version 2 of the License, or (at your option)
|
|
* any later version.
|
|
*
|
|
* This program 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 General Public License for
|
|
* more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc., 51
|
|
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#include "Athlete.h"
|
|
#include <QtGui>
|
|
#include <QIntValidator>
|
|
|
|
#include <assert.h>
|
|
|
|
#include "Pages.h"
|
|
#include "Units.h"
|
|
#include "Settings.h"
|
|
#include "Units.h"
|
|
#include "Colors.h"
|
|
#include "AddDeviceWizard.h"
|
|
#include "DeviceTypes.h"
|
|
#include "DeviceConfiguration.h"
|
|
#include "ColorButton.h"
|
|
#include "SpecialFields.h"
|
|
#include "DataProcessor.h"
|
|
#include "OAuthDialog.h"
|
|
#include "RideAutoImportConfig.h"
|
|
#include "HelpWhatsThis.h"
|
|
#if QT_VERSION >= 0x050000
|
|
#include "Dropbox.h"
|
|
#endif
|
|
#include "LocalFileStore.h"
|
|
|
|
//
|
|
// Main Config Page - tabs for each sub-page
|
|
//
|
|
GeneralPage::GeneralPage(Context *context) : context(context)
|
|
{
|
|
// layout without too wide margins
|
|
QVBoxLayout *mainLayout = new QVBoxLayout(this);
|
|
QGridLayout *configLayout = new QGridLayout;
|
|
mainLayout->addLayout(configLayout);
|
|
mainLayout->addStretch();
|
|
setContentsMargins(0,0,0,0);
|
|
mainLayout->setSpacing(0);
|
|
mainLayout->setContentsMargins(0,0,0,0);
|
|
configLayout->setSpacing(10);
|
|
|
|
//
|
|
// Language Selection
|
|
//
|
|
langLabel = new QLabel(tr("Language:"));
|
|
langCombo = new QComboBox();
|
|
langCombo->addItem(tr("English"));
|
|
langCombo->addItem(tr("French"));
|
|
langCombo->addItem(tr("Japanese"));
|
|
langCombo->addItem(tr("Portugese (Brazil)"));
|
|
langCombo->addItem(tr("Italian"));
|
|
langCombo->addItem(tr("German"));
|
|
langCombo->addItem(tr("Russian"));
|
|
langCombo->addItem(tr("Czech"));
|
|
langCombo->addItem(tr("Spanish"));
|
|
langCombo->addItem(tr("Portugese"));
|
|
langCombo->addItem(tr("Chinese (Traditional)"));
|
|
|
|
// Default to system locale
|
|
QVariant lang = appsettings->value(this, GC_LANG, QLocale::system().name());
|
|
|
|
if(lang.toString().startsWith("en")) langCombo->setCurrentIndex(0);
|
|
else if(lang.toString().startsWith("fr")) langCombo->setCurrentIndex(1);
|
|
else if(lang.toString().startsWith("ja")) langCombo->setCurrentIndex(2);
|
|
else if(lang.toString().startsWith("pt-br")) langCombo->setCurrentIndex(3);
|
|
else if(lang.toString().startsWith("it")) langCombo->setCurrentIndex(4);
|
|
else if(lang.toString().startsWith("de")) langCombo->setCurrentIndex(5);
|
|
else if(lang.toString().startsWith("ru")) langCombo->setCurrentIndex(6);
|
|
else if(lang.toString().startsWith("cs")) langCombo->setCurrentIndex(7);
|
|
else if(lang.toString().startsWith("es")) langCombo->setCurrentIndex(8);
|
|
else if(lang.toString().startsWith("pt")) langCombo->setCurrentIndex(9);
|
|
else if (lang.toString().startsWith("zh-tw")) langCombo->setCurrentIndex(10);
|
|
else langCombo->setCurrentIndex(0);
|
|
|
|
configLayout->addWidget(langLabel, 0,0, Qt::AlignRight);
|
|
configLayout->addWidget(langCombo, 0,1, Qt::AlignLeft);
|
|
|
|
QLabel *unitlabel = new QLabel(tr("Unit"));
|
|
unitCombo = new QComboBox();
|
|
unitCombo->addItem(tr("Metric"));
|
|
unitCombo->addItem(tr("Imperial"));
|
|
|
|
if (context->athlete->useMetricUnits)
|
|
unitCombo->setCurrentIndex(0);
|
|
else
|
|
unitCombo->setCurrentIndex(1);
|
|
|
|
configLayout->addWidget(unitlabel, 1,0, Qt::AlignRight);
|
|
configLayout->addWidget(unitCombo, 1,1, Qt::AlignLeft);
|
|
|
|
//
|
|
// Garmin crap
|
|
//
|
|
// garmin Smart Recording options
|
|
QVariant garminHWMark = appsettings->value(this, GC_GARMIN_HWMARK);
|
|
garminSmartRecord = new QCheckBox(tr("Use Garmin Smart Recording"), this);
|
|
QVariant isGarminSmartRecording = appsettings->value(this, GC_GARMIN_SMARTRECORD, Qt::Checked);
|
|
garminSmartRecord->setCheckState(isGarminSmartRecording.toInt() > 0 ? Qt::Checked : Qt::Unchecked);
|
|
|
|
// by default, set the threshold to 25 seconds
|
|
if (garminHWMark.isNull() || garminHWMark.toInt() == 0) garminHWMark.setValue(25);
|
|
QLabel *garminHWLabel = new QLabel(tr("Smart Recording Threshold (secs):"));
|
|
garminHWMarkedit = new QLineEdit(garminHWMark.toString(),this);
|
|
garminHWMarkedit->setInputMask("009");
|
|
|
|
configLayout->addWidget(garminSmartRecord, 2,1, Qt::AlignLeft);
|
|
configLayout->addWidget(garminHWLabel, 3,0, Qt::AlignRight);
|
|
configLayout->addWidget(garminHWMarkedit, 3,1, Qt::AlignLeft);
|
|
|
|
// Elevation hysterisis GC_ELEVATION_HYSTERISIS
|
|
QVariant elevationHysteresis = appsettings->value(this, GC_ELEVATION_HYSTERESIS);
|
|
if (elevationHysteresis.isNull() || elevationHysteresis.toFloat() == 0.0)
|
|
elevationHysteresis.setValue(3.0); // default is 1 meter
|
|
|
|
QLabel *hystlabel = new QLabel(tr("Elevation hysteresis (meters):"));
|
|
hystedit = new QLineEdit(elevationHysteresis.toString(),this);
|
|
hystedit->setInputMask("9.00");
|
|
|
|
configLayout->addWidget(hystlabel, 4,0, Qt::AlignRight);
|
|
configLayout->addWidget(hystedit, 4,1, Qt::AlignLeft);
|
|
|
|
|
|
// wbal formula preference
|
|
QLabel *wbalFormLabel = new QLabel(tr("W' bal formula:"));
|
|
wbalForm = new QComboBox(this);
|
|
wbalForm->addItem(tr("Differential"));
|
|
wbalForm->addItem(tr("Integral"));
|
|
if (appsettings->value(this, GC_WBALFORM, "diff").toString() == "diff") wbalForm->setCurrentIndex(0);
|
|
else wbalForm->setCurrentIndex(1);
|
|
|
|
configLayout->addWidget(wbalFormLabel, 5,0, Qt::AlignRight);
|
|
configLayout->addWidget(wbalForm, 5,1, Qt::AlignLeft);
|
|
|
|
|
|
//
|
|
// Warn to save on exit
|
|
warnOnExit = new QCheckBox(tr("Warn for unsaved activities on exit"), this);
|
|
warnOnExit->setChecked(appsettings->value(NULL, GC_WARNEXIT, true).toBool());
|
|
configLayout->addWidget(warnOnExit, 6,1, Qt::AlignLeft);
|
|
|
|
//
|
|
// Run API web services when running
|
|
//
|
|
int offset=0;
|
|
#ifdef GC_WANT_HTTP
|
|
offset += 1;
|
|
startHttp = new QCheckBox(tr("Enable API Web Services"), this);
|
|
startHttp->setChecked(appsettings->value(NULL, GC_START_HTTP, true).toBool());
|
|
configLayout->addWidget(startHttp, 7,1, Qt::AlignLeft);
|
|
#endif
|
|
|
|
//
|
|
// Athlete directory (home of athletes)
|
|
//
|
|
QVariant athleteDir = appsettings->value(this, GC_HOMEDIR);
|
|
athleteLabel = new QLabel(tr("Athlete Library:"));
|
|
athleteDirectory = new QLineEdit;
|
|
athleteDirectory->setText(athleteDir.toString() == "0" ? "" : athleteDir.toString());
|
|
athleteWAS = athleteDirectory->text(); // remember what we started with ...
|
|
athleteBrowseButton = new QPushButton(tr("Browse"));
|
|
athleteBrowseButton->setFixedWidth(120);
|
|
|
|
configLayout->addWidget(athleteLabel, 7 + offset,0, Qt::AlignRight);
|
|
configLayout->addWidget(athleteDirectory, 7 + offset,1);
|
|
configLayout->addWidget(athleteBrowseButton, 7 + offset,2);
|
|
|
|
connect(athleteBrowseButton, SIGNAL(clicked()), this, SLOT(browseAthleteDir()));
|
|
|
|
//
|
|
// Workout directory (train view)
|
|
//
|
|
QVariant workoutDir = appsettings->value(this, GC_WORKOUTDIR, "");
|
|
// fix old bug..
|
|
if (workoutDir == "0") workoutDir = "";
|
|
workoutLabel = new QLabel(tr("Workout Library:"));
|
|
workoutDirectory = new QLineEdit;
|
|
workoutDirectory->setText(workoutDir.toString());
|
|
workoutBrowseButton = new QPushButton(tr("Browse"));
|
|
workoutBrowseButton->setFixedWidth(120);
|
|
|
|
configLayout->addWidget(workoutLabel, 8 + offset,0, Qt::AlignRight);
|
|
configLayout->addWidget(workoutDirectory, 8 + offset,1);
|
|
configLayout->addWidget(workoutBrowseButton, 8 + offset,2);
|
|
|
|
connect(workoutBrowseButton, SIGNAL(clicked()), this, SLOT(browseWorkoutDir()));
|
|
//
|
|
// Videosync directory (train view)
|
|
//
|
|
QVariant videosyncDir = appsettings->value(this, GC_VIDEOSYNCDIR, "");
|
|
videosyncLabel = new QLabel(tr("VideoSync Library:"));
|
|
videosyncDirectory = new QLineEdit;
|
|
videosyncDirectory->setText(videosyncDir.toString());
|
|
videosyncBrowseButton = new QPushButton(tr("Browse"));
|
|
videosyncBrowseButton->setFixedWidth(120);
|
|
|
|
configLayout->addWidget(videosyncLabel, 9 + offset,0, Qt::AlignRight);
|
|
configLayout->addWidget(videosyncDirectory, 9 + offset,1);
|
|
configLayout->addWidget(videosyncBrowseButton, 9 + offset,2);
|
|
|
|
connect(videosyncBrowseButton, SIGNAL(clicked()), this, SLOT(browseVideoSyncDir()));
|
|
|
|
// save away initial values
|
|
b4.unit = unitCombo->currentIndex();
|
|
b4.hyst = elevationHysteresis.toFloat();
|
|
b4.wbal = wbalForm->currentIndex();
|
|
b4.warn = warnOnExit->isChecked();
|
|
#ifdef GC_WANT_HTTP
|
|
b4.starthttp = startHttp->isChecked();
|
|
#endif
|
|
}
|
|
|
|
|
|
qint32
|
|
GeneralPage::saveClicked()
|
|
{
|
|
// Language
|
|
static const QString langs[] = {
|
|
"en", "fr", "ja", "pt-br", "it", "de", "ru", "cs", "es", "pt", "zh-tw"
|
|
};
|
|
appsettings->setValue(GC_LANG, langs[langCombo->currentIndex()]);
|
|
|
|
// Garmin and cranks
|
|
appsettings->setValue(GC_GARMIN_HWMARK, garminHWMarkedit->text().toInt());
|
|
appsettings->setValue(GC_GARMIN_SMARTRECORD, garminSmartRecord->checkState());
|
|
|
|
// save on exit
|
|
appsettings->setValue(GC_WARNEXIT, warnOnExit->isChecked());
|
|
|
|
// Directories
|
|
appsettings->setValue(GC_WORKOUTDIR, workoutDirectory->text());
|
|
appsettings->setValue(GC_HOMEDIR, athleteDirectory->text());
|
|
appsettings->setValue(GC_VIDEOSYNCDIR, videosyncDirectory->text());
|
|
|
|
// Elevation
|
|
appsettings->setValue(GC_ELEVATION_HYSTERESIS, hystedit->text());
|
|
|
|
// wbal formula
|
|
appsettings->setValue(GC_WBALFORM, wbalForm->currentIndex() ? "int" : "diff");
|
|
|
|
if (unitCombo->currentIndex()==0)
|
|
appsettings->setValue(GC_UNIT, GC_UNIT_METRIC);
|
|
else if (unitCombo->currentIndex()==1)
|
|
appsettings->setValue(GC_UNIT, GC_UNIT_IMPERIAL);
|
|
|
|
|
|
#ifdef GC_WANT_HTTP
|
|
// start http
|
|
appsettings->setValue(GC_START_HTTP, startHttp->isChecked());
|
|
#endif
|
|
|
|
qint32 state=0;
|
|
|
|
// general stuff changed ?
|
|
#ifdef GC_WANT_HTTP
|
|
if (b4.hyst != hystedit->text().toFloat() ||
|
|
b4.starthttp != startHttp->isChecked())
|
|
#else
|
|
if (b4.hyst != hystedit->text().toFloat())
|
|
#endif
|
|
state += CONFIG_GENERAL;
|
|
|
|
if (b4.wbal != wbalForm->currentIndex())
|
|
state += CONFIG_WBAL;
|
|
|
|
// units prefs changed?
|
|
if (b4.unit != unitCombo->currentIndex())
|
|
state += CONFIG_UNITS;
|
|
|
|
return state;
|
|
}
|
|
|
|
void
|
|
GeneralPage::browseWorkoutDir()
|
|
{
|
|
QString currentDir = workoutDirectory->text();
|
|
if (!QDir(currentDir).exists()) currentDir = "";
|
|
QString dir = QFileDialog::getExistingDirectory(this, tr("Select Workout Library"),
|
|
currentDir, QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
|
|
if (dir != "") workoutDirectory->setText(dir); //only overwrite current dir, if a new was selected
|
|
}
|
|
|
|
void
|
|
GeneralPage::browseVideoSyncDir()
|
|
{
|
|
QString currentDir = videosyncDirectory->text();
|
|
if (!QDir(currentDir).exists()) currentDir = ""; QString dir = QFileDialog::getExistingDirectory(this, tr("Select Workout Library"),
|
|
currentDir, QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
|
|
if (dir != "") workoutDirectory->setText(dir); //only overwrite current dir, if a new was selected
|
|
}
|
|
|
|
|
|
void
|
|
GeneralPage::browseAthleteDir()
|
|
{
|
|
QString currentDir = athleteDirectory->text();
|
|
if (!QDir(currentDir).exists()) currentDir = "";
|
|
QString dir = QFileDialog::getExistingDirectory(this, tr("Select Athlete Library"),
|
|
currentDir, QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
|
|
if (dir != "") athleteDirectory->setText(dir); //only overwrite current dir, if a new was selected
|
|
}
|
|
|
|
//
|
|
// Passwords page
|
|
//
|
|
CredentialsPage::CredentialsPage(QWidget *parent, Context *context) : QScrollArea(parent), context(context)
|
|
{
|
|
QWidget *main = new QWidget(this);
|
|
main->setContentsMargins(0,0,0,0);
|
|
|
|
QGridLayout *grid = new QGridLayout;
|
|
unsigned int row = 0;
|
|
|
|
QFont current;
|
|
current.setWeight(QFont::Black);
|
|
|
|
QPixmap passwords = QPixmap(":/images/toolbar/passwords.png");
|
|
|
|
//////////////////////////////////////////////////
|
|
// TrainingPeaks
|
|
|
|
QLabel *tp = new QLabel(tr("TrainingPeaks"));
|
|
tp->setFont(current);
|
|
|
|
QLabel *userLabel = new QLabel(tr("Username"));
|
|
QLabel *passLabel = new QLabel(tr("Password"));
|
|
QLabel *typeLabel = new QLabel(tr("Account Type"));
|
|
|
|
tpUser = new QLineEdit(this);
|
|
tpUser->setText(appsettings->cvalue(context->athlete->cyclist, GC_TPUSER, "").toString());
|
|
|
|
tpPass = new QLineEdit(this);
|
|
tpPass->setEchoMode(QLineEdit::Password);
|
|
tpPass->setText(appsettings->cvalue(context->athlete->cyclist, GC_TPPASS, "").toString());
|
|
|
|
tpType = new QComboBox(this);
|
|
tpType->addItem("Shared Free");
|
|
tpType->addItem("Coached Free");
|
|
tpType->addItem("Self Coached Premium");
|
|
tpType->addItem("Shared Self Coached Premium");
|
|
tpType->addItem("Coached Premium");
|
|
tpType->addItem("Shared Coached Premium");
|
|
|
|
tpType->setCurrentIndex(appsettings->cvalue(context->athlete->cyclist, GC_TPTYPE, "0").toInt());
|
|
|
|
grid->addWidget(tp, row, 0);
|
|
|
|
grid->addWidget(userLabel, ++row, 0);
|
|
grid->addWidget(tpUser, row, 1, Qt::AlignLeft | Qt::AlignVCenter);
|
|
|
|
grid->addWidget(passLabel, ++row, 0);
|
|
grid->addWidget(tpPass, row, 1, Qt::AlignLeft | Qt::AlignVCenter);
|
|
|
|
grid->addWidget(typeLabel, ++row, 0);
|
|
grid->addWidget(tpType, row, 1, Qt::AlignLeft | Qt::AlignVCenter);
|
|
|
|
|
|
//////////////////////////////////////////////////
|
|
// Twitter
|
|
|
|
#ifdef GC_HAVE_KQOAUTH
|
|
QLabel *twp = new QLabel(tr("Twitter"));
|
|
twp->setFont(current);
|
|
|
|
QLabel *twauthLabel = new QLabel(tr("Authorise"));
|
|
|
|
twitterAuthorise = new QPushButton(tr("Authorise"), this);
|
|
twitterAuthorised = new QPushButton(this);
|
|
twitterAuthorised->setContentsMargins(0,0,0,0);
|
|
twitterAuthorised->setIcon(passwords.scaled(16,16));
|
|
twitterAuthorised->setIconSize(QSize(16,16));
|
|
twitterAuthorised->setFixedHeight(16);
|
|
twitterAuthorised->setFixedWidth(16);
|
|
|
|
grid->addWidget(twp, ++row, 0);
|
|
|
|
grid->addWidget(twauthLabel, ++row, 0);
|
|
grid->addWidget(twitterAuthorise, row, 1, Qt::AlignLeft | Qt::AlignVCenter);
|
|
if (appsettings->cvalue(context->athlete->cyclist, GC_TWITTER_TOKEN, "")!="")
|
|
grid->addWidget(twitterAuthorised, row, 1, Qt::AlignLeft | Qt::AlignVCenter);
|
|
else
|
|
twitterAuthorised->hide(); // if no token no show
|
|
|
|
connect(twitterAuthorise, SIGNAL(clicked()), this, SLOT(authoriseTwitter()));
|
|
#endif
|
|
|
|
#if QT_VERSION >= 0x050000 // only in QT5 or higher
|
|
//
|
|
// Authorising Dropbox via an OAuthDialog...
|
|
QLabel *dwp = new QLabel(tr("Dropbox"));
|
|
dwp->setFont(current);
|
|
|
|
QLabel *dropauthLabel = new QLabel(tr("Authorise"));
|
|
|
|
dropboxAuthorise = new QPushButton(tr("Authorise"), this);
|
|
|
|
dropboxAuthorised = new QPushButton(this);
|
|
dropboxAuthorised->setContentsMargins(0,0,0,0);
|
|
dropboxAuthorised->setIcon(passwords.scaled(16,16));
|
|
dropboxAuthorised->setIconSize(QSize(16,16));
|
|
dropboxAuthorised->setFixedHeight(16);
|
|
dropboxAuthorised->setFixedWidth(16);
|
|
|
|
grid->addWidget(dwp, ++row, 0);
|
|
|
|
grid->addWidget(dropauthLabel, ++row, 0);
|
|
grid->addWidget(dropboxAuthorise, row, 1, Qt::AlignLeft | Qt::AlignVCenter);
|
|
if (appsettings->cvalue(context->athlete->cyclist, GC_DROPBOX_TOKEN, "")!="")
|
|
grid->addWidget(dropboxAuthorised, row, 1, Qt::AlignLeft | Qt::AlignVCenter);
|
|
else
|
|
dropboxAuthorised->hide(); // if no token no show
|
|
|
|
connect(dropboxAuthorise, SIGNAL(clicked()), this, SLOT(authoriseDropbox()));
|
|
|
|
//
|
|
// Selecting the athlete folder in dropbox
|
|
QLabel *dropfolderLabel = new QLabel(tr("Athlete Folder"));
|
|
dropboxFolder = new QLineEdit(this);
|
|
dropboxFolder->setText(appsettings->cvalue(context->athlete->cyclist, GC_DROPBOX_FOLDER, "").toString());
|
|
dropboxBrowse = new QPushButton(tr("Browse"));
|
|
connect(dropboxBrowse, SIGNAL(clicked()), this, SLOT(chooseDropboxFolder()));
|
|
QHBoxLayout *dfchoose = new QHBoxLayout;
|
|
dfchoose->addWidget(dropboxFolder);
|
|
dfchoose->addWidget(dropboxBrowse);
|
|
grid->addWidget(dropfolderLabel, ++row, 0);
|
|
grid->addLayout(dfchoose, row, 1);
|
|
//grid->addWidget(twpinLabel, ++row, 0);
|
|
#endif
|
|
|
|
//////////////////////////////////////////////////
|
|
// Local FileStore
|
|
|
|
|
|
QLabel *nfs = new QLabel(tr("Shared Local Folder"));
|
|
nfs->setFont(current);
|
|
grid->addWidget(nfs, ++row, 0);
|
|
|
|
// Selecting the storage folder folder of the Local File Store
|
|
QLabel *networkFileStoreFolderLabel = new QLabel(tr("Shared Local Athlete Folder"));
|
|
networkFileStoreFolder = new QLineEdit(this);
|
|
networkFileStoreFolder->setText(appsettings->cvalue(context->athlete->cyclist, GC_NETWORKFILESTORE_FOLDER, "").toString());
|
|
networkFileStoreFolderBrowse = new QPushButton(tr("Browse"));
|
|
connect(networkFileStoreFolderBrowse, SIGNAL(clicked()), this, SLOT(chooseLocalFileStoreFolder()));
|
|
QHBoxLayout *nwfsfchoose = new QHBoxLayout;
|
|
nwfsfchoose->addWidget(networkFileStoreFolder);
|
|
nwfsfchoose->addWidget(networkFileStoreFolderBrowse);
|
|
grid->addWidget(networkFileStoreFolderLabel, ++row, 0);
|
|
grid->addLayout(nwfsfchoose, row, 1);
|
|
|
|
|
|
//////////////////////////////////////////////////
|
|
// Strava
|
|
|
|
QLabel *str = new QLabel(tr("Strava"));
|
|
str->setFont(current);
|
|
|
|
QLabel *strauthLabel = new QLabel(tr("Authorise"));
|
|
|
|
stravaAuthorise = new QPushButton(tr("Authorise"), this);
|
|
|
|
#ifndef GC_STRAVA_CLIENT_SECRET
|
|
stravaAuthorise->setEnabled(false);
|
|
#endif
|
|
|
|
stravaAuthorised = new QPushButton(this);
|
|
stravaAuthorised->setContentsMargins(0,0,0,0);
|
|
stravaAuthorised->setIcon(passwords.scaled(16,16));
|
|
stravaAuthorised->setIconSize(QSize(16,16));
|
|
stravaAuthorised->setFixedHeight(16);
|
|
stravaAuthorised->setFixedWidth(16);
|
|
|
|
grid->addWidget(str, ++row, 0);
|
|
|
|
grid->addWidget(strauthLabel, ++row, 0);
|
|
grid->addWidget(stravaAuthorise, row, 1, Qt::AlignLeft | Qt::AlignVCenter);
|
|
if (appsettings->cvalue(context->athlete->cyclist, GC_STRAVA_TOKEN, "")!="")
|
|
grid->addWidget(stravaAuthorised, row, 1, Qt::AlignLeft | Qt::AlignVCenter);
|
|
else
|
|
stravaAuthorised->hide(); // if no token no show
|
|
|
|
connect(stravaAuthorise, SIGNAL(clicked()), this, SLOT(authoriseStrava()));
|
|
|
|
//////////////////////////////////////////////////
|
|
// Cycling Analytics
|
|
|
|
|
|
QLabel *can = new QLabel(tr("Cycling Analytics"));
|
|
can->setFont(current);
|
|
QLabel *canauthLabel = new QLabel(tr("Authorise"));
|
|
|
|
cyclingAnalyticsAuthorise = new QPushButton(tr("Authorise"), this);
|
|
#ifndef GC_CYCLINGANALYTICS_CLIENT_SECRET
|
|
cyclingAnalyticsAuthorise->setEnabled(false);
|
|
#endif
|
|
|
|
cyclingAnalyticsAuthorised = new QPushButton(this);
|
|
cyclingAnalyticsAuthorised->setContentsMargins(0,0,0,0);
|
|
cyclingAnalyticsAuthorised->setIcon(passwords.scaled(16,16));
|
|
cyclingAnalyticsAuthorised->setIconSize(QSize(16,16));
|
|
cyclingAnalyticsAuthorised->setFixedHeight(16);
|
|
cyclingAnalyticsAuthorised->setFixedWidth(16);
|
|
|
|
grid->addWidget(can, ++row, 0);
|
|
|
|
grid->addWidget(canauthLabel, ++row, 0);
|
|
grid->addWidget(cyclingAnalyticsAuthorise, row, 1, Qt::AlignLeft | Qt::AlignVCenter);
|
|
if (appsettings->cvalue(context->athlete->cyclist, GC_CYCLINGANALYTICS_TOKEN, "")!="")
|
|
grid->addWidget(cyclingAnalyticsAuthorised, row, 1, Qt::AlignLeft | Qt::AlignVCenter);
|
|
else
|
|
cyclingAnalyticsAuthorised->hide();
|
|
|
|
connect(cyclingAnalyticsAuthorise, SIGNAL(clicked()), this, SLOT(authoriseCyclingAnalytics()));
|
|
|
|
|
|
//////////////////////////////////////////////////
|
|
// RideWithGPS
|
|
|
|
QLabel *rwgps = new QLabel(tr("RideWithGPS"));
|
|
rwgps->setFont(current);
|
|
|
|
QLabel *rwgpsuserLabel = new QLabel(tr("Username"));
|
|
QLabel *rwgpspassLabel = new QLabel(tr("Password"));
|
|
|
|
rideWithGPSUser = new QLineEdit(this);
|
|
rideWithGPSUser->setText(appsettings->cvalue(context->athlete->cyclist, GC_RWGPSUSER, "").toString());
|
|
|
|
rideWithGPSPass = new QLineEdit(this);
|
|
rideWithGPSPass->setEchoMode(QLineEdit::Password);
|
|
rideWithGPSPass->setText(appsettings->cvalue(context->athlete->cyclist, GC_RWGPSPASS, "").toString());
|
|
|
|
grid->addWidget(rwgps, ++row, 0);
|
|
|
|
grid->addWidget(rwgpsuserLabel, ++row, 0);
|
|
grid->addWidget(rideWithGPSUser, row, 1, Qt::AlignLeft | Qt::AlignVCenter);
|
|
|
|
grid->addWidget(rwgpspassLabel, ++row, 0);
|
|
grid->addWidget(rideWithGPSPass, row, 1, Qt::AlignLeft | Qt::AlignVCenter);
|
|
|
|
//////////////////////////////////////////////////
|
|
// Withings Wifi Scales
|
|
|
|
QLabel *wip = new QLabel(tr("Withings Wifi Scales"));
|
|
wip->setFont(current);
|
|
|
|
QLabel *wiurlLabel = new QLabel(tr("Website"));
|
|
QLabel *wiuserLabel = new QLabel(tr("User Id"));
|
|
QLabel *wipassLabel = new QLabel(tr("Public Key"));
|
|
|
|
wiURL = new QLineEdit(this);
|
|
wiURL->setText(appsettings->cvalue(context->athlete->cyclist, GC_WIURL, "http://wbsapi.withings.net/").toString());
|
|
|
|
wiUser = new QLineEdit(this);
|
|
wiUser->setText(appsettings->cvalue(context->athlete->cyclist, GC_WIUSER, "").toString());
|
|
|
|
wiPass = new QLineEdit(this);
|
|
wiPass->setText(appsettings->cvalue(context->athlete->cyclist, GC_WIKEY, "").toString());
|
|
|
|
grid->addWidget(wip, ++row, 0);
|
|
|
|
grid->addWidget(wiurlLabel, ++row, 0);
|
|
grid->addWidget(wiURL, row, 1, 0);
|
|
|
|
grid->addWidget(wiuserLabel, ++row, 0);
|
|
grid->addWidget(wiUser, row, 1, Qt::AlignLeft | Qt::AlignVCenter);
|
|
|
|
grid->addWidget(wipassLabel, ++row, 0);
|
|
grid->addWidget(wiPass, row, 1, Qt::AlignLeft | Qt::AlignVCenter);
|
|
|
|
//////////////////////////////////////////////////
|
|
// Web Calendar
|
|
|
|
QLabel *webcal = new QLabel(tr("Web Calendar"));
|
|
webcal->setFont(current);
|
|
QLabel *wcurlLabel = new QLabel(tr("Webcal URL"));
|
|
|
|
webcalURL = new QLineEdit(this);
|
|
webcalURL->setText(appsettings->cvalue(context->athlete->cyclist, GC_WEBCAL_URL, "").toString());
|
|
|
|
grid->addWidget(webcal, ++row, 0);
|
|
|
|
grid->addWidget(wcurlLabel, ++row, 0);
|
|
grid->addWidget(webcalURL, row, 1, 0);
|
|
|
|
//////////////////////////////////////////////////
|
|
// CalDAV Calendar
|
|
|
|
QLabel *dv = new QLabel(tr("CalDAV Calendar"));
|
|
dvCALDAVType = new QComboBox(this);
|
|
dvCALDAVType->addItem(tr("Generic CalDAV"));
|
|
dvCALDAVType->addItem(tr("Google Calendar"));
|
|
|
|
dvCALDAVType->setCurrentIndex(appsettings->cvalue(context->athlete->cyclist, GC_DVCALDAVTYPE, "0").toInt());
|
|
|
|
// setup and fill all fields
|
|
dv->setFont(current);
|
|
QLabel *dvurlLabel = new QLabel(tr("CalDAV URL"));
|
|
QLabel *dvuserLabel = new QLabel(tr("CalDAV User Id"));
|
|
QLabel *dvpassLabel = new QLabel(tr("CalDAV Password"));
|
|
QLabel *dvTypeLabel = new QLabel(tr("Calendar Type"));
|
|
QLabel *dvGoogleLabel = new QLabel(tr("Google CalID"));
|
|
|
|
dvURL = new QLineEdit(this);
|
|
QString url = appsettings->cvalue(context->athlete->cyclist, GC_DVURL, "").toString();
|
|
url.replace("%40", "@"); // remove escape of @ character
|
|
dvURL->setText(url);
|
|
|
|
dvUser = new QLineEdit(this);
|
|
dvUser->setText(appsettings->cvalue(context->athlete->cyclist, GC_DVUSER, "").toString());
|
|
|
|
dvPass = new QLineEdit(this);
|
|
dvPass->setEchoMode(QLineEdit::Password);
|
|
dvPass->setText(appsettings->cvalue(context->athlete->cyclist, GC_DVPASS, "").toString());
|
|
|
|
dvGoogleCalid = new QLineEdit(this);
|
|
dvGoogleCalid->setText(appsettings->cvalue(context->athlete->cyclist, GC_DVGOOGLE_CALID, "").toString());
|
|
|
|
QLabel *googleCalendarAuthLabel = new QLabel(tr("Google Calendar"));
|
|
|
|
googleCalendarAuthorise = new QPushButton(tr("Authorise"), this);
|
|
|
|
googleCalendarAuthorised = new QPushButton(this);
|
|
googleCalendarAuthorised->setContentsMargins(0,0,0,0);
|
|
googleCalendarAuthorised->setIcon(passwords.scaled(16,16));
|
|
googleCalendarAuthorised->setIconSize(QSize(16,16));
|
|
googleCalendarAuthorised->setFixedHeight(16);
|
|
googleCalendarAuthorised->setFixedWidth(16);
|
|
|
|
grid->addWidget(dv, ++row, 0);
|
|
|
|
grid->addWidget(dvTypeLabel, ++row, 0);
|
|
grid->addWidget(dvCALDAVType, row, 1, 0);
|
|
|
|
grid->addWidget(dvurlLabel, ++row, 0);
|
|
grid->addWidget(dvURL, row, 1, 0);
|
|
|
|
grid->addWidget(dvuserLabel, ++row, 0);
|
|
grid->addWidget(dvUser, row, 1, Qt::AlignLeft | Qt::AlignVCenter);
|
|
|
|
grid->addWidget(dvpassLabel, ++row, 0);
|
|
grid->addWidget(dvPass, row, 1, Qt::AlignLeft | Qt::AlignVCenter);
|
|
|
|
grid->addWidget(dvGoogleLabel, ++row, 0);
|
|
grid->addWidget(dvGoogleCalid, row, 1, Qt::AlignLeft | Qt::AlignVCenter);
|
|
|
|
grid->addWidget(googleCalendarAuthLabel, ++row, 0);
|
|
grid->addWidget(googleCalendarAuthorise, row, 1, Qt::AlignLeft | Qt::AlignVCenter);
|
|
if (appsettings->cvalue(context->athlete->cyclist, GC_GOOGLE_CALENDAR_REFRESH_TOKEN, "")!="")
|
|
grid->addWidget(googleCalendarAuthorised, row, 1, Qt::AlignLeft | Qt::AlignVCenter);
|
|
else
|
|
googleCalendarAuthorised->hide(); // if no token no show
|
|
|
|
connect(googleCalendarAuthorise, SIGNAL(clicked()), this, SLOT(authoriseGoogleCalendar()));
|
|
connect (dvCALDAVType, SIGNAL(currentIndexChanged(int)), this, SLOT(dvCALDAVTypeChanged(int)));
|
|
|
|
// activate/deactivate the input fields according to the type selected
|
|
dvCALDAVTypeChanged(dvCALDAVType->currentIndex());
|
|
|
|
//////////////////////////////////////////////////
|
|
// Trainingstagebuch
|
|
|
|
QLabel *ttb = new QLabel(tr("Trainingstagebuch"));
|
|
ttb->setFont(current);
|
|
|
|
QLabel *ttbuserLabel = new QLabel(tr("Username"));
|
|
QLabel *ttbpassLabel = new QLabel(tr("Password"));
|
|
|
|
ttbUser = new QLineEdit(this);
|
|
ttbUser->setText(appsettings->cvalue(context->athlete->cyclist, GC_TTBUSER, "").toString());
|
|
|
|
ttbPass = new QLineEdit(this);
|
|
ttbPass->setEchoMode(QLineEdit::Password);
|
|
ttbPass->setText(appsettings->cvalue(context->athlete->cyclist, GC_TTBPASS, "").toString());
|
|
|
|
grid->addWidget(ttb, ++row, 0);
|
|
|
|
grid->addWidget(ttbuserLabel, ++row, 0);
|
|
grid->addWidget(ttbUser, row, 1, Qt::AlignLeft | Qt::AlignVCenter);
|
|
|
|
grid->addWidget(ttbpassLabel, ++row, 0);
|
|
grid->addWidget(ttbPass, row, 1, Qt::AlignLeft | Qt::AlignVCenter);
|
|
|
|
//////////////////////////////////////////////////
|
|
// Selfloops
|
|
|
|
QLabel *sel = new QLabel(tr("Selfloops"));
|
|
sel->setFont(current);
|
|
|
|
QLabel *seluserLabel = new QLabel(tr("Username"));
|
|
QLabel *selpassLabel = new QLabel(tr("Password"));
|
|
|
|
selUser = new QLineEdit(this);
|
|
selUser->setText(appsettings->cvalue(context->athlete->cyclist, GC_SELUSER, "").toString());
|
|
|
|
selPass = new QLineEdit(this);
|
|
selPass->setEchoMode(QLineEdit::Password);
|
|
selPass->setText(appsettings->cvalue(context->athlete->cyclist, GC_SELPASS, "").toString());
|
|
|
|
grid->addWidget(sel, ++row, 0);
|
|
|
|
grid->addWidget(seluserLabel, ++row, 0);
|
|
grid->addWidget(selUser, row, 1, Qt::AlignLeft | Qt::AlignVCenter);
|
|
|
|
grid->addWidget(selpassLabel, ++row, 0);
|
|
grid->addWidget(selPass, row, 1, Qt::AlignLeft | Qt::AlignVCenter);
|
|
|
|
//////////////////////////////////////////////////
|
|
// Velo Hero
|
|
|
|
QLabel *velohero = new QLabel(tr("Velo Hero"));
|
|
velohero->setFont(current);
|
|
|
|
QLabel *veloherouserLabel = new QLabel(tr("Username"));
|
|
QLabel *veloheropassLabel = new QLabel(tr("Password"));
|
|
|
|
veloHeroUser = new QLineEdit(this);
|
|
veloHeroUser->setText(appsettings->cvalue(context->athlete->cyclist, GC_VELOHEROUSER, "").toString());
|
|
|
|
veloHeroPass = new QLineEdit(this);
|
|
veloHeroPass->setEchoMode(QLineEdit::Password);
|
|
veloHeroPass->setText(appsettings->cvalue(context->athlete->cyclist, GC_VELOHEROPASS, "").toString());
|
|
|
|
grid->addWidget(velohero, ++row, 0);
|
|
|
|
grid->addWidget(veloherouserLabel, ++row, 0);
|
|
grid->addWidget(veloHeroUser, row, 1, Qt::AlignLeft | Qt::AlignVCenter);
|
|
|
|
grid->addWidget(veloheropassLabel, ++row, 0);
|
|
grid->addWidget(veloHeroPass, row, 1, Qt::AlignLeft | Qt::AlignVCenter);
|
|
|
|
|
|
//////////////////////////////////////////////////
|
|
// End of grid
|
|
|
|
grid->setColumnStretch(0,0);
|
|
grid->setColumnStretch(1,3);
|
|
|
|
main->setLayout(grid);
|
|
|
|
setFrameStyle(QFrame::NoFrame);
|
|
setWidgetResizable(true);
|
|
setWidget(main);
|
|
|
|
}
|
|
|
|
#if QT_VERSION >= 0x050000 // only in QT5 or higher
|
|
void CredentialsPage::chooseDropboxFolder()
|
|
{
|
|
Dropbox dropbox(context);
|
|
|
|
// open the connection
|
|
QStringList errors;
|
|
if (dropbox.open(errors) == false) {
|
|
QMessageBox err;
|
|
err.setText(tr("Dropbox Connection Failed"));
|
|
err.setDetailedText(errors.join("\n\n"));
|
|
err.setIcon(QMessageBox::Warning);
|
|
err.exec();
|
|
return;
|
|
}
|
|
|
|
// did the user type something ?
|
|
QString path = dropboxFolder->text();
|
|
if (path == "") path = appsettings->cvalue(context->athlete->cyclist, GC_DROPBOX_FOLDER, "/").toString();
|
|
|
|
FileStoreDialog dialog(this, &dropbox, tr("Choose Athlete Directory"), path, true);
|
|
int ret = dialog.exec();
|
|
|
|
// did we actually select something?
|
|
if (ret == QDialog::Accepted) dropboxFolder->setText(dialog.pathnameSelected());
|
|
}
|
|
#endif
|
|
|
|
|
|
void CredentialsPage::chooseLocalFileStoreFolder()
|
|
{
|
|
// did the user type something ? if not, get it from the Settings
|
|
QString path = networkFileStoreFolder->text();
|
|
if (path == "") path = appsettings->cvalue(context->athlete->cyclist, GC_NETWORKFILESTORE_FOLDER, "").toString();
|
|
QString dir = QFileDialog::getExistingDirectory(this, tr("Choose Shared Local Folder Athlete Directory"),
|
|
path, QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
|
|
if (dir != "") networkFileStoreFolder->setText(dir); //only overwrite current dir, if a new was selected
|
|
|
|
}
|
|
|
|
|
|
#ifdef GC_HAVE_KQOAUTH
|
|
void CredentialsPage::authoriseTwitter()
|
|
{
|
|
OAuthDialog *oauthDialog = new OAuthDialog(context, OAuthDialog::TWITTER);
|
|
if (oauthDialog->sslLibMissing()) {
|
|
delete oauthDialog;
|
|
} else {
|
|
oauthDialog->setWindowModality(Qt::ApplicationModal);
|
|
oauthDialog->exec();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if QT_VERSION >= 0x050000 // only in QT5 or higher
|
|
void CredentialsPage::authoriseDropbox()
|
|
{
|
|
OAuthDialog *oauthDialog = new OAuthDialog(context, OAuthDialog::DROPBOX);
|
|
if (oauthDialog->sslLibMissing()) {
|
|
delete oauthDialog;
|
|
} else {
|
|
oauthDialog->setWindowModality(Qt::ApplicationModal);
|
|
oauthDialog->exec();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
void CredentialsPage::authoriseStrava()
|
|
{
|
|
OAuthDialog *oauthDialog = new OAuthDialog(context, OAuthDialog::STRAVA);
|
|
if (oauthDialog->sslLibMissing()) {
|
|
delete oauthDialog;
|
|
} else {
|
|
oauthDialog->setWindowModality(Qt::ApplicationModal);
|
|
oauthDialog->exec();
|
|
}
|
|
}
|
|
|
|
void CredentialsPage::authoriseCyclingAnalytics()
|
|
{
|
|
OAuthDialog *oauthDialog = new OAuthDialog(context, OAuthDialog::CYCLING_ANALYTICS);
|
|
if (oauthDialog->sslLibMissing()) {
|
|
delete oauthDialog;
|
|
} else {
|
|
oauthDialog->setWindowModality(Qt::ApplicationModal);
|
|
oauthDialog->exec();
|
|
}
|
|
}
|
|
|
|
void CredentialsPage::authoriseGoogleCalendar()
|
|
{
|
|
OAuthDialog *oauthDialog = new OAuthDialog(context, OAuthDialog::GOOGLE_CALENDAR);
|
|
if (oauthDialog->sslLibMissing()) {
|
|
delete oauthDialog;
|
|
} else {
|
|
oauthDialog->setWindowModality(Qt::ApplicationModal);
|
|
oauthDialog->exec();
|
|
}
|
|
}
|
|
|
|
void CredentialsPage::dvCALDAVTypeChanged(int type)
|
|
{
|
|
if (type == 0) {
|
|
// normal CALDAV selected
|
|
googleCalendarAuthorise->setEnabled(false);
|
|
dvGoogleCalid->setEnabled(false);
|
|
dvURL->setEnabled(true);
|
|
dvUser->setEnabled(true);
|
|
dvPass->setEnabled(true);
|
|
} else {
|
|
// Google Selected
|
|
googleCalendarAuthorise->setEnabled(true);
|
|
dvGoogleCalid->setEnabled(true);
|
|
dvURL->setEnabled(false);
|
|
dvUser->setEnabled(false);
|
|
dvPass->setEnabled(false);
|
|
}
|
|
// set always inactive if authorization process not feasible
|
|
#ifndef GC_GOOGLE_CALENDAR_CLIENT_SECRET
|
|
googleCalendarAuthorise->setEnabled(false);
|
|
#endif
|
|
|
|
}
|
|
|
|
qint32
|
|
CredentialsPage::saveClicked()
|
|
{
|
|
appsettings->setCValue(context->athlete->cyclist, GC_TPUSER, tpUser->text());
|
|
appsettings->setCValue(context->athlete->cyclist, GC_TPPASS, tpPass->text());
|
|
appsettings->setCValue(context->athlete->cyclist, GC_RWGPSUSER, rideWithGPSUser->text());
|
|
appsettings->setCValue(context->athlete->cyclist, GC_RWGPSPASS, rideWithGPSPass->text());
|
|
appsettings->setCValue(context->athlete->cyclist, GC_TTBUSER, ttbUser->text());
|
|
appsettings->setCValue(context->athlete->cyclist, GC_TTBPASS, ttbPass->text());
|
|
appsettings->setCValue(context->athlete->cyclist, GC_VELOHEROUSER, veloHeroUser->text());
|
|
appsettings->setCValue(context->athlete->cyclist, GC_VELOHEROPASS, veloHeroPass->text());
|
|
appsettings->setCValue(context->athlete->cyclist, GC_SELUSER, selUser->text());
|
|
appsettings->setCValue(context->athlete->cyclist, GC_SELPASS, selPass->text());
|
|
appsettings->setCValue(context->athlete->cyclist, GC_TPTYPE, tpType->currentIndex());
|
|
appsettings->setCValue(context->athlete->cyclist, GC_WIURL, wiURL->text());
|
|
appsettings->setCValue(context->athlete->cyclist, GC_WIUSER, wiUser->text());
|
|
appsettings->setCValue(context->athlete->cyclist, GC_WIKEY, wiPass->text());
|
|
appsettings->setCValue(context->athlete->cyclist, GC_WEBCAL_URL, webcalURL->text());
|
|
appsettings->setCValue(context->athlete->cyclist, GC_WEBCAL_URL, webcalURL->text());
|
|
#if QT_VERSION >= 0x050000 // only in QT5 or higher
|
|
appsettings->setCValue(context->athlete->cyclist, GC_DROPBOX_FOLDER, dropboxFolder->text());
|
|
#endif
|
|
appsettings->setCValue(context->athlete->cyclist, GC_NETWORKFILESTORE_FOLDER, networkFileStoreFolder->text());
|
|
|
|
// escape the at character
|
|
QString url = dvURL->text();
|
|
url.replace("@", "%40");
|
|
appsettings->setCValue(context->athlete->cyclist, GC_DVURL, url);
|
|
appsettings->setCValue(context->athlete->cyclist, GC_DVUSER, dvUser->text());
|
|
appsettings->setCValue(context->athlete->cyclist, GC_DVPASS, dvPass->text());
|
|
appsettings->setCValue(context->athlete->cyclist, GC_DVGOOGLE_CALID, dvGoogleCalid->text());
|
|
appsettings->setCValue(context->athlete->cyclist, GC_DVCALDAVTYPE, dvCALDAVType->currentIndex());
|
|
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// About me
|
|
//
|
|
AboutRiderPage::AboutRiderPage(QWidget *parent, Context *context) : QWidget(parent), context(context)
|
|
{
|
|
QVBoxLayout *all = new QVBoxLayout(this);
|
|
QGridLayout *grid = new QGridLayout;
|
|
#ifdef Q_OS_MAX
|
|
setContentsMargins(10,10,10,10);
|
|
grid->setSpacing(5);
|
|
all->setSpacing(5);
|
|
#endif
|
|
|
|
QLabel *nicklabel = new QLabel(tr("Nickname"));
|
|
QLabel *doblabel = new QLabel(tr("Date of Birth"));
|
|
QLabel *sexlabel = new QLabel(tr("Sex"));
|
|
|
|
nickname = new QLineEdit(this);
|
|
nickname->setText(appsettings->cvalue(context->athlete->cyclist, GC_NICKNAME, "").toString());
|
|
if (nickname->text() == "0") nickname->setText("");
|
|
|
|
dob = new QDateEdit(this);
|
|
dob->setDate(appsettings->cvalue(context->athlete->cyclist, GC_DOB).toDate());
|
|
|
|
sex = new QComboBox(this);
|
|
sex->addItem(tr("Male"));
|
|
sex->addItem(tr("Female"));
|
|
|
|
|
|
// we set to 0 or 1 for male or female since this
|
|
// is language independent (and for once the code is easier!)
|
|
sex->setCurrentIndex(appsettings->cvalue(context->athlete->cyclist, GC_SEX).toInt());
|
|
|
|
if (QFileInfo(context->athlete->home->config().canonicalPath() + "/" + "avatar.png").exists())
|
|
avatar = QPixmap(context->athlete->home->config().canonicalPath() + "/" + "avatar.png");
|
|
else
|
|
avatar = QPixmap(":/images/noavatar.png");
|
|
|
|
avatarButton = new QPushButton(this);
|
|
avatarButton->setContentsMargins(0,0,0,0);
|
|
avatarButton->setFlat(true);
|
|
avatarButton->setIcon(avatar.scaled(140,140));
|
|
avatarButton->setIconSize(QSize(140,140));
|
|
avatarButton->setFixedHeight(140);
|
|
avatarButton->setFixedWidth(140);
|
|
|
|
//
|
|
// Crank length - only used by PfPv chart (should move there!)
|
|
//
|
|
QLabel *crankLengthLabel = new QLabel(tr("Crank Length"));
|
|
QVariant crankLength = appsettings->cvalue(context->athlete->cyclist, GC_CRANKLENGTH);
|
|
crankLengthCombo = new QComboBox();
|
|
crankLengthCombo->addItem("150");
|
|
crankLengthCombo->addItem("155");
|
|
crankLengthCombo->addItem("160");
|
|
crankLengthCombo->addItem("162.5");
|
|
crankLengthCombo->addItem("165");
|
|
crankLengthCombo->addItem("167.5");
|
|
crankLengthCombo->addItem("170");
|
|
crankLengthCombo->addItem("172.5");
|
|
crankLengthCombo->addItem("175");
|
|
crankLengthCombo->addItem("177.5");
|
|
crankLengthCombo->addItem("180");
|
|
crankLengthCombo->addItem("182.5");
|
|
crankLengthCombo->addItem("185");
|
|
if(crankLength.toString() == "150") crankLengthCombo->setCurrentIndex(0);
|
|
if(crankLength.toString() == "155") crankLengthCombo->setCurrentIndex(1);
|
|
if(crankLength.toString() == "160") crankLengthCombo->setCurrentIndex(2);
|
|
if(crankLength.toString() == "162.5") crankLengthCombo->setCurrentIndex(3);
|
|
if(crankLength.toString() == "165") crankLengthCombo->setCurrentIndex(4);
|
|
if(crankLength.toString() == "167.5") crankLengthCombo->setCurrentIndex(5);
|
|
if(crankLength.toString() == "170") crankLengthCombo->setCurrentIndex(6);
|
|
if(crankLength.toString() == "172.5") crankLengthCombo->setCurrentIndex(7);
|
|
if(crankLength.toString() == "175") crankLengthCombo->setCurrentIndex(8);
|
|
if(crankLength.toString() == "177.5") crankLengthCombo->setCurrentIndex(9);
|
|
if(crankLength.toString() == "180") crankLengthCombo->setCurrentIndex(10);
|
|
if(crankLength.toString() == "182.5") crankLengthCombo->setCurrentIndex(11);
|
|
if(crankLength.toString() == "185") crankLengthCombo->setCurrentIndex(12);
|
|
|
|
//
|
|
// Wheel size
|
|
//
|
|
QLabel *wheelSizeLabel = new QLabel(tr("Wheelsize"), this);
|
|
int wheelSize = appsettings->cvalue(context->athlete->cyclist, GC_WHEELSIZE, 2100).toInt();
|
|
|
|
rimSizeCombo = new QComboBox();
|
|
rimSizeCombo->addItems(WheelSize::RIM_SIZES);
|
|
|
|
tireSizeCombo = new QComboBox();
|
|
tireSizeCombo->addItems(WheelSize::TIRE_SIZES);
|
|
|
|
|
|
wheelSizeEdit = new QLineEdit(QString("%1").arg(wheelSize),this);
|
|
wheelSizeEdit->setInputMask("0000");
|
|
wheelSizeEdit->setFixedWidth(40);
|
|
|
|
QLabel *wheelSizeUnitLabel = new QLabel(tr("mm"), this);
|
|
|
|
QHBoxLayout *wheelSizeLayout = new QHBoxLayout();
|
|
wheelSizeLayout->addWidget(rimSizeCombo);
|
|
wheelSizeLayout->addWidget(tireSizeCombo);
|
|
wheelSizeLayout->addWidget(wheelSizeEdit);
|
|
wheelSizeLayout->addWidget(wheelSizeUnitLabel);
|
|
|
|
connect(rimSizeCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(calcWheelSize()));
|
|
connect(tireSizeCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(calcWheelSize()));
|
|
connect(wheelSizeEdit, SIGNAL(textEdited(QString)), this, SLOT(resetWheelSize()));
|
|
|
|
|
|
//
|
|
// Auto Backup
|
|
//
|
|
// Selecting the storage folder folder of the Local File Store
|
|
QLabel *autoBackupFolderLabel = new QLabel(tr("Auto Backup Folder"));
|
|
autoBackupFolder = new QLineEdit(this);
|
|
autoBackupFolder->setText(appsettings->cvalue(context->athlete->cyclist, GC_AUTOBACKUP_FOLDER, "").toString());
|
|
autoBackupFolderBrowse = new QPushButton(tr("Browse"));
|
|
connect(autoBackupFolderBrowse, SIGNAL(clicked()), this, SLOT(chooseAutoBackupFolder()));
|
|
autoBackupPeriod = new QSpinBox(this);
|
|
autoBackupPeriod->setMinimum(0);
|
|
autoBackupPeriod->setMaximum(9999);
|
|
autoBackupPeriod->setSingleStep(1);
|
|
QLabel *autoBackupPeriodLabel = new QLabel(tr("Auto Backup Period"));
|
|
autoBackupPeriod->setValue(appsettings->cvalue(context->athlete->cyclist, GC_AUTOBACKUP_PERIOD, 0).toInt());
|
|
|
|
|
|
Qt::Alignment alignment = Qt::AlignLeft|Qt::AlignVCenter;
|
|
|
|
grid->addWidget(nicklabel, 0, 0, alignment);
|
|
grid->addWidget(doblabel, 1, 0, alignment);
|
|
grid->addWidget(sexlabel, 2, 0, alignment);
|
|
|
|
grid->addWidget(nickname, 0, 1, alignment);
|
|
grid->addWidget(dob, 1, 1, alignment);
|
|
grid->addWidget(sex, 2, 1, alignment);
|
|
|
|
grid->addWidget(crankLengthLabel, 4, 0, alignment);
|
|
grid->addWidget(crankLengthCombo, 4, 1, alignment);
|
|
grid->addWidget(wheelSizeLabel, 5, 0, alignment);
|
|
grid->addLayout(wheelSizeLayout, 5, 1, 1, 2, alignment);
|
|
grid->addWidget(autoBackupFolderLabel, 7,0, alignment);
|
|
grid->addWidget(autoBackupFolder, 7, 1, alignment);
|
|
grid->addWidget(autoBackupFolderBrowse, 7, 2, alignment);
|
|
grid->addWidget(autoBackupPeriodLabel, 8, 0,alignment);
|
|
grid->addWidget(autoBackupPeriod, 8, 1, alignment);
|
|
|
|
grid->addWidget(avatarButton, 0, 1, 4, 2, Qt::AlignRight|Qt::AlignVCenter);
|
|
all->addLayout(grid);
|
|
all->addStretch();
|
|
|
|
// save initial values for things we care about
|
|
// note we don't worry about age or sex at this point
|
|
// since they are not used, nor the W'bal tau used in
|
|
// the realtime code. This is limited to stuff we
|
|
// care about tracking as it is used by metrics
|
|
|
|
// height and weight as stored (always metric)
|
|
b4.wheel = wheelSize;
|
|
b4.crank = crankLengthCombo->currentIndex();
|
|
|
|
connect (avatarButton, SIGNAL(clicked()), this, SLOT(chooseAvatar()));
|
|
}
|
|
|
|
void
|
|
AboutRiderPage::chooseAvatar()
|
|
{
|
|
QString filename = QFileDialog::getOpenFileName(this, tr("Choose Picture"),
|
|
"", tr("Images (*.png *.jpg *.bmp)"));
|
|
if (filename != "") {
|
|
|
|
avatar = QPixmap(filename);
|
|
avatarButton->setIcon(avatar.scaled(140,140));
|
|
avatarButton->setIconSize(QSize(140,140));
|
|
}
|
|
}
|
|
|
|
void AboutRiderPage::chooseAutoBackupFolder()
|
|
{
|
|
// did the user type something ? if not, get it from the Settings
|
|
QString path = autoBackupFolder->text();
|
|
if (path == "") path = appsettings->cvalue(context->athlete->cyclist, GC_AUTOBACKUP_FOLDER, "").toString();
|
|
QString dir = QFileDialog::getExistingDirectory(this, tr("Choose Backup Directory"),
|
|
path, QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
|
|
if (dir != "") autoBackupFolder->setText(dir); //only overwrite current dir, if a new was selected
|
|
|
|
}
|
|
|
|
void
|
|
RiderPhysPage::unitChanged(int currentIndex)
|
|
{
|
|
if (currentIndex == 0) {
|
|
QString weighttext = QString(tr("Weight (%1)")).arg(tr("kg"));
|
|
weightlabel->setText(weighttext);
|
|
weight->setValue(weight->value() / LB_PER_KG);
|
|
|
|
QString heighttext = QString(tr("Height (%1)")).arg(tr("cm"));
|
|
heightlabel->setText(heighttext);
|
|
height->setValue(height->value() * CM_PER_INCH);
|
|
}
|
|
else {
|
|
QString weighttext = QString(tr("Weight (%1)")).arg(tr("lb"));
|
|
weightlabel->setText(weighttext);
|
|
weight->setValue(weight->value() * LB_PER_KG);
|
|
|
|
QString heighttext = QString(tr("Height (%1)")).arg(tr("in"));
|
|
heightlabel->setText(heighttext);
|
|
height->setValue(height->value() / CM_PER_INCH);
|
|
}
|
|
}
|
|
|
|
void
|
|
AboutRiderPage::calcWheelSize()
|
|
{
|
|
int diameter = WheelSize::calcPerimeter(rimSizeCombo->currentIndex(), tireSizeCombo->currentIndex());
|
|
if (diameter>0)
|
|
wheelSizeEdit->setText(QString("%1").arg(diameter));
|
|
}
|
|
|
|
void
|
|
AboutRiderPage::resetWheelSize()
|
|
{
|
|
rimSizeCombo->setCurrentIndex(0);
|
|
tireSizeCombo->setCurrentIndex(0);
|
|
}
|
|
|
|
qint32
|
|
AboutRiderPage::saveClicked()
|
|
{
|
|
appsettings->setCValue(context->athlete->cyclist, GC_NICKNAME, nickname->text());
|
|
appsettings->setCValue(context->athlete->cyclist, GC_DOB, dob->date());
|
|
|
|
|
|
appsettings->setCValue(context->athlete->cyclist, GC_SEX, sex->currentIndex());
|
|
avatar.save(context->athlete->home->config().canonicalPath() + "/" + "avatar.png", "PNG");
|
|
|
|
appsettings->setCValue(context->athlete->cyclist, GC_CRANKLENGTH, crankLengthCombo->currentText());
|
|
appsettings->setCValue(context->athlete->cyclist, GC_WHEELSIZE, wheelSizeEdit->text().toInt());
|
|
|
|
// Auto Backup
|
|
appsettings->setCValue(context->athlete->cyclist, GC_AUTOBACKUP_FOLDER, autoBackupFolder->text());
|
|
appsettings->setCValue(context->athlete->cyclist, GC_AUTOBACKUP_PERIOD, autoBackupPeriod->value());
|
|
|
|
|
|
qint32 state=0;
|
|
|
|
// general stuff changed ?
|
|
if (b4.wheel != wheelSizeEdit->text().toInt() ||
|
|
b4.crank != crankLengthCombo->currentIndex() )
|
|
state += CONFIG_GENERAL;
|
|
|
|
return state;
|
|
}
|
|
|
|
//
|
|
// About me - physiology settings
|
|
//
|
|
RiderPhysPage::RiderPhysPage(QWidget *parent, Context *context) : QWidget(parent), context(context)
|
|
{
|
|
QVBoxLayout *all = new QVBoxLayout(this);
|
|
QGridLayout *grid = new QGridLayout;
|
|
#ifdef Q_OS_MAX
|
|
setContentsMargins(10,10,10,10);
|
|
grid->setSpacing(5);
|
|
all->setSpacing(5);
|
|
#endif
|
|
|
|
QString weighttext = QString(tr("Weight (%1)")).arg(context->athlete->useMetricUnits ? tr("kg") : tr("lb"));
|
|
weightlabel = new QLabel(weighttext);
|
|
|
|
QString heighttext = QString(tr("Height (%1)")).arg(context->athlete->useMetricUnits ? tr("cm") : tr("in"));
|
|
heightlabel = new QLabel(heighttext);
|
|
|
|
wbaltaulabel = new QLabel(tr("W'bal tau (s)"));
|
|
|
|
weight = new QDoubleSpinBox(this);
|
|
weight->setMaximum(999.9);
|
|
weight->setMinimum(0.0);
|
|
weight->setDecimals(1);
|
|
weight->setValue(appsettings->cvalue(context->athlete->cyclist, GC_WEIGHT).toDouble() * (context->athlete->useMetricUnits ? 1.0 : LB_PER_KG));
|
|
|
|
height = new QDoubleSpinBox(this);
|
|
height->setMaximum(999.9);
|
|
height->setMinimum(0.0);
|
|
height->setDecimals(1);
|
|
height->setValue(appsettings->cvalue(context->athlete->cyclist, GC_HEIGHT).toDouble() * (context->athlete->useMetricUnits ? 100.0 : 100.0/CM_PER_INCH));
|
|
|
|
wbaltau = new QSpinBox(this);
|
|
wbaltau->setMinimum(30);
|
|
wbaltau->setMaximum(1200);
|
|
wbaltau->setSingleStep(10);
|
|
wbaltau->setValue(appsettings->cvalue(context->athlete->cyclist, GC_WBALTAU, 300).toInt());
|
|
|
|
//
|
|
// Performance manager
|
|
//
|
|
|
|
perfManSTSLabel = new QLabel(tr("STS average (days)"));
|
|
perfManLTSLabel = new QLabel(tr("LTS average (days)"));
|
|
perfManSTSavgValidator = new QIntValidator(1,21,this);
|
|
perfManLTSavgValidator = new QIntValidator(7,56,this);
|
|
|
|
// get config or set to defaults
|
|
QVariant perfManSTSVal = appsettings->cvalue(context->athlete->cyclist, GC_STS_DAYS);
|
|
if (perfManSTSVal.isNull() || perfManSTSVal.toInt() == 0) perfManSTSVal = 7;
|
|
QVariant perfManLTSVal = appsettings->cvalue(context->athlete->cyclist, GC_LTS_DAYS);
|
|
if (perfManLTSVal.isNull() || perfManLTSVal.toInt() == 0) perfManLTSVal = 42;
|
|
|
|
perfManSTSavg = new QLineEdit(perfManSTSVal.toString(),this);
|
|
perfManSTSavg->setValidator(perfManSTSavgValidator);
|
|
perfManLTSavg = new QLineEdit(perfManLTSVal.toString(),this);
|
|
perfManLTSavg->setValidator(perfManLTSavgValidator);
|
|
|
|
showSBToday = new QCheckBox(tr("PMC Stress Balance Today"), this);
|
|
showSBToday->setChecked(appsettings->cvalue(context->athlete->cyclist, GC_SB_TODAY).toInt());
|
|
|
|
Qt::Alignment alignment = Qt::AlignLeft|Qt::AlignVCenter;
|
|
|
|
grid->addWidget(weightlabel, 1, 0, alignment);
|
|
grid->addWidget(heightlabel, 2, 0, alignment);
|
|
grid->addWidget(wbaltaulabel, 3, 0, alignment);
|
|
|
|
grid->addWidget(weight, 1, 1, alignment);
|
|
grid->addWidget(height, 2, 1, alignment);
|
|
grid->addWidget(wbaltau, 3, 1, alignment);
|
|
|
|
|
|
grid->addWidget(perfManSTSLabel, 4, 0, alignment);
|
|
grid->addWidget(perfManSTSavg, 4, 1, alignment);
|
|
grid->addWidget(perfManLTSLabel, 5, 0, alignment);
|
|
grid->addWidget(perfManLTSavg, 5, 1, alignment);
|
|
grid->addWidget(showSBToday, 6, 1, alignment);
|
|
|
|
all->addLayout(grid);
|
|
all->addStretch();
|
|
|
|
// save initial values for things we care about
|
|
// note we don't worry about age or sex at this point
|
|
// since they are not used, nor the W'bal tau used in
|
|
// the realtime code. This is limited to stuff we
|
|
// care about tracking as it is used by metrics
|
|
|
|
// height and weight as stored (always metric)
|
|
b4.height = appsettings->cvalue(context->athlete->cyclist, GC_HEIGHT).toDouble();
|
|
b4.weight = appsettings->cvalue(context->athlete->cyclist, GC_WEIGHT).toDouble();
|
|
b4.lts = perfManLTSVal.toInt();
|
|
b4.sts = perfManSTSVal.toInt();
|
|
}
|
|
|
|
qint32
|
|
RiderPhysPage::saveClicked()
|
|
{
|
|
appsettings->setCValue(context->athlete->cyclist, GC_WEIGHT, weight->value() * (context->athlete->useMetricUnits ? 1.0 : KG_PER_LB));
|
|
appsettings->setCValue(context->athlete->cyclist, GC_HEIGHT, height->value() * (context->athlete->useMetricUnits ? 1.0/100.0 : CM_PER_INCH/100.0));
|
|
appsettings->setCValue(context->athlete->cyclist, GC_WBALTAU, wbaltau->value());
|
|
|
|
|
|
// Performance Manager
|
|
appsettings->setCValue(context->athlete->cyclist, GC_STS_DAYS, perfManSTSavg->text());
|
|
appsettings->setCValue(context->athlete->cyclist, GC_LTS_DAYS, perfManLTSavg->text());
|
|
appsettings->setCValue(context->athlete->cyclist, GC_SB_TODAY, (int) showSBToday->isChecked());
|
|
|
|
qint32 state=0;
|
|
|
|
// PMC constants changed ?
|
|
if(b4.lts != perfManLTSavg->text().toInt() ||
|
|
b4.sts != perfManSTSavg->text().toInt())
|
|
state += CONFIG_PMC;
|
|
|
|
// default height or weight changed ?
|
|
if (b4.height != appsettings->cvalue(context->athlete->cyclist, GC_HEIGHT).toDouble() ||
|
|
b4.weight != appsettings->cvalue(context->athlete->cyclist, GC_WEIGHT).toDouble()) {
|
|
state += CONFIG_ATHLETE;
|
|
}
|
|
|
|
return state;
|
|
}
|
|
|
|
//
|
|
// Realtime devices page
|
|
//
|
|
DevicePage::DevicePage(QWidget *parent, Context *context) : QWidget(parent), context(context)
|
|
{
|
|
QVBoxLayout *mainLayout = new QVBoxLayout(this);
|
|
|
|
DeviceTypes all;
|
|
devices = all.getList();
|
|
|
|
addButton = new QPushButton(tr("+"),this);
|
|
delButton = new QPushButton(tr("-"),this);
|
|
|
|
deviceList = new QTableView(this);
|
|
#ifdef Q_OS_MAC
|
|
addButton->setText(tr("Add"));
|
|
delButton->setText(tr("Delete"));
|
|
deviceList->setAttribute(Qt::WA_MacShowFocusRect, 0);
|
|
#else
|
|
addButton->setFixedSize(20,20);
|
|
delButton->setFixedSize(20,20);
|
|
#endif
|
|
deviceListModel = new deviceModel(this);
|
|
|
|
// replace standard model with ours
|
|
QItemSelectionModel *stdmodel = deviceList->selectionModel();
|
|
deviceList->setModel(deviceListModel);
|
|
delete stdmodel;
|
|
|
|
deviceList->setSortingEnabled(false);
|
|
deviceList->setSelectionBehavior(QAbstractItemView::SelectRows);
|
|
deviceList->horizontalHeader()->setStretchLastSection(true);
|
|
deviceList->verticalHeader()->hide();
|
|
deviceList->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
|
deviceList->setSelectionMode(QAbstractItemView::SingleSelection);
|
|
|
|
multiCheck = new QCheckBox(tr("Allow multiple devices in Train View"), this);
|
|
multiCheck->setChecked(appsettings->value(this, TRAIN_MULTI, false).toBool());
|
|
|
|
mainLayout->addWidget(deviceList);
|
|
QHBoxLayout *bottom = new QHBoxLayout;
|
|
bottom->setSpacing(2);
|
|
bottom->addWidget(multiCheck);
|
|
bottom->addStretch();
|
|
bottom->addWidget(addButton);
|
|
bottom->addWidget(delButton);
|
|
mainLayout->addLayout(bottom);
|
|
|
|
connect(addButton, SIGNAL(clicked()), this, SLOT(devaddClicked()));
|
|
connect(delButton, SIGNAL(clicked()), this, SLOT(devdelClicked()));
|
|
connect(context, SIGNAL(configChanged(qint32)), deviceListModel, SLOT(doReset()));
|
|
}
|
|
|
|
qint32
|
|
DevicePage::saveClicked()
|
|
{
|
|
// Save the device configuration...
|
|
DeviceConfigurations all;
|
|
all.writeConfig(deviceListModel->Configuration);
|
|
appsettings->setValue(TRAIN_MULTI, multiCheck->isChecked());
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
DevicePage::devaddClicked()
|
|
{
|
|
DeviceConfiguration add;
|
|
AddDeviceWizard *p = new AddDeviceWizard(context);
|
|
p->show();
|
|
}
|
|
|
|
void
|
|
DevicePage::devdelClicked()
|
|
{
|
|
deviceListModel->del();
|
|
}
|
|
|
|
// add a new configuration
|
|
void
|
|
deviceModel::add(DeviceConfiguration &newone)
|
|
{
|
|
insertRows(0,1, QModelIndex());
|
|
|
|
// insert name
|
|
QModelIndex index = deviceModel::index(0,0, QModelIndex());
|
|
setData(index, newone.name, Qt::EditRole);
|
|
|
|
// insert type
|
|
index = deviceModel::index(0,1, QModelIndex());
|
|
setData(index, newone.type, Qt::EditRole);
|
|
|
|
// insert portSpec
|
|
index = deviceModel::index(0,2, QModelIndex());
|
|
setData(index, newone.portSpec, Qt::EditRole);
|
|
|
|
// insert Profile
|
|
index = deviceModel::index(0,3, QModelIndex());
|
|
setData(index, newone.deviceProfile, Qt::EditRole);
|
|
|
|
// insert postProcess
|
|
index = deviceModel::index(0,4, QModelIndex());
|
|
setData(index, newone.postProcess, Qt::EditRole);
|
|
}
|
|
|
|
// delete an existing configuration
|
|
void
|
|
deviceModel::del()
|
|
{
|
|
// which row is selected in the table?
|
|
DevicePage *temp = static_cast<DevicePage*>(parent);
|
|
QItemSelectionModel *selectionModel = temp->deviceList->selectionModel();
|
|
|
|
QModelIndexList indexes = selectionModel->selectedRows();
|
|
QModelIndex index;
|
|
|
|
foreach (index, indexes) {
|
|
//int row = this->mapToSource(index).row();
|
|
removeRows(index.row(), 1, QModelIndex());
|
|
}
|
|
}
|
|
|
|
deviceModel::deviceModel(QObject *parent) : QAbstractTableModel(parent)
|
|
{
|
|
this->parent = parent;
|
|
|
|
// get current configuration
|
|
DeviceConfigurations all;
|
|
Configuration = all.getList();
|
|
}
|
|
|
|
void
|
|
deviceModel::doReset()
|
|
{
|
|
beginResetModel();
|
|
DeviceConfigurations all;
|
|
Configuration = all.getList();
|
|
endResetModel();
|
|
}
|
|
|
|
int
|
|
deviceModel::rowCount(const QModelIndex &parent) const
|
|
{
|
|
Q_UNUSED(parent);
|
|
return Configuration.size();
|
|
}
|
|
|
|
int
|
|
deviceModel::columnCount(const QModelIndex &) const
|
|
{
|
|
return 5;
|
|
}
|
|
|
|
|
|
// setup the headings!
|
|
QVariant deviceModel::headerData(int section, Qt::Orientation orientation, int role) const
|
|
{
|
|
if (role != Qt::DisplayRole) return QVariant(); // no display, no game!
|
|
|
|
if (orientation == Qt::Horizontal) {
|
|
switch (section) {
|
|
case 0:
|
|
return tr("Device Name");
|
|
case 1:
|
|
return tr("Device Type");
|
|
case 2:
|
|
return tr("Port Spec");
|
|
case 3:
|
|
return tr("Profile");
|
|
case 4:
|
|
return tr("Virtual");
|
|
default:
|
|
return QVariant();
|
|
}
|
|
}
|
|
return QVariant();
|
|
}
|
|
|
|
// return data item for row/col specified in index
|
|
QVariant deviceModel::data(const QModelIndex &index, int role) const
|
|
{
|
|
if (!index.isValid()) return QVariant();
|
|
if (index.row() >= Configuration.size() || index.row() < 0) return QVariant();
|
|
|
|
if (role == Qt::DisplayRole) {
|
|
DeviceConfiguration Entry = Configuration.at(index.row());
|
|
|
|
switch(index.column()) {
|
|
case 0 : return Entry.name;
|
|
break;
|
|
case 1 :
|
|
{
|
|
DeviceTypes all;
|
|
DeviceType lookupType = all.getType (Entry.type);
|
|
return lookupType.name;
|
|
}
|
|
break;
|
|
case 2 :
|
|
return Entry.portSpec;
|
|
break;
|
|
case 3 :
|
|
return Entry.deviceProfile;
|
|
case 4 :
|
|
return Entry.postProcess;
|
|
}
|
|
}
|
|
|
|
// how did we get here!?
|
|
return QVariant();
|
|
}
|
|
|
|
// update the model with new data
|
|
bool deviceModel::insertRows(int position, int rows, const QModelIndex &index)
|
|
{
|
|
Q_UNUSED(index);
|
|
beginInsertRows(QModelIndex(), position, position+rows-1);
|
|
|
|
for (int row=0; row < rows; row++) {
|
|
DeviceConfiguration emptyEntry;
|
|
Configuration.insert(position, emptyEntry);
|
|
}
|
|
endInsertRows();
|
|
return true;
|
|
}
|
|
|
|
// delete a row!
|
|
bool deviceModel::removeRows(int position, int rows, const QModelIndex &index)
|
|
{
|
|
Q_UNUSED(index);
|
|
beginRemoveRows(QModelIndex(), position, position+rows-1);
|
|
|
|
for (int row=0; row < rows; ++row) {
|
|
Configuration.removeAt(position);
|
|
}
|
|
|
|
endRemoveRows();
|
|
return true;
|
|
}
|
|
|
|
bool deviceModel::setData(const QModelIndex &index, const QVariant &value, int role)
|
|
{
|
|
if (index.isValid() && role == Qt::EditRole) {
|
|
int row = index.row();
|
|
|
|
DeviceConfiguration p = Configuration.value(row);
|
|
|
|
switch (index.column()) {
|
|
case 0 : //name
|
|
p.name = value.toString();
|
|
break;
|
|
case 1 : //type
|
|
p.type = value.toInt();
|
|
break;
|
|
case 2 : // spec
|
|
p.portSpec = value.toString();
|
|
break;
|
|
case 3 : // Profile
|
|
p.deviceProfile = value.toString();
|
|
break;
|
|
case 4 : // Profile
|
|
p.postProcess = value.toInt();
|
|
break;
|
|
}
|
|
Configuration.replace(row,p);
|
|
emit(dataChanged(index, index));
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static void setSizes(QComboBox *p)
|
|
{
|
|
#ifdef Q_OS_MAC
|
|
p->addItem("7");
|
|
p->addItem("9");
|
|
p->addItem("11");
|
|
p->addItem("13");
|
|
p->addItem("15");
|
|
p->addItem("17");
|
|
p->addItem("19");
|
|
p->addItem("21");
|
|
#else
|
|
p->addItem("6");
|
|
p->addItem("8");
|
|
p->addItem("10");
|
|
p->addItem("12");
|
|
p->addItem("14");
|
|
p->addItem("16");
|
|
p->addItem("18");
|
|
p->addItem("20");
|
|
#endif
|
|
}
|
|
|
|
//
|
|
// Appearances page
|
|
//
|
|
ColorsPage::ColorsPage(QWidget *parent) : QWidget(parent)
|
|
{
|
|
QVBoxLayout *mainLayout = new QVBoxLayout(this);
|
|
|
|
// chrome style -- metal or flat currently offered
|
|
QLabel *chromeLabel = new QLabel(tr("Styling" ));
|
|
chromeCombo = new QComboBox(this);
|
|
chromeCombo->addItem(tr("Metallic (Mac)"));
|
|
chromeCombo->addItem(tr("Flat Color (Windows)"));
|
|
#ifdef Q_OS_MAC // gets reset on yosemite anyway
|
|
QString chrome = appsettings->value(this, GC_CHROME, "Mac").toString();
|
|
#else
|
|
QString chrome = appsettings->value(this, GC_CHROME, "Flat").toString();
|
|
#endif
|
|
if (chrome == "Mac") chromeCombo->setCurrentIndex(0);
|
|
if (chrome == "Flat") chromeCombo->setCurrentIndex(1);
|
|
|
|
themes = new QTreeWidget;
|
|
themes->headerItem()->setText(0, tr("Swatch"));
|
|
themes->headerItem()->setText(1, tr("Name"));
|
|
themes->setColumnCount(2);
|
|
themes->setColumnWidth(0,240);
|
|
themes->setSelectionMode(QAbstractItemView::SingleSelection);
|
|
//colors->setEditTriggers(QAbstractItemView::SelectedClicked); // allow edit
|
|
themes->setUniformRowHeights(true); // causes height problems when adding - in case of non-text fields
|
|
themes->setIndentation(0);
|
|
//colors->header()->resizeSection(0,300);
|
|
|
|
colors = new QTreeWidget;
|
|
colors->headerItem()->setText(0, tr("Color"));
|
|
colors->headerItem()->setText(1, tr("Select"));
|
|
colors->setColumnCount(2);
|
|
colors->setColumnWidth(0,350);
|
|
colors->setSelectionMode(QAbstractItemView::NoSelection);
|
|
//colors->setEditTriggers(QAbstractItemView::SelectedClicked); // allow edit
|
|
colors->setUniformRowHeights(true); // causes height problems when adding - in case of non-text fields
|
|
colors->setIndentation(0);
|
|
//colors->header()->resizeSection(0,300);
|
|
|
|
QLabel *antiAliasLabel = new QLabel(tr("Antialias"));
|
|
antiAliased = new QCheckBox;
|
|
antiAliased->setChecked(appsettings->value(this, GC_ANTIALIAS, true).toBool());
|
|
#ifndef Q_OS_MAC
|
|
QLabel *rideScrollLabel = new QLabel(tr("Activity Scrollbar"));
|
|
rideScroll = new QCheckBox;
|
|
rideScroll->setChecked(appsettings->value(this, GC_RIDESCROLL, true).toBool());
|
|
QLabel *rideHeadLabel = new QLabel(tr("Activity Headings"));
|
|
rideHead = new QCheckBox;
|
|
rideHead->setChecked(appsettings->value(this, GC_RIDEHEAD, true).toBool());
|
|
#endif
|
|
lineWidth = new QDoubleSpinBox;
|
|
lineWidth->setMaximum(5);
|
|
lineWidth->setMinimum(0.5);
|
|
lineWidth->setSingleStep(0.5);
|
|
applyTheme = new QPushButton(tr("Apply Theme"));
|
|
lineWidth->setValue(appsettings->value(this, GC_LINEWIDTH, 0.5).toDouble());
|
|
|
|
QLabel *lineWidthLabel = new QLabel(tr("Line Width"));
|
|
QLabel *defaultLabel = new QLabel(tr("Default"));
|
|
QLabel *titlesLabel = new QLabel(tr("Title" ));
|
|
QLabel *markerLabel = new QLabel(tr("Chart Markers" ));
|
|
QLabel *chartLabel = new QLabel(tr("Chart Labels" ));
|
|
QLabel *calendarLabel = new QLabel(tr("Calendar Text" ));
|
|
|
|
def = new QFontComboBox(this);
|
|
titles = new QFontComboBox(this);
|
|
chartmarkers = new QFontComboBox(this);
|
|
chartlabels = new QFontComboBox(this);
|
|
calendar = new QFontComboBox(this);
|
|
|
|
defaultSize = new QComboBox(this); setSizes(defaultSize);
|
|
titlesSize = new QComboBox(this); setSizes(titlesSize);
|
|
chartmarkersSize = new QComboBox(this); setSizes(chartmarkersSize);
|
|
chartlabelsSize = new QComboBox(this); setSizes(chartlabelsSize);
|
|
calendarSize = new QComboBox(this); setSizes(calendarSize);
|
|
|
|
// get round QTBUG
|
|
def->setCurrentIndex(0);
|
|
def->setCurrentIndex(1);
|
|
def->setCurrentFont(QFont());
|
|
titles->setCurrentIndex(0);
|
|
titles->setCurrentIndex(1);
|
|
titles->setCurrentFont(QFont());
|
|
chartmarkers->setCurrentIndex(0);
|
|
chartmarkers->setCurrentIndex(1);
|
|
chartmarkers->setCurrentFont(QFont());
|
|
chartlabels->setCurrentIndex(0);
|
|
chartlabels->setCurrentIndex(1);
|
|
chartlabels->setCurrentFont(QFont());
|
|
calendar->setCurrentIndex(0);
|
|
calendar->setCurrentIndex(1);
|
|
calendar->setCurrentFont(QFont());
|
|
|
|
QFont font;
|
|
|
|
font.fromString(appsettings->value(this, GC_FONT_DEFAULT, QFont().toString()).toString());
|
|
def->setCurrentFont(font);
|
|
|
|
font.fromString(appsettings->value(this, GC_FONT_TITLES, QFont().toString()).toString());
|
|
titles->setCurrentFont(font);
|
|
|
|
font.fromString(appsettings->value(this, GC_FONT_CHARTMARKERS, QFont().toString()).toString());
|
|
chartmarkers->setCurrentFont(font);
|
|
|
|
font.fromString(appsettings->value(this, GC_FONT_CHARTLABELS, QFont().toString()).toString());
|
|
chartlabels->setCurrentFont(font);
|
|
|
|
font.fromString(appsettings->value(this, GC_FONT_CALENDAR, QFont().toString()).toString());
|
|
calendar->setCurrentFont(font);
|
|
|
|
#ifdef Q_OS_MAC
|
|
defaultSize->setCurrentIndex((appsettings->value(this, GC_FONT_DEFAULT_SIZE, 10).toInt() -7) / 2);
|
|
titlesSize->setCurrentIndex((appsettings->value(this, GC_FONT_TITLES_SIZE, 10).toInt() -7) / 2);
|
|
chartmarkersSize->setCurrentIndex((appsettings->value(this, GC_FONT_CHARTMARKERS_SIZE, 8).toInt() -7) / 2);
|
|
chartlabelsSize->setCurrentIndex((appsettings->value(this, GC_FONT_CHARTLABELS_SIZE, 8).toInt() -7) / 2);
|
|
calendarSize->setCurrentIndex((appsettings->value(this, GC_FONT_CALENDAR_SIZE, 8).toInt() -7) / 2);
|
|
#else
|
|
defaultSize->setCurrentIndex((appsettings->value(this, GC_FONT_DEFAULT_SIZE, 10).toInt() -6) / 2);
|
|
titlesSize->setCurrentIndex((appsettings->value(this, GC_FONT_TITLES_SIZE, 10).toInt() -6) / 2);
|
|
chartmarkersSize->setCurrentIndex((appsettings->value(this, GC_FONT_CHARTMARKERS_SIZE, 8).toInt() -6) / 2);
|
|
chartlabelsSize->setCurrentIndex((appsettings->value(this, GC_FONT_CHARTLABELS_SIZE, 8).toInt() -6) / 2);
|
|
calendarSize->setCurrentIndex((appsettings->value(this, GC_FONT_CALENDAR_SIZE, 8).toInt() -6) / 2);
|
|
#endif
|
|
|
|
QGridLayout *grid = new QGridLayout;
|
|
grid->setSpacing(5);
|
|
|
|
grid->addWidget(defaultLabel, 0,0);
|
|
grid->addWidget(titlesLabel, 1,0);
|
|
|
|
grid->addWidget(lineWidthLabel, 0,3);
|
|
grid->addWidget(lineWidth, 0,4);
|
|
grid->addWidget(antiAliasLabel, 1,3);
|
|
grid->addWidget(antiAliased, 1,4);
|
|
#ifndef Q_OS_MAC
|
|
grid->addWidget(rideScrollLabel, 2,3);
|
|
grid->addWidget(rideScroll, 2,4);
|
|
grid->addWidget(rideHeadLabel, 3,3);
|
|
grid->addWidget(rideHead, 3,4);
|
|
#endif
|
|
|
|
grid->addWidget(markerLabel, 2,0);
|
|
grid->addWidget(chartLabel, 3,0);
|
|
grid->addWidget(calendarLabel, 4,0);
|
|
|
|
grid->addWidget(def, 0,1, Qt::AlignVCenter|Qt::AlignLeft);
|
|
grid->addWidget(titles, 1,1, Qt::AlignVCenter|Qt::AlignLeft);
|
|
grid->addWidget(chartmarkers, 2,1, Qt::AlignVCenter|Qt::AlignLeft);
|
|
grid->addWidget(chartlabels, 3,1, Qt::AlignVCenter|Qt::AlignLeft);
|
|
grid->addWidget(calendar, 4,1, Qt::AlignVCenter|Qt::AlignLeft);
|
|
|
|
grid->addWidget(defaultSize, 0,2, Qt::AlignVCenter|Qt::AlignLeft);
|
|
grid->addWidget(titlesSize, 1,2, Qt::AlignVCenter|Qt::AlignLeft);
|
|
grid->addWidget(chartmarkersSize, 2,2, Qt::AlignVCenter|Qt::AlignLeft);
|
|
grid->addWidget(chartlabelsSize, 3,2, Qt::AlignVCenter|Qt::AlignLeft);
|
|
grid->addWidget(calendarSize, 4,2, Qt::AlignVCenter|Qt::AlignLeft);
|
|
|
|
grid->addWidget(chromeLabel, 5, 0);
|
|
grid->addWidget(chromeCombo, 5, 1, Qt::AlignVCenter|Qt::AlignLeft);
|
|
|
|
grid->setColumnStretch(0,1);
|
|
grid->setColumnStretch(1,4);
|
|
grid->setColumnStretch(2,1);
|
|
grid->setColumnStretch(3,1);
|
|
grid->setColumnStretch(4,4);
|
|
|
|
mainLayout->addLayout(grid);
|
|
|
|
colorTab = new QTabWidget(this);
|
|
colorTab->addTab(themes, tr("Theme"));
|
|
colorTab->addTab(colors, tr("Colors"));
|
|
colorTab->setCornerWidget(applyTheme);
|
|
|
|
mainLayout->addWidget(colorTab);
|
|
|
|
colorSet = GCColor::colorSet();
|
|
for (int i=0; colorSet[i].name != ""; i++) {
|
|
|
|
QTreeWidgetItem *add;
|
|
ColorButton *colorButton = new ColorButton(this, colorSet[i].name, colorSet[i].color);
|
|
add = new QTreeWidgetItem(colors->invisibleRootItem());
|
|
add->setText(0, colorSet[i].name);
|
|
colors->setItemWidget(add, 1, colorButton);
|
|
|
|
}
|
|
connect(applyTheme, SIGNAL(clicked()), this, SLOT(applyThemeClicked()));
|
|
|
|
foreach(ColorTheme theme, GCColor::themes().themes) {
|
|
|
|
QTreeWidgetItem *add;
|
|
ColorLabel *swatch = new ColorLabel(theme);
|
|
swatch->setFixedHeight(30);
|
|
add = new QTreeWidgetItem(themes->invisibleRootItem());
|
|
themes->setItemWidget(add, 0, swatch);
|
|
add->setText(1, theme.name);
|
|
|
|
}
|
|
connect(colorTab, SIGNAL(currentChanged(int)), this, SLOT(tabChanged()));
|
|
|
|
// save initial values
|
|
b4.alias = antiAliased->isChecked();
|
|
#ifndef Q_OS_MAC
|
|
b4.scroll = rideScroll->isChecked();
|
|
b4.head = rideHead->isChecked();
|
|
#endif
|
|
b4.line = lineWidth->value();
|
|
b4.chrome = chromeCombo->currentIndex();
|
|
b4.fingerprint = Colors::fingerprint(colorSet);
|
|
}
|
|
|
|
void
|
|
ColorsPage::tabChanged()
|
|
{
|
|
// are we on the them page
|
|
if (colorTab->currentIndex() == 0) applyTheme->show();
|
|
else applyTheme->hide();
|
|
}
|
|
|
|
void
|
|
ColorsPage::applyThemeClicked()
|
|
{
|
|
int index=0;
|
|
|
|
// first check we have a selection!
|
|
if (themes->currentItem() && (index=themes->invisibleRootItem()->indexOfChild(themes->currentItem())) >= 0) {
|
|
|
|
// now get the theme selected
|
|
ColorTheme theme = GCColor::themes().themes[index];
|
|
|
|
// reset to base
|
|
colorSet = GCColor::defaultColorSet();
|
|
|
|
// reset the color selection tools
|
|
colors->clear();
|
|
for (int i=0; colorSet[i].name != ""; i++) {
|
|
|
|
QColor color;
|
|
|
|
// apply theme to color
|
|
switch(i) {
|
|
|
|
case CPLOTBACKGROUND:
|
|
case CRIDEPLOTBACKGROUND:
|
|
case CTRENDPLOTBACKGROUND:
|
|
case CTRAINPLOTBACKGROUND:
|
|
color = theme.colors[0]; // background color
|
|
break;
|
|
|
|
// fg color theme.colors[1] not used YET XXX
|
|
|
|
case CPLOTSYMBOL:
|
|
case CRIDEPLOTXAXIS:
|
|
case CRIDEPLOTYAXIS:
|
|
case CPLOTMARKER:
|
|
color = theme.colors[2]; // accent color
|
|
break;
|
|
|
|
case CPLOTSELECT:
|
|
case CPLOTTRACKER:
|
|
case CINTERVALHIGHLIGHTER:
|
|
color = theme.colors[3]; // select color
|
|
break;
|
|
|
|
|
|
case CPLOTGRID: // grid doesn't have a theme color
|
|
// we make it barely distinguishable from background
|
|
{
|
|
QColor bg = theme.colors[0];
|
|
if(bg == QColor(Qt::black)) color = QColor(30,30,30);
|
|
else color = bg.darker(110);
|
|
}
|
|
break;
|
|
|
|
case CCP:
|
|
case CWBAL:
|
|
case CRIDECP:
|
|
color = theme.colors[4];
|
|
break;
|
|
|
|
case CHEARTRATE:
|
|
color = theme.colors[5];
|
|
break;
|
|
|
|
case CSPEED:
|
|
color = theme.colors[6];
|
|
break;
|
|
|
|
case CPOWER:
|
|
color = theme.colors[7];
|
|
break;
|
|
|
|
case CCADENCE:
|
|
color = theme.colors[8];
|
|
break;
|
|
|
|
case CTORQUE:
|
|
color = theme.colors[9];
|
|
break;
|
|
|
|
default:
|
|
color = colorSet[i].color;
|
|
}
|
|
|
|
QTreeWidgetItem *add;
|
|
ColorButton *colorButton = new ColorButton(this, colorSet[i].name, color);
|
|
add = new QTreeWidgetItem(colors->invisibleRootItem());
|
|
add->setText(0, colorSet[i].name);
|
|
colors->setItemWidget(add, 1, colorButton);
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
qint32
|
|
ColorsPage::saveClicked()
|
|
{
|
|
// chrome style only has 2 types for now
|
|
switch(chromeCombo->currentIndex()) {
|
|
default:
|
|
case 0:
|
|
appsettings->setValue(GC_CHROME, "Mac");
|
|
#ifdef Q_OS_MAC
|
|
// if on yosemite set ALWAYS do flat chrome
|
|
if (QSysInfo::MacintoshVersion == 12) {
|
|
appsettings->setValue(GC_CHROME, "Flat");
|
|
}
|
|
#endif
|
|
break;
|
|
case 1:
|
|
appsettings->setValue(GC_CHROME, "Flat");
|
|
break;
|
|
}
|
|
|
|
appsettings->setValue(GC_LINEWIDTH, lineWidth->value());
|
|
appsettings->setValue(GC_ANTIALIAS, antiAliased->isChecked());
|
|
#ifndef Q_OS_MAC
|
|
appsettings->setValue(GC_RIDESCROLL, rideScroll->isChecked());
|
|
appsettings->setValue(GC_RIDEHEAD, rideHead->isChecked());
|
|
#endif
|
|
|
|
// run down and get the current colors and save
|
|
for (int i=0; colorSet[i].name != ""; i++) {
|
|
QTreeWidgetItem *current = colors->invisibleRootItem()->child(i);
|
|
QColor newColor = ((ColorButton*)colors->itemWidget(current, 1))->getColor();
|
|
QString colorstring = QString("%1:%2:%3").arg(newColor.red())
|
|
.arg(newColor.green())
|
|
.arg(newColor.blue());
|
|
appsettings->setValue(colorSet[i].setting, colorstring);
|
|
}
|
|
// Font
|
|
appsettings->setValue(GC_FONT_DEFAULT, def->currentFont().toString());
|
|
appsettings->setValue(GC_FONT_TITLES, titles->currentFont().toString());
|
|
appsettings->setValue(GC_FONT_CHARTMARKERS, chartmarkers->currentFont().toString());
|
|
appsettings->setValue(GC_FONT_CHARTLABELS, chartlabels->currentFont().toString());
|
|
appsettings->setValue(GC_FONT_CALENDAR, calendar->currentFont().toString());
|
|
#ifdef Q_OS_MAC
|
|
appsettings->setValue(GC_FONT_DEFAULT_SIZE, 7+(defaultSize->currentIndex()*2));
|
|
appsettings->setValue(GC_FONT_TITLES_SIZE, 7+(titlesSize->currentIndex()*2));
|
|
appsettings->setValue(GC_FONT_CHARTMARKERS_SIZE, 7+(chartmarkersSize->currentIndex()*2));
|
|
appsettings->setValue(GC_FONT_CHARTLABELS_SIZE, 7+(chartlabelsSize->currentIndex()*2));
|
|
appsettings->setValue(GC_FONT_CALENDAR_SIZE, 7+(calendarSize->currentIndex()*2));
|
|
#else
|
|
appsettings->setValue(GC_FONT_DEFAULT_SIZE, 6+(defaultSize->currentIndex()*2));
|
|
appsettings->setValue(GC_FONT_TITLES_SIZE, 6+(titlesSize->currentIndex()*2));
|
|
appsettings->setValue(GC_FONT_CHARTMARKERS_SIZE, 6+(chartmarkersSize->currentIndex()*2));
|
|
appsettings->setValue(GC_FONT_CHARTLABELS_SIZE, 6+(chartlabelsSize->currentIndex()*2));
|
|
appsettings->setValue(GC_FONT_CALENDAR_SIZE, 6+(calendarSize->currentIndex()*2));
|
|
#endif
|
|
|
|
QFont font;
|
|
font.fromString(appsettings->value(this, GC_FONT_DEFAULT, QFont().toString()).toString());
|
|
font.setPointSize(appsettings->value(this, GC_FONT_DEFAULT_SIZE, 13).toInt());
|
|
QApplication::setFont(font);
|
|
|
|
// reread into colorset so we can check for changes
|
|
GCColor::readConfig();
|
|
|
|
// did we change anything ?
|
|
if(b4.alias != antiAliased->isChecked() ||
|
|
#ifndef Q_OS_MAC // not needed on mac
|
|
b4.scroll != rideScroll->isChecked() ||
|
|
b4.head != rideHead->isChecked() ||
|
|
#endif
|
|
b4.line != lineWidth->value() ||
|
|
b4.chrome != chromeCombo->currentIndex() ||
|
|
b4.fingerprint != Colors::fingerprint(colorSet))
|
|
return CONFIG_APPEARANCE;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
IntervalMetricsPage::IntervalMetricsPage(QWidget *parent) :
|
|
QWidget(parent), changed(false)
|
|
{
|
|
HelpWhatsThis *help = new HelpWhatsThis(this);
|
|
this->setWhatsThis(help->getWhatsThisText(HelpWhatsThis::Preferences_Metrics_Intervals));
|
|
|
|
availList = new QListWidget;
|
|
availList->setSortingEnabled(true);
|
|
availList->setSelectionMode(QAbstractItemView::SingleSelection);
|
|
QVBoxLayout *availLayout = new QVBoxLayout;
|
|
availLayout->addWidget(new QLabel(tr("Available Metrics")));
|
|
availLayout->addWidget(availList);
|
|
selectedList = new QListWidget;
|
|
selectedList->setSelectionMode(QAbstractItemView::SingleSelection);
|
|
QVBoxLayout *selectedLayout = new QVBoxLayout;
|
|
selectedLayout->addWidget(new QLabel(tr("Selected Metrics")));
|
|
selectedLayout->addWidget(selectedList);
|
|
#ifndef Q_OS_MAC
|
|
upButton = new QToolButton(this);
|
|
downButton = new QToolButton(this);
|
|
leftButton = new QToolButton(this);
|
|
rightButton = new QToolButton(this);
|
|
upButton->setArrowType(Qt::UpArrow);
|
|
downButton->setArrowType(Qt::DownArrow);
|
|
leftButton->setArrowType(Qt::LeftArrow);
|
|
rightButton->setArrowType(Qt::RightArrow);
|
|
upButton->setFixedSize(20,20);
|
|
downButton->setFixedSize(20,20);
|
|
leftButton->setFixedSize(20,20);
|
|
rightButton->setFixedSize(20,20);
|
|
#else
|
|
upButton = new QPushButton(tr("Up"));
|
|
downButton = new QPushButton(tr("Down"));
|
|
leftButton = new QPushButton("<");
|
|
rightButton = new QPushButton(">");
|
|
#endif
|
|
QVBoxLayout *buttonGrid = new QVBoxLayout;
|
|
QHBoxLayout *upLayout = new QHBoxLayout;
|
|
QHBoxLayout *inexcLayout = new QHBoxLayout;
|
|
QHBoxLayout *downLayout = new QHBoxLayout;
|
|
|
|
upLayout->addStretch();
|
|
upLayout->addWidget(upButton);
|
|
upLayout->addStretch();
|
|
|
|
inexcLayout->addStretch();
|
|
inexcLayout->addWidget(leftButton);
|
|
inexcLayout->addWidget(rightButton);
|
|
inexcLayout->addStretch();
|
|
|
|
downLayout->addStretch();
|
|
downLayout->addWidget(downButton);
|
|
downLayout->addStretch();
|
|
|
|
buttonGrid->addStretch();
|
|
buttonGrid->addLayout(upLayout);
|
|
buttonGrid->addLayout(inexcLayout);
|
|
buttonGrid->addLayout(downLayout);
|
|
buttonGrid->addStretch();
|
|
|
|
QHBoxLayout *hlayout = new QHBoxLayout;
|
|
hlayout->addLayout(availLayout);
|
|
hlayout->addLayout(buttonGrid);
|
|
hlayout->addLayout(selectedLayout);
|
|
setLayout(hlayout);
|
|
|
|
QString s;
|
|
if (appsettings->contains(GC_SETTINGS_INTERVAL_METRICS))
|
|
s = appsettings->value(this, GC_SETTINGS_INTERVAL_METRICS).toString();
|
|
else
|
|
s = GC_SETTINGS_INTERVAL_METRICS_DEFAULT;
|
|
QStringList selectedMetrics = s.split(",");
|
|
|
|
const RideMetricFactory &factory = RideMetricFactory::instance();
|
|
for (int i = 0; i < factory.metricCount(); ++i) {
|
|
QString symbol = factory.metricName(i);
|
|
if (selectedMetrics.contains(symbol))
|
|
continue;
|
|
QSharedPointer<RideMetric> m(factory.newMetric(symbol));
|
|
QTextEdit name(m->name()); // process html encoding of(TM)
|
|
QListWidgetItem *item = new QListWidgetItem(name.toPlainText());
|
|
item->setData(Qt::UserRole, symbol);
|
|
availList->addItem(item);
|
|
}
|
|
foreach (QString symbol, selectedMetrics) {
|
|
if (!factory.haveMetric(symbol))
|
|
continue;
|
|
QSharedPointer<RideMetric> m(factory.newMetric(symbol));
|
|
QTextEdit name(m->name()); // process html encoding of(TM)
|
|
QListWidgetItem *item = new QListWidgetItem(name.toPlainText());
|
|
item->setData(Qt::UserRole, symbol);
|
|
selectedList->addItem(item);
|
|
}
|
|
|
|
upButton->setEnabled(false);
|
|
downButton->setEnabled(false);
|
|
leftButton->setEnabled(false);
|
|
rightButton->setEnabled(false);
|
|
|
|
connect(upButton, SIGNAL(clicked()), this, SLOT(upClicked()));
|
|
connect(downButton, SIGNAL(clicked()), this, SLOT(downClicked()));
|
|
connect(leftButton, SIGNAL(clicked()), this, SLOT(leftClicked()));
|
|
connect(rightButton, SIGNAL(clicked()), this, SLOT(rightClicked()));
|
|
connect(availList, SIGNAL(itemSelectionChanged()),
|
|
this, SLOT(availChanged()));
|
|
connect(selectedList, SIGNAL(itemSelectionChanged()),
|
|
this, SLOT(selectedChanged()));
|
|
}
|
|
|
|
void
|
|
IntervalMetricsPage::upClicked()
|
|
{
|
|
assert(!selectedList->selectedItems().isEmpty());
|
|
QListWidgetItem *item = selectedList->selectedItems().first();
|
|
int row = selectedList->row(item);
|
|
assert(row > 0);
|
|
selectedList->takeItem(row);
|
|
selectedList->insertItem(row - 1, item);
|
|
selectedList->setCurrentItem(item);
|
|
changed = true;
|
|
}
|
|
|
|
void
|
|
IntervalMetricsPage::downClicked()
|
|
{
|
|
assert(!selectedList->selectedItems().isEmpty());
|
|
QListWidgetItem *item = selectedList->selectedItems().first();
|
|
int row = selectedList->row(item);
|
|
assert(row < selectedList->count() - 1);
|
|
selectedList->takeItem(row);
|
|
selectedList->insertItem(row + 1, item);
|
|
selectedList->setCurrentItem(item);
|
|
changed = true;
|
|
}
|
|
|
|
void
|
|
IntervalMetricsPage::leftClicked()
|
|
{
|
|
assert(!selectedList->selectedItems().isEmpty());
|
|
QListWidgetItem *item = selectedList->selectedItems().first();
|
|
selectedList->takeItem(selectedList->row(item));
|
|
availList->addItem(item);
|
|
changed = true;
|
|
selectedChanged();
|
|
}
|
|
|
|
void
|
|
IntervalMetricsPage::rightClicked()
|
|
{
|
|
assert(!availList->selectedItems().isEmpty());
|
|
QListWidgetItem *item = availList->selectedItems().first();
|
|
availList->takeItem(availList->row(item));
|
|
selectedList->addItem(item);
|
|
changed = true;
|
|
}
|
|
|
|
void
|
|
IntervalMetricsPage::availChanged()
|
|
{
|
|
rightButton->setEnabled(!availList->selectedItems().isEmpty());
|
|
}
|
|
|
|
void
|
|
IntervalMetricsPage::selectedChanged()
|
|
{
|
|
if (selectedList->selectedItems().isEmpty()) {
|
|
upButton->setEnabled(false);
|
|
downButton->setEnabled(false);
|
|
leftButton->setEnabled(false);
|
|
return;
|
|
}
|
|
QListWidgetItem *item = selectedList->selectedItems().first();
|
|
int row = selectedList->row(item);
|
|
if (row == 0)
|
|
upButton->setEnabled(false);
|
|
else
|
|
upButton->setEnabled(true);
|
|
if (row == selectedList->count() - 1)
|
|
downButton->setEnabled(false);
|
|
else
|
|
downButton->setEnabled(true);
|
|
leftButton->setEnabled(true);
|
|
}
|
|
|
|
qint32
|
|
IntervalMetricsPage::saveClicked()
|
|
{
|
|
if (!changed) return 0;
|
|
|
|
QStringList metrics;
|
|
for (int i = 0; i < selectedList->count(); ++i)
|
|
metrics << selectedList->item(i)->data(Qt::UserRole).toString();
|
|
appsettings->setValue(GC_SETTINGS_INTERVAL_METRICS, metrics.join(","));
|
|
|
|
return 0;
|
|
}
|
|
|
|
BestsMetricsPage::BestsMetricsPage(QWidget *parent) :
|
|
QWidget(parent), changed(false)
|
|
{
|
|
HelpWhatsThis *help = new HelpWhatsThis(this);
|
|
this->setWhatsThis(help->getWhatsThisText(HelpWhatsThis::Preferences_Metrics_Best));
|
|
|
|
availList = new QListWidget;
|
|
availList->setSortingEnabled(true);
|
|
availList->setSelectionMode(QAbstractItemView::SingleSelection);
|
|
QVBoxLayout *availLayout = new QVBoxLayout;
|
|
availLayout->addWidget(new QLabel(tr("Available Metrics")));
|
|
availLayout->addWidget(availList);
|
|
selectedList = new QListWidget;
|
|
selectedList->setSelectionMode(QAbstractItemView::SingleSelection);
|
|
QVBoxLayout *selectedLayout = new QVBoxLayout;
|
|
selectedLayout->addWidget(new QLabel(tr("Selected Metrics")));
|
|
selectedLayout->addWidget(selectedList);
|
|
#ifndef Q_OS_MAC
|
|
upButton = new QToolButton(this);
|
|
downButton = new QToolButton(this);
|
|
leftButton = new QToolButton(this);
|
|
rightButton = new QToolButton(this);
|
|
upButton->setArrowType(Qt::UpArrow);
|
|
downButton->setArrowType(Qt::DownArrow);
|
|
leftButton->setArrowType(Qt::LeftArrow);
|
|
rightButton->setArrowType(Qt::RightArrow);
|
|
upButton->setFixedSize(20,20);
|
|
downButton->setFixedSize(20,20);
|
|
leftButton->setFixedSize(20,20);
|
|
rightButton->setFixedSize(20,20);
|
|
#else
|
|
upButton = new QPushButton(tr("Up"));
|
|
downButton = new QPushButton(tr("Down"));
|
|
leftButton = new QPushButton("<");
|
|
rightButton = new QPushButton(">");
|
|
#endif
|
|
QVBoxLayout *buttonGrid = new QVBoxLayout;
|
|
QHBoxLayout *upLayout = new QHBoxLayout;
|
|
QHBoxLayout *inexcLayout = new QHBoxLayout;
|
|
QHBoxLayout *downLayout = new QHBoxLayout;
|
|
|
|
upLayout->addStretch();
|
|
upLayout->addWidget(upButton);
|
|
upLayout->addStretch();
|
|
|
|
inexcLayout->addStretch();
|
|
inexcLayout->addWidget(leftButton);
|
|
inexcLayout->addWidget(rightButton);
|
|
inexcLayout->addStretch();
|
|
|
|
downLayout->addStretch();
|
|
downLayout->addWidget(downButton);
|
|
downLayout->addStretch();
|
|
|
|
buttonGrid->addStretch();
|
|
buttonGrid->addLayout(upLayout);
|
|
buttonGrid->addLayout(inexcLayout);
|
|
buttonGrid->addLayout(downLayout);
|
|
buttonGrid->addStretch();
|
|
|
|
QHBoxLayout *hlayout = new QHBoxLayout;
|
|
hlayout->addLayout(availLayout);
|
|
hlayout->addLayout(buttonGrid);
|
|
hlayout->addLayout(selectedLayout);
|
|
setLayout(hlayout);
|
|
|
|
QString s;
|
|
if (appsettings->contains(GC_SETTINGS_BESTS_METRICS))
|
|
s = appsettings->value(this, GC_SETTINGS_BESTS_METRICS).toString();
|
|
else
|
|
s = GC_SETTINGS_BESTS_METRICS_DEFAULT;
|
|
QStringList selectedMetrics = s.split(",");
|
|
|
|
const RideMetricFactory &factory = RideMetricFactory::instance();
|
|
for (int i = 0; i < factory.metricCount(); ++i) {
|
|
QString symbol = factory.metricName(i);
|
|
if (selectedMetrics.contains(symbol))
|
|
continue;
|
|
QSharedPointer<RideMetric> m(factory.newMetric(symbol));
|
|
QTextEdit name(m->name()); // process html encoding of(TM)
|
|
QListWidgetItem *item = new QListWidgetItem(name.toPlainText());
|
|
item->setData(Qt::UserRole, symbol);
|
|
availList->addItem(item);
|
|
}
|
|
foreach (QString symbol, selectedMetrics) {
|
|
if (!factory.haveMetric(symbol))
|
|
continue;
|
|
QSharedPointer<RideMetric> m(factory.newMetric(symbol));
|
|
QTextEdit name(m->name()); // process html encoding of(TM)
|
|
QListWidgetItem *item = new QListWidgetItem(name.toPlainText());
|
|
item->setData(Qt::UserRole, symbol);
|
|
selectedList->addItem(item);
|
|
}
|
|
|
|
upButton->setEnabled(false);
|
|
downButton->setEnabled(false);
|
|
leftButton->setEnabled(false);
|
|
rightButton->setEnabled(false);
|
|
|
|
connect(upButton, SIGNAL(clicked()), this, SLOT(upClicked()));
|
|
connect(downButton, SIGNAL(clicked()), this, SLOT(downClicked()));
|
|
connect(leftButton, SIGNAL(clicked()), this, SLOT(leftClicked()));
|
|
connect(rightButton, SIGNAL(clicked()), this, SLOT(rightClicked()));
|
|
connect(availList, SIGNAL(itemSelectionChanged()),
|
|
this, SLOT(availChanged()));
|
|
connect(selectedList, SIGNAL(itemSelectionChanged()),
|
|
this, SLOT(selectedChanged()));
|
|
}
|
|
|
|
void
|
|
BestsMetricsPage::upClicked()
|
|
{
|
|
assert(!selectedList->selectedItems().isEmpty());
|
|
QListWidgetItem *item = selectedList->selectedItems().first();
|
|
int row = selectedList->row(item);
|
|
assert(row > 0);
|
|
selectedList->takeItem(row);
|
|
selectedList->insertItem(row - 1, item);
|
|
selectedList->setCurrentItem(item);
|
|
changed = true;
|
|
}
|
|
|
|
void
|
|
BestsMetricsPage::downClicked()
|
|
{
|
|
assert(!selectedList->selectedItems().isEmpty());
|
|
QListWidgetItem *item = selectedList->selectedItems().first();
|
|
int row = selectedList->row(item);
|
|
assert(row < selectedList->count() - 1);
|
|
selectedList->takeItem(row);
|
|
selectedList->insertItem(row + 1, item);
|
|
selectedList->setCurrentItem(item);
|
|
changed = true;
|
|
}
|
|
|
|
void
|
|
BestsMetricsPage::leftClicked()
|
|
{
|
|
assert(!selectedList->selectedItems().isEmpty());
|
|
QListWidgetItem *item = selectedList->selectedItems().first();
|
|
selectedList->takeItem(selectedList->row(item));
|
|
availList->addItem(item);
|
|
changed = true;
|
|
selectedChanged();
|
|
}
|
|
|
|
void
|
|
BestsMetricsPage::rightClicked()
|
|
{
|
|
assert(!availList->selectedItems().isEmpty());
|
|
QListWidgetItem *item = availList->selectedItems().first();
|
|
availList->takeItem(availList->row(item));
|
|
selectedList->addItem(item);
|
|
changed = true;
|
|
}
|
|
|
|
void
|
|
BestsMetricsPage::availChanged()
|
|
{
|
|
rightButton->setEnabled(!availList->selectedItems().isEmpty());
|
|
}
|
|
|
|
void
|
|
BestsMetricsPage::selectedChanged()
|
|
{
|
|
if (selectedList->selectedItems().isEmpty()) {
|
|
upButton->setEnabled(false);
|
|
downButton->setEnabled(false);
|
|
leftButton->setEnabled(false);
|
|
return;
|
|
}
|
|
QListWidgetItem *item = selectedList->selectedItems().first();
|
|
int row = selectedList->row(item);
|
|
if (row == 0)
|
|
upButton->setEnabled(false);
|
|
else
|
|
upButton->setEnabled(true);
|
|
if (row == selectedList->count() - 1)
|
|
downButton->setEnabled(false);
|
|
else
|
|
downButton->setEnabled(true);
|
|
leftButton->setEnabled(true);
|
|
}
|
|
|
|
qint32
|
|
BestsMetricsPage::saveClicked()
|
|
{
|
|
if (!changed) return 0;
|
|
|
|
QStringList metrics;
|
|
for (int i = 0; i < selectedList->count(); ++i)
|
|
metrics << selectedList->item(i)->data(Qt::UserRole).toString();
|
|
appsettings->setValue(GC_SETTINGS_BESTS_METRICS, metrics.join(","));
|
|
|
|
return 0;
|
|
}
|
|
|
|
SummaryMetricsPage::SummaryMetricsPage(QWidget *parent) :
|
|
QWidget(parent), changed(false)
|
|
{
|
|
HelpWhatsThis *help = new HelpWhatsThis(this);
|
|
this->setWhatsThis(help->getWhatsThisText(HelpWhatsThis::Preferences_Metrics_Summary));
|
|
|
|
availList = new QListWidget;
|
|
availList->setSortingEnabled(true);
|
|
availList->setSelectionMode(QAbstractItemView::SingleSelection);
|
|
QVBoxLayout *availLayout = new QVBoxLayout;
|
|
availLayout->addWidget(new QLabel(tr("Available Metrics")));
|
|
availLayout->addWidget(availList);
|
|
selectedList = new QListWidget;
|
|
selectedList->setSelectionMode(QAbstractItemView::SingleSelection);
|
|
QVBoxLayout *selectedLayout = new QVBoxLayout;
|
|
selectedLayout->addWidget(new QLabel(tr("Selected Metrics")));
|
|
selectedLayout->addWidget(selectedList);
|
|
#ifndef Q_OS_MAC
|
|
upButton = new QToolButton(this);
|
|
downButton = new QToolButton(this);
|
|
leftButton = new QToolButton(this);
|
|
rightButton = new QToolButton(this);
|
|
upButton->setArrowType(Qt::UpArrow);
|
|
downButton->setArrowType(Qt::DownArrow);
|
|
leftButton->setArrowType(Qt::LeftArrow);
|
|
rightButton->setArrowType(Qt::RightArrow);
|
|
upButton->setFixedSize(20,20);
|
|
downButton->setFixedSize(20,20);
|
|
leftButton->setFixedSize(20,20);
|
|
rightButton->setFixedSize(20,20);
|
|
#else
|
|
upButton = new QPushButton(tr("Up"));
|
|
downButton = new QPushButton(tr("Down"));
|
|
leftButton = new QPushButton("<");
|
|
rightButton = new QPushButton(">");
|
|
#endif
|
|
QVBoxLayout *buttonGrid = new QVBoxLayout;
|
|
QHBoxLayout *upLayout = new QHBoxLayout;
|
|
QHBoxLayout *inexcLayout = new QHBoxLayout;
|
|
QHBoxLayout *downLayout = new QHBoxLayout;
|
|
|
|
upLayout->addStretch();
|
|
upLayout->addWidget(upButton);
|
|
upLayout->addStretch();
|
|
|
|
inexcLayout->addStretch();
|
|
inexcLayout->addWidget(leftButton);
|
|
inexcLayout->addWidget(rightButton);
|
|
inexcLayout->addStretch();
|
|
|
|
downLayout->addStretch();
|
|
downLayout->addWidget(downButton);
|
|
downLayout->addStretch();
|
|
|
|
buttonGrid->addStretch();
|
|
buttonGrid->addLayout(upLayout);
|
|
buttonGrid->addLayout(inexcLayout);
|
|
buttonGrid->addLayout(downLayout);
|
|
buttonGrid->addStretch();
|
|
|
|
QHBoxLayout *hlayout = new QHBoxLayout;
|
|
hlayout->addLayout(availLayout);
|
|
hlayout->addLayout(buttonGrid);
|
|
hlayout->addLayout(selectedLayout);
|
|
setLayout(hlayout);
|
|
|
|
QString s = appsettings->value(this, GC_SETTINGS_SUMMARY_METRICS, GC_SETTINGS_SUMMARY_METRICS_DEFAULT).toString();
|
|
QStringList selectedMetrics = s.split(",");
|
|
|
|
const RideMetricFactory &factory = RideMetricFactory::instance();
|
|
for (int i = 0; i < factory.metricCount(); ++i) {
|
|
QString symbol = factory.metricName(i);
|
|
if (selectedMetrics.contains(symbol))
|
|
continue;
|
|
QSharedPointer<RideMetric> m(factory.newMetric(symbol));
|
|
QTextEdit name(m->name()); // process html encoding of(TM)
|
|
QListWidgetItem *item = new QListWidgetItem(name.toPlainText());
|
|
item->setData(Qt::UserRole, symbol);
|
|
availList->addItem(item);
|
|
}
|
|
foreach (QString symbol, selectedMetrics) {
|
|
if (!factory.haveMetric(symbol))
|
|
continue;
|
|
QSharedPointer<RideMetric> m(factory.newMetric(symbol));
|
|
QTextEdit name(m->name()); // process html encoding of(TM)
|
|
QListWidgetItem *item = new QListWidgetItem(name.toPlainText());
|
|
item->setData(Qt::UserRole, symbol);
|
|
selectedList->addItem(item);
|
|
}
|
|
|
|
upButton->setEnabled(false);
|
|
downButton->setEnabled(false);
|
|
leftButton->setEnabled(false);
|
|
rightButton->setEnabled(false);
|
|
|
|
connect(upButton, SIGNAL(clicked()), this, SLOT(upClicked()));
|
|
connect(downButton, SIGNAL(clicked()), this, SLOT(downClicked()));
|
|
connect(leftButton, SIGNAL(clicked()), this, SLOT(leftClicked()));
|
|
connect(rightButton, SIGNAL(clicked()), this, SLOT(rightClicked()));
|
|
connect(availList, SIGNAL(itemSelectionChanged()),
|
|
this, SLOT(availChanged()));
|
|
connect(selectedList, SIGNAL(itemSelectionChanged()),
|
|
this, SLOT(selectedChanged()));
|
|
}
|
|
|
|
void
|
|
SummaryMetricsPage::upClicked()
|
|
{
|
|
assert(!selectedList->selectedItems().isEmpty());
|
|
QListWidgetItem *item = selectedList->selectedItems().first();
|
|
int row = selectedList->row(item);
|
|
assert(row > 0);
|
|
selectedList->takeItem(row);
|
|
selectedList->insertItem(row - 1, item);
|
|
selectedList->setCurrentItem(item);
|
|
changed = true;
|
|
}
|
|
|
|
void
|
|
SummaryMetricsPage::downClicked()
|
|
{
|
|
assert(!selectedList->selectedItems().isEmpty());
|
|
QListWidgetItem *item = selectedList->selectedItems().first();
|
|
int row = selectedList->row(item);
|
|
assert(row < selectedList->count() - 1);
|
|
selectedList->takeItem(row);
|
|
selectedList->insertItem(row + 1, item);
|
|
selectedList->setCurrentItem(item);
|
|
changed = true;
|
|
}
|
|
|
|
void
|
|
SummaryMetricsPage::leftClicked()
|
|
{
|
|
assert(!selectedList->selectedItems().isEmpty());
|
|
QListWidgetItem *item = selectedList->selectedItems().first();
|
|
selectedList->takeItem(selectedList->row(item));
|
|
availList->addItem(item);
|
|
changed = true;
|
|
selectedChanged();
|
|
}
|
|
|
|
void
|
|
SummaryMetricsPage::rightClicked()
|
|
{
|
|
assert(!availList->selectedItems().isEmpty());
|
|
QListWidgetItem *item = availList->selectedItems().first();
|
|
availList->takeItem(availList->row(item));
|
|
selectedList->addItem(item);
|
|
changed = true;
|
|
}
|
|
|
|
void
|
|
SummaryMetricsPage::availChanged()
|
|
{
|
|
rightButton->setEnabled(!availList->selectedItems().isEmpty());
|
|
}
|
|
|
|
void
|
|
SummaryMetricsPage::selectedChanged()
|
|
{
|
|
if (selectedList->selectedItems().isEmpty()) {
|
|
upButton->setEnabled(false);
|
|
downButton->setEnabled(false);
|
|
leftButton->setEnabled(false);
|
|
return;
|
|
}
|
|
QListWidgetItem *item = selectedList->selectedItems().first();
|
|
int row = selectedList->row(item);
|
|
if (row == 0)
|
|
upButton->setEnabled(false);
|
|
else
|
|
upButton->setEnabled(true);
|
|
if (row == selectedList->count() - 1)
|
|
downButton->setEnabled(false);
|
|
else
|
|
downButton->setEnabled(true);
|
|
leftButton->setEnabled(true);
|
|
}
|
|
|
|
qint32
|
|
SummaryMetricsPage::saveClicked()
|
|
{
|
|
if (!changed) return 0;
|
|
|
|
QStringList metrics;
|
|
for (int i = 0; i < selectedList->count(); ++i)
|
|
metrics << selectedList->item(i)->data(Qt::UserRole).toString();
|
|
appsettings->setValue(GC_SETTINGS_SUMMARY_METRICS, metrics.join(","));
|
|
|
|
return 0;
|
|
}
|
|
|
|
MetadataPage::MetadataPage(Context *context) : context(context)
|
|
{
|
|
QVBoxLayout *layout = new QVBoxLayout(this);
|
|
|
|
// get current config using default file
|
|
keywordDefinitions = context->athlete->rideMetadata()->getKeywords();
|
|
fieldDefinitions = context->athlete->rideMetadata()->getFields();
|
|
colorfield = context->athlete->rideMetadata()->getColorField();
|
|
defaultDefinitions = context->athlete->rideMetadata()->getDefaults();
|
|
|
|
// setup maintenance pages using current config
|
|
fieldsPage = new FieldsPage(this, fieldDefinitions);
|
|
keywordsPage = new KeywordsPage(this, keywordDefinitions);
|
|
defaultsPage = new DefaultsPage(this, defaultDefinitions);
|
|
processorPage = new ProcessorPage(context);
|
|
|
|
|
|
tabs = new QTabWidget(this);
|
|
tabs->addTab(fieldsPage, tr("Fields"));
|
|
tabs->addTab(keywordsPage, tr("Notes Keywords"));
|
|
tabs->addTab(defaultsPage, tr("Defaults"));
|
|
tabs->addTab(processorPage, tr("Processing"));
|
|
|
|
|
|
// refresh the keywords combo when change tabs .. will do more often than
|
|
// needed but better that than less often than needed
|
|
connect (tabs, SIGNAL(currentChanged(int)), keywordsPage, SLOT(pageSelected()));
|
|
|
|
layout->addWidget(tabs);
|
|
|
|
// save initial values
|
|
b4.keywordFingerprint = KeywordDefinition::fingerprint(keywordDefinitions);
|
|
b4.colorfield = colorfield;
|
|
b4.fieldFingerprint = FieldDefinition::fingerprint(fieldDefinitions);
|
|
}
|
|
|
|
qint32
|
|
MetadataPage::saveClicked()
|
|
{
|
|
// get current state
|
|
fieldsPage->getDefinitions(fieldDefinitions);
|
|
keywordsPage->getDefinitions(keywordDefinitions);
|
|
defaultsPage->getDefinitions(defaultDefinitions);
|
|
|
|
// save settings
|
|
appsettings->setValue(GC_RIDEBG, keywordsPage->rideBG->isChecked());
|
|
|
|
// write to metadata.xml
|
|
RideMetadata::serialize(context->athlete->home->config().canonicalPath() + "/metadata.xml", keywordDefinitions, fieldDefinitions, colorfield, defaultDefinitions);
|
|
|
|
// save processors config
|
|
processorPage->saveClicked();
|
|
|
|
qint32 state = 0;
|
|
|
|
if (b4.keywordFingerprint != KeywordDefinition::fingerprint(keywordDefinitions) || b4.colorfield != colorfield)
|
|
state += CONFIG_NOTECOLOR;
|
|
|
|
if (b4.fieldFingerprint != FieldDefinition::fingerprint(fieldDefinitions))
|
|
state += CONFIG_FIELDS;
|
|
|
|
return state;
|
|
}
|
|
|
|
// little helper since we create/recreate combos
|
|
// for field types all over the place (init, move up, move down)
|
|
void
|
|
FieldsPage::addFieldTypes(QComboBox *p)
|
|
{
|
|
p->addItem(tr("Text"));
|
|
p->addItem(tr("Textbox"));
|
|
p->addItem(tr("ShortText"));
|
|
p->addItem(tr("Integer"));
|
|
p->addItem(tr("Double"));
|
|
p->addItem(tr("Date"));
|
|
p->addItem(tr("Time"));
|
|
p->addItem(tr("Checkbox"));
|
|
}
|
|
|
|
//
|
|
// Calendar coloring page
|
|
//
|
|
KeywordsPage::KeywordsPage(MetadataPage *parent, QList<KeywordDefinition>keywordDefinitions) : QWidget(parent), parent(parent)
|
|
{
|
|
QVBoxLayout *mainLayout = new QVBoxLayout(this);
|
|
HelpWhatsThis *help = new HelpWhatsThis(this);
|
|
this->setWhatsThis(help->getWhatsThisText(HelpWhatsThis::Preferences_DataFields_Notes_Keywords));
|
|
|
|
QHBoxLayout *field = new QHBoxLayout();
|
|
fieldLabel = new QLabel(tr("Field"),this);
|
|
fieldChooser = new QComboBox(this);
|
|
field->addWidget(fieldLabel);
|
|
field->addWidget(fieldChooser);
|
|
field->addStretch();
|
|
mainLayout->addLayout(field);
|
|
|
|
rideBG = new QCheckBox(tr("Use for Background"));
|
|
rideBG->setChecked(appsettings->value(this, GC_RIDEBG, false).toBool());
|
|
field->addWidget(rideBG);
|
|
|
|
addButton = new QPushButton(tr("+"));
|
|
deleteButton = new QPushButton(tr("-"));
|
|
#ifndef Q_OS_MAC
|
|
upButton = new QToolButton(this);
|
|
downButton = new QToolButton(this);
|
|
upButton->setArrowType(Qt::UpArrow);
|
|
downButton->setArrowType(Qt::DownArrow);
|
|
upButton->setFixedSize(20,20);
|
|
downButton->setFixedSize(20,20);
|
|
addButton->setFixedSize(20,20);
|
|
deleteButton->setFixedSize(20,20);
|
|
#else
|
|
addButton->setText(tr("Add"));
|
|
deleteButton->setText(tr("Delete"));
|
|
upButton = new QPushButton(tr("Up"));
|
|
downButton = new QPushButton(tr("Down"));
|
|
#endif
|
|
|
|
QHBoxLayout *actionButtons = new QHBoxLayout;
|
|
actionButtons->setSpacing(2);
|
|
actionButtons->addWidget(upButton);
|
|
actionButtons->addWidget(downButton);
|
|
actionButtons->addStretch();
|
|
actionButtons->addWidget(addButton);
|
|
actionButtons->addWidget(deleteButton);
|
|
|
|
keywords = new QTreeWidget;
|
|
keywords->headerItem()->setText(0, tr("Keyword"));
|
|
keywords->headerItem()->setText(1, tr("Color"));
|
|
keywords->headerItem()->setText(2, tr("Related Notes Words"));
|
|
keywords->setColumnCount(3);
|
|
keywords->setSelectionMode(QAbstractItemView::SingleSelection);
|
|
keywords->setEditTriggers(QAbstractItemView::SelectedClicked); // allow edit
|
|
//keywords->setUniformRowHeights(true); // causes height problems when adding - in case of non-text fields
|
|
keywords->setIndentation(0);
|
|
//keywords->header()->resizeSection(0,100);
|
|
//keywords->header()->resizeSection(1,45);
|
|
|
|
foreach(KeywordDefinition keyword, keywordDefinitions) {
|
|
QTreeWidgetItem *add;
|
|
ColorButton *colorButton = new ColorButton(this, keyword.name, keyword.color);
|
|
add = new QTreeWidgetItem(keywords->invisibleRootItem());
|
|
add->setFlags(add->flags() | Qt::ItemIsEditable);
|
|
|
|
// keyword
|
|
add->setText(0, keyword.name);
|
|
|
|
// color button
|
|
add->setTextAlignment(1, Qt::AlignHCenter);
|
|
keywords->setItemWidget(add, 1, colorButton);
|
|
|
|
QString text;
|
|
for (int i=0; i< keyword.tokens.count(); i++) {
|
|
if (i != keyword.tokens.count()-1)
|
|
text += keyword.tokens[i] + ",";
|
|
else
|
|
text += keyword.tokens[i];
|
|
}
|
|
|
|
// notes texts
|
|
add->setText(2, text);
|
|
}
|
|
keywords->setCurrentItem(keywords->invisibleRootItem()->child(0));
|
|
|
|
mainLayout->addWidget(keywords);
|
|
mainLayout->addLayout(actionButtons);
|
|
|
|
// connect up slots
|
|
connect(upButton, SIGNAL(clicked()), this, SLOT(upClicked()));
|
|
connect(downButton, SIGNAL(clicked()), this, SLOT(downClicked()));
|
|
connect(addButton, SIGNAL(clicked()), this, SLOT(addClicked()));
|
|
connect(deleteButton, SIGNAL(clicked()), this, SLOT(deleteClicked()));
|
|
|
|
connect(fieldChooser, SIGNAL(currentIndexChanged(int)), this, SLOT(colorfieldChanged()));
|
|
}
|
|
|
|
void
|
|
KeywordsPage::pageSelected()
|
|
{
|
|
SpecialFields sp;
|
|
QString prev = "";
|
|
|
|
// remember what was selected, if anything?
|
|
if (fieldChooser->count()) {
|
|
prev = sp.internalName(fieldChooser->itemText(fieldChooser->currentIndex()));
|
|
parent->colorfield = prev;
|
|
} else prev = parent->colorfield;
|
|
// load in texts from metadata
|
|
fieldChooser->clear();
|
|
|
|
// get the current fields definitions
|
|
QList<FieldDefinition> fromFieldsPage;
|
|
parent->fieldsPage->getDefinitions(fromFieldsPage);
|
|
foreach(FieldDefinition x, fromFieldsPage) {
|
|
if (x.type < 3) fieldChooser->addItem(sp.displayName(x.name));
|
|
}
|
|
fieldChooser->setCurrentIndex(fieldChooser->findText(sp.displayName(prev)));
|
|
}
|
|
|
|
void
|
|
KeywordsPage::colorfieldChanged()
|
|
{
|
|
SpecialFields sp;
|
|
int index = fieldChooser->currentIndex();
|
|
if (index >=0) parent->colorfield = sp.internalName(fieldChooser->itemText(fieldChooser->currentIndex()));
|
|
}
|
|
|
|
|
|
void
|
|
KeywordsPage::upClicked()
|
|
{
|
|
if (keywords->currentItem()) {
|
|
int index = keywords->invisibleRootItem()->indexOfChild(keywords->currentItem());
|
|
if (index == 0) return; // its at the top already
|
|
|
|
// movin on up!
|
|
QWidget *button = keywords->itemWidget(keywords->currentItem(),1);
|
|
ColorButton *colorButton = new ColorButton(this, ((ColorButton*)button)->getName(), ((ColorButton*)button)->getColor());
|
|
QTreeWidgetItem* moved = keywords->invisibleRootItem()->takeChild(index);
|
|
keywords->invisibleRootItem()->insertChild(index-1, moved);
|
|
keywords->setItemWidget(moved, 1, colorButton);
|
|
keywords->setCurrentItem(moved);
|
|
//LTMSettings save = (*presets)[index];
|
|
//presets->removeAt(index);
|
|
//presets->insert(index-1, save);
|
|
}
|
|
}
|
|
|
|
void
|
|
KeywordsPage::downClicked()
|
|
{
|
|
if (keywords->currentItem()) {
|
|
int index = keywords->invisibleRootItem()->indexOfChild(keywords->currentItem());
|
|
if (index == (keywords->invisibleRootItem()->childCount()-1)) return; // its at the bottom already
|
|
|
|
// movin on up!
|
|
QWidget *button = keywords->itemWidget(keywords->currentItem(),1);
|
|
ColorButton *colorButton = new ColorButton(this, ((ColorButton*)button)->getName(), ((ColorButton*)button)->getColor());
|
|
QTreeWidgetItem* moved = keywords->invisibleRootItem()->takeChild(index);
|
|
keywords->invisibleRootItem()->insertChild(index+1, moved);
|
|
keywords->setItemWidget(moved, 1, colorButton);
|
|
keywords->setCurrentItem(moved);
|
|
}
|
|
}
|
|
|
|
void
|
|
KeywordsPage::renameClicked()
|
|
{
|
|
// which one is selected?
|
|
if (keywords->currentItem()) keywords->editItem(keywords->currentItem(), 0);
|
|
}
|
|
|
|
void
|
|
KeywordsPage::addClicked()
|
|
{
|
|
int index = keywords->invisibleRootItem()->indexOfChild(keywords->currentItem());
|
|
if (index < 0) index = 0;
|
|
QTreeWidgetItem *add;
|
|
ColorButton *colorButton = new ColorButton(this, tr("New"), QColor(Qt::blue));
|
|
add = new QTreeWidgetItem;
|
|
keywords->invisibleRootItem()->insertChild(index, add);
|
|
add->setFlags(add->flags() | Qt::ItemIsEditable);
|
|
|
|
// keyword
|
|
QString text = tr("New");
|
|
for (int i=0; keywords->findItems(text, Qt::MatchExactly, 0).count() > 0; i++) {
|
|
text = QString(tr("New (%1)")).arg(i+1);
|
|
}
|
|
add->setText(0, text);
|
|
|
|
// color button
|
|
add->setTextAlignment(1, Qt::AlignHCenter);
|
|
keywords->setItemWidget(add, 1, colorButton);
|
|
|
|
// notes texts
|
|
add->setText(2, "");
|
|
}
|
|
|
|
void
|
|
KeywordsPage::deleteClicked()
|
|
{
|
|
if (keywords->currentItem()) {
|
|
int index = keywords->invisibleRootItem()->indexOfChild(keywords->currentItem());
|
|
|
|
// zap!
|
|
delete keywords->invisibleRootItem()->takeChild(index);
|
|
}
|
|
}
|
|
|
|
void
|
|
KeywordsPage::getDefinitions(QList<KeywordDefinition> &keywordList)
|
|
{
|
|
// clear current just in case
|
|
keywordList.clear();
|
|
|
|
for (int idx =0; idx < keywords->invisibleRootItem()->childCount(); idx++) {
|
|
KeywordDefinition add;
|
|
QTreeWidgetItem *item = keywords->invisibleRootItem()->child(idx);
|
|
|
|
add.name = item->text(0);
|
|
add.color = ((ColorButton*)keywords->itemWidget(item, 1))->getColor();
|
|
add.tokens = item->text(2).split(",", QString::SkipEmptyParts);
|
|
|
|
keywordList.append(add);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Ride metadata page
|
|
//
|
|
FieldsPage::FieldsPage(QWidget *parent, QList<FieldDefinition>fieldDefinitions) : QWidget(parent)
|
|
{
|
|
QGridLayout *mainLayout = new QGridLayout(this);
|
|
HelpWhatsThis *help = new HelpWhatsThis(this);
|
|
this->setWhatsThis(help->getWhatsThisText(HelpWhatsThis::Preferences_DataFields_Fields));
|
|
|
|
addButton = new QPushButton(tr("+"));
|
|
deleteButton = new QPushButton(tr("-"));
|
|
#ifndef Q_OS_MAC
|
|
upButton = new QToolButton(this);
|
|
downButton = new QToolButton(this);
|
|
upButton->setArrowType(Qt::UpArrow);
|
|
downButton->setArrowType(Qt::DownArrow);
|
|
upButton->setFixedSize(20,20);
|
|
downButton->setFixedSize(20,20);
|
|
addButton->setFixedSize(20,20);
|
|
deleteButton->setFixedSize(20,20);
|
|
#else
|
|
addButton->setText(tr("Add"));
|
|
deleteButton->setText(tr("Delete"));
|
|
upButton = new QPushButton(tr("Up"));
|
|
downButton = new QPushButton(tr("Down"));
|
|
#endif
|
|
QHBoxLayout *actionButtons = new QHBoxLayout;
|
|
actionButtons->setSpacing(2);
|
|
actionButtons->addWidget(upButton);
|
|
actionButtons->addWidget(downButton);
|
|
actionButtons->addStretch();
|
|
actionButtons->addWidget(addButton);
|
|
actionButtons->addWidget(deleteButton);
|
|
|
|
fields = new QTreeWidget;
|
|
fields->headerItem()->setText(0, tr("Screen Tab"));
|
|
fields->headerItem()->setText(1, tr("Field"));
|
|
fields->headerItem()->setText(2, tr("Type"));
|
|
fields->headerItem()->setText(3, tr("Values"));
|
|
fields->headerItem()->setText(4, tr("Diary"));
|
|
fields->setColumnWidth(0,80);
|
|
fields->setColumnWidth(1,100);
|
|
fields->setColumnWidth(2,100);
|
|
fields->setColumnWidth(3,80);
|
|
fields->setColumnWidth(4,20);
|
|
fields->setColumnCount(5);
|
|
fields->setSelectionMode(QAbstractItemView::SingleSelection);
|
|
fields->setEditTriggers(QAbstractItemView::SelectedClicked); // allow edit
|
|
//fields->setUniformRowHeights(true); // causes height problems when adding - in case of non-text fields
|
|
fields->setIndentation(0);
|
|
|
|
SpecialFields specials;
|
|
SpecialTabs specialTabs;
|
|
foreach(FieldDefinition field, fieldDefinitions) {
|
|
QTreeWidgetItem *add;
|
|
QComboBox *comboButton = new QComboBox(this);
|
|
QCheckBox *checkBox = new QCheckBox("", this);
|
|
checkBox->setChecked(field.diary);
|
|
|
|
addFieldTypes(comboButton);
|
|
comboButton->setCurrentIndex(field.type);
|
|
|
|
add = new QTreeWidgetItem(fields->invisibleRootItem());
|
|
add->setFlags(add->flags() | Qt::ItemIsEditable);
|
|
|
|
// tab name
|
|
add->setText(0, specialTabs.displayName(field.tab));
|
|
// field name
|
|
add->setText(1, specials.displayName(field.name));
|
|
// values
|
|
add->setText(3, field.values.join(","));
|
|
|
|
// type button
|
|
add->setTextAlignment(2, Qt::AlignHCenter);
|
|
fields->setItemWidget(add, 2, comboButton);
|
|
fields->setItemWidget(add, 4, checkBox);
|
|
}
|
|
fields->setCurrentItem(fields->invisibleRootItem()->child(0));
|
|
|
|
mainLayout->addWidget(fields, 0,0);
|
|
mainLayout->addLayout(actionButtons, 1,0);
|
|
|
|
// connect up slots
|
|
connect(upButton, SIGNAL(clicked()), this, SLOT(upClicked()));
|
|
connect(downButton, SIGNAL(clicked()), this, SLOT(downClicked()));
|
|
connect(addButton, SIGNAL(clicked()), this, SLOT(addClicked()));
|
|
connect(deleteButton, SIGNAL(clicked()), this, SLOT(deleteClicked()));
|
|
}
|
|
|
|
void
|
|
FieldsPage::upClicked()
|
|
{
|
|
if (fields->currentItem()) {
|
|
int index = fields->invisibleRootItem()->indexOfChild(fields->currentItem());
|
|
if (index == 0) return; // its at the top already
|
|
|
|
// movin on up!
|
|
QWidget *button = fields->itemWidget(fields->currentItem(),2);
|
|
QWidget *check = fields->itemWidget(fields->currentItem(),4);
|
|
QComboBox *comboButton = new QComboBox(this);
|
|
addFieldTypes(comboButton);
|
|
comboButton->setCurrentIndex(((QComboBox*)button)->currentIndex());
|
|
QCheckBox *checkBox = new QCheckBox("", this);
|
|
checkBox->setChecked(((QCheckBox*)check)->isChecked());
|
|
QTreeWidgetItem* moved = fields->invisibleRootItem()->takeChild(index);
|
|
fields->invisibleRootItem()->insertChild(index-1, moved);
|
|
fields->setItemWidget(moved, 2, comboButton);
|
|
fields->setItemWidget(moved, 4, checkBox);
|
|
fields->setCurrentItem(moved);
|
|
}
|
|
}
|
|
|
|
void
|
|
FieldsPage::downClicked()
|
|
{
|
|
if (fields->currentItem()) {
|
|
int index = fields->invisibleRootItem()->indexOfChild(fields->currentItem());
|
|
if (index == (fields->invisibleRootItem()->childCount()-1)) return; // its at the bottom already
|
|
|
|
QWidget *button = fields->itemWidget(fields->currentItem(),2);
|
|
QWidget *check = fields->itemWidget(fields->currentItem(),4);
|
|
QComboBox *comboButton = new QComboBox(this);
|
|
addFieldTypes(comboButton);
|
|
comboButton->setCurrentIndex(((QComboBox*)button)->currentIndex());
|
|
QCheckBox *checkBox = new QCheckBox("", this);
|
|
checkBox->setChecked(((QCheckBox*)check)->isChecked());
|
|
QTreeWidgetItem* moved = fields->invisibleRootItem()->takeChild(index);
|
|
fields->invisibleRootItem()->insertChild(index+1, moved);
|
|
fields->setItemWidget(moved, 2, comboButton);
|
|
fields->setItemWidget(moved, 4, checkBox);
|
|
fields->setCurrentItem(moved);
|
|
}
|
|
}
|
|
|
|
void
|
|
FieldsPage::renameClicked()
|
|
{
|
|
// which one is selected?
|
|
if (fields->currentItem()) fields->editItem(fields->currentItem(), 0);
|
|
}
|
|
|
|
void
|
|
FieldsPage::addClicked()
|
|
{
|
|
int index = fields->invisibleRootItem()->indexOfChild(fields->currentItem());
|
|
if (index < 0) index = 0;
|
|
QTreeWidgetItem *add;
|
|
QComboBox *comboButton = new QComboBox(this);
|
|
addFieldTypes(comboButton);
|
|
QCheckBox *checkBox = new QCheckBox("", this);
|
|
|
|
add = new QTreeWidgetItem;
|
|
fields->invisibleRootItem()->insertChild(index, add);
|
|
add->setFlags(add->flags() | Qt::ItemIsEditable);
|
|
|
|
// field
|
|
QString text = tr("New");
|
|
for (int i=0; fields->findItems(text, Qt::MatchExactly, 1).count() > 0; i++) {
|
|
text = QString(tr("New (%1)")).arg(i+1);
|
|
}
|
|
add->setText(1, text);
|
|
|
|
// type button
|
|
add->setTextAlignment(2, Qt::AlignHCenter);
|
|
fields->setItemWidget(add, 2, comboButton);
|
|
fields->setItemWidget(add, 4, checkBox);
|
|
}
|
|
|
|
void
|
|
FieldsPage::deleteClicked()
|
|
{
|
|
if (fields->currentItem()) {
|
|
int index = fields->invisibleRootItem()->indexOfChild(fields->currentItem());
|
|
|
|
// zap!
|
|
delete fields->invisibleRootItem()->takeChild(index);
|
|
}
|
|
}
|
|
|
|
void
|
|
FieldsPage::getDefinitions(QList<FieldDefinition> &fieldList)
|
|
{
|
|
SpecialFields sp;
|
|
SpecialTabs st;
|
|
QStringList checkdups;
|
|
|
|
// clear current just in case
|
|
fieldList.clear();
|
|
|
|
for (int idx =0; idx < fields->invisibleRootItem()->childCount(); idx++) {
|
|
|
|
FieldDefinition add;
|
|
QTreeWidgetItem *item = fields->invisibleRootItem()->child(idx);
|
|
|
|
// silently ignore duplicates
|
|
if (checkdups.contains(item->text(1))) continue;
|
|
else checkdups << item->text(1);
|
|
|
|
add.tab = st.internalName(item->text(0));
|
|
add.name = sp.internalName(item->text(1));
|
|
add.values = item->text(3).split(QRegExp("(, *|,)"), QString::KeepEmptyParts);
|
|
add.diary = ((QCheckBox*)fields->itemWidget(item, 4))->isChecked();
|
|
|
|
if (sp.isMetric(add.name))
|
|
add.type = 4;
|
|
else
|
|
add.type = ((QComboBox*)fields->itemWidget(item, 2))->currentIndex();
|
|
|
|
fieldList.append(add);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Data processors config page
|
|
//
|
|
ProcessorPage::ProcessorPage(Context *context) : context(context)
|
|
{
|
|
// get the available processors
|
|
const DataProcessorFactory &factory = DataProcessorFactory::instance();
|
|
processors = factory.getProcessors();
|
|
|
|
QGridLayout *mainLayout = new QGridLayout(this);
|
|
|
|
processorTree = new QTreeWidget;
|
|
processorTree->headerItem()->setText(0, tr("Processor"));
|
|
processorTree->headerItem()->setText(1, tr("Apply"));
|
|
processorTree->headerItem()->setText(2, tr("Settings"));
|
|
processorTree->headerItem()->setText(3, "Technical Name of Processor"); // add invisible Column with technical name
|
|
processorTree->setColumnCount(4);
|
|
processorTree->setSelectionMode(QAbstractItemView::NoSelection);
|
|
processorTree->setEditTriggers(QAbstractItemView::SelectedClicked); // allow edit
|
|
processorTree->setUniformRowHeights(true);
|
|
processorTree->setIndentation(0);
|
|
//processorTree->header()->resizeSection(0,150);
|
|
HelpWhatsThis *help = new HelpWhatsThis(processorTree);
|
|
processorTree->setWhatsThis(help->getWhatsThisText(HelpWhatsThis::Preferences_DataFields_Processing));
|
|
|
|
// iterate over all the processors and add an entry to the
|
|
QMapIterator<QString, DataProcessor*> i(processors);
|
|
i.toFront();
|
|
while (i.hasNext()) {
|
|
i.next();
|
|
|
|
QTreeWidgetItem *add;
|
|
|
|
add = new QTreeWidgetItem(processorTree->invisibleRootItem());
|
|
add->setFlags(add->flags() & ~Qt::ItemIsEditable);
|
|
|
|
// Processor Name: it shows the localized name
|
|
add->setText(0, i.value()->name());
|
|
// Processer Key - for Settings
|
|
add->setText(3, i.key());
|
|
|
|
// Auto or Manual run?
|
|
QComboBox *comboButton = new QComboBox(this);
|
|
comboButton->addItem(tr("Manual"));
|
|
comboButton->addItem(tr("Import"));
|
|
processorTree->setItemWidget(add, 1, comboButton);
|
|
|
|
QString configsetting = QString("dp/%1/apply").arg(i.key());
|
|
if (appsettings->value(NULL, GC_QSETTINGS_GLOBAL_GENERAL+configsetting, "Manual").toString() == "Manual")
|
|
comboButton->setCurrentIndex(0);
|
|
else
|
|
comboButton->setCurrentIndex(1);
|
|
|
|
// Get and Set the Config Widget
|
|
DataProcessorConfig *config = i.value()->processorConfig(this);
|
|
config->readConfig();
|
|
|
|
processorTree->setItemWidget(add, 2, config);
|
|
|
|
}
|
|
processorTree->setColumnHidden(3, true);
|
|
|
|
mainLayout->addWidget(processorTree, 0,0);
|
|
}
|
|
|
|
qint32
|
|
ProcessorPage::saveClicked()
|
|
{
|
|
// call each processor config widget's saveConfig() to
|
|
// write away separately
|
|
for (int i=0; i<processorTree->invisibleRootItem()->childCount(); i++) {
|
|
// auto (which means run on import) or manual?
|
|
QString configsetting = QString("dp/%1/apply").arg(processorTree->invisibleRootItem()->child(i)->text(3));
|
|
QString apply = ((QComboBox*)(processorTree->itemWidget(processorTree->invisibleRootItem()->child(i), 1)))->currentIndex() ?
|
|
"Auto" : "Manual";
|
|
appsettings->setValue(GC_QSETTINGS_GLOBAL_GENERAL+configsetting, apply);
|
|
((DataProcessorConfig*)(processorTree->itemWidget(processorTree->invisibleRootItem()->child(i), 2)))->saveConfig();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// Default values page
|
|
//
|
|
DefaultsPage::DefaultsPage(QWidget *parent, QList<DefaultDefinition>defaultDefinitions) : QWidget(parent)
|
|
{
|
|
QGridLayout *mainLayout = new QGridLayout(this);
|
|
HelpWhatsThis *help = new HelpWhatsThis(this);
|
|
this->setWhatsThis(help->getWhatsThisText(HelpWhatsThis::Preferences_DataFields_Defaults));
|
|
|
|
addButton = new QPushButton(tr("+"));
|
|
deleteButton = new QPushButton(tr("-"));
|
|
#ifndef Q_OS_MAC
|
|
upButton = new QToolButton(this);
|
|
downButton = new QToolButton(this);
|
|
upButton->setArrowType(Qt::UpArrow);
|
|
downButton->setArrowType(Qt::DownArrow);
|
|
upButton->setFixedSize(20,20);
|
|
downButton->setFixedSize(20,20);
|
|
addButton->setFixedSize(20,20);
|
|
deleteButton->setFixedSize(20,20);
|
|
#else
|
|
addButton->setText(tr("Add"));
|
|
deleteButton->setText(tr("Delete"));
|
|
upButton = new QPushButton(tr("Up"));
|
|
downButton = new QPushButton(tr("Down"));
|
|
#endif
|
|
QHBoxLayout *actionButtons = new QHBoxLayout;
|
|
actionButtons->setSpacing(2);
|
|
actionButtons->addWidget(upButton);
|
|
actionButtons->addWidget(downButton);
|
|
actionButtons->addStretch();
|
|
actionButtons->addWidget(addButton);
|
|
actionButtons->addWidget(deleteButton);
|
|
|
|
defaults = new QTreeWidget;
|
|
defaults->headerItem()->setText(0, tr("Field"));
|
|
defaults->headerItem()->setText(1, tr("Value"));
|
|
defaults->headerItem()->setText(2, tr("Linked field"));
|
|
defaults->headerItem()->setText(3, tr("Default Value"));
|
|
defaults->setColumnWidth(0,80);
|
|
defaults->setColumnWidth(1,100);
|
|
defaults->setColumnWidth(2,80);
|
|
defaults->setColumnWidth(3,100);
|
|
defaults->setColumnCount(4);
|
|
defaults->setSelectionMode(QAbstractItemView::SingleSelection);
|
|
defaults->setEditTriggers(QAbstractItemView::SelectedClicked); // allow edit
|
|
//defaults->setUniformRowHeights(true); // causes height problems when adding - in case of non-text fields
|
|
defaults->setIndentation(0);
|
|
|
|
SpecialFields specials;
|
|
foreach(DefaultDefinition adefault, defaultDefinitions) {
|
|
QTreeWidgetItem *add;
|
|
|
|
add = new QTreeWidgetItem(defaults->invisibleRootItem());
|
|
add->setFlags(add->flags() | Qt::ItemIsEditable);
|
|
|
|
// field name
|
|
add->setText(0, specials.displayName(adefault.field));
|
|
// value
|
|
add->setText(1, adefault.value);
|
|
// Linked field
|
|
add->setText(2, adefault.linkedField);
|
|
// Default Value
|
|
add->setText(3, adefault.linkedValue);
|
|
}
|
|
defaults->setCurrentItem(defaults->invisibleRootItem()->child(0));
|
|
|
|
mainLayout->addWidget(defaults, 0,0);
|
|
mainLayout->addLayout(actionButtons, 1,0);
|
|
|
|
// connect up slots
|
|
connect(upButton, SIGNAL(clicked()), this, SLOT(upClicked()));
|
|
connect(downButton, SIGNAL(clicked()), this, SLOT(downClicked()));
|
|
connect(addButton, SIGNAL(clicked()), this, SLOT(addClicked()));
|
|
connect(deleteButton, SIGNAL(clicked()), this, SLOT(deleteClicked()));
|
|
}
|
|
|
|
void
|
|
DefaultsPage::upClicked()
|
|
{
|
|
if (defaults->currentItem()) {
|
|
int index = defaults->invisibleRootItem()->indexOfChild(defaults->currentItem());
|
|
if (index == 0) return; // its at the top already
|
|
|
|
QTreeWidgetItem* moved = defaults->invisibleRootItem()->takeChild(index);
|
|
defaults->invisibleRootItem()->insertChild(index-1, moved);
|
|
defaults->setCurrentItem(moved);
|
|
}
|
|
}
|
|
|
|
void
|
|
DefaultsPage::downClicked()
|
|
{
|
|
if (defaults->currentItem()) {
|
|
int index = defaults->invisibleRootItem()->indexOfChild(defaults->currentItem());
|
|
if (index == (defaults->invisibleRootItem()->childCount()-1)) return; // its at the bottom already
|
|
|
|
QTreeWidgetItem* moved = defaults->invisibleRootItem()->takeChild(index);
|
|
defaults->invisibleRootItem()->insertChild(index+1, moved);
|
|
defaults->setCurrentItem(moved);
|
|
}
|
|
}
|
|
|
|
void
|
|
DefaultsPage::addClicked()
|
|
{
|
|
int index = defaults->invisibleRootItem()->indexOfChild(defaults->currentItem());
|
|
if (index < 0) index = 0;
|
|
QTreeWidgetItem *add;
|
|
|
|
add = new QTreeWidgetItem;
|
|
defaults->invisibleRootItem()->insertChild(index, add);
|
|
add->setFlags(add->flags() | Qt::ItemIsEditable);
|
|
|
|
// field
|
|
QString text = tr("New");
|
|
for (int i=0; defaults->findItems(text, Qt::MatchExactly, 1).count() > 0; i++) {
|
|
text = QString(tr("New (%1)")).arg(i+1);
|
|
}
|
|
add->setText(0, text);
|
|
}
|
|
|
|
void
|
|
DefaultsPage::deleteClicked()
|
|
{
|
|
if (defaults->currentItem()) {
|
|
int index = defaults->invisibleRootItem()->indexOfChild(defaults->currentItem());
|
|
|
|
// zap!
|
|
delete defaults->invisibleRootItem()->takeChild(index);
|
|
}
|
|
}
|
|
|
|
void
|
|
DefaultsPage::getDefinitions(QList<DefaultDefinition> &defaultList)
|
|
{
|
|
SpecialFields sp;
|
|
|
|
// clear current just in case
|
|
defaultList.clear();
|
|
|
|
for (int idx =0; idx < defaults->invisibleRootItem()->childCount(); idx++) {
|
|
|
|
DefaultDefinition add;
|
|
QTreeWidgetItem *item = defaults->invisibleRootItem()->child(idx);
|
|
|
|
add.field = sp.internalName(item->text(0));
|
|
add.value = item->text(1);
|
|
add.linkedField = sp.internalName(item->text(2));
|
|
add.linkedValue = item->text(3);
|
|
|
|
defaultList.append(add);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Power Zone Config page
|
|
//
|
|
ZonePage::ZonePage(Context *context) : context(context)
|
|
{
|
|
QVBoxLayout *layout = new QVBoxLayout(this);
|
|
|
|
// get current config by reading it in (leave mainwindow zones alone)
|
|
QFile zonesFile(context->athlete->home->config().canonicalPath() + "/power.zones");
|
|
if (zonesFile.exists()) {
|
|
zones.read(zonesFile);
|
|
zonesFile.close();
|
|
b4Fingerprint = zones.getFingerprint(); // remember original state
|
|
}
|
|
|
|
// setup maintenance pages using current config
|
|
schemePage = new SchemePage(this);
|
|
cpPage = new CPPage(this);
|
|
|
|
tabs = new QTabWidget(this);
|
|
tabs->addTab(cpPage, tr("Critical Power"));
|
|
tabs->addTab(schemePage, tr("Default"));
|
|
|
|
layout->addWidget(tabs);
|
|
}
|
|
|
|
qint32
|
|
ZonePage::saveClicked()
|
|
{
|
|
// write
|
|
zones.setScheme(schemePage->getScheme());
|
|
zones.write(context->athlete->home->config());
|
|
|
|
// re-read Zones in case it changed
|
|
QFile zonesFile(context->athlete->home->config().canonicalPath() + "/power.zones");
|
|
context->athlete->zones_->read(zonesFile);
|
|
|
|
// use CP for FTP?
|
|
appsettings->setCValue(context->athlete->cyclist, GC_USE_CP_FOR_FTP, cpPage->useCPForFTPCheckBox->checkState());
|
|
|
|
|
|
// did we change ?
|
|
if (zones.getFingerprint() != b4Fingerprint) return CONFIG_ZONES;
|
|
else return 0;
|
|
}
|
|
|
|
SchemePage::SchemePage(ZonePage* zonePage) : zonePage(zonePage)
|
|
{
|
|
QVBoxLayout *mainLayout = new QVBoxLayout(this);
|
|
|
|
addButton = new QPushButton(tr("+"));
|
|
deleteButton = new QPushButton(tr("-"));
|
|
#ifndef Q_OS_MAC
|
|
addButton->setFixedSize(20,20);
|
|
deleteButton->setFixedSize(20,20);
|
|
#else
|
|
addButton->setText(tr("Add"));
|
|
deleteButton->setText(tr("Delete"));
|
|
#endif
|
|
QHBoxLayout *actionButtons = new QHBoxLayout;
|
|
actionButtons->setSpacing(2);
|
|
actionButtons->addStretch();
|
|
actionButtons->addWidget(addButton);
|
|
actionButtons->addWidget(deleteButton);
|
|
|
|
scheme = new QTreeWidget;
|
|
scheme->headerItem()->setText(0, tr("Short"));
|
|
scheme->headerItem()->setText(1, tr("Long"));
|
|
scheme->headerItem()->setText(2, tr("Percent of CP"));
|
|
scheme->setColumnCount(3);
|
|
scheme->setSelectionMode(QAbstractItemView::SingleSelection);
|
|
scheme->setEditTriggers(QAbstractItemView::SelectedClicked); // allow edit
|
|
scheme->setUniformRowHeights(true);
|
|
scheme->setIndentation(0);
|
|
//scheme->header()->resizeSection(0,90);
|
|
//scheme->header()->resizeSection(1,200);
|
|
//scheme->header()->resizeSection(2,80);
|
|
|
|
// setup list
|
|
for (int i=0; i< zonePage->zones.getScheme().nzones_default; i++) {
|
|
|
|
QTreeWidgetItem *add = new QTreeWidgetItem(scheme->invisibleRootItem());
|
|
add->setFlags(add->flags() | Qt::ItemIsEditable);
|
|
|
|
// tab name
|
|
add->setText(0, zonePage->zones.getScheme().zone_default_name[i]);
|
|
// field name
|
|
add->setText(1, zonePage->zones.getScheme().zone_default_desc[i]);
|
|
|
|
// low
|
|
QDoubleSpinBox *loedit = new QDoubleSpinBox(this);
|
|
loedit->setMinimum(0);
|
|
loedit->setMaximum(1000);
|
|
loedit->setSingleStep(1.0);
|
|
loedit->setDecimals(0);
|
|
loedit->setValue(zonePage->zones.getScheme().zone_default[i]);
|
|
scheme->setItemWidget(add, 2, loedit);
|
|
}
|
|
|
|
mainLayout->addWidget(scheme);
|
|
mainLayout->addLayout(actionButtons);
|
|
|
|
// button connect
|
|
connect(addButton, SIGNAL(clicked()), this, SLOT(addClicked()));
|
|
connect(deleteButton, SIGNAL(clicked()), this, SLOT(deleteClicked()));
|
|
}
|
|
|
|
void
|
|
SchemePage::addClicked()
|
|
{
|
|
// are we at maximum already?
|
|
if (scheme->invisibleRootItem()->childCount() == 10) {
|
|
QMessageBox err;
|
|
err.setText(tr("Maximum of 10 zones reached."));
|
|
err.setIcon(QMessageBox::Warning);
|
|
err.exec();
|
|
return;
|
|
}
|
|
|
|
int index = scheme->invisibleRootItem()->childCount();
|
|
|
|
// new item
|
|
QTreeWidgetItem *add = new QTreeWidgetItem;
|
|
add->setFlags(add->flags() | Qt::ItemIsEditable);
|
|
|
|
QDoubleSpinBox *loedit = new QDoubleSpinBox(this);
|
|
loedit->setMinimum(0);
|
|
loedit->setMaximum(1000);
|
|
loedit->setSingleStep(1.0);
|
|
loedit->setDecimals(0);
|
|
loedit->setValue(100);
|
|
|
|
scheme->invisibleRootItem()->insertChild(index, add);
|
|
scheme->setItemWidget(add, 2, loedit);
|
|
|
|
// Short
|
|
QString text = tr("New");
|
|
for (int i=0; scheme->findItems(text, Qt::MatchExactly, 0).count() > 0; i++) {
|
|
text = QString(tr("New (%1)")).arg(i+1);
|
|
}
|
|
add->setText(0, text);
|
|
|
|
// long
|
|
text = tr("New");
|
|
for (int i=0; scheme->findItems(text, Qt::MatchExactly, 1).count() > 0; i++) {
|
|
text = QString(tr("New (%1)")).arg(i+1);
|
|
}
|
|
add->setText(1, text);
|
|
}
|
|
|
|
void
|
|
SchemePage::renameClicked()
|
|
{
|
|
// which one is selected?
|
|
if (scheme->currentItem()) scheme->editItem(scheme->currentItem(), 0);
|
|
}
|
|
|
|
void
|
|
SchemePage::deleteClicked()
|
|
{
|
|
if (scheme->currentItem()) {
|
|
int index = scheme->invisibleRootItem()->indexOfChild(scheme->currentItem());
|
|
delete scheme->invisibleRootItem()->takeChild(index);
|
|
}
|
|
}
|
|
|
|
// just for qSorting
|
|
struct schemeitem {
|
|
QString name, desc;
|
|
int lo;
|
|
double trimp;
|
|
bool operator<(schemeitem right) const { return lo < right.lo; }
|
|
};
|
|
|
|
ZoneScheme
|
|
SchemePage::getScheme()
|
|
{
|
|
// read the scheme widget and return a scheme object
|
|
QList<schemeitem> table;
|
|
ZoneScheme results;
|
|
|
|
// read back the details from the table
|
|
for (int i=0; i<scheme->invisibleRootItem()->childCount(); i++) {
|
|
|
|
schemeitem add;
|
|
add.name = scheme->invisibleRootItem()->child(i)->text(0);
|
|
add.desc = scheme->invisibleRootItem()->child(i)->text(1);
|
|
add.lo = ((QDoubleSpinBox *)(scheme->itemWidget(scheme->invisibleRootItem()->child(i), 2)))->value();
|
|
table.append(add);
|
|
}
|
|
|
|
// sort the list into ascending order
|
|
qSort(table);
|
|
|
|
// now update the results
|
|
results.nzones_default = 0;
|
|
foreach(schemeitem zone, table) {
|
|
results.nzones_default++;
|
|
results.zone_default.append(zone.lo);
|
|
results.zone_default_is_pct.append(true);
|
|
results.zone_default_name.append(zone.name);
|
|
results.zone_default_desc.append(zone.desc);
|
|
}
|
|
|
|
return results;
|
|
}
|
|
|
|
|
|
CPPage::CPPage(ZonePage* zonePage) : zonePage(zonePage)
|
|
{
|
|
active = false;
|
|
|
|
QGridLayout *mainLayout = new QGridLayout(this);
|
|
mainLayout->setSpacing(10);
|
|
|
|
updateButton = new QPushButton(tr("Update"));
|
|
updateButton->hide();
|
|
addButton = new QPushButton(tr("+"));
|
|
deleteButton = new QPushButton(tr("-"));
|
|
#ifndef Q_OS_MAC
|
|
addButton->setFixedSize(20,20);
|
|
updateButton->setFixedSize(60,20);
|
|
deleteButton->setFixedSize(20,20);
|
|
#else
|
|
addButton->setText(tr("Add"));
|
|
updateButton->setText(tr("Update"));
|
|
deleteButton->setText(tr("Delete"));
|
|
#endif
|
|
defaultButton = new QPushButton(tr("Def"));
|
|
defaultButton->hide();
|
|
|
|
addZoneButton = new QPushButton(tr("+"));
|
|
deleteZoneButton = new QPushButton(tr("-"));
|
|
#ifndef Q_OS_MAC
|
|
addZoneButton->setFixedSize(20,20);
|
|
deleteZoneButton->setFixedSize(20,20);
|
|
#else
|
|
addZoneButton->setText(tr("Add"));
|
|
deleteZoneButton->setText(tr("Delete"));
|
|
#endif
|
|
|
|
QHBoxLayout *zoneButtons = new QHBoxLayout;
|
|
zoneButtons->addStretch();
|
|
zoneButtons->setSpacing(0);
|
|
zoneButtons->addWidget(addZoneButton);
|
|
zoneButtons->addWidget(deleteZoneButton);
|
|
zoneButtons->addWidget(defaultButton);
|
|
|
|
QHBoxLayout *addLayout = new QHBoxLayout;
|
|
QLabel *dateLabel = new QLabel(tr("From Date"));
|
|
QLabel *cpLabel = new QLabel(tr("Critical Power"));
|
|
QLabel *ftpLabel = new QLabel(tr("FTP"));
|
|
QLabel *wLabel = new QLabel(tr("W'"));
|
|
QLabel *pmaxLabel = new QLabel(tr("Pmax"));
|
|
dateEdit = new QDateEdit;
|
|
dateEdit->setDate(QDate::currentDate());
|
|
|
|
// Use CP for FTP
|
|
bool useCPForFTP = appsettings->cvalue(zonePage->context->athlete->cyclist, GC_USE_CP_FOR_FTP, Qt::Checked).toBool();
|
|
useCPForFTPCheckBox = new QCheckBox(tr("Use CP for FTP"), this);
|
|
useCPForFTPCheckBox->setCheckState(useCPForFTP ? Qt::Checked : Qt::Unchecked);
|
|
|
|
cpEdit = new QDoubleSpinBox;
|
|
cpEdit->setMinimum(0);
|
|
cpEdit->setMaximum(1000);
|
|
cpEdit->setSingleStep(1.0);
|
|
cpEdit->setDecimals(0);
|
|
|
|
ftpEdit = new QDoubleSpinBox;
|
|
ftpEdit->setMinimum(0);
|
|
ftpEdit->setMaximum(100000);
|
|
ftpEdit->setSingleStep(100);
|
|
ftpEdit->setDecimals(0);
|
|
|
|
wEdit = new QDoubleSpinBox;
|
|
wEdit->setMinimum(0);
|
|
wEdit->setMaximum(100000);
|
|
wEdit->setSingleStep(100);
|
|
wEdit->setDecimals(0);
|
|
|
|
pmaxEdit = new QDoubleSpinBox;
|
|
pmaxEdit->setMinimum(0);
|
|
pmaxEdit->setMaximum(3000);
|
|
pmaxEdit->setSingleStep(1.0);
|
|
pmaxEdit->setDecimals(0);
|
|
|
|
QHBoxLayout *actionButtons = new QHBoxLayout;
|
|
actionButtons->setSpacing(2);
|
|
actionButtons->addWidget(cpLabel);
|
|
actionButtons->addWidget(cpEdit);
|
|
|
|
|
|
actionButtons->addWidget(ftpLabel);
|
|
actionButtons->addWidget(ftpEdit);
|
|
|
|
actionButtons->addWidget(wLabel);
|
|
actionButtons->addWidget(wEdit);
|
|
actionButtons->addWidget(pmaxLabel);
|
|
actionButtons->addWidget(pmaxEdit);
|
|
actionButtons->addStretch();
|
|
actionButtons->addWidget(updateButton);
|
|
actionButtons->addWidget(addButton);
|
|
actionButtons->addWidget(deleteButton);
|
|
//actionButtons->addWidget(defaultButton); // moved to zoneButtons
|
|
|
|
addLayout->addWidget(dateLabel);
|
|
addLayout->addWidget(dateEdit);
|
|
addLayout->addStretch();
|
|
addLayout->addWidget(useCPForFTPCheckBox);
|
|
|
|
ranges = new QTreeWidget;
|
|
int column = 0;
|
|
ranges->headerItem()->setText(column++, tr("From Date"));
|
|
ranges->headerItem()->setText(column++, tr("Critical Power"));
|
|
if (useCPForFTP) {
|
|
ranges->headerItem()->setText(column++, tr("FTP"));
|
|
}
|
|
ranges->headerItem()->setText(column++, tr("W'"));
|
|
ranges->headerItem()->setText(column++, tr("Pmax"));
|
|
ranges->setColumnCount(column);
|
|
ranges->setSelectionMode(QAbstractItemView::SingleSelection);
|
|
//ranges->setEditTriggers(QAbstractItemView::SelectedClicked); // allow edit
|
|
ranges->setUniformRowHeights(true);
|
|
ranges->setIndentation(0);
|
|
//ranges->header()->resizeSection(0,180);
|
|
|
|
// setup list of ranges
|
|
for (int i=0; i< zonePage->zones.getRangeSize(); i++) {
|
|
|
|
QTreeWidgetItem *add = new QTreeWidgetItem(ranges->invisibleRootItem());
|
|
add->setFlags(add->flags() & ~Qt::ItemIsEditable);
|
|
|
|
// Embolden ranges with manually configured zones
|
|
QFont font;
|
|
font.setWeight(zonePage->zones.getZoneRange(i).zonesSetFromCP ?
|
|
QFont::Normal : QFont::Black);
|
|
|
|
int column = 0;
|
|
// date
|
|
add->setText(column, zonePage->zones.getStartDate(i).toString(tr("MMM d, yyyy")));
|
|
add->setFont(column++, font);
|
|
|
|
// CP
|
|
add->setText(column, QString("%1").arg(zonePage->zones.getCP(i)));
|
|
add->setFont(column++, font);
|
|
|
|
if (useCPForFTP) {
|
|
// FTP
|
|
add->setText(column, QString("%1").arg(zonePage->zones.getFTP(i)));
|
|
add->setFont(column++, font);
|
|
}
|
|
|
|
// W'
|
|
add->setText(column, QString("%1").arg(zonePage->zones.getWprime(i)));
|
|
add->setFont(column++, font);
|
|
|
|
// Pmax
|
|
add->setText(column, QString("%1").arg(zonePage->zones.getPmax(i)));
|
|
add->setFont(column++, font);
|
|
|
|
}
|
|
|
|
zones = new QTreeWidget;
|
|
zones->headerItem()->setText(0, tr("Short"));
|
|
zones->headerItem()->setText(1, tr("Long"));
|
|
zones->headerItem()->setText(2, tr("From Watts"));
|
|
zones->setColumnCount(3);
|
|
zones->setSelectionMode(QAbstractItemView::SingleSelection);
|
|
zones->setEditTriggers(QAbstractItemView::SelectedClicked); // allow edit
|
|
zones->setUniformRowHeights(true);
|
|
zones->setIndentation(0);
|
|
//zones->header()->resizeSection(0,80);
|
|
//zones->header()->resizeSection(1,150);
|
|
|
|
mainLayout->addLayout(addLayout, 0,0);
|
|
mainLayout->addLayout(actionButtons, 1,0);
|
|
mainLayout->addWidget(ranges, 2,0);
|
|
mainLayout->addLayout(zoneButtons, 3,0);
|
|
mainLayout->addWidget(zones, 4,0);
|
|
|
|
// edit connect
|
|
connect(dateEdit, SIGNAL(dateChanged(QDate)), this, SLOT(rangeEdited()));
|
|
connect(cpEdit, SIGNAL(valueChanged(double)), this, SLOT(rangeEdited()));
|
|
connect(ftpEdit, SIGNAL(valueChanged(double)), this, SLOT(rangeEdited()));
|
|
connect(wEdit, SIGNAL(valueChanged(double)), this, SLOT(rangeEdited()));
|
|
connect(pmaxEdit, SIGNAL(valueChanged(double)), this, SLOT(rangeEdited()));
|
|
// button connect
|
|
connect(addButton, SIGNAL(clicked()), this, SLOT(addClicked()));
|
|
connect(updateButton, SIGNAL(clicked()), this, SLOT(editClicked()));
|
|
connect(deleteButton, SIGNAL(clicked()), this, SLOT(deleteClicked()));
|
|
connect(defaultButton, SIGNAL(clicked()), this, SLOT(defaultClicked()));
|
|
connect(addZoneButton, SIGNAL(clicked()), this, SLOT(addZoneClicked()));
|
|
connect(deleteZoneButton, SIGNAL(clicked()), this, SLOT(deleteZoneClicked()));
|
|
connect(ranges, SIGNAL(itemSelectionChanged()), this, SLOT(rangeSelectionChanged()));
|
|
connect(zones, SIGNAL(itemChanged(QTreeWidgetItem*, int)), this, SLOT(zonesChanged()));
|
|
}
|
|
|
|
void
|
|
CPPage::addClicked()
|
|
{
|
|
|
|
// get current scheme
|
|
zonePage->zones.setScheme(zonePage->schemePage->getScheme());
|
|
|
|
int cp = cpEdit->value();
|
|
if( cp <= 0 ) {
|
|
QMessageBox err;
|
|
err.setText(tr("CP must be > 0"));
|
|
err.setIcon(QMessageBox::Warning);
|
|
err.exec();
|
|
return;
|
|
}
|
|
|
|
//int index = ranges->invisibleRootItem()->childCount();
|
|
int wp = wEdit->value() ? wEdit->value() : 20000;
|
|
if (wp < 1000) wp *= 1000; // entered in kJ we want joules
|
|
|
|
int pmax = pmaxEdit->value() ? pmaxEdit->value() : 1000;
|
|
|
|
int index = zonePage->zones.addZoneRange(dateEdit->date(), cpEdit->value(), ftpEdit->value(), wp, pmax);
|
|
|
|
// new item
|
|
QTreeWidgetItem *add = new QTreeWidgetItem;
|
|
add->setFlags(add->flags() & ~Qt::ItemIsEditable);
|
|
ranges->invisibleRootItem()->insertChild(index, add);
|
|
|
|
int column = 0;
|
|
|
|
// date
|
|
add->setText(column++, dateEdit->date().toString(tr("MMM d, yyyy")));
|
|
|
|
// CP
|
|
add->setText(column++, QString("%1").arg(cpEdit->value()));
|
|
|
|
// FTP
|
|
add->setText(column++, QString("%1").arg(ftpEdit->value()));
|
|
|
|
// W'
|
|
add->setText(column++, QString("%1").arg(wp));
|
|
|
|
// Pmax
|
|
add->setText(column++, QString("%1").arg(pmax));
|
|
|
|
}
|
|
|
|
void
|
|
CPPage::editClicked()
|
|
{
|
|
// get current scheme
|
|
zonePage->zones.setScheme(zonePage->schemePage->getScheme());
|
|
|
|
int cp = cpEdit->value();
|
|
|
|
if( cp <= 0 ){
|
|
QMessageBox err;
|
|
err.setText(tr("CP must be > 0"));
|
|
err.setIcon(QMessageBox::Warning);
|
|
err.exec();
|
|
return;
|
|
}
|
|
|
|
int ftp = ftpEdit->value() ? ftpEdit->value() : cp;
|
|
int wp = wEdit->value() ? wEdit->value() : 20000;
|
|
if (wp < 1000) wp *= 1000; // entered in kJ we want joules
|
|
|
|
int pmax = pmaxEdit->value() ? pmaxEdit->value() : 1000;
|
|
|
|
QTreeWidgetItem *edit = ranges->selectedItems().at(0);
|
|
int index = ranges->indexOfTopLevelItem(edit);
|
|
|
|
|
|
int columns = 0;
|
|
|
|
// date
|
|
zonePage->zones.setStartDate(index, dateEdit->date());
|
|
edit->setText(columns++, dateEdit->date().toString(tr("MMM d, yyyy")));
|
|
|
|
// CP
|
|
zonePage->zones.setCP(index, cp);
|
|
edit->setText(columns++, QString("%1").arg(cp));
|
|
|
|
zonePage->zones.setFTP(index, ftp);
|
|
edit->setText(columns++, QString("%1").arg(ftp));
|
|
|
|
// W'
|
|
zonePage->zones.setWprime(index, wp);
|
|
edit->setText(columns++, QString("%1").arg(wp));
|
|
|
|
// Pmax
|
|
zonePage->zones.setPmax(index, pmax);
|
|
edit->setText(columns++, QString("%1").arg(pmax));
|
|
|
|
}
|
|
|
|
void
|
|
CPPage::deleteClicked()
|
|
{
|
|
if (ranges->currentItem()) {
|
|
int index = ranges->invisibleRootItem()->indexOfChild(ranges->currentItem());
|
|
delete ranges->invisibleRootItem()->takeChild(index);
|
|
zonePage->zones.deleteRange(index);
|
|
}
|
|
}
|
|
|
|
void
|
|
CPPage::defaultClicked()
|
|
{
|
|
if (ranges->currentItem()) {
|
|
|
|
int index = ranges->invisibleRootItem()->indexOfChild(ranges->currentItem());
|
|
ZoneRange current = zonePage->zones.getZoneRange(index);
|
|
|
|
// unbold
|
|
QFont font;
|
|
font.setWeight(QFont::Normal);
|
|
ranges->currentItem()->setFont(0, font);
|
|
ranges->currentItem()->setFont(1, font);
|
|
ranges->currentItem()->setFont(2, font);
|
|
|
|
|
|
// set the range to use defaults on the scheme page
|
|
zonePage->zones.setScheme(zonePage->schemePage->getScheme());
|
|
zonePage->zones.setZonesFromCP(index);
|
|
|
|
// hide the default button since we are now using defaults
|
|
defaultButton->hide();
|
|
|
|
// update the zones display
|
|
rangeSelectionChanged();
|
|
}
|
|
}
|
|
|
|
void
|
|
CPPage::rangeEdited()
|
|
{
|
|
if (ranges->currentItem()) {
|
|
int index = ranges->invisibleRootItem()->indexOfChild(ranges->currentItem());
|
|
|
|
QDate date = dateEdit->date();
|
|
QDate odate = zonePage->zones.getStartDate(index);
|
|
|
|
int cp = cpEdit->value();
|
|
int ocp = zonePage->zones.getCP(index);
|
|
|
|
int ftp = ftpEdit->value();
|
|
int oftp = zonePage->zones.getFTP(index);
|
|
|
|
int wp = wEdit->value();
|
|
int owp = zonePage->zones.getWprime(index);
|
|
|
|
int pmax = pmaxEdit->value();
|
|
int opmax = zonePage->zones.getPmax(index);
|
|
|
|
if (date != odate || cp != ocp || ftp != oftp || wp != owp || pmax != opmax)
|
|
updateButton->show();
|
|
else
|
|
updateButton->hide();
|
|
}
|
|
}
|
|
|
|
void
|
|
CPPage::rangeSelectionChanged()
|
|
{
|
|
active = true;
|
|
|
|
// wipe away current contents of zones
|
|
foreach (QTreeWidgetItem *item, zones->invisibleRootItem()->takeChildren()) {
|
|
delete zones->itemWidget(item, 2);
|
|
delete item;
|
|
}
|
|
|
|
// fill with current details
|
|
if (ranges->currentItem()) {
|
|
|
|
|
|
int index = ranges->invisibleRootItem()->indexOfChild(ranges->currentItem());
|
|
ZoneRange current = zonePage->zones.getZoneRange(index);
|
|
|
|
dateEdit->setDate(zonePage->zones.getStartDate(index));
|
|
cpEdit->setValue(zonePage->zones.getCP(index));
|
|
ftpEdit->setValue(zonePage->zones.getFTP(index));
|
|
wEdit->setValue(zonePage->zones.getWprime(index));
|
|
pmaxEdit->setValue(zonePage->zones.getPmax(index));
|
|
|
|
if (current.zonesSetFromCP) {
|
|
|
|
// reapply the scheme in case it has been changed
|
|
zonePage->zones.setScheme(zonePage->schemePage->getScheme());
|
|
zonePage->zones.setZonesFromCP(index);
|
|
current = zonePage->zones.getZoneRange(index);
|
|
|
|
defaultButton->hide();
|
|
|
|
} else defaultButton->show();
|
|
|
|
for (int i=0; i< current.zones.count(); i++) {
|
|
|
|
QTreeWidgetItem *add = new QTreeWidgetItem(zones->invisibleRootItem());
|
|
add->setFlags(add->flags() | Qt::ItemIsEditable);
|
|
|
|
// tab name
|
|
add->setText(0, current.zones[i].name);
|
|
// field name
|
|
add->setText(1, current.zones[i].desc);
|
|
|
|
// low
|
|
QDoubleSpinBox *loedit = new QDoubleSpinBox(this);
|
|
loedit->setMinimum(0);
|
|
loedit->setMaximum(1000);
|
|
loedit->setSingleStep(1.0);
|
|
loedit->setDecimals(0);
|
|
loedit->setValue(current.zones[i].lo);
|
|
zones->setItemWidget(add, 2, loedit);
|
|
connect(loedit, SIGNAL(valueChanged(double)), this, SLOT(zonesChanged()));
|
|
}
|
|
}
|
|
|
|
active = false;
|
|
}
|
|
|
|
void
|
|
CPPage::addZoneClicked()
|
|
{
|
|
// no range selected
|
|
if (!ranges->currentItem()) return;
|
|
|
|
// are we at maximum already?
|
|
if (zones->invisibleRootItem()->childCount() == 10) {
|
|
QMessageBox err;
|
|
err.setText(tr("Maximum of 10 zones reached."));
|
|
err.setIcon(QMessageBox::Warning);
|
|
err.exec();
|
|
return;
|
|
}
|
|
|
|
active = true;
|
|
int index = zones->invisibleRootItem()->childCount();
|
|
|
|
// new item
|
|
QTreeWidgetItem *add = new QTreeWidgetItem;
|
|
add->setFlags(add->flags() | Qt::ItemIsEditable);
|
|
|
|
QDoubleSpinBox *loedit = new QDoubleSpinBox(this);
|
|
loedit->setMinimum(0);
|
|
loedit->setMaximum(1000);
|
|
loedit->setSingleStep(1.0);
|
|
loedit->setDecimals(0);
|
|
loedit->setValue(100);
|
|
|
|
zones->invisibleRootItem()->insertChild(index, add);
|
|
zones->setItemWidget(add, 2, loedit);
|
|
connect(loedit, SIGNAL(editingFinished()), this, SLOT(zonesChanged()));
|
|
|
|
// Short
|
|
QString text = tr("New");
|
|
for (int i=0; zones->findItems(text, Qt::MatchExactly, 0).count() > 0; i++) {
|
|
text = QString(tr("New (%1)")).arg(i+1);
|
|
}
|
|
add->setText(0, text);
|
|
|
|
// long
|
|
text = tr("New");
|
|
for (int i=0; zones->findItems(text, Qt::MatchExactly, 1).count() > 0; i++) {
|
|
text = QString(tr("New (%1)")).arg(i+1);
|
|
}
|
|
add->setText(1, text);
|
|
active = false;
|
|
|
|
zonesChanged();
|
|
}
|
|
|
|
void
|
|
CPPage::deleteZoneClicked()
|
|
{
|
|
// no range selected
|
|
if (ranges->invisibleRootItem()->indexOfChild(ranges->currentItem()) == -1)
|
|
return;
|
|
|
|
active = true;
|
|
if (zones->currentItem()) {
|
|
int index = zones->invisibleRootItem()->indexOfChild(zones->currentItem());
|
|
delete zones->invisibleRootItem()->takeChild(index);
|
|
}
|
|
active = false;
|
|
|
|
zonesChanged();
|
|
}
|
|
|
|
void
|
|
CPPage::zonesChanged()
|
|
{
|
|
// only take changes when they are not done programmatically
|
|
// the active flag is set when the tree is being modified
|
|
// programmatically, but not when users interact with the widgets
|
|
if (active == false) {
|
|
// get the current zone range
|
|
if (ranges->currentItem()) {
|
|
|
|
int index = ranges->invisibleRootItem()->indexOfChild(ranges->currentItem());
|
|
ZoneRange current = zonePage->zones.getZoneRange(index);
|
|
|
|
// embolden that range on the list to show it has been edited
|
|
QFont font;
|
|
font.setWeight(QFont::Black);
|
|
ranges->currentItem()->setFont(0, font);
|
|
ranges->currentItem()->setFont(1, font);
|
|
ranges->currentItem()->setFont(2, font);
|
|
|
|
// show the default button to undo
|
|
defaultButton->show();
|
|
|
|
// we manually edited so save in full
|
|
current.zonesSetFromCP = false;
|
|
|
|
// create the new zoneinfos for this range
|
|
QList<ZoneInfo> zoneinfos;
|
|
for (int i=0; i< zones->invisibleRootItem()->childCount(); i++) {
|
|
QTreeWidgetItem *item = zones->invisibleRootItem()->child(i);
|
|
zoneinfos << ZoneInfo(item->text(0),
|
|
item->text(1),
|
|
((QDoubleSpinBox*)zones->itemWidget(item, 2))->value(),
|
|
0);
|
|
}
|
|
|
|
// now sort the list
|
|
qSort(zoneinfos);
|
|
|
|
// now fill the highs
|
|
for(int i=0; i<zoneinfos.count(); i++) {
|
|
if (i+1 <zoneinfos.count())
|
|
zoneinfos[i].hi = zoneinfos[i+1].lo;
|
|
else
|
|
zoneinfos[i].hi = INT_MAX;
|
|
}
|
|
current.zones = zoneinfos;
|
|
|
|
// now replace the current range struct
|
|
zonePage->zones.setZoneRange(index, current);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Zone Config page
|
|
//
|
|
HrZonePage::HrZonePage(Context *context) : context(context)
|
|
{
|
|
QVBoxLayout *layout = new QVBoxLayout(this);
|
|
|
|
// get current config by reading it in (leave mainwindow zones alone)
|
|
QFile zonesFile(context->athlete->home->config().canonicalPath() + "/hr.zones");
|
|
if (zonesFile.exists()) {
|
|
zones.read(zonesFile);
|
|
zonesFile.close();
|
|
b4Fingerprint = zones.getFingerprint(); // remember original state
|
|
}
|
|
|
|
// setup maintenance pages using current config
|
|
schemePage = new HrSchemePage(this);
|
|
ltPage = new LTPage(this);
|
|
|
|
tabs = new QTabWidget(this);
|
|
tabs->addTab(ltPage, tr("Lactate Threshold"));
|
|
tabs->addTab(schemePage, tr("Default"));
|
|
|
|
layout->addWidget(tabs);
|
|
}
|
|
|
|
qint32
|
|
HrZonePage::saveClicked()
|
|
{
|
|
zones.setScheme(schemePage->getScheme());
|
|
zones.write(context->athlete->home->config());
|
|
|
|
// reread HR zones
|
|
QFile hrzonesFile(context->athlete->home->config().canonicalPath() + "/hr.zones");
|
|
context->athlete->hrzones_->read(hrzonesFile);
|
|
|
|
// did we change ?
|
|
if (zones.getFingerprint() != b4Fingerprint) return CONFIG_ZONES;
|
|
else return 0;
|
|
}
|
|
|
|
HrSchemePage::HrSchemePage(HrZonePage* zonePage) : zonePage(zonePage)
|
|
{
|
|
QVBoxLayout *mainLayout = new QVBoxLayout(this);
|
|
mainLayout->setSpacing(5);
|
|
|
|
addButton = new QPushButton(tr("+"));
|
|
deleteButton = new QPushButton(tr("-"));
|
|
#ifndef Q_OS_MAC
|
|
addButton->setFixedSize(20,20);
|
|
deleteButton->setFixedSize(20,20);
|
|
#else
|
|
addButton->setText(tr("Add"));
|
|
deleteButton->setText(tr("Delete"));
|
|
#endif
|
|
QHBoxLayout *actionButtons = new QHBoxLayout;
|
|
actionButtons->setSpacing(2);
|
|
actionButtons->addStretch();
|
|
actionButtons->addWidget(addButton);
|
|
actionButtons->addWidget(deleteButton);
|
|
|
|
scheme = new QTreeWidget;
|
|
scheme->headerItem()->setText(0, tr("Short"));
|
|
scheme->headerItem()->setText(1, tr("Long"));
|
|
scheme->headerItem()->setText(2, tr("Percent of LT"));
|
|
scheme->headerItem()->setText(3, tr("Trimp k"));
|
|
scheme->setColumnCount(4);
|
|
scheme->setSelectionMode(QAbstractItemView::SingleSelection);
|
|
scheme->setEditTriggers(QAbstractItemView::SelectedClicked); // allow edit
|
|
scheme->setUniformRowHeights(true);
|
|
scheme->setIndentation(0);
|
|
//scheme->header()->resizeSection(0,60);
|
|
//scheme->header()->resizeSection(1,180);
|
|
//scheme->header()->resizeSection(2,65);
|
|
//scheme->header()->resizeSection(3,65);
|
|
|
|
// setup list
|
|
for (int i=0; i< zonePage->zones.getScheme().nzones_default; i++) {
|
|
|
|
QTreeWidgetItem *add = new QTreeWidgetItem(scheme->invisibleRootItem());
|
|
add->setFlags(add->flags() | Qt::ItemIsEditable);
|
|
|
|
// tab name
|
|
add->setText(0, zonePage->zones.getScheme().zone_default_name[i]);
|
|
// field name
|
|
add->setText(1, zonePage->zones.getScheme().zone_default_desc[i]);
|
|
|
|
// low
|
|
QDoubleSpinBox *loedit = new QDoubleSpinBox(this);
|
|
loedit->setMinimum(0);
|
|
loedit->setMaximum(1000);
|
|
loedit->setSingleStep(1.0);
|
|
loedit->setDecimals(0);
|
|
loedit->setValue(zonePage->zones.getScheme().zone_default[i]);
|
|
scheme->setItemWidget(add, 2, loedit);
|
|
|
|
// trimp
|
|
QDoubleSpinBox *trimpedit = new QDoubleSpinBox(this);
|
|
trimpedit->setMinimum(0);
|
|
trimpedit->setMaximum(10);
|
|
trimpedit->setSingleStep(0.1);
|
|
trimpedit->setDecimals(2);
|
|
trimpedit->setValue(zonePage->zones.getScheme().zone_default_trimp[i]);
|
|
scheme->setItemWidget(add, 3, trimpedit);
|
|
}
|
|
|
|
mainLayout->addWidget(scheme);
|
|
mainLayout->addLayout(actionButtons);
|
|
|
|
// button connect
|
|
connect(addButton, SIGNAL(clicked()), this, SLOT(addClicked()));
|
|
connect(deleteButton, SIGNAL(clicked()), this, SLOT(deleteClicked()));
|
|
}
|
|
|
|
void
|
|
HrSchemePage::addClicked()
|
|
{
|
|
// are we at maximum already?
|
|
if (scheme->invisibleRootItem()->childCount() == 10) {
|
|
QMessageBox err;
|
|
err.setText(tr("Maximum of 10 zones reached."));
|
|
err.setIcon(QMessageBox::Warning);
|
|
err.exec();
|
|
return;
|
|
}
|
|
|
|
int index = scheme->invisibleRootItem()->childCount();
|
|
|
|
// new item
|
|
QTreeWidgetItem *add = new QTreeWidgetItem;
|
|
add->setFlags(add->flags() | Qt::ItemIsEditable);
|
|
|
|
QDoubleSpinBox *loedit = new QDoubleSpinBox(this);
|
|
loedit->setMinimum(0);
|
|
loedit->setMaximum(1000);
|
|
loedit->setSingleStep(1.0);
|
|
loedit->setDecimals(0);
|
|
loedit->setValue(100);
|
|
|
|
scheme->invisibleRootItem()->insertChild(index, add);
|
|
scheme->setItemWidget(add, 2, loedit);
|
|
|
|
//trimp
|
|
QDoubleSpinBox *trimpedit = new QDoubleSpinBox(this);
|
|
trimpedit->setMinimum(0);
|
|
trimpedit->setMaximum(10);
|
|
trimpedit->setSingleStep(0.1);
|
|
trimpedit->setDecimals(2);
|
|
trimpedit->setValue(1);
|
|
|
|
scheme->setItemWidget(add, 3, trimpedit);
|
|
|
|
// Short
|
|
QString text = tr("New");
|
|
for (int i=0; scheme->findItems(text, Qt::MatchExactly, 0).count() > 0; i++) {
|
|
text = QString(tr("New (%1)")).arg(i+1);
|
|
}
|
|
add->setText(0, text);
|
|
|
|
// long
|
|
text = tr("New");
|
|
for (int i=0; scheme->findItems(text, Qt::MatchExactly, 1).count() > 0; i++) {
|
|
text = QString(tr("New (%1)")).arg(i+1);
|
|
}
|
|
add->setText(1, text);
|
|
}
|
|
|
|
void
|
|
HrSchemePage::renameClicked()
|
|
{
|
|
// which one is selected?
|
|
if (scheme->currentItem()) scheme->editItem(scheme->currentItem(), 0);
|
|
}
|
|
|
|
void
|
|
HrSchemePage::deleteClicked()
|
|
{
|
|
if (scheme->currentItem()) {
|
|
int index = scheme->invisibleRootItem()->indexOfChild(scheme->currentItem());
|
|
delete scheme->invisibleRootItem()->takeChild(index);
|
|
}
|
|
}
|
|
|
|
HrZoneScheme
|
|
HrSchemePage::getScheme()
|
|
{
|
|
// read the scheme widget and return a scheme object
|
|
QList<schemeitem> table;
|
|
HrZoneScheme results;
|
|
|
|
// read back the details from the table
|
|
for (int i=0; i<scheme->invisibleRootItem()->childCount(); i++) {
|
|
|
|
schemeitem add;
|
|
add.name = scheme->invisibleRootItem()->child(i)->text(0);
|
|
add.desc = scheme->invisibleRootItem()->child(i)->text(1);
|
|
add.lo = ((QDoubleSpinBox *)(scheme->itemWidget(scheme->invisibleRootItem()->child(i), 2)))->value();
|
|
add.trimp = ((QDoubleSpinBox *)(scheme->itemWidget(scheme->invisibleRootItem()->child(i), 3)))->value();
|
|
table.append(add);
|
|
}
|
|
|
|
// sort the list into ascending order
|
|
qSort(table);
|
|
|
|
// now update the results
|
|
results.nzones_default = 0;
|
|
foreach(schemeitem zone, table) {
|
|
results.nzones_default++;
|
|
results.zone_default.append(zone.lo);
|
|
results.zone_default_is_pct.append(true);
|
|
results.zone_default_name.append(zone.name);
|
|
results.zone_default_desc.append(zone.desc);
|
|
results.zone_default_trimp.append(zone.trimp);
|
|
}
|
|
|
|
return results;
|
|
}
|
|
|
|
|
|
LTPage::LTPage(HrZonePage* zonePage) : zonePage(zonePage)
|
|
{
|
|
active = false;
|
|
|
|
QVBoxLayout *mainLayout = new QVBoxLayout(this);
|
|
mainLayout->setSpacing(5);
|
|
|
|
updateButton = new QPushButton(tr("Update"));
|
|
updateButton->hide();
|
|
addButton = new QPushButton(tr("+"));
|
|
deleteButton = new QPushButton(tr("-"));
|
|
#ifndef Q_OS_MAC
|
|
addButton->setFixedSize(20,20);
|
|
updateButton->setFixedSize(60,20);
|
|
deleteButton->setFixedSize(20,20);
|
|
#else
|
|
updateButton->setText(tr("Update"));
|
|
addButton->setText(tr("Add"));
|
|
deleteButton->setText(tr("Delete"));
|
|
#endif
|
|
defaultButton = new QPushButton(tr("Def"));
|
|
defaultButton->hide();
|
|
|
|
addZoneButton = new QPushButton(tr("+"));
|
|
deleteZoneButton = new QPushButton(tr("-"));
|
|
#ifndef Q_OS_MAC
|
|
addZoneButton->setFixedSize(20,20);
|
|
deleteZoneButton->setFixedSize(20,20);
|
|
#else
|
|
addZoneButton->setText(tr("Add"));
|
|
deleteZoneButton->setText(tr("Delete"));
|
|
#endif
|
|
|
|
QHBoxLayout *actionButtons = new QHBoxLayout;
|
|
actionButtons->setSpacing(2);
|
|
actionButtons->addStretch();
|
|
actionButtons->addWidget(updateButton);
|
|
actionButtons->addWidget(addButton);
|
|
actionButtons->addWidget(deleteButton);
|
|
//actionButtons->addWidget(defaultButton); moved to zoneButtons
|
|
|
|
QHBoxLayout *zoneButtons = new QHBoxLayout;
|
|
zoneButtons->addStretch();
|
|
zoneButtons->addWidget(addZoneButton);
|
|
zoneButtons->addWidget(deleteZoneButton);
|
|
zoneButtons->addWidget(defaultButton);
|
|
|
|
QHBoxLayout *addLayout = new QHBoxLayout;
|
|
QLabel *dateLabel = new QLabel(tr("From Date"));
|
|
QLabel *ltLabel = new QLabel(tr("Lactate Threshold"));
|
|
dateEdit = new QDateEdit;
|
|
dateEdit->setDate(QDate::currentDate());
|
|
|
|
ltEdit = new QDoubleSpinBox;
|
|
ltEdit->setMinimum(0);
|
|
ltEdit->setMaximum(240);
|
|
ltEdit->setSingleStep(1.0);
|
|
ltEdit->setDecimals(0);
|
|
|
|
addLayout->addWidget(dateLabel);
|
|
addLayout->addWidget(dateEdit);
|
|
addLayout->addWidget(ltLabel);
|
|
addLayout->addWidget(ltEdit);
|
|
addLayout->addStretch();
|
|
|
|
QHBoxLayout *addLayout2 = new QHBoxLayout;
|
|
QLabel *restHrLabel = new QLabel(tr("Rest HR"));
|
|
QLabel *maxHrLabel = new QLabel(tr("Max HR"));
|
|
|
|
restHrEdit = new QDoubleSpinBox;
|
|
restHrEdit->setMinimum(0);
|
|
restHrEdit->setMaximum(240);
|
|
restHrEdit->setSingleStep(1.0);
|
|
restHrEdit->setDecimals(0);
|
|
|
|
maxHrEdit = new QDoubleSpinBox;
|
|
maxHrEdit->setMinimum(0);
|
|
maxHrEdit->setMaximum(240);
|
|
maxHrEdit->setSingleStep(1.0);
|
|
maxHrEdit->setDecimals(0);
|
|
|
|
addLayout2->addWidget(restHrLabel);
|
|
addLayout2->addWidget(restHrEdit);
|
|
addLayout2->addWidget(maxHrLabel);
|
|
addLayout2->addWidget(maxHrEdit);
|
|
addLayout2->addStretch();
|
|
|
|
ranges = new QTreeWidget;
|
|
ranges->headerItem()->setText(0, tr("From Date"));
|
|
ranges->headerItem()->setText(1, tr("Lactate Threshold"));
|
|
ranges->headerItem()->setText(2, tr("Rest HR"));
|
|
ranges->headerItem()->setText(3, tr("Max HR"));
|
|
ranges->setColumnCount(4);
|
|
ranges->setSelectionMode(QAbstractItemView::SingleSelection);
|
|
//ranges->setEditTriggers(QAbstractItemView::SelectedClicked); // allow edit
|
|
ranges->setUniformRowHeights(true);
|
|
ranges->setIndentation(0);
|
|
//ranges->header()->resizeSection(0,180);
|
|
|
|
// setup list of ranges
|
|
for (int i=0; i< zonePage->zones.getRangeSize(); i++) {
|
|
|
|
QTreeWidgetItem *add = new QTreeWidgetItem(ranges->invisibleRootItem());
|
|
add->setFlags(add->flags() & ~Qt::ItemIsEditable);
|
|
|
|
// Embolden ranges with manually configured zones
|
|
QFont font;
|
|
font.setWeight(zonePage->zones.getHrZoneRange(i).hrZonesSetFromLT ?
|
|
QFont::Normal : QFont::Black);
|
|
|
|
// date
|
|
add->setText(0, zonePage->zones.getStartDate(i).toString(tr("MMM d, yyyy")));
|
|
add->setFont(0, font);
|
|
|
|
// LT
|
|
add->setText(1, QString("%1").arg(zonePage->zones.getLT(i)));
|
|
add->setFont(1, font);
|
|
|
|
// Rest HR
|
|
add->setText(2, QString("%1").arg(zonePage->zones.getRestHr(i)));
|
|
add->setFont(2, font);
|
|
|
|
// Max HR
|
|
add->setText(3, QString("%1").arg(zonePage->zones.getMaxHr(i)));
|
|
add->setFont(3, font);
|
|
}
|
|
|
|
zones = new QTreeWidget;
|
|
zones->headerItem()->setText(0, tr("Short"));
|
|
zones->headerItem()->setText(1, tr("Long"));
|
|
zones->headerItem()->setText(2, tr("From BPM"));
|
|
zones->headerItem()->setText(3, tr("Trimp k"));
|
|
zones->setColumnCount(4);
|
|
zones->setSelectionMode(QAbstractItemView::SingleSelection);
|
|
zones->setEditTriggers(QAbstractItemView::SelectedClicked); // allow edit
|
|
zones->setUniformRowHeights(true);
|
|
zones->setIndentation(0);
|
|
//zones->header()->resizeSection(0,50);
|
|
//zones->header()->resizeSection(1,150);
|
|
//zones->header()->resizeSection(2,65);
|
|
//zones->header()->resizeSection(3,65);
|
|
|
|
mainLayout->addLayout(addLayout);
|
|
mainLayout->addLayout(addLayout2);
|
|
mainLayout->addLayout(actionButtons);
|
|
mainLayout->addWidget(ranges);
|
|
mainLayout->addLayout(zoneButtons);
|
|
mainLayout->addWidget(zones);
|
|
|
|
// edit connect
|
|
connect(dateEdit, SIGNAL(dateChanged(QDate)), this, SLOT(rangeEdited()));
|
|
connect(ltEdit, SIGNAL(valueChanged(double)), this, SLOT(rangeEdited()));
|
|
connect(restHrEdit, SIGNAL(valueChanged(double)), this, SLOT(rangeEdited()));
|
|
connect(maxHrEdit, SIGNAL(valueChanged(double)), this, SLOT(rangeEdited()));
|
|
|
|
// button connect
|
|
connect(updateButton, SIGNAL(clicked()), this, SLOT(editClicked()));
|
|
connect(addButton, SIGNAL(clicked()), this, SLOT(addClicked()));
|
|
connect(deleteButton, SIGNAL(clicked()), this, SLOT(deleteClicked()));
|
|
connect(defaultButton, SIGNAL(clicked()), this, SLOT(defaultClicked()));
|
|
connect(addZoneButton, SIGNAL(clicked()), this, SLOT(addZoneClicked()));
|
|
connect(deleteZoneButton, SIGNAL(clicked()), this, SLOT(deleteZoneClicked()));
|
|
connect(ranges, SIGNAL(itemSelectionChanged()), this, SLOT(rangeSelectionChanged()));
|
|
connect(zones, SIGNAL(itemChanged(QTreeWidgetItem*, int)), this, SLOT(zonesChanged()));
|
|
}
|
|
|
|
void
|
|
LTPage::addClicked()
|
|
{
|
|
// get current scheme
|
|
zonePage->zones.setScheme(zonePage->schemePage->getScheme());
|
|
|
|
//int index = ranges->invisibleRootItem()->childCount();
|
|
int index = zonePage->zones.addHrZoneRange(dateEdit->date(), ltEdit->value(), restHrEdit->value(), maxHrEdit->value());
|
|
|
|
// new item
|
|
QTreeWidgetItem *add = new QTreeWidgetItem;
|
|
add->setFlags(add->flags() & ~Qt::ItemIsEditable);
|
|
ranges->invisibleRootItem()->insertChild(index, add);
|
|
|
|
// date
|
|
add->setText(0, dateEdit->date().toString(tr("MMM d, yyyy")));
|
|
|
|
// LT
|
|
add->setText(1, QString("%1").arg(ltEdit->value()));
|
|
// Rest HR
|
|
add->setText(2, QString("%1").arg(restHrEdit->value()));
|
|
// Max HR
|
|
add->setText(3, QString("%1").arg(maxHrEdit->value()));
|
|
}
|
|
|
|
void
|
|
LTPage::editClicked()
|
|
{
|
|
// get current scheme
|
|
zonePage->zones.setScheme(zonePage->schemePage->getScheme());
|
|
|
|
QTreeWidgetItem *edit = ranges->selectedItems().at(0);
|
|
int index = ranges->indexOfTopLevelItem(edit);
|
|
|
|
// date
|
|
edit->setText(0, dateEdit->date().toString(tr("MMM d, yyyy")));
|
|
|
|
// LT
|
|
zonePage->zones.setLT(index, ltEdit->value());
|
|
edit->setText(1, QString("%1").arg(ltEdit->value()));
|
|
// Rest HR
|
|
zonePage->zones.setRestHr(index, restHrEdit->value());
|
|
edit->setText(2, QString("%1").arg(restHrEdit->value()));
|
|
// Max HR
|
|
zonePage->zones.setMaxHr(index, maxHrEdit->value());
|
|
edit->setText(3, QString("%1").arg(maxHrEdit->value()));
|
|
}
|
|
|
|
void
|
|
LTPage::deleteClicked()
|
|
{
|
|
if (ranges->currentItem()) {
|
|
int index = ranges->invisibleRootItem()->indexOfChild(ranges->currentItem());
|
|
delete ranges->invisibleRootItem()->takeChild(index);
|
|
zonePage->zones.deleteRange(index);
|
|
}
|
|
}
|
|
|
|
void
|
|
LTPage::defaultClicked()
|
|
{
|
|
if (ranges->currentItem()) {
|
|
|
|
int index = ranges->invisibleRootItem()->indexOfChild(ranges->currentItem());
|
|
HrZoneRange current = zonePage->zones.getHrZoneRange(index);
|
|
|
|
// unbold
|
|
QFont font;
|
|
font.setWeight(QFont::Normal);
|
|
ranges->currentItem()->setFont(0, font);
|
|
ranges->currentItem()->setFont(1, font);
|
|
ranges->currentItem()->setFont(2, font);
|
|
ranges->currentItem()->setFont(3, font);
|
|
|
|
|
|
// set the range to use defaults on the scheme page
|
|
zonePage->zones.setScheme(zonePage->schemePage->getScheme());
|
|
zonePage->zones.setHrZonesFromLT(index);
|
|
|
|
// hide the default button since we are now using defaults
|
|
defaultButton->hide();
|
|
|
|
// update the zones display
|
|
rangeSelectionChanged();
|
|
}
|
|
}
|
|
|
|
void
|
|
LTPage::rangeEdited()
|
|
{
|
|
if (ranges->currentItem()) {
|
|
int index = ranges->invisibleRootItem()->indexOfChild(ranges->currentItem());
|
|
|
|
QDate date = dateEdit->date();
|
|
QDate odate = zonePage->zones.getStartDate(index);
|
|
|
|
int lt = ltEdit->value();
|
|
int olt = zonePage->zones.getLT(index);
|
|
|
|
int maxhr = maxHrEdit->value();
|
|
int omaxhr = zonePage->zones.getMaxHr(index);
|
|
|
|
int resthr = restHrEdit->value();
|
|
int oresthr = zonePage->zones.getRestHr(index);
|
|
|
|
if (date != odate || lt != olt || maxhr != omaxhr || resthr != oresthr)
|
|
updateButton->show();
|
|
else
|
|
updateButton->hide();
|
|
}
|
|
}
|
|
|
|
void
|
|
LTPage::rangeSelectionChanged()
|
|
{
|
|
active = true;
|
|
|
|
// wipe away current contents of zones
|
|
foreach (QTreeWidgetItem *item, zones->invisibleRootItem()->takeChildren()) {
|
|
delete zones->itemWidget(item, 2);
|
|
delete item;
|
|
}
|
|
|
|
// fill with current details
|
|
if (ranges->currentItem()) {
|
|
|
|
int index = ranges->invisibleRootItem()->indexOfChild(ranges->currentItem());
|
|
HrZoneRange current = zonePage->zones.getHrZoneRange(index);
|
|
|
|
dateEdit->setDate(zonePage->zones.getStartDate(index));
|
|
ltEdit->setValue(zonePage->zones.getLT(index));
|
|
maxHrEdit->setValue(zonePage->zones.getMaxHr(index));
|
|
restHrEdit->setValue(zonePage->zones.getRestHr(index));
|
|
|
|
if (current.hrZonesSetFromLT) {
|
|
|
|
// reapply the scheme in case it has been changed
|
|
zonePage->zones.setScheme(zonePage->schemePage->getScheme());
|
|
zonePage->zones.setHrZonesFromLT(index);
|
|
current = zonePage->zones.getHrZoneRange(index);
|
|
|
|
defaultButton->hide();
|
|
|
|
} else defaultButton->show();
|
|
|
|
for (int i=0; i< current.zones.count(); i++) {
|
|
|
|
QTreeWidgetItem *add = new QTreeWidgetItem(zones->invisibleRootItem());
|
|
add->setFlags(add->flags() | Qt::ItemIsEditable);
|
|
|
|
// tab name
|
|
add->setText(0, current.zones[i].name);
|
|
// field name
|
|
add->setText(1, current.zones[i].desc);
|
|
|
|
// low
|
|
QDoubleSpinBox *loedit = new QDoubleSpinBox(this);
|
|
loedit->setMinimum(0);
|
|
loedit->setMaximum(1000);
|
|
loedit->setSingleStep(1.0);
|
|
loedit->setDecimals(0);
|
|
loedit->setValue(current.zones[i].lo);
|
|
zones->setItemWidget(add, 2, loedit);
|
|
connect(loedit, SIGNAL(editingFinished()), this, SLOT(zonesChanged()));
|
|
|
|
//trimp
|
|
QDoubleSpinBox *trimpedit = new QDoubleSpinBox(this);
|
|
trimpedit->setMinimum(0);
|
|
trimpedit->setMaximum(10);
|
|
trimpedit->setSingleStep(0.1);
|
|
trimpedit->setDecimals(2);
|
|
trimpedit->setValue(current.zones[i].trimp);
|
|
zones->setItemWidget(add, 3, trimpedit);
|
|
connect(trimpedit, SIGNAL(editingFinished()), this, SLOT(zonesChanged()));
|
|
|
|
}
|
|
}
|
|
|
|
active = false;
|
|
}
|
|
|
|
void
|
|
LTPage::addZoneClicked()
|
|
{
|
|
// no range selected
|
|
if (!ranges->currentItem()) return;
|
|
|
|
// are we at maximum already?
|
|
if (zones->invisibleRootItem()->childCount() == 10) {
|
|
QMessageBox err;
|
|
err.setText(tr("Maximum of 10 zones reached."));
|
|
err.setIcon(QMessageBox::Warning);
|
|
err.exec();
|
|
return;
|
|
}
|
|
|
|
active = true;
|
|
int index = zones->invisibleRootItem()->childCount();
|
|
|
|
// new item
|
|
QTreeWidgetItem *add = new QTreeWidgetItem;
|
|
add->setFlags(add->flags() | Qt::ItemIsEditable);
|
|
|
|
QDoubleSpinBox *loedit = new QDoubleSpinBox(this);
|
|
loedit->setMinimum(0);
|
|
loedit->setMaximum(1000);
|
|
loedit->setSingleStep(1.0);
|
|
loedit->setDecimals(0);
|
|
loedit->setValue(100);
|
|
|
|
zones->invisibleRootItem()->insertChild(index, add);
|
|
zones->setItemWidget(add, 2, loedit);
|
|
connect(loedit, SIGNAL(editingFinished()), this, SLOT(zonesChanged()));
|
|
|
|
//trimp
|
|
QDoubleSpinBox *trimpedit = new QDoubleSpinBox(this);
|
|
trimpedit->setMinimum(0);
|
|
trimpedit->setMaximum(10);
|
|
trimpedit->setSingleStep(0.1);
|
|
trimpedit->setDecimals(2);
|
|
trimpedit->setValue(1);
|
|
zones->setItemWidget(add, 3, trimpedit);
|
|
connect(trimpedit, SIGNAL(editingFinished()), this, SLOT(zonesChanged()));
|
|
|
|
// Short
|
|
QString text = tr("New");
|
|
for (int i=0; zones->findItems(text, Qt::MatchExactly, 0).count() > 0; i++) {
|
|
text = QString(tr("New (%1)")).arg(i+1);
|
|
}
|
|
add->setText(0, text);
|
|
|
|
// long
|
|
text = tr("New");
|
|
for (int i=0; zones->findItems(text, Qt::MatchExactly, 1).count() > 0; i++) {
|
|
text = QString(tr("New (%1)")).arg(i+1);
|
|
}
|
|
add->setText(1, text);
|
|
active = false;
|
|
|
|
zonesChanged();
|
|
}
|
|
|
|
void
|
|
LTPage::deleteZoneClicked()
|
|
{
|
|
// no range selected
|
|
if (ranges->invisibleRootItem()->indexOfChild(ranges->currentItem()) == -1)
|
|
return;
|
|
|
|
active = true;
|
|
if (zones->currentItem()) {
|
|
int index = zones->invisibleRootItem()->indexOfChild(zones->currentItem());
|
|
delete zones->invisibleRootItem()->takeChild(index);
|
|
}
|
|
active = false;
|
|
|
|
zonesChanged();
|
|
}
|
|
|
|
void
|
|
LTPage::zonesChanged()
|
|
{
|
|
// only take changes when they are not done programmatically
|
|
// the active flag is set when the tree is being modified
|
|
// programmatically, but not when users interact with the widgets
|
|
if (active == false) {
|
|
// get the current zone range
|
|
if (ranges->currentItem()) {
|
|
|
|
int index = ranges->invisibleRootItem()->indexOfChild(ranges->currentItem());
|
|
HrZoneRange current = zonePage->zones.getHrZoneRange(index);
|
|
|
|
// embolden that range on the list to show it has been edited
|
|
QFont font;
|
|
font.setWeight(QFont::Black);
|
|
ranges->currentItem()->setFont(0, font);
|
|
ranges->currentItem()->setFont(1, font);
|
|
ranges->currentItem()->setFont(2, font);
|
|
ranges->currentItem()->setFont(3, font);
|
|
|
|
// show the default button to undo
|
|
defaultButton->show();
|
|
|
|
// we manually edited so save in full
|
|
current.hrZonesSetFromLT = false;
|
|
|
|
// create the new zoneinfos for this range
|
|
QList<HrZoneInfo> zoneinfos;
|
|
for (int i=0; i< zones->invisibleRootItem()->childCount(); i++) {
|
|
QTreeWidgetItem *item = zones->invisibleRootItem()->child(i);
|
|
zoneinfos << HrZoneInfo(item->text(0),
|
|
item->text(1),
|
|
((QDoubleSpinBox*)zones->itemWidget(item, 2))->value(),
|
|
0, ((QDoubleSpinBox*)zones->itemWidget(item, 3))->value());
|
|
}
|
|
|
|
// now sort the list
|
|
qSort(zoneinfos);
|
|
|
|
// now fill the highs
|
|
for(int i=0; i<zoneinfos.count(); i++) {
|
|
if (i+1 <zoneinfos.count())
|
|
zoneinfos[i].hi = zoneinfos[i+1].lo;
|
|
else
|
|
zoneinfos[i].hi = INT_MAX;
|
|
}
|
|
current.zones = zoneinfos;
|
|
|
|
// now replace the current range struct
|
|
zonePage->zones.setHrZoneRange(index, current);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Pace Zone Config page
|
|
//
|
|
PaceZonePage::PaceZonePage(Context *context) : context(context)
|
|
{
|
|
QVBoxLayout *layout = new QVBoxLayout(this);
|
|
QHBoxLayout *hlayout = new QHBoxLayout;
|
|
|
|
sportLabel = new QLabel(tr("Sport"));
|
|
sportCombo = new QComboBox();
|
|
sportCombo->addItem(tr("Run"));
|
|
sportCombo->addItem(tr("Swim"));
|
|
sportCombo->setCurrentIndex(0);
|
|
hlayout->addStretch();
|
|
hlayout->addWidget(sportLabel);
|
|
hlayout->addWidget(sportCombo);
|
|
hlayout->addStretch();
|
|
layout->addLayout(hlayout);
|
|
connect(sportCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(changeSport(int)));
|
|
tabs = new QTabWidget(this);
|
|
layout->addWidget(tabs);
|
|
|
|
for (int i=0; i < nSports; i++) {
|
|
paceZones[i] = new PaceZones(i > 0);
|
|
|
|
// get current config by reading it in (leave mainwindow zones alone)
|
|
QFile zonesFile(context->athlete->home->config().canonicalPath() + "/" + paceZones[i]->fileName());
|
|
if (zonesFile.exists()) {
|
|
paceZones[i]->read(zonesFile);
|
|
zonesFile.close();
|
|
b4Fingerprint[i] = paceZones[i]->getFingerprint(); // remember original state
|
|
}
|
|
|
|
// setup maintenance pages using current config
|
|
schemePages[i] = new PaceSchemePage(paceZones[i]);
|
|
cvPages[i] = new CVPage(paceZones[i], schemePages[i]);
|
|
}
|
|
|
|
// finish setup for the default sport
|
|
changeSport(sportCombo->currentIndex());
|
|
}
|
|
|
|
PaceZonePage::~PaceZonePage()
|
|
{
|
|
for (int i=0; i<nSports; i++) delete paceZones[i];
|
|
}
|
|
|
|
void
|
|
PaceZonePage::changeSport(int i)
|
|
{
|
|
// change tabs according to the selected sport
|
|
tabs->clear();
|
|
tabs->addTab(cvPages[i], tr("Critical Velocity"));
|
|
tabs->addTab(schemePages[i], tr("Default"));
|
|
}
|
|
|
|
qint32
|
|
PaceZonePage::saveClicked()
|
|
{
|
|
qint32 changed = 0;
|
|
// write it
|
|
for (int i=0; i < nSports; i++) {
|
|
appsettings->setValue(paceZones[i]->paceSetting(), cvPages[i]->metric->isChecked());
|
|
paceZones[i]->setScheme(schemePages[i]->getScheme());
|
|
paceZones[i]->write(context->athlete->home->config());
|
|
|
|
// reread Pace zones
|
|
QFile pacezonesFile(context->athlete->home->config().canonicalPath() + "/" + context->athlete->pacezones_[i]->fileName());
|
|
context->athlete->pacezones_[i]->read(pacezonesFile);
|
|
|
|
// did we change ?
|
|
if (paceZones[i]->getFingerprint() != b4Fingerprint[i])
|
|
changed = CONFIG_ZONES;
|
|
}
|
|
return changed;
|
|
}
|
|
|
|
PaceSchemePage::PaceSchemePage(PaceZones* paceZones) : paceZones(paceZones)
|
|
{
|
|
QVBoxLayout *mainLayout = new QVBoxLayout(this);
|
|
|
|
addButton = new QPushButton(tr("+"));
|
|
deleteButton = new QPushButton(tr("-"));
|
|
#ifndef Q_OS_MAC
|
|
addButton->setFixedSize(20,20);
|
|
deleteButton->setFixedSize(20,20);
|
|
#else
|
|
addButton->setText(tr("Add"));
|
|
deleteButton->setText(tr("Delete"));
|
|
#endif
|
|
QHBoxLayout *actionButtons = new QHBoxLayout;
|
|
actionButtons->setSpacing(2);
|
|
actionButtons->addStretch();
|
|
actionButtons->addWidget(addButton);
|
|
actionButtons->addWidget(deleteButton);
|
|
|
|
scheme = new QTreeWidget;
|
|
scheme->headerItem()->setText(0, tr("Short"));
|
|
scheme->headerItem()->setText(1, tr("Long"));
|
|
scheme->headerItem()->setText(2, tr("Percent of CV"));
|
|
scheme->setColumnCount(3);
|
|
scheme->setSelectionMode(QAbstractItemView::SingleSelection);
|
|
scheme->setEditTriggers(QAbstractItemView::SelectedClicked); // allow edit
|
|
scheme->setUniformRowHeights(true);
|
|
scheme->setIndentation(0);
|
|
//scheme->header()->resizeSection(0,90);
|
|
//scheme->header()->resizeSection(1,200);
|
|
//scheme->header()->resizeSection(2,80);
|
|
|
|
// setup list
|
|
for (int i=0; i< paceZones->getScheme().nzones_default; i++) {
|
|
|
|
QTreeWidgetItem *add = new QTreeWidgetItem(scheme->invisibleRootItem());
|
|
add->setFlags(add->flags() | Qt::ItemIsEditable);
|
|
|
|
// tab name
|
|
add->setText(0, paceZones->getScheme().zone_default_name[i]);
|
|
// field name
|
|
add->setText(1, paceZones->getScheme().zone_default_desc[i]);
|
|
|
|
// low
|
|
QDoubleSpinBox *loedit = new QDoubleSpinBox(this);
|
|
loedit->setMinimum(0);
|
|
loedit->setMaximum(1000);
|
|
loedit->setSingleStep(1.0);
|
|
loedit->setDecimals(0);
|
|
loedit->setValue(paceZones->getScheme().zone_default[i]);
|
|
scheme->setItemWidget(add, 2, loedit);
|
|
}
|
|
|
|
mainLayout->addWidget(scheme);
|
|
mainLayout->addLayout(actionButtons);
|
|
|
|
// button connect
|
|
connect(addButton, SIGNAL(clicked()), this, SLOT(addClicked()));
|
|
connect(deleteButton, SIGNAL(clicked()), this, SLOT(deleteClicked()));
|
|
}
|
|
|
|
void
|
|
PaceSchemePage::addClicked()
|
|
{
|
|
// are we at maximum already?
|
|
if (scheme->invisibleRootItem()->childCount() == 10) {
|
|
QMessageBox err;
|
|
err.setText(tr("Maximum of 10 zones reached."));
|
|
err.setIcon(QMessageBox::Warning);
|
|
err.exec();
|
|
return;
|
|
}
|
|
|
|
int index = scheme->invisibleRootItem()->childCount();
|
|
|
|
// new item
|
|
QTreeWidgetItem *add = new QTreeWidgetItem;
|
|
add->setFlags(add->flags() | Qt::ItemIsEditable);
|
|
|
|
QDoubleSpinBox *loedit = new QDoubleSpinBox(this);
|
|
loedit->setMinimum(0);
|
|
loedit->setMaximum(1000);
|
|
loedit->setSingleStep(1.0);
|
|
loedit->setDecimals(0);
|
|
loedit->setValue(100);
|
|
|
|
scheme->invisibleRootItem()->insertChild(index, add);
|
|
scheme->setItemWidget(add, 2, loedit);
|
|
|
|
// Short
|
|
QString text = tr("New");
|
|
for (int i=0; scheme->findItems(text, Qt::MatchExactly, 0).count() > 0; i++) {
|
|
text = QString(tr("New (%1)")).arg(i+1);
|
|
}
|
|
add->setText(0, text);
|
|
|
|
// long
|
|
text = tr("New");
|
|
for (int i=0; scheme->findItems(text, Qt::MatchExactly, 1).count() > 0; i++) {
|
|
text = QString(tr("New (%1)")).arg(i+1);
|
|
}
|
|
add->setText(1, text);
|
|
}
|
|
|
|
void
|
|
PaceSchemePage::renameClicked()
|
|
{
|
|
// which one is selected?
|
|
if (scheme->currentItem()) scheme->editItem(scheme->currentItem(), 0);
|
|
}
|
|
|
|
void
|
|
PaceSchemePage::deleteClicked()
|
|
{
|
|
if (scheme->currentItem()) {
|
|
int index = scheme->invisibleRootItem()->indexOfChild(scheme->currentItem());
|
|
delete scheme->invisibleRootItem()->takeChild(index);
|
|
}
|
|
}
|
|
|
|
// just for qSorting
|
|
struct paceschemeitem {
|
|
QString name, desc;
|
|
int lo;
|
|
double trimp;
|
|
bool operator<(paceschemeitem right) const { return lo < right.lo; }
|
|
};
|
|
|
|
PaceZoneScheme
|
|
PaceSchemePage::getScheme()
|
|
{
|
|
// read the scheme widget and return a scheme object
|
|
QList<paceschemeitem> table;
|
|
PaceZoneScheme results;
|
|
|
|
// read back the details from the table
|
|
for (int i=0; i<scheme->invisibleRootItem()->childCount(); i++) {
|
|
|
|
paceschemeitem add;
|
|
add.name = scheme->invisibleRootItem()->child(i)->text(0);
|
|
add.desc = scheme->invisibleRootItem()->child(i)->text(1);
|
|
add.lo = ((QDoubleSpinBox *)(scheme->itemWidget(scheme->invisibleRootItem()->child(i), 2)))->value();
|
|
table.append(add);
|
|
}
|
|
|
|
// sort the list into ascending order
|
|
qSort(table);
|
|
|
|
// now update the results
|
|
results.nzones_default = 0;
|
|
foreach(paceschemeitem zone, table) {
|
|
results.nzones_default++;
|
|
results.zone_default.append(zone.lo);
|
|
results.zone_default_is_pct.append(true);
|
|
results.zone_default_name.append(zone.name);
|
|
results.zone_default_desc.append(zone.desc);
|
|
}
|
|
|
|
return results;
|
|
}
|
|
|
|
CVPage::CVPage(PaceZones* paceZones, PaceSchemePage *schemePage) :
|
|
paceZones(paceZones), schemePage(schemePage)
|
|
{
|
|
active = false;
|
|
|
|
QGridLayout *mainLayout = new QGridLayout(this);
|
|
mainLayout->setSpacing(10);
|
|
|
|
addButton = new QPushButton(tr("+"));
|
|
deleteButton = new QPushButton(tr("-"));
|
|
#ifndef Q_OS_MAC
|
|
addButton->setFixedSize(20,20);
|
|
deleteButton->setFixedSize(20,20);
|
|
#else
|
|
addButton->setText(tr("Add"));
|
|
deleteButton->setText(tr("Delete"));
|
|
#endif
|
|
defaultButton = new QPushButton(tr("Def"));
|
|
defaultButton->hide();
|
|
|
|
addZoneButton = new QPushButton(tr("+"));
|
|
deleteZoneButton = new QPushButton(tr("-"));
|
|
#ifndef Q_OS_MAC
|
|
addZoneButton->setFixedSize(20,20);
|
|
deleteZoneButton->setFixedSize(20,20);
|
|
#else
|
|
addZoneButton->setText(tr("Add"));
|
|
deleteZoneButton->setText(tr("Delete"));
|
|
#endif
|
|
|
|
QHBoxLayout *zoneButtons = new QHBoxLayout;
|
|
zoneButtons->addStretch();
|
|
zoneButtons->setSpacing(0);
|
|
zoneButtons->addWidget(addZoneButton);
|
|
zoneButtons->addWidget(deleteZoneButton);
|
|
|
|
QHBoxLayout *addLayout = new QHBoxLayout;
|
|
QLabel *dateLabel = new QLabel(tr("From Date"));
|
|
QLabel *cpLabel = new QLabel(tr("Critical Velocity"));
|
|
dateEdit = new QDateEdit;
|
|
dateEdit->setDate(QDate::currentDate());
|
|
|
|
// CV default is 4min/km for Running a round number inline with
|
|
// CP default and 1:36min/100 for swimming (4:1 relation)
|
|
cvEdit = new QTimeEdit(QTime::fromString(paceZones->isSwim() ? "01:36" : "04:00", "mm:ss"));
|
|
cvEdit->setMinimumTime(QTime::fromString("01:00", "mm:ss"));
|
|
cvEdit->setMaximumTime(QTime::fromString("20:00", "mm:ss"));
|
|
cvEdit->setDisplayFormat("mm:ss");
|
|
|
|
per = new QLabel(this);
|
|
metric = new QCheckBox(tr("Metric Pace"));
|
|
metric->setChecked(appsettings->value(this, paceZones->paceSetting(), true).toBool());
|
|
per->setText(paceZones->paceUnits(metric->isChecked()));
|
|
if (!metric->isChecked()) metricChanged(); // default is metric
|
|
|
|
QHBoxLayout *actionButtons = new QHBoxLayout;
|
|
actionButtons->setSpacing(2);
|
|
actionButtons->addWidget(cpLabel);
|
|
actionButtons->addWidget(cvEdit);
|
|
actionButtons->addWidget(per);
|
|
actionButtons->addStretch();
|
|
actionButtons->addWidget(metric);
|
|
actionButtons->addStretch();
|
|
actionButtons->addWidget(addButton);
|
|
actionButtons->addWidget(deleteButton);
|
|
actionButtons->addWidget(defaultButton);
|
|
|
|
addLayout->addWidget(dateLabel);
|
|
addLayout->addWidget(dateEdit);
|
|
addLayout->addStretch();
|
|
|
|
ranges = new QTreeWidget;
|
|
ranges->headerItem()->setText(0, tr("From Date"));
|
|
ranges->headerItem()->setText(1, tr("Critical Velocity"));
|
|
ranges->setColumnCount(2);
|
|
ranges->setSelectionMode(QAbstractItemView::SingleSelection);
|
|
//ranges->setEditTriggers(QAbstractItemView::SelectedClicked); // allow edit
|
|
ranges->setUniformRowHeights(true);
|
|
ranges->setIndentation(0);
|
|
//ranges->header()->resizeSection(0,180);
|
|
|
|
// setup list of ranges
|
|
for (int i=0; i< paceZones->getRangeSize(); i++) {
|
|
|
|
QTreeWidgetItem *add = new QTreeWidgetItem(ranges->invisibleRootItem());
|
|
add->setFlags(add->flags() & ~Qt::ItemIsEditable);
|
|
|
|
// Embolden ranges with manually configured zones
|
|
QFont font;
|
|
font.setWeight(paceZones->getZoneRange(i).zonesSetFromCV ?
|
|
QFont::Normal : QFont::Black);
|
|
|
|
// date
|
|
add->setText(0, paceZones->getStartDate(i).toString(tr("MMM d, yyyy")));
|
|
add->setFont(0, font);
|
|
|
|
// CV
|
|
double kph = paceZones->getCV(i);
|
|
|
|
add->setText(1, QString("%1 %2 %3 %4")
|
|
.arg(paceZones->kphToPaceString(kph, true))
|
|
.arg(paceZones->paceUnits(true))
|
|
.arg(paceZones->kphToPaceString(kph, false))
|
|
.arg(paceZones->paceUnits(false)));
|
|
add->setFont(1, font);
|
|
|
|
}
|
|
|
|
zones = new QTreeWidget;
|
|
zones->headerItem()->setText(0, tr("Short"));
|
|
zones->headerItem()->setText(1, tr("Long"));
|
|
zones->headerItem()->setText(2, tr("From"));
|
|
zones->setColumnCount(3);
|
|
zones->setSelectionMode(QAbstractItemView::SingleSelection);
|
|
zones->setEditTriggers(QAbstractItemView::SelectedClicked); // allow edit
|
|
zones->setUniformRowHeights(true);
|
|
zones->setIndentation(0);
|
|
//zones->header()->resizeSection(0,80);
|
|
//zones->header()->resizeSection(1,150);
|
|
|
|
mainLayout->addLayout(addLayout, 0,0);
|
|
mainLayout->addLayout(actionButtons, 1,0);
|
|
mainLayout->addWidget(ranges, 2,0);
|
|
mainLayout->addLayout(zoneButtons, 3,0);
|
|
mainLayout->addWidget(zones, 4,0);
|
|
|
|
// button connect
|
|
connect(addButton, SIGNAL(clicked()), this, SLOT(addClicked()));
|
|
connect(deleteButton, SIGNAL(clicked()), this, SLOT(deleteClicked()));
|
|
connect(defaultButton, SIGNAL(clicked()), this, SLOT(defaultClicked()));
|
|
connect(addZoneButton, SIGNAL(clicked()), this, SLOT(addZoneClicked()));
|
|
connect(deleteZoneButton, SIGNAL(clicked()), this, SLOT(deleteZoneClicked()));
|
|
connect(ranges, SIGNAL(itemSelectionChanged()), this, SLOT(rangeSelectionChanged()));
|
|
connect(zones, SIGNAL(itemChanged(QTreeWidgetItem*, int)), this, SLOT(zonesChanged()));
|
|
connect(metric, SIGNAL(stateChanged(int)), this, SLOT(metricChanged()));
|
|
}
|
|
|
|
void
|
|
CVPage::metricChanged()
|
|
{
|
|
// Switch between metric and imperial!
|
|
per->setText(paceZones->paceUnits(metric->isChecked()));
|
|
double kphCV = paceZones->kphFromTime(cvEdit, !metric->isChecked());
|
|
cvEdit->setTime(QTime::fromString(paceZones->kphToPaceString(kphCV, metric->isChecked()), "mm:ss"));
|
|
}
|
|
|
|
void
|
|
CVPage::addClicked()
|
|
{
|
|
// get current scheme
|
|
paceZones->setScheme(schemePage->getScheme());
|
|
|
|
int cp = paceZones->kphFromTime(cvEdit, metric->isChecked());
|
|
if( cp <= 0 ){
|
|
QMessageBox err;
|
|
err.setText(tr("CV must be > 0"));
|
|
err.setIcon(QMessageBox::Warning);
|
|
err.exec();
|
|
return;
|
|
}
|
|
|
|
int index = paceZones->addZoneRange(dateEdit->date(), paceZones->kphFromTime(cvEdit, metric->isChecked()));
|
|
|
|
// new item
|
|
QTreeWidgetItem *add = new QTreeWidgetItem;
|
|
add->setFlags(add->flags() & ~Qt::ItemIsEditable);
|
|
ranges->invisibleRootItem()->insertChild(index, add);
|
|
|
|
// date
|
|
add->setText(0, dateEdit->date().toString(tr("MMM d, yyyy")));
|
|
|
|
// CV
|
|
double kph = paceZones->kphFromTime(cvEdit, metric->isChecked());
|
|
|
|
add->setText(1, QString("%1 %2 %3 %4")
|
|
.arg(paceZones->kphToPaceString(kph, true))
|
|
.arg(paceZones->paceUnits(true))
|
|
.arg(paceZones->kphToPaceString(kph, false))
|
|
.arg(paceZones->paceUnits(false)));
|
|
|
|
}
|
|
|
|
void
|
|
CVPage::deleteClicked()
|
|
{
|
|
if (ranges->currentItem()) {
|
|
int index = ranges->invisibleRootItem()->indexOfChild(ranges->currentItem());
|
|
delete ranges->invisibleRootItem()->takeChild(index);
|
|
paceZones->deleteRange(index);
|
|
}
|
|
}
|
|
|
|
void
|
|
CVPage::defaultClicked()
|
|
{
|
|
if (ranges->currentItem()) {
|
|
|
|
int index = ranges->invisibleRootItem()->indexOfChild(ranges->currentItem());
|
|
PaceZoneRange current = paceZones->getZoneRange(index);
|
|
|
|
// unbold
|
|
QFont font;
|
|
font.setWeight(QFont::Normal);
|
|
ranges->currentItem()->setFont(0, font);
|
|
ranges->currentItem()->setFont(1, font);
|
|
ranges->currentItem()->setFont(2, font);
|
|
|
|
|
|
// set the range to use defaults on the scheme page
|
|
paceZones->setScheme(schemePage->getScheme());
|
|
paceZones->setZonesFromCV(index);
|
|
|
|
// hide the default button since we are now using defaults
|
|
defaultButton->hide();
|
|
|
|
// update the zones display
|
|
rangeSelectionChanged();
|
|
}
|
|
}
|
|
|
|
void
|
|
CVPage::rangeSelectionChanged()
|
|
{
|
|
active = true;
|
|
|
|
// wipe away current contents of zones
|
|
foreach (QTreeWidgetItem *item, zones->invisibleRootItem()->takeChildren()) {
|
|
delete zones->itemWidget(item, 2);
|
|
delete item;
|
|
}
|
|
|
|
// fill with current details
|
|
if (ranges->currentItem()) {
|
|
|
|
int index = ranges->invisibleRootItem()->indexOfChild(ranges->currentItem());
|
|
PaceZoneRange current = paceZones->getZoneRange(index);
|
|
|
|
if (current.zonesSetFromCV) {
|
|
|
|
// reapply the scheme in case it has been changed
|
|
paceZones->setScheme(schemePage->getScheme());
|
|
paceZones->setZonesFromCV(index);
|
|
current = paceZones->getZoneRange(index);
|
|
|
|
defaultButton->hide();
|
|
|
|
} else defaultButton->show();
|
|
|
|
for (int i=0; i< current.zones.count(); i++) {
|
|
|
|
QTreeWidgetItem *add = new QTreeWidgetItem(zones->invisibleRootItem());
|
|
add->setFlags(add->flags() | Qt::ItemIsEditable);
|
|
|
|
// tab name
|
|
add->setText(0, current.zones[i].name);
|
|
// field name
|
|
add->setText(1, current.zones[i].desc);
|
|
|
|
// low
|
|
QTimeEdit *loedit = new QTimeEdit(QTime::fromString(paceZones->kphToPaceString(current.zones[i].lo, metric->isChecked()), "mm:ss"), this);
|
|
loedit->setMinimumTime(QTime::fromString("00:00", "mm:ss"));
|
|
loedit->setMaximumTime(QTime::fromString("20:00", "mm:ss"));
|
|
loedit->setDisplayFormat("mm:ss");
|
|
zones->setItemWidget(add, 2, loedit);
|
|
connect(loedit, SIGNAL(editingFinished()), this, SLOT(zonesChanged()));
|
|
}
|
|
}
|
|
|
|
active = false;
|
|
}
|
|
|
|
void
|
|
CVPage::addZoneClicked()
|
|
{
|
|
// no range selected
|
|
if (!ranges->currentItem()) return;
|
|
|
|
// are we at maximum already?
|
|
if (zones->invisibleRootItem()->childCount() == 10) {
|
|
QMessageBox err;
|
|
err.setText(tr("Maximum of 10 zones reached."));
|
|
err.setIcon(QMessageBox::Warning);
|
|
err.exec();
|
|
return;
|
|
}
|
|
|
|
active = true;
|
|
int index = zones->invisibleRootItem()->childCount();
|
|
|
|
// new item
|
|
QTreeWidgetItem *add = new QTreeWidgetItem;
|
|
add->setFlags(add->flags() | Qt::ItemIsEditable);
|
|
|
|
QTimeEdit *loedit = new QTimeEdit(QTime::fromString("00:00", "mm:ss"), this);
|
|
loedit->setMinimumTime(QTime::fromString("00:00", "mm:ss"));
|
|
loedit->setMaximumTime(QTime::fromString("20:00", "mm:ss"));
|
|
loedit->setDisplayFormat("mm:ss");
|
|
|
|
zones->invisibleRootItem()->insertChild(index, add);
|
|
zones->setItemWidget(add, 2, loedit);
|
|
connect(loedit, SIGNAL(editingFinished()), this, SLOT(zonesChanged()));
|
|
|
|
// Short
|
|
QString text = tr("New");
|
|
for (int i=0; zones->findItems(text, Qt::MatchExactly, 0).count() > 0; i++) {
|
|
text = QString(tr("New (%1)")).arg(i+1);
|
|
}
|
|
add->setText(0, text);
|
|
|
|
// long
|
|
text = tr("New");
|
|
for (int i=0; zones->findItems(text, Qt::MatchExactly, 1).count() > 0; i++) {
|
|
text = QString(tr("New (%1)")).arg(i+1);
|
|
}
|
|
add->setText(1, text);
|
|
active = false;
|
|
|
|
zonesChanged();
|
|
}
|
|
|
|
void
|
|
CVPage::deleteZoneClicked()
|
|
{
|
|
// no range selected
|
|
if (ranges->invisibleRootItem()->indexOfChild(ranges->currentItem()) == -1)
|
|
return;
|
|
|
|
active = true;
|
|
if (zones->currentItem()) {
|
|
int index = zones->invisibleRootItem()->indexOfChild(zones->currentItem());
|
|
delete zones->invisibleRootItem()->takeChild(index);
|
|
}
|
|
active = false;
|
|
|
|
zonesChanged();
|
|
}
|
|
|
|
void
|
|
CVPage::zonesChanged()
|
|
{
|
|
// only take changes when they are not done programmatically
|
|
// the active flag is set when the tree is being modified
|
|
// programmatically, but not when users interact with the widgets
|
|
if (active == false) {
|
|
// get the current zone range
|
|
if (ranges->currentItem()) {
|
|
|
|
int index = ranges->invisibleRootItem()->indexOfChild(ranges->currentItem());
|
|
PaceZoneRange current = paceZones->getZoneRange(index);
|
|
|
|
// embolden that range on the list to show it has been edited
|
|
QFont font;
|
|
font.setWeight(QFont::Black);
|
|
ranges->currentItem()->setFont(0, font);
|
|
ranges->currentItem()->setFont(1, font);
|
|
ranges->currentItem()->setFont(2, font);
|
|
|
|
// show the default button to undo
|
|
defaultButton->show();
|
|
|
|
// we manually edited so save in full
|
|
current.zonesSetFromCV = false;
|
|
|
|
// create the new zoneinfos for this range
|
|
QList<PaceZoneInfo> zoneinfos;
|
|
for (int i=0; i< zones->invisibleRootItem()->childCount(); i++) {
|
|
QTreeWidgetItem *item = zones->invisibleRootItem()->child(i);
|
|
QTimeEdit *loTimeEdit = (QTimeEdit*)zones->itemWidget(item, 2);
|
|
double kph = loTimeEdit->time() == QTime(0,0,0) ? 0.0 : paceZones->kphFromTime(loTimeEdit, metric->isChecked());
|
|
zoneinfos << PaceZoneInfo(item->text(0),
|
|
item->text(1),
|
|
kph,
|
|
0);
|
|
}
|
|
|
|
// now sort the list
|
|
qSort(zoneinfos);
|
|
|
|
// now fill the highs
|
|
for(int i=0; i<zoneinfos.count(); i++) {
|
|
if (i+1 <zoneinfos.count())
|
|
zoneinfos[i].hi = zoneinfos[i+1].lo;
|
|
else
|
|
zoneinfos[i].hi = INT_MAX;
|
|
}
|
|
current.zones = zoneinfos;
|
|
|
|
// now replace the current range struct
|
|
paceZones->setZoneRange(index, current);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Season Editor
|
|
//
|
|
SeasonsPage::SeasonsPage(QWidget *parent, Context *context) : QWidget(parent), context(context)
|
|
{
|
|
QGridLayout *mainLayout = new QGridLayout(this);
|
|
QFormLayout *editLayout = new QFormLayout;
|
|
editLayout->setFieldGrowthPolicy(QFormLayout::FieldsStayAtSizeHint);
|
|
|
|
// get the list
|
|
array = context->athlete->seasons->seasons;
|
|
|
|
// Edit widgets
|
|
nameEdit = new QLineEdit(this);
|
|
typeEdit = new QComboBox(this);
|
|
foreach(QString t, Season::types) typeEdit->addItem(t);
|
|
typeEdit->setCurrentIndex(0);
|
|
fromEdit = new QDateEdit(this);
|
|
fromEdit->setCalendarPopup(true);
|
|
|
|
toEdit = new QDateEdit(this);
|
|
toEdit->setCalendarPopup(true);
|
|
|
|
// set form
|
|
editLayout->addRow(new QLabel(tr("Name")), nameEdit);
|
|
editLayout->addRow(new QLabel(tr("Type")), typeEdit);
|
|
editLayout->addRow(new QLabel(tr("From")), fromEdit);
|
|
editLayout->addRow(new QLabel(tr("To")), toEdit);
|
|
|
|
addButton = new QPushButton(tr("+"));
|
|
deleteButton = new QPushButton(tr("-"));
|
|
#ifndef Q_OS_MAC
|
|
upButton = new QToolButton(this);
|
|
downButton = new QToolButton(this);
|
|
upButton->setArrowType(Qt::UpArrow);
|
|
downButton->setArrowType(Qt::DownArrow);
|
|
upButton->setFixedSize(20,20);
|
|
downButton->setFixedSize(20,20);
|
|
addButton->setFixedSize(20,20);
|
|
deleteButton->setFixedSize(20,20);
|
|
#else
|
|
addButton->setText(tr("Add"));
|
|
deleteButton->setText(tr("Delete"));
|
|
upButton = new QPushButton(tr("Up"));
|
|
downButton = new QPushButton(tr("Down"));
|
|
#endif
|
|
|
|
QVBoxLayout *actionButtons = new QVBoxLayout;
|
|
actionButtons->setSpacing(2);
|
|
actionButtons->addWidget(deleteButton);
|
|
actionButtons->addWidget(upButton);
|
|
actionButtons->addWidget(downButton);
|
|
actionButtons->addStretch();
|
|
|
|
seasons = new QTreeWidget;
|
|
seasons->headerItem()->setText(0, tr("Name"));
|
|
seasons->headerItem()->setText(1, tr("Type"));
|
|
seasons->headerItem()->setText(2, tr("From"));
|
|
seasons->headerItem()->setText(3, tr("To"));
|
|
seasons->setColumnCount(4);
|
|
seasons->setSelectionMode(QAbstractItemView::SingleSelection);
|
|
//seasons->setEditTriggers(QAbstractItemView::SelectedClicked); // allow edit
|
|
seasons->setUniformRowHeights(true);
|
|
seasons->setIndentation(0);
|
|
|
|
#ifdef Q_OS_MAC
|
|
//seasons->header()->resizeSection(0,160); // tab
|
|
//seasons->header()->resizeSection(1,80); // name
|
|
//seasons->header()->resizeSection(2,130); // type
|
|
//seasons->header()->resizeSection(3,130); // values
|
|
#else
|
|
//seasons->header()->resizeSection(0,160); // tab
|
|
//seasons->header()->resizeSection(1,80); // name
|
|
//seasons->header()->resizeSection(2,130); // type
|
|
//seasons->header()->resizeSection(3,130); // values
|
|
#endif
|
|
|
|
foreach(Season season, array) {
|
|
QTreeWidgetItem *add;
|
|
|
|
if (season.type == Season::temporary) continue;
|
|
|
|
add = new QTreeWidgetItem(seasons->invisibleRootItem());
|
|
add->setFlags(add->flags() & ~Qt::ItemIsEditable);
|
|
|
|
// tab name
|
|
add->setText(0, season.name);
|
|
// type
|
|
add->setText(1, Season::types[static_cast<int>(season.type)]);
|
|
// from
|
|
add->setText(2, season.start.toString(tr("ddd MMM d, yyyy")));
|
|
// to
|
|
add->setText(3, season.end.toString(tr("ddd MMM d, yyyy")));
|
|
// guid -- hidden
|
|
add->setText(4, season._id.toString());
|
|
|
|
}
|
|
seasons->setCurrentItem(seasons->invisibleRootItem()->child(0));
|
|
|
|
mainLayout->addLayout(editLayout, 0,0);
|
|
mainLayout->addWidget(addButton, 0,1, Qt::AlignTop);
|
|
mainLayout->addWidget(seasons, 1,0);
|
|
mainLayout->addLayout(actionButtons, 1,1);
|
|
|
|
// set all the edits to a default value
|
|
clearEdit();
|
|
|
|
// connect up slots
|
|
connect(upButton, SIGNAL(clicked()), this, SLOT(upClicked()));
|
|
connect(downButton, SIGNAL(clicked()), this, SLOT(downClicked()));
|
|
connect(addButton, SIGNAL(clicked()), this, SLOT(addClicked()));
|
|
connect(deleteButton, SIGNAL(clicked()), this, SLOT(deleteClicked()));
|
|
}
|
|
|
|
void
|
|
SeasonsPage::clearEdit()
|
|
{
|
|
typeEdit->setCurrentIndex(0);
|
|
nameEdit->setText("");
|
|
fromEdit->setDate(QDate::currentDate());
|
|
toEdit->setDate(QDate::currentDate().addMonths(3));
|
|
}
|
|
|
|
void
|
|
SeasonsPage::upClicked()
|
|
{
|
|
if (seasons->currentItem()) {
|
|
int index = seasons->invisibleRootItem()->indexOfChild(seasons->currentItem());
|
|
if (index == 0) return; // its at the top already
|
|
|
|
// movin on up!
|
|
QTreeWidgetItem* moved = seasons->invisibleRootItem()->takeChild(index);
|
|
seasons->invisibleRootItem()->insertChild(index-1, moved);
|
|
seasons->setCurrentItem(moved);
|
|
|
|
// and move the array too
|
|
array.move(index, index-1);
|
|
}
|
|
}
|
|
|
|
void
|
|
SeasonsPage::downClicked()
|
|
{
|
|
if (seasons->currentItem()) {
|
|
int index = seasons->invisibleRootItem()->indexOfChild(seasons->currentItem());
|
|
if (index == (seasons->invisibleRootItem()->childCount()-1)) return; // its at the bottom already
|
|
|
|
QTreeWidgetItem* moved = seasons->invisibleRootItem()->takeChild(index);
|
|
seasons->invisibleRootItem()->insertChild(index+1, moved);
|
|
seasons->setCurrentItem(moved);
|
|
|
|
array.move(index, index+1);
|
|
}
|
|
}
|
|
|
|
void
|
|
SeasonsPage::renameClicked()
|
|
{
|
|
// which one is selected?
|
|
if (seasons->currentItem()) seasons->editItem(seasons->currentItem(), 0);
|
|
}
|
|
|
|
void
|
|
SeasonsPage::addClicked()
|
|
{
|
|
if (nameEdit->text() == "") return; // just ignore it
|
|
|
|
// swap if not right way around
|
|
if (fromEdit->date() > toEdit->date()) {
|
|
QDate temp = fromEdit->date();
|
|
fromEdit->setDate(toEdit->date());
|
|
toEdit->setDate(temp);
|
|
}
|
|
|
|
QTreeWidgetItem *add = new QTreeWidgetItem(seasons->invisibleRootItem());
|
|
add->setFlags(add->flags() & ~Qt::ItemIsEditable);
|
|
QString id;
|
|
|
|
// tab name
|
|
add->setText(0, nameEdit->text());
|
|
// type
|
|
add->setText(1, Season::types[typeEdit->currentIndex()]);
|
|
// from
|
|
add->setText(2, fromEdit->date().toString(tr("ddd MMM d, yyyy")));
|
|
// to
|
|
add->setText(3, toEdit->date().toString(tr("ddd MMM d, yyyy")));
|
|
// guid -- hidden
|
|
add->setText(4, (id=QUuid::createUuid().toString()));
|
|
|
|
// now clear the edits
|
|
clearEdit();
|
|
|
|
Season addSeason;
|
|
addSeason.setStart(fromEdit->date());
|
|
addSeason.setEnd(toEdit->date());
|
|
addSeason.setName(nameEdit->text());
|
|
addSeason.setType(typeEdit->currentIndex());
|
|
addSeason.setId(id);
|
|
array.append(Season());
|
|
}
|
|
|
|
void
|
|
SeasonsPage::deleteClicked()
|
|
{
|
|
if (seasons->currentItem()) {
|
|
int index = seasons->invisibleRootItem()->indexOfChild(seasons->currentItem());
|
|
|
|
// zap!
|
|
delete seasons->invisibleRootItem()->takeChild(index);
|
|
|
|
array.removeAt(index);
|
|
}
|
|
}
|
|
|
|
qint32
|
|
SeasonsPage::saveClicked()
|
|
{
|
|
// get any edits to the names and dates
|
|
// since we don't trap these as they are made
|
|
for(int i=0; i<seasons->invisibleRootItem()->childCount(); i++) {
|
|
|
|
QTreeWidgetItem *item = seasons->invisibleRootItem()->child(i);
|
|
|
|
array[i].setName(item->text(0));
|
|
array[i].setType(Season::types.indexOf(item->text(1)));
|
|
array[i].setStart(QDate::fromString(item->text(2), "ddd MMM d, yyyy"));
|
|
array[i].setEnd(QDate::fromString(item->text(3), "ddd MMM d, yyyy"));
|
|
array[i]._id = QUuid(item->text(4));
|
|
}
|
|
|
|
// write to disk
|
|
QString file = QString(context->athlete->home->config().canonicalPath() + "/seasons.xml");
|
|
SeasonParser::serialize(file, array);
|
|
|
|
// re-read
|
|
context->athlete->seasons->readSeasons();
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
AutoImportPage::AutoImportPage(Context *context) : context(context)
|
|
{
|
|
QGridLayout *mainLayout = new QGridLayout(this);
|
|
|
|
addButton = new QPushButton(tr("+"));
|
|
deleteButton = new QPushButton(tr("-"));
|
|
browseButton = new QPushButton(tr("Browse"));
|
|
#ifndef Q_OS_MAC
|
|
upButton = new QToolButton(this);
|
|
downButton = new QToolButton(this);
|
|
upButton->setArrowType(Qt::UpArrow);
|
|
downButton->setArrowType(Qt::DownArrow);
|
|
upButton->setFixedSize(20,20);
|
|
downButton->setFixedSize(20,20);
|
|
addButton->setFixedSize(20,20);
|
|
deleteButton->setFixedSize(20,20);
|
|
#else
|
|
addButton->setText(tr("Add"));
|
|
deleteButton->setText(tr("Delete"));
|
|
upButton = new QPushButton(tr("Up"));
|
|
downButton = new QPushButton(tr("Down"));
|
|
#endif
|
|
QHBoxLayout *actionButtons = new QHBoxLayout;
|
|
actionButtons->setSpacing(2);
|
|
actionButtons->addWidget(upButton);
|
|
actionButtons->addWidget(downButton);
|
|
actionButtons->addStretch();
|
|
actionButtons->addWidget(browseButton);
|
|
actionButtons->addStretch();
|
|
actionButtons->addWidget(addButton);
|
|
actionButtons->addWidget(deleteButton);
|
|
|
|
fields = new QTreeWidget;
|
|
fields->headerItem()->setText(0, tr("Directory"));
|
|
fields->headerItem()->setText(1, tr("Import Rule"));
|
|
fields->setColumnWidth(0,400);
|
|
fields->setColumnWidth(1,100);
|
|
fields->setColumnCount(2);
|
|
fields->setSelectionMode(QAbstractItemView::SingleSelection);
|
|
//fields->setUniformRowHeights(true);
|
|
fields->setIndentation(0);
|
|
|
|
fields->setCurrentItem(fields->invisibleRootItem()->child(0));
|
|
|
|
mainLayout->addWidget(fields, 0,0);
|
|
mainLayout->addLayout(actionButtons, 1,0);
|
|
|
|
context->athlete->autoImportConfig->readConfig();
|
|
QList<RideAutoImportRule> rules = context->athlete->autoImportConfig->getConfig();
|
|
int index = 0;
|
|
foreach (RideAutoImportRule rule, rules) {
|
|
QComboBox *comboButton = new QComboBox(this);
|
|
addRuleTypes(comboButton);
|
|
QTreeWidgetItem *add = new QTreeWidgetItem;
|
|
fields->invisibleRootItem()->insertChild(index, add);
|
|
add->setFlags(add->flags() | Qt::ItemIsEditable);
|
|
|
|
add->setTextAlignment(0, Qt::AlignLeft | Qt::AlignVCenter);
|
|
add->setText(0, rule.getDirectory());
|
|
|
|
add->setTextAlignment(1, Qt::AlignHCenter | Qt::AlignVCenter);
|
|
comboButton->setCurrentIndex(rule.getImportRule());
|
|
fields->setItemWidget(add, 1, comboButton);
|
|
index++;
|
|
}
|
|
|
|
// connect up slots
|
|
connect(upButton, SIGNAL(clicked()), this, SLOT(upClicked()));
|
|
connect(downButton, SIGNAL(clicked()), this, SLOT(downClicked()));
|
|
connect(addButton, SIGNAL(clicked()), this, SLOT(addClicked()));
|
|
connect(deleteButton, SIGNAL(clicked()), this, SLOT(deleteClicked()));
|
|
connect(browseButton, SIGNAL(clicked()), this, SLOT(browseImportDir()));
|
|
}
|
|
|
|
void
|
|
AutoImportPage::upClicked()
|
|
{
|
|
if (fields->currentItem()) {
|
|
int index = fields->invisibleRootItem()->indexOfChild(fields->currentItem());
|
|
if (index == 0) return; // its at the top already
|
|
|
|
//movin on up!
|
|
QWidget *button = fields->itemWidget(fields->currentItem(),1);
|
|
QComboBox *comboButton = new QComboBox(this);
|
|
addRuleTypes(comboButton);
|
|
comboButton->setCurrentIndex(((QComboBox*)button)->currentIndex());
|
|
QTreeWidgetItem* moved = fields->invisibleRootItem()->takeChild(index);
|
|
fields->invisibleRootItem()->insertChild(index-1, moved);
|
|
fields->setItemWidget(moved, 1, comboButton);
|
|
fields->setCurrentItem(moved);
|
|
}
|
|
}
|
|
|
|
void
|
|
AutoImportPage::downClicked()
|
|
{
|
|
if (fields->currentItem()) {
|
|
int index = fields->invisibleRootItem()->indexOfChild(fields->currentItem());
|
|
if (index == (fields->invisibleRootItem()->childCount()-1)) return; // its at the bottom already
|
|
|
|
QWidget *button = fields->itemWidget(fields->currentItem(),1);
|
|
QComboBox *comboButton = new QComboBox(this);
|
|
addRuleTypes(comboButton);
|
|
comboButton->setCurrentIndex(((QComboBox*)button)->currentIndex());
|
|
QTreeWidgetItem* moved = fields->invisibleRootItem()->takeChild(index);
|
|
fields->invisibleRootItem()->insertChild(index+1, moved);
|
|
fields->setItemWidget(moved, 1, comboButton);
|
|
fields->setCurrentItem(moved);
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
AutoImportPage::addClicked()
|
|
{
|
|
|
|
int index = fields->invisibleRootItem()->indexOfChild(fields->currentItem());
|
|
if (index < 0) index = 0;
|
|
|
|
QComboBox *comboButton = new QComboBox(this);
|
|
addRuleTypes(comboButton);
|
|
|
|
QTreeWidgetItem *add = new QTreeWidgetItem;
|
|
fields->invisibleRootItem()->insertChild(index, add);
|
|
add->setFlags(add->flags() | Qt::ItemIsEditable);
|
|
|
|
add->setTextAlignment(0, Qt::AlignLeft | Qt::AlignVCenter);
|
|
add->setText(0, tr("Enter directory or press [Browse] to select"));
|
|
add->setTextAlignment(1, Qt::AlignHCenter | Qt::AlignVCenter);
|
|
fields->setItemWidget(add, 1, comboButton);
|
|
|
|
}
|
|
|
|
void
|
|
AutoImportPage::deleteClicked()
|
|
{
|
|
if (fields->currentItem()) {
|
|
int index = fields->invisibleRootItem()->indexOfChild(fields->currentItem());
|
|
|
|
// zap!
|
|
delete fields->invisibleRootItem()->takeChild(index);
|
|
}
|
|
}
|
|
|
|
qint32
|
|
AutoImportPage::saveClicked()
|
|
{
|
|
|
|
rules.clear();
|
|
for(int i=0; i<fields->invisibleRootItem()->childCount(); i++) {
|
|
|
|
RideAutoImportRule rule;
|
|
rule.setDirectory(fields->invisibleRootItem()->child(i)->text(0));
|
|
|
|
QWidget *button = fields->itemWidget(fields->invisibleRootItem()->child(i),1);
|
|
rule.setImportRule(((QComboBox*)button)->currentIndex());
|
|
rules.append(rule);
|
|
|
|
}
|
|
|
|
// write to disk
|
|
QString file = QString(context->athlete->home->config().canonicalPath() + "/autoimport.xml");
|
|
RideAutoImportConfigParser::serialize(file, rules);
|
|
|
|
// re-read
|
|
context->athlete->autoImportConfig->readConfig();
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
AutoImportPage::addRuleTypes(QComboBox *p) {
|
|
|
|
RideAutoImportRule* config = new RideAutoImportRule();
|
|
QList<QString> descriptions = config->getRuleDescriptions();
|
|
|
|
foreach(QString description, descriptions) {
|
|
p->addItem(description);
|
|
}
|
|
}
|
|
|
|
void
|
|
AutoImportPage::browseImportDir()
|
|
{
|
|
QStringList selectedDirs;
|
|
if (fields->currentItem()) {
|
|
QFileDialog fileDialog(this);
|
|
fileDialog.setFileMode(QFileDialog::Directory);
|
|
fileDialog.setOptions(QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
|
|
if (fileDialog.exec()) {
|
|
selectedDirs = fileDialog.selectedFiles();
|
|
}
|
|
if (selectedDirs.count() > 0) {
|
|
QString dir = selectedDirs.at(0);
|
|
if (dir != "") {
|
|
fields->currentItem()->setText(0, dir);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
IntervalsPage::IntervalsPage(Context *context) : context(context)
|
|
{
|
|
// get config
|
|
b4.discovery = appsettings->cvalue(context->athlete->cyclist, GC_DISCOVERY, 57).toInt(); // 57 does not include search for PEAKs
|
|
|
|
QVBoxLayout *mainLayout = new QVBoxLayout(this);
|
|
QGridLayout *layout = new QGridLayout();
|
|
mainLayout->addLayout(layout);
|
|
mainLayout->addStretch();
|
|
|
|
QLabel *heading = new QLabel(tr("Enable interval auto-discovery:"));
|
|
heading->setFixedHeight(QFontMetrics(heading->font()).height() + 4);
|
|
|
|
int row = 0;
|
|
layout->addWidget(heading, row, 0, Qt::AlignRight);
|
|
|
|
user = 99;
|
|
for(int i=0; i<= static_cast<int>(RideFileInterval::last()); i++) {
|
|
|
|
// ignore until we get past user interval type
|
|
if (i == static_cast<int>(RideFileInterval::USER)) user=i;
|
|
|
|
// if past user then add
|
|
if (i>user) {
|
|
QCheckBox *add = new QCheckBox(RideFileInterval::typeDescriptionLong(static_cast<RideFileInterval::IntervalType>(i)));
|
|
checkBoxes << add;
|
|
add->setChecked(b4.discovery & RideFileInterval::intervalTypeBits(static_cast<RideFileInterval::IntervalType>(i)));
|
|
layout->addWidget(add, row++, 1, Qt::AlignLeft);
|
|
}
|
|
}
|
|
}
|
|
|
|
qint32
|
|
IntervalsPage::saveClicked()
|
|
{
|
|
int discovery = 0;
|
|
|
|
// now update discovery !
|
|
for(int i=0; i< checkBoxes.count(); i++)
|
|
if (checkBoxes[i]->isChecked())
|
|
discovery += RideFileInterval::intervalTypeBits(static_cast<RideFileInterval::IntervalType>(i+user+1));
|
|
|
|
// new value returned
|
|
appsettings->setCValue(context->athlete->cyclist, GC_DISCOVERY, discovery);
|
|
|
|
// return change !
|
|
if (b4.discovery != discovery) return CONFIG_DISCOVERY;
|
|
else return 0;
|
|
}
|