Files
qt-creator/src/plugins/qt4projectmanager/qt4nodes.cpp

1010 lines
35 KiB
C++
Raw Normal View History

2009-02-25 09:02:17 +01:00
/**************************************************************************
2008-12-02 12:01:29 +01:00
**
** This file is part of Qt Creator
**
2009-02-25 09:02:17 +01:00
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
2008-12-02 12:01:29 +01:00
**
** Contact: Qt Software Information (qt-info@nokia.com)
**
2009-02-25 09:02:17 +01:00
** Commercial Usage
**
2009-02-25 09:02:17 +01:00
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
2009-02-25 09:02:17 +01:00
** GNU Lesser General Public License Usage
**
2009-02-25 09:02:17 +01:00
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
2009-02-25 09:02:17 +01:00
** If you are unsure which license is appropriate for your use, please
** contact the sales department at qt-sales@nokia.com.
2008-12-02 12:01:29 +01:00
**
2009-02-25 09:02:17 +01:00
**************************************************************************/
2008-12-02 16:19:05 +01:00
2008-12-02 12:01:29 +01:00
#include "proeditormodel.h"
2008-12-02 16:19:05 +01:00
2008-12-02 12:01:29 +01:00
#include "profilereader.h"
#include "prowriter.h"
2008-12-02 12:01:29 +01:00
#include "qt4nodes.h"
#include "qt4project.h"
#include "qt4projectmanager.h"
#include <projectexplorer/nodesvisitor.h>
#include <projectexplorer/filewatcher.h>
2008-12-02 12:01:29 +01:00
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/fileiconprovider.h>
2008-12-02 12:01:29 +01:00
#include <coreplugin/filemanager.h>
#include <coreplugin/icore.h>
#include <coreplugin/iversioncontrol.h>
#include <coreplugin/vcsmanager.h>
#include <cpptools/cppmodelmanagerinterface.h>
#include <cplusplus/CppDocument.h>
#include <extensionsystem/pluginmanager.h>
2008-12-02 12:01:29 +01:00
2008-12-09 15:25:01 +01:00
#include <utils/qtcassert.h>
2008-12-02 12:01:29 +01:00
#include <QtCore/QDebug>
#include <QtCore/QDir>
#include <QtCore/QFile>
#include <QtCore/QFileInfo>
#include <QtCore/QTimer>
2008-12-09 15:25:01 +01:00
#include <QtGui/QPainter>
2008-12-02 12:01:29 +01:00
#include <QtGui/QMainWindow>
#include <QtGui/QMessageBox>
#include <QtGui/QPushButton>
using namespace Qt4ProjectManager;
using namespace Qt4ProjectManager::Internal;
namespace {
bool debug = false;
}
namespace {
// sorting helper function
bool sortProjectFilesByPath(ProFile *f1, ProFile *f2)
{
return f1->fileName() < f2->fileName();
}
}
/*!
\class Qt4PriFileNode
Implements abstract ProjectNode class
*/
Qt4PriFileNode::Qt4PriFileNode(Qt4Project *project, Qt4ProFileNode* qt4ProFileNode, const QString &filePath)
2008-12-02 12:01:29 +01:00
: ProjectNode(filePath),
m_project(project),
m_qt4ProFileNode(qt4ProFileNode),
m_projectFilePath(QDir::fromNativeSeparators(filePath)),
m_projectDir(QFileInfo(filePath).absolutePath()),
m_fileWatcher(new ProjectExplorer::FileWatcher(this))
2008-12-02 12:01:29 +01:00
{
Q_ASSERT(project);
setFolderName(QFileInfo(filePath).completeBaseName());
static QIcon dirIcon;
if (dirIcon.isNull()) {
// Create a custom Qt dir icon based on the system icon
Core::FileIconProvider *iconProvider = Core::FileIconProvider::instance();
QPixmap dirIconPixmap = iconProvider->overlayIcon(QStyle::SP_DirIcon,
QIcon(":/qt4projectmanager/images/qt_project.png"),
QSize(16, 16));
dirIcon.addPixmap(dirIconPixmap);
}
setIcon(dirIcon);
m_fileWatcher->addFile(filePath);
connect(m_fileWatcher, SIGNAL(fileChanged(QString)),
this, SLOT(scheduleUpdate()));
}
void Qt4PriFileNode::scheduleUpdate()
{
m_qt4ProFileNode->scheduleUpdate();
2008-12-02 12:01:29 +01:00
}
void Qt4PriFileNode::update(ProFile *includeFile, ProFileReader *reader)
{
Q_ASSERT(includeFile);
Q_ASSERT(reader);
2008-12-02 12:01:29 +01:00
// add project file node
if (m_fileNodes.isEmpty())
addFileNodes(QList<FileNode*>() << new FileNode(m_projectFilePath, ProjectFileType, false), this);
static QList<FileType> fileTypes =
(QList<FileType>() << ProjectExplorer::HeaderType
<< ProjectExplorer::SourceType
<< ProjectExplorer::FormType
<< ProjectExplorer::ResourceType
<< ProjectExplorer::UnknownFileType);
// update files
const QDir projectDir = QFileInfo(m_projectFilePath).dir();
foreach (FileType type, fileTypes) {
const QStringList qmakeVariables = varNames(type);
QStringList newFilePaths;
foreach (const QString &qmakeVariable, qmakeVariables)
newFilePaths += reader->absolutePathValues(qmakeVariable, projectDir.path(), ProFileReader::ExistingFilePaths, includeFile);
QList<FileNode*> existingFileNodes;
foreach (FileNode *fileNode, fileNodes()) {
if (fileNode->fileType() == type && !fileNode->isGenerated())
existingFileNodes << fileNode;
}
QList<FileNode*> toRemove;
QList<FileNode*> toAdd;
qSort(newFilePaths);
qSort(existingFileNodes.begin(), existingFileNodes.end(), ProjectNode::sortNodesByPath);
QList<FileNode*>::const_iterator existingNodeIter = existingFileNodes.constBegin();
QList<QString>::const_iterator newPathIter = newFilePaths.constBegin();
while (existingNodeIter != existingFileNodes.constEnd()
&& newPathIter != newFilePaths.constEnd()) {
if ((*existingNodeIter)->path() < *newPathIter) {
toRemove << *existingNodeIter;
++existingNodeIter;
} else if ((*existingNodeIter)->path() > *newPathIter) {
toAdd << new FileNode(*newPathIter, type, false);
++newPathIter;
} else { // *existingNodeIter->path() == *newPathIter
++existingNodeIter;
++newPathIter;
}
}
while (existingNodeIter != existingFileNodes.constEnd()) {
toRemove << *existingNodeIter;
++existingNodeIter;
}
while (newPathIter != newFilePaths.constEnd()) {
toAdd << new FileNode(*newPathIter, type, false);
++newPathIter;
}
if (!toRemove.isEmpty())
removeFileNodes(toRemove, this);
if (!toAdd.isEmpty())
addFileNodes(toAdd, this);
}
}
QList<ProjectNode::ProjectAction> Qt4PriFileNode::supportedActions() const
{
QList<ProjectAction> actions;
const FolderNode *folderNode = this;
const Qt4ProFileNode *proFileNode;
while (!(proFileNode = qobject_cast<const Qt4ProFileNode*>(folderNode)))
folderNode = folderNode->parentFolderNode();
Q_ASSERT(proFileNode);
switch (proFileNode->projectType()) {
case ApplicationTemplate:
case LibraryTemplate:
actions << AddFile << RemoveFile;
break;
case SubDirsTemplate:
actions << AddSubProject << RemoveSubProject;
break;
default:
break;
2008-12-02 12:01:29 +01:00
}
return actions;
}
bool Qt4PriFileNode::addSubProjects(const QStringList &proFilePaths)
{
Q_UNUSED(proFilePaths);
return false; //changeIncludes(m_includeFile, proFilePaths, AddToProFile);
2008-12-02 12:01:29 +01:00
}
bool Qt4PriFileNode::removeSubProjects(const QStringList &proFilePaths)
{
Q_UNUSED(proFilePaths);
return false; //changeIncludes(m_includeFile, proFilePaths, RemoveFromProFile);
2008-12-02 12:01:29 +01:00
}
bool Qt4PriFileNode::addFiles(const FileType fileType, const QStringList &filePaths,
QStringList *notAdded)
{
QStringList failedFiles;
changeFiles(fileType, filePaths, &failedFiles, AddToProFile);
if (notAdded)
*notAdded = failedFiles;
return failedFiles.isEmpty();
}
bool Qt4PriFileNode::removeFiles(const FileType fileType, const QStringList &filePaths,
QStringList *notRemoved)
{
QStringList failedFiles;
changeFiles(fileType, filePaths, &failedFiles, RemoveFromProFile);
if (notRemoved)
*notRemoved = failedFiles;
return failedFiles.isEmpty();
}
bool Qt4PriFileNode::renameFile(const FileType fileType, const QString &filePath,
const QString &newFilePath)
{
if (newFilePath.isEmpty())
2008-12-02 12:01:29 +01:00
return false;
if (!QFile::rename(filePath, newFilePath))
return false;
QStringList dummy;
changeFiles(fileType, QStringList() << filePath, &dummy, RemoveFromProFile);
if (!dummy.isEmpty())
return false;
changeFiles(fileType, QStringList() << newFilePath, &dummy, AddToProFile);
if (!dummy.isEmpty())
return false;
return true;
}
bool Qt4PriFileNode::changeIncludes(ProFile *includeFile, const QStringList &proFilePaths,
ChangeType change)
{
Q_UNUSED(includeFile);
Q_UNUSED(proFilePaths);
Q_UNUSED(change);
// TODO
return false;
}
bool Qt4PriFileNode::priFileWritable(const QString &path)
{
const QString dir = QFileInfo(path).dir().path();
Core::ICore *core = Core::ICore::instance();
Core::IVersionControl *versionControl = core->vcsManager()->findVersionControlForDirectory(dir);
switch (Core::EditorManager::promptReadOnlyFile(path, versionControl, core->mainWindow(), false)) {
case Core::EditorManager::RO_OpenVCS:
2008-12-02 12:01:29 +01:00
if (!versionControl->vcsOpen(path)) {
QMessageBox::warning(core->mainWindow(), tr("Failed!"), tr("Could not open the file for edit with SCC."));
2008-12-02 12:01:29 +01:00
return false;
}
break;
case Core::EditorManager::RO_MakeWriteable: {
2008-12-02 12:01:29 +01:00
const bool permsOk = QFile::setPermissions(path, QFile::permissions(path) | QFile::WriteUser);
if (!permsOk) {
QMessageBox::warning(core->mainWindow(), tr("Failed!"), tr("Could not set permissions to writable."));
2008-12-02 12:01:29 +01:00
return false;
}
break;
}
case Core::EditorManager::RO_SaveAs:
case Core::EditorManager::RO_Cancel:
2008-12-02 12:01:29 +01:00
return false;
}
return true;
}
bool Qt4PriFileNode::saveModifiedEditors(const QString &path)
{
QList<Core::IFile*> allFileHandles;
QList<Core::IFile*> modifiedFileHandles;
Core::ICore *core = Core::ICore::instance();
foreach (Core::IFile *file, core->fileManager()->managedFiles(path)) {
2008-12-02 12:01:29 +01:00
allFileHandles << file;
}
foreach (Core::IEditor *editor, core->editorManager()->editorsForFileName(path)) {
2008-12-02 12:01:29 +01:00
if (Core::IFile *editorFile = editor->file()) {
if (editorFile->isModified())
modifiedFileHandles << editorFile;
}
}
if (!modifiedFileHandles.isEmpty()) {
bool cancelled;
core->fileManager()->saveModifiedFiles(modifiedFileHandles, &cancelled,
2008-12-02 12:01:29 +01:00
tr("There are unsaved changes for project file %1.").arg(path));
if (cancelled)
return false;
// force instant reload
foreach (Core::IFile *fileHandle, allFileHandles) {
Core::IFile::ReloadBehavior reload = Core::IFile::ReloadAll;
fileHandle->modified(&reload);
}
}
return true;
}
void Qt4PriFileNode::changeFiles(const FileType fileType,
const QStringList &filePaths,
QStringList *notChanged,
ChangeType change)
{
if (filePaths.isEmpty())
return;
ProFileReader *reader = m_qt4ProFileNode->createProFileReader();
if (!reader->readProFile(m_qt4ProFileNode->path())) {
m_project->proFileParseError(tr("Error while parsing file %1. Giving up.").arg(m_projectFilePath));
delete reader;
return;
}
ProFile *includeFile = reader->proFileFor(m_projectFilePath);
2008-12-09 11:07:24 +01:00
if (!includeFile) {
m_project->proFileParseError(tr("Error while changing pro file %1.").arg(m_projectFilePath));
}
2008-12-02 12:01:29 +01:00
*notChanged = filePaths;
// Check for modified editors
if (!saveModifiedEditors(m_projectFilePath))
return;
// Check if file is readonly
ProEditorModel proModel;
proModel.setProFiles(QList<ProFile*>() << includeFile);
2008-12-02 12:01:29 +01:00
const QStringList vars = varNames(fileType);
QDir priFileDir = QDir(m_projectDir);
if (change == AddToProFile) {
// root item "<Global Scope>"
const QModelIndex root = proModel.index(0, 0);
// Check if variable item exists as child of root item
ProVariable *proVar = 0;
int row = 0;
for (; row < proModel.rowCount(root); ++row) {
if ((proVar = proModel.proVariable(root.child(row, 0)))) {
if (vars.contains(proVar->variable())
&& proVar->variableOperator() != ProVariable::RemoveOperator
&& proVar->variableOperator() != ProVariable::ReplaceOperator)
break;
else
proVar = 0;
}
}
if (!proVar) {
// Create & append new variable item
// TODO: This will always store e.g. a source file in SOURCES and not OBJECTIVE_SOURCES
proVar = new ProVariable(vars.first(), proModel.proBlock(root));
proVar->setVariableOperator(ProVariable::AddOperator);
proModel.insertItem(proVar, row, root);
}
const QModelIndex varIndex = root.child(row, 0);
foreach (const QString &filePath, filePaths) {
const QString &relativeFilePath = priFileDir.relativeFilePath(filePath);
proModel.insertItem(new ProValue(relativeFilePath, proVar),
proModel.rowCount(varIndex), varIndex);
notChanged->removeOne(filePath);
}
} else { // RemoveFromProFile
QList<QModelIndex> proVarIndexes = proModel.findVariables(vars);
QList<QModelIndex> toRemove;
QStringList relativeFilePaths;
foreach (const QString &absoluteFilePath, filePaths)
relativeFilePaths << priFileDir.relativeFilePath(absoluteFilePath);
foreach (const QModelIndex &proVarIndex, proVarIndexes) {
ProVariable *proVar = proModel.proVariable(proVarIndex);
if (proVar->variableOperator() != ProVariable::RemoveOperator
&& proVar->variableOperator() != ProVariable::ReplaceOperator) {
for (int row = proModel.rowCount(proVarIndex) - 1; row >= 0; --row) {
QModelIndex itemIndex = proModel.index(row, 0, proVarIndex);
ProItem *item = proModel.proItem(itemIndex);
if (item->kind() == ProItem::ValueKind) {
ProValue *val = static_cast<ProValue *>(item);
int index = relativeFilePaths.indexOf(val->value());
if (index != -1) {
toRemove.append(itemIndex);
notChanged->removeAt(index);
}
}
}
}
}
foreach (const QModelIndex &index, toRemove) {
proModel.removeItem(index);
}
}
// save file
save(includeFile);
delete reader;
2008-12-02 12:01:29 +01:00
}
void Qt4PriFileNode::save(ProFile *includeFile)
2008-12-02 12:01:29 +01:00
{
Core::ICore *core = Core::ICore::instance();
Core::FileManager *fileManager = core->fileManager();
QList<Core::IFile *> allFileHandles = fileManager->managedFiles(includeFile->fileName());
Core::IFile *modifiedFileHandle = 0;
2008-12-09 11:07:24 +01:00
foreach (Core::IFile *file, allFileHandles)
if (file->fileName() == includeFile->fileName())
modifiedFileHandle = file;
if (modifiedFileHandle)
fileManager->blockFileChange(modifiedFileHandle);
ProWriter pw;
const bool ok = pw.write(includeFile, includeFile->fileName());
Q_UNUSED(ok)
includeFile->setModified(false);
m_project->qt4ProjectManager()->notifyChanged(includeFile->fileName());
if (modifiedFileHandle)
fileManager->unblockFileChange(modifiedFileHandle);
2008-12-02 12:01:29 +01:00
Core::IFile::ReloadBehavior tempBehavior =
Core::IFile::ReloadAll;
foreach (Core::IFile *file, allFileHandles)
2008-12-02 12:01:29 +01:00
file->modified(&tempBehavior);
}
/*
Deletes all subprojects/files/virtual folders
*/
void Qt4PriFileNode::clear()
{
// delete files && folders && projects
if (!fileNodes().isEmpty())
removeFileNodes(fileNodes(), this);
if (!subProjectNodes().isEmpty())
removeProjectNodes(subProjectNodes());
if (!subFolderNodes().isEmpty())
removeFolderNodes(subFolderNodes(), this);
}
QStringList Qt4PriFileNode::varNames(FileType type)
{
QStringList vars;
switch (type) {
case ProjectExplorer::HeaderType:
vars << QLatin1String("HEADERS");
break;
case ProjectExplorer::SourceType:
vars << QLatin1String("SOURCES");
vars << QLatin1String("OBJECTIVE_SOURCES");
break;
case ProjectExplorer::ResourceType:
vars << QLatin1String("RESOURCES");
break;
case ProjectExplorer::FormType:
vars << QLatin1String("FORMS");
break;
default:
vars << QLatin1String("OTHER_FILES");
break;
}
return vars;
}
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/buildmanager.h>
2008-12-02 12:01:29 +01:00
/*!
\class Qt4ProFileNode
Implements abstract ProjectNode class
*/
Qt4ProFileNode::Qt4ProFileNode(Qt4Project *project,
const QString &filePath,
QObject *parent)
: Qt4PriFileNode(project, this, filePath),
2008-12-02 12:01:29 +01:00
// own stuff
m_projectType(InvalidProject),
m_isQBuildProject(false)
2008-12-02 12:01:29 +01:00
{
if (parent)
setParent(parent);
m_updateTimer.setInterval(100);
m_updateTimer.setSingleShot(true);
2008-12-02 12:01:29 +01:00
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*)));
2008-12-02 12:01:29 +01:00
}
Qt4ProFileNode::~Qt4ProFileNode()
{
}
void Qt4ProFileNode::buildStateChanged(ProjectExplorer::Project *project)
{
if (project == m_project && !ProjectExplorer::ProjectExplorerPlugin::instance()->buildManager()->isBuilding(m_project))
updateUiFiles(m_project->buildDirectory(m_project->activeBuildConfiguration()));
}
2008-12-02 12:01:29 +01:00
bool Qt4ProFileNode::hasTargets() const
{
return (projectType() == ApplicationTemplate) || (projectType() == LibraryTemplate);
}
Qt4ProjectType Qt4ProFileNode::projectType() const
{
return m_projectType;
}
QStringList Qt4ProFileNode::variableValue(const Qt4Variable var) const
{
return m_varValues.value(var);
}
void Qt4ProFileNode::scheduleUpdate()
{
m_updateTimer.start();
}
2008-12-02 12:01:29 +01:00
void Qt4ProFileNode::update()
{
ProFileReader *reader = createProFileReader();
if (!reader->readProFile(m_projectFilePath)) {
2008-12-02 12:01:29 +01:00
m_project->proFileParseError(tr("Error while parsing file %1. Giving up.").arg(m_projectFilePath));
delete reader;
2008-12-02 12:01:29 +01:00
invalidate();
return;
}
if (debug)
qDebug() << "Qt4ProFileNode - updating files for file " << m_projectFilePath;
#ifdef QTEXTENDED_QBUILD_SUPPORT
if (m_projectFilePath.endsWith("qbuild.pro")) {
m_isQBuildProject = true;
}
#endif
Qt4ProjectType projectType = InvalidProject;
switch (reader->templateType()) {
2008-12-02 12:01:29 +01:00
case ProFileEvaluator::TT_Unknown:
case ProFileEvaluator::TT_Application: {
projectType = ApplicationTemplate;
break;
}
case ProFileEvaluator::TT_Library: {
projectType = LibraryTemplate;
break;
}
case ProFileEvaluator::TT_Script: {
projectType = ScriptTemplate;
break;
}
case ProFileEvaluator::TT_Subdirs:
projectType = SubDirsTemplate;
break;
}
if (projectType != m_projectType) {
Qt4ProjectType oldType = m_projectType;
// probably all subfiles/projects have changed anyway ...
clear();
m_projectType = projectType;
foreach (NodesWatcher *watcher, watchers())
if (Qt4NodesWatcher *qt4Watcher = qobject_cast<Qt4NodesWatcher*>(watcher))
emit qt4Watcher->projectTypeChanged(this, oldType, projectType);
}
//
// Add/Remove pri files, sub projects
//
QList<ProjectNode*> existingProjectNodes = subProjectNodes();
QList<QString> newProjectFiles;
QHash<QString, ProFile*> includeFiles;
ProFile *fileForCurrentProject = 0;
{
if (projectType == SubDirsTemplate) {
foreach (const QString &subDirProject, subDirsPaths(reader))
2008-12-02 12:01:29 +01:00
newProjectFiles << subDirProject;
}
foreach (ProFile *includeFile, reader->includeFiles()) {
2008-12-02 12:01:29 +01:00
if (includeFile->fileName() == m_projectFilePath) { // this file
fileForCurrentProject = includeFile;
} else {
newProjectFiles << includeFile->fileName();
includeFiles.insert(includeFile->fileName(), includeFile);
}
}
}
qSort(existingProjectNodes.begin(), existingProjectNodes.end(),
sortNodesByPath);
qSort(newProjectFiles.begin(), newProjectFiles.end());
QList<ProjectNode*> toAdd;
QList<ProjectNode*> toRemove;
QList<ProjectNode*>::const_iterator existingNodeIter = existingProjectNodes.constBegin();
QList<QString>::const_iterator newProjectFileIter = newProjectFiles.constBegin();
while (existingNodeIter != existingProjectNodes.constEnd()
&& newProjectFileIter != newProjectFiles.constEnd()) {
if ((*existingNodeIter)->path() < *newProjectFileIter) {
toRemove << *existingNodeIter;
++existingNodeIter;
} else if ((*existingNodeIter)->path() > *newProjectFileIter) {
if (ProFile *file = includeFiles.value(*newProjectFileIter)) {
Qt4PriFileNode *priFileNode
= new Qt4PriFileNode(m_project, this,
2008-12-02 12:01:29 +01:00
*newProjectFileIter);
priFileNode->update(file, reader);
2008-12-02 12:01:29 +01:00
toAdd << priFileNode;
} else {
toAdd << createSubProFileNode(*newProjectFileIter);
}
++newProjectFileIter;
} else { // *existingNodeIter->path() == *newProjectFileIter
if (ProFile *file = includeFiles.value(*newProjectFileIter)) {
Qt4PriFileNode *priFileNode = static_cast<Qt4PriFileNode*>(*existingNodeIter);
priFileNode->update(file, reader);
2008-12-02 12:01:29 +01:00
}
++existingNodeIter;
++newProjectFileIter;
}
}
while (existingNodeIter != existingProjectNodes.constEnd()) {
toRemove << *existingNodeIter;
++existingNodeIter;
}
while (newProjectFileIter != newProjectFiles.constEnd()) {
if (ProFile *file = includeFiles.value(*newProjectFileIter)) {
Qt4PriFileNode *priFileNode
= new Qt4PriFileNode(m_project, this,
2008-12-02 12:01:29 +01:00
*newProjectFileIter);
priFileNode->update(file, reader);
2008-12-02 12:01:29 +01:00
toAdd << priFileNode;
} else {
toAdd << createSubProFileNode(*newProjectFileIter);
}
++newProjectFileIter;
}
if (!toRemove.isEmpty())
removeProjectNodes(toRemove);
if (!toAdd.isEmpty())
addProjectNodes(toAdd);
Qt4PriFileNode::update(fileForCurrentProject, reader);
2008-12-02 12:01:29 +01:00
// update other variables
QHash<Qt4Variable, QStringList> newVarValues;
newVarValues[CxxCompilerVar] << reader->value(QLatin1String("QMAKE_CXX"));
newVarValues[DefinesVar] = reader->values(QLatin1String("DEFINES"));
newVarValues[IncludePathVar] = includePaths(reader);
newVarValues[UiDirVar] = uiDirPaths(reader);
newVarValues[MocDirVar] = mocDirPaths(reader);
2008-12-02 12:01:29 +01:00
if (m_varValues != newVarValues) {
m_varValues = newVarValues;
foreach (NodesWatcher *watcher, watchers())
if (Qt4NodesWatcher *qt4Watcher = qobject_cast<Qt4NodesWatcher*>(watcher))
emit qt4Watcher->variablesChanged(this, m_varValues, newVarValues);
}
updateUiFiles(m_project->buildDirectory(m_project->activeBuildConfiguration()));
2008-12-02 12:01:29 +01:00
foreach (NodesWatcher *watcher, watchers())
if (Qt4NodesWatcher *qt4Watcher = qobject_cast<Qt4NodesWatcher*>(watcher))
emit qt4Watcher->proFileUpdated(this);
delete reader;
2008-12-02 12:01:29 +01:00
}
namespace {
// find all ui files in project
class FindUiFileNodesVisitor : public ProjectExplorer::NodesVisitor {
public:
void visitProjectNode(ProjectNode *projectNode)
{
visitFolderNode(projectNode);
}
void visitFolderNode(FolderNode *folderNode)
{
foreach (FileNode *fileNode, folderNode->fileNodes()) {
if (fileNode->fileType() == ProjectExplorer::FormType)
uiFileNodes << fileNode;
}
}
QList<FileNode*> uiFileNodes;
};
}
// 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 directory is changed
void Qt4ProFileNode::updateUiFiles(const QString &buildDirectory)
2008-12-02 12:01:29 +01:00
{
// Only those two project types can have ui files for us
2008-12-02 12:01:29 +01:00
if (m_projectType != ApplicationTemplate
&& m_projectType != LibraryTemplate)
return;
// Find all ui files
2008-12-02 12:01:29 +01:00
FindUiFileNodesVisitor uiFilesVisitor;
this->accept(&uiFilesVisitor);
const QList<FileNode*> uiFiles = uiFilesVisitor.uiFileNodes;
// Find the UiDir, there can only ever be one
QString uiDir = buildDirectory;
QStringList tmp = m_varValues[UiDirVar];
if (tmp.size() != 0)
uiDir = tmp.first();
2008-12-02 12:01:29 +01:00
// Collect all existing generated files
2008-12-02 12:01:29 +01:00
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 uiDir
2008-12-02 12:01:29 +01:00
QStringList newFilePaths;
foreach (FileNode *uiFile, uiFiles) {
const QString uiHeaderFilePath
= QString("%1/ui_%2.h").arg(uiDir, QFileInfo(uiFile->path()).completeBaseName());
if (QFileInfo(uiHeaderFilePath).exists())
newFilePaths << uiHeaderFilePath;
2008-12-02 12:01:29 +01:00
}
// Create a diff between those lists
2008-12-02 12:01:29 +01:00
QList<FileNode*> toRemove;
QList<FileNode*> toAdd;
// The list of files for which we call updateSourceFile
QStringList toUpdate;
2008-12-02 12:01:29 +01:00
qSort(newFilePaths);
qSort(existingFileNodes.begin(), existingFileNodes.end(), ProjectNode::sortNodesByPath);
QList<FileNode*>::const_iterator existingNodeIter = existingFileNodes.constBegin();
QList<QString>::const_iterator newPathIter = newFilePaths.constBegin();
while (existingNodeIter != existingFileNodes.constEnd()
&& newPathIter != newFilePaths.constEnd()) {
if ((*existingNodeIter)->path() < *newPathIter) {
toRemove << *existingNodeIter;
++existingNodeIter;
} else if ((*existingNodeIter)->path() > *newPathIter) {
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;
}
2008-12-02 12:01:29 +01:00
++existingNodeIter;
++newPathIter;
}
}
while (existingNodeIter != existingFileNodes.constEnd()) {
toRemove << *existingNodeIter;
++existingNodeIter;
}
while (newPathIter != newFilePaths.constEnd()) {
toAdd << new FileNode(*newPathIter, ProjectExplorer::HeaderType, true);
++newPathIter;
}
// Update project tree
2008-12-02 12:01:29 +01:00
if (!toRemove.isEmpty()) {
foreach (FileNode *file, toRemove)
m_uitimestamps.remove(file->path());
2008-12-02 12:01:29 +01:00
removeFileNodes(toRemove, this);
}
CppTools::CppModelManagerInterface *modelManager =
ExtensionSystem::PluginManager::instance()->getObject<CppTools::CppModelManagerInterface>();
2008-12-02 12:01:29 +01:00
if (!toAdd.isEmpty()) {
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();
}
}
}
2008-12-02 12:01:29 +01:00
addFileNodes(toAdd, this);
}
m_project->addUiFilesToCodeModel(toUpdate);
2008-12-02 12:01:29 +01:00
}
ProFileReader *Qt4PriFileNode::createProFileReader() const
2008-12-02 12:01:29 +01:00
{
ProFileReader *reader = new ProFileReader();
connect(reader, SIGNAL(errorFound(QString)),
m_project, SLOT(proFileParseError(QString)));
2008-12-02 12:01:29 +01:00
QtVersion *version = m_project->qtVersion(m_project->activeBuildConfiguration());
if (version->isValid())
2008-12-02 12:01:29 +01:00
reader->setQtVersion(version);
reader->setOutputDir(m_qt4ProFileNode->buildDir());
2008-12-02 12:01:29 +01:00
return reader;
}
Qt4ProFileNode *Qt4ProFileNode::createSubProFileNode(const QString &path)
{
Qt4ProFileNode *subProFileNode = new Qt4ProFileNode(m_project, path);
subProFileNode->update();
return subProFileNode;
}
QStringList Qt4ProFileNode::uiDirPaths(ProFileReader *reader) const
{
QStringList candidates = reader->absolutePathValues(QLatin1String("UI_DIR"),
buildDir(),
ProFileReader::ExistingPaths);
return candidates;
}
QStringList Qt4ProFileNode::mocDirPaths(ProFileReader *reader) const
{
QStringList candidates = reader->absolutePathValues(QLatin1String("MOC_DIR"),
buildDir(),
ProFileReader::ExistingPaths);
return candidates;
}
QStringList Qt4ProFileNode::includePaths(ProFileReader *reader) const
{
QStringList paths;
paths = reader->absolutePathValues(QLatin1String("INCLUDEPATH"),
m_projectDir,
ProFileReader::ExistingPaths);
paths << uiDirPaths(reader) << mocDirPaths(reader);
paths.removeDuplicates();
return paths;
}
QStringList Qt4ProFileNode::subDirsPaths(ProFileReader *reader) const
{
QStringList subProjectPaths;
const QStringList subDirVars = reader->values(QLatin1String("SUBDIRS"));
foreach (const QString &subDirVar, subDirVars) {
// Special case were subdir is just an identifier:
// "SUBDIR = subid
// subid.subdir = realdir"
// or
// "SUBDIR = subid
// subid.file = realdir/realfile.pro"
2008-12-02 12:01:29 +01:00
QString realDir;
QString realFile;
const QString subDirKey = subDirVar + QLatin1String(".subdir");
const QString subDirFileKey = subDirVar + QLatin1String(".file");
2008-12-02 12:01:29 +01:00
if (reader->contains(subDirKey))
realDir = QFileInfo(reader->value(subDirKey)).filePath();
else if (reader->contains(subDirFileKey))
realDir = QFileInfo(reader->value(subDirFileKey)).filePath();
else
2008-12-02 12:01:29 +01:00
realDir = subDirVar;
QFileInfo info(realDir);
if (!info.isAbsolute())
realDir = m_projectDir + "/" + realDir;
2008-12-02 12:01:29 +01:00
#ifdef QTEXTENDED_QBUILD_SUPPORT
// QBuild only uses project files named qbuild.pro, and subdirs are implied
if (m_isQBuildProject)
return qBuildSubDirsPaths(realDir);
#endif
if (info.suffix().isEmpty() || info.isDir()) {
realFile = QString("%1/%2.pro").arg(realDir, info.fileName());
if (!QFile::exists(realFile)) {
// parse directory for pro files - if there is only one, use that
QDir dir(realDir);
QStringList files = dir.entryList(QStringList() << "*.pro", QDir::Files);
if (files.size() == 1) {
realFile = QString("%1/%2").arg(realDir, files.first());
} else {
m_project->proFileParseError(tr("Could not find .pro file for sub dir '%1' in '%2'")
.arg(subDirVar).arg(realDir));
realFile = QString::null;
}
}
} else {
realFile = realDir;
}
if (!realFile.isEmpty() && !subProjectPaths.contains(realFile))
subProjectPaths << realFile;
}
return subProjectPaths;
}
QStringList Qt4ProFileNode::qBuildSubDirsPaths(const QString &scanDir) const
{
QStringList subProjectPaths;
// With QBuild we only look for project files named qbuild.pro
QString realFile = scanDir + "/qbuild.pro";
if (QFile::exists(realFile))
subProjectPaths << realFile;
// With QBuild 'subdirs' are implied
QDir dir(scanDir);
QStringList subDirs = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
foreach (QString subDir, subDirs) {
// 'tests' sub directories are an exception to the 'QBuild scans everything' rule.
// Tests are only build with the 'make test' command, in which case QBuild WILL look
// for a tests subdir and run everything in there.
if (subDir != "tests")
subProjectPaths += qBuildSubDirsPaths(scanDir + "/" + subDir);
}
return subProjectPaths;
}
QString Qt4PriFileNode::buildDir() const
2008-12-02 12:01:29 +01:00
{
const QDir srcDirRoot = QFileInfo(m_project->rootProjectNode()->path()).absoluteDir();
const QString relativeDir = srcDirRoot.relativeFilePath(m_projectDir);
return QDir(m_project->buildDirectory(m_project->activeBuildConfiguration())).absoluteFilePath(relativeDir);
}
/*
Sets project type to InvalidProject & deletes all subprojects/files/virtual folders
*/
void Qt4ProFileNode::invalidate()
{
if (m_projectType == InvalidProject)
return;
clear();
// change project type
Qt4ProjectType oldType = m_projectType;
m_projectType = InvalidProject;
foreach (NodesWatcher *watcher, watchers())
if (Qt4NodesWatcher *qt4Watcher = qobject_cast<Qt4NodesWatcher*>(watcher))
emit qt4Watcher->projectTypeChanged(this, oldType, InvalidProject);
}
2008-12-02 12:01:29 +01:00
Qt4NodesWatcher::Qt4NodesWatcher(QObject *parent)
: NodesWatcher(parent)
{
}