Commit 0cf9ffc0 authored by dt's avatar dt
Browse files

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.
parent 2dda23da
......@@ -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
......@@ -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)
......
......@@ -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;
......
......@@ -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;
};
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment