mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-02-13 16:18:42 +00:00
Drag and Drop Images onto a ride
.. drag and dropping images into a ride will store them in the media folder and add the filename to the "Images" metadata tag which contains a list separated by newlines. The metadata does not include the full path since we may change the path in future releases
This commit is contained in:
@@ -1628,3 +1628,99 @@ RideItem::xdataMatch(QString name, QString series, QString &mname, QString &mser
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
RideItem::addImage(QString filename)
|
||||
{
|
||||
// get list of images
|
||||
QStringList list=images();
|
||||
|
||||
// filename should be full path since we need to copy into the media folder
|
||||
// we will rename it with a number at the front that cycles
|
||||
QFileInfo fi(filename);
|
||||
if (!fi.exists() || !fi.isReadable() || !fi.isFile()) return false;
|
||||
|
||||
// lets generate a target filename
|
||||
bool isduplicate=true;
|
||||
QString targetname;
|
||||
for(int prefix=1; isduplicate; prefix++) {
|
||||
targetname=QString("%1-%2").arg(prefix).arg(fi.fileName());
|
||||
isduplicate = list.contains(targetname);
|
||||
if (!isduplicate) {
|
||||
// lets check it doesn't already exist on disk
|
||||
QFileInfo ti(context->athlete->home->media().canonicalPath() + "/" + targetname);
|
||||
isduplicate = ti.exists();
|
||||
}
|
||||
}
|
||||
|
||||
// lets copy from source full path, to media folder
|
||||
if (QFile::copy(filename, QString("%1/%2").arg(context->athlete->home->media().canonicalPath()).arg(targetname))) {
|
||||
// success !
|
||||
list << targetname;
|
||||
ride_->setTag("Images", list.join("\n"));
|
||||
|
||||
// make sure it gets saved !
|
||||
setDirty(true);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
RideItem::removeImage(QString filename)
|
||||
{
|
||||
// if it really exists then zap it!
|
||||
QFileInfo fi(context->athlete->home->media().canonicalPath() + "/" + filename);
|
||||
if (fi.exists() && fi.isReadable() && fi.isFile()) {
|
||||
|
||||
QFile::remove(fi.absoluteFilePath());
|
||||
|
||||
// remove from metadata
|
||||
QStringList list=images();
|
||||
int index= list.indexOf(filename);
|
||||
if (index != -1) list.removeAt(index);
|
||||
ride_->setTag("Images", list.join("\n"));
|
||||
|
||||
// set dirty
|
||||
setDirty(true);
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// read from the metadata but also check they actually exist
|
||||
QStringList
|
||||
RideItem::images() const
|
||||
{
|
||||
QStringList exist;
|
||||
foreach(QString filename, ride_->getTag("Images", "").split("\n")) {
|
||||
QFileInfo fi(context->athlete->home->media().canonicalPath() + "/" + filename);
|
||||
if (fi.exists() && fi.isReadable() && fi.isFile()) exist << filename;
|
||||
}
|
||||
|
||||
return exist;
|
||||
}
|
||||
|
||||
// we hide the implementation of directory here since we may change our
|
||||
// minds later and store in sub-folders etc
|
||||
QStringList RideItem::imagePaths() const
|
||||
{
|
||||
QStringList paths;
|
||||
foreach(QString filename, images())
|
||||
paths << context->athlete->home->media().canonicalPath() + "/" + filename;
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
int
|
||||
RideItem::importImages(QStringList files)
|
||||
{
|
||||
int count=0;
|
||||
|
||||
foreach(QString file, files) {
|
||||
if (addImage(file) == true) count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
@@ -129,6 +129,18 @@ class RideItem : public QObject
|
||||
// as a well formatted string
|
||||
QString getStringForSymbol(QString name, bool useMetricUnits=true);
|
||||
|
||||
// add an image to the ride- store it in the media folder and add metadata for it
|
||||
// note that in all cases the return list is the filename only, the location they
|
||||
// are stored is managed here (callers should not implement filepath)
|
||||
bool addImage(QString filename);
|
||||
bool removeImage(QString filename);
|
||||
|
||||
QStringList images() const;
|
||||
|
||||
// these two are path based
|
||||
QStringList imagePaths() const;
|
||||
int importImages(QStringList files);
|
||||
|
||||
// access the metadata
|
||||
QString getText(QString name, QString fallback) const;
|
||||
bool hasText(QString name) const;
|
||||
|
||||
@@ -623,6 +623,21 @@ heatcolor(double value)
|
||||
return returning;
|
||||
}
|
||||
|
||||
// setup list of image extensions we will support
|
||||
static QVector<QString> imageexts;
|
||||
static bool initextensions() { imageexts << ".png" << ".gif" << ".jpeg" << ".jpg" << ".bmp"; return true; }
|
||||
static bool initexts = initextensions();
|
||||
|
||||
// is the file an image?
|
||||
bool isImage(QString filename)
|
||||
{
|
||||
QString lowername = filename.toLower();
|
||||
foreach(QString ext, imageexts) {
|
||||
if (lowername.endsWith(ext)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// used std::sort, std::lower_bound et al
|
||||
|
||||
bool doubledescend(const double &s1, const double &s2) { return s1 > s2; }
|
||||
|
||||
@@ -62,6 +62,9 @@ namespace Utils
|
||||
double heat(double min, double max, double value); // return value normalised between 0-1 for min/max
|
||||
QColor heatcolor(double value); // return color hear for value between 0 and 1
|
||||
|
||||
// media
|
||||
bool isImage(QString);
|
||||
|
||||
// used std::sort, std::lower_bound et al
|
||||
struct comparedouble { bool operator()(const double p1, const double p2) { return p1 < p2; } };
|
||||
struct compareqstring { bool operator()(const QString p1, const QString p2) { return p1 < p2; } };
|
||||
|
||||
@@ -1600,11 +1600,11 @@ MainWindow::dropEvent(QDropEvent *event)
|
||||
// is this a chart file ?
|
||||
QStringList filenames;
|
||||
QList<LTMSettings> imported;
|
||||
QStringList list, workouts;
|
||||
QStringList list, workouts, images;
|
||||
for(int i=0; i<urls.count(); i++) {
|
||||
|
||||
QString filename = QFileInfo(urls.value(i).toLocalFile()).absoluteFilePath();
|
||||
fprintf(stderr, "%s\n", filename.toStdString().c_str()); fflush(stderr);
|
||||
//fprintf(stderr, "%s\n", filename.toStdString().c_str()); fflush(stderr);
|
||||
|
||||
if (filename.endsWith(".gchart", Qt::CaseInsensitive)) {
|
||||
// add to the list of charts to import
|
||||
@@ -1625,6 +1625,9 @@ MainWindow::dropEvent(QDropEvent *event)
|
||||
xmlReader.parse(source);
|
||||
imported += handler.getSettings();
|
||||
|
||||
} else if (Utils::isImage(filename)) {
|
||||
images << filename;
|
||||
|
||||
// Look for Workout files only in Train view
|
||||
} else if (currentAthleteTab->currentView() == 3 && ErgFile::isWorkout(filename)) {
|
||||
workouts << filename;
|
||||
@@ -1658,6 +1661,9 @@ MainWindow::dropEvent(QDropEvent *event)
|
||||
// import workouts
|
||||
if (workouts.count()) Library::importFiles(currentAthleteTab->context, workouts, true);
|
||||
|
||||
// import images (these will be attached to the current ride)
|
||||
if (images.count()) importImages(images);
|
||||
|
||||
// if there is anything left, process based upon view...
|
||||
if (filenames.count()) {
|
||||
|
||||
@@ -1668,6 +1674,21 @@ MainWindow::dropEvent(QDropEvent *event)
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
MainWindow::importImages(QStringList list)
|
||||
{
|
||||
// we need to be on activities view and with a current
|
||||
// ride otherwise we just ignore the list
|
||||
if (currentAthleteTab->currentView() != 1 || currentAthleteTab->context->ride == NULL) {
|
||||
QMessageBox::critical(this, tr("Import Images Failed"), tr("You can only import images on the activities view with an activity selected."));
|
||||
return;
|
||||
}
|
||||
|
||||
// lets import them
|
||||
int count = currentAthleteTab->context->ride->importImages(list);
|
||||
QMessageBox::information(this, tr("Import Images to Activity"), QString(tr("%1 images imported.")).arg(count));
|
||||
}
|
||||
|
||||
void
|
||||
MainWindow::importCharts(QStringList list)
|
||||
{
|
||||
|
||||
@@ -157,6 +157,9 @@ class MainWindow : public QMainWindow
|
||||
// chart importing
|
||||
void importCharts(QStringList);
|
||||
|
||||
// import images into the current ride
|
||||
void importImages(QStringList);
|
||||
|
||||
// open and closing windows and tabs
|
||||
void closeWindow();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user