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;