• Skip to content
  • Skip to link menu
KDE 4.4 API Reference
  • KDE API Reference
  • API Reference
  • Sitemap
  • Contact Us
 

digikam

albumdb.cpp

Go to the documentation of this file.
00001 /* ============================================================
00002  *
00003  * This file is a part of digiKam project
00004  * http://www.digikam.org
00005  *
00006  * Date        : 2004-06-18
00007  * Description :database album interface.
00008  *
00009  * Copyright (C) 2004-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu>
00010  * Copyright (C) 2006-2009 by Gilles Caulier <caulier dot gilles at gmail dot com>
00011  * Copyright (C) 2006-2009 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
00012  *
00013  * This program is free software; you can redistribute it
00014  * and/or modify it under the terms of the GNU General
00015  * Public License as published by the Free Software Foundation;
00016  * either version 2, or (at your option)
00017  * any later version.
00018  *
00019  * This program is distributed in the hope that it will be useful,
00020  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00021  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00022  * GNU General Public License for more details.
00023  *
00024  * ============================================================ */
00025 
00026 #include "albumdb.h"
00027 
00028 // C ANSI includes
00029 
00030 extern "C"
00031 {
00032 #include <sys/time.h>
00033 }
00034 
00035 // C++ includes
00036 
00037 #include <cstdio>
00038 #include <cstdlib>
00039 #include <ctime>
00040 
00041 // Qt includes
00042 
00043 #include <QFile>
00044 #include <QFileInfo>
00045 #include <QDir>
00046 
00047 // KDE includes
00048 
00049 #include <klocale.h>
00050 #include <kdebug.h>
00051 
00052 // Local includes
00053 
00054 #include "databasebackend.h"
00055 #include "collectionmanager.h"
00056 #include "collectionlocation.h"
00057 
00058 namespace Digikam
00059 {
00060 
00061 class AlbumDBPriv
00062 {
00063 
00064 public:
00065 
00066     AlbumDBPriv()
00067     {
00068         db = 0;
00069     }
00070 
00071     DatabaseBackend *db;
00072     QList<int>  recentlyAssignedTags;
00073 };
00074 
00075 AlbumDB::AlbumDB(DatabaseBackend *backend)
00076        : d(new AlbumDBPriv)
00077 {
00078     d->db = backend;
00079 }
00080 
00081 AlbumDB::~AlbumDB()
00082 {
00083     delete d;
00084 }
00085 
00086 QList<AlbumRootInfo> AlbumDB::getAlbumRoots()
00087 {
00088     QList<AlbumRootInfo> list;
00089 
00090     QList<QVariant> values;
00091     d->db->execSql( "SELECT id, label, status, type, identifier, specificPath FROM AlbumRoots;", &values );
00092 
00093     for (QList<QVariant>::const_iterator it = values.constBegin(); it != values.constEnd();)
00094     {
00095         AlbumRootInfo info;
00096         info.id           = (*it).toInt();
00097         ++it;
00098         info.label        = (*it).toString();
00099         ++it;
00100         info.status       = (*it).toInt();
00101         ++it;
00102         info.type         = (AlbumRoot::Type)(*it).toInt();
00103         ++it;
00104         info.identifier   = (*it).toString();
00105         ++it;
00106         info.specificPath = (*it).toString();
00107         ++it;
00108 
00109         list << info;
00110     }
00111 
00112     return list;
00113 }
00114 
00115 int AlbumDB::addAlbumRoot(AlbumRoot::Type type, const QString& identifier, const QString& specificPath, const QString& label)
00116 {
00117     QVariant id;
00118     d->db->execSql( QString("REPLACE INTO AlbumRoots (type, label, status, identifier, specificPath) "
00119                             "VALUES(?, ?, 0, ?, ?);"),
00120                     (int)type, label, identifier, specificPath, 0, &id);
00121 
00122     d->db->recordChangeset(AlbumRootChangeset(id.toInt(), AlbumRootChangeset::Added));
00123     return id.toInt();
00124 }
00125 
00126 void AlbumDB::deleteAlbumRoot(int rootId)
00127 {
00128     d->db->execSql( QString("DELETE FROM AlbumRoots WHERE id=?;"),
00129                     rootId );
00130     d->db->recordChangeset(AlbumRootChangeset(rootId, AlbumRootChangeset::Deleted));
00131 }
00132 
00133 void AlbumDB::migrateAlbumRoot(int rootId, const QString& identifier)
00134 {
00135     d->db->execSql( QString("UPDATE AlbumRoots SET identifier=? WHERE id=?;"),
00136                     identifier, rootId);
00137     d->db->recordChangeset(AlbumRootChangeset(rootId, AlbumRootChangeset::PropertiesChanged));
00138 }
00139 
00140 void AlbumDB::setAlbumRootLabel(int rootId, const QString& newLabel)
00141 {
00142     d->db->execSql( QString("UPDATE AlbumRoots SET label=? WHERE id=?;"),
00143                     newLabel, rootId);
00144     d->db->recordChangeset(AlbumRootChangeset(rootId, AlbumRootChangeset::PropertiesChanged));
00145 }
00146 
00147 void AlbumDB::changeAlbumRootType(int rootId, AlbumRoot::Type newType)
00148 {
00149     d->db->execSql( QString("UPDATE AlbumRoots SET type=? WHERE id=?;"),
00150                     (int)newType, rootId);
00151     d->db->recordChangeset(AlbumRootChangeset(rootId, AlbumRootChangeset::PropertiesChanged));
00152 }
00153 
00154 
00155 
00156 AlbumInfo::List AlbumDB::scanAlbums()
00157 {
00158     AlbumInfo::List aList;
00159 
00160     QList<QVariant> values;
00161     d->db->execSql( "SELECT A.albumRoot, A.id, A.relativePath, A.date, A.caption, A.collection, B.albumRoot, B.relativePath, I.name \n "
00162                     "FROM Albums AS A \n "
00163                     "  LEFT JOIN Images AS I ON A.icon=I.id \n"
00164                     "  LEFT JOIN Albums AS B ON B.id=I.album \n"
00165                     " WHERE A.albumRoot != 0;", // exclude stale albums
00166                     &values);
00167 
00168     QString iconAlbumUrl, iconName;
00169 
00170     for (QList<QVariant>::const_iterator it = values.constBegin(); it != values.constEnd();)
00171     {
00172         AlbumInfo info;
00173 
00174         info.albumRootId = (*it).toInt();
00175         ++it;
00176         info.id = (*it).toInt();
00177         ++it;
00178         info.relativePath = (*it).toString();
00179         ++it;
00180         info.date = QDate::fromString((*it).toString(), Qt::ISODate);
00181         ++it;
00182         info.caption = (*it).toString();
00183         ++it;
00184         info.category = (*it).toString();
00185         ++it;
00186         info.iconAlbumRootId = (*it).toInt(); // will be 0 if null
00187         ++it;
00188         iconAlbumUrl = (*it).toString();
00189         ++it;
00190         iconName = (*it).toString();
00191         ++it;
00192 
00193         if (!iconName.isEmpty())
00194             info.iconRelativePath = iconAlbumUrl + '/' + iconName;
00195 
00196         aList.append(info);
00197     }
00198 
00199     return aList;
00200 }
00201 
00202 TagInfo::List AlbumDB::scanTags()
00203 {
00204     TagInfo::List tList;
00205 
00206     QList<QVariant> values;
00207     d->db->execSql( "SELECT T.id, T.pid, T.name, A.relativePath, I.name, T.iconkde, A.albumRoot \n "
00208                     "FROM Tags AS T \n"
00209                     "  LEFT JOIN Images AS I ON I.id=T.icon \n "
00210                     "  LEFT JOIN Albums AS A ON A.id=I.album; ", &values );
00211 
00212     QString iconName, iconKDE, albumURL;
00213     int iconAlbumRootId;
00214 
00215     for (QList<QVariant>::const_iterator it = values.constBegin(); it != values.constEnd();)
00216     {
00217         TagInfo info;
00218 
00219         info.id     = (*it).toInt();
00220         ++it;
00221         info.pid    = (*it).toInt();
00222         ++it;
00223         info.name   = (*it).toString();
00224         ++it;
00225         albumURL    = (*it).toString();
00226         ++it;
00227         iconName    = (*it).toString();
00228         ++it;
00229         iconKDE     = (*it).toString();
00230         ++it;
00231         iconAlbumRootId = (*it).toInt(); // will be 0 if null
00232         ++it;
00233 
00234         if (albumURL.isEmpty())
00235         {
00236             info.icon = iconKDE;
00237         }
00238         else
00239         {
00240             info.iconAlbumRootId  = iconAlbumRootId;
00241             info.iconRelativePath = albumURL + '/' + iconName;
00242         }
00243 
00244         tList.append(info);
00245     }
00246 
00247     return tList;
00248 }
00249 
00250 SearchInfo::List AlbumDB::scanSearches()
00251 {
00252     SearchInfo::List searchList;
00253 
00254     QList<QVariant> values;
00255     d->db->execSql( "SELECT id, type, name, query FROM Searches;", &values);
00256 
00257     for (QList<QVariant>::const_iterator it = values.constBegin(); it != values.constEnd();)
00258     {
00259         SearchInfo info;
00260 
00261         info.id    = (*it).toInt();
00262         ++it;
00263         info.type  = (DatabaseSearch::Type)(*it).toInt();
00264         ++it;
00265         info.name  = (*it).toString();
00266         ++it;
00267         info.query = (*it).toString();
00268         ++it;
00269 
00270         searchList.append(info);
00271     }
00272 
00273     return searchList;
00274 }
00275 
00276 QList<AlbumShortInfo> AlbumDB::getAlbumShortInfos()
00277 {
00278     QList<QVariant> values;
00279     d->db->execSql( QString("SELECT Albums.id, Albums.relativePath, Albums.albumRoot from Albums; "),
00280                     &values);
00281 
00282     QList<AlbumShortInfo> albumList;
00283 
00284     for (QList<QVariant>::const_iterator it = values.constBegin(); it != values.constEnd();)
00285     {
00286         AlbumShortInfo info;
00287 
00288         info.id           = (*it).toLongLong();
00289         ++it;
00290         info.relativePath = (*it).toString();
00291         ++it;
00292         info.albumRootId  = (*it).toInt();
00293         ++it;
00294 
00295         albumList << info;
00296     }
00297 
00298     return albumList;
00299 }
00300 
00301 /*
00302 QStringList AlbumDB::getSubalbumsForPath(const QString& albumRoot,
00303                                          const QString& path,
00304                                          bool onlyDirectSubalbums)
00305 {
00306     CollectionLocation location = CollectionManager::instance()->locationForAlbumRootPath(albumRoot);
00307     if (location.isNull())
00308         return QStringList();
00309 
00310     QString subURL = path;
00311     if (!path.endsWith("/"))
00312         subURL += '/';
00313     subURL = (subURL);
00314 
00315     QList<QVariant> values;
00316 
00317     if (onlyDirectSubalbums)
00318     {
00319         d->db->execSql( QString("SELECT relativePath FROM Albums WHERE albumRoot=? AND relativePath LIKE '") +
00320                         subURL + QString("%' ") + QString("AND relativePath NOT LIKE '") +
00321                         subURL + QString("%/%'; "),
00322                         location.id(),
00323                         &values );
00324     }
00325     else
00326     {
00327         d->db->execSql( QString("SELECT relativePath FROM Albums WHERE albumRoot=? AND relativePath LIKE '") +
00328                         subURL + QString("%'; "),
00329                         location.id(),
00330                         &values );
00331     }
00332 
00333     QStringList subalbums;
00334     QString albumRootPath = location.albumRootPath();
00335     for (QList<QVariant>::iterator it = values.begin(); it != values.end(); ++it)
00336         subalbums << albumRootPath + it->toString();
00337     return subalbums;
00338 }
00339 */
00340 
00341 /*
00342 int AlbumDB::addAlbum(const QString& albumRoot, const QString& relativePath,
00343                       const QString& caption,
00344                       const QDate& date, const QString& collection)
00345 {
00346     CollectionLocation location = CollectionManager::instance()->locationForAlbumRootPath(albumRoot);
00347     if (location.isNull())
00348         return -1;
00349 
00350     return addAlbum(location.id(), relativePath, caption, date, collection);
00351 }
00352 */
00353 
00354 int AlbumDB::addAlbum(int albumRootId, const QString& relativePath,
00355                       const QString& caption,
00356                       const QDate& date, const QString& collection)
00357 {
00358     QVariant id;
00359     QList<QVariant> boundValues;
00360     boundValues << albumRootId << relativePath << date.toString(Qt::ISODate) << caption << collection;
00361 
00362     d->db->execSql( QString("REPLACE INTO Albums (albumRoot, relativePath, date, caption, collection) "
00363                             "VALUES(?, ?, ?, ?, ?);"),
00364                     boundValues, 0, &id);
00365 
00366     d->db->recordChangeset(AlbumChangeset(id.toInt(), AlbumChangeset::Added));
00367     return id.toInt();
00368 }
00369 
00370 void AlbumDB::setAlbumCaption(int albumID, const QString& caption)
00371 {
00372     d->db->execSql( QString("UPDATE Albums SET caption=? WHERE id=?;"),
00373                     caption, albumID );
00374     d->db->recordChangeset(AlbumChangeset(albumID, AlbumChangeset::PropertiesChanged));
00375 }
00376 
00377 void AlbumDB::setAlbumCategory(int albumID, const QString& category)
00378 {
00379     // TODO : change "collection" propertie in DB ALbum table to "category"
00380     d->db->execSql( QString("UPDATE Albums SET collection=? WHERE id=?;"),
00381                     category, albumID );
00382     d->db->recordChangeset(AlbumChangeset(albumID, AlbumChangeset::PropertiesChanged));
00383 }
00384 
00385 void AlbumDB::setAlbumDate(int albumID, const QDate& date)
00386 {
00387     d->db->execSql( QString("UPDATE Albums SET date=? WHERE id=?;"),
00388                     date.toString(Qt::ISODate),
00389                     albumID );
00390     d->db->recordChangeset(AlbumChangeset(albumID, AlbumChangeset::PropertiesChanged));
00391 }
00392 
00393 void AlbumDB::setAlbumIcon(int albumID, qlonglong iconID)
00394 {
00395     d->db->execSql( QString("UPDATE Albums SET icon=? WHERE id=?;"),
00396                     iconID,
00397                     albumID );
00398     d->db->recordChangeset(AlbumChangeset(albumID, AlbumChangeset::PropertiesChanged));
00399 }
00400 
00401 bool AlbumDB::getAlbumIcon(int albumID, int *albumRootId, QString *iconRelativePath)
00402 {
00403     QList<QVariant> values;
00404     d->db->execSql( QString("SELECT B.relativePath, I.name, B.albumRoot \n "
00405                             "FROM Albums AS A \n "
00406                             "  LEFT JOIN Images AS I ON I.id=A.icon \n "
00407                             "  LEFT JOIN Albums AS B ON B.id=I.album \n "
00408                             "WHERE A.id=?;"),
00409                     albumID, &values );
00410     if (values.isEmpty())
00411         return false;
00412 
00413     QList<QVariant>::const_iterator it = values.constBegin();
00414     QString album     = (*it).toString();
00415     ++it;
00416     QString iconName  = (*it).toString();
00417     ++it;
00418     *albumRootId = (*it).toInt();
00419 
00420     *iconRelativePath = album + '/' + iconName;
00421 
00422     return !iconName.isEmpty();
00423 }
00424 
00425 void AlbumDB::deleteAlbum(int albumID)
00426 {
00427     d->db->execSql( QString("DELETE FROM Albums WHERE id=?;"),
00428                     albumID );
00429     d->db->recordChangeset(AlbumChangeset(albumID, AlbumChangeset::Deleted));
00430 }
00431 
00432 void AlbumDB::makeStaleAlbum(int albumID)
00433 {
00434     // We need to work around the table constraint, no we want to delete older stale albums with
00435     // the same relativePath, and adjust relativePaths depending on albumRoot.
00436     QList<QVariant> values;
00437 
00438     // retrieve information
00439     d->db->execSql( QString("SELECT Albums.albumRoot, Albums.relativePath from Albums WHERE id=?;"),
00440                     albumID, &values);
00441 
00442     if (values.isEmpty())
00443         return;
00444 
00445     // prepend albumRootId to relativePath. relativePath is unused and officially undefined after this call.
00446     QString newRelativePath = values[0].toString() + '-' + values[1].toString();
00447 
00448     // delete older stale albums
00449     d->db->execSql( QString("DELETE FROM Albums WHERE albumRoot=0 AND relativePath=?;"),
00450                     newRelativePath );
00451 
00452     // now do our update
00453     d->db->execSql( QString("UPDATE Albums SET albumRoot=0, relativePath=? WHERE id=?;"),
00454                     newRelativePath, albumID );
00455 
00456     // for now, we make no distinction to deleteAlbums wrt to changeset
00457     d->db->recordChangeset(AlbumChangeset(albumID, AlbumChangeset::Deleted));
00458 }
00459 
00460 void AlbumDB::deleteStaleAlbums()
00461 {
00462     d->db->execSql( QString("DELETE FROM Albums WHERE albumRoot=0;") );
00463     // deliberately no changeset here, is done above
00464 }
00465 
00466 int AlbumDB::addTag(int parentTagID, const QString& name, const QString& iconKDE,
00467                     qlonglong iconID)
00468 {
00469     QVariant id;
00470     if (!d->db->execSql( QString("INSERT INTO Tags (pid, name) "
00471                                  "VALUES( ?, ?);"),
00472                          parentTagID,
00473                          name,
00474                          0, &id) )
00475     {
00476         return -1;
00477     }
00478 
00479     if (!iconKDE.isEmpty())
00480     {
00481         d->db->execSql( QString("UPDATE Tags SET iconkde=? WHERE id=?;"),
00482                         iconKDE,
00483                         id.toInt() );
00484     }
00485     else
00486     {
00487         d->db->execSql( QString("UPDATE Tags SET icon=? WHERE id=?;"),
00488                         iconID,
00489                         id.toInt());
00490     }
00491 
00492     d->db->recordChangeset(TagChangeset(id.toInt(), TagChangeset::Added));
00493     return id.toInt();
00494 }
00495 
00496 void AlbumDB::deleteTag(int tagID)
00497 {
00498     d->db->execSql( QString("DELETE FROM Tags WHERE id=?;"),
00499                     tagID );
00500     d->db->recordChangeset(TagChangeset(tagID, TagChangeset::Deleted));
00501 }
00502 
00503 void AlbumDB::setTagIcon(int tagID, const QString& iconKDE, qlonglong iconID)
00504 {
00505     int     _iconID  = iconKDE.isEmpty() ? iconID : 0;
00506     QString _iconKDE = iconKDE;
00507 
00508     if (iconKDE.isEmpty() || iconKDE.toLower() == QString("tag"))
00509         _iconKDE.clear();
00510 
00511     d->db->execSql( QString("UPDATE Tags SET iconkde=?, icon=? WHERE id=?;"),
00512                     _iconKDE, _iconID, tagID );
00513 
00514     d->db->recordChangeset(TagChangeset(tagID, TagChangeset::IconChanged));
00515 }
00516 
00517 bool AlbumDB::getTagIcon(int tagID, int *iconAlbumRootId, QString *iconAlbumRelativePath, QString *icon)
00518 {
00519     QList<QVariant> values;
00520     d->db->execSql( QString("SELECT A.relativePath, I.name, T.iconkde, A.albumRoot \n "
00521                             "FROM Tags AS T \n "
00522                             "  LEFT JOIN Images AS I ON I.id=T.icon \n "
00523                             "  LEFT JOIN Albums AS A ON A.id=I.album \n "
00524                             "WHERE T.id=?;"),
00525                     tagID, &values );
00526 
00527     if (values.isEmpty())
00528         return false;
00529 
00530     QString iconName, iconKDE, albumURL;
00531 
00532     QList<QVariant>::const_iterator it = values.constBegin();
00533 
00534     albumURL    = (*it).toString();
00535     ++it;
00536     iconName    = (*it).toString();
00537     ++it;
00538     iconKDE     = (*it).toString();
00539     ++it;
00540 
00541     *iconAlbumRootId = (*it).toInt();
00542     ++it;
00543 
00544     if (albumURL.isEmpty())
00545     {
00546         *iconAlbumRelativePath = QString();
00547         *icon = iconKDE;
00548         return !iconKDE.isEmpty();
00549     }
00550     else
00551     {
00552         *iconAlbumRelativePath = albumURL + '/' + iconName;
00553         *icon = QString();
00554         return true;
00555     }
00556 }
00557 
00558 void AlbumDB::setTagParentID(int tagID, int newParentTagID)
00559 {
00560     d->db->execSql( QString("UPDATE Tags SET pid=? WHERE id=?;"),
00561                     newParentTagID, tagID );
00562     d->db->recordChangeset(TagChangeset(tagID, TagChangeset::Reparented));
00563 }
00564 
00565 int AlbumDB::addSearch(DatabaseSearch::Type type, const QString& name, const QString& query)
00566 {
00567     QVariant id;
00568     if (!d->db->execSql(QString("INSERT INTO Searches (type, name, query) VALUES(?, ?, ?);"),
00569                         type, name, query, 0, &id) )
00570     {
00571         return -1;
00572     }
00573 
00574     d->db->recordChangeset(SearchChangeset(id.toInt(), SearchChangeset::Added));
00575     return id.toInt();
00576 }
00577 
00578 void AlbumDB::updateSearch(int searchID, DatabaseSearch::Type type,
00579                            const QString& name, const QString &query)
00580 {
00581     d->db->execSql(QString("UPDATE Searches SET type=?, name=?, query=? WHERE id=?"),
00582                    type, name, query, searchID);
00583     d->db->recordChangeset(SearchChangeset(searchID, SearchChangeset::Changed));
00584 }
00585 
00586 void AlbumDB::deleteSearch(int searchID)
00587 {
00588     d->db->execSql( QString("DELETE FROM Searches WHERE id=?"),
00589                     searchID );
00590     d->db->recordChangeset(SearchChangeset(searchID, SearchChangeset::Deleted));
00591 }
00592 
00593 void AlbumDB::deleteSearches(DatabaseSearch::Type type)
00594 {
00595     d->db->execSql( QString("DELETE FROM Searches WHERE type=?"),
00596                     type );
00597     d->db->recordChangeset(SearchChangeset(0, SearchChangeset::Deleted));
00598 }
00599 
00600 QString AlbumDB::getSearchQuery(int searchId)
00601 {
00602     QList<QVariant> values;
00603     d->db->execSql( QString("SELECT query FROM Searches WHERE id=?;"),
00604                     searchId, &values );
00605 
00606     if (values.isEmpty())
00607         return QString();
00608     else
00609         return values.first().toString();
00610 }
00611 
00612 SearchInfo AlbumDB::getSearchInfo(int searchId)
00613 {
00614     SearchInfo info;
00615 
00616     QList<QVariant> values;
00617     d->db->execSql( "SELECT id, type, name, query FROM Searches WHERE id=?;",
00618                     searchId, &values);
00619 
00620     if (values.size() == 4)
00621     {
00622         QList<QVariant>::const_iterator it = values.constBegin();
00623         info.id    = (*it).toInt();
00624         ++it;
00625         info.type  = (DatabaseSearch::Type)(*it).toInt();
00626         ++it;
00627         info.name  = (*it).toString();
00628         ++it;
00629         info.query = (*it).toString();
00630         ++it;
00631     }
00632 
00633     return info;
00634 }
00635 
00636 void AlbumDB::setSetting(const QString& keyword,
00637                          const QString& value )
00638 {
00639     d->db->execSql( QString("REPLACE into Settings VALUES (?,?);"),
00640                     keyword, value );
00641 }
00642 
00643 QString AlbumDB::getSetting(const QString& keyword)
00644 {
00645     QList<QVariant> values;
00646     d->db->execSql( QString("SELECT value FROM Settings "
00647                             "WHERE keyword=?;"),
00648                     keyword, &values );
00649 
00650     if (values.isEmpty())
00651         return QString();
00652     else
00653         return values.first().toString();
00654 }
00655 
00656 // helper method
00657 static QStringList joinMainAndUserFilterString(const QString &filter, const QString &userFilter)
00658 {
00659     QSet<QString> filterSet;
00660     QStringList userFilterList;
00661 
00662     filterSet = filter.split(';', QString::SkipEmptyParts).toSet();
00663     userFilterList = userFilter.split(';', QString::SkipEmptyParts);
00664     foreach (const QString& userFormat, userFilterList)
00665     {
00666         if (userFormat.startsWith('-'))
00667             filterSet.remove(userFormat.mid(1));
00668         else
00669             filterSet << userFormat;
00670     }
00671     return filterSet.toList();
00672 }
00673 
00674 void AlbumDB::getFilterSettings(QStringList *imageFilter, QStringList *videoFilter, QStringList *audioFilter)
00675 {
00676     QString imageFormats, videoFormats, audioFormats, userImageFormats, userVideoFormats, userAudioFormats;
00677 
00678     if (imageFilter)
00679     {
00680         imageFormats = getSetting("databaseImageFormats");
00681         userImageFormats = getSetting("databaseUserImageFormats");
00682         *imageFilter = joinMainAndUserFilterString(imageFormats, userImageFormats);
00683     }
00684 
00685     if (videoFilter)
00686     {
00687         videoFormats = getSetting("databaseVideoFormats");
00688         userVideoFormats = getSetting("databaseUserVideoFormats");
00689         *videoFilter = joinMainAndUserFilterString(videoFormats, userVideoFormats);
00690     }
00691 
00692     if (audioFilter)
00693     {
00694         audioFormats = getSetting("databaseAudioFormats");
00695         userAudioFormats = getSetting("databaseUserAudioFormats");
00696         *audioFilter = joinMainAndUserFilterString(audioFormats, userAudioFormats);
00697     }
00698 }
00699 
00700 void AlbumDB::getUserFilterSettings(QString *imageFilterString, QString *videoFilterString, QString *audioFilterString)
00701 {
00702     if (imageFilterString)
00703         *imageFilterString = getSetting("databaseUserImageFormats");
00704     if (videoFilterString)
00705         *videoFilterString = getSetting("databaseUserVideoFormats");
00706     if (audioFilterString)
00707         *audioFilterString = getSetting("databaseUserAudioFormats");
00708 }
00709 
00710 void AlbumDB::setFilterSettings(const QStringList& imageFilter, const QStringList& videoFilter, const QStringList& audioFilter)
00711 {
00712     setSetting("databaseImageFormats", imageFilter.join(";"));
00713     setSetting("databaseVideoFormats", videoFilter.join(";"));
00714     setSetting("databaseAudioFormats", audioFilter.join(";"));
00715 }
00716 
00717 // helper method
00718 static QStringList cleanUserFilterString(const QString &filterString)
00719 {
00720     // splits by either ; or space, removes "*.", trims
00721     QStringList filterList;
00722 
00723     QString wildcard("*.");
00724     QString minusWildcard("-*.");
00725     QChar dot('.');
00726     QString minusDot("-.");
00727     QChar sep( ';' );
00728     int i = filterString.indexOf( sep );
00729     if ( i == -1 && filterString.indexOf( ' ') != -1 )
00730         sep = QChar( ' ' );
00731 
00732     QStringList sepList = filterString.split(sep, QString::SkipEmptyParts);
00733     foreach (const QString& f, sepList)
00734     {
00735         if (f.startsWith(wildcard))
00736             filterList << f.mid(2).trimmed().toLower();
00737         else if (f.startsWith(minusWildcard))
00738             filterList << '-' + f.mid(3).trimmed().toLower();
00739         else if (f.startsWith(dot))
00740             filterList << f.mid(1).trimmed().toLower();
00741         else if (f.startsWith(minusDot))
00742             filterList << '-' + f.mid(2).trimmed().toLower();
00743         else
00744             filterList << f.trimmed().toLower();
00745     }
00746     return filterList;
00747 }
00748 
00749 void AlbumDB::setUserFilterSettings(const QString& imageFilterString, const QString& videoFilterString, const QString& audioFilterString)
00750 {
00751     setUserFilterSettings(cleanUserFilterString(imageFilterString),
00752                           cleanUserFilterString(videoFilterString),
00753                           cleanUserFilterString(audioFilterString));
00754 }
00755 
00756 void AlbumDB::setUserFilterSettings(const QStringList& imageFilter, const QStringList& videoFilter, const QStringList& audioFilter)
00757 {
00758     setSetting("databaseUserImageFormats", imageFilter.join(";"));
00759     setSetting("databaseUserVideoFormats", videoFilter.join(";"));
00760     setSetting("databaseUserAudioFormats", audioFilter.join(";"));
00761 }
00762 
00763 void AlbumDB::addToUserImageFilterSettings(const QString& filterString)
00764 {
00765     QStringList addList = cleanUserFilterString(filterString);
00766 
00767     QStringList currentList = getSetting("databaseUserImageFormats").split(';', QString::SkipEmptyParts);
00768 
00769     // merge lists
00770     foreach(const QString& addedFilter, addList)
00771     {
00772         if (!currentList.contains(addedFilter))
00773             currentList << addedFilter;
00774     }
00775 
00776     setSetting("databaseUserImageFormats", currentList.join(";"));
00777 }
00778 
00779 QUuid AlbumDB::databaseUuid()
00780 {
00781     QString uuidString = getSetting("databaseUUID");
00782     QUuid uuid = QUuid(uuidString);
00783     if (uuidString.isNull() || uuid.isNull())
00784     {
00785         uuid = QUuid::createUuid();
00786         setSetting("databaseUUID", uuid.toString());
00787     }
00788     return uuid;
00789 }
00790 
00791 /*
00792 QString AlbumDB::getItemCaption(qlonglong imageID)
00793 {
00794     QList<QVariant> values;
00795 
00796     d->db->execSql( QString("SELECT caption FROM Images "
00797                             "WHERE id=?;"),
00798                     imageID, &values );
00799 
00800     if (!values.isEmpty())
00801         return values.first().toString();
00802     else
00803         return QString();
00804 }
00805 
00806 QString AlbumDB::getItemCaption(int albumID, const QString& name)
00807 {
00808     QList<QVariant> values;
00809 
00810     d->db->execSql( QString("SELECT caption FROM Images "
00811                             "WHERE dirid=? AND name=?;"),
00812                     albumID,
00813                     name,
00814                     &values );
00815 
00816     if (!values.isEmpty())
00817         return values.first().toString();
00818     else
00819         return QString();
00820 }
00821 
00822 
00823 QDateTime AlbumDB::getItemDate(qlonglong imageID)
00824 {
00825     QList<QVariant> values;
00826 
00827     d->db->execSql( QString("SELECT datetime FROM Images "
00828                             "WHERE id=?;"),
00829                     imageID,
00830                     &values );
00831 
00832     if (!values.isEmpty())
00833         return QDateTime::fromString(values.first().toString(), Qt::ISODate);
00834     else
00835         return QDateTime();
00836 }
00837 
00838 QDateTime AlbumDB::getItemDate(int albumID, const QString& name)
00839 {
00840     QList<QVariant> values;
00841 
00842     d->db->execSql( QString("SELECT datetime FROM Images "
00843                             "WHERE dirid=? AND name=?;"),
00844                     albumID,
00845                     name,
00846                     &values );
00847 
00848     if (values.isEmpty())
00849         return QDateTime::fromString(values.first().toString(), Qt::ISODate);
00850     else
00851         return QDateTime();
00852 }
00853 */
00854 
00855 qlonglong AlbumDB::getImageId(int albumID, const QString& name)
00856 {
00857     QList<QVariant> values;
00858 
00859     d->db->execSql( QString("SELECT id FROM Images "
00860                             "WHERE album=? AND name=?;"),
00861                     albumID,
00862                     name,
00863                     &values );
00864 
00865     if (values.isEmpty())
00866         return -1;
00867     else
00868         return values.first().toLongLong();
00869 }
00870 
00871 QStringList AlbumDB::getItemTagNames(qlonglong imageID)
00872 {
00873     QList<QVariant> values;
00874 
00875     d->db->execSql( QString("SELECT name FROM Tags \n "
00876                             "WHERE id IN (SELECT tagid FROM ImageTags \n "
00877                             "             WHERE imageid=?) \n "
00878                             "ORDER BY name;"),
00879                     imageID,
00880                     &values );
00881 
00882     QStringList names;
00883     for (QList<QVariant>::const_iterator it = values.constBegin(); it != values.constEnd(); ++it)
00884         names << it->toString();
00885     return names;
00886 }
00887 
00888 QList<int> AlbumDB::getItemTagIDs(qlonglong imageID)
00889 {
00890     QList<QVariant> values;
00891 
00892     d->db->execSql( QString("SELECT tagid FROM ImageTags \n "
00893                             "WHERE imageID=?;"),
00894                     imageID,
00895                     &values );
00896 
00897     QList<int> ids;
00898 
00899     if (values.isEmpty())
00900         return ids;
00901 
00902     for (QList<QVariant>::const_iterator it = values.constBegin(); it != values.constEnd(); ++it)
00903         ids << it->toInt();
00904     return ids;
00905 }
00906 
00907 ItemShortInfo AlbumDB::getItemShortInfo(qlonglong imageID)
00908 {
00909     QList<QVariant> values;
00910 
00911     d->db->execSql( QString("SELECT Images.name, Albums.albumRoot, Albums.relativePath, Albums.id "
00912                             "FROM Images "
00913                             "  LEFT JOIN Albums ON Albums.id=Images.album "
00914                             "WHERE Images.id=?;"),
00915                     imageID,
00916                     &values );
00917 
00918     ItemShortInfo info;
00919 
00920     if (!values.isEmpty())
00921     {
00922         info.id          = imageID;
00923         info.itemName    = values[0].toString();
00924         info.albumRootID = values[1].toInt();
00925         info.album       = values[2].toString();
00926         info.albumID     = values[3].toInt();
00927     }
00928 
00929     return info;
00930 }
00931 
00932 ItemShortInfo AlbumDB::getItemShortInfo(int albumRootId, const QString& relativePath, const QString& name)
00933 {
00934     QList<QVariant> values;
00935 
00936     d->db->execSql( QString("SELECT Images.id, Albums.id "
00937                             " FROM Images INNER JOIN Albums "
00938                             "  ON Images.album=Albums.id "
00939                             " WHERE name=? AND albumRoot=? AND relativePath=?;"),
00940                     name, albumRootId, relativePath,
00941                     &values );
00942 
00943     ItemShortInfo info;
00944 
00945     if (!values.isEmpty())
00946     {
00947         info.id          = values[0].toLongLong();
00948         info.itemName    = name;
00949         info.albumRootID = albumRootId;
00950         info.album       = relativePath;
00951         info.albumID     = values[1].toInt();
00952     }
00953 
00954     return info;
00955 }
00956 
00957 bool AlbumDB::hasTags(const QList<qlonglong>& imageIDList)
00958 {
00959     QList<int> ids;
00960 
00961     if (imageIDList.isEmpty())
00962         return false;
00963 
00964     QList<QVariant> values;
00965     QList<QVariant> boundValues;
00966 
00967     QString sql = QString("SELECT count(tagid) FROM ImageTags "
00968                           "WHERE imageid=? ");
00969     boundValues << imageIDList.first();
00970 
00971     QList<qlonglong>::const_iterator it = imageIDList.constBegin();
00972     ++it;
00973     for (; it != imageIDList.constEnd(); ++it)
00974     {
00975         sql += QString(" OR imageid=? ");
00976         boundValues << (*it);
00977     }
00978 
00979     sql += QString(";");
00980     d->db->execSql( sql, boundValues, &values );
00981 
00982     if (values.isEmpty() || values.first().toInt() == 0)
00983         return false;
00984     else
00985         return true;
00986 }
00987 
00988 QList<int> AlbumDB::getItemCommonTagIDs(const QList<qlonglong>& imageIDList)
00989 {
00990     QList<int> ids;
00991 
00992     if (imageIDList.isEmpty())
00993         return ids;
00994 
00995     QList<QVariant> values;
00996     QList<QVariant> boundValues;
00997 
00998     QString sql = QString("SELECT DISTINCT tagid FROM ImageTags "
00999                           "WHERE imageid=? ");
01000     boundValues << imageIDList.first();
01001 
01002     QList<qlonglong>::const_iterator it = imageIDList.constBegin();
01003     ++it;
01004     for (; it != imageIDList.constEnd(); ++it)
01005     {
01006         sql += QString(" OR imageid=? ");
01007         boundValues << (*it);
01008     }
01009 
01010     sql += QString(";");
01011     d->db->execSql( sql, boundValues, &values );
01012 
01013     if (values.isEmpty())
01014         return ids;
01015 
01016     for (QList<QVariant>::const_iterator it = values.constBegin(); it != values.constEnd(); ++it)
01017         ids << it->toInt();
01018     return ids;
01019 }
01020 
01021 
01022 
01023 QVariantList AlbumDB::getImagesFields(qlonglong imageID, DatabaseFields::Images fields)
01024 {
01025     QVariantList values;
01026     if (fields != DatabaseFields::ImagesNone)
01027     {
01028         QString query("SELECT ");
01029         QStringList fieldNames = imagesFieldList(fields);
01030         query += fieldNames.join(", ");
01031         query += (" FROM Images WHERE id=?;");
01032 
01033         d->db->execSql(query, imageID, &values);
01034 
01035         // Convert date times to QDateTime, they come as QString
01036         if (fields & DatabaseFields::ModificationDate && !values.isEmpty())
01037         {
01038             int index = fieldNames.indexOf("modificationDate");
01039             values[index] = (values[index].isNull() ? QDateTime()
01040                               : QDateTime::fromString(values[index].toString(), Qt::ISODate));
01041         }
01042     }
01043     return values;
01044 }
01045 
01046 QVariantList AlbumDB::getImageInformation(qlonglong imageID, DatabaseFields::ImageInformation fields)
01047 {
01048     QVariantList values;
01049     if (fields != DatabaseFields::ImageInformationNone)
01050     {
01051         QString query("SELECT ");
01052         QStringList fieldNames = imageInformationFieldList(fields);
01053         query += fieldNames.join(", ");
01054         query += (" FROM ImageInformation WHERE imageid=?;");
01055 
01056         d->db->execSql(query, imageID, &values);
01057 
01058         // Convert date times to QDateTime, they come as QString
01059         if (fields & DatabaseFields::CreationDate && !values.isEmpty())
01060         {
01061             int index = fieldNames.indexOf("creationDate");
01062             values[index] = (values[index].isNull() ? QDateTime()
01063                               : QDateTime::fromString(values[index].toString(), Qt::ISODate));
01064         }
01065         if (fields & DatabaseFields::DigitizationDate && !values.isEmpty())
01066         {
01067             int index = fieldNames.indexOf("digitizationDate");
01068             values[index] = (values[index].isNull() ? QDateTime()
01069                               : QDateTime::fromString(values[index].toString(), Qt::ISODate));
01070         }
01071     }
01072     return values;
01073 }
01074 
01075 QVariantList AlbumDB::getImageMetadata(qlonglong imageID, DatabaseFields::ImageMetadata fields)
01076 {
01077     QVariantList values;
01078     if (fields != DatabaseFields::ImageMetadataNone)
01079     {
01080         QString query("SELECT ");
01081         QStringList fieldNames = imageMetadataFieldList(fields);
01082         query += fieldNames.join(", ");
01083         query += (" FROM ImageMetadata WHERE imageid=?;");
01084 
01085         d->db->execSql(query, imageID, &values);
01086 
01087         // For some reason REAL values may come as QString QVariants. Convert here.
01088         if (values.size() == fieldNames.size() &&
01089             (fields & DatabaseFields::Aperture ||
01090              fields & DatabaseFields::FocalLength ||
01091              fields & DatabaseFields::FocalLength35 ||
01092              fields & DatabaseFields::ExposureTime ||
01093              fields & DatabaseFields::SubjectDistance)
01094            )
01095         {
01096             for (int i=0; i<values.size(); ++i)
01097             {
01098                 if (values[i].type() == QVariant::String &&
01099                     (fieldNames[i] == "aperture" ||
01100                      fieldNames[i] == "focalLength" ||
01101                      fieldNames[i] == "focalLength35" ||
01102                      fieldNames[i] == "exposureTime" ||
01103                      fieldNames[i] == "subjectDistance")
01104                    )
01105                     values[i] = values[i].toDouble();
01106             }
01107         }
01108     }
01109     return values;
01110 }
01111 
01112 QVariantList AlbumDB::getImagePosition(qlonglong imageID, DatabaseFields::ImagePositions fields)
01113 {
01114     QVariantList values;
01115     if (fields != DatabaseFields::ImagePositionsNone)
01116     {
01117         QString query("SELECT ");
01118         QStringList fieldNames = imagePositionsFieldList(fields);
01119         query += fieldNames.join(", ");
01120         query += (" FROM ImagePositions WHERE imageid=?;");
01121 
01122         d->db->execSql(query, imageID, &values);
01123 
01124         // For some reason REAL values may come as QString QVariants. Convert here.
01125         if (values.size() == fieldNames.size() &&
01126             (fields & DatabaseFields::LatitudeNumber ||
01127              fields & DatabaseFields::LongitudeNumber ||
01128              fields & DatabaseFields::Altitude ||
01129              fields & DatabaseFields::PositionOrientation ||
01130              fields & DatabaseFields::PositionTilt ||
01131              fields & DatabaseFields::PositionRoll ||
01132              fields & DatabaseFields::PositionAccuracy)
01133            )
01134         {
01135             for (int i=0; i<values.size(); ++i)
01136             {
01137                 if (values[i].type() == QVariant::String &&
01138                     (fieldNames[i] == "latitudeNumber" ||
01139                      fieldNames[i] == "longitudeNumber" ||
01140                      fieldNames[i] == "altitude" ||
01141                      fieldNames[i] == "orientation" ||
01142                      fieldNames[i] == "tilt" ||
01143                      fieldNames[i] == "roll" ||
01144                      fieldNames[i] == "accuracy")
01145                    )
01146                     values[i] = values[i].toDouble();
01147             }
01148         }
01149     }
01150     return values;
01151 }
01152 
01153 void AlbumDB::addImageInformation(qlonglong imageID, const QVariantList& infos, DatabaseFields::ImageInformation fields)
01154 {
01155     if (fields == DatabaseFields::ImageInformationNone)
01156         return;
01157 
01158     QString query("REPLACE INTO ImageInformation ( imageid, ");
01159 
01160     QStringList fieldNames = imageInformationFieldList(fields);
01161     Q_ASSERT(fieldNames.size()==infos.size());
01162     query += fieldNames.join(", ");
01163 
01164     query += " ) VALUES (";
01165     addBoundValuePlaceholders(query, infos.size() + 1);
01166     query += ");";
01167 
01168     QVariantList boundValues;
01169     boundValues << imageID;
01170     // Take care for datetime values
01171     if (fields & DatabaseFields::CreationDate || fields & DatabaseFields::DigitizationDate)
01172     {
01173         foreach (const QVariant& value, infos)
01174         {
01175             if (value.type() == QVariant::DateTime || value.type() == QVariant::Date)
01176                 boundValues << value.toDateTime().toString(Qt::ISODate);
01177             else
01178                 boundValues << value;
01179         }
01180     }
01181     else
01182         boundValues << infos;
01183 
01184     d->db->execSql( query, boundValues );
01185     d->db->recordChangeset(ImageChangeset(imageID, fields));
01186 }
01187 
01188 void AlbumDB::changeImageInformation(qlonglong imageId, const QVariantList& infos,
01189                                      DatabaseFields::ImageInformation fields)
01190 {
01191     if (fields == DatabaseFields::ImageInformationNone)
01192         return;
01193 
01194     QString query("UPDATE ImageInformation SET ");
01195 
01196     QStringList fieldNames = imageInformationFieldList(fields);
01197     Q_ASSERT(fieldNames.size()==infos.size());
01198     query += fieldNames.join("=?,");
01199 
01200     query += "=? WHERE imageid=?;";
01201 
01202     QVariantList boundValues;
01203     // Take care for datetime values
01204     if (fields & DatabaseFields::CreationDate || fields & DatabaseFields::DigitizationDate)
01205     {
01206         foreach (const QVariant& value, infos)
01207         {
01208             if (value.type() == QVariant::DateTime || value.type() == QVariant::Date)
01209                 boundValues << value.toDateTime().toString(Qt::ISODate);
01210             else
01211                 boundValues << value;
01212         }
01213         boundValues << imageId;
01214     }
01215     else
01216         boundValues << infos << imageId;
01217 
01218     d->db->execSql( query, boundValues );
01219     d->db->recordChangeset(ImageChangeset(imageId, fields));
01220 }
01221 
01222 void AlbumDB::addImageMetadata(qlonglong imageID, const QVariantList& infos, DatabaseFields::ImageMetadata fields)
01223 {
01224     if (fields == DatabaseFields::ImageMetadataNone)
01225         return;
01226 
01227     QString query("REPLACE INTO ImageMetadata ( imageid, ");
01228 
01229     QStringList fieldNames = imageMetadataFieldList(fields);
01230     Q_ASSERT(fieldNames.size()==infos.size());
01231     query += fieldNames.join(", ");
01232 
01233     query += " ) VALUES (";
01234     addBoundValuePlaceholders(query, infos.size() + 1);
01235     query += ");";
01236 
01237     QVariantList boundValues;
01238     boundValues << imageID << infos;
01239 
01240     d->db->execSql( query, boundValues );
01241     d->db->recordChangeset(ImageChangeset(imageID, fields));
01242 }
01243 
01244 void AlbumDB::changeImageMetadata(qlonglong imageId, const QVariantList& infos,
01245                                   DatabaseFields::ImageMetadata fields)
01246 {
01247     if (fields == DatabaseFields::ImageMetadataNone)
01248         return;
01249 
01250     QString query("UPDATE ImageMetadata SET ");
01251 
01252     QStringList fieldNames = imageMetadataFieldList(fields);
01253     Q_ASSERT(fieldNames.size()==infos.size());
01254     query += fieldNames.join("=?,");
01255 
01256     query += "=? WHERE imageid=?;";
01257 
01258     QVariantList boundValues;
01259     boundValues << infos << imageId;
01260 
01261     d->db->execSql( query, boundValues );
01262     d->db->recordChangeset(ImageChangeset(imageId, fields));
01263 }
01264 
01265 void AlbumDB::addImagePosition(qlonglong imageID, const QVariantList& infos, DatabaseFields::ImagePositions fields)
01266 {
01267     if (fields == DatabaseFields::ImagePositionsNone)
01268         return;
01269 
01270     QString query("REPLACE INTO ImagePositions ( imageid, ");
01271 
01272     QStringList fieldNames = imagePositionsFieldList(fields);
01273     Q_ASSERT(fieldNames.size()==infos.size());
01274     query += fieldNames.join(", ");
01275 
01276     query += " ) VALUES (";
01277     addBoundValuePlaceholders(query, infos.size() + 1);
01278     query += ");";
01279 
01280     QVariantList boundValues;
01281     boundValues << imageID << infos;
01282 
01283     d->db->execSql( query, boundValues );
01284     d->db->recordChangeset(ImageChangeset(imageID, fields));
01285 }
01286 
01287 void AlbumDB::changeImagePosition(qlonglong imageId, const QVariantList& infos,
01288                                    DatabaseFields::ImagePositions fields)
01289 {
01290     if (fields == DatabaseFields::ImagePositionsNone)
01291         return;
01292 
01293     QString query("UPDATE ImagePositions SET ");
01294 
01295     QStringList fieldNames = imagePositionsFieldList(fields);
01296     Q_ASSERT(fieldNames.size()==infos.size());
01297     query += fieldNames.join("=?,");
01298 
01299     query += "=? WHERE imageid=?;";
01300 
01301     QVariantList boundValues;
01302     boundValues << infos << imageId;
01303 
01304     d->db->execSql( query, boundValues );
01305     d->db->recordChangeset(ImageChangeset(imageId, fields));
01306 }
01307 
01308 void AlbumDB::removeImagePosition(qlonglong imageid)
01309 {
01310     d->db->execSql( QString("DELETE FROM ImagePositions WHERE imageid=?;"),
01311                     imageid );
01312 
01313     d->db->recordChangeset(ImageChangeset(imageid, DatabaseFields::ImagePositionsAll));
01314 }
01315 
01316 QList<CommentInfo> AlbumDB::getImageComments(qlonglong imageID)
01317 {
01318     QList<CommentInfo> list;
01319 
01320     QList<QVariant> values;
01321     d->db->execSql( QString("SELECT id, type, language, author, date, comment "
01322                             "FROM ImageComments WHERE imageid=?;"),
01323                     imageID, &values);
01324 
01325     for (QList<QVariant>::const_iterator it = values.constBegin(); it != values.constEnd();)
01326     {
01327         CommentInfo info;
01328         info.imageId  = imageID;
01329 
01330         info.id       = (*it).toInt();
01331         ++it;
01332         info.type     = (DatabaseComment::Type)(*it).toInt();
01333         ++it;
01334         info.language = (*it).toString();
01335         ++it;
01336         info.author   = (*it).toString();
01337         ++it;
01338         info.date     = ((*it).isNull() ? QDateTime() : QDateTime::fromString((*it).toString(), Qt::ISODate));
01339         ++it;
01340         info.comment  = (*it).toString();
01341         ++it;
01342 
01343         list << info;
01344     }
01345 
01346     return list;
01347 }
01348 
01349 int AlbumDB::setImageComment(qlonglong imageID, const QString& comment, DatabaseComment::Type type,
01350                              const QString& language, const QString& author, const QDateTime& date)
01351 {
01352     QVariantList boundValues;
01353     boundValues << imageID << (int)type << language << author << date << comment;
01354 
01355     QVariant id;
01356     d->db->execSql( QString("REPLACE INTO ImageComments "
01357                             "( imageid, type, language, author, date, comment ) "
01358                             " VALUES (?,?,?,?,?,?);"),
01359                     boundValues, 0, &id);
01360 
01361     d->db->recordChangeset(ImageChangeset(imageID, DatabaseFields::ImageCommentsAll));
01362     return id.toInt();
01363 }
01364 
01365 void AlbumDB::changeImageComment(int commentId, qlonglong imageID, const QVariantList& infos, DatabaseFields::ImageComments fields)
01366 {
01367     if (fields == DatabaseFields::ImageCommentsNone)
01368         return;
01369 
01370     QString query("UPDATE ImageComments SET ");
01371 
01372     QStringList fieldNames = imageCommentsFieldList(fields);
01373     Q_ASSERT(fieldNames.size()==infos.size());
01374     query += fieldNames.join("=?,");
01375     query += "=? WHERE id=?;";
01376 
01377     QVariantList boundValues;
01378     boundValues << infos << commentId;
01379 
01380     d->db->execSql( query, boundValues );
01381     d->db->recordChangeset(ImageChangeset(imageID, fields));
01382 }
01383 
01384 void AlbumDB::removeImageComment(int commentid, qlonglong imageid)
01385 {
01386     d->db->execSql( QString("DELETE FROM ImageComments WHERE id=?;"),
01387                     commentid );
01388 
01389     d->db->recordChangeset(ImageChangeset(imageid, DatabaseFields::ImageCommentsAll));
01390 }
01391 
01392 QString AlbumDB::getImageProperty(qlonglong imageID, const QString& property)
01393 {
01394     QList<QVariant> values;
01395 
01396     d->db->execSql( QString("SELECT value FROM ImageProperties "
01397                             "WHERE imageid=? and property=?;"),
01398                     imageID, property,
01399                     &values);
01400 
01401     if (!values.isEmpty())
01402         return values.first().toString();
01403     else
01404         return QString();
01405 }
01406 
01407 void AlbumDB::setImageProperty(qlonglong imageID, const QString& property, const QString& value)
01408 {
01409     d->db->execSql( QString ("REPLACE INTO ImageProperties "
01410                              "(imageid, property, value) "
01411                              "VALUES(?, ?, ?);"),
01412                     imageID, property, value);
01413 }
01414 
01415 void AlbumDB::removeImageProperty(qlonglong imageID, const QString& property)
01416 {
01417     d->db->execSql( QString ("DELETE FROM ImageProperties WHERE imageid=? AND property=?;"),
01418                     imageID, property);
01419 }
01420 
01421 QList<CopyrightInfo> AlbumDB::getImageCopyright(qlonglong imageID, const QString& property)
01422 {
01423     QList<CopyrightInfo> list;
01424 
01425     QList<QVariant> values;
01426     if (property.isNull())
01427     {
01428         d->db->execSql( QString("SELECT property, value, extraValue FROM ImageCopyright "
01429                                 "WHERE imageid=?;"),
01430                         imageID, &values);
01431     }
01432     else
01433     {
01434         d->db->execSql( QString("SELECT property, value, extraValue FROM ImageCopyright "
01435                                 "WHERE imageid=? and property=?;"),
01436                         imageID, property, &values);
01437     }
01438 
01439     for (QList<QVariant>::const_iterator it = values.constBegin(); it != values.constEnd();)
01440     {
01441         CopyrightInfo info;
01442         info.id = imageID;
01443 
01444         info.property   = (*it).toString();
01445         ++it;
01446         info.value      = (*it).toString();
01447         ++it;
01448         info.extraValue = (*it).toString();
01449         ++it;
01450 
01451         list << info;
01452     }
01453 
01454     return list;
01455 }
01456 
01457 void AlbumDB::setImageCopyrightProperty(qlonglong imageID, const QString& property,
01458                                         const QString& value, const QString& extraValue,
01459                                         CopyrightPropertyUnique uniqueness)
01460 {
01461     if (uniqueness == PropertyUnique)
01462     {
01463         d->db->execSql( QString("DELETE FROM ImageCopyright "
01464                                 "WHERE imageid=? AND property=?;"),
01465                         imageID, property );
01466     }
01467     else if (uniqueness == PropertyExtraValueUnique)
01468     {
01469         d->db->execSql( QString("DELETE FROM ImageCopyright "
01470                                 "WHERE imageid=? AND property=? AND extraValue=?;"),
01471                         imageID, property, extraValue );
01472     }
01473 
01474     d->db->execSql( QString("REPLACE INTO ImageCopyright "
01475                             "(imageid, property, value, extraValue) "
01476                             "VALUES(?, ?, ?, ?);"),
01477                     imageID, property, value, extraValue);
01478 }
01479 
01480 void AlbumDB::removeImageCopyrightProperties(qlonglong imageID, const QString& property,
01481                                            const QString& extraValue, const QString& value)
01482 {
01483     int removeBy = 0;
01484     if (!property.isNull())
01485         removeBy++;
01486     else if (!extraValue.isNull())
01487         removeBy++;
01488     else if (!value.isNull())
01489         removeBy++;
01490 
01491     switch (removeBy)
01492     {
01493         case 0:
01494             d->db->execSql( QString("DELETE FROM ImageCopyright "
01495                                     "WHERE imageid=?;"),
01496                             imageID );
01497             break;
01498         case 1:
01499             d->db->execSql( QString("DELETE FROM ImageCopyright "
01500                                     "WHERE imageid=? AND property=?;"),
01501                             imageID, property );
01502             break;
01503         case 2:
01504             d->db->execSql( QString("DELETE FROM ImageCopyright "
01505                                     "WHERE imageid=? AND property=? AND extraValue=?;"),
01506                             imageID, property, extraValue );
01507             break;
01508         case 3:
01509             d->db->execSql( QString("DELETE FROM ImageCopyright "
01510                                     "WHERE imageid=? AND property=? AND extraValue=? AND value=?;"),
01511                             imageID, property, extraValue, value );
01512             break;
01513     }
01514 }
01515 
01516 bool AlbumDB::hasHaarFingerprints()
01517 {
01518     QList<QVariant> values;
01519 
01520     d->db->execSql( QString("SELECT imageid FROM ImageHaarMatrix "
01521                             "WHERE matrix IS NOT NULL LIMIT 1;"),
01522                     &values);
01523 
01524     // return true if there is at least one fingerprint
01525     return !values.isEmpty();
01526 }
01527 
01528 QList<qlonglong> AlbumDB::getDirtyOrMissingFingerprints()
01529 {
01530     QList<qlonglong> itemIDs;
01531     QList<QVariant> values;
01532 
01533     d->db->execSql( QString("SELECT id FROM Images "
01534                             "LEFT JOIN ImageHaarMatrix ON Images.id=ImageHaarMatrix.imageid "
01535                             " WHERE Images.status=1 AND Images.category=1 AND "
01536                             " ( ImageHaarMatrix.imageid IS NULL "
01537                             "   OR Images.modificationDate != ImageHaarMatrix.modificationDate "
01538                             "   OR Images.uniqueHash != ImageHaarMatrix.uniqueHash ); "),
01539                     &values );
01540 
01541     for (QList<QVariant>::const_iterator it = values.constBegin(); it != values.constEnd(); ++it)
01542     {
01543         itemIDs << (*it).toLongLong();
01544     }
01545 
01546     return itemIDs;
01547 }
01548 
01549 QStringList AlbumDB::getDirtyOrMissingFingerprintURLs()
01550 {
01551     QList<QVariant> values;
01552 
01553     d->db->execSql( QString("SELECT Albums.albumRoot, Albums.relativePath, Images.name FROM Images "
01554                             "LEFT JOIN ImageHaarMatrix ON Images.id=ImageHaarMatrix.imageid "
01555                             "LEFT JOIN Albums ON Albums.id=Images.album "
01556                             " WHERE Images.status=1 AND Images.category=1 AND "
01557                             " ( ImageHaarMatrix.imageid IS NULL "
01558                             "   OR Images.modificationDate != ImageHaarMatrix.modificationDate "
01559                             "   OR Images.uniqueHash != ImageHaarMatrix.uniqueHash ); "),
01560                     &values );
01561 
01562     QStringList urls;
01563     QString albumRootPath, relativePath, name;
01564     for (QList<QVariant>::const_iterator it = values.constBegin(); it != values.constEnd();)
01565     {
01566         albumRootPath = CollectionManager::instance()->albumRootPath((*it).toInt());
01567         ++it;
01568         relativePath = (*it).toString();
01569         ++it;
01570         name = (*it).toString();
01571         ++it;
01572         if (relativePath == "/")
01573             urls << albumRootPath + relativePath + name;
01574         else
01575             urls << albumRootPath + relativePath + '/' + name;
01576     }
01577 
01578     return urls;
01579 }
01580 
01581 QList<ItemScanInfo> AlbumDB::getIdenticalFiles(qlonglong id)
01582 {
01583     if (!id)
01584         return QList<ItemScanInfo>();
01585 
01586     QList<QVariant> values;
01587 
01588     // retrieve unique hash and file size
01589     d->db->execSql( QString("SELECT uniqueHash, fileSize FROM Images WHERE id=?; "),
01590                     id,
01591                     &values );
01592 
01593     if (values.isEmpty())
01594         return QList<ItemScanInfo>();
01595 
01596     QString uniqueHash = values[0].toString();
01597     int fileSize       = values[1].toInt();
01598 
01599     return getIdenticalFiles(fileSize, uniqueHash, id);
01600 }
01601 
01602 QList<ItemScanInfo> AlbumDB::getIdenticalFiles(int fileSize, const QString& uniqueHash, qlonglong sourceId)
01603 {
01604     // enforce validity
01605     if (uniqueHash.isEmpty() || fileSize <= 0)
01606         return QList<ItemScanInfo>();
01607 
01608     QList<QVariant> values;
01609 
01610     // find items with same fingerprint
01611     d->db->execSql( QString("SELECT id, album, name, status, category, modificationDate FROM Images "
01612                             " WHERE fileSize=? AND uniqueHash=?; "),
01613                     fileSize, uniqueHash,
01614                     &values );
01615 
01616     QList<ItemScanInfo> list;
01617     for (QList<QVariant>::const_iterator it = values.constBegin(); it != values.constEnd();)
01618     {
01619         ItemScanInfo info;
01620 
01621         info.id               = (*it).toLongLong();
01622         ++it;
01623         info.albumID          = (*it).toInt();
01624         ++it;
01625         info.itemName         = (*it).toString();
01626         ++it;
01627         info.status           = (DatabaseItem::Status)(*it).toInt();
01628         ++it;
01629         info.category         = (DatabaseItem::Category)(*it).toInt();
01630         ++it;
01631         info.modificationDate = ((*it).isNull() ? QDateTime()
01632                                     : QDateTime::fromString( (*it).toString(), Qt::ISODate ));
01633         ++it;
01634 
01635         // exclude one source id from list
01636         if (sourceId == info.id)
01637             continue;
01638 
01639         // same for all here, per definition
01640         info.uniqueHash       = uniqueHash;
01641 
01642         list << info;
01643     }
01644 
01645     return list;
01646 }
01647 
01648 QStringList AlbumDB::imagesFieldList(DatabaseFields::Images fields)
01649 {
01650     // adds no spaces at beginning or end
01651     QStringList list;
01652     if (fields & DatabaseFields::Album)
01653         list << "album";
01654     if (fields & DatabaseFields::Name)
01655         list << "name";
01656     if (fields & DatabaseFields::Status)
01657         list << "status";
01658     if (fields & DatabaseFields::Category)
01659         list << "category";
01660     if (fields & DatabaseFields::ModificationDate)
01661         list << "modificationDate";
01662     if (fields & DatabaseFields::FileSize)
01663         list << "fileSize";
01664     if (fields & DatabaseFields::UniqueHash)
01665         list << "uniqueHash";
01666 
01667     return list;
01668 }
01669 
01670 QStringList AlbumDB::imageInformationFieldList(DatabaseFields::ImageInformation fields)
01671 {
01672     // adds no spaces at beginning or end
01673     QStringList list;
01674     if (fields & DatabaseFields::Rating)
01675         list << "rating";
01676     if (fields & DatabaseFields::CreationDate)
01677         list << "creationDate";
01678     if (fields & DatabaseFields::DigitizationDate)
01679         list << "digitizationDate";
01680     if (fields & DatabaseFields::Orientation)
01681         list << "orientation";
01682     if (fields & DatabaseFields::Width)
01683         list << "width";
01684     if (fields & DatabaseFields::Height)
01685         list << "height";
01686     if (fields & DatabaseFields::Format)
01687         list << "format";
01688     if (fields & DatabaseFields::ColorDepth)
01689         list << "colorDepth";
01690     if (fields & DatabaseFields::ColorModel)
01691         list << "colorModel";
01692 
01693     return list;
01694 }
01695 
01696 QStringList AlbumDB::imageMetadataFieldList(DatabaseFields::ImageMetadata fields)
01697 {
01698     // adds no spaces at beginning or end
01699     QStringList list;
01700     if (fields & DatabaseFields::Make)
01701         list << "make";
01702     if (fields & DatabaseFields::Model)
01703         list << "model";
01704     if (fields & DatabaseFields::Lens)
01705         list << "lens";
01706     if (fields & DatabaseFields::Aperture)
01707         list << "aperture";
01708     if (fields & DatabaseFields::FocalLength)
01709         list << "focalLength";
01710     if (fields & DatabaseFields::FocalLength35)
01711         list << "focalLength35";
01712     if (fields & DatabaseFields::ExposureTime)
01713         list << "exposureTime";
01714     if (fields & DatabaseFields::ExposureProgram)
01715         list << "exposureProgram";
01716     if (fields & DatabaseFields::ExposureMode)
01717         list << "exposureMode";
01718     if (fields & DatabaseFields::Sensitivity)
01719         list << "sensitivity";
01720     if (fields & DatabaseFields::FlashMode)
01721         list << "flash";
01722     if (fields & DatabaseFields::WhiteBalance)
01723         list << "whiteBalance";
01724     if (fields & DatabaseFields::WhiteBalanceColorTemperature)
01725         list << "whiteBalanceColorTemperature";
01726     if (fields & DatabaseFields::MeteringMode)
01727         list << "meteringMode";
01728     if (fields & DatabaseFields::SubjectDistance)
01729         list << "subjectDistance";
01730     if (fields & DatabaseFields::SubjectDistanceCategory)
01731         list << "subjectDistanceCategory";
01732 
01733     return list;
01734 }
01735 
01736 QStringList AlbumDB::imagePositionsFieldList(DatabaseFields::ImagePositions fields)
01737 {
01738     // adds no spaces at beginning or end
01739     QStringList list;
01740     if (fields & DatabaseFields::Latitude)
01741         list << "latitude";
01742     if (fields & DatabaseFields::LatitudeNumber)
01743         list << "latitudeNumber";
01744     if (fields & DatabaseFields::Longitude)
01745         list << "longitude";
01746     if (fields & DatabaseFields::LongitudeNumber)
01747         list << "longitudeNumber";
01748     if (fields & DatabaseFields::Altitude)
01749         list << "altitude";
01750     if (fields & DatabaseFields::PositionOrientation)
01751         list << "orientation";
01752     if (fields & DatabaseFields::PositionTilt)
01753         list << "tilt";
01754     if (fields & DatabaseFields::PositionRoll)
01755         list << "roll";
01756     if (fields & DatabaseFields::PositionAccuracy)
01757         list << "accuracy";
01758     if (fields & DatabaseFields::PositionDescription)
01759         list << "description";
01760 
01761     return list;
01762 }
01763 
01764 QStringList AlbumDB::imageCommentsFieldList(DatabaseFields::ImageComments fields)
01765 {
01766     // adds no spaces at beginning or end
01767     QStringList list;
01768     if (fields & DatabaseFields::CommentType)
01769         list << "type";
01770     if (fields & DatabaseFields::CommentLanguage)
01771         list << "language";
01772     if (fields & DatabaseFields::CommentAuthor)
01773         list << "author";
01774     if (fields & DatabaseFields::CommentDate)
01775         list << "date";
01776     if (fields & DatabaseFields::Comment)
01777         list << "comment";
01778 
01779     return list;
01780 }
01781 
01782 
01783 void AlbumDB::addBoundValuePlaceholders(QString& query, int count)
01784 {
01785     // adds no spaces at beginning or end
01786     QString questionMarks;
01787     questionMarks.reserve(count * 2);
01788     QString questionMark("?,");
01789 
01790     for (int i=0; i<count; ++i)
01791         questionMarks += questionMark;
01792     // remove last ','
01793     questionMarks.chop(1);
01794 
01795     query += questionMarks;
01796 }
01797 
01798 int AlbumDB::findInDownloadHistory(const QString& identifier, const QString& name, int fileSize, const QDateTime& date)
01799 {
01800     QList<QVariant> values;
01801     d->db->execSql( QString("SELECT id FROM DownloadHistory WHERE "
01802                             "identifier=? AND filename=? AND filesize=? AND filedate=?;"),
01803                     identifier, name, fileSize, date.toString(Qt::ISODate), &values);
01804 
01805     if (values.isEmpty())
01806         return -1;
01807     return values.first().toInt();
01808 }
01809 
01810 int AlbumDB::addToDownloadHistory(const QString& identifier, const QString& name, int fileSize, const QDateTime& date)
01811 {
01812     QVariant id;
01813     d->db->execSql( QString("REPLACE INTO DownloadHistory "
01814                             "(identifier, filename, filesize, filedate) "
01815                             "VALUES (?,?,?,?);"),
01816                     identifier, name, fileSize, date.toString(Qt::ISODate), 0, &id);
01817 
01818     return id.toInt();
01819 }
01820 
01821 
01822 
01823 /*
01824 void AlbumDB::setItemCaption(qlonglong imageID,const QString& caption)
01825 {
01826     QList<QVariant> values;
01827 
01828     d->db->execSql( QString("UPDATE Images SET caption=? "
01829                             "WHERE id=?;"),
01830                     caption,
01831                     imageID );
01832 
01833     DatabaseAccess::attributesWatch()
01834             ->sendImageFieldChanged(imageID, DatabaseAttributesWatch::ImageComment);
01835 }
01836 
01837 
01838 void AlbumDB::setItemCaption(int albumID, const QString& name, const QString& caption)
01839 {
01840     / *
01841     QList<QVariant> values;
01842 
01843     d->db->execSql( QString("UPDATE Images SET caption=? "
01844                      "WHERE dirid=? AND name=?;")
01845              .(caption,
01846                   QString::number(albumID),
01847                   (name)) );
01848     * /
01849 
01850     // easier because of attributes watch
01851     return setItemCaption(getImageId(albumID, name), caption);
01852 }
01853 */
01854 
01855 void AlbumDB::addItemTag(qlonglong imageID, int tagID)
01856 {
01857     d->db->execSql( QString("REPLACE INTO ImageTags (imageid, tagid) "
01858                             "VALUES(?, ?);"),
01859                     imageID,
01860                     tagID );
01861 
01862     d->db->recordChangeset(ImageTagChangeset(imageID, tagID, ImageTagChangeset::Added));
01863 
01864     if (!d->recentlyAssignedTags.contains(tagID))
01865     {
01866         d->recentlyAssignedTags.push_front(tagID);
01867         if (d->recentlyAssignedTags.size() > 10)
01868             d->recentlyAssignedTags.pop_back();
01869     }
01870 }
01871 
01872 void AlbumDB::addItemTag(int albumID, const QString& name, int tagID)
01873 {
01874     /*
01875     d->db->execSql( QString("REPLACE INTO ImageTags (imageid, tagid) \n "
01876                      "(SELECT id, ? FROM Images \n "
01877                      " WHERE dirid=? AND name=?);")
01878              .tagID
01879              .albumID
01880              .(name) );
01881     */
01882 
01883     // easier because of attributes watch
01884     return addItemTag(getImageId(albumID, name), tagID);
01885 }
01886 
01887 void AlbumDB::addTagsToItems(QList<qlonglong> imageIDs, QList<int> tagIDs)
01888 {
01889     QSqlQuery query = d->db->prepareQuery("REPLACE INTO ImageTags (imageid, tagid) VALUES(?, ?);");
01890 
01891     QVariantList images;
01892     QVariantList tags;
01893 
01894     foreach (const qlonglong& imageid, imageIDs)
01895     {
01896         foreach (int tagid, tagIDs)
01897         {
01898             images << imageid;
01899             tags << tagid;
01900         }
01901     }
01902 
01903     query.addBindValue(images);
01904     query.addBindValue(tags);
01905     d->db->execBatch(query);
01906     d->db->recordChangeset(ImageTagChangeset(imageIDs, tagIDs, ImageTagChangeset::Added));
01907 }
01908 
01909 QList<int> AlbumDB::getRecentlyAssignedTags() const
01910 {
01911     return d->recentlyAssignedTags;
01912 }
01913 
01914 void AlbumDB::removeItemTag(qlonglong imageID, int tagID)
01915 {
01916     d->db->execSql( QString("DELETE FROM ImageTags "
01917                             "WHERE imageID=? AND tagid=?;"),
01918                     imageID,
01919                     tagID );
01920 
01921     d->db->recordChangeset(ImageTagChangeset(imageID, tagID, ImageTagChangeset::Removed));
01922 }
01923 
01924 void AlbumDB::removeItemAllTags(qlonglong imageID, QList<int> currentTagIds)
01925 {
01926     d->db->execSql( QString("DELETE FROM ImageTags "
01927                             "WHERE imageID=?;"),
01928                     imageID );
01929 
01930     d->db->recordChangeset(ImageTagChangeset(imageID, currentTagIds, ImageTagChangeset::RemovedAll));
01931 }
01932 
01933 void AlbumDB::removeTagsFromItems(QList<qlonglong> imageIDs, QList<int> tagIDs)
01934 {
01935     QSqlQuery query = d->db->prepareQuery("DELETE FROM ImageTags WHERE imageID=? AND tagid=?;");
01936 
01937     QVariantList images;
01938     QVariantList tags;
01939 
01940     foreach (const qlonglong& imageid, imageIDs)
01941     {
01942         foreach (int tagid, tagIDs)
01943         {
01944             images << imageid;
01945             tags << tagid;
01946         }
01947     }
01948 
01949     query.addBindValue(images);
01950     query.addBindValue(tags);
01951     d->db->execBatch(query);
01952     d->db->recordChangeset(ImageTagChangeset(imageIDs, tagIDs, ImageTagChangeset::Removed));
01953 }
01954 
01955 QStringList AlbumDB::getItemNamesInAlbum(int albumID, bool recurssive)
01956 {
01957     QList<QVariant> values;
01958 
01959     if (recurssive)
01960     {
01961         int rootId = getAlbumRootId(albumID);
01962         QString path = getAlbumRelativePath(albumID);
01963         d->db->execSql( QString("SELECT Images.name FROM Images WHERE Images.album IN "
01964                                 " (SELECT DISTINCT id FROM Albums "
01965                                 "  WHERE albumRoot=? AND (relativePath=? OR relativePath LIKE ?));"),
01966                         rootId, path, path == "/" ? "/%" : path + "/%",
01967                         &values );
01968     }
01969     else
01970     {
01971         d->db->execSql( QString("SELECT Images.name "
01972                                 "FROM Images "
01973                                 "WHERE Images.album=?"),
01974                         albumID, &values );
01975     }
01976 
01977     QStringList names;
01978     for (QList<QVariant>::const_iterator it = values.constBegin(); it != values.constEnd(); ++it)
01979         names << it->toString();
01980     return names;
01981 }
01982 
01983 /*
01984 QStringList AlbumDB::getAllItemURLsWithoutDate()
01985 {
01986     QList<QVariant> values;
01987     d->db->execSql( QString("SELECT AlbumRoots.absolutePath||Albums.relativePath||'/'||Images.name "
01988                             "FROM Images "
01989                             "  LEFT JOIN Albums ON Images.album=Albums.id "
01990                             "  LEFT JOIN AlbumRoots ON AlbumRoots.id=Albums.albumRoot "
01991                             "WHERE (Images.datetime is null or "
01992                             "       Images.datetime == '');"),
01993                     &values );
01994 
01995     QStringList urls;
01996     for (QList<QVariant>::iterator it = values.begin(); it != values.end(); ++it)
01997         urls << it->toString();
01998 
01999     return urls;
02000 }
02001 */
02002 
02003 QList<QDateTime> AlbumDB::getAllCreationDates()
02004 {
02005     QList<QVariant> values;
02006     d->db->execSql( "SELECT creationDate FROM ImageInformation "
02007                     " INNER JOIN Images ON Images.id=ImageInformation.imageid "
02008                     " WHERE Images.status=1;", &values );
02009 
02010     QList<QDateTime> list;
02011     foreach (const QVariant& value, values)
02012     {
02013         if (!value.isNull())
02014             list << QDateTime::fromString(value.toString(), Qt::ISODate);
02015     }
02016     return list;
02017 }
02018 
02019 QMap<QDateTime, int> AlbumDB::getAllCreationDatesAndNumberOfImages()
02020 {
02021     QList<QVariant> values;
02022     d->db->execSql( "SELECT creationDate FROM ImageInformation "
02023                     " INNER JOIN Images ON Images.id=ImageInformation.imageid "
02024                     " WHERE Images.status=1;", &values );
02025 
02026     QMap<QDateTime, int> datesStatMap;
02027     foreach (const QVariant& value, values)
02028     {
02029         if (!value.isNull())
02030         {
02031             QDateTime dateTime = QDateTime::fromString(value.toString(), Qt::ISODate);
02032             if ( !dateTime.isValid() )
02033                 continue;
02034 
02035             QMap<QDateTime, int>::iterator it2 = datesStatMap.find(dateTime);
02036             if ( it2 == datesStatMap.end() )
02037                 datesStatMap.insert( dateTime, 1 );
02038             else
02039                 it2.value()++;
02040         }
02041     }
02042     return datesStatMap;
02043 }
02044 
02045 QMap<int, int> AlbumDB::getNumberOfImagesInAlbums()
02046 {
02047     QList<QVariant> values, allAbumIDs;
02048     QMap<int, int>  albumsStatMap;
02049     int             albumID;
02050 
02051     // initialize allAbumIDs with all existing albums from db to prevent
02052     // wrong album image counters
02053     d->db->execSql("SELECT id from Albums", &allAbumIDs);
02054 
02055     for (QList<QVariant>::const_iterator it = allAbumIDs.constBegin(); it != allAbumIDs.constEnd(); ++it)
02056     {
02057         albumID = (*it).toInt();
02058         albumsStatMap.insert(albumID, 0);
02059     }
02060 
02061     d->db->execSql( "SELECT album FROM Images WHERE Images.status=1;", &values );
02062 
02063     for (QList<QVariant>::const_iterator it = values.constBegin(); it != values.constEnd();)
02064     {
02065         albumID = (*it).toInt();
02066         ++it;
02067 
02068         QMap<int, int>::iterator it2 = albumsStatMap.find(albumID);
02069         if ( it2 == albumsStatMap.end() )
02070             albumsStatMap.insert(albumID, 1);
02071         else
02072             it2.value()++;
02073     }
02074 
02075     return albumsStatMap;
02076 }
02077 
02078 QMap<int, int> AlbumDB::getNumberOfImagesInTags()
02079 {
02080     QList<QVariant> values, allTagIDs;
02081     QMap<int, int>  tagsStatMap;
02082     int             tagID;
02083 
02084     // initialize allTagIDs with all existing tags from db to prevent
02085     // wrong tag counters
02086     d->db->execSql(QString("SELECT id from Tags"), &allTagIDs);
02087 
02088     for (QList<QVariant>::const_iterator it = allTagIDs.constBegin(); it != allTagIDs.constEnd(); ++it)
02089     {
02090         tagID = (*it).toInt();
02091         tagsStatMap.insert(tagID, 0);
02092     }
02093 
02094     d->db->execSql( "SELECT tagid FROM ImageTags "
02095                     " LEFT JOIN Images ON Images.id=ImageTags.imageid "
02096                     " WHERE Images.status=1;", &values );
02097 
02098     for (QList<QVariant>::const_iterator it=values.constBegin(); it != values.constEnd();)
02099     {
02100         tagID = (*it).toInt();
02101         ++it;
02102 
02103         QMap<int, int>::iterator it2 = tagsStatMap.find(tagID);
02104         if ( it2 == tagsStatMap.end() )
02105             tagsStatMap.insert(tagID, 1);
02106         else
02107             it2.value()++;
02108     }
02109 
02110     return tagsStatMap;
02111 }
02112 
02113 QMap<QString,int> AlbumDB::getImageFormatStatistics()
02114 {
02115     QMap<QString, int>  map;
02116 
02117     QSqlQuery query;
02118     query = d->db->prepareQuery("SELECT COUNT(*), II.format "
02119                                 "FROM ImageInformation AS II "
02120                                 "   INNER JOIN Images ON II.imageid=images.id "
02121                                 "WHERE Images.status=1 "
02122                                 "GROUP BY II.format;");
02123     if (d->db->exec(query))
02124     {
02125         while (query.next())
02126         {
02127             QString quantity = query.value(0).toString();
02128             QString format   = query.value(1).toString();
02129             if (format.isEmpty())
02130                 continue;
02131             map[format] = quantity.isEmpty() ? 0 : quantity.toInt();
02132         }
02133     }
02134 
02135     return map;
02136 }
02137 
02138 /*
02139 QList<QPair<QString, QDateTime> > AlbumDB::getItemsAndDate()
02140 {
02141     QList<QVariant> values;
02142     d->db->execSql( "SELECT Images.name, datetime FROM Images;", &values );
02143 
02144     QList<QPair<QString, QDateTime> > data;
02145     for ( QList<QVariant>::iterator it = values.begin(); it != values.end(); )
02146     {
02147         QPair<QString, QDateTime> pair;
02148         pair.first  = (*it).toString();
02149         ++it;
02150         pair.second = QDateTime::fromString( (*it).toString(),  Qt::ISODate );
02151         ++it;
02152 
02153         if (!pair.second.isValid())
02154             continue;
02155 
02156         data << pair;
02157     }
02158     return data;
02159 }
02160 */
02161 
02162 /*
02163 int AlbumDB::getAlbumForPath(const QString& albumRoot, const QString& folder, bool create)
02164 {
02165     CollectionLocation location = CollectionManager::instance()->locationForAlbumRootPath(albumRoot);
02166     if (location.isNull())
02167         return -1;
02168 
02169     return getAlbumForPath(location.id(), folder, create);
02170 
02171 }
02172 */
02173 
02174 int AlbumDB::getAlbumForPath(int albumRootId, const QString& folder, bool create)
02175 {
02176     QList<QVariant> values;
02177     d->db->execSql( QString("SELECT id FROM Albums WHERE albumRoot=? AND relativePath=?;"),
02178                     albumRootId, folder, &values);
02179 
02180     int albumID = -1;
02181     if (values.isEmpty())
02182     {
02183         if (create)
02184             albumID = addAlbum(albumRootId, folder, QString(), QDate::currentDate(), QString());
02185     }
02186     else
02187     {
02188         albumID = values.first().toInt();
02189     }
02190 
02191     return albumID;
02192 }
02193 
02194 QList<int> AlbumDB::getAlbumAndSubalbumsForPath(int albumRootId, const QString& relativePath)
02195 {
02196     QList<QVariant> values;
02197     d->db->execSql( QString("SELECT id FROM Albums WHERE albumRoot=? AND (relativePath=? OR relativePath LIKE ?);"),
02198                     albumRootId, relativePath, (relativePath == "/" ? "/%" : relativePath + "/%"), &values);
02199 
02200     QList<int> albumIds;
02201     for (QList<QVariant>::const_iterator it = values.constBegin(); it != values.constEnd(); ++it)
02202     {
02203         albumIds << (*it).toInt();
02204     }
02205     return albumIds;
02206 }
02207 
02208 QList<int> AlbumDB::getAlbumsOnAlbumRoot(int albumRootId)
02209 {
02210     QList<QVariant> values;
02211     d->db->execSql( QString("SELECT id FROM Albums WHERE albumRoot=?;"),
02212                     albumRootId, &values);
02213 
02214     QList<int> albumIds;
02215     for (QList<QVariant>::const_iterator it = values.constBegin(); it != values.constEnd(); ++it)
02216     {
02217         albumIds << (*it).toInt();
02218     }
02219     return albumIds;
02220 }
02221 
02222 qlonglong AlbumDB::addItem(int albumID, const QString& name,
02223                            DatabaseItem::Status status,
02224                            DatabaseItem::Category category,
02225                            const QDateTime& modificationDate,
02226                            int fileSize,
02227                            const QString& uniqueHash)
02228 {
02229     QVariantList boundValues;
02230     boundValues << albumID << name << (int)status << (int)category
02231                 << modificationDate.toString(Qt::ISODate) << fileSize << uniqueHash;
02232 
02233     QVariant id;
02234     d->db->execSql ( QString ("REPLACE INTO Images "
02235                               " ( album, name, status, category, modificationDate, fileSize, uniqueHash ) "
02236                               " VALUES (?,?,?,?,?,?,?);" ),
02237                      boundValues,
02238                      0, &id);
02239 
02240     if (id.isNull())
02241         return -1;
02242     d->db->recordChangeset(ImageChangeset(id.toLongLong(), DatabaseFields::ImagesAll));
02243     d->db->recordChangeset(CollectionImageChangeset(id.toLongLong(), albumID, CollectionImageChangeset::Added));
02244     return id.toLongLong();
02245 }
02246 
02247 void AlbumDB::updateItem(qlonglong imageID, DatabaseItem::Category category,
02248                          const QDateTime& modificationDate,
02249                          int fileSize, const QString& uniqueHash)
02250 {
02251     QVariantList boundValues;
02252     boundValues << category << modificationDate << fileSize << uniqueHash << imageID;
02253     d->db->execSql( QString("UPDATE Images SET category=?, modificationDate=?, fileSize=?, uniqueHash=? WHERE id=?;"),
02254                     boundValues );
02255     d->db->recordChangeset(ImageChangeset(imageID, DatabaseFields::Category
02256                                                  | DatabaseFields::ModificationDate
02257                                                  | DatabaseFields::FileSize
02258                                                  | DatabaseFields::UniqueHash ));
02259 }
02260 
02261 QList<int> AlbumDB::getTagsFromTagPaths(const QStringList& keywordsList, bool create)
02262 {
02263     if (keywordsList.isEmpty())
02264         return QList<int>();
02265 
02266     QList<int> tagIDs;
02267 
02268     QStringList keywordsList2Create;
02269 
02270     // Create a list of the tags currently in database
02271 
02272     TagInfo::List currentTagsList;
02273 
02274     QList<QVariant> values;
02275     d->db->execSql( "SELECT id, pid, name FROM Tags;", &values );
02276 
02277     for (QList<QVariant>::const_iterator it = values.constBegin(); it != values.constEnd();)
02278     {
02279         TagInfo info;
02280 
02281         info.id   = (*it).toInt();
02282         ++it;
02283         info.pid  = (*it).toInt();
02284         ++it;
02285         info.name = (*it).toString();
02286         ++it;
02287         currentTagsList.append(info);
02288     }
02289 
02290     // For every tag in keywordsList, scan taglist to check if tag already exists.
02291 
02292     for (QStringList::const_iterator kwd = keywordsList.constBegin();
02293         kwd != keywordsList.constEnd(); ++kwd )
02294     {
02295         // split full tag "url" into list of single tag names
02296         QStringList tagHierarchy = (*kwd).split('/', QString::SkipEmptyParts);
02297         if (tagHierarchy.isEmpty())
02298             continue;
02299 
02300         // last entry in list is the actual tag name
02301         bool foundTag   = false;
02302         QString tagName = tagHierarchy.back();
02303         tagHierarchy.pop_back();
02304 
02305         for (TagInfo::List::const_iterator tag = currentTagsList.constBegin();
02306             tag != currentTagsList.constEnd(); ++tag )
02307         {
02308             // There might be multiple tags with the same name, but in different
02309             // hierarchies. We must check them all until we find the correct hierarchy
02310             if ((*tag).name == tagName)
02311             {
02312                 int parentID = (*tag).pid;
02313 
02314                 // Check hierarchy, from bottom to top
02315                 bool foundParentTag                 = true;
02316                 QStringList::iterator parentTagName = tagHierarchy.end();
02317 
02318                 while (foundParentTag && parentTagName != tagHierarchy.begin())
02319                 {
02320                     --parentTagName;
02321 
02322                     foundParentTag = false;
02323 
02324                     for (TagInfo::List::const_iterator parentTag = currentTagsList.constBegin();
02325                         parentTag != currentTagsList.constEnd(); ++parentTag )
02326                     {
02327                         // check if name is the same, and if ID is identical
02328                         // to the parent ID we got from the child tag
02329                         if ( (*parentTag).id == parentID &&
02330                             (*parentTag).name == (*parentTagName) )
02331                         {
02332                             parentID       = (*parentTag).pid;
02333                             foundParentTag = true;
02334                             break;
02335                         }
02336                     }
02337 
02338                     // If we traversed the list without a match,
02339                     // foundParentTag will be false, the while loop breaks.
02340                 }
02341 
02342                 // If we managed to traverse the full hierarchy,
02343                 // we have our tag.
02344                 if (foundParentTag)
02345                 {
02346                     // add to result list
02347                     tagIDs.append((*tag).id);
02348                     foundTag = true;
02349                     break;
02350                 }
02351             }
02352         }
02353 
02354         if (!foundTag)
02355             keywordsList2Create.append(*kwd);
02356     }
02357 
02358     // If tags do not exist in database, create them.
02359 
02360     if (create && !keywordsList2Create.isEmpty())
02361     {
02362         for (QStringList::const_iterator kwd = keywordsList2Create.constBegin();
02363             kwd != keywordsList2Create.constEnd(); ++kwd )
02364         {
02365             // split full tag "url" into list of single tag names
02366             QStringList tagHierarchy = (*kwd).split('/', QString::SkipEmptyParts);
02367 
02368             if (tagHierarchy.isEmpty())
02369                 continue;
02370 
02371             int  parentTagID      = 0;
02372             int  tagID            = 0;
02373             bool parentTagExisted = true;
02374 
02375             // Traverse hierarchy from top to bottom
02376             for (QStringList::const_iterator tagName = tagHierarchy.constBegin();
02377                 tagName != tagHierarchy.constEnd(); ++tagName)
02378             {
02379                 tagID = 0;
02380 
02381                 // if the parent tag did not exist, we need not check if the child exists
02382                 if (parentTagExisted)
02383                 {
02384                     for (TagInfo::List::const_iterator tag = currentTagsList.constBegin();
02385                         tag != currentTagsList.constEnd(); ++tag )
02386                     {
02387                         // find the tag with tag name according to tagHierarchy,
02388                         // and parent ID identical to the ID of the tag we found in
02389                         // the previous run.
02390                         if ((*tag).name == (*tagName) && (*tag).pid == parentTagID)
02391                         {
02392                             tagID = (*tag).id;
02393                             break;
02394                         }
02395                     }
02396                 }
02397 
02398                 if (tagID != 0)
02399                 {
02400                     // tag already found in DB
02401                     parentTagID = tagID;
02402                     continue;
02403                 }
02404 
02405                 // Tag does not yet exist in DB, add it
02406                 tagID = addTag(parentTagID, (*tagName), QString(), 0);
02407 
02408                 if (tagID == -1)
02409                 {
02410                     // Something is wrong in database. Abort.
02411                     break;
02412                 }
02413 
02414                 // append to our list of existing tags (for following keywords)
02415                 TagInfo info;
02416                 info.id   = tagID;
02417                 info.pid  = parentTagID;
02418                 info.name = (*tagName);
02419                 currentTagsList.append(info);
02420 
02421                 parentTagID      = tagID;
02422                 parentTagExisted = false;
02423             }
02424 
02425             // add to result list
02426             tagIDs.append(tagID);
02427         }
02428     }
02429 
02430     return tagIDs;
02431 }
02432 
02433 int AlbumDB::getItemAlbum(qlonglong imageID)
02434 {
02435     QList<QVariant> values;
02436 
02437     d->db->execSql(QString("SELECT album FROM Images WHERE id=?;"),
02438                    imageID, &values);
02439 
02440     if (!values.isEmpty())
02441         return values.first().toInt();
02442     else
02443         return 1;
02444 }
02445 
02446 QString AlbumDB::getItemName(qlonglong imageID)
02447 {
02448     QList<QVariant> values;
02449 
02450     d->db->execSql(QString("SELECT name FROM Images WHERE id=?;"),
02451                    imageID, &values);
02452 
02453     if (!values.isEmpty())
02454         return values.first().toString();
02455     else
02456         return QString();
02457 }
02458 
02459 /*
02460 bool AlbumDB::setItemDate(qlonglong imageID,
02461                           const QDateTime& datetime)
02462 {
02463     d->db->execSql ( QString ("UPDATE Images SET datetime=?"
02464                             "WHERE id=?;"),
02465                      datetime.toString(Qt::ISODate),
02466                      imageID );
02467 
02468     DatabaseAccess::attributesWatch()
02469             ->sendImageFieldChanged(imageID, DatabaseAttributesWatch::ImageDate);
02470 
02471     return true;
02472 }
02473 
02474 bool AlbumDB::setItemDate(int albumID, const QString& name,
02475                           const QDateTime& datetime)
02476 {
02477     / *
02478     d->db->execSql ( QString ("UPDATE Images SET datetime=?"
02479                        "WHERE dirid=? AND name=?;")
02480               .datetime.toString(Qt::ISODate,
02481                    QString::number(albumID),
02482                    (name)) );
02483 
02484     return true;
02485     * /
02486     // easier because of attributes watch
02487     return setItemDate(getImageId(albumID, name), datetime);
02488 }
02489 
02490 
02491 void AlbumDB::setItemRating(qlonglong imageID, int rating)
02492 {
02493     d->db->execSql ( QString ("REPLACE INTO ImageProperties "
02494                             "(imageid, property, value) "
02495                             "VALUES(?, ?, ?);"),
02496                      imageID,
02497                      QString("Rating"),
02498                      rating );
02499 
02500     DatabaseAccess::attributesWatch()
02501             ->sendImageFieldChanged(imageID, DatabaseAttributesWatch::ImageRating);
02502 }
02503 
02504 int AlbumDB::getItemRating(qlonglong imageID)
02505 {
02506     QList<QVariant> values;
02507 
02508     d->db->execSql( QString("SELECT value FROM ImageProperties "
02509                             "WHERE imageid=? and property=?;"),
02510                     imageID,
02511                     QString("Rating"),
02512                     &values);
02513 
02514     if (!values.isEmpty())
02515         return values.first().toInt();
02516     else
02517         return 0;
02518 }
02519 */
02520 
02521 QStringList AlbumDB::getItemURLsInAlbum(int albumID, ItemSortOrder sortOrder)
02522 {
02523     QList<QVariant> values;
02524 
02525     int albumRootId = getAlbumRootId(albumID);
02526     if (albumRootId == -1)
02527         return QStringList();
02528     QString albumRootPath = CollectionManager::instance()->albumRootPath(albumRootId);
02529     if (albumRootPath.isNull())
02530         return QStringList();
02531 
02532     QString sqlString;
02533     switch(sortOrder)
02534     {
02535         case ByItemName:
02536             sqlString = QString("SELECT Albums.relativePath, Images.name "
02537                                  "FROM Images INNER JOIN Albums ON Albums.id=Images.album "
02538                                  "WHERE Albums.id=? "
02539                                  "ORDER BY Images.name COLLATE NOCASE;");
02540             break;
02541         case ByItemPath:
02542             // Don't collate on the path - this is to maintain the same behavior
02543             // that happens when sort order is "By Path"
02544             sqlString = QString("SELECT Albums.relativePath, Images.name "
02545                                  "FROM Images INNER JOIN Albums ON Albums.id=Images.album "
02546                                  "WHERE Albums.id=? "
02547                                  "ORDER BY Albums.relativePath,Images.name;");
02548             break;
02549         case ByItemDate:
02550             sqlString = QString("SELECT Albums.relativePath, Images.name "
02551                                  "FROM Images INNER JOIN Albums ON Albums.id=Images.album "
02552                                  "            INNER JOIN ImageInformation ON ImageInformation.imageid=Images.id "
02553                                  "WHERE Albums.id=? "
02554                                  "ORDER BY ImageInformation.creationDate;");
02555             break;
02556         case ByItemRating:
02557             sqlString = QString("SELECT Albums.relativePath, Images.name "
02558                                  "FROM Images INNER JOIN Albums ON Albums.id=Images.album "
02559                                  "            INNER JOIN ImageInformation ON ImageInformation.imageid=Images.id "
02560                                  "WHERE Albums.id=? "
02561                                  "ORDER BY ImageInformation.rating DESC;");
02562             break;
02563         case NoItemSorting:
02564         default:
02565             sqlString = QString("SELECT Albums.relativePath, Images.name "
02566                                  "FROM Images INNER JOIN Albums ON Albums.id=Images.album "
02567                                  "WHERE Albums.id=?;");
02568             break;
02569     }
02570     // all statements take one bound value
02571     d->db->execSql(sqlString, albumID, &values);
02572 
02573     QStringList urls;
02574     QString relativePath, name;
02575     for (QList<QVariant>::const_iterator it = values.constBegin(); it != values.constEnd();)
02576     {
02577         relativePath = (*it).toString();
02578         ++it;
02579         name = (*it).toString();
02580         ++it;
02581         if (relativePath == "/")
02582             urls << albumRootPath + relativePath + name;
02583         else
02584             urls << albumRootPath + relativePath + '/' + name;
02585     }
02586 
02587     return urls;
02588 }
02589 
02590 QList<qlonglong> AlbumDB::getItemIDsInAlbum(int albumID)
02591 {
02592     QList<qlonglong> itemIDs;
02593     QList<QVariant> values;
02594 
02595     d->db->execSql( QString("SELECT id FROM Images WHERE album=?;"),
02596              albumID, &values );
02597 
02598     for (QList<QVariant>::const_iterator it = values.constBegin(); it != values.constEnd(); ++it)
02599     {
02600         itemIDs << (*it).toLongLong();
02601     }
02602 
02603     return itemIDs;
02604 }
02605 
02606 QMap<qlonglong, QString> AlbumDB::getItemIDsAndURLsInAlbum(int albumID)
02607 {
02608     int albumRootId = getAlbumRootId(albumID);
02609     if (albumRootId == -1)
02610         return QMap<qlonglong, QString>();
02611 
02612     QString albumRootPath = CollectionManager::instance()->albumRootPath(albumRootId);
02613     if (albumRootPath.isNull())
02614         return QMap<qlonglong, QString>();
02615 
02616     QMap<qlonglong, QString> itemsMap;
02617     QList<QVariant> values;
02618 
02619     d->db->execSql( QString("SELECT Images.id, Albums.relativePath, Images.name "
02620                             "FROM Images JOIN Albums ON Albums.id=Images.album "
02621                             "WHERE Albums.id=?;"),
02622                     albumID, &values );
02623 
02624     QString   path;
02625     qlonglong id;
02626     QString relativePath, name;
02627     for (QList<QVariant>::const_iterator it = values.constBegin(); it != values.constEnd();)
02628     {
02629         id = (*it).toLongLong();
02630         ++it;
02631         relativePath = (*it).toString();
02632         ++it;
02633         name = (*it).toString();
02634         ++it;
02635         if (relativePath == "/")
02636             path = albumRootPath + relativePath + name;
02637         else
02638             path = albumRootPath + relativePath + '/' + name;
02639 
02640         itemsMap.insert(id, path);
02641     };
02642 
02643     return itemsMap;
02644 }
02645 
02646 QList<ItemScanInfo> AlbumDB::getItemScanInfos(int albumID)
02647 {
02648     QList<QVariant> values;
02649 
02650     d->db->execSql( QString("SELECT id, album, name, status, category, modificationDate, uniqueHash "
02651                             "FROM Images WHERE album=?;"),
02652                     albumID,
02653                     &values );
02654 
02655     QList<ItemScanInfo> list;
02656 
02657     for (QList<QVariant>::const_iterator it = values.constBegin(); it != values.constEnd();)
02658     {
02659         ItemScanInfo info;
02660 
02661         info.id               = (*it).toLongLong();
02662         ++it;
02663         info.albumID          = (*it).toInt();
02664         ++it;
02665         info.itemName         = (*it).toString();
02666         ++it;
02667         info.status           = (DatabaseItem::Status)(*it).toInt();
02668         ++it;
02669         info.category         = (DatabaseItem::Category)(*it).toInt();
02670         ++it;
02671         info.modificationDate = ((*it).isNull() ? QDateTime()
02672                                     : QDateTime::fromString( (*it).toString(), Qt::ISODate ));
02673         ++it;
02674         info.uniqueHash       = (*it).toString();
02675         ++it;
02676 
02677         list << info;
02678     }
02679 
02680     return list;
02681 }
02682 
02683 ItemScanInfo AlbumDB::getItemScanInfo(qlonglong imageID)
02684 {
02685     QList<QVariant> values;
02686 
02687     d->db->execSql( QString("SELECT id, album, name, status, category, modificationDate, uniqueHash "
02688                             "FROM Images WHERE id=?;"),
02689                     imageID,
02690                     &values );
02691 
02692     ItemScanInfo info;
02693 
02694     if (!values.isEmpty())
02695     {
02696         QList<QVariant>::const_iterator it = values.constBegin();
02697 
02698         info.id               = (*it).toLongLong();
02699         ++it;
02700         info.albumID          = (*it).toInt();
02701         ++it;
02702         info.itemName         = (*it).toString();
02703         ++it;
02704         info.status           = (DatabaseItem::Status)(*it).toInt();
02705         ++it;
02706         info.category         = (DatabaseItem::Category)(*it).toInt();
02707         ++it;
02708         info.modificationDate = ((*it).isNull() ? QDateTime()
02709             : QDateTime::fromString( (*it).toString(), Qt::ISODate ));
02710         ++it;
02711         info.uniqueHash       = (*it).toString();
02712         ++it;
02713     }
02714 
02715     return info;
02716 }
02717 
02718 QStringList AlbumDB::getItemURLsInTag(int tagID, bool recursive)
02719 {
02720     QList<QVariant> values;
02721 
02722     QString imagesIdClause;
02723     QList<QVariant> boundValues;
02724     if (recursive)
02725     {
02726         imagesIdClause = QString("SELECT imageid FROM ImageTags "
02727                                  " WHERE tagid=? "
02728                                  " OR tagid IN (SELECT id FROM TagsTree WHERE pid=?)");
02729         boundValues << tagID << tagID;
02730     }
02731     else
02732     {
02733         imagesIdClause = QString("SELECT imageid FROM ImageTags WHERE tagid=?");
02734         boundValues << tagID;
02735     }
02736 
02737     d->db->execSql( QString("SELECT Albums.albumRoot, Albums.relativePath, Images.name "
02738                             "FROM Images JOIN Albums ON Albums.id=Images.album "
02739                             "WHERE Images.status=1 AND Images.id IN (%1);")
02740                     .arg(imagesIdClause), boundValues, &values );
02741 
02742     QStringList urls;
02743     QString albumRootPath, relativePath, name;
02744     for (QList<QVariant>::const_iterator it = values.constBegin(); it != values.constEnd();)
02745     {
02746         albumRootPath = CollectionManager::instance()->albumRootPath((*it).toInt());
02747         ++it;
02748         relativePath = (*it).toString();
02749         ++it;
02750         name = (*it).toString();
02751         ++it;
02752         if (relativePath == "/")
02753             urls << albumRootPath + relativePath + name;
02754         else
02755             urls << albumRootPath + relativePath + '/' + name;
02756     }
02757 
02758     return urls;
02759 }
02760 
02761 QList<qlonglong> AlbumDB::getItemIDsInTag(int tagID, bool recursive)
02762 {
02763     QList<qlonglong> itemIDs;
02764     QList<QVariant> values;
02765 
02766     if (recursive)
02767         d->db->execSql( QString("SELECT imageid FROM ImageTags JOIN Images ON ImageTags.imageid=Images.id "
02768                                 " WHERE Images.status=1 AND "
02769                                 " ( tagid=? "
02770                                 "   OR tagid IN (SELECT id FROM TagsTree WHERE pid=?) );"),
02771                         tagID, tagID, &values );
02772     else
02773         d->db->execSql( QString("SELECT imageid FROM ImageTags JOIN Images ON ImageTags.imageid=Images.id "
02774                                 " WHERE Images.status=1 AND tagid=?;"),
02775                  tagID, &values );
02776 
02777     for (QList<QVariant>::const_iterator it = values.constBegin(); it != values.constEnd(); ++it)
02778     {
02779         itemIDs << (*it).toLongLong();
02780     }
02781 
02782     return itemIDs;
02783 }
02784 
02785 /*
02786 QString AlbumDB::getAlbumPath(int albumID)
02787 {
02788     QList<QVariant> values;
02789     d->db->execSql( QString("SELECT AlbumRoots.absolutePath||Albums.relativePath "
02790                             "FROM Albums, AlbumRoots WHERE Albums.id=? AND AlbumRoots.id=Albums.albumRoot; "),
02791                     albumID, &values);
02792     if (!values.isEmpty())
02793         return values.first().toString();
02794     else
02795         return QString();
02796 }
02797 */
02798 
02799 QString AlbumDB::getAlbumRelativePath(int albumID)
02800 {
02801     QList<QVariant> values;
02802     d->db->execSql( QString("SELECT relativePath from Albums WHERE id=?"),
02803                     albumID, &values);
02804     if (!values.isEmpty())
02805         return values.first().toString();
02806     else
02807         return QString();
02808 }
02809 
02810 int AlbumDB::getAlbumRootId(int albumID)
02811 {
02812     QList<QVariant> values;
02813     d->db->execSql( QString("SELECT albumRoot FROM Albums WHERE id=?; "),
02814                     albumID, &values);
02815     if (!values.isEmpty())
02816         return values.first().toInt();
02817     else
02818         return -1;
02819 }
02820 
02821 QDate AlbumDB::getAlbumLowestDate(int albumID)
02822 {
02823     QList<QVariant> values;
02824     d->db->execSql( "SELECT MIN(creationDate) FROM ImageInformation "
02825                     " INNER JOIN Images ON Images.id=ImageInformation.imageid "
02826                     " WHERE Images.album=? GROUP BY Images.album;",
02827                     albumID, &values );
02828     if (!values.isEmpty())
02829         return QDate::fromString( values.first().toString(), Qt::ISODate );
02830     else
02831         return QDate();
02832 }
02833 
02834 QDate AlbumDB::getAlbumHighestDate(int albumID)
02835 {
02836     QList<QVariant> values;
02837     d->db->execSql( "SELECT MAX(creationDate) FROM ImageInformation "
02838                     " INNER JOIN Images ON Images.id=ImageInformation.imageid "
02839                     " WHERE Images.album=? GROUP BY Images.album;",
02840                     albumID , &values );
02841     if (!values.isEmpty())
02842         return QDate::fromString( values.first().toString(), Qt::ISODate );
02843     else
02844         return QDate();
02845 }
02846 
02847 QDate AlbumDB::getAlbumAverageDate(int albumID)
02848 {
02849     QList<QVariant> values;
02850     d->db->execSql( "SELECT creationDate FROM ImageInformation "
02851                     " INNER JOIN Images ON Images.id=ImageInformation.imageid "
02852                     " WHERE Images.album=?;",
02853                     albumID , &values);
02854 
02855     int differenceInSecs = 0;
02856     int amountOfImages = 0;
02857     QDateTime baseDateTime;
02858 
02859     for (QList<QVariant>::const_iterator it = values.constBegin(); it != values.constEnd(); ++it)
02860     {
02861         QDateTime itemDateTime = (*it).isNull() ? QDateTime()
02862             : QDateTime::fromString( (*it).toString(), Qt::ISODate );
02863         if (itemDateTime.isValid())
02864         {
02865             ++amountOfImages;
02866             if ( baseDateTime.isNull() )
02867                 baseDateTime=itemDateTime;
02868             else
02869                 differenceInSecs += itemDateTime.secsTo( baseDateTime );
02870         }
02871     }
02872 
02873     if ( amountOfImages > 0 )
02874     {
02875         QDateTime averageDateTime;
02876         averageDateTime.setTime_t( baseDateTime.toTime_t() -
02877                                    (int)( differenceInSecs/amountOfImages ) );
02878         return ( averageDateTime.date() );
02879     }
02880     else
02881         return QDate();
02882 }
02883 
02884 void AlbumDB::deleteItem(int albumID, const QString& file)
02885 {
02886     qlonglong imageId = getImageId(albumID, file);
02887 
02888     d->db->execSql( QString("DELETE FROM Images WHERE id=?;"),
02889                     imageId );
02890 
02891     d->db->recordChangeset(CollectionImageChangeset(imageId, albumID, CollectionImageChangeset::Deleted));
02892 }
02893 
02894 void AlbumDB::removeItemsFromAlbum(int albumID)
02895 {
02896     d->db->execSql( QString("UPDATE Images SET status=?, album=NULL WHERE album=?;"),
02897                     (int)DatabaseItem::Removed, albumID );
02898 
02899     d->db->recordChangeset(CollectionImageChangeset(QList<qlonglong>(), albumID, CollectionImageChangeset::RemovedAll));
02900 }
02901 
02902 void AlbumDB::removeItems(QList<qlonglong> itemIDs, QList<int> albumIDs)
02903 {
02904     QSqlQuery query = d->db->prepareQuery( QString("UPDATE Images SET status=?, album=NULL WHERE id=?;") );
02905 
02906     QVariantList imageIds;
02907     QVariantList status;
02908     foreach (const qlonglong& id, itemIDs)
02909     {
02910         status << (int)DatabaseItem::Removed;
02911         imageIds << id;
02912     }
02913 
02914     query.addBindValue(status);
02915     query.addBindValue(imageIds);
02916     d->db->execBatch(query);
02917 
02918     d->db->recordChangeset(CollectionImageChangeset(itemIDs, albumIDs, CollectionImageChangeset::Removed));
02919 }
02920 
02921 void AlbumDB::deleteRemovedItems()
02922 {
02923     d->db->execSql( QString("DELETE FROM Images WHERE status=?;"),
02924                     (int)DatabaseItem::Removed );
02925 
02926     d->db->recordChangeset(CollectionImageChangeset(QList<qlonglong>(), QList<int>(), CollectionImageChangeset::RemovedDeleted));
02927 }
02928 
02929 void AlbumDB::deleteRemovedItems(QList<int> albumIds)
02930 {
02931     QSqlQuery query = d->db->prepareQuery( QString("DELETE FROM Images WHERE status=? AND album=?;") );
02932 
02933     QVariantList albumBindIds;
02934     QVariantList status;
02935     foreach(int albumId, albumIds)
02936     {
02937         status << (int)DatabaseItem::Removed;
02938         albumBindIds << albumId;
02939     }
02940 
02941     query.addBindValue(status);
02942     query.addBindValue(albumBindIds);
02943     d->db->execBatch(query);
02944 
02945     d->db->recordChangeset(CollectionImageChangeset(QList<qlonglong>(), albumIds, CollectionImageChangeset::RemovedDeleted));
02946 }
02947 
02948 void AlbumDB::renameAlbum(int albumID, int newAlbumRoot, const QString& newRelativePath)
02949 {
02950     int albumRoot  = getAlbumRootId(albumID);
02951     QString oldUrl = getAlbumRelativePath(albumID);
02952 
02953     if (oldUrl == newRelativePath)
02954         return;
02955 
02956     // first delete any stale albums left behind
02957     d->db->execSql( QString("DELETE FROM Albums WHERE relativePath=? AND albumRoot=?;"),
02958                     newRelativePath, albumRoot );
02959 
02960     // now update the album
02961     d->db->execSql( QString("UPDATE Albums SET albumRoot=?, relativePath=? WHERE id=? AND albumRoot=?;"),
02962                     newAlbumRoot, newRelativePath, albumID, albumRoot );
02963     d->db->recordChangeset(AlbumChangeset(albumID, AlbumChangeset::Renamed));
02964 
02965     /*
02966     if (renameSubalbums)
02967     {
02968         // now find the list of all subalbums which need to be updated
02969         QList<QVariant> values;
02970         d->db->execSql( QString("SELECT id, relativePath FROM Albums WHERE albumRoot=? AND relativePath LIKE ?;"),
02971                         albumRoot, oldUrl + "/%", &values );
02972 
02973         // and update their url
02974         QString newChildURL;
02975         for (QList<QVariant>::iterator it = values.begin(); it != values.end(); )
02976         {
02977             int childAlbumId = (*it).toInt();
02978             ++it;
02979             newChildURL = (*it).toString();
02980             ++it;
02981             newChildURL.replace(oldUrl, newRelativePath);
02982             d->db->execSql(QString("UPDATE Albums SET albumRoot=?, relativePath=? WHERE albumRoot=? AND relativePath=?"),
02983                            newAlbumRoot, newChildURL, albumRoot, (*it) );
02984             d->db->recordChangeset(AlbumChangeset(childAlbumId, AlbumChangeset::Renamed));
02985         }
02986     }
02987     */
02988 }
02989 
02990 void AlbumDB::setTagName(int tagID, const QString& name)
02991 {
02992     d->db->execSql( QString("UPDATE Tags SET name=? WHERE id=?;"),
02993                     name, tagID );
02994     d->db->recordChangeset(TagChangeset(tagID, TagChangeset::Renamed));
02995 }
02996 
02997 void AlbumDB::moveItem(int srcAlbumID, const QString& srcName,
02998                        int dstAlbumID, const QString& dstName)
02999 {
03000     // find id of src image
03001     qlonglong imageId = getImageId(srcAlbumID, srcName);
03002 
03003     if (imageId == -1)
03004         return;
03005 
03006     // first delete any stale database entries (for destination) if any
03007     deleteItem(dstAlbumID, dstName);
03008 
03009     d->db->execSql( QString("UPDATE Images SET album=?, name=? "
03010                             "WHERE id=?;"),
03011                     dstAlbumID, dstName, imageId );
03012     d->db->recordChangeset(CollectionImageChangeset(imageId, srcAlbumID, CollectionImageChangeset::Moved));
03013     d->db->recordChangeset(CollectionImageChangeset(imageId, srcAlbumID, CollectionImageChangeset::Removed));
03014     d->db->recordChangeset(CollectionImageChangeset(imageId, dstAlbumID, CollectionImageChangeset::Added));
03015 }
03016 
03017 int AlbumDB::copyItem(int srcAlbumID, const QString& srcName,
03018                       int dstAlbumID, const QString& dstName)
03019 {
03020     // find id of src image
03021     qlonglong srcId = getImageId(srcAlbumID, srcName);
03022 
03023     if (srcId == -1 || dstAlbumID == -1 || dstName.isEmpty())
03024         return -1;
03025 
03026     // check for src == dest
03027     if (srcAlbumID == dstAlbumID && srcName == dstName)
03028         return srcId;
03029 
03030     // first delete any stale database entries if any
03031     deleteItem(dstAlbumID, dstName);
03032 
03033     // copy entry in Images table
03034     QVariant id;
03035     d->db->execSql ( QString ("INSERT INTO Images "
03036                               " ( album, name, status, category, modificationDate, fileSize, uniqueHash ) "
03037                               " SELECT ?, ?, status, category, modificationDate, fileSize, uniqueHash "
03038                               "  FROM Images WHERE id=?;"),
03039                      dstAlbumID, dstName, srcId,
03040                      0, &id);
03041 
03042     if (id.isNull())
03043         return -1;
03044 
03045     d->db->recordChangeset(ImageChangeset(id.toLongLong(), DatabaseFields::ImagesAll));
03046     d->db->recordChangeset(CollectionImageChangeset(id.toLongLong(), srcAlbumID, CollectionImageChangeset::Copied));
03047     d->db->recordChangeset(CollectionImageChangeset(id.toLongLong(), dstAlbumID, CollectionImageChangeset::Added));
03048 
03049     // copy all other tables
03050     copyImageAttributes(srcId, id.toLongLong());
03051 
03052     return id.toLongLong();
03053 }
03054 
03055 void AlbumDB::copyImageAttributes(qlonglong srcId, qlonglong dstId)
03056 {
03057     // Go through all image-specific tables and copy the entries
03058 
03059     DatabaseFields::Set fields;
03060 
03061     d->db->execSql( QString("INSERT INTO ImageHaarMatrix "
03062                             " (imageid, modificationDate, uniqueHash, matrix) "
03063                             "SELECT ?, modificationDate, uniqueHash, matrix "
03064                             "FROM ImageHaarMatrix WHERE imageid=?;"),
03065                     dstId, srcId );
03066 
03067     d->db->execSql( QString("INSERT INTO ImageInformation "
03068                             " (imageid, rating, creationDate, digitizationDate, orientation, "
03069                             "  width, height, format, colorDepth, colorModel) "
03070                             "SELECT ?, rating, creationDate, digitizationDate, orientation, "
03071                             "  width, height, format, colorDepth, colorModel "
03072                             "FROM ImageInformation WHERE imageid=?;"),
03073                     dstId, srcId );
03074     fields |= DatabaseFields::ImageInformationAll;
03075 
03076     d->db->execSql( QString("INSERT INTO ImageMetadata "
03077                             " (imageid, make, model, lens, aperture, focalLength, focalLength35, "
03078                             "  exposureTime, exposureProgram, exposureMode, sensitivity, flash, whiteBalance, "
03079                             "  whiteBalanceColorTemperature, meteringMode, subjectDistance, subjectDistanceCategory) "
03080                             "SELECT ?, make, model, lens, aperture, focalLength, focalLength35, "
03081                             "  exposureTime, exposureProgram, exposureMode, sensitivity, flash, whiteBalance, "
03082                             "  whiteBalanceColorTemperature, meteringMode, subjectDistance, subjectDistanceCategory "
03083                             "FROM ImageMetadata WHERE imageid=?;"),
03084                     dstId, srcId );
03085     fields |= DatabaseFields::ImageMetadataAll;
03086 
03087     d->db->execSql( QString("INSERT INTO ImagePositions "
03088                             " (imageid, latitude, latitudeNumber, longitude, longitudeNumber, "
03089                             "  altitude, orientation, tilt, roll, accuracy, description) "
03090                             "SELECT ?, latitude, latitudeNumber, longitude, longitudeNumber, "
03091                             "  altitude, orientation, tilt, roll, accuracy, description "
03092                             "FROM ImagePositions WHERE imageid=?;"),
03093                     dstId, srcId );
03094     fields |= DatabaseFields::ImagePositionsAll;
03095 
03096     d->db->execSql( QString("INSERT INTO ImageComments "
03097                             " (imageid, type, language, author, date, comment) "
03098                             "SELECT ?, type, language, author, date, comment "
03099                             "FROM ImageComments WHERE imageid=?;"),
03100                     dstId, srcId );
03101     fields |= DatabaseFields::ImageCommentsAll;
03102 
03103     d->db->execSql( QString("INSERT INTO ImageCopyright "
03104                             " (imageid, property, value, extraValue) "
03105                             "SELECT ?, property, value, extraValue "
03106                             "FROM ImageCopyright WHERE imageid=?;"),
03107                     dstId, srcId );
03108 
03109     d->db->recordChangeset(ImageChangeset(dstId, fields));
03110 
03111     d->db->execSql( QString("INSERT INTO ImageTags "
03112                             " (imageid, tagid) "
03113                             "SELECT ?, tagid "
03114                             "FROM ImageTags WHERE imageid=?;"),
03115                     dstId, srcId );
03116     // leave empty tag list for now
03117     d->db->recordChangeset(ImageTagChangeset(dstId, QList<int>(), ImageTagChangeset::Added));
03118 
03119     d->db->execSql( QString("INSERT INTO ImageProperties "
03120                             " (imageid, property, value) "
03121                             "SELECT ?, property, value "
03122                             "FROM ImageProperties WHERE imageid=?;"),
03123                     dstId, srcId );
03124 }
03125 
03126 bool AlbumDB::copyAlbumProperties(int srcAlbumID, int dstAlbumID)
03127 {
03128     if (srcAlbumID == dstAlbumID)
03129         return true;
03130 
03131     QList<QVariant> values;
03132     d->db->execSql( QString("SELECT date, caption, collection, icon "
03133                             "FROM Albums WHERE id=?;"),
03134                     srcAlbumID,
03135                     &values );
03136 
03137     if (values.isEmpty())
03138     {
03139         kWarning() << " src album ID " << srcAlbumID << " does not exist";
03140         return false;
03141     }
03142 
03143     QList<QVariant> boundValues;
03144     boundValues << values[0] << values[1] << values[2] << values[3];
03145     boundValues << dstAlbumID;
03146 
03147     d->db->execSql( QString("UPDATE Albums SET date=?, caption=?, "
03148                             "collection=?, icon=? WHERE id=?"),
03149                     boundValues );
03150     return true;
03151 }
03152 
03153 }  // namespace Digikam

digikam

Skip menu "digikam"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members

API Reference

Skip menu "API Reference"
  • digikam
Generated for API Reference by doxygen 1.5.9-20090814
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal