mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-02-14 00:28:42 +00:00
This commit adds a page to the config dialog in which the user can choose which metrics GC will show for intervals. The GUI design could use some work, but the functionality is there. All implemented metrics are available to choose, and choices are saved across restarts.
394 lines
14 KiB
C++
394 lines
14 KiB
C++
#include <QtGui>
|
|
#include <QSettings>
|
|
#include <assert.h>
|
|
|
|
#include "MainWindow.h"
|
|
#include "ConfigDialog.h"
|
|
#include "RealtimeWindow.h"
|
|
#include "Pages.h"
|
|
#include "Settings.h"
|
|
#include "Zones.h"
|
|
|
|
|
|
/* cyclist dialog protocol redesign:
|
|
* no zones:
|
|
* calendar disabled
|
|
* automatically go into "new" mode
|
|
* zone(s) defined:
|
|
* click on calendar: sets current zone to that associated with date
|
|
* save clicked:
|
|
* if new mode, create a new zone starting at selected date, or for all dates
|
|
* if this is only zone.
|
|
* delete clicked:
|
|
* deletes currently selected zone
|
|
*/
|
|
|
|
ConfigDialog::ConfigDialog(QDir _home, Zones *_zones, MainWindow *mainWindow) :
|
|
mainWindow(mainWindow), zones(_zones)
|
|
{
|
|
setAttribute(Qt::WA_DeleteOnClose);
|
|
|
|
home = _home;
|
|
|
|
cyclistPage = new CyclistPage(zones);
|
|
|
|
contentsWidget = new QListWidget;
|
|
contentsWidget->setViewMode(QListView::IconMode);
|
|
contentsWidget->setIconSize(QSize(96, 84));
|
|
contentsWidget->setMovement(QListView::Static);
|
|
contentsWidget->setMinimumWidth(128);
|
|
contentsWidget->setMaximumWidth(128);
|
|
contentsWidget->setMinimumHeight(128);
|
|
contentsWidget->setSpacing(12);
|
|
|
|
configPage = new ConfigurationPage();
|
|
|
|
intervalMetricsPage = new IntervalMetricsPage;
|
|
devicePage = new DevicePage(this);
|
|
|
|
pagesWidget = new QStackedWidget;
|
|
pagesWidget->addWidget(configPage);
|
|
pagesWidget->addWidget(cyclistPage);
|
|
pagesWidget->addWidget(intervalMetricsPage);
|
|
pagesWidget->addWidget(devicePage);
|
|
|
|
closeButton = new QPushButton(tr("Close"));
|
|
saveButton = new QPushButton(tr("Save"));
|
|
|
|
createIcons();
|
|
contentsWidget->setCurrentItem(contentsWidget->item(0));
|
|
|
|
// connect(closeButton, SIGNAL(clicked()), this, SLOT(reject()));
|
|
// connect(saveButton, SIGNAL(clicked()), this, SLOT(accept()));
|
|
connect(closeButton, SIGNAL(clicked()), this, SLOT(accept()));
|
|
connect(cyclistPage->btnBack, SIGNAL(clicked()), this, SLOT(back_Clicked()));
|
|
connect(cyclistPage->btnForward, SIGNAL(clicked()), this, SLOT(forward_Clicked()));
|
|
connect(cyclistPage->btnDelete, SIGNAL(clicked()), this, SLOT(delete_Clicked()));
|
|
connect(cyclistPage->calendar, SIGNAL(selectionChanged()), this, SLOT(calendarDateChanged()));
|
|
|
|
// connect the pieces...
|
|
connect(devicePage->typeSelector, SIGNAL(currentIndexChanged(int)), this, SLOT(changedType(int)));
|
|
connect(devicePage->addButton, SIGNAL(clicked()), this, SLOT(devaddClicked()));
|
|
connect(devicePage->delButton, SIGNAL(clicked()), this, SLOT(devdelClicked()));
|
|
connect(devicePage->pairButton, SIGNAL(clicked()), this, SLOT(devpairClicked()));
|
|
|
|
horizontalLayout = new QHBoxLayout;
|
|
horizontalLayout->addWidget(contentsWidget);
|
|
horizontalLayout->addWidget(pagesWidget, 1);
|
|
|
|
buttonsLayout = new QHBoxLayout;
|
|
buttonsLayout->addStretch(1);
|
|
buttonsLayout->addWidget(closeButton);
|
|
buttonsLayout->addWidget(saveButton);
|
|
|
|
mainLayout = new QVBoxLayout;
|
|
mainLayout->addLayout(horizontalLayout);
|
|
mainLayout->addStretch(1);
|
|
mainLayout->addSpacing(12);
|
|
mainLayout->addLayout(buttonsLayout);
|
|
setLayout(mainLayout);
|
|
|
|
setWindowTitle(tr("Config Dialog"));
|
|
}
|
|
|
|
void ConfigDialog::createIcons()
|
|
{
|
|
QListWidgetItem *configButton = new QListWidgetItem(contentsWidget);
|
|
configButton->setIcon(QIcon(":/images/config.png"));
|
|
configButton->setText(tr("Configuration"));
|
|
configButton->setTextAlignment(Qt::AlignHCenter);
|
|
configButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
|
|
|
|
|
|
QListWidgetItem *cyclistButton = new QListWidgetItem(contentsWidget);
|
|
cyclistButton->setIcon(QIcon(":images/cyclist.png"));
|
|
cyclistButton->setText(tr("Cyclist Info"));
|
|
cyclistButton->setTextAlignment(Qt::AlignHCenter);
|
|
cyclistButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
|
|
|
|
QListWidgetItem *intervalMetricsButton = new QListWidgetItem(contentsWidget);
|
|
intervalMetricsButton->setIcon(QIcon(":images/imetrics.png"));
|
|
intervalMetricsButton->setText(tr("Interval Metrics"));
|
|
intervalMetricsButton->setTextAlignment(Qt::AlignHCenter);
|
|
intervalMetricsButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
|
|
|
|
QListWidgetItem *realtimeButton = new QListWidgetItem(contentsWidget);
|
|
realtimeButton->setIcon(QIcon(":images/arduino.png"));
|
|
realtimeButton->setText(tr("Devices"));
|
|
realtimeButton->setTextAlignment(Qt::AlignHCenter);
|
|
realtimeButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
|
|
|
|
connect(contentsWidget,
|
|
SIGNAL(currentItemChanged(QListWidgetItem *, QListWidgetItem *)),
|
|
this, SLOT(changePage(QListWidgetItem *, QListWidgetItem*)));
|
|
|
|
connect(saveButton, SIGNAL(clicked()), this, SLOT(save_Clicked()));
|
|
}
|
|
|
|
|
|
void ConfigDialog::createNewRange()
|
|
{
|
|
}
|
|
|
|
void ConfigDialog::changePage(QListWidgetItem *current, QListWidgetItem *previous)
|
|
{
|
|
if (!current)
|
|
current = previous;
|
|
|
|
pagesWidget->setCurrentIndex(contentsWidget->row(current));
|
|
}
|
|
|
|
// if save is clicked, we want to:
|
|
// new mode: create a new zone starting at the selected date (which may be null, implying BEGIN
|
|
// ! new mode: change the CP associated with the present mode
|
|
void ConfigDialog::save_Clicked()
|
|
{
|
|
boost::shared_ptr<QSettings> settings = GetApplicationSettings();
|
|
|
|
if (configPage->langCombo->currentIndex()==0)
|
|
settings->setValue(GC_LANG, "en");
|
|
else if (configPage->langCombo->currentIndex()==1)
|
|
settings->setValue(GC_LANG, "fr");
|
|
else if (configPage->langCombo->currentIndex()==2)
|
|
settings->setValue(GC_LANG, "ja");
|
|
|
|
if (configPage->unitCombo->currentIndex()==0)
|
|
settings->setValue(GC_UNIT, "Metric");
|
|
else if (configPage->unitCombo->currentIndex()==1)
|
|
settings->setValue(GC_UNIT, "Imperial");
|
|
|
|
settings->setValue(GC_ALLRIDES_ASCENDING, configPage->allRidesAscending->checkState());
|
|
settings->setValue(GC_CRANKLENGTH, configPage->crankLengthCombo->currentText());
|
|
settings->setValue(GC_BIKESCOREDAYS, configPage->BSdaysEdit->text());
|
|
settings->setValue(GC_BIKESCOREMODE, configPage->bsModeCombo->currentText());
|
|
settings->setValue(GC_WORKOUTDIR, configPage->workoutDirectory->text());
|
|
settings->setValue(GC_INITIAL_STS, cyclistPage->perfManStart->text());
|
|
settings->setValue(GC_INITIAL_LTS, cyclistPage->perfManStart->text());
|
|
settings->setValue(GC_STS_DAYS, cyclistPage->perfManSTSavg->text());
|
|
settings->setValue(GC_LTS_DAYS, cyclistPage->perfManLTSavg->text());
|
|
|
|
// set default stress names if not set:
|
|
settings->setValue(GC_STS_NAME, settings->value(GC_STS_NAME,tr("Short Term Stress")));
|
|
settings->setValue(GC_STS_ACRONYM, settings->value(GC_STS_ACRONYM,tr("STS")));
|
|
settings->setValue(GC_LTS_NAME, settings->value(GC_LTS_NAME,tr("Long Term Stress")));
|
|
settings->setValue(GC_LTS_ACRONYM, settings->value(GC_LTS_ACRONYM,tr("LTS")));
|
|
settings->setValue(GC_SB_NAME, settings->value(GC_SB_NAME,tr("Stress Balance")));
|
|
settings->setValue(GC_SB_ACRONYM, settings->value(GC_SB_ACRONYM,tr("SB")));
|
|
|
|
|
|
// if the CP text entry reads invalid, there's nothing we can do
|
|
int cp = cyclistPage->getCP();
|
|
if (cp == 0) {
|
|
QMessageBox::warning(this, tr("Invalid CP"), "Please enter valid CP and try again.");
|
|
cyclistPage->setCPFocus();
|
|
return;
|
|
}
|
|
|
|
// if for some reason we have no zones yet, then create them
|
|
int range = cyclistPage->getCurrentRange();
|
|
|
|
// if this is new mode, or if no zone ranges are yet defined, set up the new range
|
|
if ((range == -1) || (cyclistPage->isNewMode()))
|
|
cyclistPage->setCurrentRange(range = zones->insertRangeAtDate(cyclistPage->selectedDate(), cp));
|
|
else
|
|
zones->setCP(range, cyclistPage->getText().toInt());
|
|
|
|
zones->setZonesFromCP(range);
|
|
|
|
// update the "new zone" checkbox to visible and unchecked
|
|
cyclistPage->checkboxNew->setChecked(Qt::Unchecked);
|
|
cyclistPage->checkboxNew->setEnabled(true);
|
|
|
|
zones->write(home);
|
|
|
|
intervalMetricsPage->saveClicked();
|
|
|
|
// Save the device configuration...
|
|
DeviceConfigurations all;
|
|
all.writeConfig(devicePage->deviceListModel->Configuration);
|
|
|
|
// Tell MainWindow we changed config, so it can emit the signal
|
|
// configChanged() to all its children
|
|
mainWindow->notifyConfigChanged();
|
|
}
|
|
|
|
void ConfigDialog::moveCalendarToCurrentRange() {
|
|
int range = cyclistPage->getCurrentRange();
|
|
|
|
if (range < 0)
|
|
return;
|
|
|
|
QDate date;
|
|
|
|
// put the cursor at the beginning of the selected range if it's not the first
|
|
if (range > 0)
|
|
date = zones->getStartDate(cyclistPage->getCurrentRange());
|
|
// unless the range is the first range, in which case it goes at the end of that range
|
|
// use JulianDay to subtract one day from the end date, which is actually the first
|
|
// day of the following range
|
|
else
|
|
date = QDate::fromJulianDay(zones->getEndDate(cyclistPage->getCurrentRange()).toJulianDay() - 1);
|
|
|
|
cyclistPage->setSelectedDate(date);
|
|
}
|
|
|
|
void ConfigDialog::back_Clicked()
|
|
{
|
|
QDate date;
|
|
cyclistPage->setCurrentRange(cyclistPage->getCurrentRange() - 1);
|
|
moveCalendarToCurrentRange();
|
|
}
|
|
|
|
void ConfigDialog::forward_Clicked()
|
|
{
|
|
QDate date;
|
|
cyclistPage->setCurrentRange(cyclistPage->getCurrentRange() + 1);
|
|
moveCalendarToCurrentRange();
|
|
}
|
|
|
|
void ConfigDialog::delete_Clicked() {
|
|
int range = cyclistPage->getCurrentRange();
|
|
int num_ranges = zones->getRangeSize();
|
|
assert (num_ranges > 1);
|
|
QMessageBox msgBox;
|
|
msgBox.setText(
|
|
tr("Are you sure you want to delete the zone range\n"
|
|
"from %1 to %2?\n"
|
|
"(%3 range will extend to this date range):") .
|
|
arg(zones->getStartDateString(cyclistPage->getCurrentRange())) .
|
|
arg(zones->getEndDateString(cyclistPage->getCurrentRange())) .
|
|
arg((range > 0) ? "previous" : "next")
|
|
);
|
|
QPushButton *deleteButton = msgBox.addButton(tr("Delete"),QMessageBox::YesRole);
|
|
msgBox.setStandardButtons(QMessageBox::Cancel);
|
|
msgBox.setDefaultButton(QMessageBox::Cancel);
|
|
msgBox.setIcon(QMessageBox::Critical);
|
|
msgBox.exec();
|
|
if(msgBox.clickedButton() == deleteButton)
|
|
cyclistPage->setCurrentRange(zones->deleteRange(range));
|
|
|
|
zones->write(home);
|
|
}
|
|
|
|
void ConfigDialog::calendarDateChanged() {
|
|
int range = zones->whichRange(cyclistPage->selectedDate());
|
|
assert(range >= 0);
|
|
cyclistPage->setCurrentRange(range);
|
|
}
|
|
|
|
//
|
|
// DEVICE CONFIG STUFF
|
|
//
|
|
|
|
void
|
|
ConfigDialog::changedType(int)
|
|
{
|
|
// THIS CODE IS DISABLED FOR THIS RELEASE XXX
|
|
// // disable/enable default checkboxes
|
|
// if (devicePage->devices.at(index).download == false) {
|
|
// devicePage->isDefaultDownload->setEnabled(false);
|
|
// devicePage->isDefaultDownload->setCheckState(Qt::Unchecked);
|
|
// } else {
|
|
// devicePage->isDefaultDownload->setEnabled(true);
|
|
// }
|
|
// if (devicePage->devices.at(index).realtime == false) {
|
|
// devicePage->isDefaultRealtime->setEnabled(false);
|
|
// devicePage->isDefaultRealtime->setCheckState(Qt::Unchecked);
|
|
// } else {
|
|
// devicePage->isDefaultRealtime->setEnabled(true);
|
|
// }
|
|
devicePage->setConfigPane();
|
|
}
|
|
|
|
void
|
|
ConfigDialog::devaddClicked()
|
|
{
|
|
DeviceConfiguration add;
|
|
DeviceTypes Supported;
|
|
|
|
// get values from the gui elements
|
|
add.name = devicePage->deviceName->displayText();
|
|
add.type = devicePage->typeSelector->itemData(devicePage->typeSelector->currentIndex()).toInt();
|
|
add.portSpec = devicePage->deviceSpecifier->displayText();
|
|
add.deviceProfile = devicePage->deviceProfile->displayText();
|
|
|
|
// NOT IMPLEMENTED IN THIS RELEASE XXX
|
|
//add.isDefaultDownload = devicePage->isDefaultDownload->isChecked() ? true : false;
|
|
//add.isDefaultRealtime = devicePage->isDefaultDownload->isChecked() ? true : false;
|
|
|
|
// Validate the name
|
|
QRegExp nameSpec(".+");
|
|
if (nameSpec.exactMatch(add.name) == false) {
|
|
QMessageBox::critical(0, "Invalid Device Name",
|
|
QString("Device Name should be non-blank"));
|
|
return ;
|
|
}
|
|
|
|
// Validate the portSpec
|
|
QRegExp antSpec("[^:]*:[^:]*"); // ip:port same as TCP ... for now...
|
|
QRegExp tcpSpec("[^:]*:[^:]*"); // ip:port
|
|
#ifdef WIN32
|
|
QRegExp serialSpec("COM[0-9]*"); // COMx for WIN32, /dev/* for others
|
|
#else
|
|
QRegExp serialSpec("/dev/.*"); // COMx for WIN32, /dev/* for others
|
|
#endif
|
|
|
|
// check the portSpec is valid, based upon the connection type
|
|
switch (Supported.getType(add.type).connector) {
|
|
case DEV_ANT :
|
|
if (antSpec.exactMatch(add.portSpec) == false) {
|
|
QMessageBox::critical(0, "Invalid Port Specification",
|
|
QString("For ANT devices the specifier must be ") +
|
|
"hostname:portnumber");
|
|
return ;
|
|
}
|
|
break;
|
|
case DEV_SERIAL :
|
|
if (serialSpec.exactMatch(add.portSpec) == false) {
|
|
QMessageBox::critical(0, "Invalid Port Specification",
|
|
QString("For Serial devices the specifier must be ") +
|
|
#ifdef WIN32
|
|
"COMn"
|
|
#else
|
|
"/dev/xxxxx"
|
|
#endif
|
|
);
|
|
return ;
|
|
}
|
|
break;
|
|
case DEV_TCP :
|
|
if (tcpSpec.exactMatch(add.portSpec) == false) {
|
|
QMessageBox::critical(0, "Invalid Port Specification",
|
|
QString("For TCP streaming devices the specifier must be ") +
|
|
"hostname:portnumber");
|
|
return ;
|
|
}
|
|
break;
|
|
}
|
|
|
|
devicePage->deviceListModel->add(add);
|
|
}
|
|
|
|
void
|
|
ConfigDialog::devdelClicked()
|
|
{
|
|
devicePage->deviceListModel->del();
|
|
}
|
|
|
|
void
|
|
ConfigDialog::devpairClicked()
|
|
{
|
|
DeviceConfiguration add;
|
|
|
|
// get values from the gui elements
|
|
add.name = devicePage->deviceName->displayText();
|
|
add.type = devicePage->typeSelector->itemData(devicePage->typeSelector->currentIndex()).toInt();
|
|
add.portSpec = devicePage->deviceSpecifier->displayText();
|
|
add.deviceProfile = devicePage->deviceProfile->displayText();
|
|
|
|
QProgressDialog *progress = new QProgressDialog("Looking for Devices...", "Abort Scan", 0, 200, this);
|
|
progress->setWindowModality(Qt::WindowModal);
|
|
|
|
devicePage->pairClicked(&add, progress);
|
|
}
|