Merge filesystem
authorIceyer <me@iceyer.net>
Tue, 25 Jul 2017 12:27:53 +0000 (20:27 +0800)
committerIceyer <me@iceyer.net>
Tue, 25 Jul 2017 12:29:12 +0000 (20:29 +0800)
26 files changed:
src/base/base.pri [new file with mode: 0644]
src/base/dbase.pri [deleted file]
src/base/dbase.pro [deleted file]
src/filesystem/DBaseFileWatcher [new file with mode: 0644]
src/filesystem/DFileSystemWatcher [new file with mode: 0644]
src/filesystem/DFileWatcher [new file with mode: 0644]
src/filesystem/DFileWatcherManager [new file with mode: 0644]
src/filesystem/DPathBuf [new file with mode: 0644]
src/filesystem/dbasefilewatcher.cpp [new file with mode: 0644]
src/filesystem/dbasefilewatcher.h [new file with mode: 0644]
src/filesystem/dfilesystemwatcher.h [new file with mode: 0644]
src/filesystem/dfilesystemwatcher_linux.cpp [new file with mode: 0644]
src/filesystem/dfilesystemwatcher_win.cpp [new file with mode: 0644]
src/filesystem/dfilewatcher.cpp [new file with mode: 0644]
src/filesystem/dfilewatcher.h [new file with mode: 0644]
src/filesystem/dfilewatchermanager.cpp [new file with mode: 0644]
src/filesystem/dfilewatchermanager.h [new file with mode: 0644]
src/filesystem/dpathbuf.h [new file with mode: 0644]
src/filesystem/filesystem.pri [new file with mode: 0644]
src/filesystem/private/dbasefilewatcher_p.h [new file with mode: 0644]
src/filesystem/private/dfilesystemwatcher_linux_p.h [new file with mode: 0644]
src/filesystem/private/dfilesystemwatcher_win_p.h [new file with mode: 0644]
src/filesystem/private/private.pri [new file with mode: 0644]
src/install.pri
src/log/log.pri
src/src.pro

diff --git a/src/base/base.pri b/src/base/base.pri
new file mode 100644 (file)
index 0000000..a061868
--- /dev/null
@@ -0,0 +1,14 @@
+include($$PWD/private/private.pri)
+
+HEADERS += \
+    $$PWD/dobject.h \
+    $$PWD/dsingleton.h
+
+SOURCES += \
+    $$PWD/dobject.cpp
+
+includes.files += $$PWD/*.h
+includes.files += $$PWD/*.cpp
+includes.files += \
+    $$PWD/DObject \
+    $$PWD/DSingleton
diff --git a/src/base/dbase.pri b/src/base/dbase.pri
deleted file mode 100644 (file)
index 5dc9121..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-include($$PWD/private/private.pri)
-
-HEADERS += \
-    $$PWD/dobject.h \
-    $$PWD/dsingleton.h
-
-SOURCES += \
-    $$PWD/dobject.cpp
diff --git a/src/base/dbase.pro b/src/base/dbase.pro
deleted file mode 100644 (file)
index bb7f80c..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-DCOMMON_DIR = $$PWD/../common
-include($$DCOMMON_DIR/lib.pri)
-include($$PWD/dbase.pri)
-
-QT -= gui
-
-TARGET = dtkbase
-
-DEFINES += LIBDTKBASE_LIBRARY
-
-includes.path = $${DTK_INCLUDEPATH}/DBase
-includes.files += *.h
-includes.files += DObject
-
-QMAKE_PKGCONFIG_NAME = DTK_BASE
-QMAKE_PKGCONFIG_DESCRIPTION = Deepin Tool Kit Base Header Files
-QMAKE_PKGCONFIG_INCDIR = $$includes.path
-
-DISTFILES += \
-    DSingleton
diff --git a/src/filesystem/DBaseFileWatcher b/src/filesystem/DBaseFileWatcher
new file mode 100644 (file)
index 0000000..1348031
--- /dev/null
@@ -0,0 +1 @@
+#include "dbasefilewatcher.h"
diff --git a/src/filesystem/DFileSystemWatcher b/src/filesystem/DFileSystemWatcher
new file mode 100644 (file)
index 0000000..97400f2
--- /dev/null
@@ -0,0 +1 @@
+#include "dfilesystemwatcher.h"
diff --git a/src/filesystem/DFileWatcher b/src/filesystem/DFileWatcher
new file mode 100644 (file)
index 0000000..768151a
--- /dev/null
@@ -0,0 +1 @@
+#include "dfilewatcher.h"
diff --git a/src/filesystem/DFileWatcherManager b/src/filesystem/DFileWatcherManager
new file mode 100644 (file)
index 0000000..a9f8682
--- /dev/null
@@ -0,0 +1 @@
+#include "dfilewatchermanager.h"
diff --git a/src/filesystem/DPathBuf b/src/filesystem/DPathBuf
new file mode 100644 (file)
index 0000000..961c40b
--- /dev/null
@@ -0,0 +1 @@
+#include "dpathbuf.h"
diff --git a/src/filesystem/dbasefilewatcher.cpp b/src/filesystem/dbasefilewatcher.cpp
new file mode 100644 (file)
index 0000000..c2ca79f
--- /dev/null
@@ -0,0 +1,129 @@
+/**
+ * Copyright (C) 2017 Deepin Technology Co., Ltd.
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ **/
+
+#include "dbasefilewatcher.h"
+#include "private/dbasefilewatcher_p.h"
+
+#include <QEvent>
+#include <QDebug>
+
+DCORE_BEGIN_NAMESPACE
+
+QList<DBaseFileWatcher*> DBaseFileWatcherPrivate::watcherList;
+DBaseFileWatcherPrivate::DBaseFileWatcherPrivate(DBaseFileWatcher *qq)
+    : DObjectPrivate(qq)
+{
+
+}
+
+DBaseFileWatcher::~DBaseFileWatcher()
+{
+    stopWatcher();
+    DBaseFileWatcherPrivate::watcherList.removeOne(this);
+}
+
+QUrl DBaseFileWatcher::fileUrl() const
+{
+    Q_D(const DBaseFileWatcher);
+
+    return d->url;
+}
+
+bool DBaseFileWatcher::startWatcher()
+{
+    Q_D(DBaseFileWatcher);
+
+    if (d->started)
+        return true;
+
+    if (d->start()) {
+        d->started = true;
+
+        return true;
+    }
+
+    return false;
+}
+
+bool DBaseFileWatcher::stopWatcher()
+{
+    Q_D(DBaseFileWatcher);
+
+    if (!d->started)
+        return false;
+
+    if (d->stop()) {
+        d->started = false;
+
+        return true;
+    }
+
+    return false;
+}
+
+bool DBaseFileWatcher::restartWatcher()
+{
+    bool ok = stopWatcher();
+    return ok && startWatcher();
+}
+
+void DBaseFileWatcher::setEnabledSubfileWatcher(const QUrl &subfileUrl, bool enabled)
+{
+    Q_UNUSED(subfileUrl)
+    Q_UNUSED(enabled)
+}
+
+bool DBaseFileWatcher::ghostSignal(const QUrl &targetUrl, DBaseFileWatcher::SignalType1 signal, const QUrl &arg1)
+{
+    if (!signal)
+        return false;
+
+    bool ok = false;
+
+    for (DBaseFileWatcher *watcher : DBaseFileWatcherPrivate::watcherList) {
+        if (watcher->fileUrl() == targetUrl) {
+            ok = true;
+            (watcher->*signal)(arg1);
+        }
+    }
+
+    return ok;
+}
+
+bool DBaseFileWatcher::ghostSignal(const QUrl &targetUrl, DBaseFileWatcher::SignalType2 signal, const QUrl &arg1, const QUrl &arg2)
+{
+    if (!signal)
+        return false;
+
+    bool ok = false;
+
+    for (DBaseFileWatcher *watcher : DBaseFileWatcherPrivate::watcherList) {
+        if (watcher->fileUrl() == targetUrl) {
+            ok = true;
+            (watcher->*signal)(arg1, arg2);
+        }
+    }
+
+    return ok;
+}
+
+DBaseFileWatcher::DBaseFileWatcher(DBaseFileWatcherPrivate &dd,
+                                           const QUrl &url, QObject *parent)
+    : QObject(parent)
+    , DObject(dd)
+{
+    Q_ASSERT(url.isValid());
+
+    d_func()->url = url;
+    DBaseFileWatcherPrivate::watcherList << this;
+}
+
+DCORE_END_NAMESPACE
+
+#include "moc_dbasefilewatcher.cpp"
diff --git a/src/filesystem/dbasefilewatcher.h b/src/filesystem/dbasefilewatcher.h
new file mode 100644 (file)
index 0000000..2d0b37b
--- /dev/null
@@ -0,0 +1,59 @@
+/**
+ * Copyright (C) 2017 Deepin Technology Co., Ltd.
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ **/
+
+#ifndef DBASEFILEWATCHER_H
+#define DBASEFILEWATCHER_H
+
+#include "dtkcore_global.h"
+#include "base/dobject.h"
+
+#include <QObject>
+
+DCORE_BEGIN_NAMESPACE
+
+class DBaseFileWatcherPrivate;
+class DBaseFileWatcher : public QObject, public DObject
+{
+    Q_OBJECT
+
+public:
+    ~DBaseFileWatcher();
+
+    QUrl fileUrl() const;
+
+    bool startWatcher();
+    bool stopWatcher();
+    bool restartWatcher();
+
+    virtual void setEnabledSubfileWatcher(const QUrl &subfileUrl, bool enabled = true);
+
+    using SignalType1 = void(DBaseFileWatcher::*)(const QUrl &);
+    using SignalType2 = void(DBaseFileWatcher::*)(const QUrl &, const QUrl &);
+    static bool ghostSignal(const QUrl &targetUrl, SignalType1 signal, const QUrl &arg1);
+    static bool ghostSignal(const QUrl &targetUrl, SignalType2 signal, const QUrl &arg1, const QUrl &arg2);
+
+Q_SIGNALS:
+    void fileDeleted(const QUrl &url);
+    void fileAttributeChanged(const QUrl &url);
+    void fileMoved(const QUrl &fromUrl, const QUrl &toUrl);
+    void subfileCreated(const QUrl &url);
+    void fileModified(const QUrl &url);
+    void fileClosed(const QUrl &url);
+
+protected:
+    explicit DBaseFileWatcher(DBaseFileWatcherPrivate &dd, const QUrl &url, QObject *parent = 0);
+
+private:
+    Q_DISABLE_COPY(DBaseFileWatcher)
+    D_DECLARE_PRIVATE(DBaseFileWatcher)
+};
+
+DCORE_END_NAMESPACE
+
+#endif // DBASEFILEWATCHER_H
diff --git a/src/filesystem/dfilesystemwatcher.h b/src/filesystem/dfilesystemwatcher.h
new file mode 100644 (file)
index 0000000..39cdda5
--- /dev/null
@@ -0,0 +1,54 @@
+/**
+ * Copyright (C) 2017 Deepin Technology Co., Ltd.
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ **/
+
+#ifndef DFILESYSTEMWATCHER_H
+#define DFILESYSTEMWATCHER_H
+
+#include "dtkcore_global.h"
+#include "base/dobject.h"
+
+#include <QObject>
+
+DCORE_BEGIN_NAMESPACE
+
+class DFileSystemWatcherPrivate;
+class DFileSystemWatcher : public QObject, public DObject
+{
+    Q_OBJECT
+    D_DECLARE_PRIVATE(DFileSystemWatcher)
+
+public:
+    DFileSystemWatcher(QObject *parent = Q_NULLPTR);
+    DFileSystemWatcher(const QStringList &paths, QObject *parent = Q_NULLPTR);
+    ~DFileSystemWatcher();
+
+    bool addPath(const QString &file);
+    QStringList addPaths(const QStringList &files);
+    bool removePath(const QString &file);
+    QStringList removePaths(const QStringList &files);
+
+    QStringList files() const;
+    QStringList directories() const;
+
+Q_SIGNALS:
+    void fileDeleted(const QString &path, const QString &name, QPrivateSignal);
+    void fileAttributeChanged(const QString &path, const QString &name, QPrivateSignal);
+    void fileClosed(const QString &path, const QString &name, QPrivateSignal);
+    void fileMoved(const QString &fromPath, const QString &fromName,
+                   const QString &toPath, const QString &toName, QPrivateSignal);
+    void fileCreated(const QString &path, const QString &name, QPrivateSignal);
+    void fileModified(const QString &path, const QString &name, QPrivateSignal);
+
+private:
+    Q_PRIVATE_SLOT(d_func(), void _q_readFromInotify())
+};
+
+DCORE_END_NAMESPACE
+
+#endif // DFILESYSTEMWATCHER_H
diff --git a/src/filesystem/dfilesystemwatcher_linux.cpp b/src/filesystem/dfilesystemwatcher_linux.cpp
new file mode 100644 (file)
index 0000000..aa1f637
--- /dev/null
@@ -0,0 +1,638 @@
+/**
+ * Copyright (C) 2017 Deepin Technology Co., Ltd.
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ **/
+
+#include "dfilesystemwatcher.h"
+#include "private/dfilesystemwatcher_linux_p.h"
+
+#include <QFileInfo>
+#include <QDir>
+#include <QSocketNotifier>
+#include <QDebug>
+
+#include <sys/inotify.h>
+#include <sys/fcntl.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+
+DCORE_BEGIN_NAMESPACE
+
+DFileSystemWatcherPrivate::DFileSystemWatcherPrivate(int fd, DFileSystemWatcher *qq)
+    : DObjectPrivate(qq)
+    , inotifyFd(fd)
+    , notifier(fd, QSocketNotifier::Read, qq)
+{
+    fcntl(inotifyFd, F_SETFD, FD_CLOEXEC);
+    qq->connect(&notifier, SIGNAL(activated(int)), qq, SLOT(_q_readFromInotify()));
+}
+
+DFileSystemWatcherPrivate::~DFileSystemWatcherPrivate()
+{
+    notifier.setEnabled(false);
+    Q_FOREACH (int id, pathToID)
+        inotify_rm_watch(inotifyFd, id < 0 ? -id : id);
+
+    ::close(inotifyFd);
+}
+
+QStringList DFileSystemWatcherPrivate::addPaths(const QStringList &paths, QStringList *files, QStringList *directories)
+{
+    QStringList p = paths;
+    QMutableListIterator<QString> it(p);
+    while (it.hasNext()) {
+        QString path = it.next();
+        QFileInfo fi(path);
+        bool isDir = fi.isDir();
+        if (isDir) {
+            if (directories->contains(path))
+                continue;
+        } else {
+            if (files->contains(path))
+                continue;
+        }
+
+        int wd = inotify_add_watch(inotifyFd,
+                                   QFile::encodeName(path),
+                                   (isDir
+                                    ? (0
+                                       | IN_ATTRIB
+                                       | IN_MOVE
+                                       | IN_MOVE_SELF
+                                       | IN_CREATE
+                                       | IN_DELETE
+                                       | IN_DELETE_SELF
+                                       | IN_MODIFY
+                                       )
+                                    : (0
+                                       | IN_ATTRIB
+                                       | IN_CLOSE_WRITE
+                                       | IN_MODIFY
+                                       | IN_MOVE
+                                       | IN_MOVE_SELF
+                                       | IN_DELETE_SELF
+                                       )));
+        if (wd < 0) {
+            perror("DFileSystemWatcherPrivate::addPaths: inotify_add_watch failed");
+            continue;
+        }
+
+        it.remove();
+
+        int id = isDir ? -wd : wd;
+        if (id < 0) {
+            directories->append(path);
+        } else {
+            files->append(path);
+        }
+
+        pathToID.insert(path, id);
+        idToPath.insert(id, path);
+    }
+
+    return p;
+}
+
+QStringList DFileSystemWatcherPrivate::removePaths(const QStringList &paths, QStringList *files, QStringList *directories)
+{
+    QStringList p = paths;
+    QMutableListIterator<QString> it(p);
+    while (it.hasNext()) {
+        QString path = it.next();
+        int id = pathToID.take(path);
+        QString x = idToPath.take(id);
+
+        it.remove();
+
+        if (x.isEmpty() || x != path)
+            continue;
+
+        int wd = id < 0 ? -id : id;
+        // qDebug() << "removing watch for path" << path << "wd" << wd;
+        inotify_rm_watch(inotifyFd, wd);
+
+        if (id < 0) {
+            directories->removeAll(path);
+        } else {
+            files->removeAll(path);
+        }
+    }
+
+    return p;
+}
+
+void DFileSystemWatcherPrivate::_q_readFromInotify()
+{
+    Q_Q(DFileSystemWatcher);
+//    qDebug() << "QInotifyFileSystemWatcherEngine::readFromInotify";
+
+    int buffSize = 0;
+    ioctl(inotifyFd, FIONREAD, (char *) &buffSize);
+    QVarLengthArray<char, 4096> buffer(buffSize);
+    buffSize = read(inotifyFd, buffer.data(), buffSize);
+    char *at = buffer.data();
+    char * const end = at + buffSize;
+
+    QList<inotify_event *> eventList;
+    QHash<int, QString> pathForId;
+    /// only save event: IN_MOVE_TO
+    QMap<int, QString> cookieToFilePath;
+    QMap<int, QString> cookieToFileName;
+    QSet<int> hasMoveFromByCookie;
+    while (at < end) {
+        inotify_event *event = reinterpret_cast<inotify_event *>(at);
+        QString path;
+
+        at += sizeof(inotify_event) + event->len;
+
+        int id = event->wd;
+        path = getPathFromID(id);
+        if (path.isEmpty()) {
+            // perhaps a directory?
+            id = -id;
+            path = getPathFromID(id);
+            if (path.isEmpty())
+                continue;
+        }
+
+        if (!(event->mask & IN_MOVED_TO) || !hasMoveFromByCookie.contains(event->cookie)) {
+            eventList.append(event);
+            pathForId.insert(id, path);
+        }
+
+        if (event->mask & IN_MOVED_TO) {
+            cookieToFilePath.insert(event->cookie, path);
+            cookieToFileName.insert(event->cookie, QString::fromUtf8(event->name));
+        }
+
+        if (event->mask & IN_MOVED_FROM)
+            hasMoveFromByCookie << event->cookie;
+    }
+
+//    qDebug() << "event count:" << eventList.count();
+
+    QList<inotify_event *>::const_iterator it = eventList.constBegin();
+    while (it != eventList.constEnd()) {
+        const inotify_event &event = **it;
+        ++it;
+
+//        qDebug() << "inotify event, wd" << event.wd << "cookie" << event.cookie << "mask" << hex << event.mask;
+
+        int id = event.wd;
+        QString path = pathForId.value(id);
+
+        if (path.isEmpty()) {
+            id = -id;
+            path = pathForId.value(id);
+
+            if (path.isEmpty())
+                continue;
+        }
+        const QString &name = QString::fromUtf8(event.name);
+
+//        qDebug() << "event for path" << path;
+
+//        /// TODO: Existence of invalid utf8 characters QFile can not read the file information
+//        if (event.name != QString::fromLocal8Bit(event.name).toLocal8Bit()) {
+//            if (event.mask & (IN_CREATE | IN_MOVED_TO)) {
+//                DFMGlobal::fileNameCorrection(path);
+//            }
+//        }
+
+        if ((event.mask & (IN_DELETE_SELF | IN_MOVE_SELF | IN_UNMOUNT)) != 0) {
+            do {
+                if (event.mask & IN_MOVE_SELF) {
+                    QMap<int, QString>::const_iterator iterator = cookieToFilePath.constBegin();
+
+                    bool isMove = false;
+
+                    while (iterator != cookieToFilePath.constEnd()) {
+                        const QString &_path = iterator.value();
+                        const QString &_name = cookieToFileName.value(iterator.key());
+
+                        if (QFileInfo(_path + QDir::separator() + _name) == QFileInfo(path)) {
+                            isMove = true;
+                            break;
+                        }
+
+                        ++iterator;
+                    }
+
+                    if (isMove)
+                        break;
+                }
+
+                /// Keep watcher
+//                pathToID.remove(path);
+//                idToPath.remove(id, getPathFromID(id));
+//                if (!idToPath.contains(id))
+//                    inotify_rm_watch(inotifyFd, event.wd);
+
+//                if (id < 0)
+//                    onDirectoryChanged(path, true);
+//                else
+//                    onFileChanged(path, true);
+
+                Q_EMIT q->fileDeleted(path, QString(), DFileSystemWatcher::QPrivateSignal());
+            } while (false);
+        } else {
+            if (id < 0)
+                onDirectoryChanged(path, false);
+            else
+                onFileChanged(path, false);
+        }
+
+        QString filePath = path;
+
+        if (id < 0) {
+            if (path.endsWith(QDir::separator()))
+                filePath = path + name;
+            else
+                filePath = path  + QDir::separator() + name;
+        }
+
+        if (event.mask & IN_CREATE) {
+//            qDebug() << "IN_CREATE" << filePath << name;
+
+            if (name.isEmpty()) {
+                if (pathToID.contains(path)) {
+                    q->removePath(path);
+                    q->addPath(path);
+                }
+            } else if (pathToID.contains(filePath)) {
+                q->removePath(filePath);
+                q->addPath(filePath);
+            }
+
+            Q_EMIT q->fileCreated(path, name, DFileSystemWatcher::QPrivateSignal());
+        }
+
+        if (event.mask & IN_DELETE) {
+//            qDebug() << "IN_DELETE" << filePath;
+
+            Q_EMIT q->fileDeleted(path, name, DFileSystemWatcher::QPrivateSignal());
+        }
+
+        if (event.mask & IN_MOVED_FROM) {
+            const QString &toPath = cookieToFilePath.value(event.cookie);
+            const QString toName = cookieToFileName.value(event.cookie);
+
+//            qDebug() << "IN_MOVED_FROM" << filePath << "to path:" << toPath << "to name:" << toName;
+
+            Q_EMIT q->fileMoved(path, name, toPath, toName, DFileSystemWatcher::QPrivateSignal());
+        }
+
+        if (event.mask & IN_MOVED_TO) {
+//            qDebug() << "IN_MOVED_TO" << filePath;
+
+            if (!hasMoveFromByCookie.contains(event.cookie))
+                Q_EMIT q->fileMoved(QString(), QString(), path, name, DFileSystemWatcher::QPrivateSignal());
+        }
+
+        if (event.mask & IN_ATTRIB) {
+//            qDebug() << "IN_ATTRIB" <<  event.mask << filePath;
+
+            Q_EMIT q->fileAttributeChanged(path, name, DFileSystemWatcher::QPrivateSignal());
+        }
+
+        /*only monitor file close event which is opend by write mode*/
+        if (event.mask & IN_CLOSE_WRITE) {
+//            qDebug() << "IN_CLOSE_WRITE" <<  event.mask << filePath;
+
+            Q_EMIT q->fileClosed(path, id < 0 ? name : QString(), DFileSystemWatcher::QPrivateSignal());
+        }
+
+        if (event.mask & IN_MODIFY) {
+//            qDebug() << "IN_MODIFY" <<  event.mask << filePath << name;
+
+            Q_EMIT q->fileModified(path, name, DFileSystemWatcher::QPrivateSignal());
+        }
+    }
+}
+
+QString DFileSystemWatcherPrivate::getPathFromID(int id) const
+{
+    QHash<int, QString>::const_iterator i = idToPath.find(id);
+    while (i != idToPath.constEnd() && i.key() == id) {
+        if ((i + 1) == idToPath.constEnd() || (i + 1).key() != id) {
+            return i.value();
+        }
+        ++i;
+    }
+    return QString();
+}
+
+void DFileSystemWatcherPrivate::onFileChanged(const QString &path, bool removed)
+{
+    Q_Q(DFileSystemWatcher);
+    if (!files.contains(path)) {
+        // the path was removed after a change was detected, but before we delivered the signal
+        return;
+    }
+    if (removed) {
+        files.removeAll(path);
+    }
+//    Q_EMIT q->fileChanged(path, DFileSystemWatcher::QPrivateSignal());
+}
+
+void DFileSystemWatcherPrivate::onDirectoryChanged(const QString &path, bool removed)
+{
+    Q_Q(DFileSystemWatcher);
+    if (!directories.contains(path)) {
+        // perhaps the path was removed after a change was detected, but before we delivered the signal
+        return;
+    }
+    if (removed) {
+        directories.removeAll(path);
+    }
+//    Q_EMIT q->directoryChanged(path, DFileSystemWatcher::QPrivateSignal());
+}
+
+/*!
+    \class DFileSystemWatcher
+    \inmodule QtCore
+    \brief The DFileSystemWatcher class provides an interface for monitoring files and directories for modifications.
+    \ingroup io
+    \since 4.2
+    \reentrant
+
+    DFileSystemWatcher monitors the file system for changes to files
+    and directories by watching a list of specified paths.
+
+    Call addPath() to watch a particular file or directory. Multiple
+    paths can be added using the addPaths() function. Existing paths can
+    be removed by using the removePath() and removePaths() functions.
+
+    DFileSystemWatcher examines each path added to it. Files that have
+    been added to the DFileSystemWatcher can be accessed using the
+    files() function, and directories using the directories() function.
+
+    The fileChanged() signal is emitted when a file has been modified,
+    renamed or removed from disk. Similarly, the directoryChanged()
+    signal is emitted when a directory or its contents is modified or
+    removed.  Note that DFileSystemWatcher stops monitoring files once
+    they have been renamed or removed from disk, and directories once
+    they have been removed from disk.
+
+    \note On systems running a Linux kernel without inotify support,
+    file systems that contain watched paths cannot be unmounted.
+
+    \note Windows CE does not support directory monitoring by
+    default as this depends on the file system driver installed.
+
+    \note The act of monitoring files and directories for
+    modifications consumes system resources. This implies there is a
+    limit to the number of files and directories your process can
+    monitor simultaneously. On all BSD variants, for
+    example, an open file descriptor is required for each monitored
+    file. Some system limits the number of open file descriptors to 256
+    by default. This means that addPath() and addPaths() will fail if
+    your process tries to add more than 256 files or directories to
+    the file system monitor. Also note that your process may have
+    other file descriptors open in addition to the ones for files
+    being monitored, and these other open descriptors also count in
+    the total. OS X uses a different backend and does not
+    suffer from this issue.
+
+
+    \sa QFile, QDir
+*/
+
+
+/*!
+    Constructs a new file system watcher object with the given \a parent.
+*/
+DFileSystemWatcher::DFileSystemWatcher(QObject *parent)
+    : QObject(parent)
+    , DObject()
+{
+    int fd = -1;
+#ifdef IN_CLOEXEC
+    fd = inotify_init1(IN_CLOEXEC);
+#endif
+    if (fd == -1) {
+        fd = inotify_init();
+    }
+
+    if (fd != -1)
+        d_d_ptr.reset(new DFileSystemWatcherPrivate(fd, this));
+}
+
+/*!
+    Constructs a new file system watcher object with the given \a parent
+    which monitors the specified \a paths list.
+*/
+DFileSystemWatcher::DFileSystemWatcher(const QStringList &paths, QObject *parent)
+    : DFileSystemWatcher(parent)
+{
+    addPaths(paths);
+}
+
+/*!
+    Destroys the file system watcher.
+*/
+DFileSystemWatcher::~DFileSystemWatcher()
+{ }
+
+/*!
+    Adds \a path to the file system watcher if \a path exists. The
+    path is not added if it does not exist, or if it is already being
+    monitored by the file system watcher.
+
+    If \a path specifies a directory, the directoryChanged() signal
+    will be emitted when \a path is modified or removed from disk;
+    otherwise the fileChanged() signal is emitted when \a path is
+    modified, renamed or removed.
+
+    If the watch was successful, true is returned.
+
+    Reasons for a watch failure are generally system-dependent, but
+    may include the resource not existing, access failures, or the
+    total watch count limit, if the platform has one.
+
+    \note There may be a system dependent limit to the number of
+    files and directories that can be monitored simultaneously.
+    If this limit is been reached, \a path will not be monitored,
+    and false is returned.
+
+    \sa addPaths(), removePath()
+*/
+bool DFileSystemWatcher::addPath(const QString &path)
+{
+    if (path.isEmpty()) {
+        qWarning("DFileSystemWatcher::addPath: path is empty");
+        return true;
+    }
+
+    QStringList paths = addPaths(QStringList(path));
+    return paths.isEmpty();
+}
+
+/*!
+    Adds each path in \a paths to the file system watcher. Paths are
+    not added if they not exist, or if they are already being
+    monitored by the file system watcher.
+
+    If a path specifies a directory, the directoryChanged() signal
+    will be emitted when the path is modified or removed from disk;
+    otherwise the fileChanged() signal is emitted when the path is
+    modified, renamed, or removed.
+
+    The return value is a list of paths that could not be watched.
+
+    Reasons for a watch failure are generally system-dependent, but
+    may include the resource not existing, access failures, or the
+    total watch count limit, if the platform has one.
+
+    \note There may be a system dependent limit to the number of
+    files and directories that can be monitored simultaneously.
+    If this limit has been reached, the excess \a paths will not
+    be monitored, and they will be added to the returned QStringList.
+
+    \sa addPath(), removePaths()
+*/
+QStringList DFileSystemWatcher::addPaths(const QStringList &paths)
+{
+    Q_D(DFileSystemWatcher);
+
+    QStringList p = paths;
+    QMutableListIterator<QString> it(p);
+
+    while (it.hasNext()) {
+        const QString &path = it.next();
+        if (path.isEmpty())
+            it.remove();
+    }
+
+    if (p.isEmpty()) {
+        qWarning("DFileSystemWatcher::addPaths: list is empty");
+        return QStringList();
+    }
+
+    if (d)
+        p = d->addPaths(p, &d->files, &d->directories);
+
+    return p;
+}
+
+/*!
+    Removes the specified \a path from the file system watcher.
+
+    If the watch is successfully removed, true is returned.
+
+    Reasons for watch removal failing are generally system-dependent,
+    but may be due to the path having already been deleted, for example.
+
+    \sa removePaths(), addPath()
+*/
+bool DFileSystemWatcher::removePath(const QString &path)
+{
+    if (path.isEmpty()) {
+        qWarning("DFileSystemWatcher::removePath: path is empty");
+        return true;
+    }
+
+    QStringList paths = removePaths(QStringList(path));
+    return paths.isEmpty();
+}
+
+/*!
+    Removes the specified \a paths from the file system watcher.
+
+    The return value is a list of paths which were not able to be
+    unwatched successfully.
+
+    Reasons for watch removal failing are generally system-dependent,
+    but may be due to the path having already been deleted, for example.
+
+    \sa removePath(), addPaths()
+*/
+QStringList DFileSystemWatcher::removePaths(const QStringList &paths)
+{
+    Q_D(DFileSystemWatcher);
+
+    QStringList p = paths;
+    QMutableListIterator<QString> it(p);
+
+    while (it.hasNext()) {
+        const QString &path = it.next();
+        if (path.isEmpty())
+            it.remove();
+    }
+
+    if (p.isEmpty()) {
+        qWarning("DFileSystemWatcher::removePaths: list is empty");
+        return QStringList();
+    }
+
+    if (d)
+        p = d->removePaths(p, &d->files, &d->directories);
+
+    return p;
+}
+
+/*!
+    \fn void DFileSystemWatcher::fileChanged(const QString &path)
+
+    This signal is emitted when the file at the specified \a path is
+    modified, renamed or removed from disk.
+
+    \sa directoryChanged()
+*/
+
+/*!
+    \fn void DFileSystemWatcher::directoryChanged(const QString &path)
+
+    This signal is emitted when the directory at a specified \a path
+    is modified (e.g., when a file is added or deleted) or removed
+    from disk. Note that if there are several changes during a short
+    period of time, some of the changes might not Q_EMIT this signal.
+    However, the last change in the sequence of changes will always
+    generate this signal.
+
+    \sa fileChanged()
+*/
+
+/*!
+    \fn QStringList DFileSystemWatcher::directories() const
+
+    Returns a list of paths to directories that are being watched.
+
+    \sa files()
+*/
+
+/*!
+    \fn QStringList DFileSystemWatcher::files() const
+
+    Returns a list of paths to files that are being watched.
+
+    \sa directories()
+*/
+
+QStringList DFileSystemWatcher::directories() const
+{
+    Q_D(const DFileSystemWatcher);
+
+    if (!d)
+        return QStringList();
+
+    return d->directories;
+}
+
+QStringList DFileSystemWatcher::files() const
+{
+    Q_D(const DFileSystemWatcher);
+
+    if (!d)
+        return QStringList();
+
+    return d->files;
+}
+
+DCORE_END_NAMESPACE
+
+#include "moc_dfilesystemwatcher.cpp"
diff --git a/src/filesystem/dfilesystemwatcher_win.cpp b/src/filesystem/dfilesystemwatcher_win.cpp
new file mode 100644 (file)
index 0000000..69bd597
--- /dev/null
@@ -0,0 +1,240 @@
+/**
+ * Copyright (C) 2017 Deepin Technology Co., Ltd.
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ **/
+
+#include "dfilesystemwatcher.h"
+#include "private/dfilesystemwatcher_win_p.h"
+
+DCORE_BEGIN_NAMESPACE
+
+DFileSystemWatcherPrivate::DFileSystemWatcherPrivate(int fd, DFileSystemWatcher *qq)
+    : DObjectPrivate(qq)
+{
+
+}
+
+DFileSystemWatcherPrivate::~DFileSystemWatcherPrivate()
+{
+
+}
+
+/*!
+    \class DFileSystemWatcher
+    \inmodule QtCore
+    \brief The DFileSystemWatcher class provides an interface for monitoring files and directories for modifications.
+    \ingroup io
+    \since 4.2
+    \reentrant
+
+    DFileSystemWatcher monitors the file system for changes to files
+    and directories by watching a list of specified paths.
+
+    Call addPath() to watch a particular file or directory. Multiple
+    paths can be added using the addPaths() function. Existing paths can
+    be removed by using the removePath() and removePaths() functions.
+
+    DFileSystemWatcher examines each path added to it. Files that have
+    been added to the DFileSystemWatcher can be accessed using the
+    files() function, and directories using the directories() function.
+
+    The fileChanged() signal is emitted when a file has been modified,
+    renamed or removed from disk. Similarly, the directoryChanged()
+    signal is emitted when a directory or its contents is modified or
+    removed.  Note that DFileSystemWatcher stops monitoring files once
+    they have been renamed or removed from disk, and directories once
+    they have been removed from disk.
+
+    \note On systems running a Linux kernel without inotify support,
+    file systems that contain watched paths cannot be unmounted.
+
+    \note Windows CE does not support directory monitoring by
+    default as this depends on the file system driver installed.
+
+    \note The act of monitoring files and directories for
+    modifications consumes system resources. This implies there is a
+    limit to the number of files and directories your process can
+    monitor simultaneously. On all BSD variants, for
+    example, an open file descriptor is required for each monitored
+    file. Some system limits the number of open file descriptors to 256
+    by default. This means that addPath() and addPaths() will fail if
+    your process tries to add more than 256 files or directories to
+    the file system monitor. Also note that your process may have
+    other file descriptors open in addition to the ones for files
+    being monitored, and these other open descriptors also count in
+    the total. OS X uses a different backend and does not
+    suffer from this issue.
+
+
+    \sa QFile, QDir
+*/
+
+
+/*!
+    Constructs a new file system watcher object with the given \a parent.
+*/
+DFileSystemWatcher::DFileSystemWatcher(QObject *parent)
+    : QObject(parent)
+    , DObject()
+{
+
+}
+
+/*!
+    Constructs a new file system watcher object with the given \a parent
+    which monitors the specified \a paths list.
+*/
+DFileSystemWatcher::DFileSystemWatcher(const QStringList &paths, QObject *parent)
+    : DFileSystemWatcher(parent)
+{
+    addPaths(paths);
+}
+
+/*!
+    Destroys the file system watcher.
+*/
+DFileSystemWatcher::~DFileSystemWatcher()
+{ }
+
+/*!
+    Adds \a path to the file system watcher if \a path exists. The
+    path is not added if it does not exist, or if it is already being
+    monitored by the file system watcher.
+
+    If \a path specifies a directory, the directoryChanged() signal
+    will be emitted when \a path is modified or removed from disk;
+    otherwise the fileChanged() signal is emitted when \a path is
+    modified, renamed or removed.
+
+    If the watch was successful, true is returned.
+
+    Reasons for a watch failure are generally system-dependent, but
+    may include the resource not existing, access failures, or the
+    total watch count limit, if the platform has one.
+
+    \note There may be a system dependent limit to the number of
+    files and directories that can be monitored simultaneously.
+    If this limit is been reached, \a path will not be monitored,
+    and false is returned.
+
+    \sa addPaths(), removePath()
+*/
+bool DFileSystemWatcher::addPath(const QString &path)
+{
+    return false;
+}
+
+/*!
+    Adds each path in \a paths to the file system watcher. Paths are
+    not added if they not exist, or if they are already being
+    monitored by the file system watcher.
+
+    If a path specifies a directory, the directoryChanged() signal
+    will be emitted when the path is modified or removed from disk;
+    otherwise the fileChanged() signal is emitted when the path is
+    modified, renamed, or removed.
+
+    The return value is a list of paths that could not be watched.
+
+    Reasons for a watch failure are generally system-dependent, but
+    may include the resource not existing, access failures, or the
+    total watch count limit, if the platform has one.
+
+    \note There may be a system dependent limit to the number of
+    files and directories that can be monitored simultaneously.
+    If this limit has been reached, the excess \a paths will not
+    be monitored, and they will be added to the returned QStringList.
+
+    \sa addPath(), removePaths()
+*/
+QStringList DFileSystemWatcher::addPaths(const QStringList &paths)
+{
+    return QStringList();
+}
+
+/*!
+    Removes the specified \a path from the file system watcher.
+
+    If the watch is successfully removed, true is returned.
+
+    Reasons for watch removal failing are generally system-dependent,
+    but may be due to the path having already been deleted, for example.
+
+    \sa removePaths(), addPath()
+*/
+bool DFileSystemWatcher::removePath(const QString &path)
+{
+    return false;
+}
+
+/*!
+    Removes the specified \a paths from the file system watcher.
+
+    The return value is a list of paths which were not able to be
+    unwatched successfully.
+
+    Reasons for watch removal failing are generally system-dependent,
+    but may be due to the path having already been deleted, for example.
+
+    \sa removePath(), addPaths()
+*/
+QStringList DFileSystemWatcher::removePaths(const QStringList &paths)
+{
+    return QStringList();
+}
+
+/*!
+    \fn void DFileSystemWatcher::fileChanged(const QString &path)
+
+    This signal is emitted when the file at the specified \a path is
+    modified, renamed or removed from disk.
+
+    \sa directoryChanged()
+*/
+
+/*!
+    \fn void DFileSystemWatcher::directoryChanged(const QString &path)
+
+    This signal is emitted when the directory at a specified \a path
+    is modified (e.g., when a file is added or deleted) or removed
+    from disk. Note that if there are several changes during a short
+    period of time, some of the changes might not Q_EMIT this signal.
+    However, the last change in the sequence of changes will always
+    generate this signal.
+
+    \sa fileChanged()
+*/
+
+/*!
+    \fn QStringList DFileSystemWatcher::directories() const
+
+    Returns a list of paths to directories that are being watched.
+
+    \sa files()
+*/
+
+/*!
+    \fn QStringList DFileSystemWatcher::files() const
+
+    Returns a list of paths to files that are being watched.
+
+    \sa directories()
+*/
+
+QStringList DFileSystemWatcher::directories() const
+{
+    return QStringList();
+}
+
+QStringList DFileSystemWatcher::files() const
+{
+    return QStringList();
+}
+
+DCORE_END_NAMESPACE
+
+#include "moc_dfilesystemwatcher.cpp"
diff --git a/src/filesystem/dfilewatcher.cpp b/src/filesystem/dfilewatcher.cpp
new file mode 100644 (file)
index 0000000..1e47872
--- /dev/null
@@ -0,0 +1,277 @@
+/**
+ * Copyright (C) 2017 Deepin Technology Co., Ltd.
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ **/
+
+#include "dfilewatcher.h"
+#include "private/dbasefilewatcher_p.h"
+
+#include "dfilesystemwatcher.h"
+
+#include <QDir>
+#include <QDebug>
+
+DCORE_BEGIN_NAMESPACE
+
+static QString joinFilePath(const QString &path, const QString &name)
+{
+    if (path.endsWith(QDir::separator()))
+        return path + name;
+
+    return path + QDir::separator() + name;
+}
+
+class DFileWatcherPrivate : DBaseFileWatcherPrivate
+{
+public:
+    DFileWatcherPrivate(DFileWatcher *qq)
+        : DBaseFileWatcherPrivate(qq) {}
+
+    bool start() Q_DECL_OVERRIDE;
+    bool stop() Q_DECL_OVERRIDE;
+
+    void _q_handleFileDeleted(const QString &path, const QString &parentPath);
+    void _q_handleFileAttributeChanged(const QString &path, const QString &parentPath);
+    void _q_handleFileMoved(const QString &from, const QString &fromParent, const QString &to, const QString &toParent);
+    void _q_handleFileCreated(const QString &path, const QString &parentPath);
+    void _q_handleFileModified(const QString &path, const QString &parentPath);
+    void _q_handleFileClose(const QString &path, const QString &parentPath);
+
+    static QString formatPath(const QString &path);
+
+    QString path;
+    QStringList watchFileList;
+
+    static QMap<QString, int> filePathToWatcherCount;
+
+    Q_DECLARE_PUBLIC(DFileWatcher)
+};
+
+QMap<QString, int> DFileWatcherPrivate::filePathToWatcherCount;
+Q_GLOBAL_STATIC(DFileSystemWatcher, watcher_file_private)
+
+QStringList parentPathList(const QString &path)
+{
+    QStringList list;
+    QDir dir(path);
+
+    list << path;
+
+    while (dir.cdUp()) {
+        list << dir.absolutePath();
+    }
+
+    return list;
+}
+
+bool DFileWatcherPrivate::start()
+{
+    Q_Q(DFileWatcher);
+
+    started = true;
+
+    Q_FOREACH (const QString &path, parentPathList(this->path)) {
+        if (watchFileList.contains(path))
+            continue;
+
+        if (filePathToWatcherCount.value(path, -1) <= 0) {
+            if (!watcher_file_private->addPath(path)) {
+                qWarning() << Q_FUNC_INFO << "start watch failed, file path =" << path;
+                q->stopWatcher();
+                started = false;
+                return false;
+            }
+        }
+
+        watchFileList << path;
+        filePathToWatcherCount[path] = filePathToWatcherCount.value(path, 0) + 1;
+    }
+
+    q->connect(watcher_file_private, &DFileSystemWatcher::fileDeleted,
+               q, &DFileWatcher::onFileDeleted);
+    q->connect(watcher_file_private, &DFileSystemWatcher::fileAttributeChanged,
+               q, &DFileWatcher::onFileAttributeChanged);
+    q->connect(watcher_file_private, &DFileSystemWatcher::fileMoved,
+               q, &DFileWatcher::onFileMoved);
+    q->connect(watcher_file_private, &DFileSystemWatcher::fileCreated,
+               q, &DFileWatcher::onFileCreated);
+    q->connect(watcher_file_private, &DFileSystemWatcher::fileModified,
+               q, &DFileWatcher::onFileModified);
+    q->connect(watcher_file_private, &DFileSystemWatcher::fileClosed,
+               q, &DFileWatcher::onFileClosed);
+
+    return true;
+}
+
+bool DFileWatcherPrivate::stop()
+{
+    Q_Q(DFileWatcher);
+
+    q->disconnect(watcher_file_private, 0, q, 0);
+
+    bool ok = true;
+
+    Q_FOREACH (const QString &path, watchFileList) {
+        int count = filePathToWatcherCount.value(path, 0);
+
+        --count;
+
+        if (count <= 0) {
+            filePathToWatcherCount.remove(path);
+            watchFileList.removeOne(path);
+            ok = ok && watcher_file_private->removePath(path);
+        } else {
+            filePathToWatcherCount[path] = count;
+        }
+    }
+
+    return ok;
+}
+
+void DFileWatcherPrivate::_q_handleFileDeleted(const QString &path, const QString &parentPath)
+{
+    if (path != this->path && parentPath != this->path)
+        return;
+
+    Q_Q(DFileWatcher);
+
+    Q_EMIT q->fileDeleted(QUrl::fromLocalFile(path));
+}
+
+void DFileWatcherPrivate::_q_handleFileAttributeChanged(const QString &path, const QString &parentPath)
+{
+    if (path != this->path && parentPath != this->path)
+        return;
+
+    Q_Q(DFileWatcher);
+
+    Q_EMIT q->fileAttributeChanged(QUrl::fromLocalFile(path));
+}
+
+void DFileWatcherPrivate::_q_handleFileMoved(const QString &from, const QString &fromParent, const QString &to, const QString &toParent)
+{
+    Q_Q(DFileWatcher);
+
+    if ((fromParent == this->path && toParent == this->path) || from == this->path) {
+        Q_EMIT q->fileMoved(QUrl::fromLocalFile(from), QUrl::fromLocalFile(to));
+    } else if (fromParent == this->path) {
+        Q_EMIT q->fileDeleted(QUrl::fromLocalFile(from));
+    } else if (watchFileList.contains(from)) {
+        Q_EMIT q->fileDeleted(url);
+    } else if (toParent == this->path) {
+        Q_EMIT q->subfileCreated(QUrl::fromLocalFile(to));
+    }
+}
+
+void DFileWatcherPrivate::_q_handleFileCreated(const QString &path, const QString &parentPath)
+{
+    if (path != this->path && parentPath != this->path)
+        return;
+
+    Q_Q(DFileWatcher);
+
+    Q_EMIT q->subfileCreated(QUrl::fromLocalFile(path));
+}
+
+void DFileWatcherPrivate::_q_handleFileModified(const QString &path, const QString &parentPath)
+{
+    if (path != this->path && parentPath != this->path)
+        return;
+
+    Q_Q(DFileWatcher);
+
+    Q_EMIT q->fileModified(QUrl::fromLocalFile(path));
+}
+
+void DFileWatcherPrivate::_q_handleFileClose(const QString &path, const QString &parentPath)
+{
+    if (path != this->path && parentPath != this->path)
+        return;
+
+    Q_Q(DFileWatcher);
+
+    Q_EMIT q->fileClosed(QUrl::fromLocalFile(path));
+}
+
+QString DFileWatcherPrivate::formatPath(const QString &path)
+{
+    QString p = QFileInfo(path).absoluteFilePath();
+
+    if (p.endsWith(QDir::separator()))
+        p.chop(1);
+
+    return p.isEmpty() ? path : p;
+}
+
+DFileWatcher::DFileWatcher(const QString &filePath, QObject *parent)
+    : DBaseFileWatcher(*new DFileWatcherPrivate(this), QUrl::fromLocalFile(filePath), parent)
+{
+    d_func()->path = DFileWatcherPrivate::formatPath(filePath);
+}
+
+void DFileWatcher::onFileDeleted(const QString &path, const QString &name)
+{
+    if (name.isEmpty())
+        d_func()->_q_handleFileDeleted(path, QString());
+    else
+        d_func()->_q_handleFileDeleted(joinFilePath(path, name), path);
+}
+
+void DFileWatcher::onFileAttributeChanged(const QString &path, const QString &name)
+{
+    if (name.isEmpty())
+        d_func()->_q_handleFileAttributeChanged(path, QString());
+    else
+        d_func()->_q_handleFileAttributeChanged(joinFilePath(path, name), path);
+}
+
+void DFileWatcher::onFileMoved(const QString &from, const QString &fname, const QString &to, const QString &tname)
+{
+    QString fromPath, fpPath;
+    QString toPath, tpPath;
+
+    if (fname.isEmpty()) {
+        fromPath = from;
+    } else {
+        fromPath = joinFilePath(from, fname);
+        fpPath = from;
+    }
+
+    if (tname.isEmpty()) {
+        toPath = to;
+    } else {
+        toPath = joinFilePath(to, tname);
+        tpPath = to;
+    }
+
+    d_func()->_q_handleFileMoved(fromPath, fpPath, toPath, tpPath);
+}
+
+void DFileWatcher::onFileCreated(const QString &path, const QString &name)
+{
+    d_func()->_q_handleFileCreated(joinFilePath(path, name), path);
+}
+
+void DFileWatcher::onFileModified(const QString &path, const QString &name)
+{
+    if (name.isEmpty())
+        d_func()->_q_handleFileModified(path, QString());
+    else
+        d_func()->_q_handleFileModified(joinFilePath(path, name), path);
+}
+
+void DFileWatcher::onFileClosed(const QString &path, const QString &name)
+{
+    if (name.isEmpty())
+        d_func()->_q_handleFileClose(path, QString());
+    else
+        d_func()->_q_handleFileClose(joinFilePath(path, name), path);
+}
+
+DCORE_END_NAMESPACE
+
+#include "moc_dfilewatcher.cpp"
diff --git a/src/filesystem/dfilewatcher.h b/src/filesystem/dfilewatcher.h
new file mode 100644 (file)
index 0000000..7dba2ec
--- /dev/null
@@ -0,0 +1,40 @@
+/**
+ * Copyright (C) 2017 Deepin Technology Co., Ltd.
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ **/
+
+#ifndef DFILEWATCHER_H
+#define DFILEWATCHER_H
+
+#include "dbasefilewatcher.h"
+
+DCORE_BEGIN_NAMESPACE
+
+class DFileWatcherPrivate;
+class DFileWatcher : public DBaseFileWatcher
+{
+    Q_OBJECT
+
+public:
+    explicit DFileWatcher(const QString &filePath, QObject *parent = 0);
+
+private Q_SLOTS:
+    void onFileDeleted(const QString &path, const QString &name);
+    void onFileAttributeChanged(const QString &path, const QString &name);
+    void onFileMoved(const QString &fromPath, const QString &fromName,
+                     const QString &toPath, const QString &toName);
+    void onFileCreated(const QString &path, const QString &name);
+    void onFileModified(const QString &path, const QString &name);
+    void onFileClosed(const QString &path, const QString &name);
+
+private:
+    D_DECLARE_PRIVATE(DFileWatcher)
+};
+
+DCORE_END_NAMESPACE
+
+#endif // DFILEWATCHER_H
diff --git a/src/filesystem/dfilewatchermanager.cpp b/src/filesystem/dfilewatchermanager.cpp
new file mode 100644 (file)
index 0000000..e5b60cf
--- /dev/null
@@ -0,0 +1,95 @@
+/**
+ * Copyright (C) 2017 Deepin Technology Co., Ltd.
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ **/
+
+#include "dfilewatchermanager.h"
+#include "dfilewatcher.h"
+#include "base/private/dobject_p.h"
+
+#include <QMap>
+#include <QUrl>
+
+DCORE_BEGIN_NAMESPACE
+
+class DFileWatcherManagerPrivate : public DObjectPrivate
+{
+public:
+    DFileWatcherManagerPrivate(DFileWatcherManager *qq);
+
+    QMap<QString, DFileWatcher *> watchersMap;
+
+    D_DECLARE_PUBLIC(DFileWatcherManager)
+};
+
+DFileWatcherManagerPrivate::DFileWatcherManagerPrivate(DFileWatcherManager *qq)
+    : DObjectPrivate(qq)
+{
+
+}
+
+DFileWatcherManager::DFileWatcherManager(QObject *parent)
+    : QObject(parent)
+    , DObject(*new DFileWatcherManagerPrivate(this))
+{
+
+}
+
+DFileWatcherManager::~DFileWatcherManager()
+{
+
+}
+
+DFileWatcher *DFileWatcherManager::add(const QString &filePath)
+{
+    Q_D(DFileWatcherManager);
+
+    DFileWatcher *watcher = d->watchersMap.value(filePath);
+
+    if (watcher) {
+        return watcher;
+    }
+
+    watcher = new DFileWatcher(filePath, this);
+
+    connect(watcher, &DFileWatcher::fileAttributeChanged, this, [this](const QUrl & url) {
+        Q_EMIT fileAttributeChanged(url.toLocalFile());
+    });
+    connect(watcher, &DFileWatcher::fileClosed, this, [this](const QUrl & url) {
+        Q_EMIT fileClosed(url.toLocalFile());
+    });
+    connect(watcher, &DFileWatcher::fileDeleted, this, [this](const QUrl & url) {
+        Q_EMIT fileDeleted(url.toLocalFile());
+    });
+    connect(watcher, &DFileWatcher::fileModified, this, [this](const QUrl & url) {
+        Q_EMIT fileModified(url.toLocalFile());
+    });
+    connect(watcher, &DFileWatcher::fileMoved, this, [this](const QUrl & fromUrl, const QUrl & toUrl) {
+        Q_EMIT fileMoved(fromUrl.toLocalFile(), toUrl.toLocalFile());
+    });
+    connect(watcher, &DFileWatcher::subfileCreated, this, [this](const QUrl & url) {
+        Q_EMIT subfileCreated(url.toLocalFile());
+    });
+
+    d->watchersMap[filePath] = watcher;
+    watcher->startWatcher();
+
+    return watcher;
+}
+
+void DFileWatcherManager::remove(const QString &filePath)
+{
+    Q_D(DFileWatcherManager);
+
+    DFileWatcher *watcher = d->watchersMap.take(filePath);
+
+    if (watcher) {
+        watcher->deleteLater();
+    }
+}
+
+DCORE_END_NAMESPACE
diff --git a/src/filesystem/dfilewatchermanager.h b/src/filesystem/dfilewatchermanager.h
new file mode 100644 (file)
index 0000000..f6e3e55
--- /dev/null
@@ -0,0 +1,51 @@
+/**
+ * Copyright (C) 2017 Deepin Technology Co., Ltd.
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ **/
+
+#ifndef DFILEWATCHERMANAGER_H
+#define DFILEWATCHERMANAGER_H
+
+#include "dtkcore_global.h"
+#include "base/dobject.h"
+
+#include <QObject>
+
+DCORE_BEGIN_NAMESPACE
+
+class DFileWatcher;
+
+class DFileWatcherManagerPrivate;
+class DFileWatcherManager : public QObject, public DObject
+{
+    Q_OBJECT
+
+public:
+    explicit DFileWatcherManager(QObject *parent = 0);
+    ~DFileWatcherManager();
+
+    DFileWatcher *add(const QString &filePath);
+    void remove(const QString &filePath);
+
+Q_SIGNALS:
+    void fileDeleted(const QString &filePath);
+    void fileAttributeChanged(const QString &filePath);
+    void fileMoved(const QString &fromFilePath, const QString &toFilePath);
+    void subfileCreated(const QString &filePath);
+    void fileModified(const QString &filePath);
+    void fileClosed(const QString &filePath);
+
+private:
+    QScopedPointer<DFileWatcherManagerPrivate> d_ptr;
+
+    D_DECLARE_PRIVATE(DFileWatcherManager)
+    Q_DISABLE_COPY(DFileWatcherManager)
+};
+
+DCORE_END_NAMESPACE
+
+#endif // DFILEWATCHERMANAGER_H
diff --git a/src/filesystem/dpathbuf.h b/src/filesystem/dpathbuf.h
new file mode 100644 (file)
index 0000000..5179247
--- /dev/null
@@ -0,0 +1,53 @@
+#pragma once
+
+#include <QDir>
+
+#include "dtkcore_global.h"
+
+DCORE_BEGIN_NAMESPACE
+
+class DPathBuf
+{
+public:
+    DPathBuf(const QString &path)
+    {
+        m_path = QDir(path).absolutePath();
+    }
+
+    DPathBuf operator/(const QString &p) const
+    {
+        return DPathBuf(m_path + "/" + p);
+    }
+
+    DPathBuf &operator/=(const QString &p)
+    {
+        return join(p);
+    }
+
+    DPathBuf operator/(const char *p) const
+    {
+        return operator /(QString(p));
+    }
+
+    DPathBuf &operator/=(const char *p)
+    {
+        return operator /=(QString(p));
+    }
+
+    DPathBuf &join(const QString &p)
+    {
+        m_path += "/" + p;
+        m_path = QDir(m_path).absolutePath();
+        return *this;
+    }
+
+    QString toString() const
+    {
+        return QDir::toNativeSeparators(m_path);
+    }
+
+private:
+    QString m_path;
+};
+
+DCORE_END_NAMESPACE
diff --git a/src/filesystem/filesystem.pri b/src/filesystem/filesystem.pri
new file mode 100644 (file)
index 0000000..01ff7b5
--- /dev/null
@@ -0,0 +1,37 @@
+include($$PWD/private/private.pri)
+
+HEADERS += \
+    $$PWD/dbasefilewatcher.h \
+    $$PWD/dfilesystemwatcher.h \
+    $$PWD/dfilewatcher.h \
+    $$PWD/dfilewatchermanager.h \
+    $$PWD/dpathbuf.h
+
+SOURCES += \
+    $$PWD/dbasefilewatcher.cpp \
+    $$PWD/dfilewatcher.cpp \
+    $$PWD/dfilewatchermanager.cpp
+
+
+linux {
+    CONFIG += link_pkgconfig
+    PKGCONFIG += gsettings-qt
+
+    SOURCES += \
+        $$PWD/dfilesystemwatcher_linux.cpp
+} else:win* {
+    SOURCES += \
+        $$PWD/dfilesystemwatcher_win.cpp
+} else:mac* {
+    SOURCES += \
+        $$PWD/dfilesystemwatcher_win.cpp
+}
+
+includes.files += $$PWD/*.h
+includes.files += $$PWD/*.cpp
+includes.files += \
+    $$PWD/DFileWatcher \
+    $$PWD/DBaseFileWatcher \
+    $$PWD/DFileSystemWatcher \
+    $$PWD/DFileWatcherManager \
+    $$PWD/DPathBuf
diff --git a/src/filesystem/private/dbasefilewatcher_p.h b/src/filesystem/private/dbasefilewatcher_p.h
new file mode 100644 (file)
index 0000000..9304e15
--- /dev/null
@@ -0,0 +1,37 @@
+/**
+ * Copyright (C) 2017 Deepin Technology Co., Ltd.
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ **/
+
+#ifndef DBASEFILEWATCHER_P_H
+#define DBASEFILEWATCHER_P_H
+
+#include "base/private/dobject_p.h"
+
+#include <QUrl>
+
+DCORE_BEGIN_NAMESPACE
+
+class DBaseFileWatcher;
+class DBaseFileWatcherPrivate : public DObjectPrivate
+{
+public:
+    DBaseFileWatcherPrivate(DBaseFileWatcher *qq);
+
+    virtual bool start() = 0;
+    virtual bool stop() = 0;
+
+    QUrl url;
+    bool started = false;
+    static QList<DBaseFileWatcher *> watcherList;
+
+    D_DECLARE_PUBLIC(DBaseFileWatcher)
+};
+
+DCORE_END_NAMESPACE
+
+#endif // DBASEFILEWATCHER_P_H
diff --git a/src/filesystem/private/dfilesystemwatcher_linux_p.h b/src/filesystem/private/dfilesystemwatcher_linux_p.h
new file mode 100644 (file)
index 0000000..ceb409b
--- /dev/null
@@ -0,0 +1,50 @@
+/**
+ * Copyright (C) 2017 Deepin Technology Co., Ltd.
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ **/
+
+#ifndef DFILESYSTEMWATCHER_P_H
+#define DFILESYSTEMWATCHER_P_H
+
+#include "base/private/dobject_p.h"
+
+#include <QSocketNotifier>
+#include <QHash>
+#include <QMap>
+
+DCORE_BEGIN_NAMESPACE
+
+class DFileSystemWatcher;
+class DFileSystemWatcherPrivate : public DObjectPrivate
+{
+    Q_DECLARE_PUBLIC(DFileSystemWatcher)
+
+public:
+    DFileSystemWatcherPrivate(int fd, DFileSystemWatcher *qq);
+    ~DFileSystemWatcherPrivate();
+
+    QStringList addPaths(const QStringList &paths, QStringList *files, QStringList *directories);
+    QStringList removePaths(const QStringList &paths, QStringList *files, QStringList *directories);
+
+    QStringList files, directories;
+    int inotifyFd;
+    QHash<QString, int> pathToID;
+    QMultiHash<int, QString> idToPath;
+    QSocketNotifier notifier;
+
+    // private slots
+    void _q_readFromInotify();
+
+private:
+    QString getPathFromID(int id) const;
+    void onFileChanged(const QString &path, bool removed);
+    void onDirectoryChanged(const QString &path, bool removed);
+};
+
+DCORE_END_NAMESPACE
+
+#endif // DFILESYSTEMWATCHER_P_H
diff --git a/src/filesystem/private/dfilesystemwatcher_win_p.h b/src/filesystem/private/dfilesystemwatcher_win_p.h
new file mode 100644 (file)
index 0000000..52d8af5
--- /dev/null
@@ -0,0 +1,36 @@
+/**
+ * Copyright (C) 2017 Deepin Technology Co., Ltd.
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ **/
+#ifndef DFILESYSTEMWATCHER_WIN_P_H
+#define DFILESYSTEMWATCHER_WIN_P_H
+
+#include "base/private/dobject_p.h"
+
+DCORE_BEGIN_NAMESPACE
+
+class DFileSystemWatcher;
+class DFileSystemWatcherPrivate : public DObjectPrivate
+{
+    Q_DECLARE_PUBLIC(DFileSystemWatcher)
+
+public:
+    DFileSystemWatcherPrivate(int fd, DFileSystemWatcher *qq);
+    ~DFileSystemWatcherPrivate();
+
+    // private slots
+    void _q_readFromInotify();
+};
+
+void DFileSystemWatcherPrivate::_q_readFromInotify()
+{
+
+}
+
+DCORE_END_NAMESPACE
+
+#endif // DFILESYSTEMWATCHER_WIN_P_H
diff --git a/src/filesystem/private/private.pri b/src/filesystem/private/private.pri
new file mode 100644 (file)
index 0000000..b31875f
--- /dev/null
@@ -0,0 +1,10 @@
+HEADERS += \
+    $$PWD/dbasefilewatcher_p.h
+
+linux {
+    HEADERS += \
+        $$PWD/dfilesystemwatcher_linux_p.h
+} else:win* {
+    HEADERS += \
+        $$PWD/dfilesystemwatcher_win_p.h
+}
index 17ff73bafb39b0ad9e9033cc4e01bb1f7d5463d6..f17f599f068c75eb924389237c88b2fcb219e888 100644 (file)
@@ -1,18 +1,7 @@
 includes.path = $${DTK_INCLUDEPATH}/DCore
-includes.files += $$PWD/base/*.h
-includes.files += $$PWD/base/*.cpp
-includes.files += \
-    $$PWD/base/DObject \
-    $$PWD/base/DSingleton
-
-includes.files += $$PWD/log/*.h
-includes.files += $$PWD/log/*.cpp
-includes.files += \
-    $$PWD/log/DLog
 
 INSTALLS += includes target
 
-
 QMAKE_PKGCONFIG_LIBDIR = $$target.path
 QMAKE_PKGCONFIG_VERSION = $$VERSION
 QMAKE_PKGCONFIG_DESTDIR = pkgconfig
index 50b18972d46cb6479edbf277b9e9e07bdbc72642..b263dfc209f79d4451cda4e39d31d6cdd4bd844d 100644 (file)
@@ -1 +1,6 @@
 include(cutelogger.pri)
+
+includes.files += $$PWD/*.h
+includes.files += $$PWD/*.cpp
+includes.files += \
+    $$PWD/DLog
index 64425600ba04e5121e556fc22a46698e01efafe1..9aa7dd1da0ff47882a9029f02ea3a9f06f0c2913 100644 (file)
@@ -1,7 +1,7 @@
 include($$PWD/lib.pri)
-include($$PWD/install.pri)
 
 QT -= gui
+QT += dbus
 
 TARGET = dtkcore
 
@@ -13,5 +13,9 @@ INCLUDEPATH += $$PWD
 HEADERS += \
     $$PWD/dtkcore_global.h \
 
-include($$PWD/base/dbase.pri)
+include($$PWD/base/base.pri)
 include($$PWD/log/log.pri)
+include($$PWD/filesystem/filesystem.pri)
+
+include($$PWD/install.pri)
+