forked from openkylin/ukui-search
优化inotify信号处理,解决若干由于信号处理不当导致的索引不正确问题
This commit is contained in:
parent
04a9cb487e
commit
9b835b9686
|
@ -37,7 +37,6 @@ public:
|
||||||
FileSystemWatcherPrivate(FileSystemWatcher *parent);
|
FileSystemWatcherPrivate(FileSystemWatcher *parent);
|
||||||
~FileSystemWatcherPrivate();
|
~FileSystemWatcherPrivate();
|
||||||
|
|
||||||
void addWatch(const QStringList &pathList);
|
|
||||||
void addWatchWithBlackList(const QStringList &pathList, const QStringList &blackList);
|
void addWatchWithBlackList(const QStringList &pathList, const QStringList &blackList);
|
||||||
QStringList removeWatch(const QString &path);
|
QStringList removeWatch(const QString &path);
|
||||||
QString removeWatch(int wd);
|
QString removeWatch(int wd);
|
||||||
|
@ -45,7 +44,6 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void init();
|
void init();
|
||||||
void traverse(QStringList pathList);
|
|
||||||
void addWatch(const QString &path);
|
void addWatch(const QString &path);
|
||||||
|
|
||||||
FileSystemWatcher::WatchEvents m_watchEvents;
|
FileSystemWatcher::WatchEvents m_watchEvents;
|
||||||
|
@ -55,6 +53,7 @@ private:
|
||||||
QSocketNotifier* m_notifier = nullptr;
|
QSocketNotifier* m_notifier = nullptr;
|
||||||
// wd -> url
|
// wd -> url
|
||||||
QHash<int, QString> m_watchPathHash;
|
QHash<int, QString> m_watchPathHash;
|
||||||
|
QStringList m_watchedRootPaths;
|
||||||
FileSystemWatcher *q = nullptr;
|
FileSystemWatcher *q = nullptr;
|
||||||
bool m_recursive = true;
|
bool m_recursive = true;
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,6 @@
|
||||||
#include "ukui-search-qdbus.h"
|
#include "ukui-search-qdbus.h"
|
||||||
#include "file-utils.h"
|
#include "file-utils.h"
|
||||||
using namespace UkuiSearch;
|
using namespace UkuiSearch;
|
||||||
|
|
||||||
FileSystemWatcherPrivate::FileSystemWatcherPrivate(FileSystemWatcher *parent) : q(parent)
|
FileSystemWatcherPrivate::FileSystemWatcherPrivate(FileSystemWatcher *parent) : q(parent)
|
||||||
{
|
{
|
||||||
qDebug() << "setInotifyMaxUserWatches start";
|
qDebug() << "setInotifyMaxUserWatches start";
|
||||||
|
@ -50,31 +49,6 @@ FileSystemWatcherPrivate::~FileSystemWatcherPrivate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileSystemWatcherPrivate::traverse(QStringList pathList)
|
|
||||||
{
|
|
||||||
QQueue<QString> queue;
|
|
||||||
for(QString path : pathList) {
|
|
||||||
addWatch(path);
|
|
||||||
queue.enqueue(path);
|
|
||||||
}
|
|
||||||
if(!m_recursive) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
QFileInfoList list;
|
|
||||||
QDir dir;
|
|
||||||
dir.setFilter(QDir::Dirs | QDir::NoDotAndDotDot);
|
|
||||||
while(!queue.empty()) {
|
|
||||||
dir.setPath(queue.dequeue());
|
|
||||||
list = dir.entryInfoList();
|
|
||||||
for(auto i : list) {
|
|
||||||
if(!(i.isSymLink())) {
|
|
||||||
queue.enqueue(i.absoluteFilePath());
|
|
||||||
addWatch(i.absoluteFilePath());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileSystemWatcherPrivate::addWatch(const QString &path)
|
void FileSystemWatcherPrivate::addWatch(const QString &path)
|
||||||
{
|
{
|
||||||
int wd = inotify_add_watch(m_inotifyFd, path.toStdString().c_str(), m_watchEvents | m_watchFlags);
|
int wd = inotify_add_watch(m_inotifyFd, path.toStdString().c_str(), m_watchEvents | m_watchFlags);
|
||||||
|
@ -88,11 +62,6 @@ void FileSystemWatcherPrivate::addWatch(const QString &path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileSystemWatcherPrivate::addWatch(const QStringList &pathList)
|
|
||||||
{
|
|
||||||
traverse(pathList);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileSystemWatcherPrivate::addWatchWithBlackList(const QStringList &pathList, const QStringList &blackList)
|
void FileSystemWatcherPrivate::addWatchWithBlackList(const QStringList &pathList, const QStringList &blackList)
|
||||||
{
|
{
|
||||||
QQueue<QString> bfs;
|
QQueue<QString> bfs;
|
||||||
|
@ -105,10 +74,15 @@ void FileSystemWatcherPrivate::addWatchWithBlackList(const QStringList &pathList
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for(QString path : tmpPathList) {
|
for(QString path : tmpPathList) {
|
||||||
addWatch(path);
|
if(!m_watchedRootPaths.contains(path)) {
|
||||||
bfs.enqueue(path);
|
addWatch(path);
|
||||||
|
bfs.enqueue(path);
|
||||||
|
m_watchedRootPaths.append(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!m_recursive) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QFileInfoList list;
|
QFileInfoList list;
|
||||||
QDir dir;
|
QDir dir;
|
||||||
QStringList tmpList = blackList;
|
QStringList tmpList = blackList;
|
||||||
|
@ -219,12 +193,12 @@ FileSystemWatcher::~FileSystemWatcher()
|
||||||
|
|
||||||
void FileSystemWatcher::addWatch(const QStringList &pathList)
|
void FileSystemWatcher::addWatch(const QStringList &pathList)
|
||||||
{
|
{
|
||||||
d->addWatch(pathList);
|
d->addWatchWithBlackList(pathList, QStringList());
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileSystemWatcher::addWatch(const QString &path)
|
void FileSystemWatcher::addWatch(const QString &path)
|
||||||
{
|
{
|
||||||
d->addWatch(QStringList(path));
|
d->addWatchWithBlackList(QStringList(path), QStringList());
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileSystemWatcher::addWatchWithBlackList(const QStringList &pathList, const QStringList &blackList)
|
void FileSystemWatcher::addWatchWithBlackList(const QStringList &pathList, const QStringList &blackList)
|
||||||
|
@ -235,6 +209,11 @@ void FileSystemWatcher::addWatchWithBlackList(const QStringList &pathList, const
|
||||||
QStringList FileSystemWatcher::removeWatch(const QString &path)
|
QStringList FileSystemWatcher::removeWatch(const QString &path)
|
||||||
{
|
{
|
||||||
return d->removeWatch(path);
|
return d->removeWatch(path);
|
||||||
|
for(QString watchedPath : d->m_watchedRootPaths) {
|
||||||
|
if(FileUtils::isOrUnder(watchedPath, path)) {
|
||||||
|
d->m_watchedRootPaths.removeAll(watchedPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileSystemWatcher::clearAll()
|
void FileSystemWatcher::clearAll()
|
||||||
|
@ -274,57 +253,92 @@ void FileSystemWatcher::eventProcess(int socket)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// qDebug() << "event mask:" << event->mask;
|
// qDebug() << "event mask:" << event->mask
|
||||||
|
// << "isDir:" << (event->mask & IN_ISDIR)
|
||||||
|
// << "event->wd:" << event->wd
|
||||||
|
// << "event->name" << QString(QByteArray::fromRawData(event->name, qstrnlen(event->name, event->len)))
|
||||||
|
// << "event->len" << event->len
|
||||||
|
// << "event->cookie" << event->cookie
|
||||||
|
// << "path:" << d->m_watchPathHash.value(event->wd);
|
||||||
QString path;
|
QString path;
|
||||||
|
|
||||||
if (event->mask & (EventDeleteSelf | EventMoveSelf)) {
|
if (event->mask & (EventDeleteSelf | EventMoveSelf | EventUnmount)) {
|
||||||
path = d->m_watchPathHash.value(event->wd);
|
path = d->m_watchPathHash.value(event->wd);
|
||||||
|
if(path.isEmpty()) {
|
||||||
|
i += sizeof(struct inotify_event) + event->len;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
path = d->m_watchPathHash[event->wd] + '/' + event->name;
|
QByteArray name = QByteArray::fromRawData(event->name, qstrnlen(event->name, event->len));
|
||||||
|
path = d->m_watchPathHash[event->wd];
|
||||||
|
if(name.isEmpty() || path.isEmpty()) {
|
||||||
|
i += sizeof(struct inotify_event) + event->len;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
path.append("/").append(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(event->mask & EventCreate) {
|
if(event->mask & EventCreate) {
|
||||||
// qDebug() << path << "--EventCreate";
|
// qDebug() << path << "--EventCreate";
|
||||||
Q_EMIT created(path, event->mask & IN_ISDIR);
|
Q_EMIT created(path, event->mask & IN_ISDIR);
|
||||||
if(event->mask & IN_ISDIR && d->m_recursive) {
|
if(event->mask & IN_ISDIR) {
|
||||||
if(!QFileInfo(path).isSymLink()){
|
for(const QString &createdPath : traverse(path)) {
|
||||||
addWatch(QStringList(path));
|
Q_EMIT created(createdPath, QFileInfo(createdPath).isDir());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (event->mask & EventDeleteSelf) {
|
if (event->mask & EventDeleteSelf) {
|
||||||
// qDebug() << path << "--EventDeleteSelf";
|
// qDebug() << path << "--EventDeleteSelf";
|
||||||
if(event->mask & IN_ISDIR) {
|
if(d->m_watchedRootPaths.contains(path)) {
|
||||||
d->removeWatch(event->wd);
|
// qDebug() << "EventDeleteSelf send" << path;
|
||||||
|
//All folders under this one should be deleted.
|
||||||
|
for(const QString &removedPath : d->removeWatch(path)) {
|
||||||
|
Q_EMIT deleted(removedPath, true);
|
||||||
|
}
|
||||||
|
d->m_watchedRootPaths.removeAll(path);
|
||||||
}
|
}
|
||||||
Q_EMIT deleted(path, event->mask & IN_ISDIR);
|
|
||||||
}
|
}
|
||||||
if (event->mask & EventDelete) {
|
if (event->mask & EventDelete) {
|
||||||
// qDebug() << path << "--EventDelete";
|
// qDebug() << path << "--EventDelete";
|
||||||
// we watch all folders recursively. Thus, folder removing is reported in DeleteSelf.
|
if (event->mask & IN_ISDIR) {
|
||||||
if (!(event->mask & IN_ISDIR)) {
|
for(const QString &removedPath : d->removeWatch(path)) {
|
||||||
|
Q_EMIT deleted(removedPath, true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
Q_EMIT deleted(path, false);
|
Q_EMIT deleted(path, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (event->mask & EventModify) {
|
if (event->mask & EventModify) {
|
||||||
// qDebug() << path << "--EventModify";
|
// qDebug() << path << "--EventModify";
|
||||||
Q_EMIT modified(path);
|
if(!event->mask & IN_ISDIR) {
|
||||||
|
Q_EMIT modified(path);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (event->mask & EventMoveSelf) {
|
if (event->mask & EventMoveSelf) {
|
||||||
//Problematic if the parent is not watched, otherwise
|
// qDebug() << path << "--EventMoveSelf";
|
||||||
// handled by MoveFrom/MoveTo from the parent
|
if(d->m_watchedRootPaths.contains(path)) {
|
||||||
// qDebug() << path << "--EventMoveSelf";
|
for(const QString &removedPath : d->removeWatch(path)) {
|
||||||
|
Q_EMIT moved(removedPath, true);
|
||||||
|
}
|
||||||
|
d->m_watchedRootPaths.removeAll(path);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (event->mask & EventMoveFrom) {
|
if (event->mask & EventMoveFrom) {
|
||||||
// qDebug() << path << "--EventMoveFrom";
|
// qDebug() << path << "--EventMoveFrom";
|
||||||
Q_EMIT moved(path, event->mask & IN_ISDIR);
|
if (event->mask & IN_ISDIR) {
|
||||||
|
for(const QString &removedPath : d->removeWatch(path)) {
|
||||||
|
Q_EMIT moved(removedPath, true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Q_EMIT moved(path, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (event->mask & EventMoveTo) {
|
if (event->mask & EventMoveTo) {
|
||||||
// qDebug() << path << "--EventMoveTo";
|
// qDebug() << path << "--EventMoveTo";
|
||||||
Q_EMIT created(path, event->mask & IN_ISDIR);
|
Q_EMIT moveTo(path, event->mask & IN_ISDIR);
|
||||||
if (event->mask & IN_ISDIR && d->m_recursive) {
|
if (event->mask & IN_ISDIR) {
|
||||||
if(!QFileInfo(path).isSymLink()){
|
for(const QString &createdPath : traverse(path)) {
|
||||||
addWatch(QStringList(path));
|
Q_EMIT moveTo(createdPath, QFileInfo(createdPath).isDir());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -334,14 +348,12 @@ void FileSystemWatcher::eventProcess(int socket)
|
||||||
}
|
}
|
||||||
if (event->mask & EventUnmount) {
|
if (event->mask & EventUnmount) {
|
||||||
// qDebug() << path << "--EventUnmount";
|
// qDebug() << path << "--EventUnmount";
|
||||||
if (event->mask & IN_ISDIR) {
|
|
||||||
d->removeWatch(event->wd);
|
|
||||||
}
|
|
||||||
// This is present because a unmount event is sent by inotify after unmounting, by
|
// This is present because a unmount event is sent by inotify after unmounting, by
|
||||||
// which time the watches have already been removed.
|
// which time the watches have already been removed.
|
||||||
if (path != "/") {
|
if (path != "/") {
|
||||||
Q_EMIT unmounted(path, event->mask & IN_ISDIR);
|
Q_EMIT unmounted(path, event->mask & IN_ISDIR);
|
||||||
}
|
}
|
||||||
|
d->m_watchedRootPaths.removeAll(path);
|
||||||
}
|
}
|
||||||
if (event->mask & EventAttributeChange) {
|
if (event->mask & EventAttributeChange) {
|
||||||
// qDebug() << path << "--EventAttributeChange";
|
// qDebug() << path << "--EventAttributeChange";
|
||||||
|
@ -370,3 +382,30 @@ void FileSystemWatcher::eventProcess(int socket)
|
||||||
|
|
||||||
free(buf);
|
free(buf);
|
||||||
}
|
}
|
||||||
|
QStringList FileSystemWatcher::traverse(QString &path)
|
||||||
|
{
|
||||||
|
QStringList paths;
|
||||||
|
d->addWatch(path);
|
||||||
|
if(!d->m_recursive || QFileInfo(path).isSymLink()) {
|
||||||
|
return paths;
|
||||||
|
}
|
||||||
|
QQueue<QString> queue;
|
||||||
|
queue.enqueue(path);
|
||||||
|
|
||||||
|
QFileInfoList list;
|
||||||
|
QDir dir;
|
||||||
|
dir.setFilter(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot);
|
||||||
|
dir.setSorting(QDir::DirsFirst);
|
||||||
|
while(!queue.empty()) {
|
||||||
|
dir.setPath(queue.dequeue());
|
||||||
|
list = dir.entryInfoList();
|
||||||
|
for(auto i : list) {
|
||||||
|
if(i.isDir() && !(i.isSymLink())) {
|
||||||
|
queue.enqueue(i.absoluteFilePath());
|
||||||
|
d->addWatch(i.absoluteFilePath());
|
||||||
|
}
|
||||||
|
paths.append(i.absoluteFilePath());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return paths;
|
||||||
|
}
|
||||||
|
|
|
@ -153,8 +153,16 @@ Q_SIGNALS:
|
||||||
*/
|
*/
|
||||||
void unmounted(const QString& path, bool isDir);
|
void unmounted(const QString& path, bool isDir);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emitted if a file is moved to one of the watched folders.
|
||||||
|
* Note:if the new file moved here overwrited a file already existed, there will not be a 'deleted' signal.
|
||||||
|
*/
|
||||||
|
void moveTo(const QString& path, bool isDir);
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void eventProcess(int socket);
|
void eventProcess(int socket);
|
||||||
|
QStringList traverse(QString &path);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
FileSystemWatcherPrivate* d = nullptr;
|
FileSystemWatcherPrivate* d = nullptr;
|
||||||
|
|
|
@ -26,6 +26,7 @@ FileWatcher::FileWatcher(QObject *parent) : QObject(parent), m_config(FileIndexe
|
||||||
m_pendingFileQUeue = PendingFileQueue::getInstance();
|
m_pendingFileQUeue = PendingFileQueue::getInstance();
|
||||||
|
|
||||||
connect(m_watcher, &FileSystemWatcher::created, this, &FileWatcher::onFileCreated);
|
connect(m_watcher, &FileSystemWatcher::created, this, &FileWatcher::onFileCreated);
|
||||||
|
connect(m_watcher, &FileSystemWatcher::moveTo, this, &FileWatcher::onFileMoveTo);
|
||||||
connect(m_watcher, &FileSystemWatcher::modified, this, &FileWatcher::onFileModefied);
|
connect(m_watcher, &FileSystemWatcher::modified, this, &FileWatcher::onFileModefied);
|
||||||
connect(m_watcher, &FileSystemWatcher::deleted, this, &FileWatcher::onFileDeletedOrMoved);
|
connect(m_watcher, &FileSystemWatcher::deleted, this, &FileWatcher::onFileDeletedOrMoved);
|
||||||
connect(m_watcher, &FileSystemWatcher::moved, this, &FileWatcher::onFileDeletedOrMoved);
|
connect(m_watcher, &FileSystemWatcher::moved, this, &FileWatcher::onFileDeletedOrMoved);
|
||||||
|
@ -87,6 +88,14 @@ void FileWatcher::onFileCreated(const QString &path, bool isDir)
|
||||||
m_pendingFileQUeue->enqueue(file);
|
m_pendingFileQUeue->enqueue(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FileWatcher::onFileMoveTo(const QString &path, bool isDir)
|
||||||
|
{
|
||||||
|
PendingFile file(path);
|
||||||
|
file.setIsDir(isDir);
|
||||||
|
file.setMoveTo();
|
||||||
|
m_pendingFileQUeue->enqueue(file);
|
||||||
|
}
|
||||||
|
|
||||||
void FileWatcher::onFileModefied(const QString &path)
|
void FileWatcher::onFileModefied(const QString &path)
|
||||||
{
|
{
|
||||||
PendingFile file(path);
|
PendingFile file(path);
|
||||||
|
|
|
@ -62,6 +62,7 @@ Q_SIGNALS:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void onFileCreated(const QString& path, bool isDir);
|
void onFileCreated(const QString& path, bool isDir);
|
||||||
|
void onFileMoveTo(const QString& path, bool isDir);
|
||||||
void onFileModefied(const QString& path);
|
void onFileModefied(const QString& path);
|
||||||
void onFileDeletedOrMoved(const QString& path, bool isDir);
|
void onFileDeletedOrMoved(const QString& path, bool isDir);
|
||||||
FileSystemWatcher *m_watcher = nullptr;
|
FileSystemWatcher *m_watcher = nullptr;
|
||||||
|
|
|
@ -89,7 +89,7 @@ void IndexUpdater::UpdateIndex()
|
||||||
contentDb.removeDocument(file.path());
|
contentDb.removeDocument(file.path());
|
||||||
}
|
}
|
||||||
} else if(true == suffixMap[suffix] && !file.isDir()) {
|
} else if(true == suffixMap[suffix] && !file.isDir()) {
|
||||||
if(FileUtils::isEncrypedOrUnsupport(file.path(), suffix) && file.isModified()) {
|
if(FileUtils::isEncrypedOrUnsupport(file.path(), suffix) && (file.isModified() || file.isMoveTo())) {
|
||||||
contentDb.removeDocument(file.path());
|
contentDb.removeDocument(file.path());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -98,7 +98,8 @@ void IndexUpdater::UpdateIndex()
|
||||||
if(indexer.index()) {
|
if(indexer.index()) {
|
||||||
contentDb.addDocument(indexer.document());
|
contentDb.addDocument(indexer.document());
|
||||||
++size;
|
++size;
|
||||||
} else if(file.isModified()){
|
} else if(file.isModified() || file.isMoveTo()){
|
||||||
|
|
||||||
contentDb.removeDocument(file.path());
|
contentDb.removeDocument(file.path());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,8 @@ public:
|
||||||
bool isModified() { return m_modified; }
|
bool isModified() { return m_modified; }
|
||||||
|
|
||||||
void setCreated() { m_created = true; }
|
void setCreated() { m_created = true; }
|
||||||
|
void setMoveTo() { m_moveTo = true;}
|
||||||
|
bool isMoveTo() { return m_moveTo;}
|
||||||
|
|
||||||
void setDeleted() { m_deleted = true; }
|
void setDeleted() { m_deleted = true; }
|
||||||
|
|
||||||
|
@ -66,6 +68,7 @@ private:
|
||||||
QString m_path;
|
QString m_path;
|
||||||
|
|
||||||
bool m_created : 1;
|
bool m_created : 1;
|
||||||
|
bool m_moveTo : 1;
|
||||||
bool m_deleted : 1;
|
bool m_deleted : 1;
|
||||||
bool m_modified : 1;
|
bool m_modified : 1;
|
||||||
bool m_isDir : 1;
|
bool m_isDir : 1;
|
||||||
|
|
|
@ -11,7 +11,7 @@ FileSystemWatcherTest::FileSystemWatcherTest(QObject *parent) : QObject(parent)
|
||||||
|
|
||||||
void FileSystemWatcherTest::beginSignalTest()
|
void FileSystemWatcherTest::beginSignalTest()
|
||||||
{
|
{
|
||||||
m_watcher->addWatch("/home/zpf/图片/新建文夹");
|
m_watcher->addWatch("");
|
||||||
|
|
||||||
connect(m_watcher, &FileSystemWatcher::attributeChanged,
|
connect(m_watcher, &FileSystemWatcher::attributeChanged,
|
||||||
[](const QString& fileUrl) { qDebug() << "AttrbuteChanged:" << fileUrl; });
|
[](const QString& fileUrl) { qDebug() << "AttrbuteChanged:" << fileUrl; });
|
||||||
|
|
Loading…
Reference in New Issue