[Digikam-devel] [digikam] /: On Linux, use Inotify directly for file notification changes
Gilles Caulier
caulier.gilles at gmail.com
Mon Nov 7 22:14:12 GMT 2011
Marcel,
very interesting solution. I'm sure that some bugzilla files will be
closed in the future.
KInotify do not exist in KDELibs yet ? Or do you have customized this
implementation for digiKam ?
Best
Gilles
2011/11/7 Marcel Wiesweg <marcel.wiesweg at gmx.de>:
> Git commit dcf2ba9d7e92d51b049bdd354f82ddbf75e51a31 by Marcel Wiesweg.
> Committed on 07/11/2011 at 22:47.
> Pushed by mwiesweg into branch 'master'.
>
> On Linux, use Inotify directly for file notification changes
>
> Use Sebastian Trueg's KInotify wrapper, if Inotify is available.
> This gives much more detailed reports and especially info when a file
> has been closed after write. For a detailed explanation, see
> http://trueg.wordpress.com/2011/10/13/taking-a-break-from-crash-fixing-for-usability/
> Separate the file watch code from AlbumManager to a new class, AlbumWatch.
> Do not watch directories recursively (convenient API, but often inefficient implementation
> out of our reach). We scan the directories anyway, it is possible and efficient to
> add each directory = album separately.
> If Inotify is not available (non-linux), the previous code based on
> KDirWatch and KIO is still used.
>
> CCMAIL: digikam-devel at kde.org
>
> M +5 -2 CMakeLists.txt
> M +1 -1 digikam/CMakeLists.txt
> M +7 -209 digikam/album/albummanager.cpp
> M +0 -5 digikam/album/albummanager.h
> A +512 -0 digikam/album/albumwatch.cpp [License: GPL (v2+)]
> A +90 -0 digikam/album/albumwatch.h [License: GPL (v2+)]
> A +517 -0 libs/3rdparty/kinotify/kinotify.cpp [License: LGPL (v2+)]
> A +198 -0 libs/3rdparty/kinotify/kinotify.h [License: LGPL (v2+)]
>
> http://commits.kde.org/digikam/dcf2ba9d7e92d51b049bdd354f82ddbf75e51a31
>
> diff --git a/CMakeLists.txt b/CMakeLists.txt
> index 7f2ed8b..327dd79 100644
> --- a/CMakeLists.txt
> +++ b/CMakeLists.txt
> @@ -1293,9 +1293,10 @@ IF(DIGIKAM_CAN_BE_COMPILED)
> ${CMAKE_CURRENT_SOURCE_DIR}/libs/3rdparty/sqlite2/where.c
> )
>
> - SET(libkmemoryinfo_SRCS
> + SET(libkde3rdparty_SRCS
> ${CMAKE_CURRENT_SOURCE_DIR}/libs/3rdparty/kmemoryinfo/kmemoryinfo.cpp
> - )
> + ${CMAKE_CURRENT_SOURCE_DIR}/libs/3rdparty/kinotify/kinotify.cpp
> + )
>
> SET(libhaar_SRCS
> ${CMAKE_CURRENT_SOURCE_DIR}/libs/database/haar/haar.cpp
> @@ -1383,6 +1384,7 @@ IF(DIGIKAM_CAN_BE_COMPILED)
> ${CMAKE_CURRENT_SOURCE_DIR}/digikam/album/albumselectwidget.cpp
> ${CMAKE_CURRENT_SOURCE_DIR}/digikam/album/albumthumbnailloader.cpp
> ${CMAKE_CURRENT_SOURCE_DIR}/digikam/album/albumtreeview.cpp
> + ${CMAKE_CURRENT_SOURCE_DIR}/digikam/album/albumwatch.cpp
>
> ${CMAKE_CURRENT_SOURCE_DIR}/digikam/database/databaseguierrorhandler.cpp
> ${CMAKE_CURRENT_SOURCE_DIR}/digikam/database/dbstatdlg.cpp
> @@ -1496,6 +1498,7 @@ IF(DIGIKAM_CAN_BE_COMPILED)
> ${CMAKE_CURRENT_SOURCE_DIR}/libs/3rdparty/lprof
> ${CMAKE_CURRENT_SOURCE_DIR}/libs/3rdparty/sqlite2
> ${CMAKE_CURRENT_SOURCE_DIR}/libs/3rdparty/kmemoryinfo
> + ${CMAKE_CURRENT_SOURCE_DIR}/libs/3rdparty/kinotify
> ${CMAKE_CURRENT_SOURCE_DIR}/libs/database
> ${CMAKE_CURRENT_SOURCE_DIR}/libs/database/haar
> ${CMAKE_CURRENT_SOURCE_DIR}/libs/database/imagehistory
> diff --git a/digikam/CMakeLists.txt b/digikam/CMakeLists.txt
> index 1f9ed01..e015354 100644
> --- a/digikam/CMakeLists.txt
> +++ b/digikam/CMakeLists.txt
> @@ -39,7 +39,7 @@ SET(digikamcore_LIB_SRCS
> ${libtthread_SRCS}
> ${libversionmanager_SRCS}
> ${libkgeomaphelper_SRCS}
> - ${libkmemoryinfo_SRCS}
> + ${libkde3rdparty_SRCS}
>
> # widgets and dialogs
> ${libcommonwidgets_SRCS}
> diff --git a/digikam/album/albummanager.cpp b/digikam/album/albummanager.cpp
> index b7ea3e3..a198516 100644
> --- a/digikam/album/albummanager.cpp
> +++ b/digikam/album/albummanager.cpp
> @@ -80,6 +80,7 @@ extern "C"
> #include "albumdb.h"
> #include "album.h"
> #include "albumsettings.h"
> +#include "albumwatch.h"
> #include "collectionlocation.h"
> #include "collectionmanager.h"
> #include <config-digikam.h>
> @@ -164,7 +165,7 @@ public:
> dateListJob(0),
> tagListJob(0),
> personListJob(0),
> - dirWatch(0),
> + albumWatch(0),
> rootPAlbum(0),
> rootTAlbum(0),
> rootDAlbum(0),
> @@ -191,16 +192,12 @@ public:
> int dbPort;
> bool dbInternalServer;
>
> - QList<QDateTime> dbPathModificationDateList;
> - QList<QString> dirWatchBlackList;
> -
> KIO::TransferJob* albumListJob;
> KIO::TransferJob* dateListJob;
> KIO::TransferJob* tagListJob;
> KIO::TransferJob* personListJob;
>
> - KDirWatch* dirWatch;
> - QStringList dirWatchAddedDirs;
> + AlbumWatch* albumWatch;
>
> PAlbum* rootPAlbum;
> TAlbum* rootTAlbum;
> @@ -233,24 +230,6 @@ public:
>
> public:
>
> - QList<QDateTime> buildDirectoryModList(const QFileInfo& dbFile)
> - {
> - // retrieve modification dates
> - QList<QDateTime> modList;
> - QFileInfoList fileInfoList = dbFile.dir().entryInfoList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
> -
> - // build list
> - foreach (const QFileInfo& info, fileInfoList)
> - {
> - // ignore digikam4.db and journal and other temporary files
> - if (!dirWatchBlackList.contains(info.fileName()))
> - {
> - modList << info.lastModified();
> - }
> - }
> - return modList;
> - }
> -
> QString labelForAlbumRootAlbum(const CollectionLocation& location)
> {
> QString label = location.label();
> @@ -309,10 +288,7 @@ AlbumManager::AlbumManager()
> : d(new AlbumManagerPriv)
> {
> internalInstance = this;
> - d->dirWatch = new KDirWatch(this);
> -
> - connect(d->dirWatch, SIGNAL(dirty(QString)),
> - this, SLOT(slotDirWatchDirty(QString)));
> + d->albumWatch = new AlbumWatch(this);
>
> // these operations are pretty fast, no need for long queuing
> d->scanPAlbumsTimer = new QTimer(this);
> @@ -670,20 +646,10 @@ bool AlbumManager::setDatabase(const DatabaseParameters& params, bool priority,
> disconnect(DatabaseAccess::databaseWatch(), 0, this, 0);
> }
>
> - d->dbPathModificationDateList.clear();
> + d->albumWatch->clear();
>
> cleanUp();
>
> - foreach (const QString& addedDirectory, d->dirWatchAddedDirs)
> - {
> - d->dirWatch->removeDir(addedDirectory);
> - }
> - d->dirWatchAddedDirs.clear();
> -
> - QDBusConnection::sessionBus().disconnect(QString(), QString(), "org.kde.KDirNotify", "FileMoved", 0, 0);
> - QDBusConnection::sessionBus().disconnect(QString(), QString(), "org.kde.KDirNotify", "FilesAdded", 0, 0);
> - QDBusConnection::sessionBus().disconnect(QString(), QString(), "org.kde.KDirNotify", "FilesRemoved", 0, 0);
> -
> d->currentAlbum = 0;
> emit signalAlbumCurrentChanged(0);
> emit signalAlbumsCleared();
> @@ -741,6 +707,8 @@ bool AlbumManager::setDatabase(const DatabaseParameters& params, bool priority,
> return true;
> }
>
> + d->albumWatch->setDatabaseParameters(params);
> +
> // still suspended from above
> ScanController::instance()->resumeCollectionScan();
>
> @@ -984,16 +952,9 @@ bool AlbumManager::setDatabase(const DatabaseParameters& params, bool priority,
>
> // -- ---------------------------------------------------------
>
> - d->dirWatchBlackList.clear();
> -
> #ifdef USE_THUMBS_DB
> QApplication::setOverrideCursor(Qt::WaitCursor);
>
> - if (params.isSQLite())
> - {
> - d->dirWatchBlackList << "thumbnails-digikam.db" << "thumbnails-digikam.db-journal";
> - }
> -
> ThumbnailLoadThread::initializeThumbnailDatabase(DatabaseAccess::parameters().thumbnailParameters(),
> new DatabaseThumbnailInfoProvider());
>
> @@ -1005,18 +966,6 @@ bool AlbumManager::setDatabase(const DatabaseParameters& params, bool priority,
>
> // -- ---------------------------------------------------------
>
> - // measures to filter out KDirWatch signals caused by database operations
> - if (params.isSQLite())
> - {
> - QFileInfo dbFile(params.SQLiteDatabaseFile());
> - d->dirWatchBlackList << dbFile.fileName() << dbFile.fileName() + "-journal";
> -
> - // ensure this is done after setting up the black list
> - d->dbPathModificationDateList = d->buildDirectoryModList(dbFile);
> - }
> -
> - // -- ---------------------------------------------------------
> -
> #ifdef HAVE_NEPOMUK
>
> if (checkNepomukService())
> @@ -1107,33 +1056,6 @@ void AlbumManager::startScan()
>
> d->changed = false;
>
> - KDirWatch::Method m = d->dirWatch->internalMethod();
> - QString mName("FAM");
> -
> - if (m == KDirWatch::DNotify)
> - {
> - mName = QString("DNotify");
> - }
> - else if (m == KDirWatch::Stat)
> - {
> - mName = QString("Stat");
> - }
> - else if (m == KDirWatch::INotify)
> - {
> - mName = QString("INotify");
> - }
> -
> - kDebug() << "KDirWatch method = " << mName;
> -
> - // connect to KDirNotify
> -
> - QDBusConnection::sessionBus().connect(QString(), QString(), "org.kde.KDirNotify", "FileMoved",
> - this, SLOT(slotKioFileMoved(QString,QString)));
> - QDBusConnection::sessionBus().connect(QString(), QString(), "org.kde.KDirNotify", "FilesAdded",
> - this, SLOT(slotKioFilesAdded(QString)));
> - QDBusConnection::sessionBus().connect(QString(), QString(), "org.kde.KDirNotify", "FilesRemoved",
> - this, SLOT(slotKioFilesDeleted(QStringList)));
> -
> // create root albums
> d->rootPAlbum = new PAlbum(i18n("My Albums"));
> insertPAlbum(d->rootPAlbum, 0);
> @@ -1230,12 +1152,6 @@ void AlbumManager::slotCollectionLocationPropertiesChanged(const CollectionLocat
>
> void AlbumManager::addAlbumRoot(const CollectionLocation& location)
> {
> - if (!d->dirWatch->contains(location.albumRootPath()))
> - {
> - d->dirWatchAddedDirs << location.albumRootPath();
> - d->dirWatch->addDir(location.albumRootPath(), KDirWatch::WatchSubDirs);
> - }
> -
> PAlbum* album = d->albumRootAlbumHash.value(location.id());
>
> if (!album)
> @@ -1251,7 +1167,6 @@ void AlbumManager::addAlbumRoot(const CollectionLocation& location)
>
> void AlbumManager::removeAlbumRoot(const CollectionLocation& location)
> {
> - d->dirWatch->removeDir(location.albumRootPath());
> // retrieve and remove from hash
> PAlbum* album = d->albumRootAlbumHash.take(location.id());
>
> @@ -3399,121 +3314,4 @@ void AlbumManager::slotImageTagChange(const ImageTagChangeset& changeset)
> }
> }
>
> -void AlbumManager::slotNotifyFileChange(const QString& path)
> -{
> - //kDebug() << "Detected file change at" << path;
> - ScanController::instance()->scheduleCollectionScanRelaxed(path);
> -}
> -
> -void AlbumManager::slotDirWatchDirty(const QString& path)
> -{
> - // Filter out dirty signals triggered by changes on the database file
> - foreach (const QString& bannedFile, d->dirWatchBlackList)
> - {
> - if (path.endsWith(bannedFile))
> - {
> - return;
> - }
> - }
> -
> - DatabaseParameters params = DatabaseAccess::parameters();
> -
> - if (params.isSQLite())
> - {
> - QFileInfo info(path);
> - QDir dir;
> -
> - if (info.isDir())
> - {
> - dir = QDir(path);
> - }
> - else
> - {
> - dir = info.dir();
> - }
> -
> - QFileInfo dbFile(params.SQLiteDatabaseFile());
> -
> - // Workaround for broken KDirWatch in KDE 4.2.4
> - if (path.startsWith(dbFile.filePath()))
> - {
> - return;
> - }
> -
> - // is the signal for the directory containing the database file?
> - if (dbFile.dir() == dir)
> - {
> - // retrieve modification dates
> - QList<QDateTime> modList = d->buildDirectoryModList(dbFile);
> -
> - // check for equality
> - if (modList == d->dbPathModificationDateList)
> - {
> - //kDebug() << "Filtering out db-file-triggered dir watch signal";
> - // we can skip the signal
> - return;
> - }
> -
> - // set new list
> - d->dbPathModificationDateList = modList;
> - }
> - }
> -
> - kDebug() << "KDirWatch detected change at" << path;
> -
> - slotNotifyFileChange(path);
> -}
> -
> -void AlbumManager::slotKioFileMoved(const QString& urlFrom, const QString& urlTo)
> -{
> - kDebug() << urlFrom << urlTo;
> - handleKioNotification(KUrl(urlFrom));
> - handleKioNotification(KUrl(urlTo));
> -}
> -
> -void AlbumManager::slotKioFilesAdded(const QString& url)
> -{
> - kDebug() << url;
> - handleKioNotification(KUrl(url));
> -}
> -
> -void AlbumManager::slotKioFilesDeleted(const QStringList& urls)
> -{
> - kDebug() << urls;
> - foreach (const QString& url, urls)
> - {
> - handleKioNotification(KUrl(url));
> - }
> -}
> -
> -void AlbumManager::handleKioNotification(const KUrl& url)
> -{
> - if (url.isLocalFile())
> - {
> - QString path = url.directory();
> -
> - //kDebug() << path << !CollectionManager::instance()->albumRootPath(path).isEmpty();
> - // check path is in our collection
> - if (CollectionManager::instance()->albumRootPath(path).isNull())
> - {
> - return;
> - }
> -
> - kDebug() << "KDirNotify detected file change at" << path;
> -
> - slotNotifyFileChange(path);
> - }
> - else
> - {
> - DatabaseUrl dbUrl(url);
> -
> - if (dbUrl.isAlbumUrl())
> - {
> - QString path = dbUrl.fileUrl().directory();
> - kDebug() << "KDirNotify detected file change at" << path;
> - slotNotifyFileChange(path);
> - }
> - }
> -}
> -
> } // namespace Digikam
> diff --git a/digikam/album/albummanager.h b/digikam/album/albummanager.h
> index d8a577d..4710a0f 100644
> --- a/digikam/album/albummanager.h
> +++ b/digikam/album/albummanager.h
> @@ -658,11 +658,6 @@ private Q_SLOTS:
> void slotPeopleJobResult(KJob* job);
> void slotPeopleJobData(KIO::Job* job, const QByteArray& data);
>
> - void slotDirWatchDirty(const QString& path);
> - void slotKioFileMoved(const QString& urlFrom, const QString& urlTo);
> - void slotKioFilesDeleted(const QStringList& urls);
> - void slotKioFilesAdded(const QString& directory);
> - void slotNotifyFileChange(const QString& directory);
> void slotCollectionLocationStatusChanged(const CollectionLocation&, int);
> void slotCollectionLocationPropertiesChanged(const CollectionLocation& location);
> void slotAlbumChange(const AlbumChangeset& changeset);
> diff --git a/digikam/album/albumwatch.cpp b/digikam/album/albumwatch.cpp
> new file mode 100644
> index 0000000..5011062
> --- /dev/null
> +++ b/digikam/album/albumwatch.cpp
> @@ -0,0 +1,512 @@
> +/* ============================================================
> + *
> + * This file is a part of digiKam project
> + * http://www.digikam.org
> + *
> + * Date : 2011-11-07
> + * Description : Directory watch interface
> + *
> + * Copyright (C) 2011 by Marcel Wiesweg <marcel.wiesweg at gmx.de>
> + *
> + * 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.
> + *
> + * ============================================================ */
> +
> +#include "albumwatch.moc"
> +
> +// Qt includes
> +
> +#include <QDateTime>
> +#include <QDBusConnection>
> +#include <QDir>
> +#include <QFileInfo>
> +
> +// KDE includes
> +
> +#include <kdebug.h>
> +#include <kdirwatch.h>
> +
> +// Local includes
> +
> +#include "album.h"
> +#include "albummanager.h"
> +#include "collectionlocation.h"
> +#include "collectionmanager.h"
> +#include "databaseparameters.h"
> +#include "kinotify.h"
> +#include "scancontroller.h"
> +
> +namespace Digikam
> +{
> +
> +enum Mode
> +{
> + InotifyMode,
> + KDirWatchMode
> +};
> +
> +class AlbumWatch::AlbumWatchPriv
> +{
> +public:
> +
> + AlbumWatchPriv(AlbumWatch* q)
> + : inotify(0),
> + dirWatch(0),
> + connectedToKIO(false),
> + q(q)
> +
> + {
> + }
> +
> + void determineMode();
> + bool isInotifyMode() const { return mode == InotifyMode; }
> +
> + Mode mode;
> +
> + KInotify* inotify;
> + KDirWatch* dirWatch;
> + QStringList dirWatchAddedDirs;
> + bool connectedToKIO;
> +
> + DatabaseParameters params;
> + QStringList fileNameBlackList;
> + QList<QDateTime> dbPathModificationDateList;
> +
> + AlbumWatch* const q;
> +
> + bool inBlackList(const QString& path);
> + QList<QDateTime> buildDirectoryModList(const QFileInfo& dbFile);
> + bool inDirWatchParametersBlackList(const QFileInfo& info, const QString& path);
> +};
> +
> +void AlbumWatch::AlbumWatchPriv::determineMode()
> +{
> + if (KInotify().available())
> + {
> + mode = InotifyMode;
> + }
> + else
> + {
> + mode = KDirWatchMode;
> + }
> +}
> +
> +bool AlbumWatch::AlbumWatchPriv::inBlackList(const QString& path)
> +{
> + // Filter out dirty signals triggered by changes on the database file
> + foreach (const QString& bannedFile, fileNameBlackList)
> + {
> + if (path.endsWith(bannedFile))
> + {
> + return true;
> + }
> + }
> + return false;
> +}
> +
> +// --------- //
> +
> +AlbumWatch::AlbumWatch(AlbumManager* parent)
> + : QObject(parent),
> + d(new AlbumWatchPriv(this))
> +{
> + d->determineMode();
> +
> + if (d->isInotifyMode())
> + {
> + connectToKInotify();
> + }
> + else
> + {
> + connectToKDirWatch();
> + connectToKIO();
> + }
> +
> + connect(parent, SIGNAL(signalAlbumAdded(Album*)),
> + this, SLOT(slotAlbumAdded(Album*)));
> + connect(parent, SIGNAL(signalAlbumAboutToBeDeleted(Album*)),
> + this, SLOT(slotAlbumAboutToBeDeleted(Album*)));
> +}
> +
> +AlbumWatch::~AlbumWatch()
> +{
> + delete d;
> +}
> +
> +void AlbumWatch::clear()
> +{
> + if (d->dirWatch)
> + {
> + foreach (const QString& addedDirectory, d->dirWatchAddedDirs)
> + {
> + d->dirWatch->removeDir(addedDirectory);
> + }
> + d->dirWatchAddedDirs.clear();
> + }
> +
> + if (d->connectedToKIO)
> + {
> + QDBusConnection::sessionBus().disconnect(QString(), QString(), "org.kde.KDirNotify", "FileMoved", 0, 0);
> + QDBusConnection::sessionBus().disconnect(QString(), QString(), "org.kde.KDirNotify", "FilesAdded", 0, 0);
> + QDBusConnection::sessionBus().disconnect(QString(), QString(), "org.kde.KDirNotify", "FilesRemoved", 0, 0);
> +
> + d->connectedToKIO = false;
> + }
> +
> + if (d->inotify)
> + {
> + d->inotify->removeAllWatches();
> + }
> +}
> +
> +void AlbumWatch::setDatabaseParameters(const DatabaseParameters& params)
> +{
> + d->params = params;
> +
> + d->fileNameBlackList.clear();
> + // filter out notifications caused by database operations
> + if (params.isSQLite())
> + {
> + d->fileNameBlackList << "thumbnails-digikam.db" << "thumbnails-digikam.db-journal";
> +
> + QFileInfo dbFile(params.SQLiteDatabaseFile());
> + d->fileNameBlackList << dbFile.fileName() << dbFile.fileName() + "-journal";
> +
> + // ensure this is done after setting up the black list
> + d->dbPathModificationDateList = d->buildDirectoryModList(dbFile);
> + }
> +}
> +
> +void AlbumWatch::slotAlbumAdded(Album* a)
> +{
> + if (a->isRoot() || a->type() != Album::PHYSICAL)
> + {
> + return;
> + }
> +
> + PAlbum* album = static_cast<PAlbum*>(a);
> +
> + CollectionLocation location = CollectionManager::instance()->locationForAlbumRootId(album->albumRootId());
> + if (!location.isAvailable())
> + {
> + return;
> + }
> + QString dir = album->folderPath();
> + if (dir.isEmpty())
> + {
> + return;
> + }
> +
> + if (d->isInotifyMode())
> + {
> + d->inotify->watchDirectory(dir);
> + }
> + else
> + {
> + if (!d->dirWatch->contains(dir))
> + {
> + d->dirWatchAddedDirs << dir;
> + d->dirWatch->addDir(dir, KDirWatch::WatchFiles | KDirWatch::WatchDirOnly);
> + }
> + }
> +}
> +
> +void AlbumWatch::slotAlbumAboutToBeDeleted(Album* a)
> +{
> + if (a->isRoot() || a->type() != Album::PHYSICAL)
> + {
> + return;
> + }
> +
> + PAlbum* album = static_cast<PAlbum*>(a);
> + QString dir = album->folderPath();
> + if (dir.isEmpty())
> + {
> + return;
> + }
> +
> + if (d->isInotifyMode())
> + {
> + d->inotify->removeDirectory(dir);
> + }
> + else
> + {
> + d->dirWatch->removeDir(album->folderPath());
> + }
> +}
> +
> +void AlbumWatch::rescanDirectory(const QString& dir)
> +{
> + kDebug() << "Detected change, triggering rescan of directory" << dir;
> + ScanController::instance()->scheduleCollectionScanRelaxed(dir);
> +}
> +
> +/* ---------- KInotify ---------- */
> +
> +void AlbumWatch::connectToKInotify()
> +{
> + if (d->inotify)
> + {
> + return;
> + }
> +
> + d->inotify = new KInotify(this);
> +
> + connect( d->inotify, SIGNAL( moved( QString, QString ) ),
> + this, SLOT( slotFileMoved( QString, QString ) ) );
> + connect( d->inotify, SIGNAL( deleted( QString, bool ) ),
> + this, SLOT( slotFileDeleted( QString, bool ) ) );
> + connect( d->inotify, SIGNAL( created( QString, bool ) ),
> + this, SLOT( slotFileCreated( QString, bool ) ) );
> + connect( d->inotify, SIGNAL( closedWrite( QString ) ),
> + this, SLOT( slotFileClosedAfterWrite( QString ) ) );
> + connect( d->inotify, SIGNAL( watchUserLimitReached() ),
> + this, SLOT( slotInotifyWatchUserLimitReached() ) );
> +}
> +
> +void AlbumWatch::slotFileMoved(const QString& from, const QString& to)
> +{
> + // we could add a copyOrMoveHint here...but identical-file detection seems to work well
> + rescanPath(from);
> + rescanPath(to);
> +}
> +
> +void AlbumWatch::slotFileDeleted(const QString& path, bool isDir)
> +{
> + Q_UNUSED(isDir);
> + rescanPath(path);
> +}
> +
> +void AlbumWatch::slotFileCreated(const QString& path, bool isDir)
> +{
> + if (isDir)
> + {
> + rescanPath(path);
> + }
> + // for files, rely on ClosedAfterWrite only,
> + // which always comes after create if the operation has finished
> +}
> +
> +void AlbumWatch::slotFileClosedAfterWrite(const QString& path)
> +{
> + rescanPath(path);
> +}
> +
> +void AlbumWatch::slotInotifyWatchUserLimitReached()
> +{
> + kError() << "Reached inotify limit";
> +}
> +
> +void AlbumWatch::rescanPath(const QString& path)
> +{
> + if (d->inBlackList(path))
> + {
> + return;
> + }
> + KUrl url(path);
> + rescanDirectory(url.directory());
> +}
> +
> +/* ---------- KDirWatch ---------- */
> +
> +QList<QDateTime> AlbumWatch::AlbumWatchPriv::buildDirectoryModList(const QFileInfo& dbFile)
> +{
> + // retrieve modification dates
> + QList<QDateTime> modList;
> + QFileInfoList fileInfoList = dbFile.dir().entryInfoList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
> +
> + // build list
> + foreach (const QFileInfo& info, fileInfoList)
> + {
> + // ignore digikam4.db and journal and other temporary files
> + if (!fileNameBlackList.contains(info.fileName()))
> + {
> + modList << info.lastModified();
> + }
> + }
> + return modList;
> +}
> +
> +bool AlbumWatch::AlbumWatchPriv::inDirWatchParametersBlackList(const QFileInfo& info, const QString& path)
> +{
> + if (params.isSQLite())
> + {
> + QDir dir;
> +
> + if (info.isDir())
> + {
> + dir = QDir(path);
> + }
> + else
> + {
> + dir = info.dir();
> + }
> +
> + QFileInfo dbFile(params.SQLiteDatabaseFile());
> +
> + // Workaround for broken KDirWatch in KDE 4.2.4
> + if (path.startsWith(dbFile.filePath()))
> + {
> + return true;
> + }
> +
> + // is the signal for the directory containing the database file?
> + if (dbFile.dir() == dir)
> + {
> + // retrieve modification dates
> + QList<QDateTime> modList = buildDirectoryModList(dbFile);
> +
> + // check for equality
> + if (modList == dbPathModificationDateList)
> + {
> + //kDebug() << "Filtering out db-file-triggered dir watch signal";
> + // we can skip the signal
> + return true;
> + }
> +
> + // set new list
> + dbPathModificationDateList = modList;
> + }
> + }
> +
> + return false;
> +}
> +
> +void AlbumWatch::slotDirWatchDirty(const QString& path)
> +{
> + if (d->inBlackList(path))
> + {
> + return;
> + }
> +
> + QFileInfo info(path);
> + if (d->inDirWatchParametersBlackList(info, path))
> + {
> + return;
> + }
> +
> + kDebug() << "KDirWatch detected change at" << path;
> +
> + if (info.isDir())
> + {
> + rescanDirectory(path);
> + }
> + else
> + {
> + rescanDirectory(info.path());
> + }
> +}
> +
> +void AlbumWatch::connectToKDirWatch()
> +{
> + if (d->dirWatch)
> + {
> + return;
> + }
> +
> + KDirWatch::Method m = d->dirWatch->internalMethod();
> + QString mName("FAM");
> +
> + if (m == KDirWatch::DNotify)
> + {
> + mName = QString("DNotify");
> + }
> + else if (m == KDirWatch::Stat)
> + {
> + mName = QString("Stat");
> + }
> + else if (m == KDirWatch::INotify)
> + {
> + mName = QString("INotify");
> + }
> +
> + kDebug() << "KDirWatch method = " << mName;
> +
> + d->dirWatch = new KDirWatch(this);
> +
> + connect(d->dirWatch, SIGNAL(dirty(QString)),
> + this, SLOT(slotDirWatchDirty(QString)));
> +}
> +
> +
> +/* ---------- KIO ---------- */
> +
> +void AlbumWatch::connectToKIO()
> +{
> + if (d->connectedToKIO)
> + {
> + return;
> + }
> +
> + QDBusConnection::sessionBus().connect(QString(), QString(), "org.kde.KDirNotify", "FileMoved",
> + this, SLOT(slotKioFileMoved(QString,QString)));
> + QDBusConnection::sessionBus().connect(QString(), QString(), "org.kde.KDirNotify", "FilesAdded",
> + this, SLOT(slotKioFilesAdded(QString)));
> + QDBusConnection::sessionBus().connect(QString(), QString(), "org.kde.KDirNotify", "FilesRemoved",
> + this, SLOT(slotKioFilesDeleted(QStringList)));
> +
> + d->connectedToKIO = true;
> +}
> +
> +void AlbumWatch::slotKioFileMoved(const QString& urlFrom, const QString& urlTo)
> +{
> + kDebug() << urlFrom << urlTo;
> + handleKioNotification(KUrl(urlFrom));
> + handleKioNotification(KUrl(urlTo));
> +}
> +
> +void AlbumWatch::slotKioFilesDeleted(const QStringList& urls)
> +{
> + kDebug() << urls;
> + foreach (const QString& url, urls)
> + {
> + handleKioNotification(KUrl(url));
> + }
> +}
> +
> +void AlbumWatch::slotKioFilesAdded(const QString& url)
> +{
> + kDebug() << url;
> + handleKioNotification(KUrl(url));
> +}
> +
> +void AlbumWatch::handleKioNotification(const KUrl& url)
> +{
> + if (url.isLocalFile())
> + {
> + QString path = url.directory();
> +
> + //kDebug() << path << !CollectionManager::instance()->albumRootPath(path).isEmpty();
> + // check path is in our collection
> + if (CollectionManager::instance()->albumRootPath(path).isNull())
> + {
> + return;
> + }
> +
> + kDebug() << "KDirNotify detected file change at" << path;
> +
> + rescanDirectory(path);
> + }
> + else
> + {
> + DatabaseUrl dbUrl(url);
> +
> + if (dbUrl.isAlbumUrl())
> + {
> + QString path = dbUrl.fileUrl().directory();
> + kDebug() << "KDirNotify detected file change at" << path;
> + rescanDirectory(path);
> + }
> + }
> +}
> +
> +}
> \ No newline at end of file
> diff --git a/digikam/album/albumwatch.h b/digikam/album/albumwatch.h
> new file mode 100644
> index 0000000..62e89e1
> --- /dev/null
> +++ b/digikam/album/albumwatch.h
> @@ -0,0 +1,90 @@
> +/* ============================================================
> + *
> + * This file is a part of digiKam project
> + * http://www.digikam.org
> + *
> + * Date : 2011-11-07
> + * Description : Directory watch interface
> + *
> + * Copyright (C) 2011 by Marcel Wiesweg <marcel.wiesweg at gmx.de>
> + *
> + * 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 ALBUMWATCH_H
> +#define ALBUMWATCH_H
> +
> +// Qt includes
> +
> +#include <QThread>
> +#include <QString>
> +
> +// KDE includes
> +
> +#include <kurl.h>
> +
> +// Local includes
> +
> +namespace Digikam
> +{
> +
> +class Album;
> +class AlbumManager;
> +class DatabaseParameters;
> +
> +class AlbumWatch : public QObject
> +{
> + Q_OBJECT
> +
> +public:
> +
> + AlbumWatch(AlbumManager* parent = 0);
> + ~AlbumWatch();
> +
> + void clear();
> + void setDatabaseParameters(const DatabaseParameters& params);
> +
> +protected Q_SLOTS:
> +
> + void slotAlbumAdded(Album* album);
> + void slotAlbumAboutToBeDeleted(Album* album);
> +
> + void slotFileMoved(const QString& from, const QString& to);
> + void slotFileDeleted(const QString& urlString, bool isDir);
> + void slotFileCreated(const QString& path, bool isDir);
> + void slotFileClosedAfterWrite(const QString&);
> + void slotInotifyWatchUserLimitReached();
> +
> + void slotDirWatchDirty(const QString& path);
> + void slotKioFileMoved(const QString& urlFrom, const QString& urlTo);
> + void slotKioFilesDeleted(const QStringList& urls);
> + void slotKioFilesAdded(const QString& directory);
> +
> +private:
> +
> + void rescanDirectory(const QString& dir);
> + void rescanPath(const QString& path);
> +
> + void connectToKInotify();
> + void connectToKDirWatch();
> + void connectToKIO();
> + void handleKioNotification(const KUrl& url);
> +
> + class AlbumWatchPriv;
> + AlbumWatchPriv* const d;
> +
> +};
> +
> +}
> +
> +#endif
> diff --git a/libs/3rdparty/kinotify/kinotify.cpp b/libs/3rdparty/kinotify/kinotify.cpp
> new file mode 100644
> index 0000000..676f727
> --- /dev/null
> +++ b/libs/3rdparty/kinotify/kinotify.cpp
> @@ -0,0 +1,517 @@
> +/* This file is part of the KDE libraries
> + Copyright (C) 2007-2010 Sebastian Trueg <trueg at kde.org>
> +
> + This library is free software; you can redistribute it and/or
> + modify it under the terms of the GNU Library General Public
> + License as published by the Free Software Foundation; either
> + version 2 of the License, or (at your option) any later version.
> +
> + This library 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
> + Library General Public License for more details.
> +
> + You should have received a copy of the GNU Library General Public License
> + along with this library; see the file COPYING.LIB. If not, write to
> + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
> + Boston, MA 02110-1301, USA.
> +*/
> +
> +// BASED ON kinotify.cpp, Nepomuk Core, 689b7b57f60945ca0dfd175877d3073560d73ffc, 2011/10/27
> +
> +#include "kinotify.h"
> +
> +// INotify is Linux-only
> +#if defined(Q_OS_LINUX)
> +
> +#include <QtCore/QSocketNotifier>
> +#include <QtCore/QHash>
> +#include <QtCore/QDirIterator>
> +#include <QtCore/QFile>
> +#include <QtCore/QQueue>
> +#include <QtCore/QScopedArrayPointer>
> +
> +#include <kdebug.h>
> +
> +#include <sys/inotify.h>
> +#include <sys/utsname.h>
> +#include <sys/types.h>
> +#include <sys/stat.h>
> +#include <unistd.h>
> +#include <fcntl.h>
> +#include <errno.h>
> +#include <dirent.h>
> +
> +
> +namespace {
> + const int EVENT_STRUCT_SIZE = sizeof( struct inotify_event );
> +
> + // we need one event to fit into the buffer, the problem is that the name
> + // is a variable length array
> + const int EVENT_BUFFER_SIZE = EVENT_STRUCT_SIZE + 1024*16;
> +
> + QByteArray stripTrailingSlash( const QByteArray& path ) {
> + QByteArray p( path );
> + if ( p.endsWith( '/' ) )
> + p.truncate( p.length()-1 );
> + return p;
> + }
> +
> + QByteArray concatPath( const QByteArray& p1, const QByteArray& p2 ) {
> + QByteArray p(p1);
> + if( p.isEmpty() || p[p.length()-1] != '/' )
> + p.append('/');
> + p.append(p2);
> + return p;
> + }
> +}
> +
> +namespace Digikam
> +{
> +
> +class KInotify::Private
> +{
> +public:
> + Private( KInotify* parent )
> + : watchHiddenFolders( false ),
> + m_inotifyFd( -1 ),
> + m_notifier( 0 ),
> + q( parent) {
> + }
> +
> + ~Private() {
> + close();
> + }
> +
> + QHash<int, QByteArray> cookies;
> + QHash<int, QByteArray> watchPathHash;
> + QHash<QByteArray, int> pathWatchHash;
> +
> + /// queue of paths to install watches for
> + QQueue<QByteArray> pathsToWatch;
> +
> + unsigned char eventBuffer[EVENT_BUFFER_SIZE];
> +
> + // FIXME: only stored from the last addWatch call
> + WatchEvents mode;
> + WatchFlags flags;
> +
> + bool watchHiddenFolders;
> +
> + int inotify() {
> + if ( m_inotifyFd < 0 ) {
> + open();
> + }
> + return m_inotifyFd;
> + }
> +
> + void close() {
> + delete m_notifier;
> + m_notifier = 0;
> +
> + ::close( m_inotifyFd );
> + m_inotifyFd = -1;
> + }
> +
> + bool addWatch( const QByteArray& path ) {
> + // we always need the unmount event to maintain our path hash
> + WatchEvents newMode = mode;
> + WatchFlags newFlags = flags;
> +
> + if( !q->filterWatch( path, newMode, newFlags ) ) {
> + return true;
> + }
> + const int mask = newMode|newFlags|EventUnmount;
> +
> + int wd = inotify_add_watch( inotify(), path.data(), mask );
> + if ( wd > 0 ) {
> +// kDebug() << "Successfully added watch for" << path << pathHash.count();
> + QByteArray normalized = stripTrailingSlash( path );
> + watchPathHash.insert( wd, normalized );
> + pathWatchHash.insert( normalized, wd );
> + return true;
> + }
> + else {
> + kDebug() << "Failed to create watch for" << path;
> + static bool userLimitReachedSignaled = false;
> + if ( !userLimitReachedSignaled && errno == ENOSPC ) {
> + kDebug() << "User limit reached. Please raise the inotify user watch limit.";
> + userLimitReachedSignaled = true;
> + emit q->watchUserLimitReached();
> + }
> + return false;
> + }
> + }
> +
> + bool addWatchesRecursively( const QByteArray& path )
> + {
> + if ( !addWatch( path ) )
> + return false;
> +
> + const int len = offsetof(struct dirent, d_name) +
> + pathconf(path.data(), _PC_NAME_MAX) + 1;
> + QScopedArrayPointer<char> entryData( new char[len] );
> + struct dirent* entry = ( struct dirent* )entryData.data();
> +
> + DIR* dir = opendir( path.data() );
> + if ( dir ) {
> + struct dirent *result = 0;
> + while ( !readdir_r( dir, entry, &result ) ) {
> +
> + if ( !result ) {
> + // end of folder
> + break;
> + }
> +
> + if ( ( entry->d_type == DT_UNKNOWN ||
> + entry->d_type == DT_DIR ) &&
> + ( watchHiddenFolders ||
> + qstrncmp( entry->d_name, ".", 1 ) ) &&
> + qstrcmp( entry->d_name, "." ) &&
> + qstrcmp( entry->d_name, ".." ) ) {
> + bool isDir = true;
> + QByteArray subDir = concatPath( path, QByteArray::fromRawData( entry->d_name, qstrlen( entry->d_name ) ) );
> + if ( entry->d_type == DT_UNKNOWN ) {
> + struct stat buf;
> + lstat( subDir.data(), &buf );
> + isDir = S_ISDIR( buf.st_mode );
> + }
> +
> + if ( isDir ) {
> + pathsToWatch.enqueue( subDir );
> + }
> + }
> + }
> +
> + closedir( dir );
> + return true;
> + }
> + else {
> + kDebug() << "Could not open dir" << path;
> + return false;
> + }
> + }
> +
> + void removeWatch( int wd ) {
> + //kDebug() << wd << watchPathHash[wd];
> + pathWatchHash.remove( watchPathHash.take( wd ) );
> + inotify_rm_watch( inotify(), wd );
> + }
> +
> + void _k_addWatches() {
> + // add the next batch of paths
> + for ( int i = 0; i < 100; ++i ) {
> + if ( pathsToWatch.isEmpty() ||
> + !addWatchesRecursively( pathsToWatch.dequeue() ) ) {
> + return;
> + }
> + }
> +
> + // asyncroneously add the next batch
> + if ( !pathsToWatch.isEmpty() ) {
> + QMetaObject::invokeMethod( q, "_k_addWatches", Qt::QueuedConnection );
> + }
> + else {
> + //kDebug() << "All watches installed";
> + }
> + }
> +
> +private:
> + void open() {
> + m_inotifyFd = inotify_init();
> + delete m_notifier;
> + if ( m_inotifyFd > 0 ) {
> + fcntl( m_inotifyFd, F_SETFD, FD_CLOEXEC );
> + kDebug() << "Successfully opened connection to inotify:" << m_inotifyFd;
> + m_notifier = new QSocketNotifier( m_inotifyFd, QSocketNotifier::Read );
> + connect( m_notifier, SIGNAL( activated( int ) ), q, SLOT( slotEvent( int ) ) );
> + }
> + }
> +
> + int m_inotifyFd;
> + QSocketNotifier* m_notifier;
> +
> + KInotify* q;
> +};
> +
> +
> +KInotify::KInotify( QObject* parent )
> + : QObject( parent ),
> + d( new Private( this ) )
> +{
> +}
> +
> +
> +KInotify::~KInotify()
> +{
> + delete d;
> +}
> +
> +
> +bool KInotify::available() const
> +{
> + if( d->inotify() > 0 ) {
> + // trueg: Copied from KDirWatch.
> + struct utsname uts;
> + int major, minor, patch;
> + if ( uname(&uts) < 0 ) {
> + return false; // *shrug*
> + }
> + else if ( sscanf( uts.release, "%d.%d.%d", &major, &minor, &patch) != 3 ) {
> + return false; // *shrug*
> + }
> + else if( major * 1000000 + minor * 1000 + patch < 2006014 ) { // <2.6.14
> + kDebug(7001) << "Can't use INotify, Linux kernel too old";
> + return false;
> + }
> +
> + return true;
> + }
> + else {
> + return false;
> + }
> +}
> +
> +
> +bool KInotify::watchingPath( const QString& path ) const
> +{
> + const QByteArray p( stripTrailingSlash( QFile::encodeName( path ) ) );
> + return d->pathWatchHash.contains(p);
> +}
> +
> +
> +bool KInotify::addWatch( const QString& path, WatchEvents mode, WatchFlags flags )
> +{
> + //kDebug() << path;
> +
> + d->mode = mode;
> + d->flags = flags;
> + d->pathsToWatch.append( QFile::encodeName( path ) );
> + d->_k_addWatches();
> + return true;
> +}
> +
> +bool KInotify::watchDirectory(const QString& path)
> +{
> + d->mode = WatchEvents(EventMove | EventDelete | EventDeleteSelf | EventCloseWrite | EventCreate);
> + d->flags = WatchFlags();
> + return d->addWatch(QFile::encodeName(path));
> +}
> +
> +bool KInotify::watchDirectoryAndSubdirs(const QString& path)
> +{
> + return addWatch(path,
> + WatchEvents(EventMove | EventDelete | EventDeleteSelf | EventCloseWrite | EventCreate),
> + WatchFlags());
> +}
> +
> +bool KInotify::removeWatch( const QString& path )
> +{
> + //kDebug() << path;
> + QByteArray encodedPath = QFile::encodeName( path );
> + QHash<int, QByteArray>::iterator it = d->watchPathHash.begin();
> + while ( it != d->watchPathHash.end() ) {
> + if ( it.value().startsWith( encodedPath ) ) {
> + inotify_rm_watch( d->inotify(), it.key() );
> + d->pathWatchHash.remove(it.value());
> + it = d->watchPathHash.erase( it );
> + }
> + else {
> + ++it;
> + }
> + }
> + return true;
> +}
> +
> +bool KInotify::removeDirectory( const QString& path )
> +{
> + QByteArray encodedPath = QFile::encodeName( path );
> +
> + int wd = d->pathWatchHash.value(encodedPath);
> + if (wd)
> + {
> + d->removeWatch(wd);
> + }
> + return true;
> +}
> +
> +bool KInotify::removeAllWatches()
> +{
> + foreach (int wd, d->pathWatchHash)
> + {
> + d->removeWatch(wd);
> + }
> + return true;
> +}
> +
> +
> +bool KInotify::filterWatch( const QString & path, WatchEvents & modes, WatchFlags & flags )
> +{
> + Q_UNUSED( path );
> + Q_UNUSED( modes );
> + Q_UNUSED( flags );
> + return true;
> +}
> +
> +
> +void KInotify::slotEvent( int socket )
> +{
> + // read at least one event
> + const int len = read( socket, d->eventBuffer, EVENT_BUFFER_SIZE );
> + int i = 0;
> + while ( i < len && len-i >= EVENT_STRUCT_SIZE ) {
> + const struct inotify_event* event = ( struct inotify_event* )&d->eventBuffer[i];
> +
> + QByteArray path;
> +
> + // the event name only contains an interesting value if we get an event for a file/folder inside
> + // a watched folder. Otherwise we should ignore it
> + if ( event->mask & (EventDeleteSelf|EventMoveSelf) ) {
> + path = d->watchPathHash.value( event->wd );
> + }
> + else {
> + // we cannot use event->len here since it contains the size of the buffer and not the length of the string
> + const QByteArray eventName = QByteArray::fromRawData( event->name, qstrlen(event->name) );
> + const QByteArray hashedPath = d->watchPathHash.value( event->wd );
> + path = concatPath( hashedPath, eventName );
> + }
> +
> + Q_ASSERT( !path.isEmpty() || event->mask & EventIgnored );
> + Q_ASSERT( path != "/" || event->mask & EventIgnored );
> +
> + // now signal the event
> + if ( event->mask & EventAccess) {
> +// kDebug() << path << "EventAccess";
> + emit accessed( QFile::decodeName(path) );
> + }
> + if ( event->mask & EventAttributeChange ) {
> +// kDebug() << path << "EventAttributeChange";
> + emit attributeChanged( QFile::decodeName(path) );
> + }
> + if ( event->mask & EventCloseWrite ) {
> +// kDebug() << path << "EventCloseWrite";
> + emit closedWrite( QFile::decodeName(path) );
> + }
> + if ( event->mask & EventCloseRead ) {
> +// kDebug() << path << "EventCloseRead";
> + emit closedRead( QFile::decodeName(path) );
> + }
> + if ( event->mask & EventCreate ) {
> +// kDebug() << path << "EventCreate";
> + /* Disable auto-recursion
> + if ( event->mask & IN_ISDIR ) {
> + // FIXME: store the mode and flags somewhere
> + addWatch( path, d->mode, d->flags );
> + }
> + */
> + emit created( QFile::decodeName(path), event->mask & IN_ISDIR );
> + }
> + if ( event->mask & EventDeleteSelf ) {
> + //kDebug() << path << "EventDeleteSelf";
> + d->removeWatch( event->wd );
> + emit deleted( QFile::decodeName(path), event->mask & IN_ISDIR );
> + }
> + if ( event->mask & EventDelete ) {
> +// kDebug() << path << "EventDelete";
> + /* Disable auto-recursion
> + // we watch all folders recursively. Thus, folder removing is reported in DeleteSelf.
> + if( !(event->mask & IN_ISDIR) )
> + */
> + emit deleted( QFile::decodeName(path), false );
> + }
> + if ( event->mask & EventModify ) {
> +// kDebug() << path << "EventModify";
> + emit modified( QFile::decodeName(path) );
> + }
> + if ( event->mask & EventMoveSelf ) {
> +// kDebug() << path << "EventMoveSelf";
> + kWarning() << "EventMoveSelf: THIS CASE IS NOT HANDLED PROPERLY!";
> + }
> + if ( event->mask & EventMoveFrom ) {
> +// kDebug() << path << "EventMoveFrom";
> + d->cookies[event->cookie] = path;
> + }
> + if ( event->mask & EventMoveTo ) {
> + // check if we have a cookie for this one
> + if ( d->cookies.contains( event->cookie ) ) {
> + const QByteArray oldPath = d->cookies.take(event->cookie);
> +
> + // update the path cache
> + if( event->mask & IN_ISDIR ) {
> + QHash<QByteArray, int>::iterator it = d->pathWatchHash.find(oldPath);
> + if( it != d->pathWatchHash.end() ) {
> + //kDebug() << oldPath << path;
> + const int wd = it.value();
> + d->watchPathHash[wd] = path;
> + d->pathWatchHash.erase(it);
> + d->pathWatchHash.insert( path, wd );
> + }
> + }
> +// kDebug() << oldPath << "EventMoveTo" << path;
> + emit moved( QFile::encodeName(oldPath), QFile::decodeName(path) );
> + }
> + else {
> + kDebug() << "No cookie for move information of" << path;
> + }
> + }
> + if ( event->mask & EventOpen ) {
> +// kDebug() << path << "EventOpen";
> + emit opened( QFile::decodeName(path) );
> + }
> + if ( event->mask & EventUnmount ) {
> +// kDebug() << path << "EventUnmount. removing from path hash";
> + if ( event->mask & IN_ISDIR ) {
> + d->removeWatch( event->wd );
> + }
> + emit unmounted( QFile::decodeName(path) );
> + }
> + if ( event->mask & EventQueueOverflow ) {
> + // This should not happen since we grab all events as soon as they arrive
> + kDebug() << path << "EventQueueOverflow";
> +// emit queueOverflow();
> + }
> + if ( event->mask & EventIgnored ) {
> + kDebug() << path << "EventIgnored";
> + }
> +
> + i += EVENT_STRUCT_SIZE + event->len;
> + }
> +
> + if ( len < 0 ) {
> + kDebug() << "Failed to read event.";
> + }
> +}
> +
> +} // namespace Digikam
> +
> +#else // defined(Q_OS_LINUX)
> +
> +// Dummy implementation for non-linux
> +
> +namespace Digikam
> +{
> +
> +class KInotify::Private
> +{
> +public:
> + void _k_addWatches() {}
> +};
> +KInotify::KInotify(QObject* parent) : QObject(parent), d(0) {}
> +KInotify::~KInotify() {}
> +bool KInotify::available() const { return false; }
> +bool KInotify::watchingPath( const QString&) const { return false; }
> +bool KInotify::filterWatch( const QString &, WatchEvents & , WatchFlags&) { return false; }
> +bool KInotify::addWatch( const QString&, WatchEvents, WatchFlags) { return false; }
> +bool KInotify::removeWatch( const QString&) { return false; }
> +bool KInotify::removeAllWatches() { return false; }
> +bool KInotify::watchDirectory(const QString& ) { return false; }
> +bool KInotify::watchDirectoryAndSubdirs(const QString&) { return false; }
> +void KInotify::slotEvent( int ) { }
> +
> +}
> +
> +#endif // defined(Q_OS_LINUX)
> +
> +// must be at the bottom for Q_PRIVATE_SLOT
> +#include "kinotify.moc"
> +
> diff --git a/libs/3rdparty/kinotify/kinotify.h b/libs/3rdparty/kinotify/kinotify.h
> new file mode 100644
> index 0000000..ec619ad
> --- /dev/null
> +++ b/libs/3rdparty/kinotify/kinotify.h
> @@ -0,0 +1,198 @@
> +/* This file is part of the KDE libraries
> + Copyright (C) 2007-2010 Sebastian Trueg <trueg at kde.org>
> +
> + This library is free software; you can redistribute it and/or
> + modify it under the terms of the GNU Library General Public
> + License as published by the Free Software Foundation; either
> + version 2 of the License, or (at your option) any later version.
> +
> + This library 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
> + Library General Public License for more details.
> +
> + You should have received a copy of the GNU Library General Public License
> + along with this library; see the file COPYING.LIB. If not, write to
> + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
> + Boston, MA 02110-1301, USA.
> +*/
> +
> +#ifndef _KINOTIFY_H_
> +#define _KINOTIFY_H_
> +
> +#include <QtCore/QObject>
> +#include <QtCore/QFlags>
> +
> +#include "digikam_export.h"
> +
> +namespace Digikam
> +{
> +
> +/**
> + * A simple wrapper around inotify which only allows
> + * to add folders recursively.
> + *
> + * Warning: moving of top-level folders is not supported and
> + * results in undefined behaviour.
> + */
> +class KInotify : public QObject
> +{
> + Q_OBJECT
> +
> +public:
> + KInotify( QObject* parent = 0 );
> + virtual ~KInotify();
> +
> + /**
> + * Inotify events that can occur. Use with addWatch
> + * to define the events that should be watched.
> + *
> + * These flags correspond to the native Linux inotify flags.
> + */
> + enum WatchEvent {
> + EventAccess = 0x00000001, /**< File was accessed (read, compare inotify's IN_ACCESS) */
> + EventAttributeChange = 0x00000004, /**< Metadata changed (permissions, timestamps, extended attributes, etc., compare inotify's IN_ATTRIB) */
> + EventCloseWrite = 0x00000008, /**< File opened for writing was closed (compare inotify's IN_CLOSE_WRITE) */
> + EventCloseRead = 0x00000010, /**< File not opened for writing was closed (compare inotify's IN_CLOSE_NOWRITE) */
> + EventCreate = 0x00000100, /** File/directory created in watched directory (compare inotify's IN_CREATE) */
> + EventDelete = 0x00000200, /**< File/directory deleted from watched directory (compare inotify's IN_DELETE) */
> + EventDeleteSelf = 0x00000400, /**< Watched file/directory was itself deleted (compare inotify's IN_DELETE_SELF) */
> + EventModify = 0x00000002, /**< File was modified (compare inotify's IN_MODIFY) */
> + EventMoveSelf = 0x00000800, /**< Watched file/directory was itself moved (compare inotify's IN_MOVE_SELF) */
> + EventMoveFrom = 0x00000040, /**< File moved out of watched directory (compare inotify's IN_MOVED_FROM) */
> + EventMoveTo = 0x00000080, /**< File moved into watched directory (compare inotify's IN_MOVED_TO) */
> + EventOpen = 0x00000020, /**< File was opened (compare inotify's IN_OPEN) */
> + EventUnmount = 0x00002000, /**< Backing fs was unmounted (compare inotify's IN_UNMOUNT) */
> + EventQueueOverflow = 0x00004000, /**< Event queued overflowed (compare inotify's IN_Q_OVERFLOW) */
> + EventIgnored = 0x00008000, /**< File was ignored (compare inotify's IN_IGNORED) */
> + EventMove = ( EventMoveFrom|EventMoveTo),
> + EventAll = ( EventAccess|
> + EventAttributeChange|
> + EventCloseWrite|
> + EventCloseRead|
> + EventCreate|
> + EventDelete|
> + EventDeleteSelf|
> + EventModify|
> + EventMoveSelf|
> + EventMoveFrom|
> + EventMoveTo|
> + EventOpen )
> + };
> + Q_DECLARE_FLAGS(WatchEvents, WatchEvent)
> +
> + /**
> + * Watch flags
> + *
> + * These flags correspond to the native Linux inotify flags.
> + */
> + enum WatchFlag {
> + FlagOnlyDir = 0x01000000, /**< Only watch the path if it is a directory (IN_ONLYDIR) */
> + FlagDoNotFollow = 0x02000000, /**< Don't follow a sym link (IN_DONT_FOLLOW) */
> + FlagOneShot = 0x80000000 /**< Only send event once (IN_ONESHOT) */
> + };
> + Q_DECLARE_FLAGS(WatchFlags, WatchFlag)
> +
> + /**
> + * \return \p true if inotify is available and usable.
> + */
> + bool available() const;
> +
> + bool watchingPath( const QString& path ) const;
> +
> +protected:
> + /**
> + * Called for every folder that is being watched.
> + * Returns true if the watch should be add or false if it should NOT be added.
> + */
> + virtual bool filterWatch( const QString & path, WatchEvents & modes, WatchFlags & flags );
> +
> +public Q_SLOTS:
> + virtual bool addWatch( const QString& path, WatchEvents modes, WatchFlags flags = WatchFlags() );
> + bool removeWatch( const QString& path );
> + bool removeAllWatches();
> +
> + bool watchDirectory(const QString& path);
> + bool watchDirectoryAndSubdirs(const QString& path);
> + bool removeDirectory( const QString& path );
> +
> +Q_SIGNALS:
> + /**
> + * Emitted if a file is accessed (KInotify::EventAccess)
> + */
> + void accessed( const QString& file );
> +
> + /**
> + * Emitted if file attributes are changed (KInotify::EventAttributeChange)
> + */
> + void attributeChanged( const QString& file );
> +
> + /**
> + * Emitted if FIXME (KInotify::EventCloseWrite)
> + */
> + void closedWrite( const QString& file );
> +
> + /**
> + * Emitted if FIXME (KInotify::EventCloseRead)
> + */
> + void closedRead( const QString& file );
> +
> + /**
> + * Emitted if a new file has been created in one of the watched
> + * folders (KInotify::EventCreate)
> + */
> + void created( const QString& file, bool isDir );
> +
> + /**
> + * Emitted if a watched file or folder has been deleted.
> + * This includes files in watched foldes (KInotify::EventDelete and KInotify::EventDeleteSelf)
> + */
> + void deleted( const QString& file, bool isDir );
> +
> + /**
> + * Emitted if a watched file is modified (KInotify::EventModify)
> + */
> + void modified( const QString& file );
> +
> + /**
> + * Emitted if a file or folder has been moved or renamed.
> + *
> + * \warning The moved signal will only be emitted if both the source and target folder
> + * are being watched.
> + */
> + void moved( const QString& oldName, const QString& newName );
> +
> + /**
> + * Emitted if a file is opened (KInotify::EventOpen)
> + */
> + void opened( const QString& file );
> +
> + /**
> + * Emitted if a watched path has been unmounted (KInotify::EventUnmount)
> + */
> + void unmounted( const QString& file );
> +
> + /**
> + * Emitted if during updating the internal watch structures (recursive watches)
> + * the inotify user watch limit was reached.
> + *
> + * This means that not all requested paths can be watched until the user watch
> + * limit is increased.
> + *
> + * This signal will only be emitted once.
> + */
> + void watchUserLimitReached();
> +
> +private Q_SLOTS:
> + void slotEvent( int );
> +
> +private:
> + class Private;
> + Private* const d;
> +
> + Q_PRIVATE_SLOT( d, void _k_addWatches() )
> +};
> +
> +}
> +
> +#endif
> _______________________________________________
> Digikam-devel mailing list
> Digikam-devel at kde.org
> https://mail.kde.org/mailman/listinfo/digikam-devel
>
More information about the Digikam-devel
mailing list