diff --git a/src/RideCache.cpp b/src/RideCache.cpp new file mode 100644 index 000000000..20017fd47 --- /dev/null +++ b/src/RideCache.cpp @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2014 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 "RideCache.h" + +#include "Context.h" +#include "Athlete.h" +#include "Route.h" +#include "RouteWindow.h" + +RideCache::RideCache(Context *context) : context(context) +{ + // set the list + // populate ride list + RideItem *last = NULL; + QStringListIterator i(RideFileFactory::instance().listRideFiles(context->athlete->home->activities())); + while (i.hasNext()) { + QString name = i.next(); + QDateTime dt; + if (RideFile::parseRideFileName(name, &dt)) { + last = new RideItem(context->athlete->home->activities().canonicalPath(), name, dt, context); + rides_ << last; + } + } + + + // load the store - will unstale once cache restored + load(); + + // do we have any stale items ? + refresh(); +} + +RideCache::~RideCache() +{ + // save to store + save(); +} + +// add a new ride +void +RideCache::addRide(QString name, bool dosignal) +{ + // ignore malformed names + QDateTime dt; + if (!RideFile::parseRideFileName(name, &dt)) return; + + // new ride item + RideItem *last = new RideItem(context->athlete->home->activities().canonicalPath(), name, dt, context); + + // now add to the list, or replace if already there + bool added = false; + for (int index=0; index < rides_.count(); index++) { + if (rides_[index]->fileName == last->fileName) { + rides_[index] = last; + break; + } + } + + if (!added) rides_ << last; + qSort(rides_); // sort by date + + // refresh metrics for *this ride only* + // XXX not implemented yet XXX + + if (dosignal) context->notifyRideAdded(last); // here so emitted BEFORE rideSelected is emitted! + + //Search routes + context->athlete->routes->searchRoutesInRide(last->ride()); + + // notify everyone to select it + context->ride = last; + context->notifyRideSelected(last); +} + +void +RideCache::removeCurrentRide() +{ + if (context->ride == NULL) return; + + RideItem *select = NULL; // ride to select once its gone + RideItem *todelete = context->ride; + + bool found = false; + int index=0; // index to wipe out + + // find ours in the list and select the one + // immediately after it, but if it is the last + // one on the list select the one before + for(index=0; index < rides_.count(); index++) { + + if (rides_[index]->fileName == context->ride->fileName) { + + // bingo! + found = true; + if (rides_.count()-index > 1) select = rides_[index+1]; + else if (index > 0) select = rides_[index-1]; + break; + + } + } + + // WTAF!? + if (!found) { + qDebug()<<"ERROR: delete not found."; + return; + } + + // remove from the cache, before deleting it this is so + // any aggregating functions no longer see it, when recalculating + // during aride deleted operation + rides_.remove(index, 1); + + // delete the file by renaming it + QString strOldFileName = context->ride->fileName; + + QFile file(context->athlete->home->activities().canonicalPath() + "/" + strOldFileName); + // purposefully don't remove the old ext so the user wouldn't have to figure out what the old file type was + QString strNewName = strOldFileName + ".bak"; + + // in case there was an existing bak file, delete it + // ignore errors since it probably isn't there. + QFile::remove(context->athlete->home->activities().canonicalPath() + "/" + strNewName); + + if (!file.rename(context->athlete->home->activities().canonicalPath() + "/" + strNewName)) { + QMessageBox::critical(NULL, "Rename Error", tr("Can't rename %1 to %2") + .arg(strOldFileName).arg(strNewName)); + } + + // remove any other derived/additional files; notes, cpi etc + QStringList extras; + extras << "notes" << "cpi" << "cpx"; + foreach (QString extension, extras) { + + QString deleteMe = QFileInfo(strOldFileName).baseName() + "." + extension; + QFile::remove(context->athlete->home->activities().canonicalPath() + "/" + deleteMe); + } + + // rename also the source files either in /imports or in /downloads to allow a second round of import + QString sourceFilename = todelete->ride()->getTag("Source Filename", ""); + if (sourceFilename != "") { + // try to rename in both directories /imports and /downloads + // but don't report any errors - files may have been backup already + QFile old1 (context->athlete->home->imports().canonicalPath() + "/" + sourceFilename); + old1.rename(context->athlete->home->imports().canonicalPath() + "/" + sourceFilename + ".bak"); + QFile old2 (context->athlete->home->downloads().canonicalPath() + "/" + sourceFilename); + old2.rename(context->athlete->home->downloads().canonicalPath() + "/" + sourceFilename + ".bak"); + } + + // we don't want the whole delete, select next flicker + context->mainWindow->setUpdatesEnabled(false); + + // select a different ride + context->ride = select; + + // notify AFTER deleted from DISK.. + context->notifyRideDeleted(todelete); + + // ..but before MEMORY cleared + todelete->freeMemory(); + delete todelete; + + // now we can update + context->mainWindow->setUpdatesEnabled(true); + QApplication::processEvents(); + + // now select another ride + context->notifyRideSelected(select); +} + +// work with "rideDB.json" XXX not implemented yet +void RideCache::load() {} +void RideCache::save() {} + +// refresh the metrics XXX not implemented yet +void +RideCache::refresh() +{ + if (isRunning()) return; + else { + + bool stale = false; + foreach(RideItem *item, rides_) { + if (item->isstale) { + stale = true; + break; + } + } + if (stale) start(); + } +} + +void RideCache::run() +{ + //XXX not implemented but where metrics and meta get refreshed. +} diff --git a/src/RideCache.h b/src/RideCache.h new file mode 100644 index 000000000..98395ef92 --- /dev/null +++ b/src/RideCache.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2014 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 _GC_RideCache_h +#define _GC_RideCache_h 1 + +#include "GoldenCheetah.h" +#include "MainWindow.h" +#include "RideFile.h" +#include "RideItem.h" + +#include +#include + +class Context; + +class RideCache : public QThread +{ + public: + + RideCache(Context *context); + ~RideCache(); + + // the ride list + QVector&rides() { return rides_; } + + // add/remove a ride to the list + void addRide(QString name, bool dosignal); + void removeCurrentRide(); + + // restore / dump cache to disk (json) + void load(); + void save(); + + // the background refresher ! + void refresh(); // calls start() and doesn't wait + + // the thread code that gets run to refresh + void run(); + + protected: + + private: + + Context *context; + QVector rides_; +}; + +#endif // _GC_RideCache_h