mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-04-15 05:32:21 +00:00
unified ride import wizard
All the file import options are now supported by one dialog. As an extra special bonus, GC now supports dragging and dropping files to import them.
This commit is contained in:
committed by
Sean Rhea
parent
b3c31b8c64
commit
127f136249
@@ -28,6 +28,7 @@
|
||||
#include "PowerHist.h"
|
||||
#include "RideItem.h"
|
||||
#include "RideFile.h"
|
||||
#include "RideImportWizard.h"
|
||||
#include "QuarqRideFile.h"
|
||||
#include "RideMetric.h"
|
||||
#include "Settings.h"
|
||||
@@ -95,6 +96,7 @@ MainWindow::MainWindow(const QDir &home) :
|
||||
settings->setValue(GC_SETTINGS_LAST, home.dirName());
|
||||
|
||||
setWindowIcon(QIcon(":images/gc.png"));
|
||||
setAcceptDrops(true);
|
||||
|
||||
QFile zonesFile(home.absolutePath() + "/power.zones");
|
||||
if (zonesFile.exists()) {
|
||||
@@ -579,19 +581,8 @@ MainWindow::MainWindow(const QDir &home) :
|
||||
SLOT(exportCSV()), tr("Ctrl+E"));
|
||||
rideMenu->addAction(tr("&Export to XML..."), this,
|
||||
SLOT(exportXML()));
|
||||
rideMenu->addAction(tr("&Import from SRM..."), this,
|
||||
SLOT(importSRM()), tr("Ctrl+I"));
|
||||
rideMenu->addAction(tr("&Import from CSV..."), this,
|
||||
SLOT (importCSV()), tr ("Ctrl+S"));
|
||||
rideMenu->addAction(tr("&Import from TCX..."), this,
|
||||
SLOT (importTCX()));
|
||||
rideMenu->addAction(tr("&Import from Polar..."), this,
|
||||
SLOT (importPolar()));
|
||||
rideMenu->addAction(tr("&Import from WKO..."), this,
|
||||
SLOT (importWKO()));
|
||||
if (quarqInterpreterInstalled())
|
||||
rideMenu->addAction(tr("&Import from Quarq ANT+ log..."), this,
|
||||
SLOT (importQuarq()));
|
||||
rideMenu->addAction(tr("&Import from File..."), this,
|
||||
SLOT (importFile()), tr ("Ctrl+I"));
|
||||
rideMenu->addAction(tr("Find &best intervals..."), this,
|
||||
SLOT(findBestIntervals()), tr ("Ctrl+B"));
|
||||
rideMenu->addAction(tr("Split &ride..."), this,
|
||||
@@ -629,6 +620,24 @@ MainWindow::MainWindow(const QDir &home) :
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MainWindow::dragEnterEvent(QDragEnterEvent *event)
|
||||
{
|
||||
event->acceptProposedAction(); // whatever you wanna drop we will try and process!
|
||||
}
|
||||
|
||||
void
|
||||
MainWindow::dropEvent(QDropEvent *event)
|
||||
{
|
||||
QList<QUrl> urls = event->mimeData()->urls();
|
||||
if (urls.isEmpty()) return;
|
||||
|
||||
// We have something to process then
|
||||
RideImportWizard *dialog = new RideImportWizard (&urls, home, this);
|
||||
dialog->process(); // do it!
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
MainWindow::addRide(QString name, bool bSelect /*=true*/)
|
||||
{
|
||||
@@ -839,346 +848,31 @@ MainWindow::exportCSV()
|
||||
ride->ride->writeAsCsv(file, useMetricUnits);
|
||||
}
|
||||
|
||||
void MainWindow::importCSV()
|
||||
{
|
||||
// Prompt the user for the ride date
|
||||
boost::scoped_ptr<DatePickerDialog> dpd(new DatePickerDialog(this));
|
||||
|
||||
dpd->exec();
|
||||
|
||||
if(dpd->canceled == true)
|
||||
return;
|
||||
|
||||
QFile file ( dpd->fileName );
|
||||
QStringList errors;
|
||||
boost::scoped_ptr<RideFile> ride(
|
||||
RideFileFactory::instance().openRideFile(file, errors));
|
||||
|
||||
if (!ride || !errors.empty())
|
||||
{
|
||||
QString all =
|
||||
( ride
|
||||
? tr ( "Non-fatal problem(s) opening %1:" )
|
||||
: tr ( "Fatal problem(s) opening %1:" ) ).arg ( dpd->fileName );
|
||||
QStringListIterator i ( errors );
|
||||
while ( i.hasNext() )
|
||||
all += "\n" + i.next();
|
||||
if (ride)
|
||||
QMessageBox::warning ( this, tr ( "Open Warning" ), all );
|
||||
else {
|
||||
QMessageBox::critical ( this, tr ( "Open Error" ), all );
|
||||
return;
|
||||
}
|
||||
}
|
||||
ride->setStartTime(dpd->date);
|
||||
|
||||
QChar zero = QLatin1Char ( '0' );
|
||||
|
||||
QString name = QString ( "%1_%2_%3_%4_%5_%6.csv" )
|
||||
.arg ( ride->startTime().date().year(), 4, 10, zero )
|
||||
.arg ( ride->startTime().date().month(), 2, 10, zero )
|
||||
.arg ( ride->startTime().date().day(), 2, 10, zero )
|
||||
.arg ( ride->startTime().time().hour(), 2, 10, zero )
|
||||
.arg ( ride->startTime().time().minute(), 2, 10, zero )
|
||||
.arg ( ride->startTime().time().second(), 2, 10, zero );
|
||||
|
||||
if ( !file.copy ( home.absolutePath() + "/" + name ) )
|
||||
{
|
||||
QMessageBox::critical ( this, tr ( "Copy Error" ),
|
||||
tr ( "Couldn't copy %1" )
|
||||
.arg ( dpd->fileName ) );
|
||||
return;
|
||||
}
|
||||
|
||||
addRide ( name );
|
||||
}
|
||||
|
||||
void
|
||||
MainWindow::importSRM()
|
||||
MainWindow::importFile()
|
||||
{
|
||||
QVariant lastDirVar = settings->value(GC_SETTINGS_LAST_IMPORT_PATH);
|
||||
QString lastDir = (lastDirVar != QVariant())
|
||||
? lastDirVar.toString() : QDir::homePath();
|
||||
QStringList fileNames = QFileDialog::getOpenFileNames(
|
||||
this, tr("Import SRM"), lastDir,
|
||||
tr("SRM Binary Format (*.srm)"));
|
||||
|
||||
QStringList fileNames;
|
||||
if (quarqInterpreterInstalled()) {
|
||||
fileNames = QFileDialog::getOpenFileNames(
|
||||
this, tr("Import from File"), lastDir,
|
||||
tr("Raw Powertap Files (*.raw);;Comma Separated Variable (*.csv);;SRM training files (*.srm);;Garmin Training Centre (*.tcx);;Polar Precision (*.hrm);;WKO+ Files (*.wko);;Quarq ANT+ (*.qla);;All files (*.*)"));
|
||||
} else {
|
||||
fileNames = QFileDialog::getOpenFileNames(
|
||||
this, tr("Import from File"), lastDir,
|
||||
tr("Raw Powertap Files (*.raw);;Comma Separated Variable (*.csv);;SRM training files (*.srm);;Garmin Training Centre (*.tcx);;Polar Precision (*.hrm);;WKO+ Files (*.wko);;All files (*.*)"));
|
||||
|
||||
}
|
||||
|
||||
if (!fileNames.isEmpty()) {
|
||||
lastDir = QFileInfo(fileNames.front()).absolutePath();
|
||||
settings->setValue(GC_SETTINGS_LAST_IMPORT_PATH, lastDir);
|
||||
}
|
||||
QStringList fileNamesCopy = fileNames; // QT doc says iterate over a copy
|
||||
QStringListIterator i(fileNamesCopy);
|
||||
while (i.hasNext()) {
|
||||
QString fileName = i.next();
|
||||
QFile file(fileName);
|
||||
QStringList errors;
|
||||
|
||||
boost::scoped_ptr<RideFile> ride(
|
||||
RideFileFactory::instance().openRideFile(file, errors));
|
||||
|
||||
if (!ride || !errors.empty()) {
|
||||
QString all = (ride
|
||||
? tr("Non-fatal problem(s) opening %1:")
|
||||
: tr("Fatal problem(s) opening %1:")).arg(fileName);
|
||||
QStringListIterator i(errors);
|
||||
while (i.hasNext())
|
||||
all += "\n" + i.next();
|
||||
if (ride)
|
||||
QMessageBox::warning(this, tr("Open Warning"), all);
|
||||
else {
|
||||
QMessageBox::critical(this, tr("Open Error"), all);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
QChar zero = QLatin1Char('0');
|
||||
QString name = QString("%1_%2_%3_%4_%5_%6.srm")
|
||||
.arg(ride->startTime().date().year(), 4, 10, zero)
|
||||
.arg(ride->startTime().date().month(), 2, 10, zero)
|
||||
.arg(ride->startTime().date().day(), 2, 10, zero)
|
||||
.arg(ride->startTime().time().hour(), 2, 10, zero)
|
||||
.arg(ride->startTime().time().minute(), 2, 10, zero)
|
||||
.arg(ride->startTime().time().second(), 2, 10, zero);
|
||||
|
||||
if (!file.copy(home.absolutePath() + "/" + name)) {
|
||||
QMessageBox::critical(this, tr("Copy Error"),
|
||||
tr("Couldn't copy %1").arg(fileName));
|
||||
return;
|
||||
}
|
||||
|
||||
addRide(name);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MainWindow::importTCX()
|
||||
{
|
||||
QVariant lastDirVar = settings->value(GC_SETTINGS_LAST_IMPORT_PATH);
|
||||
QString lastDir = (lastDirVar != QVariant())
|
||||
? lastDirVar.toString() : QDir::homePath();
|
||||
QStringList fileNames = QFileDialog::getOpenFileNames(
|
||||
this, tr("Import TCX"), lastDir,
|
||||
tr("TCX Format (*.tcx)"));
|
||||
if (!fileNames.isEmpty()) {
|
||||
lastDir = QFileInfo(fileNames.front()).absolutePath();
|
||||
settings->setValue(GC_SETTINGS_LAST_IMPORT_PATH, lastDir);
|
||||
}
|
||||
QStringList fileNamesCopy = fileNames; // QT doc says iterate over a copy
|
||||
QStringListIterator i(fileNames);
|
||||
while (i.hasNext()) {
|
||||
QString fileName = i.next();
|
||||
QFile file(fileName);
|
||||
QStringList errors;
|
||||
|
||||
boost::scoped_ptr<RideFile> ride(
|
||||
RideFileFactory::instance().openRideFile(file, errors));
|
||||
|
||||
if (!ride || !errors.empty()) {
|
||||
QString all = (ride
|
||||
? tr("Non-fatal problem(s) opening %1:")
|
||||
: tr("Fatal problem(s) opening %1:")).arg(fileName);
|
||||
QStringListIterator i(errors);
|
||||
while (i.hasNext())
|
||||
all += "\n" + i.next();
|
||||
if (ride)
|
||||
QMessageBox::warning(this, tr("Open Warning"), all);
|
||||
else {
|
||||
QMessageBox::critical(this, tr("Open Error"), all);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
QChar zero = QLatin1Char('0');
|
||||
QString name = QString("%1_%2_%3_%4_%5_%6.tcx")
|
||||
.arg(ride->startTime().date().year(), 4, 10, zero)
|
||||
.arg(ride->startTime().date().month(), 2, 10, zero)
|
||||
.arg(ride->startTime().date().day(), 2, 10, zero)
|
||||
.arg(ride->startTime().time().hour(), 2, 10, zero)
|
||||
.arg(ride->startTime().time().minute(), 2, 10, zero)
|
||||
.arg(ride->startTime().time().second(), 2, 10, zero);
|
||||
|
||||
if (!file.copy(home.absolutePath() + "/" + name)) {
|
||||
QMessageBox::critical(this, tr("Copy Error"),
|
||||
tr("Couldn't copy %1").arg(fileName));
|
||||
return;
|
||||
}
|
||||
|
||||
addRide(name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
MainWindow::importWKO()
|
||||
{
|
||||
QVariant lastDirVar = settings->value(GC_SETTINGS_LAST_IMPORT_PATH);
|
||||
QString lastDir = (lastDirVar != QVariant())
|
||||
? lastDirVar.toString() : QDir::homePath();
|
||||
QStringList fileNames = QFileDialog::getOpenFileNames(
|
||||
this, tr("Import WKO"), lastDir,
|
||||
tr("WKO Format (*.wko)"));
|
||||
if (!fileNames.isEmpty()) {
|
||||
lastDir = QFileInfo(fileNames.front()).absolutePath();
|
||||
settings->setValue(GC_SETTINGS_LAST_IMPORT_PATH, lastDir);
|
||||
}
|
||||
QStringList fileNamesCopy = fileNames; // QT doc says iterate over a copy
|
||||
QStringListIterator i(fileNames);
|
||||
while (i.hasNext()) {
|
||||
QString fileName = i.next();
|
||||
QFile file(fileName);
|
||||
QStringList errors;
|
||||
|
||||
boost::scoped_ptr<RideFile> ride(
|
||||
RideFileFactory::instance().openRideFile(file, errors));
|
||||
|
||||
if (!ride || !errors.empty()) {
|
||||
QString all = (ride
|
||||
? tr("Non-fatal problem(s) opening %1:")
|
||||
: tr("Fatal problem(s) opening %1:")).arg(fileName);
|
||||
QStringListIterator i(errors);
|
||||
while (i.hasNext())
|
||||
all += "\n" + i.next();
|
||||
if (ride)
|
||||
QMessageBox::warning(this, tr("Open Warning"), all);
|
||||
else {
|
||||
QMessageBox::critical(this, tr("Open Error"), all);
|
||||
}
|
||||
|
||||
} else if (ride) {
|
||||
|
||||
QChar zero = QLatin1Char('0');
|
||||
QString name = QString("%1_%2_%3_%4_%5_%6.wko")
|
||||
.arg(ride->startTime().date().year(), 4, 10, zero)
|
||||
.arg(ride->startTime().date().month(), 2, 10, zero)
|
||||
.arg(ride->startTime().date().day(), 2, 10, zero)
|
||||
.arg(ride->startTime().time().hour(), 2, 10, zero)
|
||||
.arg(ride->startTime().time().minute(), 2, 10, zero)
|
||||
.arg(ride->startTime().time().second(), 2, 10, zero);
|
||||
|
||||
if (!file.copy(home.absolutePath() + "/" + name)) {
|
||||
QMessageBox::critical(this, tr("Copy Error"),
|
||||
tr("Couldn't copy %1").arg(fileName));
|
||||
|
||||
} else {
|
||||
|
||||
addRide(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MainWindow::importPolar()
|
||||
{
|
||||
QVariant lastDirVar = settings->value(GC_SETTINGS_LAST_IMPORT_PATH);
|
||||
QString lastDir = (lastDirVar != QVariant())
|
||||
? lastDirVar.toString() : QDir::homePath();
|
||||
QStringList fileNames = QFileDialog::getOpenFileNames(
|
||||
this, tr("Import Polar"), lastDir,
|
||||
tr("Polar Format (*.hrm)"));
|
||||
if (!fileNames.isEmpty()) {
|
||||
lastDir = QFileInfo(fileNames.front()).absolutePath();
|
||||
settings->setValue(GC_SETTINGS_LAST_IMPORT_PATH, lastDir);
|
||||
}
|
||||
QStringList fileNamesCopy = fileNames;
|
||||
QStringListIterator i(fileNames);
|
||||
while (i.hasNext()) {
|
||||
QString fileName = i.next();
|
||||
QFile file(fileName);
|
||||
QStringList errors;
|
||||
|
||||
boost::scoped_ptr<RideFile> ride(
|
||||
RideFileFactory::instance().openRideFile(file, errors));
|
||||
|
||||
if (!ride || !errors.empty()) {
|
||||
QString all = (ride
|
||||
? tr("Non-fatal problem(s) opening %1:")
|
||||
: tr("Fatal problem(s) opening %1:")).arg(fileName);
|
||||
QStringListIterator i(errors);
|
||||
while (i.hasNext())
|
||||
all += "\n" + i.next();
|
||||
if (ride)
|
||||
QMessageBox::warning(this, tr("Open Warning"), all);
|
||||
else {
|
||||
QMessageBox::critical(this, tr("Open Error"), all);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
QChar zero = QLatin1Char('0');
|
||||
QString name = QString("%1_%2_%3_%4_%5_%6.hrm")
|
||||
.arg(ride->startTime().date().year(), 4, 10, zero)
|
||||
.arg(ride->startTime().date().month(), 2, 10, zero)
|
||||
.arg(ride->startTime().date().day(), 2, 10, zero)
|
||||
.arg(ride->startTime().time().hour(), 2, 10, zero)
|
||||
.arg(ride->startTime().time().minute(), 2, 10, zero)
|
||||
.arg(ride->startTime().time().second(), 2, 10, zero);
|
||||
|
||||
if (!file.copy(home.absolutePath() + "/" + name)) {
|
||||
QMessageBox::critical(this, tr("Copy Error"),
|
||||
tr("Couldn't copy %1").arg(fileName));
|
||||
return;
|
||||
}
|
||||
|
||||
addRide(name);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MainWindow::importQuarq()
|
||||
{
|
||||
QVariant lastDirVar = settings->value(GC_SETTINGS_LAST_IMPORT_PATH);
|
||||
QString lastDir = (lastDirVar != QVariant())
|
||||
? lastDirVar.toString() : QDir::homePath();
|
||||
QStringList fileNames = QFileDialog::getOpenFileNames(
|
||||
this, tr("Import Ant"), lastDir,
|
||||
tr("Quarq ANT+ Format (*.qla)"));
|
||||
if (!fileNames.isEmpty()) {
|
||||
lastDir = QFileInfo(fileNames.front()).absolutePath();
|
||||
settings->setValue(GC_SETTINGS_LAST_IMPORT_PATH, lastDir);
|
||||
}
|
||||
QStringList fileNamesCopy = fileNames;
|
||||
QStringListIterator i(fileNames);
|
||||
while (i.hasNext()) {
|
||||
QString fileName = i.next();
|
||||
QFile file(fileName);
|
||||
QStringList errors;
|
||||
|
||||
boost::scoped_ptr<RideFile> ride(
|
||||
RideFileFactory::instance().openRideFile(file, errors));
|
||||
|
||||
if (!ride || !errors.empty()) {
|
||||
QString all = (ride
|
||||
? tr("Non-fatal problem(s) opening %1:")
|
||||
: tr("Fatal problem(s) opening %1:")).arg(fileName);
|
||||
QStringListIterator i(errors);
|
||||
while (i.hasNext())
|
||||
all += "\n" + i.next();
|
||||
if (ride)
|
||||
QMessageBox::warning(this, tr("Open Warning"), all);
|
||||
else {
|
||||
QMessageBox::critical(this, tr("Open Error"), all);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
QChar zero = QLatin1Char('0');
|
||||
QString name = QString("%1_%2_%3_%4_%5_%6.qla")
|
||||
.arg(ride->startTime().date().year(), 4, 10, zero)
|
||||
.arg(ride->startTime().date().month(), 2, 10, zero)
|
||||
.arg(ride->startTime().date().day(), 2, 10, zero)
|
||||
.arg(ride->startTime().time().hour(), 2, 10, zero)
|
||||
.arg(ride->startTime().time().minute(), 2, 10, zero)
|
||||
.arg(ride->startTime().time().second(), 2, 10, zero);
|
||||
|
||||
if (!file.copy(home.absolutePath() + "/" + name)) {
|
||||
QMessageBox::critical(this, tr("Copy Error"),
|
||||
tr("Couldn't copy %1").arg(fileName+" to "+home.absolutePath()+"/"+name));
|
||||
return;
|
||||
}
|
||||
|
||||
addRide(name);
|
||||
QStringList fileNamesCopy = fileNames; // QT doc says iterate over a copy
|
||||
RideImportWizard *import = new RideImportWizard(fileNamesCopy, home, this);
|
||||
import->process();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -53,6 +53,8 @@ class MainWindow : public QMainWindow
|
||||
virtual void resizeEvent(QResizeEvent*);
|
||||
virtual void moveEvent(QMoveEvent*);
|
||||
virtual void closeEvent(QCloseEvent*);
|
||||
virtual void dragEnterEvent(QDragEnterEvent *);
|
||||
virtual void dropEvent(QDropEvent *);
|
||||
|
||||
private slots:
|
||||
void rideSelected();
|
||||
@@ -64,12 +66,7 @@ class MainWindow : public QMainWindow
|
||||
void manualRide();
|
||||
void exportCSV();
|
||||
void exportXML();
|
||||
void importCSV();
|
||||
void importSRM();
|
||||
void importTCX();
|
||||
void importWKO();
|
||||
void importPolar();
|
||||
void importQuarq();
|
||||
void importFile();
|
||||
void findBestIntervals();
|
||||
void splitRide();
|
||||
void deleteRide();
|
||||
|
||||
865
src/RideImportWizard.cpp
Normal file
865
src/RideImportWizard.cpp
Normal file
@@ -0,0 +1,865 @@
|
||||
/*
|
||||
* Copyright (c) 2006 Sean C. Rhea (srhea@srhea.net)
|
||||
*
|
||||
* 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 <assert.h>
|
||||
#include <QDebug>
|
||||
#include "RideItem.h"
|
||||
#include "RideFile.h"
|
||||
#include "RideImportWizard.h"
|
||||
#include "MainWindow.h"
|
||||
#include "QuarqRideFile.h"
|
||||
#include <QWaitCondition>
|
||||
|
||||
|
||||
// drag and drop passes urls ... convert to a list of files and call main constructor
|
||||
RideImportWizard::RideImportWizard(QList<QUrl> *urls, QDir &home, MainWindow *main, QWidget *parent) : QDialog(parent)
|
||||
{
|
||||
QList<QString> filenames;
|
||||
for (int i=0; i<urls->count(); i++)
|
||||
filenames.append(QFileInfo(urls->value(i).toLocalFile()).absoluteFilePath());
|
||||
init(filenames, home, main);
|
||||
filenames.clear();
|
||||
}
|
||||
|
||||
RideImportWizard::RideImportWizard(QList<QString> files, QDir &home, MainWindow *main, QWidget *parent) : QDialog(parent)
|
||||
{
|
||||
init(files, home, main);
|
||||
}
|
||||
|
||||
void
|
||||
RideImportWizard::init(QList<QString> files, QDir &home, MainWindow *main)
|
||||
{
|
||||
|
||||
// initialise dialog box
|
||||
tableWidget = new QTableWidget(files.count(), 6, this);
|
||||
tableWidget->setItemDelegate(new RideDelegate(1)); // use a delegate for column 1 date
|
||||
tableWidget->verticalHeader()->setDefaultSectionSize(20);
|
||||
phaseLabel = new QLabel;
|
||||
progressBar = new QProgressBar();
|
||||
todayButton = new QComboBox();
|
||||
todayButton->addItem(tr("Select Date..."));
|
||||
todayButton->addItem(tr("Today"));
|
||||
todayButton->addItem(tr("Last Monday"));
|
||||
todayButton->addItem(tr("Last Tuesday"));
|
||||
todayButton->addItem(tr("Last Wednesday"));
|
||||
todayButton->addItem(tr("Last Thursday"));
|
||||
todayButton->addItem(tr("Last Friday"));
|
||||
todayButton->addItem(tr("Last Saturday"));
|
||||
todayButton->addItem(tr("Last Sunday"));
|
||||
todayButton->addItem(tr("Choose Date"));
|
||||
cancelButton = new QPushButton(tr("Cancel"));
|
||||
abortButton = new QPushButton(tr("Abort"));
|
||||
overFiles = new QCheckBox(tr("Overwrite Existing Files"));
|
||||
// initially the cancel, overwrite and today widgets are hidden
|
||||
// they only appear whilst we are asking the user for dates
|
||||
cancelButton->setHidden(true);
|
||||
todayButton->setHidden(true);
|
||||
overFiles->setHidden(true);
|
||||
overwriteFiles = false;
|
||||
|
||||
aborted = false;
|
||||
|
||||
// NOTE: abort button morphs into save and finish button later
|
||||
connect(abortButton, SIGNAL(clicked()), this, SLOT(abortClicked()));
|
||||
|
||||
// only used when editing dates
|
||||
connect(todayButton, SIGNAL(activated(int)), this, SLOT(todayClicked(int)));
|
||||
connect(cancelButton, SIGNAL(clicked()), this, SLOT(cancelClicked()));
|
||||
connect(overFiles, SIGNAL(clicked()), this, SLOT(overClicked()));
|
||||
|
||||
// title & headings
|
||||
setWindowTitle(tr("Import Ride Files"));
|
||||
QTableWidgetItem *filenameHeading = new QTableWidgetItem;
|
||||
filenameHeading->setText(tr("Filename"));
|
||||
tableWidget->setHorizontalHeaderItem(0, filenameHeading);
|
||||
|
||||
QTableWidgetItem *dateHeading = new QTableWidgetItem;
|
||||
dateHeading->setText(tr("Date"));
|
||||
tableWidget->setHorizontalHeaderItem(1, dateHeading);
|
||||
|
||||
QTableWidgetItem *timeHeading = new QTableWidgetItem;
|
||||
timeHeading->setText(tr("Time"));
|
||||
tableWidget->setHorizontalHeaderItem(2, timeHeading);
|
||||
|
||||
QTableWidgetItem *durationHeading = new QTableWidgetItem;
|
||||
durationHeading->setText(tr("Duration"));
|
||||
tableWidget->setHorizontalHeaderItem(3, durationHeading);
|
||||
|
||||
QTableWidgetItem *distanceHeading = new QTableWidgetItem;
|
||||
distanceHeading->setText(tr("Distance"));
|
||||
tableWidget->setHorizontalHeaderItem(4, distanceHeading);
|
||||
|
||||
QTableWidgetItem *statusHeading = new QTableWidgetItem;
|
||||
statusHeading->setText(tr("Import Status"));
|
||||
tableWidget->setHorizontalHeaderItem(5, statusHeading);
|
||||
|
||||
// save target dir
|
||||
this->home = home;
|
||||
mainwindow = main;
|
||||
|
||||
// Fill in the filenames and all the textItems
|
||||
for (int i=0; i < files.count(); i++) {
|
||||
QTableWidgetItem *t;
|
||||
|
||||
filenames.append(QFileInfo(files[i]).absoluteFilePath());
|
||||
blanks.append(true); // by default editable
|
||||
|
||||
// Filename
|
||||
t = new QTableWidgetItem();
|
||||
t->setText(QFileInfo(files[i]).fileName());
|
||||
t->setFlags(t->flags() & (~Qt::ItemIsEditable));
|
||||
tableWidget->setItem(i,0,t);
|
||||
|
||||
// Date
|
||||
t = new QTableWidgetItem();
|
||||
t->setText(tr(""));
|
||||
t->setFlags(t->flags() & (~Qt::ItemIsEditable));
|
||||
t->setBackgroundColor(Qt::red);
|
||||
tableWidget->setItem(i,1,t);
|
||||
|
||||
// Time
|
||||
t = new QTableWidgetItem();
|
||||
t->setText(tr(""));
|
||||
t->setFlags(t->flags() & (~Qt::ItemIsEditable));
|
||||
tableWidget->setItem(i,2,t);
|
||||
|
||||
// Duration
|
||||
t = new QTableWidgetItem();
|
||||
t->setText(tr(""));
|
||||
t->setFlags(t->flags() & (~Qt::ItemIsEditable));
|
||||
tableWidget->setItem(i,3,t);
|
||||
|
||||
// Distance
|
||||
t = new QTableWidgetItem();
|
||||
t->setText(tr(""));
|
||||
t->setFlags(t->flags() & (~Qt::ItemIsEditable));
|
||||
tableWidget->setItem(i,4,t);
|
||||
|
||||
// Import Status
|
||||
t = new QTableWidgetItem();
|
||||
t->setText(tr(""));
|
||||
t->setFlags(t->flags() & (~Qt::ItemIsEditable));
|
||||
tableWidget->setItem(i,5,t);
|
||||
}
|
||||
|
||||
// put into our dialog box
|
||||
// layout
|
||||
QHBoxLayout *buttons = new QHBoxLayout;
|
||||
buttons->addWidget(phaseLabel);
|
||||
buttons->addStretch();
|
||||
buttons->addWidget(todayButton);
|
||||
buttons->addStretch();
|
||||
buttons->addWidget(overFiles);
|
||||
buttons->addWidget(cancelButton);
|
||||
buttons->addWidget(abortButton);
|
||||
|
||||
QVBoxLayout *contents = new QVBoxLayout(this);
|
||||
contents->addWidget(tableWidget);
|
||||
contents->addWidget(progressBar);
|
||||
contents->addLayout(buttons);
|
||||
setLayout(contents);
|
||||
|
||||
// adjust all the sizes to look tidy
|
||||
tableWidget->setColumnWidth(0, 200); // filename
|
||||
tableWidget->setColumnWidth(1, 120); // date
|
||||
tableWidget->setColumnWidth(2, 120); // time
|
||||
tableWidget->setColumnWidth(3, 100); // duration
|
||||
tableWidget->setColumnWidth(4, 70); // distance
|
||||
tableWidget->setColumnWidth(5, 250); // status
|
||||
|
||||
// max height for 16 items and a scrollbar on right if > 16 items
|
||||
// for some reason the window is wider for 10-16 items too.
|
||||
// someone that understands width/geometry and layouts better
|
||||
// than me should clean up the logic here, no doubt using a
|
||||
// single call to geometry/hint. But for now it looks good
|
||||
// on Leopard, no doubt not so good on Windows
|
||||
resize(920 +
|
||||
((files.count() > 16 ? 24 : 0) +
|
||||
(files.count() > 9 && files.count() < 17) ? 8 : 0),
|
||||
118 + (files.count() > 16 ? 17*20 : (files.count()+1) * 20));
|
||||
|
||||
tableWidget->adjustSize();
|
||||
|
||||
// Refresh prior to running down the list & processing...
|
||||
this->show();
|
||||
}
|
||||
|
||||
int
|
||||
RideImportWizard::process()
|
||||
{
|
||||
|
||||
// set progress bar limits - for each file we
|
||||
// will make 5 passes over the files
|
||||
// 1. checking it is a file ane readable
|
||||
// 2. parsing it with the RideFileReader
|
||||
// 3. [optional] collect date/time information from user
|
||||
// 4. copy file into Library
|
||||
// 5. Process for CPI (not implemented yet)
|
||||
|
||||
// So, therefore the progress bar runs from 0 to files*4. (since step 5 is not implemented yet)
|
||||
progressBar->setMinimum(0);
|
||||
progressBar->setMaximum(filenames.count()*4);
|
||||
|
||||
// Pass one - Is it valid?
|
||||
phaseLabel->setText(tr("Step 1 of 4: Check file permissions"));
|
||||
for (int i=0; i < filenames.count(); i++) {
|
||||
|
||||
// get fullpath name for processing
|
||||
QFileInfo thisfile(filenames[i]);
|
||||
|
||||
if (thisfile.exists() && thisfile.isFile() && thisfile.isReadable()) {
|
||||
|
||||
// is it one we understand ?
|
||||
QRegExp suffixes;
|
||||
if (quarqInterpreterInstalled())
|
||||
suffixes.setPattern("^(raw|srm|csv|tcx|qla|hrm|wko)$");
|
||||
else
|
||||
suffixes.setPattern("^(raw|srm|csv|tcx|hrm|wko)$");
|
||||
|
||||
|
||||
if (suffixes.exactMatch(thisfile.suffix())) {
|
||||
|
||||
// Woot. We know how to parse this baby
|
||||
tableWidget->item(i,5)->setText(tr("Queued"));
|
||||
|
||||
} else {
|
||||
tableWidget->item(i,5)->setText(tr("Error - Unknown file type"));
|
||||
}
|
||||
|
||||
} else {
|
||||
// Cannot open
|
||||
tableWidget->item(i,5)->setText(tr("Error - Not a valid file"));
|
||||
}
|
||||
|
||||
progressBar->setValue(progressBar->value()+1);
|
||||
|
||||
}
|
||||
|
||||
QApplication::processEvents();
|
||||
if (aborted) { done(0); }
|
||||
repaint();
|
||||
|
||||
// Pass 2 - Read in with the relevant RideFileReader method
|
||||
|
||||
phaseLabel->setText(tr("Step 2 of 4: Validating Files"));
|
||||
for (int i=0; i< filenames.count(); i++) {
|
||||
|
||||
|
||||
// does the status say Queued?
|
||||
if (!tableWidget->item(i,5)->text().startsWith(tr("Error"))) {
|
||||
|
||||
QStringList errors;
|
||||
QFile thisfile(filenames[i]);
|
||||
|
||||
tableWidget->item(i,5)->setText(tr("Parsing..."));
|
||||
tableWidget->setCurrentCell(i,5);
|
||||
QApplication::processEvents();
|
||||
if (aborted) { done(0); }
|
||||
this->repaint();
|
||||
|
||||
boost::scoped_ptr<RideFile> ride(RideFileFactory::instance().openRideFile(thisfile, errors));
|
||||
|
||||
// did it parse ok?
|
||||
if (ride && errors.empty()) {
|
||||
|
||||
// ITS A KEEPER, LETS GET SOME METADATA ON DISPLAY
|
||||
tableWidget->item(i,5)->setText(tr("Validated"));
|
||||
|
||||
// Set Date and Time
|
||||
if (ride->startTime().isNull()) {
|
||||
|
||||
// Poo. The user needs to supply the date/time for this ride
|
||||
blanks[i] = true;
|
||||
tableWidget->item(i,1)->setText(tr(""));
|
||||
tableWidget->item(i,2)->setText(tr(""));
|
||||
|
||||
} else {
|
||||
|
||||
// Cool, the date and time was extrcted from the source file
|
||||
blanks[i] = false;
|
||||
tableWidget->item(i,1)->setText(ride->startTime().toString(tr("dd MMM yyyy")));
|
||||
tableWidget->item(i,2)->setText(ride->startTime().toString(tr("hh:mm:ss ap")));
|
||||
}
|
||||
|
||||
tableWidget->item(i,1)->setTextAlignment(Qt::AlignRight); // put in the middle
|
||||
tableWidget->item(i,2)->setTextAlignment(Qt::AlignRight); // put in the middle
|
||||
|
||||
// show duration by looking at last data point
|
||||
int secs = ride->dataPoints().last()->secs;
|
||||
QChar zero = QLatin1Char ( '0' );
|
||||
QString time = QString("%1:%2:%3").arg(secs/3600,2,10,zero)
|
||||
.arg(secs%3600/60,2,10,zero)
|
||||
.arg(secs%60,2,10,zero);
|
||||
tableWidget->item(i,3)->setText(time);
|
||||
tableWidget->item(i,3)->setTextAlignment(Qt::AlignHCenter); // put in the middle
|
||||
|
||||
// show distance by looking at last data point
|
||||
int km = ride->dataPoints().last()->km;
|
||||
QString dist = QString ("%1 km").arg(km, 1,10,zero);
|
||||
tableWidget->item(i,4)->setText(dist);
|
||||
tableWidget->item(i,4)->setTextAlignment(Qt::AlignRight); // put in the middle
|
||||
|
||||
} else {
|
||||
// nope - can't handle this file
|
||||
tableWidget->item(i,5)->setText(tr("Error - cannot parse"));
|
||||
}
|
||||
}
|
||||
progressBar->setValue(progressBar->value()+1);
|
||||
QApplication::processEvents();
|
||||
if (aborted) { done(0); }
|
||||
this->repaint();
|
||||
}
|
||||
|
||||
// Pass 3 - get missing date and times for imported files
|
||||
phaseLabel->setText(tr("Step 3 of 4: Confirm Date and Time"));
|
||||
|
||||
int needdates=0;
|
||||
bool first = true;
|
||||
for (int i=0; i<filenames.count(); i++) {
|
||||
|
||||
// ignore errors
|
||||
QTableWidgetItem *t = tableWidget->item(i,5);
|
||||
if (t->text().startsWith(tr("Error"))) continue;
|
||||
|
||||
// date needed?
|
||||
if (blanks[i]) {
|
||||
needdates++;
|
||||
t = tableWidget->item(i,1);
|
||||
t->setFlags(t->flags() | (Qt::ItemIsEditable)); // make editable ONLY if not present -
|
||||
// we cannot override the RideFileReader
|
||||
if (first) {
|
||||
tableWidget->editItem(t);
|
||||
first = false;
|
||||
}
|
||||
t = tableWidget->item(i,2);
|
||||
t->setFlags(t->flags() | (Qt::ItemIsEditable)); // make editable ONLY if not present -
|
||||
// we cannot override the RideFileReader
|
||||
}
|
||||
// does nothing for the moment
|
||||
progressBar->setValue(progressBar->value()+1);
|
||||
progressBar->repaint();
|
||||
}
|
||||
|
||||
// Wait for user to press save
|
||||
abortButton->setText(tr("Save"));
|
||||
aborted = false;
|
||||
|
||||
cancelButton->setHidden(false);
|
||||
todayButton->setHidden(false);
|
||||
overFiles->setHidden(false);
|
||||
|
||||
if (needdates == 0) {
|
||||
// no need to wait for the user to input dates
|
||||
// nd press save if all the dates are set
|
||||
// (i.e. they got set by the file importers already)
|
||||
// do nothing for now since we need to confirm dates
|
||||
// and confirm overwrite files rather than importing
|
||||
// without user intervention
|
||||
|
||||
abortButton->setDisabled(false);
|
||||
|
||||
// abortClicked();
|
||||
} else {
|
||||
|
||||
// de-activate Save button until the dates and times are sorted
|
||||
abortButton->setDisabled(true);
|
||||
}
|
||||
connect(tableWidget, SIGNAL(itemChanged(QTableWidgetItem *)), this, SLOT(activateSave()));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
RideImportWizard::overClicked()
|
||||
{
|
||||
overwriteFiles = overFiles->isChecked();
|
||||
}
|
||||
|
||||
void
|
||||
RideImportWizard::activateSave()
|
||||
{
|
||||
|
||||
for (int i=0; i<filenames.count(); i++) {
|
||||
|
||||
// ignore errors
|
||||
QTableWidgetItem *t = tableWidget->item(i,5);
|
||||
if (t->text().startsWith(tr("Error"))) continue;
|
||||
|
||||
// date needed?
|
||||
t = tableWidget->item(i,1);
|
||||
if (t->text() == "") return;
|
||||
|
||||
// time needed?
|
||||
t = tableWidget->item(i,2);
|
||||
if (t->text() == "") return;
|
||||
}
|
||||
// if you got here then all entries that need a date have a date
|
||||
abortButton->setDisabled(false);
|
||||
}
|
||||
|
||||
// the code here is tedious. checking for conditions about total ride time
|
||||
// and if it fits in a day, then distributing them over the day, but
|
||||
// only if the time is not set, and then if its being set to today
|
||||
// then start must be before the current time cause you can't log
|
||||
// data for future rides ... and so on.
|
||||
//
|
||||
// but then this feature is gonna save a lot of typing over time ...
|
||||
//
|
||||
void
|
||||
RideImportWizard::todayClicked(int index)
|
||||
{
|
||||
QDate selectedDate; // the date we're gonna apply to the row(s) highlighted
|
||||
|
||||
// 0 = nothing selected, 1 = Today - 2 = last monday thru to 8 = last sunday
|
||||
if (index == 0) { // no selection
|
||||
return;
|
||||
} else if (index == 1) { // today
|
||||
selectedDate = QDate().currentDate();
|
||||
} else if (index == 10) { // other date - set focus on first highlighted date
|
||||
for (int i=0; i<filenames.count(); i++) {
|
||||
if (tableWidget->item(i,0)->isSelected() ||
|
||||
tableWidget->item(i,1)->isSelected() ||
|
||||
tableWidget->item(i,2)->isSelected() ||
|
||||
tableWidget->item(i,3)->isSelected() ||
|
||||
tableWidget->item(i,4)->isSelected() ||
|
||||
tableWidget->item(i,5)->isSelected()) {
|
||||
if (blanks[i]) {
|
||||
tableWidget->editItem(tableWidget->item(i,1));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
} else { // last mon/tue/wed/thu/fri/sat/sun
|
||||
int daysago = (QDate().currentDate().dayOfWeek() - index + 8) % 7;
|
||||
if (!daysago) daysago=7; // e.g.last wednesday is 7 days ago if today is wednesday
|
||||
selectedDate = QDate().fromJulianDay(QDate().currentDate().toJulianDay()-daysago);
|
||||
}
|
||||
|
||||
// Only apply to selected date - set time to current time - ride duration
|
||||
// pretty daft but at least it sets it to something, anything is gonna be random
|
||||
int countselected = 0;
|
||||
int totalduration = 0;
|
||||
for (int i=0; i< filenames.count(); i++) {
|
||||
if (tableWidget->item(i,0)->isSelected() ||
|
||||
tableWidget->item(i,1)->isSelected() ||
|
||||
tableWidget->item(i,2)->isSelected() ||
|
||||
tableWidget->item(i,3)->isSelected() ||
|
||||
tableWidget->item(i,4)->isSelected() ||
|
||||
tableWidget->item(i,5)->isSelected()) {
|
||||
countselected++;
|
||||
|
||||
// look at rides with missing date - we need to populate those
|
||||
if (blanks[i]) {
|
||||
QTime duration = QTime().fromString(tableWidget->item(i,3)->text(), tr("hh:mm:ss"));
|
||||
totalduration += duration.hour() * 3600 +
|
||||
duration.minute() * 60 +
|
||||
duration.second();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// More than a days worth of rides so can't squeeze into a single day!
|
||||
if (totalduration > (24 * 3600)) {
|
||||
QMessageBox::warning ( this, tr ( "Invalid Selection" ), tr ( "More than 24hrs of rides to fit into a day" ));
|
||||
return;
|
||||
}
|
||||
|
||||
// if it is today then start from now - total rides duration
|
||||
// if that goes negative then just do the std thing
|
||||
int rstart=0;
|
||||
if (index == 1) {
|
||||
|
||||
QTime timenow = QTime().currentTime();
|
||||
|
||||
int now = timenow.hour() * 3600 +
|
||||
timenow.minute() * 60 +
|
||||
timenow.second();
|
||||
|
||||
rstart = now - totalduration;
|
||||
|
||||
}
|
||||
|
||||
if (rstart <= 0) { // i.e. still not set properly...
|
||||
// if it is not being set to "today" then spread them across the
|
||||
// day back to back with equal time before and after noon.
|
||||
rstart = ((24*3600) - totalduration) /2;
|
||||
}
|
||||
|
||||
// zip through the rows and where highlighted set the date
|
||||
// if the start time is not set set it to rstart and increment
|
||||
// by the duration of the ride.
|
||||
for (int i=0; i< filenames.count(); i++) {
|
||||
if (tableWidget->item(i,0)->isSelected() ||
|
||||
tableWidget->item(i,1)->isSelected() ||
|
||||
tableWidget->item(i,2)->isSelected() ||
|
||||
tableWidget->item(i,3)->isSelected() ||
|
||||
tableWidget->item(i,4)->isSelected() ||
|
||||
tableWidget->item(i,5)->isSelected()) {
|
||||
|
||||
// set the date to date selected -- only if was originally blank
|
||||
if (blanks[i])
|
||||
tableWidget->item(i,1)->setText(selectedDate.toString(tr("dd MMM yyyy")));
|
||||
|
||||
// look at rides with missing start time - we need to populate those
|
||||
if (blanks[i]) {
|
||||
|
||||
// ride duration
|
||||
QTime duration = QTime().fromString(tableWidget->item(i,3)->text(), tr("hh:mm:ss"));
|
||||
|
||||
// ride start time
|
||||
QTime time(rstart/3600, rstart%3600/60, rstart%60);
|
||||
tableWidget->item(i,2)->setText(time.toString(tr("hh:mm:ss a")));
|
||||
rstart += duration.hour() * 3600 +
|
||||
duration.minute() * 60 +
|
||||
duration.second();
|
||||
}
|
||||
}
|
||||
}
|
||||
// phew! - repaint!
|
||||
QApplication::processEvents();
|
||||
tableWidget->repaint();
|
||||
}
|
||||
|
||||
void
|
||||
RideImportWizard::cancelClicked()
|
||||
{
|
||||
done(0); // you are the weakest link, goodbye.
|
||||
}
|
||||
|
||||
// info structure used by cpi updater
|
||||
struct cpi_file_info {
|
||||
QString file, inname, outname;
|
||||
};
|
||||
|
||||
|
||||
void
|
||||
RideImportWizard::abortClicked()
|
||||
{
|
||||
|
||||
// if done when labelled abort we kill off this dialog
|
||||
QString label = abortButton->text();
|
||||
|
||||
if (label == "Abort") {
|
||||
aborted=true; // terminated. I'll be back.
|
||||
return;
|
||||
}
|
||||
|
||||
if (label == "Finish") {
|
||||
// phew. our work is done.
|
||||
done(0);
|
||||
return;
|
||||
}
|
||||
|
||||
int needdates=0;
|
||||
for (int i=0; i<filenames.count(); i++) {
|
||||
|
||||
QTableWidgetItem *t = tableWidget->item(i,5);
|
||||
if (t->text().startsWith(tr("Error"))) continue;
|
||||
// date needed?
|
||||
t = tableWidget->item(i,1);
|
||||
if (t->text() == "") {
|
||||
needdates++;
|
||||
t->setFlags(t->flags() | (Qt::ItemIsEditable));
|
||||
}
|
||||
|
||||
// time needed?
|
||||
t = tableWidget->item(i,2);
|
||||
if (t->text() == "") {
|
||||
needdates++;
|
||||
t->setFlags(t->flags() | (Qt::ItemIsEditable));
|
||||
}
|
||||
}
|
||||
|
||||
if (needdates) return; // no dice dude, we need those dates filled in!
|
||||
|
||||
// if done when labelled save we copy the files and run the cpi calculator
|
||||
phaseLabel->setText(tr("Step 4 of 4: Save to Library"));
|
||||
|
||||
abortButton->setText(tr("Abort"));
|
||||
aborted = false;
|
||||
cancelButton->setHidden(true);
|
||||
todayButton->setHidden(true);
|
||||
overFiles->setHidden(true);
|
||||
|
||||
// now set this fields uneditable again ... yeesh.
|
||||
for (int i=0; i <filenames.count(); i++) {
|
||||
QTableWidgetItem *t = tableWidget->item(i,1);
|
||||
t->setFlags(t->flags() & (~Qt::ItemIsEditable));
|
||||
t = tableWidget->item(i,2);
|
||||
t->setFlags(t->flags() & (~Qt::ItemIsEditable));
|
||||
}
|
||||
|
||||
QChar zero = QLatin1Char ( '0' );
|
||||
|
||||
for (int i=0; i< filenames.count(); i++) {
|
||||
|
||||
if (tableWidget->item(i,5)->text().startsWith(tr("Error"))) continue; // skip error
|
||||
|
||||
tableWidget->item(i,5)->setText(tr("Saving..."));
|
||||
tableWidget->setCurrentCell(i,5);
|
||||
QApplication::processEvents();
|
||||
if (aborted) { done(0); }
|
||||
this->repaint();
|
||||
|
||||
// Setup the ridetime as a QDateTime
|
||||
QDateTime ridedatetime = QDateTime(QDate().fromString(tableWidget->item(i,1)->text(), tr("dd MMM yyyy")),
|
||||
QTime().fromString(tableWidget->item(i,2)->text(), tr("hh:mm:ss a")));
|
||||
QString suffix = QFileInfo(filenames[i]).suffix();
|
||||
QString targetnosuffix = QString ( "%1_%2_%3_%4_%5_%6" )
|
||||
.arg ( ridedatetime.date().year(), 4, 10, zero )
|
||||
.arg ( ridedatetime.date().month(), 2, 10, zero )
|
||||
.arg ( ridedatetime.date().day(), 2, 10, zero )
|
||||
.arg ( ridedatetime.time().hour(), 2, 10, zero )
|
||||
.arg ( ridedatetime.time().minute(), 2, 10, zero )
|
||||
.arg ( ridedatetime.time().second(), 2, 10, zero );
|
||||
QString target = QString ("%1.%2" )
|
||||
.arg ( targetnosuffix )
|
||||
.arg ( suffix );
|
||||
QString fulltarget = home.absolutePath() + "/" + target;
|
||||
// so now we have sourcefile in 'filenames[i]' and target file name in 'target'
|
||||
if (!fulltarget.compare(filenames[i])) { // they are the same file! so skip copy
|
||||
tableWidget->item(i,5)->setText(tr("Error - Source is Target"));
|
||||
} else if (QFileInfo(fulltarget).exists()) {
|
||||
if (overwriteFiles) {
|
||||
tableWidget->item(i,5)->setText(tr("Overwriting file..."));
|
||||
QFile source(filenames[i]);
|
||||
QString fulltargettmp(home.absolutePath() + tr("/") + targetnosuffix + tr(".tmp"));
|
||||
|
||||
if (source.copy(fulltargettmp)) {
|
||||
// tmp version saved now zap original
|
||||
QFile original(fulltarget);
|
||||
original.remove(); // zap!
|
||||
// mv tmp to target
|
||||
QFile temp(fulltargettmp);
|
||||
if (temp.rename(fulltarget)) {
|
||||
tableWidget->item(i,5)->setText(tr("File Overwritten"));
|
||||
//no need to add since its already there!
|
||||
//mainwindow->addRide(QFileInfo(fulltarget).fileName(), true); // add to tree view
|
||||
} else
|
||||
tableWidget->item(i,5)->setText(tr("Error - overwrite failed"));
|
||||
} else {
|
||||
tableWidget->item(i,5)->setText(tr("Error - overwrite failed"));
|
||||
}
|
||||
} else {
|
||||
tableWidget->item(i,5)->setText(tr("Error - File exists"));
|
||||
}
|
||||
} else {
|
||||
tableWidget->item(i,5)->setText(tr("Saving file..."));
|
||||
QFile source(filenames[i]);
|
||||
if (source.copy(fulltarget)) {
|
||||
tableWidget->item(i,5)->setText(tr("File Saved"));
|
||||
mainwindow->addRide(QFileInfo(fulltarget).fileName(), true); // add to tree view
|
||||
} else
|
||||
tableWidget->item(i,5)->setText(tr("Error - copy failed"));
|
||||
}
|
||||
QApplication::processEvents();
|
||||
if (aborted) { done(0); }
|
||||
progressBar->setValue(progressBar->value()+1);
|
||||
this->repaint();
|
||||
}
|
||||
|
||||
#if 0 // NOT UNTIL CPINTPLOT.CPP IS REFACTORED TO SEPERATE CPI FILES MAINTENANCE FROM CP PLOT CODE
|
||||
// if done when labelled save we copy the files and run the cpi calculator
|
||||
phaseLabel->setText(tr("Step 5 of 5: Calculating Critical Powers"));
|
||||
|
||||
abortButton->setText(tr("Abort"));
|
||||
aborted = false;
|
||||
|
||||
for (int i=0; i< filenames.count(); i++) {
|
||||
|
||||
if (!tableWidget->item(i,5)->text().startsWith(tr("Error"))) {
|
||||
tableWidget->item(i,5)->setText(tr("Calculating..."));
|
||||
tableWidget->setCurrentCell(i,5);
|
||||
QApplication::processEvents();
|
||||
if (aborted) { done(0); }
|
||||
this->repaint();
|
||||
|
||||
// calculated
|
||||
|
||||
// change status
|
||||
tableWidget->item(i,5)->setText(tr("Completed."));
|
||||
}
|
||||
QApplication::processEvents();
|
||||
if (aborted) { done(0); }
|
||||
progressBar->setValue(progressBar->value()+1);
|
||||
this->repaint();
|
||||
}
|
||||
#endif // not until CPINTPLOT IS REFACTORED
|
||||
|
||||
|
||||
// how did we get on in the end then ...
|
||||
int completed = 0;
|
||||
for (int i=0; i< filenames.count(); i++)
|
||||
if (!tableWidget->item(i,5)->text().startsWith(tr("Error"))) {
|
||||
completed++;
|
||||
}
|
||||
|
||||
tableWidget->setSortingEnabled(true); // so you can browse through errors etc
|
||||
QString donemessage = QString("Import Complete. %1 of %2 successful.")
|
||||
.arg(completed, 1, 10, zero)
|
||||
.arg(filenames.count(), 1, 10, zero);
|
||||
progressBar->setValue(progressBar->maximum());
|
||||
phaseLabel->setText(donemessage);
|
||||
abortButton->setText(tr("Finish"));
|
||||
aborted = false;
|
||||
}
|
||||
|
||||
// destructor - not sure it ever gets called tho, even by RideImportWizard::done()
|
||||
RideImportWizard::~RideImportWizard()
|
||||
{
|
||||
// Fill in the filenames and all the textItems
|
||||
for (int i=0; i < filenames.count(); i++) {
|
||||
QTableWidgetItem *t;
|
||||
t = tableWidget->item(i,0); delete t;
|
||||
t = tableWidget->item(i,1); delete t;
|
||||
t = tableWidget->item(i,2); delete t;
|
||||
t = tableWidget->item(i,3); delete t;
|
||||
t = tableWidget->item(i,4); delete t;
|
||||
t = tableWidget->item(i,5); delete t;
|
||||
}
|
||||
delete tableWidget;
|
||||
filenames.clear();
|
||||
delete phaseLabel;
|
||||
delete progressBar;
|
||||
delete abortButton;
|
||||
delete cancelButton;
|
||||
delete todayButton;
|
||||
delete overFiles;
|
||||
}
|
||||
|
||||
|
||||
// Below is the code to implement a custom ItemDelegate to support
|
||||
// editing of the date and time of the Ride inside a QTableWidget
|
||||
// the ItemDelegate is registered for every cell in the table
|
||||
// and only kicks into life for the columns in which the date and
|
||||
// time are stored, and only when the DateTime for that row is 0.
|
||||
|
||||
// construct the delegate - save away the column containing the date
|
||||
// bearing in mind the time is in the next
|
||||
// column.
|
||||
RideDelegate::RideDelegate(int dateColumn, QObject *parent) : QItemDelegate(parent)
|
||||
{
|
||||
this->dateColumn = dateColumn;
|
||||
}
|
||||
|
||||
|
||||
// paint the cells for date and time - pass through for all the
|
||||
// other cells. Uses the model data to display.
|
||||
void RideDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
|
||||
const QModelIndex &index) const
|
||||
{
|
||||
if (index.column() == dateColumn) {
|
||||
|
||||
QString value = index.model()->data(index, Qt::DisplayRole).toString();
|
||||
// display with angles to show its doing something
|
||||
QString text = QString("%1").arg(value);
|
||||
|
||||
QStyleOptionViewItem myOption = option;
|
||||
myOption.displayAlignment = Qt::AlignRight | Qt::AlignVCenter;
|
||||
drawDisplay(painter, myOption, myOption.rect, text);
|
||||
drawFocus(painter, myOption, myOption.rect);
|
||||
|
||||
} else if (index.column() == dateColumn+1) {
|
||||
|
||||
QString value = index.model()->data(index, Qt::DisplayRole).toString();
|
||||
// display with angles to show its doing something
|
||||
QString text = QString("%1").arg(value);
|
||||
|
||||
QStyleOptionViewItem myOption = option;
|
||||
myOption.displayAlignment = Qt::AlignRight | Qt::AlignVCenter;
|
||||
drawDisplay(painter, myOption, myOption.rect, text);
|
||||
drawFocus(painter, myOption, myOption.rect);
|
||||
|
||||
} else {
|
||||
|
||||
QItemDelegate::paint(painter, option, index);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// setup editor for edit of field!!
|
||||
QWidget *RideDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
|
||||
{
|
||||
if (index.column() == dateColumn) {
|
||||
|
||||
// edit that date!
|
||||
QDateEdit *dateEdit = new QDateEdit(parent);
|
||||
dateEdit->setDisplayFormat("dd MMM yyyy");
|
||||
connect(dateEdit, SIGNAL(editingFinished()), this, SLOT(commitAndCloseDateEditor()));
|
||||
return dateEdit;
|
||||
} else if (index.column() == dateColumn+1) {
|
||||
|
||||
// edit that time
|
||||
QTimeEdit *timeEdit = new QTimeEdit(parent);
|
||||
timeEdit->setDisplayFormat("hh:mm:ss a");
|
||||
connect(timeEdit, SIGNAL(editingFinished()), this, SLOT(commitAndCloseTimeEditor()));
|
||||
return timeEdit;
|
||||
} else {
|
||||
return QItemDelegate::createEditor(parent, option, index);
|
||||
}
|
||||
}
|
||||
|
||||
// user hit tab or return so save away the data to our model
|
||||
void RideDelegate::commitAndCloseDateEditor()
|
||||
{
|
||||
QDateEdit *editor = qobject_cast<QDateEdit *>(sender());
|
||||
emit commitData(editor);
|
||||
emit closeEditor(editor);
|
||||
}
|
||||
|
||||
// user hit tab or return so save away the data to our model
|
||||
void RideDelegate::commitAndCloseTimeEditor()
|
||||
{
|
||||
QTimeEdit *editor = qobject_cast<QTimeEdit *>(sender());
|
||||
emit commitData(editor);
|
||||
emit closeEditor(editor);
|
||||
}
|
||||
|
||||
// We don't set anything because the data is saved within the view not the model!
|
||||
void RideDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
|
||||
{
|
||||
// stored as text field
|
||||
if (index.column() == dateColumn) {
|
||||
QDateEdit *dateEdit = qobject_cast<QDateEdit *>(editor);
|
||||
QDate date = QDate().fromString(index.model()->data(index, Qt::DisplayRole).toString(), "dd MMM yyyy");;
|
||||
dateEdit->setDate(date);
|
||||
} else if (index.column() == dateColumn+1) {
|
||||
QTimeEdit *timeEdit = qobject_cast<QTimeEdit *>(editor);
|
||||
QTime time = QTime().fromString(index.model()->data(index, Qt::DisplayRole).toString(), "hh:mm:ss a");;
|
||||
timeEdit->setTime(time);
|
||||
}
|
||||
}
|
||||
|
||||
// We don't set anything because the data is saved within the view not the model!
|
||||
void RideDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
|
||||
{
|
||||
|
||||
// stored as text field
|
||||
if (index.column() == dateColumn) {
|
||||
QDateEdit *dateEdit = qobject_cast<QDateEdit *>(editor);
|
||||
QString value = dateEdit->date().toString("dd MMM yyyy");
|
||||
// Place in the view
|
||||
model->setData(index, value, Qt::DisplayRole);
|
||||
} else if (index.column() == dateColumn+1) {
|
||||
QTimeEdit *timeEdit = qobject_cast<QTimeEdit *>(editor);
|
||||
QString value = timeEdit->time().toString("hh:mm:ss a");
|
||||
model->setData(index, value, Qt::DisplayRole);
|
||||
}
|
||||
}
|
||||
|
||||
100
src/RideImportWizard.h
Normal file
100
src/RideImportWizard.h
Normal file
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright (c) 2007 Sean C. Rhea (srhea@srhea.net)
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#ifndef _RideImportWizard_h
|
||||
#define _RideImportWizard_h
|
||||
|
||||
#include <QtGui>
|
||||
#include <QTableWidget>
|
||||
#include <QProgressBar>
|
||||
#include <QList>
|
||||
#include <QListIterator>
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
#include "MainWindow.h"
|
||||
|
||||
// Dialog class to show filenames, import progress and to capture user input
|
||||
// of ride date and time
|
||||
|
||||
class RideImportWizard : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
RideImportWizard(QList<QUrl> *urls, QDir &home, MainWindow *main, QWidget *parent = 0);
|
||||
RideImportWizard(QList<QString> files, QDir &home, MainWindow *main, QWidget *parent = 0);
|
||||
~RideImportWizard();
|
||||
int process();
|
||||
|
||||
signals:
|
||||
|
||||
private slots:
|
||||
void abortClicked();
|
||||
void cancelClicked();
|
||||
void todayClicked(int index);
|
||||
void overClicked();
|
||||
void activateSave();
|
||||
|
||||
private:
|
||||
void init(QList<QString> files, QDir &home, MainWindow *main);
|
||||
QList <QString> filenames; // list of filenames passed
|
||||
QList <bool> blanks; // record of which have a RideFileReader returned date & time
|
||||
QDir home; // target directory
|
||||
bool aborted;
|
||||
QLabel *phaseLabel;
|
||||
QTableWidget *tableWidget;
|
||||
QProgressBar *progressBar;
|
||||
QPushButton *abortButton; // also used for save and finish
|
||||
QPushButton *cancelButton; // cancel when asking for dates
|
||||
QComboBox *todayButton; // set date to today when asking for dates
|
||||
QCheckBox *overFiles; // chance to set overwrite when asking for dates
|
||||
bool overwriteFiles; // flag to overwrite files from checkbox
|
||||
MainWindow *mainwindow; // caller
|
||||
};
|
||||
|
||||
// Item Delegate for Editing Date and Time of Ride inside the
|
||||
// QTableWidget
|
||||
|
||||
class RideDelegate : public QItemDelegate
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
RideDelegate(int dateColumn, QObject *parent = 0);
|
||||
|
||||
// override inherited methods
|
||||
// repaint the cell
|
||||
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
|
||||
|
||||
// setup editor in the cell
|
||||
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const;
|
||||
// Fetch data from model ready for editing
|
||||
void setEditorData(QWidget *editor, const QModelIndex &index) const;
|
||||
|
||||
// Save data back to model after editing
|
||||
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const;
|
||||
|
||||
private slots:
|
||||
void commitAndCloseTimeEditor();
|
||||
void commitAndCloseDateEditor();
|
||||
|
||||
private:
|
||||
int dateColumn; // date is always followed by time
|
||||
};
|
||||
|
||||
#endif // _RideImportWizard_h
|
||||
|
||||
@@ -59,6 +59,7 @@ HEADERS += \
|
||||
RawRideFile.h \
|
||||
RideFile.h \
|
||||
RideItem.h \
|
||||
RideImportWizard.h \
|
||||
RideMetric.h \
|
||||
SrmRideFile.h \
|
||||
TcxParser.h \
|
||||
@@ -105,6 +106,7 @@ SOURCES += \
|
||||
RawRideFile.cpp \
|
||||
RideFile.cpp \
|
||||
RideItem.cpp \
|
||||
RideImportWizard.cpp \
|
||||
RideMetric.cpp \
|
||||
SrmRideFile.cpp \
|
||||
TcxParser.cpp \
|
||||
|
||||
Reference in New Issue
Block a user