project tree watcher interface plan

dukju ahn dukjuahn at gmail.com
Mon Jun 4 17:45:25 UTC 2007


2007/6/4, Andreas Pakulat <apaku at gmx.de>:
> On 04.06.07 13:19:50, dukju ahn wrote:
> > 2007/6/4, Matt Rogers <mattr at kde.org>:
> > >
> > >On Jun 4, 2007, at 9:43 AM, dukju ahn wrote:
> > >
> > >> Apart from the general wrapper around QFileSystemWatcher, what
> > >> I want to discuss here is specific project tree watcher which
> > >> targets project managers.
> > >>
> > >> Although we can use the general wrapper discussed, I think that
> > >> using general wrapper for project managers is duplication of memory
> > >> usages. The key to project manager tree watcher is that it should
> > >> stores
> > >> Project**Items for each watched path. And it can compare difference
> > >> using that project**items.
> > >>
> > >> So my plan is that the "general" wrapper around QFSWachter will not
> > >> be used for projecttreewatcher.
> > >>
> > >> Also, rather than emitting signal, my plan is that we have base class
> > >> named "projecttreewatcher", and provides virtual interfaces, because
> > >> we can provide some default implementations. Especially,
> > >> directoryCreated() and deleted() implementation would be common
> > >> among the project managers, since makefile parsing event will be
> > >> taken place in fileCreated() deleted() modified() method..
> > >>
> > >> Any objection?. I expect as before.

Index: projectfilesystemwatcher.h
===================================================================
--- projectfilesystemwatcher.h	(revision 671357)
+++ projectfilesystemwatcher.h	(working copy)
@@ -16,12 +16,17 @@
 class QFileSystemWatcher;
 // template <typename T1, typename T2> class QHash;
 #include <QHash>
+#include <QEvent>
+#include "kurl.h"
 namespace KDevelop
 {
 class ProjectFolderItem;
 class ProjectFileItem;
+class IProjectFileManager;
 }

+class ProjectFileSystemWatcherPrivate;
+
 /**
  * Simple QFileSystemWatcher wrapper. Designed to be used in custom
make manager.
  * Emits directory/file changed signal with ProjectFolderItem or
ProjectFileItem
@@ -30,7 +35,7 @@
 {
     Q_OBJECT
 public:
-    ProjectFileSystemWatcher( QObject* parent = 0 );
+    ProjectFileSystemWatcher( KDevelop::IProjectFileManager* manager,
QObject* parent = 0 );
     virtual ~ProjectFileSystemWatcher();

     void addDirectory( const QString &path,
KDevelop::ProjectFolderItem *folderItem );
@@ -40,7 +45,7 @@
     void removeFile( const QString & path );
 //     void removePaths ( const QStringList & paths );

-Q_SIGNALS:
+// Q_SIGNALS:
     /**
      * Emitted when the files or subdirectories under @arg path were
created or deleted.
      *
@@ -51,22 +56,43 @@
      * is deleted, the directoryChanged( parentDir ) will be emitted.
But if child directory
      * is not empty, then directoryChanged( childDir ) will be emitted.
      */
-    void directoryChanged( const QString & path,
KDevelop::ProjectFolderItem* folderItem );
+//     void directoryChanged( const QString & path,
KDevelop::ProjectFolderItem* folderItem );

     /**
      * Emitted when the contents of file were modified, or the file
itself was deleted on disk.
      */
-    void fileChanged( const QString & path,
KDevelop::ProjectFileItem* fileItem );
+//     void fileChanged( const QString & path,
KDevelop::ProjectFileItem* fileItem );

 private Q_SLOTS:
     void slotDirChanged( const QString& path );
+    void slotDirChangedInternal( const QString &dir,
KDevelop::ProjectFolderItem* folderItem );
     void slotFileChanged( const QString& path );

+protected:
+    /**
+     * called when the contents of file were modified, or the file
itself was deleted on disk.
+     */
+    virtual void fileChanged( const QString & path,
KDevelop::ProjectFileItem* fileItem ) = 0;
+
+    /**
+     * called when files under @arg parentFolder were created or deleted
+     */
+    virtual void filesCreated( const KUrl::List &files,
KDevelop::ProjectFolderItem *parentFolder ) = 0;
+    virtual void filesDeleted( const QList<KDevelop::ProjectFileItem*> &files,
+                               KDevelop::ProjectFolderItem *parentFolder ) = 0;
+
+    /**
+     * called when subdirectories under @arg parentFolder were
created or deleted
+     */
+    virtual void directoriesCreated( const KUrl::List &files,
KDevelop::ProjectFolderItem *parentFolder );
+    virtual void directoriesDeleted( const
QList<KDevelop::ProjectFolderItem*> &dirs,
+                                     KDevelop::ProjectFolderItem
*parentFolder );
+
+    void parseDirectoryRecursively( KDevelop::ProjectFolderItem* dir,
+                                    KDevelop::IProjectFileManager* manager );
+
 private:
-    QFileSystemWatcher *m_watch;
-    QHash< QString, KDevelop::ProjectFolderItem* > m_folderHash;
-    QHash< QString, KDevelop::ProjectFileItem* > m_fileHash;
-
+    ProjectFileSystemWatcherPrivate *d;
 };

 #endif
Index: custommakefswatcher.cpp
===================================================================
--- custommakefswatcher.cpp	(revision 0)
+++ custommakefswatcher.cpp	(revision 0)
@@ -0,0 +1,117 @@
+#include "custommakefswatcher.h"
+#include "custommakemanager.h"
+#include "custommakemodelitems.h"
+#include <QFileInfo>
+#include "iprojectcontroller.h"
+#include "iproject.h"
+#include "icore.h"
+#include <kdebug.h>
+
+CustomMakeFSWatcher::CustomMakeFSWatcher(CustomMakeManager* manager,
QObject* parent)
+    : ProjectFileSystemWatcher( manager, parent )
+{
+    m_customMan = manager;
+}
+CustomMakeFSWatcher::~CustomMakeFSWatcher()
+{}
+
+void CustomMakeFSWatcher::filesCreated( const KUrl::List &files,
KDevelop::ProjectFolderItem *parentFolder )
+{
+    Q_FOREACH( KUrl _file, files )
+    {
+        KDevelop::ProjectFileItem *newitem = new KDevelop::ProjectFileItem(
+            parentFolder->project(), _file, parentFolder );
+        // if Makefile, parse new targets and add to watcher
+        if( _file.fileName() == QString( "Makefile" ) ) // TODO
portable, setting aware
+        {
+            QStringList newTargets = m_customMan->parseCustomMakeFile( _file );
+            Q_FOREACH( QString newTarget, newTargets )
+            {
+                new CustomMakeTargetItem( parentFolder->project(),
newTarget, parentFolder );
+            }
+            addFile( _file.toLocalFile(), newitem );
+        }
+    }
+}
+void CustomMakeFSWatcher::filesDeleted( const
QList<KDevelop::ProjectFileItem*> &files,
+                            KDevelop::ProjectFolderItem *parentFolder )
+{
+    Q_FOREACH( KDevelop::ProjectFileItem *_item, files )
+    {
+        int row = _item->row();
+        parentFolder->removeRow( row );
+    }
+}
+
+// void CustomMakeFSWatcher::directoriesCreated( const KUrl::List
&files, KDevelop::ProjectFolderItem *parentFolder )
+// {
+//
+// }
+// void CustomMakeFSWatcher::directoriesDeleted( const
QList<KDevelop::ProjectFolderItem*> &dirs,
+//                                     KDevelop::ProjectFolderItem
*parentFolder )
+// {
+// }
+
+void CustomMakeFSWatcher::fileChanged( const QString& file,
KDevelop::ProjectFileItem* fileItem)
+{
+    kDebug() << "File " << file << " changed " << endl;
+
+    QFileInfo info( file );
+    if( info.fileName() != QString("Makefile") )
+        return;
+
+    // find Makefile item, because it is allowed to be null
+    KDevelop::ProjectFileItem *makefileItem=0;
+    KDevelop::IProject *project=0;
+    if( !fileItem )
+    {
+        KUrl url(file);
+        project =
m_customMan->core()->projectController()->findProjectForUrl( url );
+        Q_ASSERT(project);
+        makefileItem = project->fileForUrl( KUrl(file) );
+    }
+    else
+    {
+        makefileItem = fileItem;
+        project = fileItem->project();
+    }
+    Q_ASSERT(makefileItem);
+
+    // find parent folder item
+    QStandardItem *stditem = makefileItem->parent();
+    KDevelop::ProjectBuildFolderItem *parentFolder =
+            dynamic_cast<KDevelop::ProjectBuildFolderItem*>( stditem );
+    if( !parentFolder )
+        return;
+
+    // delete every targets in the fileItem's parent directory
+    QList<KDevelop::ProjectTargetItem*> targets = parentFolder->targetList();
+    Q_FOREACH( KDevelop::ProjectTargetItem* _deletingTarget, targets )
+    {
+        int targetrow = _deletingTarget->row();
+        parentFolder->removeRow( targetrow );
+    }
+
+    // determine whether the file contents were modified or the
entire file itself was deleted.
+    if( info.exists() == false )
+    {
+        // Makefile deleted
+        KDevelop::ProjectItem *prjitem = project->projectItem();
+        CustomMakeProjectItem *cmpi =
dynamic_cast<CustomMakeProjectItem*>( prjitem );
+        cmpi->fsWatcher()->removeFile( file );
+        int makefileRow = makefileItem->row();
+        parentFolder->removeRow( makefileRow );
+    }
+    else
+    {
+        // Makefile contents modified
+        QStringList newTargets = m_customMan->parseCustomMakeFile(
KUrl(file) );
+        Q_FOREACH( QString newTarget, newTargets )
+        {
+            new CustomMakeTargetItem( project, newTarget, parentFolder );
+        }
+    }
+
+}
+
+#include "custommakefswatcher.moc"
Index: custommakefswatcher.h
===================================================================
--- custommakefswatcher.h	(revision 0)
+++ custommakefswatcher.h	(revision 0)
@@ -0,0 +1,41 @@
+#ifndef CUSTOMMAKEFSWATCHER_H
+#define CUSTOMMAKEFSWATCHER_H
+
+#include "projectfilesystemwatcher.h"
+
+class KUrl;
+class KUrl::List;
+
+namespace KDevelop
+{
+class ProjectFileItem;
+class ProjectFolderItem;
+class IProjectFileManager;
+}
+
+class CustomMakeManager;
+
+class CustomMakeFSWatcher : public ProjectFileSystemWatcher
+{
+    Q_OBJECT
+public:
+    CustomMakeFSWatcher(CustomMakeManager* manager, QObject* parent = 0);
+    virtual ~CustomMakeFSWatcher();
+
+protected:
+    virtual void filesCreated( const KUrl::List &files,
KDevelop::ProjectFolderItem *parentFolder );
+    virtual void filesDeleted( const QList<KDevelop::ProjectFileItem*> &files,
+                               KDevelop::ProjectFolderItem *parentFolder );
+
+//     virtual void directoriesCreated( const KUrl::List &files,
KDevelop::ProjectFolderItem *parentFolder );
+//     virtual void directoriesDeleted( const
QList<KDevelop::ProjectFolderItem*> &dirs,
+//                                      KDevelop::ProjectFolderItem
*parentFolder );
+
+    virtual void fileChanged( const QString& file,
KDevelop::ProjectFileItem* fileItem);
+
+private:
+    CustomMakeManager *m_customMan;
+
+};
+
+#endif
Index: projectfilesystemwatcher.cpp
===================================================================
--- projectfilesystemwatcher.cpp	(revision 671357)
+++ projectfilesystemwatcher.cpp	(working copy)
@@ -10,78 +10,293 @@

 #include "projectmodel.h"
 #include "projectfilesystemwatcher.h"
+#include "iprojectfilemanager.h"
 #include <QHash>
 #include <QFileSystemWatcher>
+#include <QDir>
+#include <QFileInfo>
+#include <QQueue>
+#include <QCoreApplication>
+#include "kdebug.h"

-ProjectFileSystemWatcher::ProjectFileSystemWatcher( QObject* parent  )
+class ProjectFileSystemWatcherPrivate
+{
+public:
+    QFileSystemWatcher *m_watch;
+    QHash< QString, KDevelop::ProjectFolderItem* > m_folderHash;
+    QHash< QString, KDevelop::ProjectFileItem* > m_fileHash;
+    KDevelop::IProjectFileManager *m_manager;
+};
+
+ProjectFileSystemWatcher::ProjectFileSystemWatcher(
KDevelop::IProjectFileManager *manager,
+                                                    QObject* parent  )
     : QObject( parent )
+    , d( new ProjectFileSystemWatcherPrivate )
 {
-    m_watch = new QFileSystemWatcher(this);
-    connect( m_watch, SIGNAL(directoryChanged ( const QString &)),
+    d->m_watch = new QFileSystemWatcher(this);
+    connect( d->m_watch, SIGNAL(directoryChanged ( const QString &)),
              this, SLOT(slotDirChanged(const QString&)) );
-    connect( m_watch, SIGNAL(fileChanged ( const QString &)),
+    connect( d->m_watch, SIGNAL(fileChanged ( const QString &)),
              this, SLOT(slotFileChanged(const QString&)) );
+    d->m_manager = manager;
 }

 ProjectFileSystemWatcher::~ProjectFileSystemWatcher()
 {
-    delete m_watch;
+    delete d->m_watch;
+    delete d;
 }

 void ProjectFileSystemWatcher::addDirectory( const QString &path,
KDevelop::ProjectFolderItem *folderItem )
 {
-    m_watch->addPath( path );
-    m_folderHash.insert( path, folderItem );
+    d->m_watch->addPath( path );
+    d->m_folderHash.insert( path, folderItem );
 }

 void ProjectFileSystemWatcher::addFile( const QString &path,
KDevelop::ProjectFileItem *fileItem )
 {
-    m_watch->addPath( path );
+    d->m_watch->addPath( path );
     if( fileItem )
     {
-        m_fileHash.insert( path, fileItem );
+        d->m_fileHash.insert( path, fileItem );
     }
 }

 void ProjectFileSystemWatcher::removeDirectory( const QString & path )
 {
-    m_watch->removePath( path );
+//     d->m_watch->removePath( path );
 //     if( m_folderHash.contains( path ) )
 //     {
-    m_folderHash.remove( path );
+    kDebug() << "Removing Directory from Watcher " << path << endl;
+    d->m_folderHash.remove( path );
 //     }

 }

 void ProjectFileSystemWatcher::removeFile( const QString & path )
 {
-    m_watch->removePath( path );
-    m_fileHash.remove( path );
+//     d->m_watch->removePath( path );
+    kDebug() << "Removing file from Watcher " << path << endl;
+    d->m_fileHash.remove( path );
 }

 // void ProjectFileSystemWatcher::removePaths( const QStringList & paths );

 void ProjectFileSystemWatcher::slotDirChanged( const QString& path )
 {
-    Q_ASSERT( m_folderHash.contains(path) );
-    if( m_folderHash.contains(path) )
+//     Q_ASSERT( d->m_folderHash.contains(path) );
+    if( d->m_folderHash.contains(path) )
     {
-        KDevelop::ProjectFolderItem *folder = m_folderHash.value( path );
-        emit directoryChanged( path, folder );
+        KDevelop::ProjectFolderItem *folder = d->m_folderHash.value( path );
+        slotDirChangedInternal( path, folder );
     }
 }

 void ProjectFileSystemWatcher::slotFileChanged( const QString& path )
 {
-    if( m_fileHash.contains(path) )
+    Q_ASSERT( d->m_fileHash.contains(path) );
+    if( d->m_fileHash.contains(path) )
     {
-        KDevelop::ProjectFileItem *file = m_fileHash.value( path );
-        emit fileChanged( path, file );
+        KDevelop::ProjectFileItem *file = d->m_fileHash.value( path );
+        fileChanged( path, file );
     }
     else
     {
-        emit fileChanged( path, 0 );
+        fileChanged( path, 0 );
     }
 }

+void ProjectFileSystemWatcher::slotDirChangedInternal( const QString
&dir, KDevelop::ProjectFolderItem* folderItem )
+{
+    kDebug() << "Directory " << dir << " changed " << endl;
+
+    QDir changedDir( dir );
+
+    if( !changedDir.exists() )
+    {
+        //directory itself deleted
+        int row = folderItem->row();
+        QStandardItem *parent = folderItem->parent();
+        parent->removeRow( row );
+
+        this->removeDirectory( dir );
+
+        return;
+    }
+    else //subdirectory or file is created or deleted.
+    {
+        // retrieve current disk info
+        QFileInfoList fileEntries = changedDir.entryInfoList(QDir::Files);
+        QFileInfoList dirEntries =
changedDir.entryInfoList(QDir::NoDotAndDotDot | QDir::Dirs);
+
+        // convert disk info into QStringList
+        QStringList fileList;
+        for ( int i = 0; i < fileEntries.count(); ++i )
+        {
+            QFileInfo fileInfo = fileEntries.at( i );
+            QString absFilePath = fileInfo.absoluteFilePath();
+            fileList << absFilePath;
+        }
+        QStringList dirList;
+        for ( int i = 0; i < dirEntries.count(); ++i )
+        {
+            QFileInfo fileInfo = dirEntries.at( i );
+            QString absFilePath = fileInfo.absoluteFilePath();
+            dirList << absFilePath;
+        }
+
+        // retrieve model item info, and convert into QStringList
+        QList<KDevelop::ProjectFileItem*> itemFileList =
folderItem->fileList();
+        QStringList itemFileListString;
+        Q_FOREACH( KDevelop::ProjectFileItem* _item, itemFileList )
+        {
+            itemFileListString << _item->url().toLocalFile();
+        }
+
+        QList<KDevelop::ProjectFolderItem*> itemFolderList =
folderItem->folderList();
+        QStringList itemFolderListString;
+        Q_FOREACH( KDevelop::ProjectFolderItem *_item, itemFolderList )
+        {
+            itemFolderListString << _item->url().toLocalFile();
+        }
+
+        // Compare the difference between disk file and model items
+
+        // round 1 -- file
+//         KUrl::List deletedFileUrl;
+        QList< KDevelop::ProjectFileItem* > deletedItems;
+        Q_FOREACH( KDevelop::ProjectFileItem* _fileItem, itemFileList )
+        {
+            if( fileList.contains( _fileItem->url().toLocalFile() ) == false )
+            {
+                // disk file was deleted, so project file items
should be also deleted
+
+                // ### note: if some file, previously added to
watching list, was deleted,
+                // than this directoryChanged() is not emitted.
Rather fileChanged() is emitted.
+//                 if( deletingCandidate.fileName() ==
QString("Makefile") ) // TODO portable, setting aware
+//                 {
+//                     //delete every targets in this directory
+//                     QList<KDevelop::ProjectTargetItem*> targets =
folderItem->targetList();
+//                     Q_FOREACH( KDevelop::ProjectTargetItem*
_deletingTarget, targets )
+//                     {
+//                         int targetrow = _deletingTarget->row();
+//                         folderItem->removeRow( targetrow );
+//                     }
+//                     cmpi->fsWatcher()->removeFile(
deletingCandidate.toLocalFile() );
+//                 }
+//                 int row = _fileItem->row();
+//                 folderItem->removeRow( row );
+
+//                 deletedFileUrl << deletingCandidate;
+                deletedItems << _fileItem;
+            }
+        }
+        if( !deletedItems.isEmpty() )
+            filesDeleted( deletedItems, folderItem );
+
+        KUrl::List createdFileUrl;
+        Q_FOREACH( QString diskFile, fileList )
+        {
+            if( itemFileListString.contains( diskFile ) == false )
+            {
+                // disk file was created, file items should be also created
+//                 KDevelop::ProjectFileItem *newitem = new
KDevelop::ProjectFileItem(
+//                         folderItem->project(), KUrl(diskFile), folderItem );
+//                 // if Makefile, parse new targets and add to watcher
+//                 if( diskFile.endsWith( "/Makefile" ) ) // TODO
portable, setting aware
+//                 {
+//                     QStringList newTargets = parseCustomMakeFile(
KUrl(diskFile) );
+//                     Q_FOREACH( QString newTarget, newTargets )
+//                     {
+//                         new CustomMakeTargetItem(
folderItem->project(), newTarget, folderItem );
+//                     }
+//                     cmpi->fsWatcher()->addFile( diskFile, newitem );
+//                 }
+                createdFileUrl << KUrl(diskFile);
+            }
+        }
+        if( !createdFileUrl.isEmpty() )
+            filesCreated( createdFileUrl, folderItem );
+
+        // round 2 -- directory
+//         KUrl::List deletedDirs;
+        QList< KDevelop::ProjectFolderItem* > deletedDirs;
+        Q_FOREACH( KDevelop::ProjectFolderItem* _folderItem, itemFolderList )
+        {
+            if( dirList.contains( _folderItem->url().toLocalFile() ) == false)
+            {
+//                 int row = _folderItem->row();
+//                 QString tobeRemovedDir = _folderItem->url().toLocalFile();
+//                 folderItem->removeRow( row );
+//
+//                 this->removeDirectory( tobeRemovedDir );
+                deletedDirs << _folderItem;
+            }
+        }
+        if( !deletedDirs.isEmpty() )
+            directoriesDeleted( deletedDirs, folderItem );
+
+        KUrl::List createdDirs;
+        Q_FOREACH( QString diskDir, dirList )
+        {
+            if( itemFolderListString.contains( diskDir ) == false )
+            {
+//                 KDevelop::ProjectBuildFolderItem *newitem =new
KDevelop::ProjectBuildFolderItem(
+//                         folderItem->project(), KUrl(diskDir), folderItem );
+//
+//                 this->addDirectory( diskDir, newitem );
+//                 this->parseDirectoryRecursively( newitem, d->m_manager );
+                createdDirs << KUrl(diskDir);
+            }
+        }
+        if( !createdDirs.isEmpty() )
+            directoriesCreated( createdDirs, folderItem );
+    }
+}
+
+void ProjectFileSystemWatcher::parseDirectoryRecursively(
KDevelop::ProjectFolderItem* dir,
+
KDevelop::IProjectFileManager* manager )
+{
+    QQueue< QList<KDevelop::ProjectFolderItem*> > workQueue;
+    QList<KDevelop::ProjectFolderItem*> initial;
+    initial.append( dir );
+    workQueue.enqueue( initial );
+
+    while( workQueue.count() > 0 )
+    {
+        QList<KDevelop::ProjectFolderItem*> front = workQueue.dequeue();
+        Q_FOREACH( KDevelop::ProjectFolderItem* _item, front )
+        {
+            QList<KDevelop::ProjectFolderItem*> workingList =
manager->parse( _item );
+            if( workingList.count() > 0 )
+                workQueue.enqueue( workingList );
+        }
+    }
+}
+
+void ProjectFileSystemWatcher::directoriesCreated( const KUrl::List &files,
+                                    KDevelop::ProjectFolderItem *parentFolder )
+{
+    Q_FOREACH( KUrl _file, files )
+    {
+        KDevelop::ProjectBuildFolderItem *newitem = new
KDevelop::ProjectBuildFolderItem(
+            parentFolder->project(), _file, parentFolder );
+
+        this->addDirectory( _file.toLocalFile(), newitem );
+        this->parseDirectoryRecursively( newitem, d->m_manager );
+    }
+}
+void ProjectFileSystemWatcher::directoriesDeleted( const
QList<KDevelop::ProjectFolderItem*> &dirs,
+                                    KDevelop::ProjectFolderItem *parentFolder )
+{
+    Q_FOREACH( KDevelop::ProjectFolderItem *_item, dirs )
+    {
+        int row = _item->row();
+        QString tobeRemovedDir = _item->url().toLocalFile();
+        parentFolder->removeRow( row );
+
+        this->removeDirectory( tobeRemovedDir );
+    }
+}
+
 #include "projectfilesystemwatcher.moc"




More information about the KDevelop-devel mailing list