summaryrefslogtreecommitdiffstats
path: root/src/tdeioslave
diff options
context:
space:
mode:
authorMichele Calgaro <[email protected]>2024-11-22 18:41:30 +0900
committerMichele Calgaro <[email protected]>2024-11-22 20:55:03 +0900
commit5bed6e4a4c916a97f8fe4d1b07f7eecf4d733b90 (patch)
treef89cc49efc9ca1d0e1579ecb079ee7e7088ff8c8 /src/tdeioslave
parent0bfbf616d9c1fd7abb1bd02732389ab35e5f8771 (diff)
downloaddigikam-5bed6e4a4c916a97f8fe4d1b07f7eecf4d733b90.tar.gz
digikam-5bed6e4a4c916a97f8fe4d1b07f7eecf4d733b90.zip
Rename 'digikam' folder to 'src'
Signed-off-by: Michele Calgaro <[email protected]> (cherry picked from commit ee0d99607c14cb63d3ebdb3a970b508949fa8219)
Diffstat (limited to 'src/tdeioslave')
-rw-r--r--src/tdeioslave/Makefile.am72
-rw-r--r--src/tdeioslave/digikamalbums.cpp1969
-rw-r--r--src/tdeioslave/digikamalbums.h108
-rw-r--r--src/tdeioslave/digikamalbums.protocol45
-rw-r--r--src/tdeioslave/digikamdates.cpp313
-rw-r--r--src/tdeioslave/digikamdates.h54
-rw-r--r--src/tdeioslave/digikamdates.protocol44
-rw-r--r--src/tdeioslave/digikamsearch.cpp734
-rw-r--r--src/tdeioslave/digikamsearch.h100
-rw-r--r--src/tdeioslave/digikamsearch.protocol38
-rw-r--r--src/tdeioslave/digikamtags.cpp325
-rw-r--r--src/tdeioslave/digikamtags.h60
-rw-r--r--src/tdeioslave/digikamtags.protocol44
-rw-r--r--src/tdeioslave/digikamthumbnail.cpp635
-rw-r--r--src/tdeioslave/digikamthumbnail.h76
-rw-r--r--src/tdeioslave/digikamthumbnail.protocol38
-rw-r--r--src/tdeioslave/sqlitedb.cpp197
-rw-r--r--src/tdeioslave/sqlitedb.h58
18 files changed, 4910 insertions, 0 deletions
diff --git a/src/tdeioslave/Makefile.am b/src/tdeioslave/Makefile.am
new file mode 100644
index 00000000..28b8a2ff
--- /dev/null
+++ b/src/tdeioslave/Makefile.am
@@ -0,0 +1,72 @@
+METASOURCES = AUTO
+
+if with_included_sqlite3
+ LIB_SQLITE3_LOCAL = $(top_builddir)/src/libs/sqlite3/libsqlite3.la
+ SQLITE3_INCLUDES = -I$(top_srcdir)/src/libs/sqlite3
+endif
+
+INCLUDES = -I$(top_srcdir)/src/libs/jpegutils \
+ -I$(top_srcdir)/src/libs/dimg \
+ -I$(top_srcdir)/src/libs/dmetadata \
+ -I$(top_srcdir)/src/digikam \
+ $(SQLITE3_INCLUDES) \
+ $(LIBKEXIV2_CFLAGS) $(LIBKDCRAW_CFLAGS) \
+ $(all_includes)
+
+kde_module_LTLIBRARIES = tdeio_digikamthumbnail.la tdeio_digikamtags.la \
+ tdeio_digikamalbums.la tdeio_digikamdates.la \
+ tdeio_digikamsearch.la
+
+protocol_DATA = digikamthumbnail.protocol digikamtags.protocol \
+ digikamalbums.protocol digikamdates.protocol \
+ digikamsearch.protocol
+
+protocoldir = $(kde_servicesdir)
+
+# NOTE: if local libsqlite3 is used LIB_SQLITE3 is null.
+# if shared libsqlite3 is used LIB_SQLITE3_LOCAL is null.
+
+# -- digikam TDEIO image thumbnails generator ----------------------------------------
+
+tdeio_digikamthumbnail_la_SOURCES = digikamthumbnail.cpp
+
+tdeio_digikamthumbnail_la_LIBADD = $(LIB_TDEIO) $(LIBJPEG) $(LIBPNG) \
+ $(top_builddir)/src/libs/jpegutils/libjpegutils.la \
+ $(top_builddir)/src/libs/dimg/libdimg.la
+
+tdeio_digikamthumbnail_la_LDFLAGS = -module -avoid-version $(KDE_PLUGIN) $(all_libraries) -ltdecore -ltdeui $(LIB_TQT) -ltdefx
+
+# -- digikam tags TDEIO --------------------------------------------------------------
+
+tdeio_digikamtags_la_SOURCES = digikamtags.cpp sqlitedb.cpp
+
+tdeio_digikamtags_la_LIBADD = $(LIB_TDEIO) $(LIB_SQLITE3) $(LIB_SQLITE3_LOCAL)
+
+tdeio_digikamtags_la_LDFLAGS = -module -avoid-version $(all_libraries) $(KDE_PLUGIN) -ltdecore -ltdeui $(LIB_TQT) -ltdefx
+
+# -- digikam albums TDEIO ------------------------------------------------------------
+
+tdeio_digikamalbums_la_SOURCES = digikamalbums.cpp sqlitedb.cpp
+
+tdeio_digikamalbums_la_LIBADD = $(top_builddir)/src/libs/jpegutils/libjpegutils.la \
+ $(top_builddir)/src/libs/dimg/libdimg.la \
+ $(LIB_TDEIO) $(LIB_SQLITE3) $(LIB_SQLITE3_LOCAL)
+
+
+tdeio_digikamalbums_la_LDFLAGS = -module -avoid-version $(all_libraries) $(KDE_PLUGIN) -ltdecore -ltdeui $(LIB_TQT) -ltdefx
+
+# -- digikam dates TDEIO -------------------------------------------------------------
+
+tdeio_digikamdates_la_SOURCES = digikamdates.cpp sqlitedb.cpp
+
+tdeio_digikamdates_la_LIBADD = $(LIB_TDEIO) $(LIB_SQLITE3) $(LIB_SQLITE3_LOCAL)
+
+tdeio_digikamdates_la_LDFLAGS = -module -avoid-version $(all_libraries) $(KDE_PLUGIN) -ltdecore -ltdeui $(LIB_TQT) -ltdefx
+
+# -- digikam search TDEIO ------------------------------------------------------------
+
+tdeio_digikamsearch_la_SOURCES = digikamsearch.cpp sqlitedb.cpp
+
+tdeio_digikamsearch_la_LIBADD = $(LIB_TDEIO) $(LIB_SQLITE3) $(LIB_SQLITE3_LOCAL)
+
+tdeio_digikamsearch_la_LDFLAGS = -module -avoid-version $(all_libraries) $(KDE_PLUGIN) -ltdecore -ltdeui $(LIB_TQT) -ltdefx
diff --git a/src/tdeioslave/digikamalbums.cpp b/src/tdeioslave/digikamalbums.cpp
new file mode 100644
index 00000000..c16080af
--- /dev/null
+++ b/src/tdeioslave/digikamalbums.cpp
@@ -0,0 +1,1969 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2005-04-21
+ * Description : a tdeio-slave to process file operations on
+ * digiKam albums.
+ *
+ * Copyright (C) 2005 by Renchi Raju <[email protected]>
+ *
+ * Lots of the file io code is copied from KDE file tdeioslave.
+ * Copyright for the KDE file tdeioslave follows:
+ * Copyright (C) 2000-2002 Stephan Kulow <[email protected]>
+ * Copyright (C) 2000-2002 David Faure <[email protected]>
+ * Copyright (C) 2000-2002 Waldo Bastian <[email protected]>
+ *
+ * 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.
+ *
+ * ============================================================ */
+
+#define MAX_IPC_SIZE (1024*32)
+
+// 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 <cstdlib>
+#include <cstdio>
+#include <ctime>
+#include <cerrno>
+
+// TQt includes.
+
+#include <tqfile.h>
+#include <tqfileinfo.h>
+#include <tqdatastream.h>
+#include <tqregexp.h>
+#include <tqdir.h>
+
+// KDE includes.
+
+#include <tdeglobal.h>
+#include <tdelocale.h>
+#include <kinstance.h>
+#include <tdefilemetainfo.h>
+#include <kmimetype.h>
+#include <kdebug.h>
+#include <tdeio/global.h>
+#include <tdeio/ioslave_defaults.h>
+#include <klargefile.h>
+#include <tdeversion.h>
+
+// LibKDcraw includes.
+
+#include <libkdcraw/version.h>
+#include <libkdcraw/kdcraw.h>
+
+#if KDCRAW_VERSION < 0x000106
+#include <libkdcraw/dcrawbinary.h>
+#endif
+
+// Local includes.
+
+#include "dmetadata.h"
+#include "sqlitedb.h"
+#include "digikam_export.h"
+#include "digikamalbums.h"
+
+tdeio_digikamalbums::tdeio_digikamalbums(const TQCString &pool_socket,
+ const TQCString &app_socket)
+ : SlaveBase("tdeio_digikamalbums", pool_socket, app_socket)
+{
+}
+
+tdeio_digikamalbums::~tdeio_digikamalbums()
+{
+}
+
+static TQValueList<TQRegExp> makeFilterList( const TQString &filter )
+{
+ TQValueList<TQRegExp> 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<TQRegExp>& filters,
+ const TQString &fileName )
+{
+ TQValueList<TQRegExp>::ConstIterator rit = filters.begin();
+ while ( rit != filters.end() ) {
+ if ( (*rit).exactMatch(fileName) )
+ return true;
+ ++rit;
+ }
+ return false;
+}
+
+void tdeio_digikamalbums::special(const TQByteArray& data)
+{
+ bool folders = (metaData("folders") == "yes");
+
+ TQString libraryPath;
+ KURL kurl;
+ TQString url;
+ TQString urlWithTrailingSlash;
+ TQString filter;
+ int getDimensions;
+ int scan = 0;
+ int recurseAlbums;
+ int recurseTags;
+
+ TQDataStream ds(data, IO_ReadOnly);
+ ds >> libraryPath;
+ ds >> kurl;
+ ds >> filter;
+ ds >> getDimensions;
+ ds >> recurseAlbums;
+ ds >> recurseTags;
+ if (!ds.atEnd())
+ ds >> scan;
+
+ libraryPath = TQDir::cleanDirPath(libraryPath);
+
+ if (m_libraryPath != libraryPath)
+ {
+ m_libraryPath = libraryPath;
+ m_sqlDB.closeDB();
+ m_sqlDB.openDB(libraryPath);
+ }
+
+ url = kurl.path();
+
+ if (scan)
+ {
+ scanAlbum(url);
+ finished();
+ return;
+ }
+
+ TQValueList<TQRegExp> regex = makeFilterList(filter);
+ TQByteArray ba;
+
+ if (folders) // Special mode to stats all album items
+ {
+ TQMap<int, int> albumsStatMap;
+ TQStringList values, allAbumIDs;
+ int albumID;
+
+ // initialize allAbumIDs with all existing albums from db to prevent
+ // wrong album image counters
+ m_sqlDB.execSql(TQString("SELECT id from Albums"), &allAbumIDs);
+
+ for ( TQStringList::iterator it = allAbumIDs.begin(); it != allAbumIDs.end(); ++it)
+ {
+ albumID = (*it).toInt();
+ albumsStatMap.insert(albumID, 0);
+ }
+
+ // now we can count the images assigned to albums
+ m_sqlDB.execSql(TQString("SELECT dirid, Images.name FROM Images "
+ "WHERE Images.dirid IN (SELECT DISTINCT id FROM Albums)"), &values);
+
+ for ( TQStringList::iterator it = values.begin(); it != values.end(); )
+ {
+ albumID = (*it).toInt();
+ ++it;
+
+ if ( matchFilterList( regex, *it ) )
+ {
+ TQMap<int, int>::iterator it2 = albumsStatMap.find(albumID);
+ if ( it2 == albumsStatMap.end() )
+ albumsStatMap.insert(albumID, 1);
+ else
+ albumsStatMap.replace(albumID, it2.data() + 1);
+ }
+
+ ++it;
+ }
+
+ TQDataStream os(ba, IO_WriteOnly);
+ os << albumsStatMap;
+ }
+ else
+ {
+ TQStringList albumvalues;
+ if (recurseAlbums)
+ {
+ // Search for albums and sub-albums:
+ // For this, get the path with a trailing "/".
+ // Otherwise albums on the same level like "Paris", "Paris 2006",
+ // would be found in addition to "Paris/*".
+ urlWithTrailingSlash = kurl.path(1);
+
+ m_sqlDB.execSql(TQString("SELECT DISTINCT id, url FROM Albums WHERE url='%1' OR url LIKE '%2\%';")
+ .arg(escapeString(url)).arg(escapeString(urlWithTrailingSlash)), &albumvalues);
+ }
+ else
+ {
+ // Search for albums
+
+ m_sqlDB.execSql(TQString("SELECT DISTINCT id, url FROM Albums WHERE url='%1';")
+ .arg(escapeString(url)), &albumvalues);
+ }
+
+ TQDataStream* os = new TQDataStream(ba, IO_WriteOnly);
+
+ TQString base;
+ TQ_LLONG id;
+ TQString name;
+ TQString date;
+ TQSize dims;
+
+ struct stat stbuf;
+
+ TQStringList values;
+ TQString albumurl;
+ int albumid;
+
+ // Loop over all albums:
+ int count = 0 ;
+ for (TQStringList::iterator albumit = albumvalues.begin(); albumit != albumvalues.end();)
+ {
+ albumid = (*albumit).toLongLong();
+ ++albumit;
+ albumurl = *albumit;
+ ++albumit;
+
+ base = libraryPath + albumurl + '/';
+
+ values.clear();
+ m_sqlDB.execSql(TQString("SELECT id, name, datetime FROM Images "
+ "WHERE dirid = %1;")
+ .arg(albumid), &values);
+
+ // Loop over all images in each album (specified by its albumid).
+ for (TQStringList::iterator it = values.begin(); it != values.end();)
+ {
+ id = (*it).toLongLong();
+ ++it;
+ name = *it;
+ ++it;
+ date = *it;
+ ++it;
+
+ if (!matchFilterList(regex, name))
+ continue;
+
+ if (::stat(TQFile::encodeName(base + name), &stbuf) != 0)
+ continue;
+
+ dims = TQSize();
+ if (getDimensions)
+ {
+ TQFileInfo fileInfo(base + name);
+#if KDCRAW_VERSION < 0x000106
+ TQString rawFilesExt(KDcrawIface::DcrawBinary::instance()->rawFiles());
+#else
+ TQString rawFilesExt(KDcrawIface::KDcraw::rawFiles());
+#endif
+ TQString ext = fileInfo.extension(false).upper();
+
+ if (!ext.isEmpty() && rawFilesExt.upper().contains(ext))
+ {
+ Digikam::DMetadata metaData(base + name);
+ dims = metaData.getImageDimensions();
+ }
+ else
+ {
+ KFileMetaInfo metaInfo(base + name);
+ 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 << id;
+ *os << albumid;
+ *os << name;
+ *os << date;
+ *os << static_cast<size_t>(stbuf.st_size);
+ *os << dims;
+
+ count++;
+
+ // Send images in batches of 200.
+ if (count > 200)
+ {
+ delete os;
+ os = 0;
+
+ SlaveBase::data(ba);
+ ba.resize(0);
+
+ count = 0;
+ os = new TQDataStream(ba, IO_WriteOnly);
+ }
+ }
+ count++;
+ }
+ }
+
+ SlaveBase::data(ba);
+
+ finished();
+}
+
+static int write_all(int fd, const char *buf, size_t len)
+{
+ while (len > 0)
+ {
+ ssize_t written = write(fd, buf, len);
+ if (written < 0)
+ {
+ if (errno == EINTR)
+ continue;
+ return -1;
+ }
+ buf += written;
+ len -= written;
+ }
+ return 0;
+}
+
+void tdeio_digikamalbums::get( const KURL& url )
+{
+// Code duplication from file:// ioslave
+ kdDebug() << k_funcinfo << " : " << url << endl;
+
+ // get the libraryPath
+ TQString libraryPath = url.user();
+ if (libraryPath.isEmpty())
+ {
+ error(TDEIO::ERR_UNKNOWN, "Album Library Path not supplied to tdeioslave");
+ return;
+ }
+
+ // no need to open the db. we don't need to read/write to it
+
+ TQCString path(TQFile::encodeName(libraryPath + url.path()));
+ KDE_struct_stat buff;
+ if ( KDE_stat( path.data(), &buff ) == -1 )
+ {
+ if ( errno == EACCES )
+ error( TDEIO::ERR_ACCESS_DENIED, url.url() );
+ else
+ error( TDEIO::ERR_DOES_NOT_EXIST, url.url() );
+ return;
+ }
+
+ if ( S_ISDIR( buff.st_mode ) )
+ {
+ error( TDEIO::ERR_IS_DIRECTORY, url.url() );
+ return;
+ }
+
+ if ( !S_ISREG( buff.st_mode ) )
+ {
+ error( TDEIO::ERR_CANNOT_OPEN_FOR_READING, url.url() );
+ return;
+ }
+
+ int fd = KDE_open( path.data(), O_RDONLY);
+ if ( fd < 0 )
+ {
+ error( TDEIO::ERR_CANNOT_OPEN_FOR_READING, url.url() );
+ return;
+ }
+
+ // Determine the mimetype of the file to be retrieved, and emit it.
+ // This is mandatory in all slaves (for KRun/BrowserRun to work).
+ KMimeType::Ptr mt = KMimeType::findByURL( libraryPath + url.path(), buff.st_mode,
+ true);
+ emit mimeType( mt->name() );
+
+ totalSize( buff.st_size );
+
+ char buffer[ MAX_IPC_SIZE ];
+ TQByteArray array;
+ TDEIO::filesize_t processed_size = 0;
+
+ while (1)
+ {
+ int n = ::read( fd, buffer, MAX_IPC_SIZE );
+ if (n == -1)
+ {
+ if (errno == EINTR)
+ continue;
+ error( TDEIO::ERR_COULD_NOT_READ, url.url());
+ close(fd);
+ return;
+ }
+ if (n == 0)
+ break; // Finished
+
+ array.setRawData(buffer, n);
+ data( array );
+ array.resetRawData(buffer, n);
+
+ processed_size += n;
+ processedSize( processed_size );
+ }
+
+ data( TQByteArray() );
+ close( fd );
+
+ processedSize( buff.st_size );
+ finished();
+}
+
+void tdeio_digikamalbums::put(const KURL& url, int permissions, bool overwrite, bool /*resume*/)
+{
+// Code duplication from file:// ioslave
+ kdDebug() << k_funcinfo << " : " << url.url() << endl;
+
+ // get the libraryPath
+ TQString libraryPath = url.user();
+ if (libraryPath.isEmpty())
+ {
+ error(TDEIO::ERR_UNKNOWN, "Album Library Path not supplied to tdeioslave");
+ return;
+ }
+
+ // open the db if needed
+ if (m_libraryPath != libraryPath)
+ {
+ m_libraryPath = libraryPath;
+ m_sqlDB.closeDB();
+ m_sqlDB.openDB(m_libraryPath);
+ }
+
+ // build the album list
+ buildAlbumList();
+
+ // get the parent album
+ AlbumInfo album = findAlbum(url.directory());
+ if (album.id == -1)
+ {
+ error(TDEIO::ERR_UNKNOWN, i18n("Source album %1 not found in database")
+ .arg(url.directory()));
+ return;
+ }
+
+
+ TQString dest = libraryPath + url.path();
+ TQCString _dest( TQFile::encodeName(dest));
+
+ // check if the original file exists and we are not allowed to overwrite it
+ KDE_struct_stat buff;
+ bool origExists = (KDE_lstat( _dest.data(), &buff ) != -1);
+ if ( origExists && !overwrite)
+ {
+ if (S_ISDIR(buff.st_mode))
+ error( TDEIO::ERR_DIR_ALREADY_EXIST, url.url() );
+ else
+ error( TDEIO::ERR_FILE_ALREADY_EXIST, url.url() );
+ return;
+ }
+
+ // get the permissions we are supposed to set
+ mode_t initialPerms;
+ if (permissions != -1)
+ initialPerms = permissions | S_IWUSR | S_IRUSR;
+ else
+ initialPerms = 0666;
+
+ // open the destination file
+ int fd = KDE_open(_dest.data(), O_CREAT | O_TRUNC | O_WRONLY, initialPerms);
+ if ( fd < 0 )
+ {
+ kdWarning() << "####################### COULD NOT OPEN " << dest << endl;
+ if ( errno == EACCES )
+ error( TDEIO::ERR_WRITE_ACCESS_DENIED, url.url() );
+ else
+ error( TDEIO::ERR_CANNOT_OPEN_FOR_WRITING, url.url() );
+ return;
+ }
+
+ int result;
+
+ // Loop until we get 0 (end of data)
+ do
+ {
+ TQByteArray buffer;
+ dataReq();
+ result = readData( buffer );
+
+ if (result >= 0)
+ {
+ if (write_all( fd, buffer.data(), buffer.size()))
+ {
+ if ( errno == ENOSPC ) // disk full
+ {
+ error( TDEIO::ERR_DISK_FULL, url.url());
+ result = -1;
+ }
+ else
+ {
+ kdWarning() << "Couldn't write. Error:" << strerror(errno) << endl;
+ error( TDEIO::ERR_COULD_NOT_WRITE, url.url());
+ result = -1;
+ }
+ }
+ }
+ }
+ while ( result > 0 );
+
+ // An error occurred deal with it.
+ if (result < 0)
+ {
+ kdDebug() << "Error during 'put'. Aborting." << endl;
+
+ close(fd);
+ remove(_dest);
+ return;
+ }
+
+ // close the file
+ if ( close(fd) )
+ {
+ kdWarning() << "Error when closing file descriptor:" << strerror(errno) << endl;
+ error( TDEIO::ERR_COULD_NOT_WRITE, url.url());
+ return;
+ }
+
+ // set final permissions
+ if ( permissions != -1 )
+ {
+ if (::chmod(_dest.data(), permissions) != 0)
+ {
+ // couldn't chmod. Eat the error if the filesystem apparently doesn't support it.
+ if ( TDEIO::testFileSystemFlag( _dest, TDEIO::SupportsChmod ) )
+ warning( i18n( "Could not change permissions for\n%1" ).arg( url.url() ) );
+ }
+ }
+
+ // set modification time
+ const TQString mtimeStr = metaData( "modified" );
+ if ( !mtimeStr.isEmpty() ) {
+ TQDateTime dt = TQDateTime::fromString( mtimeStr, TQt::ISODate );
+ if ( dt.isValid() ) {
+ KDE_struct_stat dest_statbuf;
+ if (KDE_stat( _dest.data(), &dest_statbuf ) == 0) {
+ struct utimbuf utbuf;
+ utbuf.actime = dest_statbuf.st_atime; // access time, unchanged
+ utbuf.modtime = dt.toTime_t(); // modification time
+ kdDebug() << k_funcinfo << "setting modtime to " << utbuf.modtime << endl;
+ utime( _dest.data(), &utbuf );
+ }
+ }
+
+ }
+
+ // First check if the file is already in database
+ if (!findImage(album.id, url.fileName()))
+ {
+ // Now insert the file into the database
+ addImage(album.id, m_libraryPath + url.path());
+ }
+
+ // We have done our job => finish
+ finished();
+}
+
+void tdeio_digikamalbums::copy( const KURL &src, const KURL &dst, int mode, bool overwrite )
+{
+// Code duplication from file:// ioslave?
+ kdDebug() << k_funcinfo << "Src: " << src.path() << ", Dst: " << dst.path() << endl;
+
+ // get the album library path
+ TQString libraryPath = src.user();
+ if (libraryPath.isEmpty())
+ {
+ error(TDEIO::ERR_UNKNOWN, "Album Library Path not supplied to tdeioslave");
+ return;
+ }
+
+ // check that the src and dst album library paths match
+ TQString dstLibraryPath = dst.user();
+ if (libraryPath != dstLibraryPath)
+ {
+ error(TDEIO::ERR_UNKNOWN,
+ TQString("Source and Destination have different Album Library Paths. ") +
+ TQString("Src: ") + src.user() +
+ TQString(", Dest: ") + dst.user());
+ return;
+ }
+
+ // open the db if needed
+ if (m_libraryPath != libraryPath)
+ {
+ m_libraryPath = libraryPath;
+ m_sqlDB.closeDB();
+ m_sqlDB.openDB(m_libraryPath);
+ }
+
+ // build the album list
+ buildAlbumList();
+
+ // find the src parent album
+ AlbumInfo srcAlbum = findAlbum(src.directory());
+ if (srcAlbum.id == -1)
+ {
+ error(TDEIO::ERR_UNKNOWN, TQString("Source album %1 not found in database")
+ .arg(src.directory()));
+ return;
+ }
+
+ // find the dst parent album
+ AlbumInfo dstAlbum = findAlbum(dst.directory());
+ if (dstAlbum.id == -1)
+ {
+ error(TDEIO::ERR_UNKNOWN, TQString("Destination album %1 not found in database")
+ .arg(dst.directory()));
+ return;
+ }
+
+ // if the filename is .digikam_properties, we have been asked to copy the
+ // metadata of the src album to the dst album
+ if (src.fileName() == ".digikam_properties")
+ {
+ // no duplication in AlbumDB?
+ // copy metadata of album to destination album
+ m_sqlDB.execSql( TQString("UPDATE Albums SET date='%1', caption='%2', "
+ "collection='%3', icon=%4 ")
+ .arg(srcAlbum.date.toString(TQt::ISODate),
+ escapeString(srcAlbum.caption),
+ escapeString(srcAlbum.collection),
+ TQString::number(srcAlbum.icon)) +
+ TQString( " WHERE id=%1" )
+ .arg(dstAlbum.id) );
+ finished();
+ return;
+ }
+
+ TQCString _src( TQFile::encodeName(libraryPath + src.path()));
+ TQCString _dst( TQFile::encodeName(libraryPath + dst.path()));
+
+ // stat the src file
+ KDE_struct_stat buff_src;
+ if ( KDE_stat( _src.data(), &buff_src ) == -1 )
+ {
+ if ( errno == EACCES )
+ error( TDEIO::ERR_ACCESS_DENIED, src.url() );
+ else
+ error( TDEIO::ERR_DOES_NOT_EXIST, src.url() );
+ return;
+ }
+
+ // bail out if its a directory
+ if ( S_ISDIR( buff_src.st_mode ) )
+ {
+ error( TDEIO::ERR_IS_DIRECTORY, src.url() );
+ return;
+ }
+
+ // bail out if its a socket or fifo
+ if ( S_ISFIFO( buff_src.st_mode ) || S_ISSOCK ( buff_src.st_mode ) )
+ {
+ error( TDEIO::ERR_CANNOT_OPEN_FOR_READING, src.url() );
+ return;
+ }
+
+ // stat the dst file
+ KDE_struct_stat buff_dest;
+ bool dest_exists = ( KDE_lstat( _dst.data(), &buff_dest ) != -1 );
+ if ( dest_exists )
+ {
+ // bail out if its a directory
+ if (S_ISDIR(buff_dest.st_mode))
+ {
+ error( TDEIO::ERR_DIR_ALREADY_EXIST, dst.url() );
+ return;
+ }
+
+ // if !overwrite bail out
+ if (!overwrite)
+ {
+ error( TDEIO::ERR_FILE_ALREADY_EXIST, dst.url() );
+ return;
+ }
+
+ // If the destination is a symlink and overwrite is true,
+ // remove the symlink first to prevent the scenario where
+ // the symlink actually points to current source!
+ if (overwrite && S_ISLNK(buff_dest.st_mode))
+ {
+ remove( _dst.data() );
+ }
+ }
+
+ // now open the src file
+ int src_fd = KDE_open( _src.data(), O_RDONLY);
+ if ( src_fd < 0 )
+ {
+ error( TDEIO::ERR_CANNOT_OPEN_FOR_READING, src.path() );
+ return;
+ }
+
+ // get the permissions we are supposed to set
+ mode_t initialMode;
+ if (mode != -1)
+ initialMode = mode | S_IWUSR;
+ else
+ initialMode = 0666;
+
+ // open the destination file
+ int dest_fd = KDE_open(_dst.data(), O_CREAT | O_TRUNC | O_WRONLY, initialMode);
+ if ( dest_fd < 0 )
+ {
+ kdDebug() << "###### COULD NOT WRITE " << dst.url() << endl;
+ if ( errno == EACCES )
+ {
+ error( TDEIO::ERR_WRITE_ACCESS_DENIED, dst.url() );
+ }
+ else
+ {
+ error( TDEIO::ERR_CANNOT_OPEN_FOR_WRITING, dst.url() );
+ }
+ close(src_fd);
+ return;
+ }
+
+ // emit the total size for copying
+ totalSize( buff_src.st_size );
+
+ TDEIO::filesize_t processed_size = 0;
+ char buffer[ MAX_IPC_SIZE ];
+ int n;
+
+ while (1)
+ {
+ // read in chunks of MAX_IPC_SIZE
+ n = ::read( src_fd, buffer, MAX_IPC_SIZE );
+
+ if (n == -1)
+ {
+ if (errno == EINTR)
+ continue;
+ error( TDEIO::ERR_COULD_NOT_READ, src.path());
+ close(src_fd);
+ close(dest_fd);
+ return;
+ }
+
+ // Finished ?
+ if (n == 0)
+ break;
+
+ // write to the destination file
+ if (write_all( dest_fd, buffer, n))
+ {
+ close(src_fd);
+ close(dest_fd);
+
+ if ( errno == ENOSPC ) // disk full
+ {
+ error( TDEIO::ERR_DISK_FULL, dst.url());
+ remove( _dst.data() );
+ }
+ else
+ {
+ kdWarning() << "Couldn't write[2]. Error:" << strerror(errno) << endl;
+ error( TDEIO::ERR_COULD_NOT_WRITE, dst.url());
+ }
+ return;
+ }
+
+ processedSize( processed_size );
+ }
+
+
+ close( src_fd );
+
+ if (close( dest_fd))
+ {
+ kdWarning() << "Error when closing file descriptor[2]:" << strerror(errno) << endl;
+ error( TDEIO::ERR_COULD_NOT_WRITE, dst.url());
+ return;
+ }
+
+ // set final permissions
+ if ( mode != -1 )
+ {
+ if (::chmod(_dst.data(), mode) != 0)
+ {
+ // Eat the error if the filesystem apparently doesn't support chmod.
+ if ( TDEIO::testFileSystemFlag( _dst, TDEIO::SupportsChmod ) )
+ warning( i18n( "Could not change permissions for\n%1" ).arg( dst.url() ) );
+ }
+ }
+
+ // copy access and modification time
+ struct utimbuf ut;
+ ut.actime = buff_src.st_atime;
+ ut.modtime = buff_src.st_mtime;
+ if ( ::utime( _dst.data(), &ut ) != 0 )
+ {
+ kdWarning() << TQString::fromLatin1("Couldn't preserve access and modification time for\n%1")
+ .arg( dst.url() ) << endl;
+ }
+
+ // now copy the metadata over
+ copyImage(srcAlbum.id, src.fileName(), dstAlbum.id, dst.fileName());
+
+ processedSize( buff_src.st_size );
+ finished();
+}
+
+void tdeio_digikamalbums::rename( const KURL& src, const KURL& dst, bool overwrite )
+{
+// Code duplication from file:// ioslave?
+ kdDebug() << k_funcinfo << "Src: " << src << ", Dst: " << dst << endl;
+
+ // if the filename is .digikam_properties fake that we renamed it
+ if (src.fileName() == ".digikam_properties")
+ {
+ finished();
+ return;
+ }
+
+ TQString libraryPath = src.user();
+ if (libraryPath.isEmpty())
+ {
+ error(TDEIO::ERR_UNKNOWN, "Album Library Path not supplied to tdeioslave");
+ return;
+ }
+
+ TQString dstLibraryPath = dst.user();
+ if (libraryPath != dstLibraryPath)
+ {
+ error(TDEIO::ERR_UNKNOWN,
+ i18n("Source and Destination have different Album Library Paths.\n"
+ "Source: %1\n"
+ "Destination: %2")
+ .arg(src.user())
+ .arg(dst.user()));
+ return;
+ }
+
+ // open album db if needed
+ if (m_libraryPath != libraryPath)
+ {
+ m_libraryPath = libraryPath;
+ m_sqlDB.closeDB();
+ m_sqlDB.openDB(m_libraryPath);
+ }
+
+ TQCString csrc( TQFile::encodeName(libraryPath + src.path()));
+ TQCString cdst( TQFile::encodeName(libraryPath + dst.path()));
+
+ // stat the source file/folder
+ KDE_struct_stat buff_src;
+ if ( KDE_stat( csrc.data(), &buff_src ) == -1 )
+ {
+ if ( errno == EACCES )
+ error( TDEIO::ERR_ACCESS_DENIED, src.url() );
+ else
+ error( TDEIO::ERR_DOES_NOT_EXIST, src.url() );
+ return;
+ }
+
+ // stat the destination file/folder
+ KDE_struct_stat buff_dest;
+ bool dest_exists = ( KDE_stat( cdst.data(), &buff_dest ) != -1 );
+ if ( dest_exists )
+ {
+ if (S_ISDIR(buff_dest.st_mode))
+ {
+ error( TDEIO::ERR_DIR_ALREADY_EXIST, dst.url() );
+ return;
+ }
+
+ if (!overwrite)
+ {
+ error( TDEIO::ERR_FILE_ALREADY_EXIST, dst.url() );
+ return;
+ }
+ }
+
+
+ // build album list
+ buildAlbumList();
+
+ AlbumInfo srcAlbum, dstAlbum;
+
+ // check if we are renaming an album or a image
+ bool renamingAlbum = S_ISDIR(buff_src.st_mode);
+
+ if (renamingAlbum)
+ {
+ srcAlbum = findAlbum(src.path());
+ if (srcAlbum.id == -1)
+ {
+ error(TDEIO::ERR_UNKNOWN, i18n("Source album %1 not found in database")
+ .arg(src.url()));
+ return;
+ }
+ }
+ else
+ {
+ srcAlbum = findAlbum(src.directory());
+ if (srcAlbum.id == -1)
+ {
+ error(TDEIO::ERR_UNKNOWN, i18n("Source album %1 not found in database")
+ .arg(src.directory()));
+ return;
+ }
+
+ dstAlbum = findAlbum(dst.directory());
+ if (dstAlbum.id == -1)
+ {
+ error(TDEIO::ERR_UNKNOWN, i18n("Destination album %1 not found in database")
+ .arg(dst.directory()));
+ return;
+ }
+ }
+
+ // actually rename the file/folder
+ if ( ::rename(csrc.data(), cdst.data()))
+ {
+ if (( errno == EACCES ) || (errno == EPERM))
+ {
+ TQFileInfo toCheck(libraryPath + src.path());
+ if (!toCheck.isWritable())
+ error( TDEIO::ERR_CANNOT_RENAME_ORIGINAL, src.path() );
+ else
+ error( TDEIO::ERR_ACCESS_DENIED, dst.path() );
+ }
+ else if (errno == EXDEV)
+ {
+ error( TDEIO::ERR_UNSUPPORTED_ACTION, i18n("This file/folder is on a different "
+ "filesystem through symlinks. "
+ "Moving/Renaming files between "
+ "them is currently unsupported "));
+ }
+ else if (errno == EROFS)
+ { // The file is on a read-only filesystem
+ error( TDEIO::ERR_CANNOT_DELETE, src.url() );
+ }
+ else {
+ error( TDEIO::ERR_CANNOT_RENAME, src.url() );
+ }
+ return;
+ }
+
+ // renaming done. now update the database
+ if (renamingAlbum)
+ {
+ renameAlbum(srcAlbum.url, dst.path());
+ }
+ else
+ {
+ renameImage(srcAlbum.id, src.fileName(),
+ dstAlbum.id, dst.fileName());
+ }
+
+ finished();
+}
+
+void tdeio_digikamalbums::stat( const KURL& url )
+{
+ TQString libraryPath = url.user();
+ if (libraryPath.isEmpty())
+ {
+ error(TDEIO::ERR_UNKNOWN, "Album Library Path not supplied to tdeioslave");
+ return;
+ }
+
+ TDEIO::UDSEntry entry;
+ if (!createUDSEntry(libraryPath + url.path(), entry))
+ {
+ error(TDEIO::ERR_DOES_NOT_EXIST, url.path(-1));
+ return;
+ }
+
+ statEntry(entry);
+ finished();
+}
+
+void tdeio_digikamalbums::listDir( const KURL& url )
+{
+// Code duplication from file:// ioslave?
+ kdDebug() << k_funcinfo << " : " << url.path() << endl;
+
+ TQString libraryPath = url.user();
+ if (libraryPath.isEmpty())
+ {
+ error(TDEIO::ERR_UNKNOWN, "Album Library Path not supplied to tdeioslave");
+ kdWarning() << "Album Library Path not supplied to tdeioslave" << endl;
+ return;
+ }
+
+ KDE_struct_stat stbuf;
+ TQString path = libraryPath + url.path();
+ if (KDE_stat(TQFile::encodeName(path), &stbuf) != 0)
+ {
+ error(TDEIO::ERR_DOES_NOT_EXIST, url.path(-1));
+ return;
+ }
+
+ TQDir dir(path);
+ if (!dir.isReadable())
+ {
+ error( TDEIO::ERR_CANNOT_ENTER_DIRECTORY, url.path());
+ return;
+ }
+
+ const TQFileInfoList *list = dir.entryInfoList(TQDir::All|TQDir::Hidden);
+ TQFileInfoListIterator it( *list );
+ TQFileInfo *fi;
+
+ TDEIO::UDSEntry entry;
+ createDigikamPropsUDSEntry(entry);
+ listEntry(entry, false);
+ while ((fi = it.current()) != 0)
+ {
+ if (fi->fileName() != "." && fi->fileName() != ".." || fi->extension(true) == "digikamtempfile.tmp")
+ {
+ createUDSEntry(fi->absFilePath(), entry);
+ listEntry(entry, false);
+ }
+ ++it;
+ }
+
+ entry.clear();
+ listEntry(entry, true);
+ finished();
+}
+
+void tdeio_digikamalbums::mkdir( const KURL& url, int permissions )
+{
+// Code duplication from file:// ioslave?
+ kdDebug() << k_funcinfo << " : " << url.url() << endl;
+
+ TQString libraryPath = url.user();
+ if (libraryPath.isEmpty())
+ {
+ error(TDEIO::ERR_UNKNOWN, "Album Library Path not supplied to tdeioslave");
+ return;
+ }
+
+ if (m_libraryPath != libraryPath)
+ {
+ m_libraryPath = libraryPath;
+ m_sqlDB.closeDB();
+ m_sqlDB.openDB(m_libraryPath);
+ }
+
+ TQString path = libraryPath + url.path();
+ TQCString _path( TQFile::encodeName(path));
+
+ KDE_struct_stat buff;
+ if ( KDE_stat( _path, &buff ) == -1 )
+ {
+ if ( ::mkdir( _path.data(), 0777 /*umask will be applied*/ ) != 0 )
+ {
+ if ( errno == EACCES )
+ {
+ error( TDEIO::ERR_ACCESS_DENIED, path );
+ return;
+ }
+ else if ( errno == ENOSPC )
+ {
+ error( TDEIO::ERR_DISK_FULL, path );
+ return;
+ }
+ else
+ {
+ error( TDEIO::ERR_COULD_NOT_MKDIR, path );
+ return;
+ }
+ }
+ else
+ {
+ // code similar to AlbumDB::addAlbum
+ m_sqlDB.execSql( TQString("REPLACE INTO Albums (url, date) "
+ "VALUES('%1','%2')")
+ .arg(escapeString(url.path()),
+ TQDate::currentDate().toString(TQt::ISODate)) );
+
+ if ( permissions != -1 )
+ {
+ if ( ::chmod( _path.data(), permissions ) == -1 )
+ error( TDEIO::ERR_CANNOT_CHMOD, path );
+ else
+ finished();
+ }
+ else
+ finished();
+ return;
+ }
+ }
+
+ if ( S_ISDIR( buff.st_mode ) )
+ {
+ error( TDEIO::ERR_DIR_ALREADY_EXIST, path );
+ return;
+ }
+
+ error( TDEIO::ERR_FILE_ALREADY_EXIST, path );
+}
+
+void tdeio_digikamalbums::chmod( const KURL& url, int permissions )
+{
+// Code duplication from file:// ioslave?
+ kdDebug() << k_funcinfo << " : " << url.url() << endl;
+
+ // get the album library path
+ TQString libraryPath = url.user();
+ if (libraryPath.isEmpty())
+ {
+ error(TDEIO::ERR_UNKNOWN, "Album Library Path not supplied to tdeioslave");
+ return;
+ }
+
+ TQCString path( TQFile::encodeName(libraryPath + url.path()));
+ if ( ::chmod( path.data(), permissions ) == -1 )
+ error( TDEIO::ERR_CANNOT_CHMOD, url.url() );
+ else
+ finished();
+}
+
+void tdeio_digikamalbums::del( const KURL& url, bool isfile)
+{
+// Code duplication from file:// ioslave?
+ kdDebug() << k_funcinfo << " : " << url.url() << endl;
+
+ // get the album library path
+ TQString libraryPath = url.user();
+ if (libraryPath.isEmpty())
+ {
+ error(TDEIO::ERR_UNKNOWN, "Album Library Path not supplied to tdeioslave");
+ return;
+ }
+
+ // open the db if needed
+ if (m_libraryPath != libraryPath)
+ {
+ m_libraryPath = libraryPath;
+ m_sqlDB.closeDB();
+ m_sqlDB.openDB(m_libraryPath);
+ }
+
+ // build the album list
+ buildAlbumList();
+
+ TQCString path( TQFile::encodeName(libraryPath + url.path()));
+
+ if (isfile)
+ {
+ kdDebug( ) << "Deleting file "<< url.url() << endl;
+
+ // if the filename is .digikam_properties fake that we deleted it
+ if (url.fileName() == ".digikam_properties")
+ {
+ finished();
+ return;
+ }
+
+ // find the Album to which this file belongs.
+ AlbumInfo album = findAlbum(url.directory());
+ if (album.id == -1)
+ {
+ error(TDEIO::ERR_UNKNOWN, i18n("Source album %1 not found in database")
+ .arg(url.directory()));
+ return;
+ }
+
+ // actually delete the file
+ if ( unlink( path.data() ) == -1 )
+ {
+ if ((errno == EACCES) || (errno == EPERM))
+ error( TDEIO::ERR_ACCESS_DENIED, url.url());
+ else if (errno == EISDIR)
+ error( TDEIO::ERR_IS_DIRECTORY, url.url());
+ else
+ error( TDEIO::ERR_CANNOT_DELETE, url.url() );
+ return;
+ }
+
+ // successful deletion. now remove file entry from the database
+ delImage(album.id, url.fileName());
+ }
+ else
+ {
+ kdDebug( ) << "Deleting directory " << url.url() << endl;
+
+ // find the corresponding album entry
+ AlbumInfo album = findAlbum(url.path());
+ if (album.id == -1)
+ {
+ error(TDEIO::ERR_UNKNOWN, i18n("Source album %1 not found in database")
+ .arg(url.path()));
+ return;
+ }
+
+ if ( ::rmdir( path.data() ) == -1 )
+ {
+ // TODO handle symlink delete
+
+ if ((errno == EACCES) || (errno == EPERM))
+ {
+ error( TDEIO::ERR_ACCESS_DENIED, url.url());
+ return;
+ }
+ else
+ {
+ kdDebug() << "could not rmdir " << perror << endl;
+ error( TDEIO::ERR_COULD_NOT_RMDIR, url.url() );
+ return;
+ }
+ }
+
+ // successful deletion. now remove album entry from the database
+ delAlbum(album.id);
+ }
+
+ finished();
+
+}
+
+bool tdeio_digikamalbums::createUDSEntry(const TQString& path, TDEIO::UDSEntry& entry)
+{
+ entry.clear();
+
+ KDE_struct_stat stbuf;
+ if (KDE_stat(TQFile::encodeName(path), &stbuf) != 0)
+ return false;
+
+ TDEIO::UDSAtom atom;
+
+ atom.m_uds = TDEIO::UDS_FILE_TYPE;
+ atom.m_long = stbuf.st_mode & S_IFMT;
+ entry.append( atom );
+
+ atom.m_uds = TDEIO::UDS_ACCESS;
+ atom.m_long = stbuf.st_mode & 07777;
+ entry.append( atom );
+
+ atom.m_uds = TDEIO::UDS_SIZE;
+ atom.m_long = stbuf.st_size;
+ entry.append( atom );
+
+ atom.m_uds = TDEIO::UDS_MODIFICATION_TIME;
+ atom.m_long = stbuf.st_mtime;
+ entry.append( atom );
+
+ atom.m_uds = TDEIO::UDS_ACCESS_TIME;
+ atom.m_long = stbuf.st_atime;
+ entry.append( atom );
+
+ atom.m_uds = TDEIO::UDS_NAME;
+ atom.m_str = TQFileInfo(path).fileName();
+ entry.append(atom);
+
+ /*
+ // If we provide the local path, a TDEIO::CopyJob will optimize away
+ // the use of our custom digikamalbums:/ ioslave, which breaks
+ // copying the database entry:
+ // Disabling this as a temporary solution for bug #137282
+ // This code is intended as a fix for bug #122653.
+#if KDE_IS_VERSION(3,4,0)
+ atom.m_uds = TDEIO::UDS_LOCAL_PATH;
+ atom.m_str = path;
+ entry.append(atom);
+#endif
+ */
+
+ return true;
+}
+
+void tdeio_digikamalbums::createDigikamPropsUDSEntry(TDEIO::UDSEntry& entry)
+{
+ entry.clear();
+
+ TDEIO::UDSAtom atom;
+
+ atom.m_uds = TDEIO::UDS_FILE_TYPE;
+ atom.m_long = S_IFREG;
+ entry.append( atom );
+
+ atom.m_uds = TDEIO::UDS_ACCESS;
+ atom.m_long = 00666;
+ entry.append( atom );
+
+ atom.m_uds = TDEIO::UDS_SIZE;
+ atom.m_long = 0;
+ entry.append( atom );
+
+ atom.m_uds = TDEIO::UDS_MODIFICATION_TIME;
+ atom.m_long = TQDateTime::currentDateTime().toTime_t();
+ entry.append( atom );
+
+ atom.m_uds = TDEIO::UDS_ACCESS_TIME;
+ atom.m_long = TQDateTime::currentDateTime().toTime_t();
+ entry.append( atom );
+
+ atom.m_uds = TDEIO::UDS_NAME;
+ atom.m_str = ".digikam_properties";
+ entry.append(atom);
+}
+
+void tdeio_digikamalbums::buildAlbumList()
+{
+// simplified from AlbumDB::scanAlbums()
+ m_albumList.clear();
+
+ TQStringList values;
+ m_sqlDB.execSql( TQString("SELECT id, url, date, caption, collection, icon "
+ "FROM Albums;"), &values );
+
+ 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;
+ info.icon = (*it).toLongLong();
+ ++it;
+
+ m_albumList.append(info);
+ }
+}
+
+AlbumInfo tdeio_digikamalbums::findAlbum(const TQString& url, bool addIfNotExists)
+{
+// similar to AlbumDB::getOrCreateAlbumId
+ AlbumInfo album;
+ for (TQValueList<AlbumInfo>::const_iterator it = m_albumList.begin();
+ it != m_albumList.end(); ++it)
+ {
+ if ((*it).url == url)
+ {
+ album = *it;
+ return album;
+ }
+ }
+
+ album.id = -1;
+
+ if (addIfNotExists)
+ {
+ TQFileInfo fi(m_libraryPath + url);
+ if (!fi.exists() || !fi.isDir())
+ return album;
+
+ m_sqlDB.execSql(TQString("INSERT INTO Albums (url, date) "
+ "VALUES('%1', '%2')")
+ .arg(escapeString(url),
+ fi.lastModified().date().toString(TQt::ISODate)));
+
+ album.id = m_sqlDB.lastInsertedRow();
+ album.url = url;
+ album.date = fi.lastModified().date();
+ album.icon = 0;
+
+ m_albumList.append(album);
+ }
+
+ return album;
+}
+
+void tdeio_digikamalbums::delAlbum(int albumID)
+{
+// code duplication from AlbumDB::deleteAlbum
+ m_sqlDB.execSql(TQString("DELETE FROM Albums WHERE id='%1'")
+ .arg(albumID));
+}
+
+void tdeio_digikamalbums::renameAlbum(const TQString& oldURL, const TQString& newURL)
+{
+// similar to AlbumDB::setAlbumURL, but why more extended?
+ // first update the url of the album which was renamed
+
+ m_sqlDB.execSql( TQString("UPDATE Albums SET url='%1' WHERE url='%2'")
+ .arg(escapeString(newURL),
+ escapeString(oldURL)));
+
+ // now find the list of all subalbums which need to be updated
+ TQStringList values;
+ m_sqlDB.execSql( TQString("SELECT url FROM Albums WHERE url LIKE '%1/%';")
+ .arg(oldURL), &values );
+
+ // and update their url
+ TQString newChildURL;
+ for (TQStringList::iterator it = values.begin(); it != values.end(); ++it)
+ {
+ newChildURL = *it;
+ newChildURL.replace(oldURL, newURL);
+ m_sqlDB.execSql(TQString("UPDATE Albums SET url='%1' WHERE url='%2'")
+ .arg(escapeString(newChildURL),
+ escapeString(*it)));
+ }
+}
+
+bool tdeio_digikamalbums::findImage(int albumID, const TQString& name) const
+{
+// no similar method in AlbumDB?
+ TQStringList values;
+
+ m_sqlDB.execSql( TQString("SELECT name FROM Images "
+ "WHERE dirid=%1 AND name='%2';")
+ .arg(albumID)
+ .arg(escapeString(name)),
+ &values );
+
+ return !(values.isEmpty());
+}
+
+// from albuminfo.h
+class TagInfo
+{
+public:
+
+ typedef TQValueList<TagInfo> List;
+
+ int id;
+ int pid;
+ TQString name;
+ TQString icon;
+};
+
+void tdeio_digikamalbums::addImage(int albumID, const TQString& filePath)
+{
+// Code duplication: ScanLib::storeItemInDatabase, AlbumDB::addItem,
+// AlbumDB::setItemRating, AlbumDB::addItemTag, AlbumDB::addTag
+
+ // from ScanLib::storeItemInDatabase
+ TQString comment;
+ TQDateTime datetime;
+ int rating = 0;
+
+ Digikam::DMetadata metadata(filePath);
+
+ // Trying to get comments from image :
+ // In first, from standard JPEG comments, or
+ // In second, from EXIF comments tag, or
+ // In third, from IPTC comments tag.
+
+ comment = metadata.getImageComment();
+
+ // Trying to get date and time from image :
+ // In first, from EXIF date & time tags, or
+ // In second, from IPTC date & time tags.
+
+ datetime = metadata.getImageDateTime();
+
+ // Trying to get image rating from IPTC Urgency tag.
+ rating = metadata.getImageRating();
+
+ if (!datetime.isValid())
+ {
+ TQFileInfo info(filePath);
+ datetime = info.lastModified();
+ }
+
+ // Try to get image tags from IPTC keywords tags.
+ TQStringList keywordsList = metadata.getImageKeywords();
+
+ // from AlbumDB::addItem
+ m_sqlDB.execSql(TQString("REPLACE INTO Images "
+ "(dirid, name, datetime, caption) "
+ "VALUES(%1, '%2', '%3', '%4')")
+ .arg(TQString::number(albumID),
+ escapeString(TQFileInfo(filePath).fileName()),
+ datetime.toString(TQt::ISODate),
+ escapeString(comment)));
+
+ TQ_LLONG imageID = m_sqlDB.lastInsertedRow();
+
+ // from AlbumDB::setItemRating
+ if (imageID != -1 && rating != -1)
+ {
+ m_sqlDB.execSql(TQString("REPLACE INTO ImageProperties "
+ "(imageid, property, value) "
+ "VALUES(%1, '%2', '%3');")
+ .arg(imageID)
+ .arg("Rating")
+ .arg(rating) );
+ }
+
+ // Set existing tags in database or create new tags if not exist.
+
+ if ( imageID != -1 && !keywordsList.isEmpty() )
+ {
+ TQStringList keywordsList2Create;
+
+ // Create a list of the tags currently in database
+
+ TagInfo::List tagsList;
+
+ TQStringList values;
+ m_sqlDB.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;
+ tagsList.append(info);
+ }
+
+ // For every tag in keywordsList, scan taglist to check if tag already exists.
+
+ for (TQStringList::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 = tagsList.begin();
+ tag != tagsList.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 = tagsList.begin();
+ parentTag != tagsList.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)
+ {
+ // from AlbumDB::addItemTag
+ m_sqlDB.execSql( TQString("REPLACE INTO ImageTags (imageid, tagid) "
+ "VALUES(%1, %2);")
+ .arg(imageID)
+ .arg((*tag).id) );
+ foundTag = true;
+ break;
+ }
+ }
+ }
+
+ if (!foundTag)
+ keywordsList2Create.append(*kwd);
+ }
+
+ // If tags do not exist in database, create them.
+
+ if (!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 = tagsList.begin();
+ tag != tagsList.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
+ // from AlbumDB::addTag
+ m_sqlDB.execSql( TQString("INSERT INTO Tags (pid, name, icon) "
+ "VALUES( %1, '%2', 0)")
+ .arg(parentTagID)
+ .arg(escapeString(*tagName)));
+ tagID = m_sqlDB.lastInsertedRow();
+
+ 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);
+ tagsList.append(info);
+
+ parentTagID = tagID;
+ parentTagExisted = false;
+ }
+
+ // from AlbumDB::addItemTag
+ m_sqlDB.execSql( TQString("REPLACE INTO ImageTags (imageid, tagid) "
+ "VALUES(%1, %2);")
+ .arg(imageID)
+ .arg(tagID) );
+ }
+ }
+ }
+}
+
+void tdeio_digikamalbums::delImage(int albumID, const TQString& name)
+{
+// code duplication from AlbumDB::deleteItem
+ m_sqlDB.execSql( TQString("DELETE FROM Images "
+ "WHERE dirid=%1 AND name='%2';")
+ .arg(albumID)
+ .arg(escapeString(name)) );
+}
+
+void tdeio_digikamalbums::renameImage(int oldAlbumID, const TQString& oldName,
+ int newAlbumID, const TQString& newName)
+{
+// code duplication from AlbumDB::deleteItem, AlbumDB::moveItem
+ // first delete any stale entries for the destination file
+ m_sqlDB.execSql( TQString("DELETE FROM Images "
+ "WHERE dirid=%1 AND name='%2';")
+ .arg(newAlbumID)
+ .arg(escapeString(newName)) );
+
+ // now update the dirid and/or name of the file
+ m_sqlDB.execSql( TQString("UPDATE Images SET dirid=%1, name='%2' "
+ "WHERE dirid=%3 AND name='%4';")
+ .arg(TQString::number(newAlbumID),
+ escapeString(newName),
+ TQString::number(oldAlbumID),
+ escapeString(oldName)) );
+}
+
+void tdeio_digikamalbums::copyImage(int srcAlbumID, const TQString& srcName,
+ int dstAlbumID, const TQString& dstName)
+{
+// code duplication from AlbumDB::copyItem
+ // check for src == dest
+ if (srcAlbumID == dstAlbumID && srcName == dstName)
+ {
+ error( TDEIO::ERR_FILE_ALREADY_EXIST, dstName );
+ return;
+ }
+
+ // find id of src image
+ TQStringList values;
+ m_sqlDB.execSql( TQString("SELECT id FROM Images "
+ "WHERE dirid=%1 AND name='%2';")
+ .arg(TQString::number(srcAlbumID), escapeString(srcName)),
+ &values);
+
+ if (values.isEmpty())
+ {
+ error(TDEIO::ERR_UNKNOWN, i18n("Source image %1 not found in database")
+ .arg(srcName));
+ return;
+ }
+
+ int srcId = values[0].toInt();
+
+ // first delete any stale entries for the destination file
+ m_sqlDB.execSql( TQString("DELETE FROM Images "
+ "WHERE dirid=%1 AND name='%2';")
+ .arg(TQString::number(dstAlbumID), escapeString(dstName)) );
+
+ // copy entry in Images table
+ m_sqlDB.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 = m_sqlDB.lastInsertedRow();
+
+ // copy tags
+ m_sqlDB.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)
+ m_sqlDB.execSql( TQString("INSERT INTO ImageProperties (imageid, property, value) "
+ "SELECT %1, property, value FROM ImageProperties "
+ "WHERE imageid=%2;")
+ .arg(TQString::number(dstId), TQString::number(srcId)) );
+}
+
+void tdeio_digikamalbums::scanAlbum(const TQString& url)
+{
+ scanOneAlbum(url);
+ removeInvalidAlbums();
+}
+
+void tdeio_digikamalbums::scanOneAlbum(const TQString& url)
+{
+ TQDir dir(m_libraryPath + url);
+ if (!dir.exists() || !dir.isReadable())
+ {
+ return;
+ }
+
+ TQString subURL = url;
+ if (!url.endsWith("/"))
+ subURL += '/';
+ subURL = escapeString( subURL);
+
+ {
+ // scan albums
+
+ TQStringList currAlbumList;
+ m_sqlDB.execSql( TQString("SELECT url FROM Albums WHERE ") +
+ TQString("url LIKE '") + subURL + TQString("%' ") +
+ TQString("AND url NOT LIKE '") + subURL + TQString("%/%' "),
+ &currAlbumList );
+
+
+ const TQFileInfoList* infoList = dir.entryInfoList(TQDir::Dirs);
+ if (!infoList)
+ return;
+
+ TQFileInfoListIterator it(*infoList);
+ TQFileInfo* fi;
+
+ TQStringList newAlbumList;
+ while ((fi = it.current()) != 0)
+ {
+ ++it;
+
+ if (fi->fileName() == "." || fi->fileName() == "..")
+ {
+ continue;
+ }
+
+ TQString u = TQDir::cleanDirPath(url + '/' + fi->fileName());
+
+ if (currAlbumList.contains(u))
+ {
+ continue;
+ }
+
+ newAlbumList.append(u);
+ }
+
+ for (TQStringList::iterator it = newAlbumList.begin();
+ it != newAlbumList.end(); ++it)
+ {
+ kdDebug() << "New Album: " << *it << endl;
+
+ TQFileInfo fi(m_libraryPath + *it);
+ m_sqlDB.execSql(TQString("INSERT INTO Albums (url, date) "
+ "VALUES('%1', '%2')")
+ .arg(escapeString(*it),
+ fi.lastModified().date().toString(TQt::ISODate)));
+
+ scanAlbum(*it);
+ }
+ }
+
+ if (url != "/")
+ {
+ // scan files
+
+ TQStringList values;
+
+ m_sqlDB.execSql( TQString("SELECT id FROM Albums WHERE url='%1'")
+ .arg(escapeString(url)), &values );
+ if (values.isEmpty())
+ return;
+
+ int albumID = values.first().toInt();
+
+ TQStringList currItemList;
+ m_sqlDB.execSql( TQString("SELECT name FROM Images WHERE dirid=%1")
+ .arg(albumID), &currItemList );
+
+ const TQFileInfoList* infoList = dir.entryInfoList(TQDir::Files);
+ if (!infoList)
+ return;
+
+ TQFileInfoListIterator it(*infoList);
+ TQFileInfo* fi;
+
+ // add any new files we find to the db
+ while ((fi = it.current()) != 0)
+ {
+ ++it;
+
+ // ignore temp files we created ourselves
+ if (fi->extension(true) == "digikamtempfile.tmp")
+ {
+ continue;
+ }
+
+ if (currItemList.contains(fi->fileName()))
+ {
+ currItemList.remove(fi->fileName());
+ continue;
+ }
+
+ addImage(albumID, m_libraryPath + url + '/' + fi->fileName());
+ }
+
+ // currItemList now contains deleted file list. remove them from db
+ for (TQStringList::iterator it = currItemList.begin();
+ it != currItemList.end(); ++it)
+ {
+ delImage(albumID, *it);
+ }
+ }
+}
+
+void tdeio_digikamalbums::removeInvalidAlbums()
+{
+ TQStringList urlList;
+
+ m_sqlDB.execSql(TQString("SELECT url FROM Albums;"),
+ &urlList);
+
+ m_sqlDB.execSql("BEGIN TRANSACTION");
+
+ struct stat stbuf;
+
+ for (TQStringList::iterator it = urlList.begin();
+ it != urlList.end(); ++it)
+ {
+ if (::stat(TQFile::encodeName(m_libraryPath + *it), &stbuf) == 0)
+ continue;
+
+ kdDebug() << "Deleted Album: " << *it << endl;
+ m_sqlDB.execSql(TQString("DELETE FROM Albums WHERE url='%1'")
+ .arg(escapeString(*it)));
+ }
+
+ m_sqlDB.execSql("COMMIT TRANSACTION");
+}
+
+/* TDEIO slave registration */
+
+extern "C"
+{
+ DIGIKAM_EXPORT int kdemain(int argc, char **argv)
+ {
+ TDELocale::setMainCatalogue("digikam");
+ TDEInstance instance( "tdeio_digikamalbums" );
+ TDEGlobal::locale();
+
+ if (argc != 4) {
+ kdDebug() << "Usage: tdeio_digikamalbums protocol domain-socket1 domain-socket2"
+ << endl;
+ exit(-1);
+ }
+
+ tdeio_digikamalbums slave(argv[2], argv[3]);
+ slave.dispatchLoop();
+
+ return 0;
+ }
+}
diff --git a/src/tdeioslave/digikamalbums.h b/src/tdeioslave/digikamalbums.h
new file mode 100644
index 00000000..c75e8c9c
--- /dev/null
+++ b/src/tdeioslave/digikamalbums.h
@@ -0,0 +1,108 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2005-04-21
+ * Description : a dio-slave to process file operations on
+ * digiKam albums.
+ *
+ * Copyright (C) 2005 by Renchi Raju <[email protected]>
+ *
+ * Lots of the file io code is copied from KDE file tdeioslave.
+ * Copyright for the KDE file tdeioslave follows:
+ * Copyright (C) 2000-2002 Stephan Kulow <[email protected]>
+ * Copyright (C) 2000-2002 David Faure <[email protected]>
+ * Copyright (C) 2000-2002 Waldo Bastian <[email protected]>
+ *
+ * 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.
+ * ============================================================ */
+
+#ifndef DIGIKAMALBUMS_H
+#define DIGIKAMALBUMS_H
+
+// TQt includes.
+
+#include <tqvaluelist.h>
+#include <tqdatetime.h>
+#include <sqlitedb.h>
+
+// KDE includes.
+
+#include <tdeio/slavebase.h>
+
+class TQStringList;
+
+class AlbumInfo
+{
+public:
+
+ int id;
+ TQ_LLONG icon;
+ TQString url;
+ TQString caption;
+ TQString collection;
+ TQDate date;
+};
+
+class tdeio_digikamalbums : public TDEIO::SlaveBase
+{
+
+public:
+
+ tdeio_digikamalbums(const TQCString &pool_socket,
+ const TQCString &app_socket);
+ ~tdeio_digikamalbums();
+
+ void special(const TQByteArray& data);
+
+ void get( const KURL& url );
+ void put( const KURL& url, int _mode, bool _overwrite, bool _resume );
+ void copy( const KURL &src, const KURL &dest, int mode, bool overwrite );
+ void rename( const KURL &src, const KURL &dest, bool overwrite );
+
+ void stat( const KURL& url );
+ void listDir( const KURL& url );
+ void mkdir( const KURL& url, int permissions );
+ void chmod( const KURL& url, int permissions );
+ void del( const KURL& url, bool isfile);
+
+private:
+
+ bool createUDSEntry(const TQString& path, TDEIO::UDSEntry& entry);
+ void createDigikamPropsUDSEntry(TDEIO::UDSEntry& entry);
+
+ void buildAlbumList();
+ AlbumInfo findAlbum(const TQString& url, bool addIfNotExists=true);
+ void delAlbum(int albumID);
+ void renameAlbum(const TQString& oldURL, const TQString& newURL);
+ bool findImage(int albumID, const TQString& name) const;
+ void addImage(int albumID, const TQString& filePath);
+ void delImage(int albumID, const TQString& name);
+ void renameImage(int oldAlbumID, const TQString& oldName,
+ int newAlbumID, const TQString& newName);
+ void copyImage(int srcAlbumID, const TQString& srcName,
+ int dstAlbumID, const TQString& dstName);
+
+ void scanAlbum(const TQString& url);
+ void scanOneAlbum(const TQString& url);
+ void removeInvalidAlbums();
+
+private:
+
+ SqliteDB m_sqlDB;
+ TQString m_libraryPath;
+ TQValueList<AlbumInfo> m_albumList;
+};
+
+
+#endif /* DIGIKAMALBUMS_H */
diff --git a/src/tdeioslave/digikamalbums.protocol b/src/tdeioslave/digikamalbums.protocol
new file mode 100644
index 00000000..e2b094af
--- /dev/null
+++ b/src/tdeioslave/digikamalbums.protocol
@@ -0,0 +1,45 @@
+[Protocol]
+exec=tdeio_digikamalbums
+protocol=digikamalbums
+input=stream
+output=stream
+listing=Name,Type,Date,AccessDate,Access
+reading=true
+writing=true
+makedir=true
+linking=false
+deleting=true
+moving=true
+maxInstances=1
+Description=digiKam albums tdeioslave
+Description[br]=tdeioslave an albomoù evit Digikam
+Description[ca]=Kioslave d'àlbums del digiKam
+Description[da]=digiKam albummer-tdeioslave
+Description[de]=digiKam Ein-/Ausgabemodul für digiKam-Alben
+Description[el]=tdeioslave Άλμπουμ του digiKam
+Description[es]=tdeioslave de digiKam para álbumes
+Description[et]=DigiKami albumite TDEIO-moodul
+Description[fa]=tdeioslave آلبومهای digiKam
+Description[fi]=digiKamin tdeioslave-palvelu albumeille
+Description[gl]=Kioslave de albuns de digiKam
+Description[hr]=digiKam tdeioslave za albume
+Description[is]=digiKam albúma tdeioslave
+Description[it]=Kioslave degli album di digiKam
+Description[ja]=digiKam アルバム tdeioslave
+Description[nds]=In-/Utgaavmoduul för Alben vun digiKam
+Description[nl]=Digikam Albums tdeioslave
+Description[pa]=ਡਿਜ਼ੀਕੈਮ ਐਲਬਮ tdeioslave
+Description[pl]=Wtyczka protokołu albumów digiKam
+Description[pt]='tdeioslave' de álbuns do digiKam
+Description[pt_BR]='tdeioslave' de álbuns do digiKam
+Description[ru]=Digikam tdeioslave - альбомы
+Description[sk]=tdeioslave albumov digiKamu
+Description[sr]=tdeioslave digiKam-ових албума
+Description[sr@Latn]=tdeioslave digiKam-ovih albuma
+Description[sv]=I/O-slav för Digikams album
+Description[tr]=digiKam albümler tdeioslave
+Description[uk]=Підлеглий В/В альбомів для Digikam
+Description[vi]=tdeioslave tập ảnh digiKam
+Description[xx]=xxdigiKam albums tdeioslavexx
+Class=:local
+Icon=digikam
diff --git a/src/tdeioslave/digikamdates.cpp b/src/tdeioslave/digikamdates.cpp
new file mode 100644
index 00000000..9e7f4e27
--- /dev/null
+++ b/src/tdeioslave/digikamdates.cpp
@@ -0,0 +1,313 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2005-04-21
+ * Description : a tdeio-slave to process date query on
+ * digiKam albums.
+ *
+ * Copyright (C) 2005 by Renchi Raju <[email protected]>
+ * Copyright (C) 2006-2008 by Gilles Caulier <caulier dot gilles at gmail dot com>
+ *
+ * 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 <sys/stat.h>
+#include <sys/types.h>
+#include <sys/time.h>
+}
+
+// C++ includes.
+
+#include <cstdlib>
+#include <cstdio>
+#include <ctime>
+
+// TQt includes.
+
+#include <tqfile.h>
+#include <tqdatastream.h>
+#include <tqregexp.h>
+#include <tqbuffer.h>
+
+// KDE includes.
+
+#include <tdeio/global.h>
+#include <tdeglobal.h>
+#include <tdelocale.h>
+#include <kinstance.h>
+#include <tdefilemetainfo.h>
+#include <kdebug.h>
+
+// Local includes.
+
+#include "digikam_export.h"
+#include "digikamdates.h"
+
+tdeio_digikamdates::tdeio_digikamdates(const TQCString &pool_socket,
+ const TQCString &app_socket)
+ : SlaveBase("tdeio_digikamdates", pool_socket, app_socket)
+{
+}
+
+tdeio_digikamdates::~tdeio_digikamdates()
+{
+}
+
+static TQValueList<TQRegExp> makeFilterList( const TQString &filter )
+{
+ TQValueList<TQRegExp> 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<TQRegExp>& filters,
+ const TQString &fileName )
+{
+ TQValueList<TQRegExp>::ConstIterator rit = filters.begin();
+ while ( rit != filters.end() ) {
+ if ( (*rit).exactMatch(fileName) )
+ return true;
+ ++rit;
+ }
+ return false;
+}
+
+void tdeio_digikamdates::special(const TQByteArray& data)
+{
+ bool folders = (metaData("folders") == "yes");
+
+ TQString libraryPath;
+ KURL kurl;
+ TQString url;
+ TQString filter;
+ int getDimensions;
+ int recurseAlbums;
+ int recurseTags;
+
+ TQDataStream ds(data, IO_ReadOnly);
+ ds >> libraryPath;
+ ds >> kurl;
+ ds >> filter;
+ ds >> getDimensions;
+ ds >> recurseAlbums;
+ ds >> recurseTags;
+
+ url = kurl.path();
+
+ TQValueList<TQRegExp> regex = makeFilterList(filter);
+
+ if (m_libraryPath != libraryPath)
+ {
+ m_libraryPath = libraryPath;
+ m_db.closeDB();
+ m_db.openDB(libraryPath);
+ }
+
+ TQByteArray ba;
+
+ if (folders) // Special mode to stats all dates from collection
+ {
+ TQMap<TQDateTime, int> datesStatMap;
+ TQStringList values;
+ TQString name, dateStr;
+ TQDateTime dateTime;
+
+ m_db.execSql( "SELECT name, datetime FROM Images;", &values );
+
+ for ( TQStringList::iterator it = values.begin(); it != values.end(); )
+ {
+ name = *it;
+ ++it;
+ dateStr = *it;
+ ++it;
+
+ if ( !matchFilterList( regex, name ) )
+ continue;
+
+ dateTime = TQDateTime::fromString( dateStr, TQt::ISODate );
+ if ( !dateTime.isValid() )
+ continue;
+
+ TQMap<TQDateTime, int>::iterator it2 = datesStatMap.find(dateTime);
+ if ( it2 == datesStatMap.end() )
+ {
+ datesStatMap.insert( dateTime, 1 );
+ }
+ else
+ {
+ datesStatMap.replace( dateTime, it2.data() + 1 );
+ }
+ }
+
+ TQDataStream os(ba, IO_WriteOnly);
+ os << datesStatMap;
+ }
+ else
+ {
+ TQStringList subpaths = TQStringList::split("/", url, false);
+ if (subpaths.count() == 4)
+ {
+ int yrStart = TQString(subpaths[0]).toInt();
+ int moStart = TQString(subpaths[1]).toInt();
+ int yrEnd = TQString(subpaths[2]).toInt();
+ int moEnd = TQString(subpaths[3]).toInt();
+
+ TQString moStartStr, moEndStr;
+ moStartStr.sprintf("%.2d", moStart);
+ moEndStr.sprintf("%.2d", moEnd);
+
+ TQStringList values;
+
+ m_db.execSql(TQString("SELECT Images.id, Images.name, Images.dirid, \n "
+ " Images.datetime, Albums.url \n "
+ "FROM Images, Albums \n "
+ "WHERE Images.datetime < '%1-%2-01' \n "
+ "AND Images.datetime >= '%3-%4-01' \n "
+ "AND Albums.id=Images.dirid \n "
+ "ORDER BY Albums.id;")
+ .arg(yrEnd, 4)
+ .arg(moEndStr, 2)
+ .arg(yrStart, 4)
+ .arg(moStartStr, 2),
+ &values, 0, false);
+
+ 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<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 TQDataStream(ba, IO_WriteOnly);
+ }
+ }
+
+ delete os;
+ }
+ }
+
+ SlaveBase::data(ba);
+
+ finished();
+}
+
+/* TDEIO slave registration */
+
+extern "C"
+{
+ DIGIKAM_EXPORT int kdemain(int argc, char **argv)
+ {
+ TDELocale::setMainCatalogue("digikam");
+ TDEInstance instance( "tdeio_digikamdates" );
+ TDEGlobal::locale();
+
+ if (argc != 4) {
+ kdDebug() << "Usage: tdeio_digikamdates protocol domain-socket1 domain-socket2"
+ << endl;
+ exit(-1);
+ }
+
+ tdeio_digikamdates slave(argv[2], argv[3]);
+ slave.dispatchLoop();
+
+ return 0;
+ }
+}
+
diff --git a/src/tdeioslave/digikamdates.h b/src/tdeioslave/digikamdates.h
new file mode 100644
index 00000000..4ba75749
--- /dev/null
+++ b/src/tdeioslave/digikamdates.h
@@ -0,0 +1,54 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2005-04-21
+ * Description : a tdeio-slave to process date query on
+ * digiKam albums.
+ *
+ * Copyright (C) 2005 by Renchi Raju <[email protected]>
+ *
+ * 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.
+ * ============================================================ */
+
+#ifndef DIGIKAMDATES_H
+#define DIGIKAMDATES_H
+
+// KDE includes.
+
+#include <tdeio/slavebase.h>
+
+// Local includes.
+
+#include "sqlitedb.h"
+
+class TQStringList;
+
+class tdeio_digikamdates : public TDEIO::SlaveBase
+{
+
+public:
+
+ tdeio_digikamdates(const TQCString &pool_socket,
+ const TQCString &app_socket);
+ ~tdeio_digikamdates();
+
+ void special(const TQByteArray& data);
+
+private:
+
+ SqliteDB m_db;
+ TQString m_libraryPath;
+};
+
+#endif /* DIGIKAMDATES_H */
diff --git a/src/tdeioslave/digikamdates.protocol b/src/tdeioslave/digikamdates.protocol
new file mode 100644
index 00000000..a21352bd
--- /dev/null
+++ b/src/tdeioslave/digikamdates.protocol
@@ -0,0 +1,44 @@
+[Protocol]
+exec=tdeio_digikamdates
+protocol=digikamdates
+input=stream
+output=stream
+listing=Name,Type
+reading=false
+writing=false
+makedir=false
+linking=false
+deleting=false
+moving=false
+maxInstances=1
+Description=digiKam dates tdeioslave
+Description[ca]=Kioslave de dates del digiKam
+Description[da]=Digikam datoer-tdeioslave
+Description[de]=digiKam Ein-/Ausgabemodul für digiKam-Datumsangaben
+Description[el]=tdeioslave ημερομηνιών του digiKam
+Description[es]=tdeioslave de digiKam para fechas
+Description[et]=DigiKami kuupäevade TDEIO-moodul
+Description[fa]=tdeioslave تاریخهای digiKam
+Description[fi]=digiKamin tdeioslave-palvelu päiväyksille
+Description[gl]=Kioslave de datas de digiKam
+Description[hr]=digiKam tdeioslave za datume
+Description[is]=digiKam dagsetninga tdeioslave
+Description[it]=Kioslave delle date di digiKam
+Description[ja]=digiKam 日付 tdeioslave
+Description[nds]=In-/Utgaavmoduul för Daten vun digiKam
+Description[nl]=Digikam Datums tdeioslave
+Description[pa]=ਡਿਜ਼ੀਕੈਮ ਮਿਤੀ tdeioslave
+Description[pl]=Wtyczka protokołu dat digiKama
+Description[pt]='tdeioslave' de datas do digiKam
+Description[pt_BR]='tdeioslave' de datas do digiKam
+Description[ru]=Digikam tdeioslave - даты
+Description[sk]=tdeioslave dátumov digiKamu
+Description[sr]=tdeioslave digiKam-ових датума
+Description[sr@Latn]=tdeioslave digiKam-ovih datuma
+Description[sv]=I/O-slav för Digikams datum
+Description[tr]=digiKam tarihler tdeioslave
+Description[uk]=Підлеглий В/В дат для digiKam
+Description[vi]=tdeioslave ngày tháng digiKam
+Description[xx]=xxdigiKam dates tdeioslavexx
+Class=:local
+Icon=digikam
diff --git a/src/tdeioslave/digikamsearch.cpp b/src/tdeioslave/digikamsearch.cpp
new file mode 100644
index 00000000..225a900f
--- /dev/null
+++ b/src/tdeioslave/digikamsearch.cpp
@@ -0,0 +1,734 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2005-04-21
+ * Description : a tdeio-slave to process search on digiKam albums
+ *
+ * Copyright (C) 2005 by Renchi Raju <[email protected]>
+ *
+ * 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>
+
+// TQt includes.
+
+#include <tqfile.h>
+#include <tqdatastream.h>
+#include <tqtextstream.h>
+#include <tqregexp.h>
+#include <tqdir.h>
+#include <tqvariant.h>
+#include <tqmap.h>
+
+// KDE includes.
+
+#include <tdeglobal.h>
+#include <tdelocale.h>
+#include <kcalendarsystem.h>
+#include <kinstance.h>
+#include <tdefilemetainfo.h>
+#include <kmimetype.h>
+#include <kdebug.h>
+#include <tdeio/global.h>
+#include <tdeio/ioslave_defaults.h>
+#include <klargefile.h>
+
+// Local includes.
+
+#include "digikam_export.h"
+#include "digikamsearch.h"
+
+tdeio_digikamsearch::tdeio_digikamsearch(const TQCString &pool_socket,
+ const TQCString &app_socket)
+ : SlaveBase("tdeio_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();
+ }
+}
+
+tdeio_digikamsearch::~tdeio_digikamsearch()
+{
+}
+
+static TQValueList<TQRegExp> makeFilterList( const TQString &filter )
+{
+ TQValueList<TQRegExp> 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<TQRegExp>& filters,
+ const TQString &fileName )
+{
+ TQValueList<TQRegExp>::ConstIterator rit = filters.begin();
+ while ( rit != filters.end() )
+ {
+ if ( (*rit).exactMatch(fileName) )
+ return true;
+ ++rit;
+ }
+ return false;
+}
+
+void tdeio_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<TQRegExp> 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<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 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 tdeio_digikamsearch::buildQuery(const KURL& url) const
+{
+ int count = url.queryItem("count").toInt();
+ if (count <= 0)
+ return TQString();
+
+ TQMap<int, RuleType> 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<SKey> todo;
+ todo.append( ALBUMNAME );
+ todo.append( IMAGENAME );
+ todo.append( TAGNAME );
+ todo.append( ALBUMCAPTION );
+ todo.append( ALBUMCOLLECTION );
+ todo.append( IMAGECAPTION );
+ todo.append( RATING );
+
+ sqlQuery += '(';
+ TQValueListIterator<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;
+}
+
+TQString tdeio_digikamsearch::subQuery(enum tdeio_digikamsearch::SKey key,
+ enum tdeio_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`, `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("$$@@$$", 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, TQt::ISODate);
+ if (!date.isValid())
+ return query;
+
+ query = TQString(" (Images.datetime > '%1' AND Images.datetime < '%2') ")
+ .arg(date.addDays(-1).toString(TQt::ISODate))
+ .arg(date.addDays( 1).toString(TQt::ISODate));
+ }
+
+ return query;
+}
+
+/* TDEIO slave registration */
+
+extern "C"
+{
+ DIGIKAM_EXPORT int kdemain(int argc, char **argv)
+ {
+ TDELocale::setMainCatalogue("digikam");
+ TDEInstance instance( "tdeio_digikamsearch" );
+ TDEGlobal::locale();
+
+ if (argc != 4)
+ {
+ kdDebug() << "Usage: tdeio_digikamsearch protocol domain-socket1 domain-socket2"
+ << endl;
+ exit(-1);
+ }
+
+ tdeio_digikamsearch slave(argv[2], argv[3]);
+ slave.dispatchLoop();
+
+ return 0;
+ }
+}
+
+TQString tdeio_digikamsearch::possibleDate(const TQString& str, bool& exact) const
+{
+ TQDate date = TQDate::fromString(str, TQt::ISODate);
+ if (date.isValid())
+ {
+ exact = true;
+ return date.toString(TQt::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();
+}
diff --git a/src/tdeioslave/digikamsearch.h b/src/tdeioslave/digikamsearch.h
new file mode 100644
index 00000000..a17a6c82
--- /dev/null
+++ b/src/tdeioslave/digikamsearch.h
@@ -0,0 +1,100 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2005-04-21
+ * Description : a tdeio-slave to process search on digiKam albums
+ *
+ * Copyright (C) 2005 by Renchi Raju <[email protected]>
+ *
+ * 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.
+ * ============================================================ */
+
+#ifndef DIGIKAMSEARCH_H
+#define DIGIKAMSEARCH_H
+
+// KDE includes.
+
+#include <tdeio/slavebase.h>
+
+// Local includes.
+
+#include "sqlitedb.h"
+
+class TQStringList;
+
+class tdeio_digikamsearch : public TDEIO::SlaveBase
+{
+
+public:
+
+ enum SKey
+ {
+ ALBUM = 0,
+ ALBUMNAME,
+ ALBUMCAPTION,
+ ALBUMCOLLECTION,
+ TAG,
+ TAGNAME,
+ IMAGENAME,
+ IMAGECAPTION,
+ IMAGEDATE,
+ KEYWORD,
+ RATING
+ };
+
+ enum SOperator
+ {
+ EQ = 0,
+ NE,
+ LT,
+ GT,
+ LIKE,
+ NLIKE,
+ LTE,
+ GTE
+ };
+
+public:
+
+ tdeio_digikamsearch(const TQCString &pool_socket, const TQCString &app_socket);
+ ~tdeio_digikamsearch();
+
+ void special(const TQByteArray& data);
+
+private:
+
+ TQString buildQuery(const KURL& url) const;
+
+ TQString subQuery(enum SKey key, enum SOperator op, const TQString& val) const;
+
+ TQString possibleDate(const TQString& str, bool& exact) const;
+
+private:
+
+ class RuleType
+ {
+ public:
+
+ SKey key;
+ SOperator op;
+ TQString val;
+ };
+
+ SqliteDB m_db;
+ TQString m_libraryPath;
+ TQString m_longMonths[12];
+ TQString m_shortMonths[12];
+};
+
+#endif /* DIGIKAMSEARCH_H */
diff --git a/src/tdeioslave/digikamsearch.protocol b/src/tdeioslave/digikamsearch.protocol
new file mode 100644
index 00000000..be2f3d4a
--- /dev/null
+++ b/src/tdeioslave/digikamsearch.protocol
@@ -0,0 +1,38 @@
+[Protocol]
+exec=tdeio_digikamsearch
+protocol=digikamsearch
+input=none
+output=filesystem,stream
+maxInstances=1
+Description=digiKam search tdeioslave
+Description[br]=tdeioslave klask evit Digikam
+Description[ca]=Kioslave de cerca del digiKam
+Description[da]=Digikam søge-tdeioslave
+Description[de]=digiKam Ein-/Ausgabemodul für digiKam-Suchen
+Description[el]=tdeioslave αναζήτησης του digiKam
+Description[es]=tdeioslave de digiKam de búsqueda
+Description[et]=DigiKami otsimise TDEIO-moodul
+Description[fa]=tdeioslave جستجوی digiKam
+Description[fi]=digiKamin tdeioslave-palvelu hauille
+Description[gl]=Kioslave de procura de digiKam
+Description[hr]=digiKam tdeioslave za pretraživanje
+Description[is]=digiKam leitar tdeioslave
+Description[it]=Kioslave delle ricerche di digiKam
+Description[ja]=digiKam 検索 tdeioslave
+Description[nds]=In-/Utgaavmoduul för de Söök vun digiKam
+Description[nl]=Digikam Zoeken tdeioslave
+Description[pa]=ਡਿਜ਼ੀਕੈਮ ਖੋਜ tdeioslave
+Description[pl]=Wtyczka protokołu do przeszukiwania digiKama
+Description[pt]='tdeioslave' de procura do digiKam
+Description[pt_BR]='tdeioslave' de procura do digiKam
+Description[ru]=Digikam tdeioslave - поиск
+Description[sk]=tdeioslave vyhľadávaní digiKamu
+Description[sr]=tdeioslave digiKam-ових тражења
+Description[sr@Latn]=tdeioslave digiKam-ovih traženja
+Description[sv]=I/O-slav för Digikams sökning
+Description[tr]=digiKam arama tdeioslave
+Description[uk]=Підлеглий В/В пошуку для digiKam
+Description[vi]=tdeioslave tìm kiếm digiKam
+Description[xx]=xxdigiKam search tdeioslavexx
+Class=:local
+Icon=digikam
diff --git a/src/tdeioslave/digikamtags.cpp b/src/tdeioslave/digikamtags.cpp
new file mode 100644
index 00000000..3fd14987
--- /dev/null
+++ b/src/tdeioslave/digikamtags.cpp
@@ -0,0 +1,325 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2004-07-09
+ * Description : a tdeio-slave to process tag query on
+ * digiKam albums.
+ *
+ * Copyright (C) 2004 by Renchi Raju <[email protected]>
+ *
+ * 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 <sys/stat.h>
+#include <sys/types.h>
+#include <sys/time.h>
+}
+
+// C++ includes.
+
+#include <cstdlib>
+#include <cstdio>
+#include <ctime>
+
+// TQt incudes.
+
+#include <tqfile.h>
+#include <tqfileinfo.h>
+#include <tqstring.h>
+#include <tqdir.h>
+#include <tqregexp.h>
+
+// KDE includes.
+
+#include <kinstance.h>
+#include <kdebug.h>
+#include <kurl.h>
+#include <tdelocale.h>
+#include <tdeglobal.h>
+#include <kstandarddirs.h>
+#include <tdeio/global.h>
+#include <tdefilemetainfo.h>
+
+// Local includes.
+
+#include "digikam_export.h"
+#include "digikamtags.h"
+
+tdeio_digikamtagsProtocol::tdeio_digikamtagsProtocol(const TQCString &pool_socket,
+ const TQCString &app_socket)
+ : SlaveBase("tdeio_digikamtags", pool_socket, app_socket)
+{
+}
+
+tdeio_digikamtagsProtocol::~tdeio_digikamtagsProtocol()
+{
+}
+
+static TQValueList<TQRegExp> makeFilterList( const TQString &filter )
+{
+ TQValueList<TQRegExp> 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<TQRegExp>& filters,
+ const TQString &fileName )
+{
+ TQValueList<TQRegExp>::ConstIterator rit = filters.begin();
+ while ( rit != filters.end() ) {
+ if ( (*rit).exactMatch(fileName) )
+ return true;
+ ++rit;
+ }
+ return false;
+}
+
+void tdeio_digikamtagsProtocol::special(const TQByteArray& data)
+{
+ bool folders = (metaData("folders") == "yes");
+
+ TQString libraryPath;
+ KURL kurl;
+ TQString url;
+ TQString filter;
+ int getDimensions;
+ int tagID;
+ int recurseAlbums;
+ int recurseTags;
+
+ TQDataStream ds(data, IO_ReadOnly);
+ ds >> libraryPath;
+ ds >> kurl;
+ ds >> filter;
+ ds >> getDimensions;
+ ds >> recurseAlbums;
+ ds >> recurseTags;
+
+ url = kurl.path();
+
+ TQValueList<TQRegExp> regex = makeFilterList(filter);
+
+ if (m_libraryPath != libraryPath)
+ {
+ m_libraryPath = libraryPath;
+ m_db.closeDB();
+ m_db.openDB(libraryPath);
+ }
+
+ TQByteArray ba;
+
+ if (folders) // Special mode to stats all tag items
+ {
+ TQMap<int, int> tagsStatMap;
+ int tagID, imageID;
+ TQStringList values, allTagIDs;
+
+ // initialize allTagIDs with all existing tags from db to prevent
+ // wrong tag counters
+ m_db.execSql(TQString("SELECT id from Tags"), &allTagIDs);
+
+ for ( TQStringList::iterator it = allTagIDs.begin(); it != allTagIDs.end(); ++it)
+ {
+ tagID = (*it).toInt();
+ tagsStatMap.insert(tagID, 0);
+ }
+
+ // now we can count the tags assigned to images
+ m_db.execSql(TQString("SELECT ImageTags.tagid, Images.name FROM ImageTags, Images "
+ "WHERE ImageTags.imageid=Images.id"), &values);
+
+ for ( TQStringList::iterator it = values.begin(); it != values.end(); )
+ {
+ tagID = (*it).toInt();
+ ++it;
+
+ if ( matchFilterList( regex, *it ) )
+ {
+ TQMap<int, int>::iterator it2 = tagsStatMap.find(tagID);
+ if ( it2 == tagsStatMap.end() )
+ tagsStatMap.insert(tagID, 1);
+ else
+ tagsStatMap.replace(tagID, it2.data() + 1);
+ }
+
+ ++it;
+ }
+
+ TQDataStream os(ba, IO_WriteOnly);
+ os << tagsStatMap;
+ }
+ else
+ {
+ tagID = TQStringList::split('/',url).last().toInt();
+
+ TQStringList values;
+
+ if (recurseTags)
+ {
+ // Obtain all images with the given tag, or with this tag as a parent.
+ m_db.execSql( TQString( "SELECT DISTINCT Images.id, Images.name, Images.dirid, \n "
+ " Images.datetime, Albums.url \n "
+ " FROM Images, Albums \n "
+ " WHERE Images.id IN \n "
+ " (SELECT imageid FROM ImageTags \n "
+ " WHERE tagid=%1 \n "
+ " OR tagid IN (SELECT id FROM TagsTree WHERE pid=%2)) \n "
+ " AND Albums.id=Images.dirid \n " )
+ .arg(tagID)
+ .arg(tagID), &values );
+ }
+ else
+ {
+ // Obtain all images with the given tag
+ m_db.execSql( TQString( "SELECT DISTINCT Images.id, Images.name, Images.dirid, \n "
+ " Images.datetime, Albums.url \n "
+ " FROM Images, Albums \n "
+ " WHERE Images.id IN \n "
+ " (SELECT imageid FROM ImageTags \n "
+ " WHERE tagid=%1) \n "
+ " AND Albums.id=Images.dirid \n " )
+ .arg(tagID), &values );
+ }
+
+ TQ_LLONG imageid;
+ TQString name;
+ TQString path;
+ int dirid;
+ TQString date;
+ TQString purl;
+ TQSize dims;
+
+ int count = 0;
+ TQDataStream* os = new TQDataStream(ba, IO_WriteOnly);
+
+ struct stat stbuf;
+ 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<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 TQDataStream(ba, IO_WriteOnly);
+ }
+ }
+
+ delete os;
+ }
+
+ SlaveBase::data(ba);
+
+ finished();
+}
+
+/* TDEIO slave registration */
+
+extern "C"
+{
+ DIGIKAM_EXPORT int kdemain(int argc, char **argv)
+ {
+ TDELocale::setMainCatalogue("digikam");
+ TDEInstance instance( "tdeio_digikamtags" );
+ ( void ) TDEGlobal::locale();
+
+ kdDebug() << "*** tdeio_digikamtag started ***" << endl;
+
+ if (argc != 4) {
+ kdDebug() << "Usage: tdeio_digikamtags protocol domain-socket1 domain-socket2"
+ << endl;
+ exit(-1);
+ }
+
+ tdeio_digikamtagsProtocol slave(argv[2], argv[3]);
+ slave.dispatchLoop();
+
+ kdDebug() << "*** tdeio_digikamtags finished ***" << endl;
+ return 0;
+ }
+}
+
diff --git a/src/tdeioslave/digikamtags.h b/src/tdeioslave/digikamtags.h
new file mode 100644
index 00000000..9f9099d4
--- /dev/null
+++ b/src/tdeioslave/digikamtags.h
@@ -0,0 +1,60 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2004-07-09
+ * Description : a tdeio-slave to process tag query on
+ * digiKam albums.
+ *
+ * Copyright (C) 2004 by Renchi Raju <[email protected]>
+ *
+ * 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.
+ *
+ * ============================================================ */
+
+#ifndef DIGIKAMTAGS_H
+#define DIGIKAMTAGS_H
+
+// TQt includes.
+
+#include <tqstring.h>
+
+// KDE includes.
+
+#include <tdeio/slavebase.h>
+
+// Local includes.
+
+#include "sqlitedb.h"
+
+class KURL;
+class TQCString;
+
+class tdeio_digikamtagsProtocol : public TDEIO::SlaveBase
+{
+public:
+
+ tdeio_digikamtagsProtocol(const TQCString &pool_socket,
+ const TQCString &app_socket);
+ virtual ~tdeio_digikamtagsProtocol();
+
+ void special(const TQByteArray& data);
+
+private:
+
+ SqliteDB m_db;
+ TQString m_libraryPath;
+};
+
+
+#endif /* DIGIKAMTAGS_H */
diff --git a/src/tdeioslave/digikamtags.protocol b/src/tdeioslave/digikamtags.protocol
new file mode 100644
index 00000000..88c33e65
--- /dev/null
+++ b/src/tdeioslave/digikamtags.protocol
@@ -0,0 +1,44 @@
+[Protocol]
+exec=tdeio_digikamtags
+protocol=digikamtags
+input=none
+output=filesystem
+listing=Name,Type
+reading=true
+writing=false
+makedir=false
+linking=false
+deleting=false
+moving=false
+maxInstances=1
+Description=digikam tags tdeioslave
+Description[ca]=Kioslave d'etiquetes del digiKam
+Description[da]=Digikam mærker-tdeioslave
+Description[de]=digiKam Ein-/Ausgabemodul für digiKam-Stichwörter
+Description[el]=tdeioslave ετικετών του digiKam
+Description[es]=tdeioslave de digiKampara etiquetas
+Description[et]=DigiKami siltide TDEIO-moodul
+Description[fa]=tdeioslave برچسبهای digikam
+Description[fi]=digiKamin tdeioslave-palvelu tunnisteille
+Description[gl]=Kioslave de marcas de digiKam
+Description[hr]=digiKam tdeioslave za oznake
+Description[is]=Digikam merkja tdeioslave
+Description[it]=Kioslave dei tag di digiKam
+Description[ja]=digiKam タグ tdeioslave
+Description[nds]=In-/Utgaavmoduul för Betekers vun digiKam
+Description[nl]=Digikam Tags tdeioslave
+Description[pa]=ਡਿਜ਼ੀਕੈਮ ਟੈਗ tdeioslave
+Description[pl]=Wtyczka protokołu do znaczników digiKama
+Description[pt]='tdeioslave' de marcas do digiKam
+Description[pt_BR]='tdeioslave' de marcas do digiKam
+Description[ru]=Digikam tdeioslave - метки
+Description[sk]=tdeioslave tagov digiKamu
+Description[sr]=tdeioslave Digikam-ових ознака
+Description[sr@Latn]=tdeioslave Digikam-ovih oznaka
+Description[sv]=I/O-slav för Digikams etiketter
+Description[tr]=digiKam etiketler tdeioslave
+Description[uk]=Підлеглий В/В міток для digiKam
+Description[vi]=tdeioslave thẻ digiKam
+Description[xx]=xxdigikam tags tdeioslavexx
+Class=:local
+Icon=digikam
diff --git a/src/tdeioslave/digikamthumbnail.cpp b/src/tdeioslave/digikamthumbnail.cpp
new file mode 100644
index 00000000..3dd4df28
--- /dev/null
+++ b/src/tdeioslave/digikamthumbnail.cpp
@@ -0,0 +1,635 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2003-01-15
+ * Description : digiKam TDEIO slave to get image thumbnails.
+ * This tdeio-slave support this freedesktop
+ * specification about thumbnails mamagement:
+ * http://jens.triq.net/thumbnail-spec
+ *
+ * Copyright (C) 2003-2005 by Renchi Raju <[email protected]>
+ * Copyright (C) 2003-2009 by Gilles Caulier <caulier dot gilles at gmail dot com>
+ *
+ * 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.
+ *
+ * ============================================================ */
+
+#define XMD_H
+#define PNG_BYTES_TO_CHECK 4
+
+// C++ includes.
+
+#include <cstdlib>
+#include <cstdio>
+#include <cstring>
+
+// TQt includes.
+
+#include <tqcstring.h>
+#include <tqstring.h>
+#include <tqimage.h>
+#include <tqdatastream.h>
+#include <tqfile.h>
+#include <tqfileinfo.h>
+#include <tqdir.h>
+#include <tqwmatrix.h>
+#include <tqregexp.h>
+#include <tqapplication.h>
+
+// KDE includes.
+
+#include <kdebug.h>
+#include <kurl.h>
+#include <kinstance.h>
+#include <kimageio.h>
+#include <tdelocale.h>
+#include <tdeglobal.h>
+#include <kstandarddirs.h>
+#include <kmdcodec.h>
+#include <tdetempfile.h>
+#include <ktrader.h>
+#include <klibloader.h>
+#include <kmimetype.h>
+#include <kprocess.h>
+#include <tdeio/global.h>
+#include <tdeio/thumbcreator.h>
+#include <tdefilemetainfo.h>
+
+// LibKDcraw includes.
+
+#include <libkdcraw/version.h>
+#include <libkdcraw/kdcraw.h>
+
+#if KDCRAW_VERSION < 0x000106
+#include <libkdcraw/dcrawbinary.h>
+#endif
+
+// Local includes.
+
+#include "dimg.h"
+#include "dmetadata.h"
+#include "jpegutils.h"
+#include "digikamthumbnail.h"
+#include "digikam_export.h"
+
+// C Ansi includes.
+
+extern "C"
+{
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <sys/time.h>
+#include <png.h>
+}
+
+using namespace TDEIO;
+using namespace Digikam;
+
+tdeio_digikamthumbnailProtocol::tdeio_digikamthumbnailProtocol(int argc, char** argv)
+ : SlaveBase("tdeio_digikamthumbnail", argv[2], argv[3])
+{
+ argc_ = argc;
+ argv_ = argv;
+ app_ = 0;
+ digiKamFingerPrint = TQString("Digikam Thumbnail Generator");
+ createThumbnailDirs();
+}
+
+tdeio_digikamthumbnailProtocol::~tdeio_digikamthumbnailProtocol()
+{
+}
+
+void tdeio_digikamthumbnailProtocol::get(const KURL& url)
+{
+ int size = metaData("size").toInt();
+ bool exif = (metaData("exif") == "yes");
+
+ cachedSize_ = (size <= 128) ? 128 : 256;
+
+ if (cachedSize_ <= 0)
+ {
+ error(TDEIO::ERR_INTERNAL, i18n("No or invalid size specified"));
+ kdWarning() << "No or invalid size specified" << endl;
+ return;
+ }
+
+ // generate the thumbnail path
+ TQString uri = KURL(url.path()).url(); // "file://" uri
+ KMD5 md5( TQFile::encodeName(uri).data() );
+ TQString thumbPath = (cachedSize_ == 128) ? smallThumbPath_ : bigThumbPath_;
+ thumbPath += TQFile::encodeName( md5.hexDigest() + ".png" ).data();
+
+ TQImage img;
+ bool regenerate = true;
+
+ // stat the original file
+ struct stat st;
+ if (::stat(TQFile::encodeName(url.path(-1)), &st) != 0)
+ {
+ error(TDEIO::ERR_INTERNAL, i18n("File does not exist"));
+ return;
+ }
+
+ // NOTE: if thumbnail have not been generated by digiKam (konqueror for example),
+ // force to recompute it, else we use it.
+
+ img = loadPNG(thumbPath);
+ if (!img.isNull())
+ {
+ if (img.text("Thumb::MTime") == TQString::number(st.st_mtime) &&
+ img.text("Software") == digiKamFingerPrint)
+ regenerate = false;
+ }
+
+ if (regenerate)
+ {
+ // To speed-up thumb extraction, we trying to load image using the file extension.
+ if ( !loadByExtension(img, url.path()) )
+ {
+ // Try JPEG loading : JPEG files without using Exif Thumb.
+ if ( !loadJPEG(img, url.path()) )
+ {
+ // Try to load with dcraw : RAW files.
+ if (!KDcrawIface::KDcraw::loadDcrawPreview(img, url.path()) )
+ {
+ // Try to load with DImg : TIFF, PNG, etc.
+ if (!loadDImg(img, url.path()) )
+ {
+ // Try to load with KDE thumbcreators : video files and others stuff.
+ loadKDEThumbCreator(img, url.path());
+ }
+ }
+ }
+ }
+
+ if (img.isNull())
+ {
+ error(TDEIO::ERR_INTERNAL, i18n("Cannot create thumbnail for %1")
+ .arg(url.prettyURL()));
+ kdWarning() << "Cannot create thumbnail for " << url.path() << endl;
+ return;
+ }
+
+ if (TQMAX(img.width(),img.height()) != cachedSize_)
+ img = img.smoothScale(cachedSize_, cachedSize_, TQImage::ScaleMin);
+
+ if (img.depth() != 32)
+ img = img.convertDepth(32);
+
+ if (exif)
+ exifRotate(url.path(), img);
+
+ img.setText(TQString("Thumb::URI").latin1(), 0, uri);
+ img.setText(TQString("Thumb::MTime").latin1(), 0, TQString::number(st.st_mtime));
+ img.setText(TQString("Software").latin1(), 0, digiKamFingerPrint);
+
+ KTempFile temp(thumbPath + "-digikam-", ".png");
+ if (temp.status() == 0)
+ {
+ img.save(temp.name(), "PNG", 0);
+ ::rename(TQFile::encodeName(temp.name()),
+ TQFile::encodeName(thumbPath));
+ }
+ }
+
+ img = img.smoothScale(size, size, TQImage::ScaleMin);
+
+ if (img.isNull())
+ {
+ error(TDEIO::ERR_INTERNAL, "Thumbnail is null");
+ return;
+ }
+
+ TQByteArray imgData;
+ TQDataStream stream( imgData, IO_WriteOnly );
+
+ const TQString shmid = metaData("shmid");
+ if (shmid.isEmpty())
+ {
+ stream << img;
+ }
+ else
+ {
+ void *shmaddr = shmat(shmid.toInt(), 0, 0);
+
+ if (shmaddr == (void *)-1)
+ {
+ error(TDEIO::ERR_INTERNAL, "Failed to attach to shared memory segment " + shmid);
+ kdWarning() << "Failed to attach to shared memory segment " << shmid << endl;
+ return;
+ }
+
+ if (img.width() * img.height() > cachedSize_ * cachedSize_)
+ {
+ error(TDEIO::ERR_INTERNAL, "Image is too big for the shared memory segment");
+ kdWarning() << "Image is too big for the shared memory segment" << endl;
+ shmdt((char*)shmaddr);
+ return;
+ }
+
+ stream << img.width() << img.height() << img.depth();
+ memcpy(shmaddr, img.bits(), img.numBytes());
+ shmdt((char*)shmaddr);
+ }
+
+ data(imgData);
+ finished();
+}
+
+bool tdeio_digikamthumbnailProtocol::loadByExtension(TQImage& image, const TQString& path)
+{
+ TQFileInfo fileInfo(path);
+ if (!fileInfo.exists())
+ return false;
+
+ // Try to use embedded preview image from metadata.
+ DMetadata metadata(path);
+ if (metadata.getImagePreview(image))
+ {
+ kdDebug() << "Use Exif/Iptc preview extraction. Size of image: "
+ << image.width() << "x" << image.height() << endl;
+ return true;
+ }
+
+ // Else, use the right way depending of image file extension.
+ TQString ext = fileInfo.extension(false).upper();
+#if KDCRAW_VERSION < 0x000106
+ TQString rawFilesExt(KDcrawIface::DcrawBinary::instance()->rawFiles());
+#else
+ TQString rawFilesExt(KDcrawIface::KDcraw::rawFiles());
+#endif
+
+ if (!ext.isEmpty())
+ {
+ if (ext == TQString("JPEG") || ext == TQString("JPG") || ext == TQString("JPE"))
+ return (loadJPEG(image, path));
+ else if (ext == TQString("PNG"))
+ return (loadDImg(image, path));
+ else if (ext == TQString("TIFF") || ext == TQString("TIF"))
+ return (loadDImg(image, path));
+ else if (rawFilesExt.upper().contains(ext))
+ return (KDcrawIface::KDcraw::loadDcrawPreview(image, path));
+ }
+
+ return false;
+}
+
+bool tdeio_digikamthumbnailProtocol::loadJPEG(TQImage& image, const TQString& path)
+{
+ return Digikam::loadJPEGScaled(image, path, cachedSize_);
+}
+
+void tdeio_digikamthumbnailProtocol::exifRotate(const TQString& filePath, TQImage& thumb)
+{
+ // Rotate thumbnail based on metadata orientation information
+
+ DMetadata metadata(filePath);
+ DMetadata::ImageOrientation orientation = metadata.getImageOrientation();
+
+ if (orientation == DMetadata::ORIENTATION_NORMAL ||
+ orientation == DMetadata::ORIENTATION_UNSPECIFIED)
+ return;
+
+ TQWMatrix matrix;
+
+ switch (orientation)
+ {
+ case DMetadata::ORIENTATION_NORMAL:
+ case DMetadata::ORIENTATION_UNSPECIFIED:
+ break;
+
+ case DMetadata::ORIENTATION_HFLIP:
+ matrix.scale(-1, 1);
+ break;
+
+ case DMetadata::ORIENTATION_ROT_180:
+ matrix.rotate(180);
+ break;
+
+ case DMetadata::ORIENTATION_VFLIP:
+ matrix.scale(1, -1);
+ break;
+
+ case DMetadata::ORIENTATION_ROT_90_HFLIP:
+ matrix.scale(-1, 1);
+ matrix.rotate(90);
+ break;
+
+ case DMetadata::ORIENTATION_ROT_90:
+ matrix.rotate(90);
+ break;
+
+ case DMetadata::ORIENTATION_ROT_90_VFLIP:
+ matrix.scale(1, -1);
+ matrix.rotate(90);
+ break;
+
+ case DMetadata::ORIENTATION_ROT_270:
+ matrix.rotate(270);
+ break;
+ }
+
+ // transform accordingly
+ thumb = thumb.xForm(matrix);
+}
+
+TQImage tdeio_digikamthumbnailProtocol::loadPNG(const TQString& path)
+{
+ png_uint_32 w32, h32;
+ int w, h;
+ bool has_alpha;
+ bool has_grey;
+ FILE *f;
+ png_structp png_ptr = NULL;
+ png_infop info_ptr = NULL;
+ int bit_depth, color_type, interlace_type;
+
+ has_alpha = 0;
+ has_grey = 0;
+
+ TQImage qimage;
+
+ f = fopen(path.latin1(), "rb");
+ if (!f)
+ return qimage;
+
+ unsigned char buf[PNG_BYTES_TO_CHECK];
+
+ fread(buf, 1, PNG_BYTES_TO_CHECK, f);
+ if (png_sig_cmp(buf, 0, PNG_BYTES_TO_CHECK))
+ {
+ fclose(f);
+ return qimage;
+ }
+ rewind(f);
+
+ png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if (!png_ptr)
+ {
+ fclose(f);
+ return qimage;
+ }
+
+ info_ptr = png_create_info_struct(png_ptr);
+ if (!info_ptr)
+ {
+ png_destroy_read_struct(&png_ptr, NULL, NULL);
+ fclose(f);
+ return qimage;
+ }
+
+ if (setjmp(png_jmpbuf(png_ptr)))
+ {
+ png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+ fclose(f);
+ return qimage;
+ }
+
+ png_init_io(png_ptr, f);
+ png_read_info(png_ptr, info_ptr);
+ png_get_IHDR(png_ptr, info_ptr, (png_uint_32 *) (&w32),
+ (png_uint_32 *) (&h32), &bit_depth, &color_type,
+ &interlace_type, NULL, NULL);
+
+ w = w32;
+ h = h32;
+
+ qimage.create(w, h, 32);
+
+ if (color_type == PNG_COLOR_TYPE_PALETTE)
+ png_set_expand(png_ptr);
+
+ if (color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+ has_alpha = 1;
+
+ if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+ {
+ has_alpha = 1;
+ has_grey = 1;
+ }
+
+ if (color_type == PNG_COLOR_TYPE_GRAY)
+ has_grey = 1;
+
+ unsigned char **lines;
+ int i;
+
+ if (has_alpha)
+ png_set_expand(png_ptr);
+
+ if (TQImage::systemByteOrder() == TQImage::LittleEndian)
+ {
+ png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
+ png_set_bgr(png_ptr);
+ }
+ else
+ {
+ png_set_swap_alpha(png_ptr);
+ png_set_filler(png_ptr, 0xff, PNG_FILLER_BEFORE);
+ }
+
+ /* 16bit color -> 8bit color */
+ if ( bit_depth == 16 )
+ png_set_strip_16(png_ptr);
+
+ /* pack all pixels to byte boundaires */
+
+ png_set_packing(png_ptr);
+ if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
+ png_set_expand(png_ptr);
+
+ lines = (unsigned char **)malloc(h * sizeof(unsigned char *));
+ if (!lines)
+ {
+ png_read_end(png_ptr, info_ptr);
+ png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL);
+ fclose(f);
+ return qimage;
+ }
+
+ if (has_grey)
+ {
+ png_set_gray_to_rgb(png_ptr);
+ if (png_get_bit_depth(png_ptr, info_ptr) < 8)
+ png_set_expand_gray_1_2_4_to_8(png_ptr);
+ }
+
+ int sizeOfUint = sizeof(unsigned int);
+ for (i = 0 ; i < h ; i++)
+ lines[i] = ((unsigned char *)(qimage.bits())) + (i * w * sizeOfUint);
+
+ png_read_image(png_ptr, lines);
+ free(lines);
+
+ png_textp text_ptr;
+ int num_text=0;
+ png_get_text(png_ptr,info_ptr,&text_ptr,&num_text);
+ while (num_text--)
+ {
+ qimage.setText(text_ptr->key,0,text_ptr->text);
+ text_ptr++;
+ }
+
+ png_read_end(png_ptr, info_ptr);
+ png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL);
+ fclose(f);
+
+ return qimage;
+}
+
+// -- Load using DImg ---------------------------------------------------------------------
+
+bool tdeio_digikamthumbnailProtocol::loadDImg(TQImage& image, const TQString& path)
+{
+ Digikam::DImg dimg_im;
+
+ // to disable raw loader - does not work from ioslave
+ dimg_im.setAttribute("noeventloop", true);
+
+ if (!dimg_im.load(path))
+ {
+ return false;
+ }
+
+ image = dimg_im.copyTQImage();
+ org_width_ = image.width();
+ org_height_ = image.height();
+
+ if ( TQMAX(org_width_, org_height_) != cachedSize_ )
+ {
+ TQSize sz(dimg_im.width(), dimg_im.height());
+ sz.scale(cachedSize_, cachedSize_, TQSize::ScaleMin);
+ image.scale(sz.width(), sz.height());
+ }
+
+ new_width_ = image.width();
+ new_height_ = image.height();
+
+ image.setAlphaBuffer(true) ;
+
+ return true;
+}
+
+// -- Load using KDE API ---------------------------------------------------------------------
+
+bool tdeio_digikamthumbnailProtocol::loadKDEThumbCreator(TQImage& image, const TQString& path)
+{
+ // this sucks royally. some of the thumbcreators need an instance of
+ // app running so that they can use pixmap. till they get their
+ // code fixed, we will have to create a qapp instance.
+ if (!app_)
+ app_ = new TQApplication(argc_, argv_);
+
+ TQString mimeType = KMimeType::findByURL(path)->name();
+ if (mimeType.isEmpty())
+ {
+ kdDebug() << "Mimetype not found" << endl;
+ return false;
+ }
+
+ TQString mimeTypeAlt = mimeType.replace(TQRegExp("/.*"), "/*");
+
+ TQString plugin;
+
+ TDETrader::OfferList plugins = TDETrader::self()->query("ThumbCreator");
+ for (TDETrader::OfferList::ConstIterator it = plugins.begin(); it != plugins.end(); ++it)
+ {
+ TQStringList mimeTypes = (*it)->property("MimeTypes").toStringList();
+ for (TQStringList::ConstIterator mt = mimeTypes.begin(); mt != mimeTypes.end(); ++mt)
+ {
+ if ((*mt) == mimeType || (*mt) == mimeTypeAlt)
+ {
+ plugin=(*it)->library();
+ break;
+ }
+ }
+
+ if (!plugin.isEmpty())
+ break;
+ }
+
+ if (plugin.isEmpty())
+ {
+ kdDebug() << "No relevant plugin found " << endl;
+ return false;
+ }
+
+ KLibrary *library = KLibLoader::self()->library(TQFile::encodeName(plugin));
+ if (!library)
+ {
+ kdDebug() << "Plugin library not found " << plugin << endl;
+ return false;
+ }
+
+ ThumbCreator *creator = 0;
+ newCreator create = (newCreator)library->symbol("new_creator");
+ if (create)
+ creator = create();
+
+ if (!creator)
+ {
+ kdDebug() << "Cannot load ThumbCreator " << plugin << endl;
+ return false;
+ }
+
+ if (!creator->create(path, cachedSize_, cachedSize_, image))
+ {
+ kdDebug() << "Cannot create thumbnail for " << path << endl;
+ delete creator;
+ return false;
+ }
+
+ delete creator;
+ return true;
+}
+
+void tdeio_digikamthumbnailProtocol::createThumbnailDirs()
+{
+ TQString path = TQDir::homeDirPath() + "/.thumbnails/";
+
+ smallThumbPath_ = path + "normal/";
+ bigThumbPath_ = path + "large/";
+
+ TDEStandardDirs::makeDir(smallThumbPath_, 0700);
+ TDEStandardDirs::makeDir(bigThumbPath_, 0700);
+}
+
+// -- TDEIO slave registration ---------------------------------------------------------------------
+
+extern "C"
+{
+ DIGIKAM_EXPORT int kdemain(int argc, char **argv)
+ {
+ TDELocale::setMainCatalogue("digikam");
+ TDEInstance instance( "tdeio_digikamthumbnail" );
+ ( void ) TDEGlobal::locale();
+
+ if (argc != 4)
+ {
+ kdDebug() << "Usage: tdeio_digikamthumbnail protocol domain-socket1 domain-socket2"
+ << endl;
+ exit(-1);
+ }
+
+ KImageIO::registerFormats();
+
+ tdeio_digikamthumbnailProtocol slave(argc, argv);
+ slave.dispatchLoop();
+
+ return 0;
+ }
+}
diff --git a/src/tdeioslave/digikamthumbnail.h b/src/tdeioslave/digikamthumbnail.h
new file mode 100644
index 00000000..a83db66b
--- /dev/null
+++ b/src/tdeioslave/digikamthumbnail.h
@@ -0,0 +1,76 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2003-01-15
+ * Description : digiKam TDEIO slave to get image thumbnails.
+ *
+ * Copyright (C) 2003-2005 by Renchi Raju <[email protected]>
+ * Copyright (C) 2003-2009 by Gilles Caulier <caulier dot gilles at gmail dot com>
+ *
+ * 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.
+ *
+ * ============================================================ */
+
+#ifndef _digikamthumbnail_H_
+#define _digikamthumbnail_H_
+
+// KDE includes.
+
+#include <tdeio/slavebase.h>
+
+class TQString;
+class TQImage;
+class TQApplication;
+
+class KURL;
+
+class tdeio_digikamthumbnailProtocol : public TDEIO::SlaveBase
+{
+
+public:
+
+ tdeio_digikamthumbnailProtocol(int argc, char** argv);
+ virtual ~tdeio_digikamthumbnailProtocol();
+ virtual void get(const KURL& url);
+
+private:
+
+ bool loadByExtension(TQImage& image, const TQString& path);
+ bool loadJPEG(TQImage& image, const TQString& path);
+ void exifRotate(const TQString& filePath, TQImage& thumb);
+ TQImage loadPNG(const TQString& path);
+ bool loadDImg(TQImage& image, const TQString& path);
+ bool loadKDEThumbCreator(TQImage& image, const TQString& path);
+ void createThumbnailDirs();
+
+private:
+
+ int cachedSize_;
+
+ int org_width_;
+ int org_height_;
+ int new_width_;
+ int new_height_;
+
+ int argc_;
+ char **argv_;
+
+ TQString digiKamFingerPrint;
+ TQString smallThumbPath_;
+ TQString bigThumbPath_;
+
+ TQApplication *app_;
+};
+
+#endif // _digikamthumbnail_H_
diff --git a/src/tdeioslave/digikamthumbnail.protocol b/src/tdeioslave/digikamthumbnail.protocol
new file mode 100644
index 00000000..3980f895
--- /dev/null
+++ b/src/tdeioslave/digikamthumbnail.protocol
@@ -0,0 +1,38 @@
+[Protocol]
+exec=tdeio_digikamthumbnail
+protocol=digikamthumbnail
+input=stream
+output=stream
+reading=true
+source=false
+Description=digiKam thumbnail tdeioslave
+Description[br]=tdeioslave ar skeudennigoù evit Digikam
+Description[ca]=Kioslave de miniatures del digiKam
+Description[da]=Digikam miniature-tdeioslave
+Description[de]=digiKam Ein-/Ausgabemodul für digiKam-Miniaturbilder
+Description[el]=tdeioslave εικόνων επισκόπησης του digiKam
+Description[es]=tdeioslave de digiKam de miniaturas
+Description[et]=DigiKami pisipiltide TDEIO-moodul
+Description[fa]=tdeioslave ریزنقش digiKam
+Description[fi]=digiKamin tdeioslave-palvelu pienoiskuville
+Description[gl]=Kioslave de miniaturas de digiKam
+Description[hr]=digiKam tdeioslave za sličice
+Description[is]=Digikam smámynda tdeioslave
+Description[it]=Kioslave delle miniature di digiKam
+Description[ja]=digiKam サムネイル tdeioslave
+Description[nds]=In-/Utgaavmoduul för Vöransichtbiller vun digiKam
+Description[nl]=Digikam Miniatuur tdeioslave
+Description[pa]=ਡਿਜ਼ੀਕੈਮ ਥੰਮਨੇਲ tdeioslave
+Description[pl]=Wtyczka protokołu miniaturek digiKama
+Description[pt]='tdeioslave' de miniaturas do digiKam
+Description[pt_BR]='tdeioslave' de miniaturas do digiKam
+Description[ru]=Digikam tdeioslave - миниатюры
+Description[sk]=tdeioslave miniatúr digiKamu
+Description[sr]=tdeioslave digiKam-ових умањених приказа
+Description[sr@Latn]=tdeioslave digiKam-ovih umanjenih prikaza
+Description[sv]=I/O-slav för Digikams miniatyrbilder
+Description[tr]=digiKam küçükresim tdeioslave
+Description[uk]=Підлеглий В/В мініатюр для digiKam
+Description[vi]=tdeioslave ảnh mẫu digiKam
+Description[xx]=xxdigiKam thumbnail tdeioslavexx
+Icon=digikam
diff --git a/src/tdeioslave/sqlitedb.cpp b/src/tdeioslave/sqlitedb.cpp
new file mode 100644
index 00000000..3df6b80b
--- /dev/null
+++ b/src/tdeioslave/sqlitedb.cpp
@@ -0,0 +1,197 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2005-06-05
+ * Description : TQSlite DB interface.
+ *
+ * Copyright (C) 2005 by Renchi Raju <[email protected]>
+ *
+ * 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.
+ *
+ * ============================================================ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h" // Needed for NFS_HACK
+#endif
+
+// TQt includes.
+
+#include <tqstringlist.h>
+#include <tqdir.h>
+#include <tqfile.h>
+
+// KDE includes.
+
+#include <tdeio/global.h>
+#include <kdebug.h>
+
+// SQlite includes.
+
+#include "sqlite3.h"
+
+// Local includes.
+
+#include "sqlitedb.h"
+
+SqliteDB::SqliteDB()
+{
+ m_db = 0;
+}
+
+SqliteDB::~SqliteDB()
+{
+ closeDB();
+}
+
+void SqliteDB::openDB(const TQString& directory)
+{
+ if (m_db)
+ {
+ closeDB();
+ }
+
+ TQString dbPath = directory + "/digikam3.db";
+
+#ifdef NFS_HACK
+ dbPath = TQDir::homeDirPath() + "/.trinity/share/apps/digikam/" +
+ TDEIO::encodeFileName(TQDir::cleanDirPath(dbPath));
+#endif
+
+ sqlite3_open(TQFile::encodeName(dbPath), &m_db);
+ if (m_db == 0)
+ {
+ kdWarning() << "Cannot open database: "
+ << sqlite3_errmsg(m_db)
+ << endl;
+ }
+}
+
+void SqliteDB::closeDB()
+{
+ if (m_db)
+ {
+ sqlite3_close(m_db);
+ m_db = 0;
+ }
+}
+
+bool SqliteDB::execSql(const TQString& sql, TQStringList* const values,
+ TQString* errMsg, bool debug ) const
+{
+ if ( debug )
+ kdDebug() << "SQL-query: " << sql << endl;
+
+ if ( !m_db )
+ {
+ kdWarning() << k_funcinfo << "SQLite pointer == NULL"
+ << endl;
+ if (errMsg)
+ {
+ *errMsg = TQString::fromLatin1("SQLite database not open");
+ }
+ return false;
+ }
+
+ const char* tail;
+ sqlite3_stmt* stmt;
+ int error;
+
+ //compile SQL program to virtual machine
+ error = sqlite3_prepare(m_db, sql.utf8(), -1, &stmt, &tail);
+ if ( error != SQLITE_OK )
+ {
+ kdWarning() << k_funcinfo
+ << "sqlite_compile error: "
+ << sqlite3_errmsg(m_db)
+ << " on query: "
+ << sql << endl;
+ if (errMsg)
+ {
+ *errMsg = TQString::fromLatin1("sqlite_compile error: ") +
+ TQString::fromLatin1(sqlite3_errmsg(m_db)) +
+ TQString::fromLatin1(" on query: ") +
+ sql;
+ }
+ 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 )
+ {
+ kdWarning() << "sqlite_step error: "
+ << sqlite3_errmsg( m_db )
+ << " on query: "
+ << sql << endl;
+ if (errMsg)
+ {
+ *errMsg = TQString::fromLatin1("sqlite_step error: ") +
+ TQString::fromLatin1(sqlite3_errmsg(m_db)) +
+ TQString::fromLatin1(" on query: ") +
+ sql;
+ }
+ return false;
+ }
+
+ return true;
+}
+
+void SqliteDB::setSetting( const TQString& keyword, const TQString& value )
+{
+ execSql( TQString("REPLACE into Settings VALUES ('%1','%2');")
+ .arg( escapeString(keyword) )
+ .arg( escapeString(value) ));
+}
+
+TQString SqliteDB::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];
+}
+
+extern TQString escapeString(const TQString& str)
+{
+ TQString st(str);
+ st.replace( "'", "''" );
+ return st;
+}
+
+TQ_LLONG SqliteDB::lastInsertedRow() const
+{
+ return sqlite3_last_insert_rowid(m_db);
+}
diff --git a/src/tdeioslave/sqlitedb.h b/src/tdeioslave/sqlitedb.h
new file mode 100644
index 00000000..a3b793f8
--- /dev/null
+++ b/src/tdeioslave/sqlitedb.h
@@ -0,0 +1,58 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2005-06-05
+ * Description : TQSlite DB interface.
+ *
+ * Copyright (C) 2005 by Renchi Raju <[email protected]>
+ *
+ * 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.
+ * ============================================================ */
+
+#ifndef SQLITEDB_H
+#define SQLITEDB_H
+
+// TQt includes.
+
+#include <tqstring.h>
+
+class TQStringList;
+
+class SqliteDB
+{
+
+public:
+
+ SqliteDB();
+ ~SqliteDB();
+
+ void openDB(const TQString& directory);
+ void closeDB();
+
+ bool execSql(const TQString& sql, TQStringList* const values = 0,
+ TQString* const errMsg = 0, bool debug = false) const;
+
+ TQ_LLONG lastInsertedRow() const;
+
+ void setSetting( const TQString& keyword, const TQString& value );
+ TQString getSetting( const TQString& keyword );
+
+private:
+
+ mutable struct sqlite3* m_db;
+};
+
+extern TQString escapeString(const TQString& str);
+
+#endif /* SQLITEDB_H */