diff --git a/src/BatchExportDialog.cpp b/src/BatchExportDialog.cpp new file mode 100644 index 000000000..f67aa3189 --- /dev/null +++ b/src/BatchExportDialog.cpp @@ -0,0 +1,250 @@ + +/* + * Copyright (c) 2011 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 "BatchExportDialog.h" + +BatchExportDialog::BatchExportDialog(MainWindow *main) : QDialog(main), main(main) +{ + setAttribute(Qt::WA_DeleteOnClose); + setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint); + setWindowTitle(tr("Activity Batch Export")); + + // make the dialog a resonable size + setMinimumWidth(550); + setMinimumHeight(400); + + QVBoxLayout *layout = new QVBoxLayout; + setLayout(layout); + + files = new QTreeWidget; + files->headerItem()->setText(0, tr("")); + files->headerItem()->setText(1, tr("Filename")); + files->headerItem()->setText(2, tr("Date")); + files->headerItem()->setText(3, tr("Time")); + files->headerItem()->setText(4, tr("Action")); + + files->setColumnCount(5); + files->setColumnWidth(0, 30); // selector + files->setColumnWidth(1, 190); // filename + files->setColumnWidth(2, 95); // date + files->setColumnWidth(3, 90); // time + files->setSelectionMode(QAbstractItemView::SingleSelection); + files->setEditTriggers(QAbstractItemView::SelectedClicked); // allow edit + files->setUniformRowHeights(true); + files->setIndentation(0); + + // populate with each ride in the ridelist + const QTreeWidgetItem *allRides = main->allRideItems(); + + for (int i=0; ichildCount(); i++) { + + RideItem *rideItem = static_cast(allRides->child(i)); + + QTreeWidgetItem *add = new QTreeWidgetItem(files->invisibleRootItem()); + add->setFlags(add->flags() | Qt::ItemIsEditable); + + // selector + QCheckBox *checkBox = new QCheckBox("", this); + checkBox->setChecked(true); + files->setItemWidget(add, 0, checkBox); + + // we will wipe the original file + add->setText(1, rideItem->fileName); + add->setText(2, rideItem->dateTime.toString(tr("dd MMM yyyy"))); + add->setText(3, rideItem->dateTime.toString(tr("hh:mm:ss ap"))); + + // interval action + add->setText(4, "Export"); + } + + // format and directory + QGridLayout *grid = new QGridLayout; + formatLabel = new QLabel("Export as", this); + format = new QComboBox(this); + + const RideFileFactory &rff = RideFileFactory::instance(); + foreach(QString suffix, rff.writeSuffixes()) format->addItem(rff.description(suffix)); + + selectDir = new QPushButton("Browse", this); + dirLabel = new QLabel ("Export to", this); + dirName = new QLabel(QDir::home().absolutePath(), this); + all = new QCheckBox("check/uncheck all", this); + all->setChecked(true); + + grid->addWidget(formatLabel, 0,0, Qt::AlignLeft); + grid->addWidget(format, 0,1, Qt::AlignLeft); + grid->addWidget(dirLabel, 1,0, Qt::AlignLeft); + grid->addWidget(dirName, 1,1, Qt::AlignLeft); + grid->addWidget(selectDir, 1,2, Qt::AlignLeft); + grid->addWidget(all, 2,0, Qt::AlignLeft); + grid->setColumnStretch(0, 1); + grid->setColumnStretch(1, 10); + + // buttons + QHBoxLayout *buttons = new QHBoxLayout; + status = new QLabel("", this); + status->hide(); + overwrite = new QCheckBox("Overwrite existing files", this); + cancel = new QPushButton("Cancel", this); + ok = new QPushButton("Export", this); + buttons->addWidget(overwrite); + buttons->addWidget(status); + buttons->addStretch(); + buttons->addWidget(cancel); + buttons->addWidget(ok); + + layout->addLayout(grid); + layout->addWidget(files); + layout->addLayout(buttons); + + exports = fails = 0; + + // connect signals and slots up.. + connect(selectDir, SIGNAL(clicked()), this, SLOT(selectClicked())); + connect(ok, SIGNAL(clicked()), this, SLOT(okClicked())); + connect(all, SIGNAL(stateChanged(int)), this, SLOT(allClicked())); + connect(cancel, SIGNAL(clicked()), this, SLOT(cancelClicked())); +} + +void +BatchExportDialog::selectClicked() +{ + QString dir = QFileDialog::getExistingDirectory(this, tr("Select Target Directory"), + dirName->text(), + QFileDialog::ShowDirsOnly + | QFileDialog::DontResolveSymlinks); + if (dir!="") dirName->setText(dir); + return; +} + +void +BatchExportDialog::allClicked() +{ + // set/uncheck all rides according to the "all" + bool checked = all->isChecked(); + + for(int i=0; iinvisibleRootItem()->childCount(); i++) { + QTreeWidgetItem *current = files->invisibleRootItem()->child(i); + static_cast(files->itemWidget(current,0))->setChecked(checked); + } +} + +void +BatchExportDialog::okClicked() +{ + if (ok->text() == "Export") { + aborted = false; + + overwrite->hide(); + status->setText("Exporting..."); + status->show(); + cancel->hide(); + ok->setText("Abort"); + exportFiles(); + status->setText(QString("%1 activities exported, %2 failed or skipped.").arg(exports).arg(fails)); + ok->setText("Finish"); + + } else if (ok->text() == "Abort") { + aborted = true; + } else if (ok->text() == "Finish") { + accept(); // our work is done! + } +} + +void +BatchExportDialog::cancelClicked() +{ + reject(); +} + +void +BatchExportDialog::exportFiles() +{ + // what format to export as? + QString type = RideFileFactory::instance().writeSuffixes().at(format->currentIndex()); + + // loop through the table and export all selected + for(int i=0; iinvisibleRootItem()->childCount(); i++) { + + // give user a chance to abort.. + QApplication::processEvents(); + + // did they? + if (aborted == true) return; // user aborted! + + QTreeWidgetItem *current = files->invisibleRootItem()->child(i); + + // is it selected + if (static_cast(files->itemWidget(current,0))->isChecked()) { + + files->setCurrentItem(current); QApplication::processEvents(); + + QString filename = dirName->text() + "/" + QFileInfo(current->text(1)).baseName() + "." + type; + + if (QFile(filename).exists()) { + if (overwrite->isChecked() == false) { + // skip existing files + current->setText(4, "Exists - not exported"); QApplication::processEvents(); + fails++; + continue; + + } else { + + // remove existing + QFile(filename).remove(); + current->setText(4, "Removing..."); QApplication::processEvents(); + } + + } + // this one then + current->setText(4, "Reading..."); QApplication::processEvents(); + + // open it.. + QStringList errors; + QList rides; + QFile thisfile(QString(main->home.absolutePath()+"/"+current->text(1))); + RideFile *ride = RideFileFactory::instance().openRideFile(main, thisfile, errors, &rides); + + // open success? + if (ride) { + + + current->setText(4, "Writing..."); QApplication::processEvents(); + QFile out(filename); + bool success = RideFileFactory::instance().writeRideFile(main, ride, out, type); + + if (success) { + exports++; + current->setText(4, "Exported"); QApplication::processEvents(); + } else { + fails++; + current->setText(4, "Write failed"); QApplication::processEvents(); + } + + delete ride; // free memory! + + // open failed + } else { + + current->setText(4, "Read error"); QApplication::processEvents(); + + } + } + } +} diff --git a/src/BatchExportDialog.h b/src/BatchExportDialog.h new file mode 100644 index 000000000..c3c958988 --- /dev/null +++ b/src/BatchExportDialog.h @@ -0,0 +1,80 @@ + +/* + * Copyright (c) 2011 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 + */ + +#ifndef _BatchExportDialog_h +#define _BatchExportDialog_h +#include "GoldenCheetah.h" +#include "MainWindow.h" +#include "Settings.h" +#include "Units.h" + +#include "RideItem.h" +#include "RideFile.h" + +#include +#include +#include +#include +#include +#include +#include + +// Dialog class to show filenames, import progress and to capture user input +// of ride date and time + +class BatchExportDialog : public QDialog +{ + Q_OBJECT + G_OBJECT + + +public: + BatchExportDialog(MainWindow *main); + + QTreeWidget *files; // choose files to export + +signals: + +private slots: + void cancelClicked(); + void okClicked(); + void selectClicked(); + void exportFiles(); + void allClicked(); + +private: + MainWindow *main; + bool aborted; + + QCheckBox *all; + + QComboBox *format; + QLabel *formatLabel; + + QPushButton *selectDir; + QLabel *dirLabel, *dirName; + + QCheckBox *overwrite; + QPushButton *cancel, *ok; + + int exports, fails; + QLabel *status; +}; +#endif // _BatchExportDialog_h + diff --git a/src/GcRideFile.cpp b/src/GcRideFile.cpp index 465184eb0..5e1c11ff0 100644 --- a/src/GcRideFile.cpp +++ b/src/GcRideFile.cpp @@ -28,7 +28,7 @@ static int gcFileReaderRegistered = RideFileFactory::instance().registerReader( - "gc", "GoldenCheetah XML Format", new GcFileReader()); + "gc", "GoldenCheetah XML", new GcFileReader()); RideFile * GcFileReader::openRideFile(QFile &file, QStringList &errors, QList*) const diff --git a/src/JsonRideFile.y b/src/JsonRideFile.y index 2c566f13f..585de63d8 100644 --- a/src/JsonRideFile.y +++ b/src/JsonRideFile.y @@ -226,7 +226,7 @@ string: STRING { JsonString = unprotect(JsonRideFiletex static int jsonFileReaderRegistered = RideFileFactory::instance().registerReader( - "json", "GoldenCheetah Json Format", new JsonFileReader()); + "json", "GoldenCheetah Json", new JsonFileReader()); RideFile * JsonFileReader::openRideFile(QFile &file, QStringList &errors, QList*) const diff --git a/src/KmlRideFile.cpp b/src/KmlRideFile.cpp index 87286f15a..234b4f81e 100644 --- a/src/KmlRideFile.cpp +++ b/src/KmlRideFile.cpp @@ -62,7 +62,7 @@ using kmldom::StyleMapPtr; static int kmlFileReaderRegistered = RideFileFactory::instance().registerReader( - "kml", "Google Earth KML Format", new KmlFileReader()); + "kml", "Google Earth KML", new KmlFileReader()); // // Utility functions // diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index 31ed4e44e..dd9d5e263 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -52,6 +52,7 @@ #include "ToolsDialog.h" #include "MetricAggregator.h" #include "SplitActivityWizard.h" +#include "BatchExportDialog.h" #include "TwitterDialog.h" #include "WithingsDownload.h" #include "CalendarDownload.h" @@ -440,8 +441,8 @@ MainWindow::MainWindow(const QDir &home) : rideMenu->addAction(tr("&Import from file..."), this, SLOT (importFile()), tr ("Ctrl+I")); rideMenu->addAction(tr("&Manual activity entry..."), this, SLOT(manualRide()), tr("Ctrl+M")); rideMenu->addSeparator (); - rideMenu->addAction(tr("&Export ..."), this, SLOT(exportRide()), tr("Ctrl+E")); - rideMenu->addAction(tr("&Batch export ..."), this, SLOT(exportBatch()), tr("Ctrl+B")); + rideMenu->addAction(tr("&Export..."), this, SLOT(exportRide()), tr("Ctrl+E")); + rideMenu->addAction(tr("&Batch export..."), this, SLOT(exportBatch()), tr("Ctrl+B")); rideMenu->addAction(tr("Export Metrics as CSV..."), this, SLOT(exportMetrics()), tr("")); #ifdef GC_HAVE_SOAP rideMenu->addSeparator (); @@ -1090,7 +1091,8 @@ MainWindow::currentRide() void MainWindow::exportBatch() { - // XXX todo + BatchExportDialog *d = new BatchExportDialog(this); + d->exec(); } void diff --git a/src/TcxRideFile.cpp b/src/TcxRideFile.cpp index 2c8e6ffce..945dfc591 100644 --- a/src/TcxRideFile.cpp +++ b/src/TcxRideFile.cpp @@ -30,7 +30,7 @@ static int tcxFileReaderRegistered = RideFileFactory::instance().registerReader( - "tcx", "Garmin Training Centre", new TcxFileReader()); + "tcx", "Garmin Training Centre TCX", new TcxFileReader()); RideFile *TcxFileReader::openRideFile(QFile &file, QStringList &errors, QList*list) const { diff --git a/src/src.pro b/src/src.pro index 4ba29e669..64ee02849 100644 --- a/src/src.pro +++ b/src/src.pro @@ -163,6 +163,7 @@ HEADERS += \ ANTMessages.h \ ANTlocalController.h \ ANTplusController.h \ + BatchExportDialog.h \ BestIntervalDialog.h \ BinRideFile.h \ BingMap.h \ @@ -325,6 +326,7 @@ SOURCES += \ ANTlocalController.cpp \ ANTplusController.cpp \ BasicRideMetrics.cpp \ + BatchExportDialog.cpp \ BestIntervalDialog.cpp \ BikeScore.cpp \ BinRideFile.cpp \