From d2ebedac2097912c860876d286d867aa81a667da Mon Sep 17 00:00:00 2001 From: Mark Liversedge Date: Thu, 20 Dec 2012 13:30:49 +0000 Subject: [PATCH] Workout Library Part 1 of 3 Implementing a workout library that keeps track of media and workouts that can be used in train view. This first part implements; - library.xml to record search paths - search dialog to find media/workouts Part 2 and 3 will bring: - Sqllite libraryDB to store found details - Update traintool to use libraryDB and display icons, duration/distance, IF and TSS in list - import and drag-n-drop of new media/workouts --- src/ErgFile.cpp | 19 ++ src/ErgFile.h | 1 + src/ErgFilePlot.h | 8 +- src/Library.cpp | 416 ++++++++++++++++++++++++++++++++++++++++ src/Library.h | 119 ++++++++++++ src/LibraryParser.cpp | 99 ++++++++++ src/LibraryParser.h | 51 +++++ src/MainWindow.cpp | 23 ++- src/MainWindow.h | 2 + src/QtMacVideoWindow.h | 2 + src/QtMacVideoWindow.mm | 93 +++++---- src/TrainTool.cpp | 4 +- src/VideoWindow.cpp | 56 +++--- src/VideoWindow.h | 3 +- src/src.pro | 4 + 15 files changed, 833 insertions(+), 67 deletions(-) create mode 100644 src/Library.cpp create mode 100644 src/Library.h create mode 100644 src/LibraryParser.cpp create mode 100644 src/LibraryParser.h diff --git a/src/ErgFile.cpp b/src/ErgFile.cpp index a0714801a..3f7618fc6 100644 --- a/src/ErgFile.cpp +++ b/src/ErgFile.cpp @@ -21,6 +21,25 @@ #include #include "Units.h" +// Supported file types +static QStringList supported; +static bool setSupported() +{ + ::supported << ".erg"; + ::supported << ".mrc"; + ::supported << ".crs"; + ::supported << ".pgmf"; + return true; +} +static bool isinit = setSupported(); +bool ErgFile::isWorkout(QString name) +{ + foreach(QString extension, supported) { + if (name.endsWith(extension, Qt::CaseInsensitive)) + return true; + } + return false; +} ErgFile::ErgFile(QString filename, int &mode, double Cp, MainWindow *main) : Cp(Cp), filename(filename), main(main), mode(mode) { diff --git a/src/ErgFile.h b/src/ErgFile.h index 44e3b90d9..655dc9876 100644 --- a/src/ErgFile.h +++ b/src/ErgFile.h @@ -72,6 +72,7 @@ class ErgFile ~ErgFile(); // delete the contents static ErgFile *fromContent(QString, MainWindow *); // read from memory + static bool isWorkout(QString); // is this a supported workout? void reload(); // reload after messed about void parseComputrainer(QString p = ""); // its an erg,crs or mrc file diff --git a/src/ErgFilePlot.h b/src/ErgFilePlot.h index 16adf26d2..16c96aa68 100644 --- a/src/ErgFilePlot.h +++ b/src/ErgFilePlot.h @@ -111,7 +111,13 @@ class HourTimeScaleDraw: public QwtScaleDraw public: HourTimeScaleDraw() { } - virtual QwtText label(double v) const { return QString("%1").arg(round(v/60000)); } + virtual QwtText label(double v) const { + v /= 1000; + QTime t = QTime().addSecs(v); + if (scaleMap().sDist() > 5) + return t.toString("hh:mm"); + return t.toString("hh:mm:ss"); + } }; class ErgFilePlot : public QwtPlot diff --git a/src/Library.cpp b/src/Library.cpp new file mode 100644 index 000000000..d1282fac1 --- /dev/null +++ b/src/Library.cpp @@ -0,0 +1,416 @@ +/* + * Copyright (c) 2012 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 "Library.h" +#include "Settings.h" +#include "LibraryParser.h" +#include +#include +#include +#include +#include +#include + +// helpers +#ifdef Q_OS_MAC +#include "QtMacVideoWindow.h" +#else +#include "VideoWindow.h" +#endif +#include "ErgFile.h" + +QList libraries; // keep track of all the library search paths (global) + +// +// MAINTAIN 'libraries' GLOBAL +// +Library * +Library::findLibrary(QString name) +{ + foreach(Library *l, ::libraries) + if (l->name == name) + return l; + + return NULL; +} + +void +Library::initialise(QDir home) +{ + // Search paths from library.xml + if (libraries.count() == 0) { + + // it sits above all cyclist directories + home.cdUp(); + + // lets read library.xml, if not there then add workout config + // if thats not set then add home + QFile libraryXML(home.absolutePath() + "/library.xml"); + if (libraryXML.exists() == true) { + + // parse it! + QXmlInputSource source(&libraryXML); + QXmlSimpleReader xmlReader; + LibraryParser(handler); + xmlReader.setContentHandler(&handler); + xmlReader.setErrorHandler(&handler); + xmlReader.parse( source ); + libraries = handler.getLibraries(); + + } else { + + Library *one = new Library; + one->name = "Media Library"; + QString spath = appsettings->value(NULL, GC_WORKOUTDIR).toString(); + if (spath == "") spath = home.absolutePath(); + one->paths.append(spath); + libraries.append(one); + } + } +} + +// +// SEARCHDIALOG -- user select paths and files and run a search +// +LibrarySearchDialog::LibrarySearchDialog(MainWindow *mainWindow) : mainWindow(mainWindow) +{ + setAttribute(Qt::WA_DeleteOnClose); + setWindowTitle(tr("Search for Workouts and Media")); + setMinimumWidth(600); + + searcher = NULL; + + findWorkouts = new QCheckBox(tr("Workout files (.erg, .mrc etc)"), this); + findWorkouts->setChecked(true); + findMedia = new QCheckBox(tr("Video files (.mp4, .avi etc)"), this); + findMedia->setChecked(true); + + addPath = new QPushButton("+", this); + removePath = new QPushButton("-", this); +#ifndef Q_OS_MAC + addPath->setFixedSize(20,20); + removePath->setFixedSize(20,20); +#endif + + searchPathTable = new QTreeWidget(this); +#ifdef Q_OS_MAC + // get rid of annoying focus rectangle + searchPathTable->setAttribute(Qt::WA_MacShowFocusRect, 0); +#endif + searchPathTable->setColumnCount(1); + searchPathTable->setIndentation(0); + searchPathTable->headerItem()->setText(0, tr("Search Path")); + searchPathTable->setSelectionMode(QAbstractItemView::SingleSelection); + searchPathTable->setAlternatingRowColors (false); + + library = Library::findLibrary("Media Library"); + if (library) { + int i=1; + foreach (QString path, library->paths) { + QTreeWidgetItem *item = new QTreeWidgetItem(searchPathTable->invisibleRootItem(), i++); + item->setText(0, path); + } + } + + pathLabelTitle = new QLabel(tr("Searching..."), this); + pathLabel = new QLabel(this); + + mediaCountTitle = new QLabel(tr("Videos"), this); + mediaCount = new QLabel(this); + workoutCountTitle = new QLabel(tr("Workouts"), this); + workoutCount = new QLabel(this); + + cancelButton = new QPushButton(tr("Cancel"), this); + cancelButton->setDefault(false); + searchButton = new QPushButton(tr("Search"), this); + searchButton->setDefault(true); + + QLabel *searchLabel = new QLabel(tr("Files to search for:"), this); + + QVBoxLayout *mainLayout = new QVBoxLayout(this); + + mainLayout->addWidget(searchLabel); + mainLayout->addWidget(findWorkouts, Qt::AlignCenter); + mainLayout->addWidget(findMedia, Qt::AlignCenter); + + QHBoxLayout *editButtons = new QHBoxLayout; + QVBoxLayout *tableLayout = new QVBoxLayout; + editButtons->addWidget(addPath); + editButtons->addWidget(removePath); + editButtons->addStretch(); + editButtons->setSpacing(2); + tableLayout->setSpacing(2); + tableLayout->addWidget(searchPathTable); + tableLayout->addLayout(editButtons); + mainLayout->addLayout(tableLayout); + + QGridLayout *progressLayout = new QGridLayout; + progressLayout->addWidget(pathLabelTitle, 0,0); + progressLayout->addWidget(mediaCountTitle, 0,1); + progressLayout->addWidget(workoutCountTitle, 0,2); + progressLayout->addWidget(pathLabel, 1,0); + progressLayout->addWidget(mediaCount, 1,1); + progressLayout->addWidget(workoutCount, 1,2); + progressLayout->setColumnStretch(0, 7); + progressLayout->setColumnStretch(1, 1); + progressLayout->setColumnStretch(2, 1); + mainLayout->addLayout(progressLayout); + + QHBoxLayout *buttons = new QHBoxLayout; + buttons->addStretch(); + buttons->addWidget(cancelButton); + buttons->addWidget(searchButton); + + mainLayout->addStretch(); + mainLayout->addLayout(buttons); + + setSearching(false); + + connect(addPath, SIGNAL(clicked()), this, SLOT(addDirectory())); + connect(removePath, SIGNAL(clicked()), this, SLOT(removeDirectory())); + connect(cancelButton, SIGNAL(clicked()), this, SLOT(cancel())); + connect(searchButton, SIGNAL(clicked()), this, SLOT(search())); +} + +void +LibrarySearchDialog::setWidgets() +{ + if (searching) { + setFixedHeight(200); + searchButton->hide(); + cancelButton->setText(tr("Abort Search")); + searchPathTable->hide(); + addPath->hide(); + removePath->hide(); + + pathLabelTitle->show(); + pathLabel->show(); + mediaCountTitle->show(); + mediaCount->show(); + workoutCountTitle->show(); + workoutCount->show(); + + } else { + setFixedHeight(300); + searchButton->show(); + cancelButton->setText(tr("Cancel")); + searchPathTable->show(); + addPath->show(); + removePath->show(); + + pathLabelTitle->hide(); + pathLabel->hide(); + mediaCountTitle->hide(); + mediaCount->hide(); + workoutCountTitle->hide(); + workoutCount->hide(); + } +} + +void +LibrarySearchDialog::search() +{ + if (searchButton->text() == tr("Save")) { + + // first lets update the library paths to + // reflect the user selections + if (library) { + library->paths.clear(); + for(int i=0; iinvisibleRootItem()->childCount(); i++) { + QTreeWidgetItem *item = searchPathTable->invisibleRootItem()->child(i); + QString path = item->text(0); + + library->paths.append(path); + } + + // now write to disk.. + LibraryParser::serialize(mainWindow->home); + } + + // ok, we;ve completed a search without aborting + // so lets rebuild the database of workouts and videos + // using what we found... + updateDB(); + close(); + } + + if (searching) { + + // do next search path... + if (++pathIndex >= searchPathTable->invisibleRootItem()->childCount()) { + + searcher = NULL; + + pathLabel->setText(tr("Search completed.")); + pathLabelTitle->setText(""); + searchButton->setText(tr("Save")); + searchButton->show(); + cancelButton->hide(); + + return; + + } else { + + QTreeWidgetItem *item = searchPathTable->invisibleRootItem()->child(pathIndex); + QString path = item->text(0); + searcher = new LibrarySearch(path); + } + + } else { + + setSearching(true); + workoutCountN = videoCountN = pathIndex = 0; + workoutCount->setText(QString("%1").arg(++workoutCountN)); + mediaCount->setText(QString("%1").arg(++videoCountN)); + QTreeWidgetItem *item = searchPathTable->invisibleRootItem()->child(pathIndex); + QString path = item->text(0); + searcher = new LibrarySearch(path); + } + + connect(searcher, SIGNAL(done()), this, SLOT(search())); + connect(searcher, SIGNAL(searching(QString)), this, SLOT(pathsearching(QString))); + connect(searcher, SIGNAL(foundVideo(QString)), this, SLOT(foundVideo(QString))); + connect(searcher, SIGNAL(foundWorkout(QString)), this, SLOT(foundWorkout(QString))); + + searcher->start(); +} + +void +LibrarySearchDialog::pathsearching(QString text) +{ + pathLabel->setText(text); +} + +void +LibrarySearchDialog::foundWorkout(QString name) +{ + workoutCount->setText(QString("%1").arg(++workoutCountN)); + workoutsFound << name; +} + +void +LibrarySearchDialog::foundVideo(QString name) +{ + mediaCount->setText(QString("%1").arg(++videoCountN)); + videosFound << name; +qDebug()<<"vid:"<abort(); + searcher = NULL; + // we will NOT get a done signal... + } + + // ...so lets clean up + setSearching(false); + return; + } + + // lets close + accept(); +} + +void +LibrarySearchDialog::addDirectory() +{ + QString dir = QFileDialog::getExistingDirectory(this, tr("Select Directory"), + "", QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks); + + // add to tree + if (dir != "") { + QTreeWidgetItem *item = new QTreeWidgetItem(searchPathTable->invisibleRootItem(), + searchPathTable->invisibleRootItem()->childCount()+1); + item->setText(0, dir); + + } +} + +void +LibrarySearchDialog::removeDirectory() +{ + // remove the currently selected item + if (searchPathTable->selectedItems().isEmpty()) return; + + QTreeWidgetItem *which = searchPathTable->selectedItems().first(); + if (which) { + searchPathTable->invisibleRootItem()->removeChild(which); + delete which; + } +} + +void +LibrarySearchDialog::updateDB() +{ + // update the database of workouts and videos + //XXX update db here... +} + +// +// SEARCH -- traverse a directory looking for files and signal to notify of progress etc +// + +LibrarySearch::LibrarySearch(QString path) : path(path) +{ + aborted = false; +} + +void +LibrarySearch::run() +{ + MediaHelper helper; + + // file tree walking + QDirIterator directory_walker(path, QDirIterator::Subdirectories | QDirIterator::FollowSymlinks); + while(directory_walker.hasNext()){ + + directory_walker.next(); + + // whizz through every file in the directory + // if it has the right extension then we are happy + QString name = directory_walker.filePath(); + + // skip . files + if (QFileInfo(name).fileName().startsWith(",")) continue; + + emit searching(QFileInfo(name).filePath()); + + // we've been told to stop! + if (aborted) { + // we don't emit done -- since it kicks off another search + return; + } + + // is a video? + if (helper.isMedia(name)) emit foundVideo(name); + // is a workout? + if (ErgFile::isWorkout(name)) emit foundWorkout(name); + } + emit done(); +}; + +void +LibrarySearch::abort() +{ + aborted = true; +} diff --git a/src/Library.h b/src/Library.h new file mode 100644 index 000000000..ff3b7e655 --- /dev/null +++ b/src/Library.h @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2012 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 _Library_h +#define _Library_h +#include "GoldenCheetah.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class Library +{ + public: + QString name; // e.g. Media Library + QList paths; // array of search paths for files in this library + + static void initialise(QDir); // init + static Library *findLibrary(QString); +}; + +extern QList libraries; // keep track of all Library search paths for all users + +class LibrarySearch; +class LibrarySearchDialog : public QDialog +{ + Q_OBJECT + + public: + LibrarySearchDialog(MainWindow *mainWindow); + + private slots: + void search(); + void cancel(); + + void pathsearching(QString); + void foundWorkout(QString); + void foundVideo(QString); + + void addDirectory(); + void removeDirectory(); + + void updateDB(); + + private: + MainWindow *mainWindow; + Library *library; + LibrarySearch *searcher; + bool searching; + int pathIndex, workoutCountN, videoCountN; + + QStringList workoutsFound, videosFound; + + // let us know we are searching + void setSearching(bool amsearching) { + searching = amsearching; + setWidgets(); + } + + // update widgets to switch between searching and not searching + void setWidgets(); + + // gui widgets + QCheckBox *findWorkouts, + *findMedia; + QPushButton *addPath, + *removePath; + QTreeWidget *searchPathTable; + QTreeWidgetItem *allPaths; + QLabel *pathLabelTitle, *mediaCountTitle, *workoutCountTitle; + QLabel *pathLabel, *mediaCount, *workoutCount; + QPushButton *cancelButton, + *searchButton; +}; + +class LibrarySearch : public QThread +{ + Q_OBJECT + + public: + LibrarySearch(QString path); + void run(); + + public slots: + void abort(); + + + signals: + void searching(QString); + void done(); + void foundVideo(QString); + void foundWorkout(QString); + + private: + volatile bool aborted; + QString path; +}; + +#endif // _Library_h diff --git a/src/LibraryParser.cpp b/src/LibraryParser.cpp new file mode 100644 index 000000000..3566234bb --- /dev/null +++ b/src/LibraryParser.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2012 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 "Library.h" +#include "LibraryParser.h" +#include + +static inline QString unquote(QString quoted) +{ + return quoted.mid(1,quoted.length()-2); +} + +bool LibraryParser::startDocument() +{ + buffer.clear(); + return TRUE; +} + +bool LibraryParser::endElement( const QString&, const QString&, const QString &qName ) +{ + if(qName == "library") { + libraries.append(library); + } + // another search path for this library + if (qName == "path") { + library->paths.append(buffer.trimmed()); + } + return TRUE; +} + +bool LibraryParser::startElement( const QString&, const QString&, const QString &name, const QXmlAttributes &attrs) +{ + // start of a new library definition + buffer.clear(); + if(name == "library") { + library = new Library(); + for(int i=0; iname = attrs.value(i); + } + } + + return TRUE; +} + +bool LibraryParser::characters(const QString& str) +{ + buffer += str; + return TRUE; +} + +bool +LibraryParser::serialize(QDir home) +{ + // we write to root of all cyclists + home.cdUp(); + + // open file - truncate contents + QString filename = home.absolutePath() + "/library.xml"; + QFile file(filename); + file.open(QFile::WriteOnly); + file.resize(0); + QTextStream out(&file); + out.setCodec("UTF-8"); + + + // write out to file + foreach (Library *l, ::libraries) { + // begin document + out << QString("\n").arg(l->name); + + // paths... + foreach(QString p, l->paths) + out << QString("\t%1\n").arg(p); + + // end document + out << "\n"; + } + + + // close file + file.close(); + + return true; // success +} diff --git a/src/LibraryParser.h b/src/LibraryParser.h new file mode 100644 index 000000000..bede72dd8 --- /dev/null +++ b/src/LibraryParser.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2012 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 _LibraryParser_h +#define _LibraryParser_h +#include "GoldenCheetah.h" + +#include +#include "Library.h" + +class LibraryParser : public QXmlDefaultHandler +{ + +public: + // save + static bool serialize(QDir); + + // restore + bool startDocument(); + bool endElement(const QString&, const QString&, const QString &qName); + bool startElement(const QString&, const QString&, const QString &name, const QXmlAttributes &attrs); + bool characters(const QString& str); + + QList &getLibraries() { return libraries; } + +protected: + + // state whilst parsing + QString buffer; + Library *library; // the one being currently processed + + // all libraries read + QList libraries; + +}; +#endif //LibraryParser diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index 6c03126ec..29ee75016 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -119,8 +119,10 @@ #include #include -QList mainwindows; // keep track of all the MainWindows we have open +#include "Library.h" +#include "LibraryParser.h" +QList mainwindows; // keep track of all the MainWindows we have open MainWindow::MainWindow(const QDir &home) : home(home), session(0), isclean(false), ismultisave(false), @@ -136,7 +138,13 @@ MainWindow::MainWindow(const QDir &home) : static const QIcon tileIcon(":images/toolbar/main/tile.png"); static const QIcon fullIcon(":images/toolbar/main/togglefull.png"); - mainwindows.append(this); // add us to the list of open windows + /*---------------------------------------------------------------------- + * Basic State / Config + *--------------------------------------------------------------------*/ + mainwindows.append(this); // add us to the list of open windows + + // search paths + Library::initialise(home); // Network proxy QNetworkProxyQuery npq(QUrl("http://www.google.com")); @@ -800,6 +808,7 @@ MainWindow::MainWindow(const QDir &home) : optionsMenu->addSeparator(); optionsMenu->addAction(tr("Workout Wizard"), this, SLOT(showWorkoutWizard())); optionsMenu->addAction(tr("Get Workouts from ErgDB"), this, SLOT(downloadErgDB())); + optionsMenu->addAction(tr("Manage Media/Workout Library"), this, SLOT(manageLibrary())); #ifdef GC_HAVE_ICAL optionsMenu->addSeparator(); @@ -1972,6 +1981,16 @@ MainWindow::downloadErgDB() } } +/*---------------------------------------------------------------------- + * Workout/Media Library + *--------------------------------------------------------------------*/ +void +MainWindow::manageLibrary() +{ + LibrarySearchDialog *search = new LibrarySearchDialog(this); + search->exec(); +} + /*---------------------------------------------------------------------- * TrainingPeaks.com *--------------------------------------------------------------------*/ diff --git a/src/MainWindow.h b/src/MainWindow.h index 592df1e7c..517d59831 100644 --- a/src/MainWindow.h +++ b/src/MainWindow.h @@ -70,6 +70,7 @@ class ChartSettings; class QtMacSegmentedButton; class GcScopeBar; class RideFileCache; +class Library; extern QList mainwindows; // keep track of all the MainWindows we have open @@ -261,6 +262,7 @@ class MainWindow : public QMainWindow void uploadRideWithGPSAction(); void uploadTtb(); void downloadErgDB(); + void manageLibrary(); void manualProcess(QString); #ifdef GC_HAVE_SOAP void uploadTP(); diff --git a/src/QtMacVideoWindow.h b/src/QtMacVideoWindow.h index c0928e2de..f86c1f13d 100644 --- a/src/QtMacVideoWindow.h +++ b/src/QtMacVideoWindow.h @@ -58,8 +58,10 @@ class MediaHelper // get a list of supported media // found in the supplied directory QStringList listMedia(QDir directory); + bool isMedia(QString); private: + QStringList supported; }; class QtMacMovieView : public QMacCocoaViewContainer diff --git a/src/QtMacVideoWindow.mm b/src/QtMacVideoWindow.mm index c2b97ce68..cf65ce670 100644 --- a/src/QtMacVideoWindow.mm +++ b/src/QtMacVideoWindow.mm @@ -26,6 +26,28 @@ #include "QtMacVideoWindow.h" + +static inline NSString *darwinQStringToNSString (const QString &aString) +{ + return [reinterpret_cast (CFStringCreateWithCharacters + (0, reinterpret_cast (aString.unicode()), aString.length())) autorelease]; +} + +static QString qt_mac_NSStringToQString(const NSString *nsstr) +{ + NSRange range; + range.location = 0; + range.length = [nsstr length]; + QString result(range.length, QChar(0)); + + unichar *chars = new unichar[range.length]; + [nsstr getCharacters:chars range:range]; + result = QString::fromUtf16(chars, range.length); + delete[] chars; + return result; +} + + VideoWindow::VideoWindow(MainWindow *parent, const QDir &home) : GcWindow(parent), home(home), main(parent), hasMovie(false) { @@ -99,13 +121,6 @@ void VideoWindow::seekPlayback(long ms) [movie setCurrentTime:newTime]; } - -static inline NSString *darwinQStringToNSString (const QString &aString) -{ - return [reinterpret_cast (CFStringCreateWithCharacters - (0, reinterpret_cast (aString.unicode()), aString.length())) autorelease]; -} - void VideoWindow::mediaSelected(QString filename) { NativeQTMovieRef old = movie; // so we can invalidate once View has been reset @@ -141,41 +156,41 @@ void VideoWindow::mediaSelected(QString filename) if (old) [old invalidate]; } -MediaHelper::MediaHelper() { } +MediaHelper::MediaHelper() +{ + // get a QTMove object to get the extensions we need +#if 0 + NSArray *types = [QTMovie movieFileTypes:QTIncludeCommonTypes]; + for (unsigned int i=0; i<[types count]; ++i) { + QString type = qt_mac_NSStringToQString([types objectAtIndex:i]); + if (type.startsWith("'")) continue; // skip 'xxx ' types + + // weird file format associations for QTKit (?) .. probably a few others too... + if (type == "gif") continue; // skip image formats + if (type == "pdf") continue; // skip document formats + if (type == "wav") continue; // skip document formats + if (type == "snd") continue; // skip sound .. + supported << QString(".%1").arg(type); // .xxx added + } +#endif + // too many non video formats are returned, so we just list the main + // formats we know are video and supported + supported << ".mp4"; + supported << ".mov"; + supported << ".avi"; + supported << ".avi"; + supported << ".3gp"; + supported << ".3g2"; +} + MediaHelper::~MediaHelper() { } -// convert an NSString to a QString -static QString qt_mac_NSStringToQString(const NSString *nsstr) -{ - NSRange range; - range.location = 0; - range.length = [nsstr length]; - QString result(range.length, QChar(0)); - - unichar *chars = new unichar[range.length]; - [nsstr getCharacters:chars range:range]; - result = QString::fromUtf16(chars, range.length); - delete[] chars; - return result; -} - QStringList MediaHelper::listMedia(QDir dir) { - QStringList supported; QStringList returning; - // get a QTMove object to get the extensions we need - NSArray *types = [QTMovie movieFileTypes:QTIncludeCommonTypes]; - for (unsigned int i=0; i<[types count]; ++i) { - QString type = qt_mac_NSStringToQString([types objectAtIndex:i]); - - if (type.startsWith("'")) continue; // skip 'xxx ' types - - supported << QString(".%1").arg(type); // .xxx added - } - // go through the sub directories QDirIterator directory_walker(dir, QDirIterator::Subdirectories | QDirIterator::FollowSymlinks); @@ -198,6 +213,16 @@ MediaHelper::listMedia(QDir dir) return returning; } +bool +MediaHelper::isMedia(QString filename) +{ + foreach(QString extension, supported) { + if (filename.endsWith(extension, Qt::CaseInsensitive)) + return true; + } + return false; +} + QtMacMovieView::QtMacMovieView (QWidget *parent) : QMacCocoaViewContainer (0, parent) { #if QT_VERSION >= 0x040800 // see QT-BUG 22574, QMacCocoaContainer on 4.8 is "broken" diff --git a/src/TrainTool.cpp b/src/TrainTool.cpp index db69d0fd3..fd0fb87cc 100644 --- a/src/TrainTool.cpp +++ b/src/TrainTool.cpp @@ -192,8 +192,8 @@ TrainTool::TrainTool(MainWindow *parent, const QDir &home) : GcWindow(parent), h intensitySlider = new QSlider(Qt::Horizontal, this); intensitySlider->setAutoFillBackground(false); intensitySlider->setFocusPolicy(Qt::NoFocus); - intensitySlider->setMinimum(50); - intensitySlider->setMaximum(150); + intensitySlider->setMinimum(75); + intensitySlider->setMaximum(125); intensitySlider->setValue(100); slideLayout->addStretch(); slideLayout->addWidget(intensitySlider); diff --git a/src/VideoWindow.cpp b/src/VideoWindow.cpp index ce7012a05..7713f6813 100644 --- a/src/VideoWindow.cpp +++ b/src/VideoWindow.cpp @@ -193,6 +193,25 @@ void VideoWindow::mediaSelected(QString filename) MediaHelper::MediaHelper() { + // construct a list of supported types + // Using the basic list from the VLC + // Wiki here: http://www.videolan.org/vlc/features.html and then looked for + // the common extensions used from here: http://www.fileinfo.com/filetypes/video + supported << ".3GP"; + supported << ".ASF"; + supported << ".AVI"; + supported << ".DIVX"; + supported << ".FLV"; + supported << ".M4V"; + supported << ".MKV"; + supported << ".MOV"; + supported << ".MP4"; + supported << ".MPEG"; + supported << ".MPG"; + supported << ".MXF"; + supported << ".VOB"; + supported << ".WMV"; + } MediaHelper::~MediaHelper() @@ -202,35 +221,8 @@ MediaHelper::~MediaHelper() QStringList MediaHelper::listMedia(QDir dir) { - QStringList supported; QStringList returning; - // construct a list of supported types - // Using the basic list from the VLC - // Wiki here: http://www.videolan.org/vlc/features.html and then looked for - // the common extensions used from here: http://www.fileinfo.com/filetypes/video - supported << ".3GP"; - supported << ".ASF"; - supported << ".AVI"; - supported << ".DIVX"; - supported << ".FLAC"; - supported << ".FLV"; - supported << ".M4V"; - supported << ".MKV"; - supported << ".MOV"; - supported << ".MP4"; - supported << ".MPEG"; - supported << ".MPG"; - supported << ".MXF"; - supported << ".Nut"; - supported << ".OGG"; - supported << ".OGM"; - supported << ".RM"; - supported << ".VOB"; - supported << ".WAV"; - supported << ".WMA"; - supported << ".WMV"; - // go through the sub directories QDirIterator directory_walker(dir, QDirIterator::Subdirectories | QDirIterator::FollowSymlinks); @@ -252,3 +244,13 @@ MediaHelper::listMedia(QDir dir) } return returning; } + +bool +MediaHelper::isMedia(QString name) +{ + foreach (QString extension, supported) { + if (name.endsWith(extension, Qt::CaseInsensitive)) + return true; + } + return false; +} diff --git a/src/VideoWindow.h b/src/VideoWindow.h index fe20a2c80..fd83a7c21 100644 --- a/src/VideoWindow.h +++ b/src/VideoWindow.h @@ -53,9 +53,10 @@ class MediaHelper // get a list of supported media // found in the supplied directory QStringList listMedia(QDir directory); + bool isMedia(QString filename); private: - + QStringList supported; libvlc_instance_t * inst; }; diff --git a/src/src.pro b/src/src.pro index f61caf575..497a68e1e 100644 --- a/src/src.pro +++ b/src/src.pro @@ -288,6 +288,8 @@ HEADERS += \ IntervalTreeView.h \ JouleDevice.h \ JsonRideFile.h \ + Library.h \ + LibraryParser.h \ LogTimeScaleDraw.h \ LogTimeScaleEngine.h \ LTMCanvasPicker.h \ @@ -480,6 +482,8 @@ SOURCES += \ IntervalTreeView.cpp \ JouleDevice.cpp \ LeftRightBalance.cpp \ + Library.cpp \ + LibraryParser.cpp \ LogTimeScaleDraw.cpp \ LogTimeScaleEngine.cpp \ LTMCanvasPicker.cpp \