/* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2005-04-21 * Description : a kio-slave to process search on digiKam albums * * Copyright (C) 2005 by Renchi Raju * * 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. * * ============================================================ */ // C Ansi includes. extern "C" { #include #include #include #include #include #include } // C++ includes. #include #include #include #include // TQt includes. #include #include #include #include #include #include #include // KDE includes. #include #include #include #include #include #include #include #include #include #include // Local includes. #include "digikam_export.h" #include "digikamsearch.h" kio_digikamsearch::kio_digikamsearch(const TQCString &pool_socket, const TQCString &app_socket) : SlaveBase("kio_digikamsearch", pool_socket, app_socket) { // build a lookup table for month names const KCalendarSystem* cal = TDEGlobal::locale()->calendar(); for (int i=1; i<=12; ++i) { m_shortMonths[i-1] = cal->monthName(i, 2000, true).lower(); m_longMonths[i-1] = cal->monthName(i, 2000, false).lower(); } } kio_digikamsearch::~kio_digikamsearch() { } static TQValueList makeFilterList( const TQString &filter ) { TQValueList regExps; if ( filter.isEmpty() ) return regExps; TQChar sep( ';' ); int i = filter.find( sep, 0 ); if ( i == -1 && filter.find( ' ', 0 ) != -1 ) sep = TQChar( ' ' ); TQStringList list = TQStringList::split( sep, filter ); TQStringList::Iterator it = list.begin(); while ( it != list.end() ) { regExps << TQRegExp( (*it).stripWhiteSpace(), false, true ); ++it; } return regExps; } static bool matchFilterList( const TQValueList& filters, const TQString &fileName ) { TQValueList::ConstIterator rit = filters.begin(); while ( rit != filters.end() ) { if ( (*rit).exactMatch(fileName) ) return true; ++rit; } return false; } void kio_digikamsearch::special(const TQByteArray& data) { TQString libraryPath; KURL url; TQString filter; int getDimensions; int listingType = 0; int recurseAlbums; int recurseTags; TQDataStream ds(data, IO_ReadOnly); ds >> libraryPath; ds >> url; ds >> filter; ds >> getDimensions; ds >> recurseAlbums; ds >> recurseTags; if (!ds.atEnd()) ds >> listingType; if (m_libraryPath != libraryPath) { m_libraryPath = libraryPath; m_db.closeDB(); m_db.openDB(libraryPath); } TQValueList regex = makeFilterList(filter); TQByteArray ba; if (listingType == 0) { TQString sqlQuery; // query head sqlQuery = "SELECT Images.id, Images.name, Images.dirid, Images.datetime, Albums.url " "FROM Images, Albums LEFT JOIN ImageProperties ON Images.id = Imageproperties.imageid " "WHERE ( "; // query body sqlQuery += buildQuery(url); // query tail sqlQuery += " ) "; sqlQuery += " AND (Albums.id=Images.dirid); "; TQStringList values; TQString errMsg; if (!m_db.execSql(sqlQuery, &values)) { error(TDEIO::ERR_INTERNAL, errMsg); return; } TQ_LLONG imageid; TQString name; TQString path; int dirid; TQString date; TQString purl; TQSize dims; struct stat stbuf; int count = 0; TQDataStream* os = new TQDataStream(ba, IO_WriteOnly); for (TQStringList::iterator it = values.begin(); it != values.end();) { imageid = (*it).toLongLong(); ++it; name = *it; ++it; dirid = (*it).toInt(); ++it; date = *it; ++it; purl = *it; ++it; if (!matchFilterList(regex, name)) continue; path = m_libraryPath + purl + '/' + name; if (::stat(TQFile::encodeName(path), &stbuf) != 0) continue; dims = TQSize(); if (getDimensions) { KFileMetaInfo metaInfo(path); if (metaInfo.isValid()) { if (metaInfo.containsGroup("Jpeg EXIF Data")) { dims = metaInfo.group("Jpeg EXIF Data"). item("Dimensions").value().toSize(); } else if (metaInfo.containsGroup("General")) { dims = metaInfo.group("General"). item("Dimensions").value().toSize(); } else if (metaInfo.containsGroup("Technical")) { dims = metaInfo.group("Technical"). item("Dimensions").value().toSize(); } } } *os << imageid; *os << dirid; *os << name; *os << date; *os << static_cast(stbuf.st_size); *os << dims; count++; if (count > 200) { delete os; os = 0; SlaveBase::data(ba); ba.resize(0); count = 0; os = new TQDataStream(ba, IO_WriteOnly); } } delete os; } else { TQString sqlQuery; // query head sqlQuery = "SELECT Albums.url||'/'||Images.name " "FROM Images, Albums LEFT JOIN ImageProperties on Images.id = ImageProperties.imageid " "WHERE ( "; // query body sqlQuery += buildQuery(url); // query tail sqlQuery += " ) "; sqlQuery += " AND (Albums.id=Images.dirid) "; sqlQuery += " LIMIT 500;"; TQStringList values; TQString errMsg; if (!m_db.execSql(sqlQuery, &values, &errMsg)) { error(TDEIO::ERR_INTERNAL, errMsg); return; } TQDataStream ds(ba, IO_WriteOnly); for (TQStringList::iterator it = values.begin(); it != values.end(); ++it) { if (matchFilterList(regex, *it)) { ds << m_libraryPath + *it; } } } SlaveBase::data(ba); finished(); } TQString kio_digikamsearch::buildQuery(const KURL& url) const { int count = url.queryItem("count").toInt(); if (count <= 0) return TQString(); TQMap rulesMap; for (int i=1; i<=count; i++) { RuleType rule; TQString key = url.queryItem(TQString::number(i) + ".key").lower(); TQString op = url.queryItem(TQString::number(i) + ".op").lower(); if (key == "album") { rule.key = ALBUM; } else if (key == "albumname") { rule.key = ALBUMNAME; } else if (key == "albumcaption") { rule.key = ALBUMCAPTION; } else if (key == "albumcollection") { rule.key = ALBUMCOLLECTION; } else if (key == "imagename") { rule.key = IMAGENAME; } else if (key == "imagecaption") { rule.key = IMAGECAPTION; } else if (key == "imagedate") { rule.key = IMAGEDATE; } else if (key == "tag") { rule.key = TAG; } else if (key == "tagname") { rule.key = TAGNAME; } else if (key == "keyword") { rule.key = KEYWORD; } else if (key == "rating") { rule.key = RATING; } else { kdWarning() << "Unknown rule type: " << key << " passed to tdeioslave" << endl; continue; } if (op == "eq") rule.op = EQ; else if (op == "ne") rule.op = NE; else if (op == "lt") rule.op = LT; else if (op == "lte") rule.op = LTE; else if (op == "gt") rule.op = GT; else if (op == "gte") rule.op = GTE; else if (op == "like") rule.op = LIKE; else if (op == "nlike") rule.op = NLIKE; else { kdWarning() << "Unknown op type: " << op << " passed to tdeioslave" << endl; continue; } rule.val = url.queryItem(TQString::number(i) + ".val"); rulesMap.insert(i, rule); } TQString sqlQuery; TQStringList strList = TQStringList::split(" ", url.path()); for ( TQStringList::Iterator it = strList.begin(); it != strList.end(); ++it ) { bool ok; int num = (*it).toInt(&ok); if (ok) { RuleType rule = rulesMap[num]; if (rule.key == KEYWORD) { bool exact; TQString possDate = possibleDate(rule.val, exact); if (!possDate.isEmpty()) { rule.key = IMAGEDATE; rule.val = possDate; if (exact) { rule.op = EQ; } else { rule.op = LIKE; } sqlQuery += subQuery(rule.key, rule.op, rule.val); } else { TQValueList todo; todo.append( ALBUMNAME ); todo.append( IMAGENAME ); todo.append( TAGNAME ); todo.append( ALBUMCAPTION ); todo.append( ALBUMCOLLECTION ); todo.append( IMAGECAPTION ); todo.append( RATING ); sqlQuery += '('; TQValueListIterator it; it = todo.begin(); while ( it != todo.end() ) { sqlQuery += subQuery(*it, rule.op, rule.val); ++it; if ( it != todo.end() ) sqlQuery += " OR "; } sqlQuery += ')'; } } else { sqlQuery += subQuery(rule.key, rule.op, rule.val); } } else { sqlQuery += ' ' + *it + ' '; } } return sqlQuery; } TQString kio_digikamsearch::subQuery(enum kio_digikamsearch::SKey key, enum kio_digikamsearch::SOperator op, const TQString& val) const { TQString query; switch (key) { case(ALBUM): { if (op == EQ || op == NE) query = " (Images.dirid $$##$$ $$@@$$) "; else // LIKE AND NLIKE query = " (Images.dirid IN " " (SELECT a.id FROM Albums a, Albums b " " WHERE a.url $$##$$ '%' || b.url || '%' AND b.id = $$@@$$))"; query.replace("$$@@$$", TQString::fromLatin1("'") + escapeString(val) + TQString::fromLatin1("'")); break; } case(ALBUMNAME): { query = " (Images.dirid IN " " (SELECT id FROM Albums WHERE url $$##$$ $$@@$$)) "; break; } case(ALBUMCAPTION): { query = " (Images.dirid IN " " (SELECT id FROM Albums WHERE caption $$##$$ $$@@$$)) "; break; } case(ALBUMCOLLECTION): { query = " (Images.dirid IN " " (SELECT id FROM Albums WHERE collection $$##$$ $$@@$$)) "; break; } case(TAG): { if (op == EQ) query = " (Images.id IN " " (SELECT imageid FROM ImageTags " " WHERE tagid = $$@@$$)) "; else if (op == NE) query = " (Images.id NOT IN " " (SELECT imageid FROM ImageTags " " WHERE tagid = $$@@$$)) "; else if (op == LIKE) query = " (Images.id IN " " (SELECT ImageTags.imageid FROM ImageTags JOIN TagsTree on ImageTags.tagid = TagsTree.id " " WHERE TagsTree.pid = $$@@$$ or ImageTags.tagid = $$@@$$ )) "; else // op == NLIKE query = " (Images.id NOT IN " " (SELECT ImageTags.imageid FROM ImageTags JOIN TagsTree on ImageTags.tagid = TagsTree.id " " WHERE TagsTree.pid = $$@@$$ or ImageTags.tagid = $$@@$$ )) "; // query = " (Images.id IN " // " (SELECT imageid FROM ImageTags " // " WHERE tagid $$##$$ $$@@$$)) "; query.replace("$$@@$$", TQString::fromLatin1("'") + escapeString(val) + TQString::fromLatin1("'")); break; } case(TAGNAME): { if (op == EQ) query = " (Images.id IN " " (SELECT imageid FROM ImageTags " " WHERE tagid IN " " (SELECT id FROM Tags WHERE name = $$@@$$))) "; else if (op == NE) query = " (Images.id NOT IN " " (SELECT imageid FROM ImageTags " " WHERE tagid IN " " (SELECT id FROM Tags WHERE name = $$@@$$))) "; else if (op == LIKE) query = " (Images.id IN " " (SELECT ImageTags.imageid FROM ImageTags JOIN TagsTree on ImageTags.tagid = TagsTree.id " " WHERE TagsTree.pid = (SELECT id FROM Tags WHERE name LIKE $$@@$$) " " OR ImageTags.tagid = (SELECT id FROM Tags WHERE name LIKE $$@@$$) )) "; else // op == NLIKE query = " (Images.id NOT IN " " (SELECT ImageTags.imageid FROM ImageTags JOIN TagsTree on ImageTags.tagid = TagsTree.id " " WHERE TagsTree.pid = (SELECT id FROM Tags WHERE name LIKE $$@@$$) " " OR ImageTags.tagid = (SELECT id FROM Tags WHERE name LIKE $$@@$$) )) "; // query.replace("$$@@$$", TQString::fromLatin1("'") + escapeString(val) // + TQString::fromLatin1("'")); break; } case(IMAGENAME): { query = " (Images.name $$##$$ $$@@$$) "; break; } case(IMAGECAPTION): { query = " (Images.caption $$##$$ $$@@$$) "; break; } case(IMAGEDATE): { query = " (Images.datetime $$##$$ $$@@$$) "; break; } case (KEYWORD): { kdWarning() << "KEYWORD Detected which is not possible" << endl; break; } case(RATING): { // For searches for `rating=0`, `rating>=0`, `rating<=0`, // `rating c` with c=1,2,3,4,5, // special care has to be taken: Images which were never rated // have no ImageProperties.property='Rating', but // need to be treated like having a rating of 0. // This is achieved by including all images which do // not have property='Rating'. if ( ( val=="0" and (op==EQ or op==GTE or op==LTE) ) or (val!="0" and (op==LT or op==LTE or op==NE) ) ) { query = " ( (ImageProperties.value $$##$$ $$@@$$ and ImageProperties.property='Rating') or (Images.id NOT IN (SELECT imageid FROM ImageProperties WHERE property='Rating') ) )"; } else { query = " (ImageProperties.value $$##$$ $$@@$$ and ImageProperties.property='Rating') "; } break; } } if (key != TAG) { switch (op) { case(EQ): { query.replace("$$##$$", "="); query.replace("$$@@$$", TQString::fromLatin1("'") + escapeString(val) + TQString::fromLatin1("'")); break; } case(NE): { query.replace("$$##$$", "<>"); query.replace("$$@@$$", TQString::fromLatin1("'") + escapeString(val) + TQString::fromLatin1("'")); break; } case(LT): { query.replace("$$##$$", "<"); query.replace("$$@@$$", TQString::fromLatin1("'") + escapeString(val) + TQString::fromLatin1("'")); break; } case(GT): { query.replace("$$##$$", ">"); query.replace("$$@@$$", TQString::fromLatin1("'") + escapeString(val) + TQString::fromLatin1("'")); break; } case(LTE): { query.replace("$$##$$", "<="); query.replace("$$@@$$", TQString::fromLatin1("'") + escapeString(val) + TQString::fromLatin1("'")); break; } case(GTE): { query.replace("$$##$$", ">="); query.replace("$$@@$$", TQString::fromLatin1("'") + escapeString(val) + TQString::fromLatin1("'")); break; } case(LIKE): { query.replace("$$##$$", "LIKE"); query.replace("$$@@$$", TQString::fromLatin1("'%") + escapeString(val) + TQString::fromLatin1("%'")); break; } case(NLIKE): { query.replace("$$##$$", "NOT LIKE"); query.replace("$$@@$$", TQString::fromLatin1("'%") + escapeString(val) + TQString::fromLatin1("%'")); break; } } } // special case for imagedate. If the key is imagedate and the operator is EQ, // we need to split it into two rules if (key == IMAGEDATE && op == EQ) { TQDate date = TQDate::fromString(val, Qt::ISODate); if (!date.isValid()) return query; query = TQString(" (Images.datetime > '%1' AND Images.datetime < '%2') ") .arg(date.addDays(-1).toString(Qt::ISODate)) .arg(date.addDays( 1).toString(Qt::ISODate)); } return query; } /* KIO slave registration */ extern "C" { DIGIKAM_EXPORT int kdemain(int argc, char **argv) { KLocale::setMainCatalogue("digikam"); TDEInstance instance( "kio_digikamsearch" ); TDEGlobal::locale(); if (argc != 4) { kdDebug() << "Usage: kio_digikamsearch protocol domain-socket1 domain-socket2" << endl; exit(-1); } kio_digikamsearch slave(argv[2], argv[3]); slave.dispatchLoop(); return 0; } } TQString kio_digikamsearch::possibleDate(const TQString& str, bool& exact) const { TQDate date = TQDate::fromString(str, Qt::ISODate); if (date.isValid()) { exact = true; return date.toString(Qt::ISODate); } exact = false; bool ok; int num = str.toInt(&ok); if (ok) { // ok. its an int, does it look like a year? if (1970 <= num && num <= TQDate::currentDate().year()) { // very sure its a year return TQString("%1-%-%").arg(num); } } else { // hmm... not a year. is it a particular month? for (int i=1; i<=12; i++) { if (str.lower() == m_shortMonths[i-1] || str.lower() == m_longMonths[i-1]) { TQString monGlob; monGlob.sprintf("%.2d", i); monGlob = "%-" + monGlob + "-%"; return monGlob; } } } return TQString(); }