You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
digikam/digikam/tdeioslave/digikamalbums.cpp

1970 lines
57 KiB

/* ============================================================
*
* This file is a part of digiKam project
* http://www.digikam.org
*
* Date : 2005-04-21
* Description : a kio-slave to process file operations on
* digiKam albums.
*
* Copyright (C) 2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu>
*
* 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 <coolo@kde.org>
* Copyright (C) 2000-2002 David Faure <faure@kde.org>
* Copyright (C) 2000-2002 Waldo Bastian <bastian@kde.org>
*
* 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 <kglobal.h>
#include <klocale.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"
kio_digikamalbums::kio_digikamalbums(const TQCString &pool_socket,
const TQCString &app_socket)
: SlaveBase("kio_digikamalbums", pool_socket, app_socket)
{
}
kio_digikamalbums::~kio_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 kio_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 kio_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 kio_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, Qt::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 kio_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(Qt::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 kio_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 kio_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 kio_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 kio_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(Qt::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 kio_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 kio_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 kio_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 kio_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 kio_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, Qt::ISODate);
++it;
info.caption = *it;
++it;
info.collection = *it;
++it;
info.icon = (*it).toLongLong();
++it;
m_albumList.append(info);
}
}
AlbumInfo kio_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(Qt::ISODate)));
album.id = m_sqlDB.lastInsertedRow();
album.url = url;
album.date = fi.lastModified().date();
album.icon = 0;
m_albumList.append(album);
}
return album;
}
void kio_digikamalbums::delAlbum(int albumID)
{
// code duplication from AlbumDB::deleteAlbum
m_sqlDB.execSql(TQString("DELETE FROM Albums WHERE id='%1'")
.arg(albumID));
}
void kio_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 kio_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 kio_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(Qt::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 kio_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 kio_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 kio_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 kio_digikamalbums::scanAlbum(const TQString& url)
{
scanOneAlbum(url);
removeInvalidAlbums();
}
void kio_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(Qt::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 kio_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");
}
/* KIO slave registration */
extern "C"
{
DIGIKAM_EXPORT int kdemain(int argc, char **argv)
{
KLocale::setMainCatalogue("digikam");
TDEInstance instance( "kio_digikamalbums" );
TDEGlobal::locale();
if (argc != 4) {
kdDebug() << "Usage: kio_digikamalbums protocol domain-socket1 domain-socket2"
<< endl;
exit(-1);
}
kio_digikamalbums slave(argv[2], argv[3]);
slave.dispatchLoop();
return 0;
}
}