From 0cf9ffc03183f9d460618f928e945448d8ed4f02 Mon Sep 17 00:00:00 2001
From: dt <qtc-commiter@nokia.com>
Date: Wed, 14 Jan 2009 17:13:17 +0100
Subject: [PATCH] Fixes:    Updating the completion for ui files

Details:  That is we update all generated headers (which are new or
modfied) in the C++ Engine. Big if, this only works if we find the
correct path for the ui header files. Which is known to sometimes not
work, I'll fix that next.
---
 .../qt4projectmanager/directorywatcher.cpp    | 161 ------------------
 .../qt4projectmanager/directorywatcher.h      |  40 -----
 src/plugins/qt4projectmanager/qt4nodes.cpp    | 119 +++++++------
 src/plugins/qt4projectmanager/qt4nodes.h      |   9 +-
 4 files changed, 74 insertions(+), 255 deletions(-)

diff --git a/src/plugins/qt4projectmanager/directorywatcher.cpp b/src/plugins/qt4projectmanager/directorywatcher.cpp
index d20444098e0..b054f1ff291 100644
--- a/src/plugins/qt4projectmanager/directorywatcher.cpp
+++ b/src/plugins/qt4projectmanager/directorywatcher.cpp
@@ -44,165 +44,6 @@ enum { debugWatcher = 0 };
 namespace Qt4ProjectManager {
 namespace Internal {
 
-int DirectoryWatcher::m_objectCount = 0;
-QHash<QString,int> DirectoryWatcher::m_directoryCount;
-QFileSystemWatcher *DirectoryWatcher::m_watcher = 0;
-
-/*
- \class DirectoryWatcher
-
-   A wrapper for QFileSystemWatcher that collects
-   consecutive changes to a registered directory and emits directoryChanged() and fileChanged().
-
-   Note that files added are only monitored if the parent directory is added, too.
-
-   All instances of DirectoryWatcher share one QFileSystemWatcher object.
-   That's because every QFileSystemWatcher object consumes a file descriptor,
-   even if no files are watched.
-*/
-DirectoryWatcher::DirectoryWatcher(QObject *parent) :
-    QObject(parent),
-    m_timer(0)
-{
-    if (!m_watcher)
-        m_watcher = new QFileSystemWatcher();
-    ++m_objectCount;
-    connect(m_watcher, SIGNAL(directoryChanged(QString)),
-            this, SLOT(slotDirectoryChanged(QString)));
-}
-
-DirectoryWatcher::~DirectoryWatcher()
-{
-    foreach (const QString &dir, m_directories)
-        removeDirectory(dir);
-    if (--m_objectCount == 0) {
-        delete m_watcher;
-        m_watcher = 0;
-    }
-}
-
-QStringList DirectoryWatcher::directories() const
-{
-    if (debugWatcher)
-        qDebug() << Q_FUNC_INFO << m_directories;
-    return m_directories;
-}
-
-void DirectoryWatcher::addDirectory(const QString &dir)
-{
-    if (debugWatcher)
-        qDebug() << Q_FUNC_INFO << dir;
-    if (m_directories.contains(dir))
-        return;
-    m_directories += dir;
-    if (m_directoryCount[dir] == 0)
-        m_watcher->addPath(dir);
-    m_directoryCount[dir] += 1;
-}
-
-void DirectoryWatcher::removeDirectory(const QString &dir)
-{
-    if (debugWatcher)
-        qDebug() << Q_FUNC_INFO << dir;
-    m_directories.removeOne(dir);
-    m_directoryCount[dir] -= 1;
-    if (m_directoryCount[dir] == 0)
-      m_watcher->removePath(dir);
-}
-
-QStringList DirectoryWatcher::files() const
-{
-    if (debugWatcher)
-        qDebug() << Q_FUNC_INFO << m_files.keys();
-    return m_files.keys();
-}
-
-void DirectoryWatcher::addFile(const QString &filePath)
-{
-    addFiles(QStringList() << filePath);
-}
-
-void DirectoryWatcher::addFiles(const QStringList &filePaths)
-{
-    foreach (const QString filePath, filePaths) {
-        QFileInfo file(filePath);
-        m_files.insert(file.absoluteFilePath(),file.lastModified());
-    }
-}
-
-void DirectoryWatcher::removeFile(const QString &filePath)
-{
-    m_files.remove(filePath);
-}
-
-void DirectoryWatcher::slotDirectoryChanged(const QString &path)
-{
-    if (debugWatcher)
-        qDebug() << Q_FUNC_INFO << path;
-    if (!m_directories.contains(path)
-        || m_pendingDirectories.contains(path))
-        return;
-
-    if (!m_timer) {
-        m_timer = new QTimer(this);
-        m_timer->setSingleShot(true);
-        m_timer->setInterval(500); // delay for 0.5 sec
-        connect(m_timer, SIGNAL(timeout()), this, SLOT(slotDelayedDirectoriesChanged()));
-    }
-    if (!m_timer->isActive())
-        m_timer->start();
-    m_pendingDirectories.push_back(path);
-}
-
-void DirectoryWatcher::slotDelayedDirectoriesChanged()
-{
-    if (debugWatcher)
-        qDebug() << Q_FUNC_INFO << " emitting " << m_pendingDirectories;
-    const QStringList::const_iterator cend = m_pendingDirectories.constEnd();
-    for (QStringList::const_iterator it = m_pendingDirectories.constBegin(); it != cend; ++it) {
-        const QString dir = *it;
-        if (!QFileInfo(dir).exists())
-            removeDirectory(*it);
-        emit directoryChanged(*it);
-        updateFileList(*it);
-    }
-    m_pendingDirectories.clear();
-}
-
-void DirectoryWatcher::updateFileList(const QString &dir)
-{
-    const QStringList monitoredFiles = m_files.keys();
-    QStringList removedFiles = monitoredFiles;
-    if (QFileInfo(dir).exists()) {
-        // Compare directory contents and emit signals
-        QFileInfoList entryInfoList
-                = QDir(dir).entryInfoList(QDir::Files|QDir::CaseSensitive);
-        // Loop over directory creating the new map of file->time, removing
-        // the existing entries from the old map
-        const QFileInfoList::const_iterator cend = entryInfoList.constEnd();
-        for (QFileInfoList::const_iterator filIt = entryInfoList.constBegin();
-            filIt != cend; ++filIt) {
-            const QString path = filIt->absoluteFilePath();
-            FileModificationTimeMap::iterator mapIt = m_files.find(path);
-            if (mapIt != m_files.end()) {
-                const QDateTime lastModified = filIt->lastModified();
-                if (lastModified > mapIt.value()) {
-                    if (debugWatcher)
-                        qDebug() << Q_FUNC_INFO << "emitting file changed" << path;
-                    emit fileChanged(path);
-                    m_files[path] = lastModified;
-                }
-                removedFiles.removeOne(path);
-            }
-        }
-    }
-
-    if (!removedFiles.isEmpty()) {
-        foreach (const QString &file, removedFiles)
-            removeFile(file);
-    }
-}
-
 int FileWatcher::m_objectCount = 0;
 QHash<QString,int> FileWatcher::m_fileCount;
 QFileSystemWatcher *FileWatcher::m_watcher = 0;
@@ -256,7 +97,5 @@ void FileWatcher::removeFile(const QString &file)
       m_watcher->removePath(file);
 }
 
-
-
 } // namespace Internal
 } // namespace Qt4ProjectManager
diff --git a/src/plugins/qt4projectmanager/directorywatcher.h b/src/plugins/qt4projectmanager/directorywatcher.h
index 57f015330a1..35ceaed91e1 100644
--- a/src/plugins/qt4projectmanager/directorywatcher.h
+++ b/src/plugins/qt4projectmanager/directorywatcher.h
@@ -47,46 +47,6 @@ QT_END_NAMESPACE
 namespace Qt4ProjectManager {
 namespace Internal {
 
-class DirectoryWatcher : public QObject
-{
-    Q_DISABLE_COPY(DirectoryWatcher)
-    Q_OBJECT
-public:
-    explicit DirectoryWatcher(QObject *parent = 0);
-    virtual ~DirectoryWatcher();
-
-    QStringList directories() const;
-    void addDirectory(const QString &dir);
-    void removeDirectory(const QString &dir);
-
-    QStringList files() const;
-    void addFile(const QString &filePath);
-    void addFiles(const QStringList &filePaths);
-    void removeFile(const QString &filePath);
-
-signals:
-    void directoryChanged(const QString &path);
-    void fileChanged(const QString &path);
-
-private slots:
-    void slotDirectoryChanged(const QString &);
-    void slotDelayedDirectoriesChanged();
-
-private:
-    void updateFileList(const QString &dir);
-
-    static int m_objectCount;
-    static QHash<QString,int> m_directoryCount;
-    static QFileSystemWatcher *m_watcher;
-
-    QTimer *m_timer;
-    QStringList m_directories;
-    QStringList m_pendingDirectories;
-
-    typedef QHash<QString, QDateTime> FileModificationTimeMap;
-    FileModificationTimeMap m_files;
-};
-
 class FileWatcher : public QObject
 {
     Q_DISABLE_COPY(FileWatcher)
diff --git a/src/plugins/qt4projectmanager/qt4nodes.cpp b/src/plugins/qt4projectmanager/qt4nodes.cpp
index 8f836f64982..b32a47ac512 100644
--- a/src/plugins/qt4projectmanager/qt4nodes.cpp
+++ b/src/plugins/qt4projectmanager/qt4nodes.cpp
@@ -49,6 +49,7 @@
 #include <coreplugin/vcsmanager.h>
 
 #include <cpptools/cppmodelmanagerinterface.h>
+#include <cplusplus/CppDocument.h>
 
 #include <utils/qtcassert.h>
 
@@ -488,6 +489,9 @@ QStringList Qt4PriFileNode::varNames(FileType type)
     return vars;
 }
 
+#include <projectexplorer/projectexplorer.h>
+#include <projectexplorer/buildmanager.h>
+
 /*!
   \class Qt4ProFileNode
   Implements abstract ProjectNode class
@@ -498,8 +502,7 @@ Qt4ProFileNode::Qt4ProFileNode(Qt4Project *project,
         : Qt4PriFileNode(project, this, filePath),
           // own stuff
           m_projectType(InvalidProject),
-          m_isQBuildProject(false),
-          m_dirWatcher(new DirectoryWatcher(this))
+          m_isQBuildProject(false)
 {
     if (parent)
         setParent(parent);
@@ -507,14 +510,13 @@ Qt4ProFileNode::Qt4ProFileNode(Qt4Project *project,
     m_updateTimer.setInterval(100);
     m_updateTimer.setSingleShot(true);
 
-    connect(m_dirWatcher, SIGNAL(directoryChanged(const QString&)),
-            this, SLOT(updateGeneratedFiles()));
-    connect(m_dirWatcher, SIGNAL(fileChanged(const QString&)),
-            this, SLOT(fileChanged(const QString&)));
     connect(m_project, SIGNAL(activeBuildConfigurationChanged()),
             this, SLOT(update()));
     connect(&m_updateTimer, SIGNAL(timeout()),
             this, SLOT(update()));
+
+    connect(ProjectExplorer::ProjectExplorerPlugin::instance()->buildManager(), SIGNAL(buildStateChanged(ProjectExplorer::Project*)),
+            this, SLOT(buildStateChanged(ProjectExplorer::Project*)));
 }
 
 Qt4ProFileNode::~Qt4ProFileNode()
@@ -522,6 +524,12 @@ Qt4ProFileNode::~Qt4ProFileNode()
 
 }
 
+void Qt4ProFileNode::buildStateChanged(ProjectExplorer::Project *project)
+{
+    if (project == m_project && !ProjectExplorer::ProjectExplorerPlugin::instance()->buildManager()->isBuilding(m_project))
+        updateUiFiles();
+}
+
 bool Qt4ProFileNode::hasTargets() const
 {
     return (projectType() == ApplicationTemplate) || (projectType() == LibraryTemplate);
@@ -689,7 +697,7 @@ void Qt4ProFileNode::update()
                 emit qt4Watcher->variablesChanged(this, m_varValues, newVarValues);
     }
 
-    updateGeneratedFiles();
+    updateUiFiles();
 
     foreach (NodesWatcher *watcher, watchers())
         if (Qt4NodesWatcher *qt4Watcher = qobject_cast<Qt4NodesWatcher*>(watcher))
@@ -698,15 +706,6 @@ void Qt4ProFileNode::update()
     delete reader;
 }
 
-void Qt4ProFileNode::fileChanged(const QString &filePath)
-{
-    CppTools::CppModelManagerInterface *modelManager =
-        ExtensionSystem::PluginManager::instance()->getObject<CppTools::CppModelManagerInterface>();
-
-    // TODO compress
-    modelManager->updateSourceFiles(QStringList() << filePath);
-}
-
 namespace {
     // find all ui files in project
     class FindUiFileNodesVisitor : public ProjectExplorer::NodesVisitor {
@@ -726,53 +725,54 @@ namespace {
     };
 }
 
-/*
-  Adds ui_xxx.h files to tree and monitors them / the UI_DIR directory for changes
-  */
-void Qt4ProFileNode::updateGeneratedFiles()
+// This function is triggered after a build, and updates the state ui files
+// That is it adds files that didn't exist yet to the project tree, and calls
+// updateSourceFiles() for files that changed
+// It does so by storing a modification time for each ui file we know about.
+
+// TODO this function should also be called if the build configuration changes
+// since the build directory could change, and thus the generated files that are present
+// TODO check that it works
+void Qt4ProFileNode::updateUiFiles()
 {
+    // Only those two project types can have ui files for us
     if (m_projectType != ApplicationTemplate
         && m_projectType != LibraryTemplate)
         return;
 
+    // Find all ui files
     FindUiFileNodesVisitor uiFilesVisitor;
     this->accept(&uiFilesVisitor);
     const QList<FileNode*> uiFiles = uiFilesVisitor.uiFileNodes;
 
-    // monitor uic dir (only if there are .ui files)
+    // Find the UiDir, there can only ever be one
+    QString uiDir; // We should default to the build directory
+    QStringList tmp = m_varValues[UiDirVar];
+    if (tmp.size() != 0)
+        uiDir = tmp.first();
 
-    QSet<QString> oldUiDirs = m_dirWatcher->directories().toSet();
-    QSet<QString> newUiDirs =
-            (!uiFiles.isEmpty()) ? m_varValues[UiDirVar].toSet() : QSet<QString>();
-    foreach (const QString &uiDir, oldUiDirs - newUiDirs)
-        m_dirWatcher->removeDirectory(uiDir);
-    foreach (const QString &uiDir, newUiDirs - oldUiDirs)
-        m_dirWatcher->addDirectory(uiDir);
-
-    // update generated files
-
-    // Already existing FileNodes
+    // Collect all existing generated files
     QList<FileNode*> existingFileNodes;
     foreach (FileNode *file, fileNodes()) {
         if (file->isGenerated())
             existingFileNodes << file;
     }
 
-
     // Convert uiFile to uiHeaderFilePath, find all headers that correspond
-    // and try to find them in uicDirs
+    // and try to find them in uiDir
     QStringList newFilePaths;
-    foreach (const QString &uicDir, m_varValues[UiDirVar]) {
-        foreach (FileNode *uiFile, uiFiles) {
-            const QString uiHeaderFilePath
-                    = QString("%1/ui_%2.h").arg(uicDir, QFileInfo(uiFile->path()).baseName());
-            if (QFileInfo(uiHeaderFilePath).exists())
-                newFilePaths << uiHeaderFilePath;
-        }
+    foreach (FileNode *uiFile, uiFiles) {
+        const QString uiHeaderFilePath
+                = QString("%1/ui_%2.h").arg(uiDir, QFileInfo(uiFile->path()).baseName());
+        if (QFileInfo(uiHeaderFilePath).exists())
+            newFilePaths << uiHeaderFilePath;
     }
 
+    // Create a diff between those lists
     QList<FileNode*> toRemove;
     QList<FileNode*> toAdd;
+    // The list of files for which we call updateSourceFile
+    QStringList toUpdate;
 
     qSort(newFilePaths);
     qSort(existingFileNodes.begin(), existingFileNodes.end(), ProjectNode::sortNodesByPath);
@@ -788,6 +788,13 @@ void Qt4ProFileNode::updateGeneratedFiles()
             toAdd << new FileNode(*newPathIter, ProjectExplorer::HeaderType, true);
             ++newPathIter;
         } else { // *existingNodeIter->path() == *newPathIter
+            QString fileName = (*existingNodeIter)->path();
+            QMap<QString, QDateTime>::const_iterator it = m_uitimestamps.find(fileName);
+            QDateTime lastModified = QFileInfo(fileName).lastModified();
+            if (it == m_uitimestamps.constEnd() || it.value() < lastModified) {
+                toUpdate << fileName;
+                m_uitimestamps[fileName] = lastModified;
+            }
             ++existingNodeIter;
             ++newPathIter;
         }
@@ -801,16 +808,33 @@ void Qt4ProFileNode::updateGeneratedFiles()
         ++newPathIter;
     }
 
+    // Update project tree
     if (!toRemove.isEmpty()) {
         foreach (FileNode *file, toRemove)
-            m_dirWatcher->removeFile(file->path());
+            m_uitimestamps.remove(file->path());
         removeFileNodes(toRemove, this);
     }
+
+    CppTools::CppModelManagerInterface *modelManager =
+        ExtensionSystem::PluginManager::instance()->getObject<CppTools::CppModelManagerInterface>();
+
     if (!toAdd.isEmpty()) {
-        foreach (FileNode *file, toAdd)
-            m_dirWatcher->addFile(file->path());
+        foreach (FileNode *file, toAdd) {
+            m_uitimestamps.insert(file->path(), QFileInfo(file->path()).lastModified());
+            toUpdate << file->path();
+
+            // Also adding files depending on that.
+            QString fileName = QFileInfo(file->path()).fileName();
+            foreach (CPlusPlus::Document::Ptr doc, modelManager->snapshot()) {
+                if (doc->includedFiles().contains(fileName)) {
+                    if (!toUpdate.contains(doc->fileName()))
+                        toUpdate << doc->fileName();
+                }
+            }
+        }
         addFileNodes(toAdd, this);
     }
+    modelManager->updateSourceFiles(toUpdate);
 }
 
 ProFileReader *Qt4PriFileNode::createProFileReader() const
@@ -957,13 +981,6 @@ void Qt4ProFileNode::invalidate()
 
     clear();
 
-    // remove monitored files/directories
-    foreach (const QString &file, m_dirWatcher->files())
-        m_dirWatcher->removeFile(file);
-    foreach (const QString &dir, m_dirWatcher->directories())
-        m_dirWatcher->removeDirectory(dir);
-
-
     // change project type
     Qt4ProjectType oldType = m_projectType;
     m_projectType = InvalidProject;
diff --git a/src/plugins/qt4projectmanager/qt4nodes.h b/src/plugins/qt4projectmanager/qt4nodes.h
index 9ea6b07c71c..5d428cc6720 100644
--- a/src/plugins/qt4projectmanager/qt4nodes.h
+++ b/src/plugins/qt4projectmanager/qt4nodes.h
@@ -35,10 +35,13 @@
 #define QT4NODES_H
 
 #include <projectexplorer/projectnodes.h>
+#include <projectexplorer/project.h>
 
 #include <QtCore/QHash>
 #include <QtCore/QStringList>
 #include <QtCore/QTimer>
+#include <QtCore/QDateTime>
+#include <QtCore/QMap>
 
 // defined in proitems.h
 QT_BEGIN_NAMESPACE
@@ -184,8 +187,8 @@ public slots:
     void scheduleUpdate();
     void update();
 private slots:
-    void fileChanged(const QString &filePath);
-    void updateGeneratedFiles();
+    void updateUiFiles();
+    void buildStateChanged(ProjectExplorer::Project*);
 
 private:
     Qt4ProFileNode *createSubProFileNode(const QString &path);
@@ -205,7 +208,7 @@ private:
     bool m_isQBuildProject;
     QTimer m_updateTimer;
 
-    DirectoryWatcher *m_dirWatcher;
+    QMap<QString, QDateTime> m_uitimestamps;
     friend class Qt4NodeHierarchy;
 };
 
-- 
GitLab