summaryrefslogtreecommitdiffstats
path: root/digikam/kioslave/digikamsearch.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'digikam/kioslave/digikamsearch.cpp')
-rw-r--r--digikam/kioslave/digikamsearch.cpp734
1 files changed, 734 insertions, 0 deletions
diff --git a/digikam/kioslave/digikamsearch.cpp b/digikam/kioslave/digikamsearch.cpp
new file mode 100644
index 00000000..b6eee9fc
--- /dev/null
+++ b/digikam/kioslave/digikamsearch.cpp
@@ -0,0 +1,734 @@
+/* ============================================================
+ *
+ * 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 <renchi@pooh.tam.uiuc.edu>
+ *
+ * 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 <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <utime.h>
+}
+
+// C++ includes.
+
+#include <cerrno>
+#include <cstdlib>
+#include <cstdio>
+#include <ctime>
+
+// Qt includes.
+
+#include <qfile.h>
+#include <qdatastream.h>
+#include <qtextstream.h>
+#include <qregexp.h>
+#include <qdir.h>
+#include <qvariant.h>
+#include <qmap.h>
+
+// KDE includes.
+
+#include <kglobal.h>
+#include <klocale.h>
+#include <kcalendarsystem.h>
+#include <kinstance.h>
+#include <kfilemetainfo.h>
+#include <kmimetype.h>
+#include <kdebug.h>
+#include <kio/global.h>
+#include <kio/ioslave_defaults.h>
+#include <klargefile.h>
+
+// Local includes.
+
+#include "digikam_export.h"
+#include "digikamsearch.h"
+
+kio_digikamsearch::kio_digikamsearch(const QCString &pool_socket,
+ const QCString &app_socket)
+ : SlaveBase("kio_digikamsearch", pool_socket, app_socket)
+{
+ // build a lookup table for month names
+ const KCalendarSystem* cal = KGlobal::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 QValueList<QRegExp> makeFilterList( const QString &filter )
+{
+ QValueList<QRegExp> regExps;
+ if ( filter.isEmpty() )
+ return regExps;
+
+ QChar sep( ';' );
+ int i = filter.find( sep, 0 );
+ if ( i == -1 && filter.find( ' ', 0 ) != -1 )
+ sep = QChar( ' ' );
+
+ QStringList list = QStringList::split( sep, filter );
+ QStringList::Iterator it = list.begin();
+ while ( it != list.end() )
+ {
+ regExps << QRegExp( (*it).stripWhiteSpace(), false, true );
+ ++it;
+ }
+ return regExps;
+}
+
+static bool matchFilterList( const QValueList<QRegExp>& filters,
+ const QString &fileName )
+{
+ QValueList<QRegExp>::ConstIterator rit = filters.begin();
+ while ( rit != filters.end() )
+ {
+ if ( (*rit).exactMatch(fileName) )
+ return true;
+ ++rit;
+ }
+ return false;
+}
+
+void kio_digikamsearch::special(const QByteArray& data)
+{
+ QString libraryPath;
+ KURL url;
+ QString filter;
+ int getDimensions;
+ int listingType = 0;
+ int recurseAlbums;
+ int recurseTags;
+
+ QDataStream 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);
+ }
+
+ QValueList<QRegExp> regex = makeFilterList(filter);
+ QByteArray ba;
+
+ if (listingType == 0)
+ {
+ QString 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); ";
+
+ QStringList values;
+ QString errMsg;
+ if (!m_db.execSql(sqlQuery, &values))
+ {
+ error(KIO::ERR_INTERNAL, errMsg);
+ return;
+ }
+
+ Q_LLONG imageid;
+ QString name;
+ QString path;
+ int dirid;
+ QString date;
+ QString purl;
+ QSize dims;
+ struct stat stbuf;
+
+ int count = 0;
+ QDataStream* os = new QDataStream(ba, IO_WriteOnly);
+
+ for (QStringList::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(QFile::encodeName(path), &stbuf) != 0)
+ continue;
+
+ dims = QSize();
+ 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<size_t>(stbuf.st_size);
+ *os << dims;
+
+ count++;
+
+ if (count > 200)
+ {
+ delete os;
+ os = 0;
+
+ SlaveBase::data(ba);
+ ba.resize(0);
+
+ count = 0;
+ os = new QDataStream(ba, IO_WriteOnly);
+ }
+ }
+
+ delete os;
+ }
+ else
+ {
+ QString 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;";
+
+ QStringList values;
+ QString errMsg;
+ if (!m_db.execSql(sqlQuery, &values, &errMsg))
+ {
+ error(KIO::ERR_INTERNAL, errMsg);
+ return;
+ }
+
+ QDataStream ds(ba, IO_WriteOnly);
+ for (QStringList::iterator it = values.begin(); it != values.end(); ++it)
+ {
+ if (matchFilterList(regex, *it))
+ {
+ ds << m_libraryPath + *it;
+ }
+ }
+ }
+
+ SlaveBase::data(ba);
+
+ finished();
+}
+
+QString kio_digikamsearch::buildQuery(const KURL& url) const
+{
+ int count = url.queryItem("count").toInt();
+ if (count <= 0)
+ return QString();
+
+ QMap<int, RuleType> rulesMap;
+
+ for (int i=1; i<=count; i++)
+ {
+ RuleType rule;
+
+ QString key = url.queryItem(QString::number(i) + ".key").lower();
+ QString op = url.queryItem(QString::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 kioslave"
+ << 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 kioslave"
+ << endl;
+ continue;
+ }
+
+ rule.val = url.queryItem(QString::number(i) + ".val");
+
+ rulesMap.insert(i, rule);
+ }
+
+ QString sqlQuery;
+
+ QStringList strList = QStringList::split(" ", url.path());
+ for ( QStringList::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;
+ QString 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
+ {
+ QValueList<SKey> todo;
+ todo.append( ALBUMNAME );
+ todo.append( IMAGENAME );
+ todo.append( TAGNAME );
+ todo.append( ALBUMCAPTION );
+ todo.append( ALBUMCOLLECTION );
+ todo.append( IMAGECAPTION );
+ todo.append( RATING );
+
+ sqlQuery += '(';
+ QValueListIterator<SKey> 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;
+}
+
+QString kio_digikamsearch::subQuery(enum kio_digikamsearch::SKey key,
+ enum kio_digikamsearch::SOperator op,
+ const QString& val) const
+{
+ QString 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("$$@@$$", QString::fromLatin1("'") + escapeString(val)
+ + QString::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("$$@@$$", QString::fromLatin1("'") + escapeString(val)
+ + QString::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("$$@@$$", QString::fromLatin1("'") + escapeString(val)
+// + QString::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`, `rating<=c`, `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("$$@@$$", QString::fromLatin1("'") + escapeString(val)
+ + QString::fromLatin1("'"));
+ break;
+ }
+ case(NE):
+ {
+ query.replace("$$##$$", "<>");
+ query.replace("$$@@$$", QString::fromLatin1("'") + escapeString(val)
+ + QString::fromLatin1("'"));
+ break;
+ }
+ case(LT):
+ {
+ query.replace("$$##$$", "<");
+ query.replace("$$@@$$", QString::fromLatin1("'") + escapeString(val)
+ + QString::fromLatin1("'"));
+ break;
+ }
+ case(GT):
+ {
+ query.replace("$$##$$", ">");
+ query.replace("$$@@$$", QString::fromLatin1("'") + escapeString(val)
+ + QString::fromLatin1("'"));
+ break;
+ }
+ case(LTE):
+ {
+ query.replace("$$##$$", "<=");
+ query.replace("$$@@$$", QString::fromLatin1("'") + escapeString(val)
+ + QString::fromLatin1("'"));
+ break;
+ }
+ case(GTE):
+ {
+ query.replace("$$##$$", ">=");
+ query.replace("$$@@$$", QString::fromLatin1("'") + escapeString(val)
+ + QString::fromLatin1("'"));
+ break;
+ }
+ case(LIKE):
+ {
+ query.replace("$$##$$", "LIKE");
+ query.replace("$$@@$$", QString::fromLatin1("'%") + escapeString(val)
+ + QString::fromLatin1("%'"));
+ break;
+ }
+ case(NLIKE):
+ {
+ query.replace("$$##$$", "NOT LIKE");
+ query.replace("$$@@$$", QString::fromLatin1("'%") + escapeString(val)
+ + QString::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)
+ {
+ QDate date = QDate::fromString(val, Qt::ISODate);
+ if (!date.isValid())
+ return query;
+
+ query = QString(" (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");
+ KInstance instance( "kio_digikamsearch" );
+ KGlobal::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;
+ }
+}
+
+QString kio_digikamsearch::possibleDate(const QString& str, bool& exact) const
+{
+ QDate date = QDate::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 <= QDate::currentDate().year())
+ {
+ // very sure its a year
+ return QString("%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])
+ {
+ QString monGlob;
+ monGlob.sprintf("%.2d", i);
+ monGlob = "%-" + monGlob + "-%";
+ return monGlob;
+ }
+ }
+ }
+
+ return QString();
+}