From a603dd5b5bfb9729935b5cf95baed48dca1dcfe8 Mon Sep 17 00:00:00 2001 From: Mark Liversedge Date: Sat, 22 Dec 2012 19:05:36 +0000 Subject: [PATCH] Workout Library Part 3 of 3 Linked up the training db to the train tool to allow users to select from the db. Further enhancements to the list could be made, but leaving this till we move to OpenTrainer. Still need to add drag and drop to copy into the library or add reference to the file. Fixes #301. --- src/Library.cpp | 4 +- src/TrainDB.cpp | 32 ++++++-- src/TrainDB.h | 10 ++- src/TrainTool.cpp | 188 +++++++++++++++++++--------------------------- src/TrainTool.h | 12 ++- 5 files changed, 119 insertions(+), 127 deletions(-) diff --git a/src/Library.cpp b/src/Library.cpp index f875c236c..6eda2f529 100644 --- a/src/Library.cpp +++ b/src/Library.cpp @@ -375,7 +375,7 @@ LibrarySearchDialog::removeDirectory() void LibrarySearchDialog::updateDB() { - trainDB->connection().transaction(); + trainDB->startLUW(); // workouts foreach(QString ergFile, workoutsFound) { @@ -391,7 +391,7 @@ LibrarySearchDialog::updateDB() trainDB->importVideo(video); } - trainDB->connection().commit(); + trainDB->endLUW(); } // diff --git a/src/TrainDB.cpp b/src/TrainDB.cpp index 89963822a..799ef27f7 100644 --- a/src/TrainDB.cpp +++ b/src/TrainDB.cpp @@ -25,7 +25,7 @@ // Rev Date Who What Changed // 01 21 Dec 2012 Mark Liversedge Initial Build -static int TrainDBSchemaVersion = 2; +static int TrainDBSchemaVersion = 1; TrainDB *trainDB; TrainDB::TrainDB(QDir home) : home(home) @@ -91,12 +91,16 @@ bool TrainDB::createVideoTable() // we need to create it! if (rc && createTables) { - QString createVideoTable = "create table videos (filename varchar primary key," + QString createVideoTable = "create table videos (filepath varchar primary key," + "filename varchar," "timestamp integer," "length integer);"; rc = query.exec(createVideoTable); + // insert the 'DVD' record for playing currently loaded DVD + //XXX rc = query.exec("INSERT INTO videos (filepath, filename) values (\"\", \"DVD\");"); + // add row to version database query.exec("DELETE FROM version where table_name = \"videos\""); @@ -131,7 +135,8 @@ bool TrainDB::createWorkoutTable() // we need to create it! if (rc && createTables) { - QString createMetricTable = "create table workouts (filename varchar primary key," + QString createMetricTable = "create table workouts (filepath varchar primary key," + "filename," "timestamp integer," "description varchar," "source varchar," @@ -144,6 +149,14 @@ bool TrainDB::createWorkoutTable() rc = query.exec(createMetricTable); + QString manualErg = QString("INSERT INTO workouts (filepath, filename) values (\"//1\", \"%1\");") + .arg(tr("Manual Erg Mode")); + rc = query.exec(manualErg); + + QString manualCrs = QString("INSERT INTO workouts (filepath, filename) values (\"//2\", \"%1\");") + .arg(tr("Manual Slope Mode")); + rc = query.exec(manualCrs); + // add row to version database query.exec("DELETE FROM version where table_name = \"workouts\""); @@ -257,12 +270,13 @@ bool TrainDB::importWorkout(QString pathname, ErgFile *ergFile) QDateTime timestamp = QDateTime::currentDateTime(); // zap the current row - if there is one - query.prepare("DELETE FROM workouts WHERE filename = ?;"); + query.prepare("DELETE FROM workouts WHERE filepath = ?;"); query.addBindValue(pathname); query.exec(); // construct an insert statement - QString insertStatement = "insert into workouts ( filename, " + QString insertStatement = "insert into workouts ( filepath, " + "filename," "timestamp," "description," "source," @@ -271,11 +285,12 @@ bool TrainDB::importWorkout(QString pathname, ErgFile *ergFile) "coggan_tss," "coggan_if," "elevation," - "grade ) values ( ?,?,?,?,?,?,?,?,?,? );"; + "grade ) values ( ?,?,?,?,?,?,?,?,?,?,? );"; query.prepare(insertStatement); // filename, timestamp, ride date query.addBindValue(pathname); + query.addBindValue(QFileInfo(pathname).fileName()); query.addBindValue(timestamp); query.addBindValue(ergFile->Name); query.addBindValue(ergFile->Source); @@ -298,16 +313,17 @@ bool TrainDB::importVideo(QString pathname) QDateTime timestamp = QDateTime::currentDateTime(); // zap the current row - if there is one - query.prepare("DELETE FROM videos WHERE filename = ?;"); + query.prepare("DELETE FROM videos WHERE filepath = ?;"); query.addBindValue(pathname); query.exec(); // construct an insert statement - QString insertStatement = "insert into videos ( filename ) values ( ? );"; + QString insertStatement = "insert into videos ( filepath,filename ) values ( ?,? );"; query.prepare(insertStatement); // filename, timestamp, ride date query.addBindValue(pathname); + query.addBindValue(QFileInfo(pathname).fileName()); // go do it! bool rc = query.exec(); diff --git a/src/TrainDB.h b/src/TrainDB.h index b11a0b3d1..502ee5651 100644 --- a/src/TrainDB.h +++ b/src/TrainDB.h @@ -26,9 +26,11 @@ #include class ErgFile; -class TrainDB +class TrainDB : public QObject { + Q_OBJECT + public: // get connection name @@ -44,9 +46,15 @@ class TrainDB TrainDB(QDir home); ~TrainDB(); + void startLUW() { dbconn.transaction(); } + void endLUW() { dbconn.commit(); emit dataChanged(); } + bool importWorkout(QString pathname, ErgFile *ergFile); bool importVideo(QString pathname); //XXX simple for now + signals: + void dataChanged(); + private: QDir home; QSqlDatabase db; diff --git a/src/TrainTool.cpp b/src/TrainTool.cpp index 54469502a..ecc281e99 100644 --- a/src/TrainTool.cpp +++ b/src/TrainTool.cpp @@ -49,6 +49,7 @@ #endif #include // isnan and isinf +#include "TrainDB.h" TrainTool::TrainTool(MainWindow *parent, const QDir &home) : GcWindow(parent), home(home), main(parent) { @@ -79,16 +80,35 @@ TrainTool::TrainTool(MainWindow *parent, const QDir &home) : GcWindow(parent), h #endif #if defined Q_OS_MAC || defined GC_HAVE_VLC - mediaTree = new QTreeWidget; - mediaTree->setFrameStyle(QFrame::NoFrame); - mediaTree->setColumnCount(1); - mediaTree->setSelectionMode(QAbstractItemView::SingleSelection); + videoModel = new QSqlTableModel(this, trainDB->connection()); + videoModel->setTable("videos"); + videoModel->setEditStrategy(QSqlTableModel::OnManualSubmit); + videoModel->select(); + while (videoModel->canFetchMore(QModelIndex())) videoModel->fetchMore(QModelIndex()); + + mediaTree = new QTreeView; + mediaTree->setModel(videoModel); + + // hide unwanted columns and header + for(int i=0; iheader()->count(); i++) + mediaTree->setColumnHidden(i, true); + mediaTree->setColumnHidden(1, false); // show filename mediaTree->header()->hide(); - mediaTree->setAlternatingRowColors (false); - mediaTree->setIndentation(5); - allMedia = new QTreeWidgetItem(mediaTree, HEAD_TYPE); - allMedia->setText(0, tr("Video / Media")); - mediaTree->expandItem(allMedia); + + mediaTree->setSortingEnabled(true); + mediaTree->setAlternatingRowColors(false); + mediaTree->setEditTriggers(QAbstractItemView::NoEditTriggers); // read-only + mediaTree->expandAll(); + mediaTree->header()->setCascadingSectionResizes(true); // easier to resize this way + mediaTree->setContextMenuPolicy(Qt::CustomContextMenu); + mediaTree->header()->setStretchLastSection(true); + mediaTree->header()->setMinimumSectionSize(0); + mediaTree->header()->setFocusPolicy(Qt::NoFocus); + mediaTree->setFrameStyle(QFrame::NoFrame); +#ifdef Q_OS_MAC + mediaTree->header()->setSortIndicatorShown(false); // blue looks nasty + mediaTree->setAttribute(Qt::WA_MacShowFocusRect, 0); +#endif #endif deviceTree = new QTreeWidget; @@ -106,17 +126,34 @@ TrainTool::TrainTool(MainWindow *parent, const QDir &home) : GcWindow(parent), h deviceTree->expandItem(allDevices); deviceTree->setContextMenuPolicy(Qt::CustomContextMenu); - workoutTree = new QTreeWidget; - workoutTree->setFrameStyle(QFrame::NoFrame); - workoutTree->setColumnCount(1); - workoutTree->setSelectionMode(QAbstractItemView::SingleSelection); - workoutTree->header()->hide(); - workoutTree->setAlternatingRowColors (false); - workoutTree->setIndentation(5); + workoutModel = new QSqlTableModel(this, trainDB->connection()); + workoutModel->setTable("workouts"); + workoutModel->setEditStrategy(QSqlTableModel::OnManualSubmit); + workoutModel->select(); + while (workoutModel->canFetchMore(QModelIndex())) workoutModel->fetchMore(QModelIndex()); - allWorkouts = new QTreeWidgetItem(workoutTree, HEAD_TYPE); - allWorkouts->setText(0, tr("Workout Library")); - workoutTree->expandItem(allWorkouts); + workoutTree = new QTreeView; + workoutTree->setModel(workoutModel); + + // hide unwanted columns and header + for(int i=0; iheader()->count(); i++) + workoutTree->setColumnHidden(i, true); + workoutTree->setColumnHidden(1, false); // show filename + workoutTree->header()->hide(); + workoutTree->setFrameStyle(QFrame::NoFrame); + workoutTree->setAlternatingRowColors(false); + workoutTree->setEditTriggers(QAbstractItemView::NoEditTriggers); // read-only + workoutTree->expandAll(); + workoutTree->header()->setCascadingSectionResizes(true); // easier to resize this way + workoutTree->setContextMenuPolicy(Qt::CustomContextMenu); + workoutTree->header()->setStretchLastSection(true); + workoutTree->header()->setMinimumSectionSize(0); + workoutTree->header()->setFocusPolicy(Qt::NoFocus); + workoutTree->setFrameStyle(QFrame::NoFrame); +#ifdef Q_OS_MAC + workoutTree->header()->setSortIndicatorShown(false); // blue looks nasty + workoutTree->setAttribute(Qt::WA_MacShowFocusRect, 0); +#endif // TOOLBAR BUTTONS ETC QHBoxLayout *toolallbuttons=new QHBoxLayout; // on toolbar @@ -274,21 +311,16 @@ TrainTool::TrainTool(MainWindow *parent, const QDir &home) : GcWindow(parent), h //connect(serverTree,SIGNAL(itemSelectionChanged()), this, SLOT(serverTreeWidgetSelectionChanged())); connect(deviceTree,SIGNAL(itemSelectionChanged()), this, SLOT(deviceTreeWidgetSelectionChanged())); connect(deviceTree,SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(deviceTreeMenuPopup(const QPoint &))); - connect(workoutTree,SIGNAL(itemSelectionChanged()), this, SLOT(workoutTreeWidgetSelectionChanged())); #if defined Q_OS_MAC || defined GC_HAVE_VLC - connect(mediaTree,SIGNAL(itemSelectionChanged()), this, SLOT(mediaTreeWidgetSelectionChanged())); + connect(mediaTree->selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)), + this, SLOT(mediaTreeWidgetSelectionChanged())); #endif connect(main, SIGNAL(configChanged()), this, SLOT(configChanged())); + connect(trainDB, SIGNAL(dataChanged()), this, SLOT(refresh())); + connect(workoutTree->selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)), this, SLOT(workoutTreeWidgetSelectionChanged())); // add a watch on all directories QVariant workoutDir = appsettings->value(NULL, GC_WORKOUTDIR); -#if 0 //XXX Performance issues with this - watcher = boost::shared_ptr(new QFileSystemWatcher()); - watcher->addPaths(workoutDir.toStringList()); - - connect(&*watcher,SIGNAL(directoryChanged(QString)),this,SLOT(configChanged())); - connect(&*watcher,SIGNAL(fileChanged(QString)),this,SLOT(configChanged())); -#endif // set home main = parent; @@ -337,6 +369,15 @@ TrainTool::TrainTool(MainWindow *parent, const QDir &home) : GcWindow(parent), h } +void +TrainTool::refresh() +{ + videoModel->select(); + while (videoModel->canFetchMore(QModelIndex())) videoModel->fetchMore(QModelIndex()); + workoutModel->select(); + while (workoutModel->canFetchMore(QModelIndex())) workoutModel->fetchMore(QModelIndex()); +} + void TrainTool::configChanged() { @@ -394,46 +435,6 @@ TrainTool::configChanged() deviceTree->setCurrentItem(allDevices->child(0)); } - // WORKOUTS - // zap whats there - QList workouts = allWorkouts->takeChildren(); - for (int i=0; isetText(0, tr("Manual Ergo Mode")); - QTreeWidgetItem *slopemode = new QTreeWidgetItem(allWorkouts, WORKOUT_TYPE); - slopemode->setText(0, tr("Manual Slope Mode")); - - // add all the workouts in the library - QVariant workoutDir = appsettings->value(this, GC_WORKOUTDIR); - QStringListIterator w(listWorkoutFiles(workoutDir.toString())); - while (w.hasNext()) { - QString name = w.next(); - QTreeWidgetItem *work = new QTreeWidgetItem(allWorkouts, WORKOUT_TYPE); - work->setText(0, name); - } - -#if defined Q_OS_MAC || defined GC_HAVE_VLC - // MEDIA - QList media = allMedia->takeChildren(); - for (int i=0; isetText(0, "DVD"); -#endif - - MediaHelper mediaHelper; - foreach(QString video, mediaHelper.listMedia(QDir(workoutDir.toString()))) { - - // add a media line for the video (it might be a song though...) - QTreeWidgetItem *media = new QTreeWidgetItem(allMedia, WORKOUT_TYPE); - media->setText(0, video); - } -#endif - // Athlete FTP=285; // default to 285 if zones are not set int range = main->zones()->whichRange(QDate::currentDate()); @@ -501,18 +502,8 @@ TrainTool::devices() void TrainTool::workoutTreeWidgetSelectionChanged() { - assert(workoutTree->selectedItems().size() <= 1); - if (workoutTree->selectedItems().isEmpty()) - workout = NULL; - else { - QTreeWidgetItem *which = workoutTree->selectedItems().first(); - if (which->type() != WORKOUT_TYPE) - workout = NULL; - else - workout = which; - } - - //int mode; + QModelIndex current = workoutTree->currentIndex(); + QString filename = workoutModel->data(workoutModel->index(current.row(), 0), Qt::DisplayRole).toString(); // wip away the current selected workout if (ergFile) { @@ -520,15 +511,8 @@ TrainTool::workoutTreeWidgetSelectionChanged() ergFile = NULL; } - // which one is selected? - if (currentWorkout() == NULL || currentWorkout()->type() != WORKOUT_TYPE) { - main->notifyErgFileSelected(NULL); - setLabels(); - return; - } - // is it the auto mode? - int index = workoutItems()->indexOfChild((QTreeWidgetItem *)currentWorkout()); + int index = current.row(); if (index == 0) { // ergo mode main->notifyErgFileSelected(NULL); @@ -545,10 +529,7 @@ TrainTool::workoutTreeWidgetSelectionChanged() //ergPlot->setVisible(false); } else { // workout mode - QVariant workoutDir = appsettings->value(this, GC_WORKOUTDIR); - QString fileName = workoutDir.toString() + "/" + currentWorkout()->text(0); // filename - - ergFile = new ErgFile(fileName, mode, main); + ergFile = new ErgFile(filename, mode, main); if (ergFile->isValid()) { status |= RT_WORKOUT; @@ -604,26 +585,9 @@ TrainTool::listWorkoutFiles(const QDir &dir) const void TrainTool::mediaTreeWidgetSelectionChanged() { - assert(mediaTree->selectedItems().size() <= 1); - if (mediaTree->selectedItems().isEmpty()) - media = NULL; - else { - QTreeWidgetItem *which = mediaTree->selectedItems().first(); - if (which->type() != WORKOUT_TYPE) - media = NULL; - else - media = which; - } - - // which one is selected? - if (currentMedia() == NULL || currentMedia()->type() != WORKOUT_TYPE) { - main->notifyMediaSelected(""); - return; - } - - QVariant workoutDir = appsettings->value(this, GC_WORKOUTDIR); - QString fileName = workoutDir.toString() + "/" + currentMedia()->text(0); // filename - main->notifyMediaSelected(fileName); + QModelIndex current = mediaTree->currentIndex(); + QString filename = videoModel->data(videoModel->index(current.row(), 0), Qt::DisplayRole).toString(); + main->notifyMediaSelected(filename); } /*-------------------------------------------------------------------------------- diff --git a/src/TrainTool.h b/src/TrainTool.h index dd7b1f5de..dcf98bec2 100644 --- a/src/TrainTool.h +++ b/src/TrainTool.h @@ -35,6 +35,7 @@ #include "math.h" // for round() #include "Units.h" // for MILES_PER_KM #include +#include // Status settings #define RT_MODE_ERGO 0x0001 // load generation modes @@ -84,7 +85,6 @@ class TrainTool : public GcWindow const QTreeWidgetItem *currentWorkout() { return workout; } const QTreeWidgetItem *currentMedia() { return media; } const QTreeWidgetItem *workoutItems() { return allWorkouts; } - const QTreeWidgetItem *mediaItems() { return allMedia; } const QTreeWidgetItem *currentServer() { return server; } const QTreeWidgetItem *serverItems() { return allServers; } @@ -133,6 +133,8 @@ class TrainTool : public GcWindow void deviceTreeMenuPopup(const QPoint &); void deleteDevice(); + void refresh(); // when TrainDB is updated... + //XXX void workoutTreeMenuPopup(const QPoint &); //XXX void mediaTreeMenuPopup(const QPoint &); @@ -174,13 +176,15 @@ class TrainTool : public GcWindow QWidget *toolbarButtons; - QTreeWidget *workoutTree; + QSqlTableModel *videoModel; + QSqlTableModel *workoutModel; + QTreeWidget *deviceTree; QTreeWidget *serverTree; - QTreeWidget *mediaTree; + QTreeView *workoutTree; + QTreeView *mediaTree; QTreeWidgetItem *allServers; - QTreeWidgetItem *allMedia; QTreeWidgetItem *allDevices; QTreeWidgetItem *server; QTreeWidgetItem *allWorkouts;