/* * Copyright (C) by Klaas Freitag * * 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; version 2 of the License. * * 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. */ // event masks #include "mirall/folderwatcher.h" #include "mirall/folder.h" #include #include #include #include #include #include #include #include #if defined(Q_OS_WIN) #include "mirall/folderwatcher_win.h" #elif defined(Q_OS_MAC) #include "mirall/folderwatcher_mac.h" #elif defined(Q_OS_UNIX) #include "mirall/folderwatcher_linux.h" #endif namespace Mirall { FolderWatcher::FolderWatcher(const QString &root, QObject *parent) : QObject(parent) { _d.reset(new FolderWatcherPrivate(this, root)); _timer.start(); } FolderWatcher::~FolderWatcher() { } void FolderWatcher::addIgnoreListFile( const QString& file ) { if( file.isEmpty() ) return; QFile infile( file ); if (!infile.open(QIODevice::ReadOnly | QIODevice::Text)) return; while (!infile.atEnd()) { QString line = QString::fromLocal8Bit( infile.readLine() ).trimmed(); if( !(line.startsWith( QLatin1Char('#') ) || line.isEmpty()) ) { _ignores.append(line); } } } QStringList FolderWatcher::ignores() const { return _ignores; } bool FolderWatcher::pathIsIgnored( const QString& path ) { if( path.isEmpty() ) return true; // Remember: here only directories are checked! // If that changes to files too at some day, remember to check // for the database name as well as the trailing slash rule for // dirs only. Best use csync_ignore than somehow. foreach (QString pattern, _ignores) { QRegExp regexp(pattern); regexp.setPatternSyntax(QRegExp::Wildcard); QFileInfo fInfo(path); if( fInfo.isHidden() ) { qDebug() << "* Discarded as is hidden!"; return true; } if(pattern.endsWith('/')) { // directory only pattern. But since only dirs here, we cut off the trailing dir. pattern.remove(pattern.length()-1, 1); // remove the last char. } // if the pattern contains / it needs to match the entire path if (pattern.contains('/') && regexp.exactMatch(path)) { qDebug() << "* Discarded by ignore pattern: " << path; return true; } QStringList components = path.split('/'); foreach (const QString& comp, components) { if(regexp.exactMatch(comp)) { qDebug() << "* Discarded by component ignore pattern " << comp; return true; } } } return false; } void FolderWatcher::changeDetected( const QString& path ) { QStringList paths(path); changeDetected(paths); } void FolderWatcher::changeDetected( const QStringList& paths ) { // qDebug() << Q_FUNC_INFO << paths; // TODO: this shortcut doesn't look very reliable: // - why is the timeout only 1 second? // - what if there are more than one file being updated frequently? // - why do we skip the file alltogether instead of e.g. reducing the upload frequency? // Check if the same path was reported within the last second. QSet pathsSet = paths.toSet(); if( pathsSet == _lastPaths && _timer.elapsed() < 1000 ) { // the same path was reported within the last second. Skip. return; } _lastPaths = pathsSet; _timer.restart(); QSet changedFolders; // ------- handle ignores: for (int i = 0; i < paths.size(); ++i) { QString path = paths[i]; if( pathIsIgnored(path) ) { continue; } QFileInfo fi(path); if (fi.isDir()) { changedFolders.insert(path); } else { changedFolders.insert(fi.dir().path()); } } if (changedFolders.isEmpty()) { return; } qDebug() << "detected changes in folders:" << changedFolders; foreach (const QString &path, changedFolders) { emit folderChanged(path); } } void FolderWatcher::addPath(const QString &path ) { _d->addPath(path); } void FolderWatcher::removePath(const QString &path ) { _d->removePath(path); } } // namespace Mirall