mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-04-15 05:32:21 +00:00
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
This commit is contained in:
@@ -21,6 +21,25 @@
|
||||
#include <stdint.h>
|
||||
#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)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
416
src/Library.cpp
Normal file
416
src/Library.cpp
Normal file
@@ -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 <QVBoxLayout>
|
||||
#include <QHeaderView>
|
||||
#include <QLabel>
|
||||
#include <QApplication>
|
||||
#include <QDirIterator>
|
||||
#include <QFileInfo>
|
||||
|
||||
// helpers
|
||||
#ifdef Q_OS_MAC
|
||||
#include "QtMacVideoWindow.h"
|
||||
#else
|
||||
#include "VideoWindow.h"
|
||||
#endif
|
||||
#include "ErgFile.h"
|
||||
|
||||
QList<Library*> 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; i<searchPathTable->invisibleRootItem()->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:"<<name;
|
||||
}
|
||||
|
||||
void
|
||||
LibrarySearchDialog::cancel()
|
||||
{
|
||||
if (searching) {
|
||||
|
||||
if (searcher) {
|
||||
searcher->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;
|
||||
}
|
||||
119
src/Library.h
Normal file
119
src/Library.h
Normal file
@@ -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 <QDir>
|
||||
#include <QLabel>
|
||||
#include <QDialog>
|
||||
#include <QCheckBox>
|
||||
#include <QPushButton>
|
||||
#include <QTextEdit>
|
||||
#include <QTreeWidget>
|
||||
#include <QTreeWidgetItem>
|
||||
#include <QThread>
|
||||
|
||||
class Library
|
||||
{
|
||||
public:
|
||||
QString name; // e.g. Media Library
|
||||
QList<QString> paths; // array of search paths for files in this library
|
||||
|
||||
static void initialise(QDir); // init
|
||||
static Library *findLibrary(QString);
|
||||
};
|
||||
|
||||
extern QList<Library *> 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
|
||||
99
src/LibraryParser.cpp
Normal file
99
src/LibraryParser.cpp
Normal file
@@ -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 <QDebug>
|
||||
|
||||
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; i<attrs.count(); i++) {
|
||||
if (attrs.qName(i) == "name") library->name = 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("<library name=\"%1\">\n").arg(l->name);
|
||||
|
||||
// paths...
|
||||
foreach(QString p, l->paths)
|
||||
out << QString("\t<path>%1</path>\n").arg(p);
|
||||
|
||||
// end document
|
||||
out << "</library>\n";
|
||||
}
|
||||
|
||||
|
||||
// close file
|
||||
file.close();
|
||||
|
||||
return true; // success
|
||||
}
|
||||
51
src/LibraryParser.h
Normal file
51
src/LibraryParser.h
Normal file
@@ -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 <QXmlDefaultHandler>
|
||||
#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<Library*> &getLibraries() { return libraries; }
|
||||
|
||||
protected:
|
||||
|
||||
// state whilst parsing
|
||||
QString buffer;
|
||||
Library *library; // the one being currently processed
|
||||
|
||||
// all libraries read
|
||||
QList<Library *> libraries;
|
||||
|
||||
};
|
||||
#endif //LibraryParser
|
||||
@@ -119,8 +119,10 @@
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
#include <boost/version.hpp>
|
||||
|
||||
QList<MainWindow *> mainwindows; // keep track of all the MainWindows we have open
|
||||
#include "Library.h"
|
||||
#include "LibraryParser.h"
|
||||
|
||||
QList<MainWindow *> 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
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
@@ -70,6 +70,7 @@ class ChartSettings;
|
||||
class QtMacSegmentedButton;
|
||||
class GcScopeBar;
|
||||
class RideFileCache;
|
||||
class Library;
|
||||
|
||||
extern QList<MainWindow *> 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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -26,6 +26,28 @@
|
||||
|
||||
#include "QtMacVideoWindow.h"
|
||||
|
||||
|
||||
static inline NSString *darwinQStringToNSString (const QString &aString)
|
||||
{
|
||||
return [reinterpret_cast<const NSString *> (CFStringCreateWithCharacters
|
||||
(0, reinterpret_cast<const UniChar *> (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<const NSString *> (CFStringCreateWithCharacters
|
||||
(0, reinterpret_cast<const UniChar *> (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"
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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 \
|
||||
|
||||
Reference in New Issue
Block a user