Files
GoldenCheetah/src/ManualRideDialog.cpp
Chris Cleeland 65b9c14255 allow a calendar popup
Interestingly, Chris Cleeland, and apparently only Chris Cleeland, can't
enter manual rides for dates in the past without this patch.

Fixes #7.
2010-01-06 08:21:07 -05:00

378 lines
12 KiB
C++

/*
* Copyright (c) 2009 Eric Murray (ericm@lne.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 "ManualRideDialog.h"
#include "MainWindow.h"
#include "Settings.h"
#include <assert.h>
#include <string.h>
#include <errno.h>
#include <QtGui>
#include <math.h>
#include <boost/bind.hpp>
ManualRideDialog::ManualRideDialog(MainWindow *mainWindow,
const QDir &home, bool useMetric) :
mainWindow(mainWindow), home(home)
{
useMetricUnits = useMetric;
int row;
mainWindow->getBSFactors(timeBS,distanceBS,timeDP,distanceDP);
setAttribute(Qt::WA_DeleteOnClose);
setWindowTitle(tr("Manually Enter Ride Data"));
// ride date
QLabel *manualDateLabel = new QLabel(tr("Ride date: "), this);
dateTimeEdit = new QDateTimeEdit( QDateTime::currentDateTime(), this );
// Wed 6/24/09 6:55 AM
dateTimeEdit->setDisplayFormat(tr("ddd MMM d, yyyy h:mm AP"));
dateTimeEdit->setCalendarPopup(true);
// ride length
QLabel *manualLengthLabel = new QLabel(tr("Ride length: "), this);
QHBoxLayout *manualLengthLayout = new QHBoxLayout;
hrslbl = new QLabel(tr("hours"),this);
hrslbl->setFrameStyle(QFrame::Panel | QFrame::Sunken);
hrsentry = new QLineEdit(this);
QIntValidator * hoursValidator = new QIntValidator(0,99,this);
//hrsentry->setInputMask("09");
hrsentry->setValidator(hoursValidator);
manualLengthLayout->addWidget(hrslbl);
manualLengthLayout->addWidget(hrsentry);
minslbl = new QLabel(tr("mins"),this);
minslbl->setFrameStyle(QFrame::Panel | QFrame::Sunken);
minsentry = new QLineEdit(this);
QIntValidator * secsValidator = new QIntValidator(0,60,this);
//minsentry->setInputMask("00");
minsentry->setValidator(secsValidator);
manualLengthLayout->addWidget(minslbl);
manualLengthLayout->addWidget(minsentry);
secslbl = new QLabel(tr("secs"),this);
secslbl->setFrameStyle(QFrame::Panel | QFrame::Sunken);
secsentry = new QLineEdit(this);
//secsentry->setInputMask("00");
secsentry->setValidator(secsValidator);
manualLengthLayout->addWidget(secslbl);
manualLengthLayout->addWidget(secsentry);
// ride distance
QString *DistanceString = new QString(tr("Distance "));
if (useMetricUnits)
DistanceString->append("(" + tr("km") + "):");
else
DistanceString->append("(" + tr("miles") + "):");
QLabel *DistanceLabel = new QLabel(*DistanceString, this);
QDoubleValidator * distanceValidator = new QDoubleValidator(0,1000,2,this);
distanceentry = new QLineEdit(this);
//distanceentry->setInputMask("009.00");
distanceentry->setValidator(distanceValidator);
// AvgHR
QLabel *HRLabel = new QLabel(tr("Average HR: "), this);
HRentry = new QLineEdit(this);
QIntValidator *hrValidator = new QIntValidator(0,200,this);
//HRentry->setInputMask("099");
HRentry->setValidator(hrValidator);
// how to estimate BikeScore:
QLabel *BSEstLabel;
boost::shared_ptr<QSettings> settings = GetApplicationSettings();
QVariant BSmode = settings->value(GC_BIKESCOREMODE);
estBSbyTimeButton = NULL;
estBSbyDistButton = NULL;
if (timeBS || distanceBS) {
BSEstLabel = new QLabel(tr("Estimate BikeScore by: "));
if (timeBS) {
estBSbyTimeButton = new QRadioButton(tr("Time"));
// default to time based unless no timeBS factor
if (BSmode.toString() != "distance")
estBSbyTimeButton->setDown(true);
}
if (distanceBS) {
estBSbyDistButton = new QRadioButton(tr("Distance"));
if (BSmode.toString() == "distance" || ! timeBS)
estBSbyDistButton->setDown(true);
}
}
// BikeScore
QLabel *ManualBSLabel = new QLabel(tr("BikeScore: "), this);
BSentry = new QLineEdit(this);
BSentry->setInputMask("009");
BSentry->clear();
// DanielsPoints
QLabel *ManualDPLabel = new QLabel(tr("Daniels Points: "), this);
DPentry = new QLineEdit(this);
DPentry->setInputMask("009");
DPentry->clear();
// buttons
enterButton = new QPushButton(tr("&OK"), this);
cancelButton = new QPushButton(tr("&Cancel"), this);
// don't let Enter write a new (and possibly incomplete) manual file:
enterButton->setDefault(false);
cancelButton->setDefault(true);
// Set up the layout:
QGridLayout *glayout = new QGridLayout(this);
row = 0;
glayout->addWidget(manualDateLabel, row, 0);
glayout->addWidget(dateTimeEdit, row, 1, 1, -1);
row++;
glayout->addWidget(manualLengthLabel, row, 0);
glayout->addLayout(manualLengthLayout,row,1,1,-1);
row++;
glayout->addWidget(DistanceLabel,row,0);
glayout->addWidget(distanceentry,row,1,1,-1);
row++;
glayout->addWidget(HRLabel,row,0);
glayout->addWidget(HRentry,row,1,1,-1);
row++;
if (timeBS || distanceBS) {
glayout->addWidget(BSEstLabel,row,0);
if (estBSbyTimeButton)
glayout->addWidget(estBSbyTimeButton,row,1,1,-1);
if (estBSbyDistButton)
glayout->addWidget(estBSbyDistButton,row,2,1,-1);
row++;
}
glayout->addWidget(ManualBSLabel,row,0);
glayout->addWidget(BSentry,row,1,1,-1);
row++;
glayout->addWidget(ManualDPLabel,row,0);
glayout->addWidget(DPentry,row,1,1,-1);
row++;
glayout->addWidget(enterButton,row,1);
glayout->addWidget(cancelButton,row,2);
this->resize(QSize(400,275));
connect(enterButton, SIGNAL(clicked()), this, SLOT(enterClicked()));
connect(cancelButton, SIGNAL(clicked()), this, SLOT(cancelClicked()));
connect(hrsentry, SIGNAL(editingFinished()), this, SLOT(setBsEst()));
//connect(secsentry, SIGNAL(editingFinished()), this, SLOT(setBsEst()));
connect(minsentry, SIGNAL(editingFinished()), this, SLOT(setBsEst()));
connect(distanceentry, SIGNAL(editingFinished()), this, SLOT(setBsEst()));
if (estBSbyTimeButton)
connect(estBSbyTimeButton,SIGNAL(clicked()),this, SLOT(bsEstChanged()));
if (estBSbyDistButton)
connect(estBSbyDistButton,SIGNAL(clicked()),this, SLOT(bsEstChanged()));
}
void
ManualRideDialog::estBSFromDistance()
{
// calculate distance-based BS estimate
if (distanceBS) {
double dist = distanceentry->text().toFloat();
// cast to int so QLineEdit doesn't interpret "51.3" as "513"
BSentry->clear();
BSentry->insert(QString("%1").arg((int) (dist * distanceBS)));
DPentry->clear();
DPentry->insert(QString("%1").arg((int) (dist * distanceDP)));
}
}
void
ManualRideDialog::estBSFromTime()
{
// calculate time-based BS estimate
if (timeBS) {
double hrs = hrsentry->text().toInt()
+ minsentry->text().toInt() / 60.0
+ secsentry->text().toInt() / 3600.0;
BSentry->clear();
BSentry->insert(QString("%1").arg((int)(hrs * timeBS)));
DPentry->clear();
DPentry->insert(QString("%1").arg((int)(hrs * timeDP)));
}
}
void
ManualRideDialog::bsEstChanged()
{
if (estBSbyDistButton) {
if (estBSbyDistButton->isChecked())
estBSFromDistance();
else
estBSFromTime();
}
}
void
ManualRideDialog::setBsEst()
{
if (estBSbyDistButton) {
if (estBSbyDistButton->isChecked())
estBSFromDistance();
else
estBSFromTime();
}
}
void
ManualRideDialog::cancelClicked()
{
reject();
}
void
ManualRideDialog::enterClicked()
{
// write data to manual entry file
if (filename == "") {
char tmp[32];
// use user's time for file:
QDateTime lt = dateTimeEdit->dateTime().toLocalTime();
sprintf(tmp, "%04d_%02d_%02d_%02d_%02d_%02d.man",
lt.date().year(), lt.date().month(),
lt.date().day(), lt.time().hour(),
lt.time().minute(),
lt.time().second());
filename = tmp;
filepath = home.absolutePath() + "/" + filename;
FILE *out = fopen(filepath.toAscii().constData(), "r");
if (out) {
fclose(out);
if (QMessageBox::warning(
this,
tr("Ride Already Downloaded"),
tr("This ride appears to have already ")
+ tr("been downloaded. Do you want to ")
+ tr("download it again and overwrite ")
+ tr("the previous download?"),
tr("&Overwrite"), tr("&Cancel"),
QString(), 1, 1) == 1) {
reject();
return ;
}
}
}
QString tmpname;
{
// QTemporaryFile doesn't actually close the file on .close(); it
// closes the file when in its destructor. On Windows, we can't
// rename an open file. So let tmp go out of scope before calling
// rename.
QString tmpl = home.absoluteFilePath(".ptdl.XXXXXX");
QTemporaryFile tmp(tmpl);
tmp.setAutoRemove(false);
if (!tmp.open()) {
QMessageBox::critical(this, tr("Error"),
tr("Failed to create temporary file ")
+ tmpl + ": " + tmp.error());
reject();
return;
}
QTextStream out(&tmp);
tmpname = tmp.fileName(); // after close(), tmp.fileName() is ""
/*
* File format:
* "manual"
* "minutes,mph,watts,miles,hr,bikescore" # header (metric or imperial)
* minutes,mph,watts,miles,hr,bikeScore # data
*/
out << "manual\n";
if (useMetricUnits)
out << "minutes,kmh,watts,km,hr,bikescore,daniels_points\n";
else
out << "minutes,mph,watts,miles,hr,bikescore,daniels_points\n";
// data
double secs = (hrsentry->text().toInt() * 3600) +
(minsentry->text().toInt() * 60) +
(secsentry->text().toInt());
out << secs/60.0;
out << ",";
out << distanceentry->text().toFloat() / (secs / 3600.0);
out << ",";
out << 0.0; // watts
out << ",";
out << distanceentry->text().toFloat();
out << ",";
out << HRentry->text().toInt();
out << ",";
out << BSentry->text().toInt();
out << ",";
out << DPentry->text().toInt();
out << "\n";
tmp.close();
// QTemporaryFile initially has permissions set to 0600.
// Make it readable by everyone.
tmp.setPermissions(tmp.permissions()
| QFile::ReadOwner | QFile::ReadUser
| QFile::ReadGroup | QFile::ReadOther);
}
#ifdef __WIN32__
// Windows ::rename won't overwrite an existing file.
if (QFile::exists(filepath)) {
QFile old(filepath);
if (!old.remove()) {
QMessageBox::critical(this, tr("Error"),
tr("Failed to remove existing file ")
+ filepath + ": " + old.error());
QFile::remove(tmpname);
reject();
}
}
#endif
// Use ::rename() instead of QFile::rename() to get forced overwrite.
if (rename(QFile::encodeName(tmpname), QFile::encodeName(filepath)) < 0) {
QMessageBox::critical(this, tr("Error"),
tr("Failed to rename ") + tmpname + tr(" to ")
+ filepath + ": " + strerror(errno));
QFile::remove(tmpname);
reject();
return;
}
mainWindow->addRide(filename);
accept();
}