/* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2004-06-18 * Description : database album interface. * * Copyright (C) 2004-2005 by Renchi Raju * Copyright (C) 2006-2007 by Gilles Caulier * Copyright (C) 2006-2007 by Marcel Wiesweg * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ /** @file albumdb.cpp */ // C Ansi includes. extern "C" { #include "sqlite3.h" #include } // C++ includes. #include #include #include // TQt includes. #include #include #include // KDE includes. #include // Local includes. #include "ddebug.h" #include "albummanager.h" #include "album.h" #include "albumdb.h" #include "albumsettings.h" namespace Digikam { typedef struct sqlite3_stmt sqlite3_stmt; typedef struct sqlite3 sqleet3; // hehe. class AlbumDBPriv { public: AlbumDBPriv() { valid = false; dataBase = 0; } bool valid; sqleet3 *dataBase; IntList recentlyAssignedTags; }; AlbumDB::AlbumDB() { d = new AlbumDBPriv; } AlbumDB::~AlbumDB() { if (d->dataBase) { sqlite3_close(d->dataBase); } delete d; } bool AlbumDB::isValid() const { return d->valid; } void AlbumDB::setDBPath(const TQString& path) { if (d->dataBase) { sqlite3_close(d->dataBase); d->dataBase = 0; } d->valid = false; sqlite3_open(TQFile::encodeName(path), &d->dataBase); if (d->dataBase == 0) { DWarning() << "Cannot open database: " << sqlite3_errmsg(d->dataBase) << endl; } else { initDB(); } } void AlbumDB::initDB() { d->valid = false; // Check if we have the required tables TQStringList values; if (!execSql( TQString("SELECT name FROM sqlite_master" " WHERE type='table'" " ORDER BY name;"), &values )) { return; } if (!values.contains("Albums")) { if (!execSql( TQString("CREATE TABLE Albums\n" " (id INTEGER PRIMARY KEY,\n" " url TEXT NOT NULL UNIQUE,\n" " date DATE NOT NULL,\n" " caption TEXT,\n" " collection TEXT,\n" " icon INTEGER);") )) { return; } if (!execSql( TQString("CREATE TABLE Tags\n" " (id INTEGER PRIMARY KEY,\n" " pid INTEGER,\n" " name TEXT NOT NULL,\n" " icon INTEGER,\n" " iconkde TEXT,\n" " UNIQUE (name, pid));") )) { return; } if (!execSql( TQString("CREATE TABLE TagsTree\n" " (id INTEGER NOT NULL,\n" " pid INTEGER NOT NULL,\n" " UNIQUE (id, pid));") )) { return; } if (!execSql( TQString("CREATE TABLE Images\n" " (id INTEGER PRIMARY KEY,\n" " name TEXT NOT NULL,\n" " dirid INTEGER NOT NULL,\n" " caption TEXT,\n" " datetime DATETIME,\n" " UNIQUE (name, dirid));") )) { return; } if (!execSql( TQString("CREATE TABLE ImageTags\n" " (imageid INTEGER NOT NULL,\n" " tagid INTEGER NOT NULL,\n" " UNIQUE (imageid, tagid));") )) { return; } if (!execSql( TQString("CREATE TABLE ImageProperties\n" " (imageid INTEGER NOT NULL,\n" " property TEXT NOT NULL,\n" " value TEXT NOT NULL,\n" " UNIQUE (imageid, property));") )) { return; } if ( !execSql( TQString( "CREATE TABLE Searches \n" " (id INTEGER PRIMARY KEY, \n" " name TEXT NOT NULL UNIQUE, \n" " url TEXT NOT NULL);" ) ) ) { return; } if (!execSql( TQString("CREATE TABLE Settings \n" "(keyword TEXT NOT NULL UNIQUE,\n" " value TEXT);") )) return; else setSetting("DBVersion","1"); // TODO: see which more indices are needed // create indices execSql("CREATE INDEX dir_index ON Images (dirid);"); execSql("CREATE INDEX tag_index ON ImageTags (tagid);"); // create triggers // trigger: delete from Images/ImageTags/ImageProperties // if Album has been deleted execSql("CREATE TRIGGER delete_album DELETE ON Albums\n" "BEGIN\n" " DELETE FROM ImageTags\n" " WHERE imageid IN (SELECT id FROM Images WHERE dirid=OLD.id);\n" " DELETE From ImageProperties\n" " WHERE imageid IN (SELECT id FROM Images WHERE dirid=OLD.id);\n" " DELETE FROM Images\n" " WHERE dirid = OLD.id;\n" "END;"); // trigger: delete from ImageTags/ImageProperties // if Image has been deleted execSql("CREATE TRIGGER delete_image DELETE ON Images\n" "BEGIN\n" " DELETE FROM ImageTags\n" " WHERE imageid=OLD.id;\n" " DELETE From ImageProperties\n " " WHERE imageid=OLD.id;\n" " UPDATE Albums SET icon=null \n " " WHERE icon=OLD.id;\n" " UPDATE Tags SET icon=null \n " " WHERE icon=OLD.id;\n" "END;"); // trigger: delete from ImageTags if Tag has been deleted execSql("CREATE TRIGGER delete_tag DELETE ON Tags\n" "BEGIN\n" " DELETE FROM ImageTags WHERE tagid=OLD.id;\n" "END;"); // trigger: insert into TagsTree if Tag has been added execSql("CREATE TRIGGER insert_tagstree AFTER INSERT ON Tags\n" "BEGIN\n" " INSERT INTO TagsTree\n" " SELECT NEW.id, NEW.pid\n" " UNION\n" " SELECT NEW.id, pid FROM TagsTree WHERE id=NEW.pid;\n" "END;"); // trigger: delete from TagsTree if Tag has been deleted execSql("CREATE TRIGGER delete_tagstree DELETE ON Tags\n" "BEGIN\n" " DELETE FROM Tags\n" " WHERE id IN (SELECT id FROM TagsTree WHERE pid=OLD.id);\n" " DELETE FROM TagsTree\n" " WHERE id IN (SELECT id FROM TagsTree WHERE pid=OLD.id);\n" " DELETE FROM TagsTree\n" " WHERE id=OLD.id;\n" "END;"); // trigger: delete from TagsTree if Tag has been deleted execSql("CREATE TRIGGER move_tagstree UPDATE OF pid ON Tags\n" "BEGIN\n" " DELETE FROM TagsTree\n" " WHERE\n" " ((id = OLD.id)\n" " OR\n" " id IN (SELECT id FROM TagsTree WHERE pid=OLD.id))\n" " AND\n" " pid IN (SELECT pid FROM TagsTree WHERE id=OLD.id);\n" " INSERT INTO TagsTree\n" " SELECT NEW.id, NEW.pid\n" " UNION\n" " SELECT NEW.id, pid FROM TagsTree WHERE id=NEW.pid\n" " UNION\n" " SELECT id, NEW.pid FROM TagsTree WHERE pid=NEW.id\n" " UNION\n" " SELECT A.id, B.pid FROM TagsTree A, TagsTree B\n" " WHERE\n" " A.pid = NEW.id AND B.id = NEW.pid;\n" "END;"); } d->valid = true; } AlbumInfo::List AlbumDB::scanAlbums() { AlbumInfo::List aList; TQString basePath(AlbumManager::instance()->getLibraryPath()); TQStringList values; execSql( "SELECT A.id, A.url, A.date, A.caption, A.collection, B.url, I.name \n " "FROM Albums AS A \n " " LEFT OUTER JOIN Images AS I ON A.icon=I.id \n" " LEFT OUTER JOIN Albums AS B ON B.id=I.dirid;", &values); TQString iconAlbumUrl, iconName; for (TQStringList::iterator it = values.begin(); it != values.end();) { AlbumInfo info; info.id = (*it).toInt(); ++it; info.url = *it; ++it; info.date = TQDate::fromString(*it, TQt::ISODate); ++it; info.caption = *it; ++it; info.collection = *it; ++it; iconAlbumUrl = *it; ++it; iconName = *it; ++it; if (!iconName.isEmpty()) { info.icon = basePath + iconAlbumUrl + '/' + iconName; } aList.append(info); } return aList; } TagInfo::List AlbumDB::scanTags() { TagInfo::List tList; TQString basePath(AlbumManager::instance()->getLibraryPath()); TQStringList values; execSql( "SELECT T.id, T.pid, T.name, A.url, I.name, T.iconkde \n " "FROM Tags AS T LEFT OUTER JOIN Images AS I ON I.id=T.icon \n " " LEFT OUTER JOIN Albums AS A ON A.id=I.dirid; ", &values ); TQString iconName, iconKDE, albumURL; for (TQStringList::iterator it = values.begin(); it != values.end();) { TagInfo info; info.id = (*it).toInt(); ++it; info.pid = (*it).toInt(); ++it; info.name = *it; ++it; albumURL = *it; ++it; iconName = *it; ++it; iconKDE = *it; ++it; if ( albumURL.isEmpty() ) { info.icon = iconKDE; } else { info.icon = basePath + albumURL + '/' + iconName; } tList.append(info); } return tList; } SearchInfo::List AlbumDB::scanSearches() { SearchInfo::List searchList; TQStringList values; execSql( "SELECT id, name, url FROM Searches;", &values); for (TQStringList::iterator it = values.begin(); it != values.end();) { SearchInfo info; info.id = (*it).toInt(); ++it; info.name = (*it); ++it; info.url = (*it); ++it; searchList.append(info); } return searchList; } void AlbumDB::beginTransaction() { execSql( "BEGIN TRANSACTION;" ); } void AlbumDB::commitTransaction() { execSql( "COMMIT TRANSACTION;" ); } int AlbumDB::addAlbum(const TQString& url, const TQString& caption, const TQDate& date, const TQString& collection) { if (!d->dataBase) return -1; execSql( TQString("REPLACE INTO Albums (url, date, caption, collection) " "VALUES('%1', '%2', '%3', '%4');") .arg(escapeString(url), date.toString(TQt::ISODate), escapeString(caption), escapeString(collection))); int id = sqlite3_last_insert_rowid(d->dataBase); return id; } void AlbumDB::setAlbumCaption(int albumID, const TQString& caption) { execSql( TQString("UPDATE Albums SET caption='%1' WHERE id=%2;") .arg(escapeString(caption), TQString::number(albumID) )); } void AlbumDB::setAlbumCollection(int albumID, const TQString& collection) { execSql( TQString("UPDATE Albums SET collection='%1' WHERE id=%2;") .arg(escapeString(collection), TQString::number(albumID)) ); } void AlbumDB::setAlbumDate(int albumID, const TQDate& date) { execSql( TQString("UPDATE Albums SET date='%1' WHERE id=%2;") .arg(date.toString(TQt::ISODate)) .arg(albumID) ); } void AlbumDB::setAlbumIcon(int albumID, TQ_LLONG iconID) { execSql( TQString("UPDATE Albums SET icon=%1 WHERE id=%2;") .arg(iconID) .arg(albumID) ); } TQString AlbumDB::getAlbumIcon(int albumID) { TQStringList values; execSql( TQString("SELECT B.url, I.name \n " "FROM Albums AS A \n " " LEFT OUTER JOIN Images AS I ON I.id=A.icon \n " " LEFT OUTER JOIN Albums AS B ON B.id=I.dirid \n " "WHERE A.id=%1;") .arg(albumID), &values ); if (values.isEmpty()) return TQString(); TQStringList::iterator it = values.begin(); TQString url = *it; ++it; TQString icon = *it; if (icon.isEmpty()) return TQString(); TQString basePath(AlbumManager::instance()->getLibraryPath()); basePath += url; basePath += '/' + icon; return basePath; } void AlbumDB::deleteAlbum(int albumID) { execSql( TQString("DELETE FROM Albums WHERE id=%1") .arg(albumID) ); } int AlbumDB::addTag(int parentTagID, const TQString& name, const TQString& iconKDE, TQ_LLONG iconID) { if (!d->dataBase) return -1; if (!execSql( TQString("INSERT INTO Tags (pid, name) " "VALUES( %1, '%2')") .arg(parentTagID) .arg(escapeString(name)))) { return -1; } int id = sqlite3_last_insert_rowid(d->dataBase); if (!iconKDE.isEmpty()) { execSql( TQString("UPDATE Tags SET iconkde='%1' WHERE id=%2;") .arg(escapeString(iconKDE), TQString::number(id))); } else { execSql( TQString("UPDATE Tags SET icon=%1 WHERE id=%2;") .arg(iconID) .arg(id)); } return id; } void AlbumDB::deleteTag(int tagID) { execSql( TQString("DELETE FROM Tags WHERE id=%1") .arg(tagID) ); } void AlbumDB::setTagIcon(int tagID, const TQString& iconKDE, TQ_LLONG iconID) { if (!iconKDE.isEmpty()) { execSql( TQString("UPDATE Tags SET iconkde='%1', icon=0 WHERE id=%2;") .arg(escapeString(iconKDE), TQString::number(tagID))); } else { execSql( TQString("UPDATE Tags SET icon=%1 WHERE id=%2;") .arg(iconID) .arg(tagID)); } } TQString AlbumDB::getTagIcon(int tagID) { TQStringList values; execSql( TQString("SELECT A.url, I.name, T.iconkde \n " "FROM Tags AS T \n " " LEFT OUTER JOIN Images AS I ON I.id=T.icon \n " " LEFT OUTER JOIN Albums AS A ON A.id=I.dirid \n " "WHERE T.id=%1;") .arg(tagID), &values ); if (values.isEmpty()) return TQString(); TQString basePath(AlbumManager::instance()->getLibraryPath()); TQString iconName, iconKDE, albumURL, icon; TQStringList::iterator it = values.begin(); albumURL = *it; ++it; iconName = *it; ++it; iconKDE = *it; ++it; if ( albumURL.isEmpty() ) { icon = iconKDE; } else { icon = basePath + albumURL + '/' + iconName; } return icon; } void AlbumDB::setTagParentID(int tagID, int newParentTagID) { execSql( TQString("UPDATE Tags SET pid=%1 WHERE id=%2;") .arg(newParentTagID) .arg(tagID) ); } int AlbumDB::addSearch(const TQString& name, const KURL& url) { if (!d->dataBase) return -1; TQString str("INSERT INTO Searches (name, url) \n" "VALUES('$$@@$$', '$$##$$');"); str.replace("$$@@$$", escapeString(name)); str.replace("$$##$$", escapeString(url.url())); if (!execSql(str)) { return -1; } return sqlite3_last_insert_rowid(d->dataBase); } void AlbumDB::updateSearch(int searchID, const TQString& name, const KURL& url) { TQString str = TQString("UPDATE Searches SET name='$$@@$$', url='$$##$$' \n" "WHERE id=%1") .arg(searchID); str.replace("$$@@$$", escapeString(name)); str.replace("$$##$$", escapeString(url.url())); execSql(str); } void AlbumDB::deleteSearch(int searchID) { execSql( TQString("DELETE FROM Searches WHERE id=%1") .arg(searchID) ); } void AlbumDB::setSetting(const TQString& keyword, const TQString& value ) { execSql( TQString("REPLACE into Settings VALUES ('%1','%2');") .arg(escapeString(keyword), escapeString(value) )); } TQString AlbumDB::getSetting(const TQString& keyword) { TQStringList values; execSql( TQString("SELECT value FROM Settings " "WHERE keyword='%1';") .arg(escapeString(keyword)), &values ); if (values.isEmpty()) return TQString(); else return values[0]; } bool AlbumDB::execSql(const TQString& sql, TQStringList* const values, const bool debug) { if ( debug ) DDebug() << "SQL-query: " << sql << endl; if ( !d->dataBase ) { DWarning() << k_funcinfo << "SQLite pointer == NULL" << endl; return false; } const char* tail; sqlite3_stmt* stmt; int error; //compile SQL program to virtual machine error = sqlite3_prepare(d->dataBase, sql.utf8(), -1, &stmt, &tail); if ( error != SQLITE_OK ) { DWarning() << k_funcinfo << "sqlite_compile error: " << sqlite3_errmsg(d->dataBase) << " on query: " << sql << endl; return false; } int cols = sqlite3_column_count(stmt); while ( true ) { error = sqlite3_step( stmt ); if ( error == SQLITE_DONE || error == SQLITE_ERROR ) break; //iterate over columns for ( int i = 0; values && i < cols; i++ ) { *values << TQString::fromUtf8( (const char*)sqlite3_column_text( stmt, i ) ); } } sqlite3_finalize( stmt ); if ( error != SQLITE_DONE ) { DWarning() << "sqlite_step error: " << sqlite3_errmsg( d->dataBase ) << " on query: " << sql << endl; return false; } return true; } TQString AlbumDB::escapeString(TQString str) const { str.replace( "'", "''" ); return str; } TQString AlbumDB::getItemCaption(TQ_LLONG imageID) { TQStringList values; execSql( TQString("SELECT caption FROM Images " "WHERE id=%1;") .arg(imageID), &values ); if (!values.isEmpty()) return values[0]; else return TQString(); } TQString AlbumDB::getItemCaption(int albumID, const TQString& name) { TQStringList values; execSql( TQString("SELECT caption FROM Images " "WHERE dirid=%1 AND name='%2';") .arg(albumID) .arg(escapeString(name)), &values ); if (!values.isEmpty()) return values[0]; else return TQString(); } TQDateTime AlbumDB::getItemDate(TQ_LLONG imageID) { TQStringList values; execSql( TQString("SELECT datetime FROM Images " "WHERE id=%1;") .arg(imageID), &values ); if (values.isEmpty()) return TQDateTime(); else return TQDateTime::fromString(values[0], TQt::ISODate); } TQDateTime AlbumDB::getItemDate(int albumID, const TQString& name) { TQStringList values; execSql( TQString("SELECT datetime FROM Images " "WHERE dirid=%1 AND name='%2';") .arg(albumID) .arg(escapeString(name)), &values ); if (values.isEmpty()) return TQDateTime(); else return TQDateTime::fromString(values[0], TQt::ISODate); } TQ_LLONG AlbumDB::getImageId(int albumID, const TQString& name) { TQStringList values; execSql( TQString("SELECT id FROM Images " "WHERE dirid=%1 AND name='%2';") .arg(albumID) .arg(escapeString(name)), &values ); if (values.isEmpty()) return -1; else return (values[0]).toLongLong(); } TQStringList AlbumDB::getItemTagNames(TQ_LLONG imageID) { TQStringList values; execSql( TQString("SELECT name FROM Tags \n " "WHERE id IN (SELECT tagid FROM ImageTags \n " " WHERE imageid=%1) \n " "ORDER BY name;") .arg(imageID), &values ); return values; } IntList AlbumDB::getItemTagIDs(TQ_LLONG imageID) { TQStringList values; execSql( TQString("SELECT tagid FROM ImageTags \n " "WHERE imageID=%1;") .arg(imageID), &values ); IntList ids; if (values.isEmpty()) return ids; for (TQStringList::iterator it=values.begin(); it != values.end(); ++it) { ids << (*it).toInt(); } return ids; } bool AlbumDB::hasTags(const LLongList& imageIDList) { IntList ids; if (imageIDList.isEmpty()) return false; TQStringList values; TQString sql = TQString("SELECT count(tagid) FROM ImageTags " "WHERE imageid=%1 ") .arg(imageIDList.first()); LLongList::const_iterator iter = imageIDList.begin(); ++iter; while (iter != imageIDList.end()) { sql += TQString(" OR imageid=%2 ") .arg(*iter); ++iter; } sql += TQString(";"); execSql( sql, &values ); if (values[0] == "0") return false; else return true; } IntList AlbumDB::getItemCommonTagIDs(const LLongList& imageIDList) { IntList ids; if (imageIDList.isEmpty()) return ids; TQStringList values; TQString sql = TQString("SELECT DISTINCT tagid FROM ImageTags " "WHERE imageid=%1 ") .arg(imageIDList.first()); LLongList::const_iterator iter = imageIDList.begin(); ++iter; while (iter != imageIDList.end()) { sql += TQString(" OR imageid=%2 ") .arg(*iter); ++iter; } sql += TQString(";"); execSql( sql, &values ); if (values.isEmpty()) return ids; for (TQStringList::iterator it=values.begin(); it != values.end(); ++it) { ids << (*it).toInt(); } return ids; } void AlbumDB::setItemCaption(TQ_LLONG imageID,const TQString& caption) { TQStringList values; execSql( TQString("UPDATE Images SET caption='%1' " "WHERE id=%2;") .arg(escapeString(caption), TQString::number(imageID) )); } void AlbumDB::setItemCaption(int albumID, const TQString& name, const TQString& caption) { TQStringList values; execSql( TQString("UPDATE Images SET caption='%1' " "WHERE dirid=%2 AND name='%3';") .arg(escapeString(caption), TQString::number(albumID), escapeString(name)) ); } void AlbumDB::addItemTag(TQ_LLONG imageID, int tagID) { execSql( TQString("REPLACE INTO ImageTags (imageid, tagid) " "VALUES(%1, %2);") .arg(imageID) .arg(tagID) ); if (!d->recentlyAssignedTags.contains(tagID)) { d->recentlyAssignedTags.push_front(tagID); if (d->recentlyAssignedTags.size() > 10) d->recentlyAssignedTags.pop_back(); } } void AlbumDB::addItemTag(int albumID, const TQString& name, int tagID) { execSql( TQString("REPLACE INTO ImageTags (imageid, tagid) \n " "(SELECT id, %1 FROM Images \n " " WHERE dirid=%2 AND name='%3');") .arg(tagID) .arg(albumID) .arg(escapeString(name)) ); } IntList AlbumDB::getRecentlyAssignedTags() const { return d->recentlyAssignedTags; } void AlbumDB::removeItemTag(TQ_LLONG imageID, int tagID) { execSql( TQString("DELETE FROM ImageTags " "WHERE imageID=%1 AND tagid=%2;") .arg(imageID) .arg(tagID) ); } void AlbumDB::removeItemAllTags(TQ_LLONG imageID) { execSql( TQString("DELETE FROM ImageTags " "WHERE imageID=%1;") .arg(imageID) ); } TQStringList AlbumDB::getItemNamesInAlbum(int albumID, bool recurssive) { TQStringList values; if (recurssive) { KURL url(getAlbumURL(albumID)); execSql( TQString("SELECT Images.name " "FROM Images " "WHERE Images.dirid " "IN (SELECT DISTINCT id " "FROM Albums " "WHERE url='%1' OR url LIKE '\%%2\%')") .arg(escapeString(url.path())).arg(escapeString(url.path(1))), &values); } else { execSql( TQString("SELECT Images.name " "FROM Images " "WHERE Images.dirid=%1") .arg(albumID), &values ); } return values; } TQStringList AlbumDB::getAllItemURLsWithoutDate() { TQStringList values; execSql( TQString("SELECT Albums.url||'/'||Images.name " "FROM Images, Albums " "WHERE Images.dirid=Albums.Id " "AND (Images.datetime is null or " " Images.datetime == '');"), &values ); TQString libraryPath = AlbumManager::instance()->getLibraryPath() + '/'; for (TQStringList::iterator it = values.begin(); it != values.end(); ++it) { *it = libraryPath + *it; } return values; } int AlbumDB::getOrCreateAlbumId(const TQString& folder) { TQStringList values; execSql( TQString("SELECT id FROM Albums WHERE url ='%1';") .arg( escapeString(folder) ), &values); int albumID; if (values.isEmpty()) { execSql( TQString ("INSERT INTO Albums (url, date) " "VALUES ('%1','%2')") .arg(escapeString(folder), TQDateTime::currentDateTime().toString(TQt::ISODate)) ); albumID = sqlite3_last_insert_rowid(d->dataBase); } else albumID = values[0].toInt(); return albumID; } TQ_LLONG AlbumDB::addItem(int albumID, const TQString& name, const TQDateTime& datetime, const TQString& comment, int rating, const TQStringList &keywordsList) { execSql ( TQString ("REPLACE INTO Images " "( caption , datetime, name, dirid ) " " VALUES ('%1','%2','%3',%4) " ) .arg(escapeString(comment), datetime.toString(TQt::ISODate), escapeString(name), TQString::number(albumID)) ); TQ_LLONG item = sqlite3_last_insert_rowid(d->dataBase); // Set Rating value to item in database. if ( item != -1 && rating != -1 ) setItemRating(item, rating); // Set existing tags in database or create new tags if not exist. if ( item != -1 && !keywordsList.isEmpty() ) { IntList tagIDs = getTagsFromTagPaths(keywordsList); for (IntList::iterator it = tagIDs.begin(); it != tagIDs.end(); ++it) { addItemTag(item, *it); } } return item; } IntList AlbumDB::getTagsFromTagPaths(const TQStringList &keywordsList, bool create) { if (keywordsList.isEmpty()) return IntList(); IntList tagIDs; TQStringList keywordsList2Create; // Create a list of the tags currently in database TagInfo::List currentTagsList; TQStringList values; execSql( "SELECT id, pid, name FROM Tags;", &values ); for (TQStringList::iterator it = values.begin(); it != values.end();) { TagInfo info; info.id = (*it).toInt(); ++it; info.pid = (*it).toInt(); ++it; info.name = *it; ++it; currentTagsList.append(info); } // For every tag in keywordsList, scan taglist to check if tag already exists. for (TQStringList::const_iterator kwd = keywordsList.begin(); kwd != keywordsList.end(); ++kwd ) { // split full tag "url" into list of single tag names TQStringList tagHierarchy = TQStringList::split('/', *kwd); if (tagHierarchy.isEmpty()) continue; // last entry in list is the actual tag name bool foundTag = false; TQString tagName = tagHierarchy.back(); tagHierarchy.pop_back(); for (TagInfo::List::iterator tag = currentTagsList.begin(); tag != currentTagsList.end(); ++tag ) { // There might be multiple tags with the same name, but in different // hierarchies. We must check them all until we find the correct hierarchy if ((*tag).name == tagName) { int parentID = (*tag).pid; // Check hierarchy, from bottom to top bool foundParentTag = true; TQStringList::iterator parentTagName = tagHierarchy.end(); while (foundParentTag && parentTagName != tagHierarchy.begin()) { --parentTagName; foundParentTag = false; for (TagInfo::List::iterator parentTag = currentTagsList.begin(); parentTag != currentTagsList.end(); ++parentTag ) { // check if name is the same, and if ID is identical // to the parent ID we got from the child tag if ( (*parentTag).id == parentID && (*parentTag).name == (*parentTagName) ) { parentID = (*parentTag).pid; foundParentTag = true; break; } } // If we traversed the list without a match, // foundParentTag will be false, the while loop breaks. } // If we managed to traverse the full hierarchy, // we have our tag. if (foundParentTag) { // add to result list tagIDs.append((*tag).id); foundTag = true; break; } } } if (!foundTag) keywordsList2Create.append(*kwd); } // If tags do not exist in database, create them. if (create && !keywordsList2Create.isEmpty()) { for (TQStringList::iterator kwd = keywordsList2Create.begin(); kwd != keywordsList2Create.end(); ++kwd ) { // split full tag "url" into list of single tag names TQStringList tagHierarchy = TQStringList::split('/', *kwd); if (tagHierarchy.isEmpty()) continue; int parentTagID = 0; int tagID = 0; bool parentTagExisted = true; // Traverse hierarchy from top to bottom for (TQStringList::iterator tagName = tagHierarchy.begin(); tagName != tagHierarchy.end(); ++tagName) { tagID = 0; // if the parent tag did not exist, we need not check if the child exists if (parentTagExisted) { for (TagInfo::List::iterator tag = currentTagsList.begin(); tag != currentTagsList.end(); ++tag ) { // find the tag with tag name according to tagHierarchy, // and parent ID identical to the ID of the tag we found in // the previous run. if ((*tag).name == (*tagName) && (*tag).pid == parentTagID) { tagID = (*tag).id; break; } } } if (tagID != 0) { // tag already found in DB parentTagID = tagID; continue; } // Tag does not yet exist in DB, add it tagID = addTag(parentTagID, (*tagName), TQString(), 0); if (tagID == -1) { // Something is wrong in database. Abort. break; } // append to our list of existing tags (for following keywords) TagInfo info; info.id = tagID; info.pid = parentTagID; info.name = (*tagName); currentTagsList.append(info); parentTagID = tagID; parentTagExisted = false; } // add to result list tagIDs.append(tagID); } } return tagIDs; } int AlbumDB::getItemAlbum(TQ_LLONG imageID) { TQStringList values; execSql ( TQString ("SELECT dirid FROM Images " "WHERE id=%1;") .arg(imageID), &values); if (!values.isEmpty()) return values.first().toInt(); else return 1; } TQString AlbumDB::getItemName(TQ_LLONG imageID) { TQStringList values; execSql ( TQString ("SELECT name FROM Images " "WHERE id=%1;") .arg(imageID), &values); if (!values.isEmpty()) return values.first(); else return TQString(); } bool AlbumDB::setItemDate(TQ_LLONG imageID, const TQDateTime& datetime) { execSql ( TQString ("UPDATE Images SET datetime='%1'" "WHERE id=%2;") .arg(datetime.toString(TQt::ISODate), TQString::number(imageID)) ); return true; } bool AlbumDB::setItemDate(int albumID, const TQString& name, const TQDateTime& datetime) { execSql ( TQString ("UPDATE Images SET datetime='%1'" "WHERE dirid=%2 AND name='%3';") .arg(datetime.toString(TQt::ISODate), TQString::number(albumID), escapeString(name)) ); return true; } void AlbumDB::setItemRating(TQ_LLONG imageID, int rating) { execSql ( TQString ("REPLACE INTO ImageProperties " "(imageid, property, value) " "VALUES(%1, '%2', '%3');") .arg(imageID) .arg("Rating") .arg(rating) ); } int AlbumDB::getItemRating(TQ_LLONG imageID) { TQStringList values; execSql( TQString("SELECT value FROM ImageProperties " "WHERE imageid=%1 and property='%2';") .arg(imageID) .arg("Rating"), &values); if (!values.isEmpty()) return values[0].toInt(); else return 0; } TQStringList AlbumDB::getItemURLsInAlbum(int albumID) { TQStringList values; TQString basePath(AlbumManager::instance()->getLibraryPath()); AlbumSettings::ImageSortOrder order = AlbumSettings::instance()->getImageSortOrder(); TQString sqlString; switch(order) { case AlbumSettings::ByIName: sqlString = TQString("SELECT Albums.url||'/'||Images.name FROM Images, Albums " "WHERE Albums.id=%1 AND Albums.id=Images.dirid " "ORDER BY Images.name COLLATE NOCASE;") .arg(albumID); break; case AlbumSettings::ByIPath: // Dont collate on the path - this is to maintain the same behaviour // that happens when sort order is "By Path" sqlString = TQString("SELECT Albums.url||'/'||Images.name FROM Images, Albums " "WHERE Albums.id=%1 AND Albums.id=Images.dirid " "ORDER BY Albums.url,Images.name;") .arg(albumID); break; case AlbumSettings::ByIDate: sqlString = TQString("SELECT Albums.url||'/'||Images.name FROM Images, Albums " "WHERE Albums.id=%1 AND Albums.id=Images.dirid " "ORDER BY Images.datetime;") .arg(albumID); break; case AlbumSettings::ByIRating: sqlString = TQString("SELECT Albums.url||'/'||Images.name FROM Images, Albums, ImageProperties " "WHERE Albums.id=%1 AND Albums.id=Images.dirid " "AND Images.id = ImageProperties.imageid " "AND ImageProperties.property='Rating' " "ORDER BY ImageProperties.value DESC;") .arg(albumID); break; default: sqlString = TQString("SELECT Albums.url||'/'||Images.name FROM Images, Albums " "WHERE Albums.id=%1 AND Albums.id=Images.dirid;") .arg(albumID); break; } execSql( sqlString, &values ); for (TQStringList::iterator it = values.begin(); it != values.end(); ++it) { *it = basePath + *it; } return values; } LLongList AlbumDB::getItemIDsInAlbum(int albumID) { LLongList itemIDs; TQStringList itemNames = getItemNamesInAlbum(albumID); for (TQStringList::iterator it = itemNames.begin(); it != itemNames.end(); ++it) { TQ_LLONG id = getImageId(albumID, *it); itemIDs.append(id); } return itemIDs; } TQStringList AlbumDB::getItemURLsInTag(int tagID, bool recursive) { TQStringList values; TQString basePath(AlbumManager::instance()->getLibraryPath()); TQString imagesIdClause; if (recursive) imagesIdClause = TQString("SELECT imageid FROM ImageTags " " WHERE tagid=%1 " " OR tagid IN (SELECT id FROM TagsTree WHERE pid=%2)") .arg(tagID).arg(tagID); else imagesIdClause = TQString("SELECT imageid FROM ImageTags WHERE tagid=%1").arg(tagID); execSql( TQString("SELECT Albums.url||'/'||Images.name FROM Images, Albums " "WHERE Images.id IN (%1) " "AND Albums.id=Images.dirid;") .arg(imagesIdClause), &values ); for (TQStringList::iterator it = values.begin(); it != values.end(); ++it) { *it = basePath + *it; } return values; } LLongList AlbumDB::getItemIDsInTag(int tagID, bool recursive) { LLongList itemIDs; TQStringList values; if (recursive) execSql( TQString("SELECT imageid FROM ImageTags " " WHERE tagid=%1 " " OR tagid IN (SELECT id FROM TagsTree WHERE pid=%2)") .arg(tagID).arg(tagID), &values ); else execSql( TQString("SELECT imageid FROM ImageTags WHERE tagid=%1;") .arg(tagID), &values ); for (TQStringList::iterator it = values.begin(); it != values.end(); ++it) { itemIDs << (*it).toLong(); } return itemIDs; } TQString AlbumDB::getAlbumURL(int albumID) { TQStringList values; execSql( TQString("SELECT url from Albums where id=%1") .arg( albumID), &values); return values[0]; } TQDate AlbumDB::getAlbumLowestDate(int albumID) { TQStringList values; execSql( TQString("SELECT MIN(datetime) FROM Images " "WHERE dirid=%1 GROUP BY dirid") .arg( albumID ), &values); TQDate itemDate = TQDate::fromString( values[0], TQt::ISODate ); return itemDate; } TQDate AlbumDB::getAlbumHighestDate(int albumID) { TQStringList values; execSql( TQString("SELECT MAX(datetime) FROM Images " "WHERE dirid=%1 GROUP BY dirid") .arg( albumID ), &values); TQDate itemDate = TQDate::fromString( values[0], TQt::ISODate ); return itemDate; } TQDate AlbumDB::getAlbumAverageDate(int albumID) { TQStringList values; execSql( TQString("SELECT datetime FROM Images WHERE dirid=%1") .arg( albumID ), &values); int differenceInSecs = 0; int amountOfImages = 0; TQDateTime baseDateTime; for (TQStringList::iterator it = values.begin(); it != values.end(); ++it) { TQDateTime itemDateTime = TQDateTime::fromString( *it, TQt::ISODate ); if (itemDateTime.isValid()) { ++amountOfImages; if ( baseDateTime.isNull() ) baseDateTime=itemDateTime; else differenceInSecs += itemDateTime.secsTo( baseDateTime ); } } if ( amountOfImages > 0 ) { TQDateTime averageDateTime; averageDateTime.setTime_t( baseDateTime.toTime_t() - (int)( differenceInSecs/amountOfImages ) ); return ( averageDateTime.date() ); } else return TQDate(); } void AlbumDB::deleteItem(int albumID, const TQString& file) { execSql( TQString("DELETE FROM Images " "WHERE dirid=%1 AND name='%2';") .arg(albumID) .arg(escapeString(file)) ); } void AlbumDB::setAlbumURL(int albumID, const TQString& url) { TQString u = escapeString(url); // first delete any stale albums left behind execSql( TQString("DELETE FROM Albums WHERE url = '%1'") .arg(u) ); // now update the album url execSql( TQString("UPDATE Albums SET url = '%1' WHERE id = %2;") .arg(u, TQString::number(albumID) )); } void AlbumDB::setTagName(int tagID, const TQString& name) { execSql( TQString("UPDATE Tags SET name='%1' WHERE id=%2;") .arg(escapeString(name), TQString::number(tagID) )); } void AlbumDB::moveItem(int srcAlbumID, const TQString& srcName, int dstAlbumID, const TQString& dstName) { // first delete any stale database entries if any deleteItem(dstAlbumID, dstName); execSql( TQString("UPDATE Images SET dirid=%1, name='%2' " "WHERE dirid=%3 AND name='%4';") .arg(TQString::number(dstAlbumID), escapeString(dstName), TQString::number(srcAlbumID), escapeString(srcName)) ); } int AlbumDB::copyItem(int srcAlbumID, const TQString& srcName, int dstAlbumID, const TQString& dstName) { // check for src == dest if (srcAlbumID == dstAlbumID && srcName == dstName) return -1; // find id of src image TQStringList values; execSql( TQString("SELECT id FROM Images " "WHERE dirid=%1 AND name='%2';") .arg(TQString::number(srcAlbumID), escapeString(srcName)), &values); if (values.isEmpty()) return -1; int srcId = values[0].toInt(); // first delete any stale database entries if any deleteItem(dstAlbumID, dstName); // copy entry in Images table execSql( TQString("INSERT INTO Images (dirid, name, caption, datetime) " "SELECT %1, '%2', caption, datetime FROM Images " "WHERE id=%3;") .arg(TQString::number(dstAlbumID), escapeString(dstName), TQString::number(srcId)) ); int dstId = sqlite3_last_insert_rowid(d->dataBase); // copy tags execSql( TQString("INSERT INTO ImageTags (imageid, tagid) " "SELECT %1, tagid FROM ImageTags " "WHERE imageid=%2;") .arg(TQString::number(dstId), TQString::number(srcId)) ); // copy properties (rating) execSql( TQString("INSERT INTO ImageProperties (imageid, property, value) " "SELECT %1, property, value FROM ImageProperties " "WHERE imageid=%2;") .arg(TQString::number(dstId), TQString::number(srcId)) ); return dstId; } TQ_LLONG AlbumDB::lastInsertedRow() { return sqlite3_last_insert_rowid(d->dataBase); } } // namespace Digikam