/* * dirwatch_unix.cpp - detect changes of directory content * Copyright (C) 2003 Justin Karneges * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include"dirwatch.h" #include #include #include #include #include #include class DWEvent : public QEvent { public: DWEvent(int _state) :QEvent(QEvent::User) { state = _state; } int state; }; class DWThread : public QThread { public: DWThread(QObject *_par, HANDLE _h) : QThread() { par = _par; h = _h; } void run() { while(WaitForSingleObject(h, INFINITE) == WAIT_OBJECT_0) { // send a changed event QThread::postEvent(par, new DWEvent(1)); if(!FindNextChangeNotification(h)) { // send an error event QThread::postEvent(par, new DWEvent(0)); return; } } } QObject *par; HANDLE h; }; class DWEntry : public QObject { Q_OBJECT public: static DWEntry * create(const QString &s) { HANDLE h = FindFirstChangeNotificationA(QFile::encodeName(QDir::convertSeparators(s)), TRUE, FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_SECURITY); if(h == INVALID_HANDLE_VALUE) return 0; DWEntry *e = new DWEntry; e->dir = s; e->h = h; e->thread = new DWThread(e, h); e->thread->start(); return e; } ~DWEntry() { if(thread) { FindCloseChangeNotification(h); thread->wait(); delete thread; } } signals: void changed(); protected: bool event(QEvent *e) { if(e->type() == QEvent::User) { DWEvent *de = (DWEvent *)e; // error? if(de->state == 0) { thread->wait(); delete thread; FindCloseChangeNotification(h); thread = 0; } else { dirty = true; changed(); } return true; } return false; } public: QString dir; QValueList idList; bool dirty; private: DWEntry() { dirty = false; } HANDLE h; DWThread *thread; }; class DirWatchPlatform::Private : public QObject { Q_OBJECT public: Private(DirWatchPlatform *_par) { par = _par; list.setAutoDelete(true); connect(&t, SIGNAL(timeout()), this, SLOT(slotNotify())); } ~Private() { list.clear(); } QTimer t; DirWatchPlatform *par; QPtrList list; int addItem(const QString &s) { DWEntry *e = findEntryByDir(s); if(!e) { e = DWEntry::create(s); if(!e) return -1; connect(e, SIGNAL(changed()), SLOT(slotChanged())); list.append(e); } int id = getUniqueId(); e->idList.append(id); return id; } void removeItem(int id) { DWEntry *e = findEntryById(id); if(!e) return; e->idList.remove(id); if(e->idList.isEmpty()) list.removeRef(e); } int getUniqueId() { for(int n = 0;; ++n) { QPtrListIterator it(list); bool found = false; for(DWEntry *e; (e = it.current()); ++it) { for(QValueList::ConstIterator idi = e->idList.begin(); idi != e->idList.end(); ++idi) { if(*idi == n) { found = true; break; } } if(found) break; } if(!found) return n; } } DWEntry * findEntryByDir(const QString &s) { QPtrListIterator it(list); for(DWEntry *e; (e = it.current()); ++it) { if(e->dir == s) return e; } return 0; } DWEntry * findEntryById(int id) { QPtrListIterator it(list); for(DWEntry *e; (e = it.current()); ++it) { for(QValueList::ConstIterator idi = e->idList.begin(); idi != e->idList.end(); ++idi) { if(*idi == id) return e; } } return 0; } private slots: void slotChanged() { // use a timer to combine multiple changed events into one if(!t.isActive()) t.start(200, true); } void slotNotify() { // see who is dirty QPtrListIterator it(list); for(DWEntry *e; (e = it.current()); ++it) { if(e->dirty) { e->dirty = false; for(QValueList::ConstIterator idi = e->idList.begin(); idi != e->idList.end(); ++idi) { par->triggerDirChanged(*idi); } } } } }; DirWatchPlatform::DirWatchPlatform() :QObject(0) { d = new Private(this); } DirWatchPlatform::~DirWatchPlatform() { delete d; } bool DirWatchPlatform::init() { return true; } int DirWatchPlatform::addDir(const QString &s) { return d->addItem(s); } void DirWatchPlatform::removeDir(int id) { d->removeItem(id); } #include"dirwatch_win.moc"