2008-12-02 12:01:29 +01:00
|
|
|
/***************************************************************************
|
|
|
|
**
|
|
|
|
** This file is part of Qt Creator
|
|
|
|
**
|
|
|
|
** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
|
|
|
|
**
|
|
|
|
** Contact: Qt Software Information (qt-info@nokia.com)
|
|
|
|
**
|
2008-12-02 14:17:16 +01:00
|
|
|
**
|
|
|
|
** Non-Open Source Usage
|
|
|
|
**
|
2008-12-02 12:01:29 +01:00
|
|
|
** Licensees may use this file in accordance with the Qt Beta Version
|
|
|
|
** License Agreement, Agreement version 2.2 provided with the Software or,
|
|
|
|
** alternatively, in accordance with the terms contained in a written
|
2008-12-02 14:17:16 +01:00
|
|
|
** agreement between you and Nokia.
|
|
|
|
**
|
|
|
|
** GNU General Public License Usage
|
|
|
|
**
|
2008-12-02 12:01:29 +01:00
|
|
|
** Alternatively, this file may be used under the terms of the GNU General
|
|
|
|
** Public License versions 2.0 or 3.0 as published by the Free Software
|
|
|
|
** Foundation and appearing in the file LICENSE.GPL included in the packaging
|
|
|
|
** of this file. Please review the following information to ensure GNU
|
|
|
|
** General Public Licensing requirements will be met:
|
|
|
|
**
|
|
|
|
** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
|
|
|
|
** http://www.gnu.org/copyleft/gpl.html.
|
|
|
|
**
|
|
|
|
** In addition, as a special exception, Nokia gives you certain additional
|
2008-12-02 14:17:16 +01:00
|
|
|
** rights. These rights are described in the Nokia Qt GPL Exception
|
|
|
|
** version 1.2, included in the file GPL_EXCEPTION.txt in this package.
|
|
|
|
**
|
|
|
|
***************************************************************************/
|
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 "profilecache.h"
|
|
|
|
#include "profilereader.h"
|
|
|
|
#include "qt4nodes.h"
|
|
|
|
#include "qt4project.h"
|
|
|
|
#include "qt4projectmanager.h"
|
|
|
|
#include "directorywatcher.h"
|
|
|
|
|
|
|
|
#include <projectexplorer/nodesvisitor.h>
|
|
|
|
|
|
|
|
#include <coreplugin/editormanager/editormanager.h>
|
|
|
|
#include <coreplugin/filemanager.h>
|
|
|
|
#include <coreplugin/icore.h>
|
|
|
|
#include <coreplugin/iversioncontrol.h>
|
|
|
|
#include <coreplugin/vcsmanager.h>
|
|
|
|
|
|
|
|
#include <cpptools/cppmodelmanagerinterface.h>
|
|
|
|
|
|
|
|
#include <QtCore/QDebug>
|
|
|
|
#include <QtCore/QDir>
|
|
|
|
#include <QtCore/QFile>
|
|
|
|
#include <QtCore/QFileInfo>
|
|
|
|
#include <QtCore/QTimer>
|
|
|
|
#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,
|
|
|
|
const QString &filePath)
|
|
|
|
: ProjectNode(filePath),
|
|
|
|
m_core(project->qt4ProjectManager()->core()),
|
|
|
|
m_project(project),
|
|
|
|
m_projectFilePath(filePath),
|
|
|
|
m_projectDir(QFileInfo(filePath).absolutePath()),
|
|
|
|
m_includeFile(0),
|
|
|
|
m_saveTimer(new QTimer(this))
|
|
|
|
{
|
|
|
|
Q_ASSERT(project);
|
|
|
|
setFolderName(QFileInfo(filePath).baseName());
|
|
|
|
setIcon(QIcon(":/qt4projectmanager/images/qt_project.png"));
|
|
|
|
|
|
|
|
// m_saveTimer is used for the delayed saving of the pro file
|
|
|
|
// so that multiple insert/remove calls in one event loop run
|
|
|
|
// trigger just one save call.
|
|
|
|
m_saveTimer->setSingleShot(true);
|
|
|
|
connect(m_saveTimer, SIGNAL(timeout()), this, SLOT(save()));
|
|
|
|
}
|
|
|
|
|
|
|
|
void Qt4PriFileNode::update(ProFile *includeFile, ProFileReader *reader)
|
|
|
|
{
|
|
|
|
Q_ASSERT(includeFile);
|
|
|
|
Q_ASSERT(reader);
|
|
|
|
|
|
|
|
m_includeFile = includeFile;
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
if (m_includeFile) {
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return actions;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Qt4PriFileNode::addSubProjects(const QStringList &proFilePaths)
|
|
|
|
{
|
|
|
|
if (!m_includeFile)
|
|
|
|
return false;
|
|
|
|
return changeIncludes(m_includeFile, proFilePaths, AddToProFile);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Qt4PriFileNode::removeSubProjects(const QStringList &proFilePaths)
|
|
|
|
{
|
|
|
|
if (!m_includeFile)
|
|
|
|
return false;
|
|
|
|
return changeIncludes(m_includeFile, proFilePaths, RemoveFromProFile);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Qt4PriFileNode::addFiles(const FileType fileType, const QStringList &filePaths,
|
|
|
|
QStringList *notAdded)
|
|
|
|
{
|
|
|
|
if (!m_includeFile)
|
|
|
|
return false;
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
if (!m_includeFile)
|
|
|
|
return false;
|
|
|
|
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 (!m_includeFile || newFilePath.isEmpty())
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
enum ReadOnlyAction { RO_Cancel, RO_OpenSCC, RO_MakeWriteable };
|
|
|
|
}
|
|
|
|
|
|
|
|
static ReadOnlyAction promptReadOnly(const QString &fileName, bool hasSCC, QWidget *parent)
|
|
|
|
{
|
|
|
|
QMessageBox msgBox(QMessageBox::Question, QObject::tr("File is Read Only"),
|
|
|
|
QObject::tr("The file %1 is read only.").arg(fileName),
|
|
|
|
QMessageBox::Cancel, parent);
|
|
|
|
|
|
|
|
QPushButton *sccButton = 0;
|
|
|
|
if (hasSCC)
|
|
|
|
sccButton = msgBox.addButton(QObject::tr("Open with SCC"), QMessageBox::AcceptRole);
|
|
|
|
QPushButton *makeWritableButton = msgBox.addButton(QObject::tr("Make writable"), QMessageBox::AcceptRole);
|
|
|
|
if (hasSCC)
|
|
|
|
msgBox.setDefaultButton(sccButton);
|
|
|
|
else
|
|
|
|
msgBox.setDefaultButton(makeWritableButton);
|
|
|
|
msgBox.exec();
|
|
|
|
QAbstractButton *clickedButton = msgBox.clickedButton();
|
|
|
|
if (clickedButton == sccButton)
|
|
|
|
return RO_OpenSCC;
|
|
|
|
if (clickedButton == makeWritableButton)
|
|
|
|
return RO_MakeWriteable;
|
|
|
|
return RO_Cancel;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Qt4PriFileNode::priFileWritable(const QString &path)
|
|
|
|
{
|
|
|
|
const QString dir = QFileInfo(path).dir().path();
|
|
|
|
Core::IVersionControl *versionControl = m_core->vcsManager()->findVersionControlForDirectory(dir);
|
|
|
|
switch (promptReadOnly(path, versionControl != 0, m_core->mainWindow())) {
|
|
|
|
case RO_OpenSCC:
|
|
|
|
if (!versionControl->vcsOpen(path)) {
|
|
|
|
QMessageBox::warning(m_core->mainWindow(), tr("Failed!"), tr("Could not open the file for edit with SCC."));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case RO_MakeWriteable: {
|
|
|
|
const bool permsOk = QFile::setPermissions(path, QFile::permissions(path) | QFile::WriteUser);
|
|
|
|
if (!permsOk) {
|
|
|
|
QMessageBox::warning(m_core->mainWindow(), tr("Failed!"), tr("Could not set permissions to writable."));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case RO_Cancel: {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Qt4PriFileNode::saveModifiedEditors(const QString &path)
|
|
|
|
{
|
|
|
|
QList<Core::IFile*> allFileHandles;
|
|
|
|
QList<Core::IFile*> modifiedFileHandles;
|
|
|
|
|
|
|
|
foreach (Core::IFile *file, m_core->fileManager()->managedFiles(path)) {
|
|
|
|
allFileHandles << file;
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach (Core::IEditor *editor, m_core->editorManager()->editorsForFileName(path)) {
|
|
|
|
if (Core::IFile *editorFile = editor->file()) {
|
|
|
|
if (editorFile->isModified())
|
|
|
|
modifiedFileHandles << editorFile;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!modifiedFileHandles.isEmpty()) {
|
|
|
|
bool cancelled;
|
|
|
|
m_core->fileManager()->saveModifiedFiles(modifiedFileHandles, &cancelled,
|
|
|
|
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;
|
|
|
|
|
|
|
|
*notChanged = filePaths;
|
|
|
|
|
|
|
|
// Check for modified editors
|
|
|
|
if (!saveModifiedEditors(m_projectFilePath))
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Check if file is readonly
|
|
|
|
ProFileCache *cache = m_project->qt4ProjectManager()->proFileCache();
|
|
|
|
if (cache->fileInterface(m_projectFilePath)->isReadOnly() && !priFileWritable(m_projectFilePath))
|
|
|
|
return;
|
|
|
|
|
|
|
|
ProEditorModel proModel;
|
|
|
|
proModel.setProFiles(QList<ProFile*>() << m_includeFile);
|
|
|
|
|
|
|
|
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
|
|
|
|
if (!m_saveTimer->isActive())
|
|
|
|
m_saveTimer->start();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Qt4PriFileNode::save()
|
|
|
|
{
|
|
|
|
// Prevent any reload questions; just reload
|
|
|
|
Core::FileManager *fileManager = m_core->fileManager();
|
|
|
|
ProFileCache *cache = m_project->qt4ProjectManager()->proFileCache();
|
|
|
|
|
|
|
|
QList<Core::IFile *> allFileHandles = fileManager->managedFiles(m_includeFile->fileName());
|
|
|
|
Core::IFile *modifiedFileHandle = cache->fileInterface(m_includeFile->fileName());
|
|
|
|
|
|
|
|
fileManager->blockFileChange(modifiedFileHandle);
|
|
|
|
modifiedFileHandle->save();
|
|
|
|
fileManager->unblockFileChange(modifiedFileHandle);
|
|
|
|
|
|
|
|
Core::IFile::ReloadBehavior tempBehavior =
|
|
|
|
Core::IFile::ReloadAll;
|
|
|
|
foreach (Core::IFile *file, allFileHandles) {
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\class Qt4ProFileNode
|
|
|
|
Implements abstract ProjectNode class
|
|
|
|
*/
|
|
|
|
Qt4ProFileNode::Qt4ProFileNode(Qt4Project *project,
|
|
|
|
const QString &filePath,
|
|
|
|
QObject *parent)
|
|
|
|
: Qt4PriFileNode(project, filePath),
|
|
|
|
// own stuff
|
|
|
|
m_projectType(InvalidProject),
|
|
|
|
m_isQBuildProject(false),
|
|
|
|
m_cache(project->qt4ProjectManager()->proFileCache()),
|
|
|
|
m_dirWatcher(new DirectoryWatcher(this))
|
|
|
|
{
|
|
|
|
if (parent)
|
|
|
|
setParent(parent);
|
|
|
|
|
|
|
|
connect(m_dirWatcher, SIGNAL(directoryChanged(const QString&)),
|
|
|
|
this, SLOT(update()));
|
|
|
|
connect(m_dirWatcher, SIGNAL(fileChanged(const QString&)),
|
|
|
|
this, SLOT(fileChanged(const QString&)));
|
|
|
|
connect(m_project, SIGNAL(activeBuildConfigurationChanged()),
|
|
|
|
this, SLOT(update()));
|
|
|
|
}
|
|
|
|
|
|
|
|
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::update()
|
|
|
|
{
|
|
|
|
ProFileReader *reader = createProFileReader();
|
|
|
|
if (!reader->readProFile(m_projectFilePath)) {
|
|
|
|
m_project->proFileParseError(tr("Error while parsing file %1. Giving up.").arg(m_projectFilePath));
|
|
|
|
delete reader;
|
|
|
|
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()) {
|
|
|
|
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))
|
|
|
|
newProjectFiles << subDirProject;
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach (ProFile *includeFile, reader->includeFiles()) {
|
|
|
|
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,
|
|
|
|
*newProjectFileIter);
|
|
|
|
priFileNode->update(file, reader);
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
++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,
|
|
|
|
*newProjectFileIter);
|
|
|
|
priFileNode->update(file, reader);
|
|
|
|
toAdd << priFileNode;
|
|
|
|
} else {
|
|
|
|
toAdd << createSubProFileNode(*newProjectFileIter);
|
|
|
|
}
|
|
|
|
++newProjectFileIter;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!toRemove.isEmpty())
|
|
|
|
removeProjectNodes(toRemove);
|
|
|
|
if (!toAdd.isEmpty())
|
|
|
|
addProjectNodes(toAdd);
|
|
|
|
|
|
|
|
Qt4PriFileNode::update(fileForCurrentProject, reader);
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
updateGeneratedFiles();
|
|
|
|
m_project->qt4ProjectManager()->proFileCache()->updateDependencies(reader->includeFiles().toSet(), this);
|
|
|
|
|
|
|
|
delete reader;
|
|
|
|
foreach (NodesWatcher *watcher, watchers())
|
|
|
|
if (Qt4NodesWatcher *qt4Watcher = qobject_cast<Qt4NodesWatcher*>(watcher))
|
|
|
|
emit qt4Watcher->proFileUpdated(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Qt4ProFileNode::fileChanged(const QString &filePath)
|
|
|
|
{
|
|
|
|
CppTools::CppModelManagerInterface *modelManager =
|
|
|
|
m_core->pluginManager()->getObject<CppTools::CppModelManagerInterface>();
|
|
|
|
|
|
|
|
modelManager->updateSourceFiles(QStringList() << filePath);
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Adds ui_xxx.h files to tree and monitors them / the UI_DIR directory for changes
|
|
|
|
*/
|
|
|
|
void Qt4ProFileNode::updateGeneratedFiles()
|
|
|
|
{
|
|
|
|
if (m_projectType != ApplicationTemplate
|
|
|
|
&& m_projectType != LibraryTemplate)
|
|
|
|
return;
|
|
|
|
|
|
|
|
FindUiFileNodesVisitor uiFilesVisitor;
|
|
|
|
this->accept(&uiFilesVisitor);
|
|
|
|
const QList<FileNode*> uiFiles = uiFilesVisitor.uiFileNodes;
|
|
|
|
|
|
|
|
// monitor uic dir (only if there are .ui files)
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
QList<FileNode*> existingFileNodes;
|
|
|
|
foreach (FileNode *file, fileNodes()) {
|
|
|
|
if (file->isGenerated())
|
|
|
|
existingFileNodes << file;
|
|
|
|
}
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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, ProjectExplorer::HeaderType, true);
|
|
|
|
++newPathIter;
|
|
|
|
} else { // *existingNodeIter->path() == *newPathIter
|
|
|
|
++existingNodeIter;
|
|
|
|
++newPathIter;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
while (existingNodeIter != existingFileNodes.constEnd()) {
|
|
|
|
toRemove << *existingNodeIter;
|
|
|
|
++existingNodeIter;
|
|
|
|
}
|
|
|
|
while (newPathIter != newFilePaths.constEnd()) {
|
|
|
|
toAdd << new FileNode(*newPathIter, ProjectExplorer::HeaderType, true);
|
|
|
|
++newPathIter;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!toRemove.isEmpty()) {
|
|
|
|
foreach (FileNode *file, toRemove)
|
|
|
|
m_dirWatcher->removeFile(file->path());
|
|
|
|
removeFileNodes(toRemove, this);
|
|
|
|
}
|
|
|
|
if (!toAdd.isEmpty()) {
|
|
|
|
foreach (FileNode *file, toAdd)
|
|
|
|
m_dirWatcher->addFile(file->path());
|
|
|
|
addFileNodes(toAdd, this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ProFileReader *Qt4ProFileNode::createProFileReader() const
|
|
|
|
{
|
|
|
|
ProFileReader *reader = new ProFileReader(m_cache);
|
|
|
|
connect(reader, SIGNAL(errorFound(const QString &)),
|
|
|
|
m_project, SLOT(proFileParseError(const QString &)));
|
|
|
|
|
|
|
|
QtVersion *version = m_project->qtVersion(m_project->activeBuildConfiguration());
|
|
|
|
if (version->isValid()) {
|
|
|
|
reader->setQtVersion(version);
|
|
|
|
}
|
|
|
|
|
|
|
|
QHash<QString,QStringList> variables;
|
|
|
|
variables.insert(QLatin1String("OUT_PWD"), QStringList(buildDir()));
|
|
|
|
reader->addVariables(variables);
|
|
|
|
|
|
|
|
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"
|
|
|
|
|
|
|
|
QString realDir;
|
|
|
|
QString realFile;
|
|
|
|
const QString subDirKey = subDirVar + QLatin1String(".subdir");
|
|
|
|
if (reader->contains(subDirKey))
|
|
|
|
realDir = reader->value(subDirKey);
|
|
|
|
else
|
|
|
|
realDir = subDirVar;
|
|
|
|
QFileInfo info(realDir);
|
|
|
|
if (!info.isAbsolute())
|
|
|
|
realDir = QString("%1/%2").arg(m_projectDir, realDir);
|
|
|
|
|
|
|
|
#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 Qt4ProFileNode::buildDir() const
|
|
|
|
{
|
|
|
|
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();
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
|
|
|
|
|
|
|
|
foreach (NodesWatcher *watcher, watchers())
|
|
|
|
if (Qt4NodesWatcher *qt4Watcher = qobject_cast<Qt4NodesWatcher*>(watcher))
|
|
|
|
emit qt4Watcher->projectTypeChanged(this, oldType, InvalidProject);
|
|
|
|
}
|
|
|
|
|
|
|
|
Qt4NodesWatcher::Qt4NodesWatcher(QObject *parent)
|
|
|
|
: NodesWatcher(parent)
|
|
|
|
{
|
|
|
|
}
|