[digikam] /: On Linux, use Inotify directly for file notification changes

classic Classic list List threaded Threaded
17 messages Options
Reply | Threaded
Open this post in threaded view
|

[digikam] /: On Linux, use Inotify directly for file notification changes

Marcel Wiesweg
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: [hidden email]

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 <[hidden email]>
+ *
+ * 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 <[hidden email]>
+ *
+ * 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 <[hidden email]>
+
+   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 <[hidden email]>
+
+   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
[hidden email]
https://mail.kde.org/mailman/listinfo/digikam-devel
Reply | Threaded
Open this post in threaded view
|

Re: [digikam] /: On Linux, use Inotify directly for file notification changes

Gilles Caulier-4
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 <[hidden email]>:

> 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: [hidden email]
>
> 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 <[hidden email]>
> + *
> + * 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 <[hidden email]>
> + *
> + * 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 <[hidden email]>
> +
> +   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 <[hidden email]>
> +
> +   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
> [hidden email]
> https://mail.kde.org/mailman/listinfo/digikam-devel
>
_______________________________________________
Digikam-devel mailing list
[hidden email]
https://mail.kde.org/mailman/listinfo/digikam-devel
Reply | Threaded
Open this post in threaded view
|

Re: [digikam] /: On Linux, use Inotify directly for file notification changes

Marcel Wiesweg


> 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 ?

It's not yet in kdelibs, it was only recently written for use inside the Nepomuk indexer. The code was copied and slightly customized from Nepomuk Core.
In the current form, I'm not sure it will ever be in kdelibs: For Linux, it's the best solution, because we get information in better quality, and we get the information which we really need (closed-after-write). But Inotify is definitely Linux-only.
If there are comparable solutions for Windows and Mac, the way to go would be to extend KDirWatch API and offer this cross-platform. But I dont know about available APIs on these systems.

Marcel
--
NEU: FreePhone - 0ct/min Handyspartarif mit Geld-zurück-Garantie!
Jetzt informieren: http://www.gmx.net/de/go/freephone
_______________________________________________
Digikam-devel mailing list
[hidden email]
https://mail.kde.org/mailman/listinfo/digikam-devel
Reply | Threaded
Open this post in threaded view
|

Re: [digikam] /: On Linux, use Inotify directly for file notification changes

Gilles Caulier-4
Thanks Marcel for the info ,

Ananta Palani who work under Windows to port digiKam. He build installer and can help us around this subject...

Best

Gilles Caulier

2011/11/8 Marcel Wiesweg <[hidden email]>:
>
>
>> 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 ?
>
> It's not yet in kdelibs, it was only recently written for use inside the Nepomuk indexer. The code was copied and slightly customized from Nepomuk Core.
> In the current form, I'm not sure it will ever be in kdelibs: For Linux, it's the best solution, because we get information in better quality, and we get the information which we really need (closed-after-write). But Inotify is definitely Linux-only.
> If there are comparable solutions for Windows and Mac, the way to go would be to extend KDirWatch API and offer this cross-platform. But I dont know about available APIs on these systems.
>
> Marcel
> --
> NEU: FreePhone - 0ct/min Handyspartarif mit Geld-zurück-Garantie!
> Jetzt informieren: http://www.gmx.net/de/go/freephone
> _______________________________________________
> Digikam-devel mailing list
> [hidden email]
> https://mail.kde.org/mailman/listinfo/digikam-devel
>
> --------------------------------------------------------------------------------------------------------------

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

> --------------------------------------------------------------------------------------------------------------

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: [hidden email]

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

_______________________________________________
Digikam-devel mailing list
[hidden email]
https://mail.kde.org/mailman/listinfo/digikam-devel
Reply | Threaded
Open this post in threaded view
|

Re: [digikam] /: On Linux, use Inotify directly for file notification changes

Ananta Palani
Yes, this is the highest priority for me. I would like to fix all file handling in Windows. Right now it is extremely slow, and deleting a file is not possible. This is not only a problem for digiKam, but also for KDE on Windows in general (see, for example, Dolphin). I will try to implement these improvements as time allows.

-Ananta

On Tue, Nov 8, 2011 at 9:14 AM, Gilles Caulier <[hidden email]> wrote:
Thanks Marcel for the info ,

Ananta Palani who work under Windows to port digiKam. He build installer and can help us around this subject...

Best

Gilles Caulier

2011/11/8 Marcel Wiesweg <[hidden email]>:
>
>
>> 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 ?
>
> It's not yet in kdelibs, it was only recently written for use inside the Nepomuk indexer. The code was copied and slightly customized from Nepomuk Core.
> In the current form, I'm not sure it will ever be in kdelibs: For Linux, it's the best solution, because we get information in better quality, and we get the information which we really need (closed-after-write). But Inotify is definitely Linux-only.
> If there are comparable solutions for Windows and Mac, the way to go would be to extend KDirWatch API and offer this cross-platform. But I dont know about available APIs on these systems.
>
> Marcel
> --
> NEU: FreePhone - 0ct/min Handyspartarif mit Geld-zurück-Garantie!
> Jetzt informieren: http://www.gmx.net/de/go/freephone
> _______________________________________________
> Digikam-devel mailing list
> [hidden email]
> https://mail.kde.org/mailman/listinfo/digikam-devel
>
> --------------------------------------------------------------------------------------------------------------

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

> --------------------------------------------------------------------------------------------------------------

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: [hidden email]

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


_______________________________________________
Digikam-devel mailing list
[hidden email]
https://mail.kde.org/mailman/listinfo/digikam-devel
Reply | Threaded
Open this post in threaded view
|

Re: [digikam] /: On Linux, use Inotify directly for file notification changes

Gilles Caulier-4
Thanks Ananta.

Marcel,

current implementation do not compile here :

[ 88%] Building CXX object core/digikam/CMakeFiles/digikam.dir/digikamadaptor.cpp.o
Linking CXX executable digikam
CMakeFiles/digikam.dir/album/albumwatch.cpp.o: In function `Digikam::AlbumWatch::AlbumWatchPriv::determineMode()':
/mnt/data/Devel/GIT/2.x/core/digikam/album/albumwatch.cpp:93: undefined reference to `Digikam::KInotify::KInotify(QObject*)'
/mnt/data/Devel/GIT/2.x/core/digikam/album/albumwatch.cpp:93: undefined reference to `Digikam::KInotify::available() const'
/mnt/data/Devel/GIT/2.x/core/digikam/album/albumwatch.cpp:93: undefined reference to `Digikam::KInotify::~KInotify()'
/mnt/data/Devel/GIT/2.x/core/digikam/album/albumwatch.cpp:93: undefined reference to `Digikam::KInotify::~KInotify()'
CMakeFiles/digikam.dir/album/albumwatch.cpp.o: In function `Digikam::AlbumWatch::clear()':
/mnt/data/Devel/GIT/2.x/core/digikam/album/albumwatch.cpp:167: undefined reference to `Digikam::KInotify::removeAllWatches()'
CMakeFiles/digikam.dir/album/albumwatch.cpp.o: In function `Digikam::AlbumWatch::slotAlbumAdded(Digikam::Album*)':
/mnt/data/Devel/GIT/2.x/core/digikam/album/albumwatch.cpp:211: undefined reference to `Digikam::KInotify::watchDirectory(QString const&)'
CMakeFiles/digikam.dir/album/albumwatch.cpp.o: In function `Digikam::AlbumWatch::slotAlbumAboutToBeDeleted(Digikam::Album*)':
/mnt/data/Devel/GIT/2.x/core/digikam/album/albumwatch.cpp:239: undefined reference to `Digikam::KInotify::removeDirectory(QString const&)'
CMakeFiles/digikam.dir/album/albumwatch.cpp.o: In function `Digikam::AlbumWatch::connectToKInotify()':
/mnt/data/Devel/GIT/2.x/core/digikam/album/albumwatch.cpp:262: undefined reference to `Digikam::KInotify::KInotify(QObject*)'
collect2: ld a retourné 1 code d'état d'exécution
make[2]: *** [core/digikam/digikam] Erreur 1
make[1]: *** [core/digikam/CMakeFiles/digikam.dir/all] Erreur 2
make: *** [all] Erreur 2

Perhaos it's an export problem, as usual. I will try to clean up all and rebuild to see...

Gilles

2011/11/8 Ananta Palani <[hidden email]>
Yes, this is the highest priority for me. I would like to fix all file handling in Windows. Right now it is extremely slow, and deleting a file is not possible. This is not only a problem for digiKam, but also for KDE on Windows in general (see, for example, Dolphin). I will try to implement these improvements as time allows.

-Ananta


On Tue, Nov 8, 2011 at 9:14 AM, Gilles Caulier <[hidden email]> wrote:
Thanks Marcel for the info ,

Ananta Palani who work under Windows to port digiKam. He build installer and can help us around this subject...

Best

Gilles Caulier

2011/11/8 Marcel Wiesweg <[hidden email]>:
>
>
>> 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 ?
>
> It's not yet in kdelibs, it was only recently written for use inside the Nepomuk indexer. The code was copied and slightly customized from Nepomuk Core.
> In the current form, I'm not sure it will ever be in kdelibs: For Linux, it's the best solution, because we get information in better quality, and we get the information which we really need (closed-after-write). But Inotify is definitely Linux-only.
> If there are comparable solutions for Windows and Mac, the way to go would be to extend KDirWatch API and offer this cross-platform. But I dont know about available APIs on these systems.
>
> Marcel
> --
> NEU: FreePhone - 0ct/min Handyspartarif mit Geld-zurück-Garantie!
> Jetzt informieren: http://www.gmx.net/de/go/freephone
> _______________________________________________
> Digikam-devel mailing list
> [hidden email]
> https://mail.kde.org/mailman/listinfo/digikam-devel
>
> --------------------------------------------------------------------------------------------------------------

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

> --------------------------------------------------------------------------------------------------------------

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: [hidden email]

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


_______________________________________________
Digikam-devel mailing list
[hidden email]
https://mail.kde.org/mailman/listinfo/digikam-devel



_______________________________________________
Digikam-devel mailing list
[hidden email]
https://mail.kde.org/mailman/listinfo/digikam-devel
Reply | Threaded
Open this post in threaded view
|

Re: [digikam] /: On Linux, use Inotify directly for file notification changes

Marcel Wiesweg
In reply to this post by Ananta Palani

> Yes, this is the highest priority for me. I would like to fix all file
> handling in Windows. Right now it is extremely slow, and deleting a file
> is
> not possible. This is not only a problem for digiKam, but also for KDE on
> Windows in general (see, for example, Dolphin). I will try to implement
> these improvements as time allows.

There seems to be the FindFirstChangeNotification API:
http://msdn.microsoft.com/en-us/library/windows/desktop/aa364417%28v=vs.85%29.aspx
with FILE_NOTIFY_CHANGE_LAST_WRITE apparently providing what we'd need.
Not sure if QSocketNotifier takes the relevant handle, it would be an elegant solution (similar to the KInotify implementation). I didn't check though if KDirWatch already implements that.

Marcel
--
NEU: FreePhone - 0ct/min Handyspartarif mit Geld-zurück-Garantie!
Jetzt informieren: http://www.gmx.net/de/go/freephone
_______________________________________________
Digikam-devel mailing list
[hidden email]
https://mail.kde.org/mailman/listinfo/digikam-devel
Reply | Threaded
Open this post in threaded view
|

Re: [digikam] /: On Linux, use Inotify directly for file notification changes

Gilles Caulier-4
In reply to this post by Gilles Caulier-4
Fixed in git/master. I also review code and polished coding style to be more readable...

Gilles Caulier

2011/11/8 Gilles Caulier <[hidden email]>
Thanks Ananta.

Marcel,

current implementation do not compile here :

[ 88%] Building CXX object core/digikam/CMakeFiles/digikam.dir/digikamadaptor.cpp.o
Linking CXX executable digikam
CMakeFiles/digikam.dir/album/albumwatch.cpp.o: In function `Digikam::AlbumWatch::AlbumWatchPriv::determineMode()':
/mnt/data/Devel/GIT/2.x/core/digikam/album/albumwatch.cpp:93: undefined reference to `Digikam::KInotify::KInotify(QObject*)'
/mnt/data/Devel/GIT/2.x/core/digikam/album/albumwatch.cpp:93: undefined reference to `Digikam::KInotify::available() const'
/mnt/data/Devel/GIT/2.x/core/digikam/album/albumwatch.cpp:93: undefined reference to `Digikam::KInotify::~KInotify()'
/mnt/data/Devel/GIT/2.x/core/digikam/album/albumwatch.cpp:93: undefined reference to `Digikam::KInotify::~KInotify()'
CMakeFiles/digikam.dir/album/albumwatch.cpp.o: In function `Digikam::AlbumWatch::clear()':
/mnt/data/Devel/GIT/2.x/core/digikam/album/albumwatch.cpp:167: undefined reference to `Digikam::KInotify::removeAllWatches()'
CMakeFiles/digikam.dir/album/albumwatch.cpp.o: In function `Digikam::AlbumWatch::slotAlbumAdded(Digikam::Album*)':
/mnt/data/Devel/GIT/2.x/core/digikam/album/albumwatch.cpp:211: undefined reference to `Digikam::KInotify::watchDirectory(QString const&)'
CMakeFiles/digikam.dir/album/albumwatch.cpp.o: In function `Digikam::AlbumWatch::slotAlbumAboutToBeDeleted(Digikam::Album*)':
/mnt/data/Devel/GIT/2.x/core/digikam/album/albumwatch.cpp:239: undefined reference to `Digikam::KInotify::removeDirectory(QString const&)'
CMakeFiles/digikam.dir/album/albumwatch.cpp.o: In function `Digikam::AlbumWatch::connectToKInotify()':
/mnt/data/Devel/GIT/2.x/core/digikam/album/albumwatch.cpp:262: undefined reference to `Digikam::KInotify::KInotify(QObject*)'
collect2: ld a retourné 1 code d'état d'exécution
make[2]: *** [core/digikam/digikam] Erreur 1
make[1]: *** [core/digikam/CMakeFiles/digikam.dir/all] Erreur 2
make: *** [all] Erreur 2

Perhaos it's an export problem, as usual. I will try to clean up all and rebuild to see...

Gilles

2011/11/8 Ananta Palani <[hidden email]>
Yes, this is the highest priority for me. I would like to fix all file handling in Windows. Right now it is extremely slow, and deleting a file is not possible. This is not only a problem for digiKam, but also for KDE on Windows in general (see, for example, Dolphin). I will try to implement these improvements as time allows.

-Ananta


On Tue, Nov 8, 2011 at 9:14 AM, Gilles Caulier <[hidden email]> wrote:
Thanks Marcel for the info ,

Ananta Palani who work under Windows to port digiKam. He build installer and can help us around this subject...

Best

Gilles Caulier

2011/11/8 Marcel Wiesweg <[hidden email]>:
>
>
>> 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 ?
>
> It's not yet in kdelibs, it was only recently written for use inside the Nepomuk indexer. The code was copied and slightly customized from Nepomuk Core.
> In the current form, I'm not sure it will ever be in kdelibs: For Linux, it's the best solution, because we get information in better quality, and we get the information which we really need (closed-after-write). But Inotify is definitely Linux-only.
> If there are comparable solutions for Windows and Mac, the way to go would be to extend KDirWatch API and offer this cross-platform. But I dont know about available APIs on these systems.
>
> Marcel
> --
> NEU: FreePhone - 0ct/min Handyspartarif mit Geld-zurück-Garantie!
> Jetzt informieren: http://www.gmx.net/de/go/freephone
> _______________________________________________
> Digikam-devel mailing list
> [hidden email]
> https://mail.kde.org/mailman/listinfo/digikam-devel
>
> --------------------------------------------------------------------------------------------------------------

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

> --------------------------------------------------------------------------------------------------------------

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: [hidden email]

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


_______________________________________________
Digikam-devel mailing list
[hidden email]
https://mail.kde.org/mailman/listinfo/digikam-devel




_______________________________________________
Digikam-devel mailing list
[hidden email]
https://mail.kde.org/mailman/listinfo/digikam-devel
Reply | Threaded
Open this post in threaded view
|

Re: [digikam] /: On Linux, use Inotify directly for file notification changes

Gilles Caulier-4
Ananta,

please check if my last commits compile fine under Windows. Thanks in advance

Gilles

2011/11/8 Gilles Caulier <[hidden email]>
Fixed in git/master. I also review code and polished coding style to be more readable...

Gilles Caulier


2011/11/8 Gilles Caulier <[hidden email]>
Thanks Ananta.

Marcel,

current implementation do not compile here :

[ 88%] Building CXX object core/digikam/CMakeFiles/digikam.dir/digikamadaptor.cpp.o
Linking CXX executable digikam
CMakeFiles/digikam.dir/album/albumwatch.cpp.o: In function `Digikam::AlbumWatch::AlbumWatchPriv::determineMode()':
/mnt/data/Devel/GIT/2.x/core/digikam/album/albumwatch.cpp:93: undefined reference to `Digikam::KInotify::KInotify(QObject*)'
/mnt/data/Devel/GIT/2.x/core/digikam/album/albumwatch.cpp:93: undefined reference to `Digikam::KInotify::available() const'
/mnt/data/Devel/GIT/2.x/core/digikam/album/albumwatch.cpp:93: undefined reference to `Digikam::KInotify::~KInotify()'
/mnt/data/Devel/GIT/2.x/core/digikam/album/albumwatch.cpp:93: undefined reference to `Digikam::KInotify::~KInotify()'
CMakeFiles/digikam.dir/album/albumwatch.cpp.o: In function `Digikam::AlbumWatch::clear()':
/mnt/data/Devel/GIT/2.x/core/digikam/album/albumwatch.cpp:167: undefined reference to `Digikam::KInotify::removeAllWatches()'
CMakeFiles/digikam.dir/album/albumwatch.cpp.o: In function `Digikam::AlbumWatch::slotAlbumAdded(Digikam::Album*)':
/mnt/data/Devel/GIT/2.x/core/digikam/album/albumwatch.cpp:211: undefined reference to `Digikam::KInotify::watchDirectory(QString const&)'
CMakeFiles/digikam.dir/album/albumwatch.cpp.o: In function `Digikam::AlbumWatch::slotAlbumAboutToBeDeleted(Digikam::Album*)':
/mnt/data/Devel/GIT/2.x/core/digikam/album/albumwatch.cpp:239: undefined reference to `Digikam::KInotify::removeDirectory(QString const&)'
CMakeFiles/digikam.dir/album/albumwatch.cpp.o: In function `Digikam::AlbumWatch::connectToKInotify()':
/mnt/data/Devel/GIT/2.x/core/digikam/album/albumwatch.cpp:262: undefined reference to `Digikam::KInotify::KInotify(QObject*)'
collect2: ld a retourné 1 code d'état d'exécution
make[2]: *** [core/digikam/digikam] Erreur 1
make[1]: *** [core/digikam/CMakeFiles/digikam.dir/all] Erreur 2
make: *** [all] Erreur 2

Perhaos it's an export problem, as usual. I will try to clean up all and rebuild to see...

Gilles

2011/11/8 Ananta Palani <[hidden email]>
Yes, this is the highest priority for me. I would like to fix all file handling in Windows. Right now it is extremely slow, and deleting a file is not possible. This is not only a problem for digiKam, but also for KDE on Windows in general (see, for example, Dolphin). I will try to implement these improvements as time allows.

-Ananta


On Tue, Nov 8, 2011 at 9:14 AM, Gilles Caulier <[hidden email]> wrote:
Thanks Marcel for the info ,

Ananta Palani who work under Windows to port digiKam. He build installer and can help us around this subject...

Best

Gilles Caulier

2011/11/8 Marcel Wiesweg <[hidden email]>:
>
>
>> 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 ?
>
> It's not yet in kdelibs, it was only recently written for use inside the Nepomuk indexer. The code was copied and slightly customized from Nepomuk Core.
> In the current form, I'm not sure it will ever be in kdelibs: For Linux, it's the best solution, because we get information in better quality, and we get the information which we really need (closed-after-write). But Inotify is definitely Linux-only.
> If there are comparable solutions for Windows and Mac, the way to go would be to extend KDirWatch API and offer this cross-platform. But I dont know about available APIs on these systems.
>
> Marcel
> --
> NEU: FreePhone - 0ct/min Handyspartarif mit Geld-zurück-Garantie!
> Jetzt informieren: http://www.gmx.net/de/go/freephone
> _______________________________________________
> Digikam-devel mailing list
> [hidden email]
> https://mail.kde.org/mailman/listinfo/digikam-devel
>
> --------------------------------------------------------------------------------------------------------------

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

> --------------------------------------------------------------------------------------------------------------

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: [hidden email]

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


_______________________________________________
Digikam-devel mailing list
[hidden email]
https://mail.kde.org/mailman/listinfo/digikam-devel





_______________________________________________
Digikam-devel mailing list
[hidden email]
https://mail.kde.org/mailman/listinfo/digikam-devel
Reply | Threaded
Open this post in threaded view
|

Re: [digikam] /: On Linux, use Inotify directly for file notification changes

Ananta Palani
In reply to this post by Ananta Palani
I have updated git with the missing function to allow build on Windows. There is a problem running the new code, however.

digiKam crashes on line 93 of albumwatch.cpp because of KInotify().available(). It happens due to the windows constructor. I haven't learned Qt yet, so I'm not sure what it expects. The problem appears to be setting d(0) in the initialization list of the constructor (although parent also appears to be 0). Here is the constructor for windows from kinotify.cpp:

   KInotify::KInotify(QObject* parent) : QObject(parent), d(0) {}

Let me know if I can be more help.

-Ananta


_______________________________________________
Digikam-devel mailing list
[hidden email]
https://mail.kde.org/mailman/listinfo/digikam-devel
Reply | Threaded
Open this post in threaded view
|

Re: [digikam] /: On Linux, use Inotify directly for file notification changes

Marcel Wiesweg


> digiKam crashes on line 93 of albumwatch.cpp because of
> KInotify().available(). It happens due to the windows constructor. I
> haven't learned Qt yet, so I'm not sure what it expects. The problem
> appears to be setting d(0) in the initialization list of the constructor
> (although parent also appears to be 0). Here is the constructor for windows
> from kinotify.cpp:
>
>    KInotify::KInotify(QObject* parent) : QObject(parent), d(0) {}
>
> Let me know if I can be more help.

Please try with current git. This is only dummy code, embarassing that it
crashes...

Marcel
_______________________________________________
Digikam-devel mailing list
[hidden email]
https://mail.kde.org/mailman/listinfo/digikam-devel
Reply | Threaded
Open this post in threaded view
|

Re: [digikam] /: On Linux, use Inotify directly for file notification changes

Ananta Palani
In reply to this post by Ananta Palani
I made changes to albumwatch.cpp and kinotify.cpp to allow building/running on windows. You forgot to make the windows 'available()' static, and used an instance of KDirWatch before it was created. Please double-check that these are desirable changes.

-Ananta


_______________________________________________
Digikam-devel mailing list
[hidden email]
https://mail.kde.org/mailman/listinfo/digikam-devel
Reply | Threaded
Open this post in threaded view
|

Re: [digikam] /: On Linux, use Inotify directly for file notification changes

Gilles Caulier-4
Ananta,

Can you try to compile libkdcraw from git master with MSVC ?

Do you have any compilation errors or warnings (excepted the depreated warnings that i introduced in kdcraw.h) ?

In all case M$ compiler warnings are always instructive. It's always a good idea to check all warnings from MSVC. We have already found some glitch in code that GCC never found.

In all case, if you find any comprehensible warnings generated by M$ compiler, just inform us through this ML.

Gilles

2011/11/10 Ananta Palani <[hidden email]>
I made changes to albumwatch.cpp and kinotify.cpp to allow building/running on windows. You forgot to make the windows 'available()' static, and used an instance of KDirWatch before it was created. Please double-check that these are desirable changes.

-Ananta


_______________________________________________
Digikam-devel mailing list
[hidden email]
https://mail.kde.org/mailman/listinfo/digikam-devel



_______________________________________________
Digikam-devel mailing list
[hidden email]
https://mail.kde.org/mailman/listinfo/digikam-devel
Reply | Threaded
Open this post in threaded view
|

Re: [digikam] /: On Linux, use Inotify directly for file notification changes

Gilles Caulier-4
In reply to this post by Marcel Wiesweg
Marcel,

I just tested under Linux. I deleted an physical album from Album Tree-view on the right side of icon-view, using pop-up menu, and album still visible.

If i try to delete again, digiKAm said that album do not exist.  So, it's really deleted from HDD.

I need to press F5 to refresh album tree-view.

Gilles

_______________________________________________
Digikam-devel mailing list
[hidden email]
https://mail.kde.org/mailman/listinfo/digikam-devel
Reply | Threaded
Open this post in threaded view
|

Re: [digikam] /: On Linux, use Inotify directly for file notification changes

Gilles Caulier-4
Marcel,

In fact it's the same problem if i want to delete an item from an album. Item is not remove from icon view, but it really removed from HDD. I need to press F5 to refresh view properly...

Gilles Caulier

2011/11/10 Gilles Caulier <[hidden email]>
Marcel,

I just tested under Linux. I deleted an physical album from Album Tree-view on the right side of icon-view, using pop-up menu, and album still visible.

If i try to delete again, digiKAm said that album do not exist.  So, it's really deleted from HDD.

I need to press F5 to refresh album tree-view.

Gilles


_______________________________________________
Digikam-devel mailing list
[hidden email]
https://mail.kde.org/mailman/listinfo/digikam-devel
Reply | Threaded
Open this post in threaded view
|

Re: [digikam] /: On Linux, use Inotify directly for file notification changes

brad
In reply to this post by Marcel Wiesweg

On Monday, November 07, 2011 11:06:29 PM Marcel Wiesweg wrote:

> 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-u

> sability/ 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.

>

 

Sorry to chime in on this a month and a half late. I'm just getting around to building digikam on OS X again. It appears that this addition to digikam is causing problems on OS X. Above, it is stated that this should on effect linux; however this appears to be triggering an old QT but in OS X. There is a bug report, which is 'fixed' with code that still appears to trigger there error

 

here's the old bug report -- supposed to have been fix in 4.7: https://bugreports.qt.nokia.com//browse/QTBUG-2478

 

Now when i run digikam, i am seeing only thumbnails that are cached but can't access any pictures (i get an error, can't load file) in my albums and constantly getting the following error (reported in the above bug report):


  QKqueueFileSystemWatcherEngine::addPaths: open: Too many open files in system
  QFileSystemWatcher: failed to add paths: /Volumes/Home Directory/Pictures/Saved pictures/Pic0193.jpg
  QKqueueFileSystemWatcherEngine::addPaths: open: Too many open files in system
  QFileSystemWatcher: failed to add paths: /Volumes/Home Directory/Pictures/Saved pictures/Pic0194.jpg
  QKqueueFileSystemWatcherEngine::addPaths: open: Too many open files in system
  QFileSystemWatcher: failed to add paths: /Volumes/Home Directory/Pictures/Saved pictures/Pic0195.jpg
  QKqueueFileSystemWatcherEngine::addPaths: open: Too many open files in system
  QFileSystemWatcher: failed to add paths: /Volumes/Home Directory/Pictures/Saved pictures/Pic0200.jpg
  QKqueueFileSystemWatcherEngine::addPaths: open: Too many open files in system
  QFileSystemWatcher: failed to add paths: /Volumes/Home Directory/Pictures/Saved pictures/Pic0201.jpg

Is anyone else building digikam on OS X and seeing this error? Or is it just me? 
- brad

 

 


_______________________________________________
Digikam-devel mailing list
[hidden email]
https://mail.kde.org/mailman/listinfo/digikam-devel
Reply | Threaded
Open this post in threaded view
|

Re: [digikam] /: On Linux, use Inotify directly for file notification changes

Gilles Caulier-4
Please report these information to digiKam bugzilla please.

Gilles Caulier

2011/12/18 brad <[hidden email]>:

> On Monday, November 07, 2011 11:06:29 PM Marcel Wiesweg wrote:
>
>> 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-u
>
>> sability/ 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.
>
>>
>
>
>
> Sorry to chime in on this a month and a half late. I'm just getting around
> to building digikam on OS X again. It appears that this addition to digikam
> is causing problems on OS X. Above, it is stated that this should on effect
> linux; however this appears to be triggering an old QT but in OS X. There is
> a bug report, which is 'fixed' with code that still appears to trigger there
> error
>
>
>
> here's the old bug report -- supposed to have been fix in 4.7:
> https://bugreports.qt.nokia.com//browse/QTBUG-2478
>
>
>
> Now when i run digikam, i am seeing only thumbnails that are cached but
> can't access any pictures (i get an error, can't load file) in my albums and
> constantly getting the following error (reported in the above bug report):
>
>
>   QKqueueFileSystemWatcherEngine::addPaths: open: Too many open files in
> system
>   QFileSystemWatcher: failed to add paths: /Volumes/Home
> Directory/Pictures/Saved pictures/Pic0193.jpg
>   QKqueueFileSystemWatcherEngine::addPaths: open: Too many open files in
> system
>   QFileSystemWatcher: failed to add paths: /Volumes/Home
> Directory/Pictures/Saved pictures/Pic0194.jpg
>   QKqueueFileSystemWatcherEngine::addPaths: open: Too many open files in
> system
>   QFileSystemWatcher: failed to add paths: /Volumes/Home
> Directory/Pictures/Saved pictures/Pic0195.jpg
>   QKqueueFileSystemWatcherEngine::addPaths: open: Too many open files in
> system
>   QFileSystemWatcher: failed to add paths: /Volumes/Home
> Directory/Pictures/Saved pictures/Pic0200.jpg
>   QKqueueFileSystemWatcherEngine::addPaths: open: Too many open files in
> system
>   QFileSystemWatcher: failed to add paths: /Volumes/Home
> Directory/Pictures/Saved pictures/Pic0201.jpg
>
> Is anyone else building digikam on OS X and seeing this error? Or is it just
> me?
> - brad
>
>
>
>
>
>
> _______________________________________________
> Digikam-devel mailing list
> [hidden email]
> https://mail.kde.org/mailman/listinfo/digikam-devel
>
_______________________________________________
Digikam-devel mailing list
[hidden email]
https://mail.kde.org/mailman/listinfo/digikam-devel