diff options
author | Michele Calgaro <[email protected]> | 2024-11-22 18:41:30 +0900 |
---|---|---|
committer | Michele Calgaro <[email protected]> | 2024-11-22 20:55:03 +0900 |
commit | 5bed6e4a4c916a97f8fe4d1b07f7eecf4d733b90 (patch) | |
tree | f89cc49efc9ca1d0e1579ecb079ee7e7088ff8c8 /src/tdeioslave | |
parent | 0bfbf616d9c1fd7abb1bd02732389ab35e5f8771 (diff) | |
download | digikam-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.am | 72 | ||||
-rw-r--r-- | src/tdeioslave/digikamalbums.cpp | 1969 | ||||
-rw-r--r-- | src/tdeioslave/digikamalbums.h | 108 | ||||
-rw-r--r-- | src/tdeioslave/digikamalbums.protocol | 45 | ||||
-rw-r--r-- | src/tdeioslave/digikamdates.cpp | 313 | ||||
-rw-r--r-- | src/tdeioslave/digikamdates.h | 54 | ||||
-rw-r--r-- | src/tdeioslave/digikamdates.protocol | 44 | ||||
-rw-r--r-- | src/tdeioslave/digikamsearch.cpp | 734 | ||||
-rw-r--r-- | src/tdeioslave/digikamsearch.h | 100 | ||||
-rw-r--r-- | src/tdeioslave/digikamsearch.protocol | 38 | ||||
-rw-r--r-- | src/tdeioslave/digikamtags.cpp | 325 | ||||
-rw-r--r-- | src/tdeioslave/digikamtags.h | 60 | ||||
-rw-r--r-- | src/tdeioslave/digikamtags.protocol | 44 | ||||
-rw-r--r-- | src/tdeioslave/digikamthumbnail.cpp | 635 | ||||
-rw-r--r-- | src/tdeioslave/digikamthumbnail.h | 76 | ||||
-rw-r--r-- | src/tdeioslave/digikamthumbnail.protocol | 38 | ||||
-rw-r--r-- | src/tdeioslave/sqlitedb.cpp | 197 | ||||
-rw-r--r-- | src/tdeioslave/sqlitedb.h | 58 |
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 */ |