/*************************************************************************** ** ** This file is part of Qt Creator ** ** Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Qt Software Information (qt-info@nokia.com) ** ** ** Non-Open Source Usage ** ** 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 ** agreement between you and Nokia. ** ** GNU General Public License Usage ** ** 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 ** rights. These rights are described in the Nokia Qt GPL Exception ** version 1.3, included in the file GPL_EXCEPTION.txt in this package. ** ***************************************************************************/ #include "proeditormodel.h" #include "directorywatcher.h" #include "profilereader.h" #include "prowriter.h" #include "qt4nodes.h" #include "qt4project.h" #include "qt4projectmanager.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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) : ProjectNode(filePath), m_project(project), m_qt4ProFileNode(qt4ProFileNode), m_projectFilePath(QDir::fromNativeSeparators(filePath)), m_projectDir(QFileInfo(filePath).absolutePath()), m_fileWatcher(new FileWatcher(this)) { Q_ASSERT(project); setFolderName(QFileInfo(filePath).baseName()); setIcon(QIcon(":/qt4projectmanager/images/qt_project.png")); m_fileWatcher->addFile(filePath); connect(m_fileWatcher, SIGNAL(fileChanged(QString)), this, SLOT(scheduleUpdate())); } void Qt4PriFileNode::scheduleUpdate() { m_qt4ProFileNode->scheduleUpdate(); } void Qt4PriFileNode::update(ProFile *includeFile, ProFileReader *reader) { Q_ASSERT(includeFile); Q_ASSERT(reader); // add project file node if (m_fileNodes.isEmpty()) addFileNodes(QList() << new FileNode(m_projectFilePath, ProjectFileType, false), this); static QList fileTypes = (QList() << 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 existingFileNodes; foreach (FileNode *fileNode, fileNodes()) { if (fileNode->fileType() == type && !fileNode->isGenerated()) existingFileNodes << fileNode; } QList toRemove; QList toAdd; qSort(newFilePaths); qSort(existingFileNodes.begin(), existingFileNodes.end(), ProjectNode::sortNodesByPath); QList::const_iterator existingNodeIter = existingFileNodes.constBegin(); QList::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 Qt4PriFileNode::supportedActions() const { QList actions; const FolderNode *folderNode = this; const Qt4ProFileNode *proFileNode; while (!(proFileNode = qobject_cast(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) { Q_UNUSED(proFilePaths); return false; //changeIncludes(m_includeFile, proFilePaths, AddToProFile); } bool Qt4PriFileNode::removeSubProjects(const QStringList &proFilePaths) { Q_UNUSED(proFilePaths); return false; //changeIncludes(m_includeFile, proFilePaths, RemoveFromProFile); } 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()) 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 = ExtensionSystem::PluginManager::instance()->getObject(); Core::IVersionControl *versionControl = core->vcsManager()->findVersionControlForDirectory(dir); switch (Core::EditorManager::promptReadOnlyFile(path, versionControl, core->mainWindow(), false)) { case Core::EditorManager::RO_OpenVCS: if (!versionControl->vcsOpen(path)) { QMessageBox::warning(core->mainWindow(), tr("Failed!"), tr("Could not open the file for edit with SCC.")); return false; } break; case Core::EditorManager::RO_MakeWriteable: { 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.")); return false; } break; } case Core::EditorManager::RO_SaveAs: case Core::EditorManager::RO_Cancel: return false; } return true; } bool Qt4PriFileNode::saveModifiedEditors(const QString &path) { QList allFileHandles; QList modifiedFileHandles; Core::ICore *core = ExtensionSystem::PluginManager::instance()->getObject(); foreach (Core::IFile *file, core->fileManager()->managedFiles(path)) { allFileHandles << file; } foreach (Core::IEditor *editor, core->editorManager()->editorsForFileName(path)) { if (Core::IFile *editorFile = editor->file()) { if (editorFile->isModified()) modifiedFileHandles << editorFile; } } if (!modifiedFileHandles.isEmpty()) { bool cancelled; 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; 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); if (!includeFile) { m_project->proFileParseError(tr("Error while changing pro file %1.").arg(m_projectFilePath)); } *notChanged = filePaths; // Check for modified editors if (!saveModifiedEditors(m_projectFilePath)) return; // Check if file is readonly ProEditorModel proModel; proModel.setProFiles(QList() << includeFile); const QStringList vars = varNames(fileType); QDir priFileDir = QDir(m_projectDir); if (change == AddToProFile) { // root item "" 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 proVarIndexes = proModel.findVariables(vars); QList 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(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; } void Qt4PriFileNode::save(ProFile *includeFile) { Core::ICore *core = ExtensionSystem::PluginManager::instance()->getObject(); Core::FileManager *fileManager = core->fileManager(); QList allFileHandles = fileManager->managedFiles(includeFile->fileName()); Core::IFile *modifiedFileHandle = 0; 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); 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; } #include #include /*! \class Qt4ProFileNode Implements abstract ProjectNode class */ Qt4ProFileNode::Qt4ProFileNode(Qt4Project *project, const QString &filePath, QObject *parent) : Qt4PriFileNode(project, this, filePath), // own stuff m_projectType(InvalidProject), m_isQBuildProject(false) { if (parent) setParent(parent); m_updateTimer.setInterval(100); m_updateTimer.setSingleShot(true); 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() { } 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); } 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(); } 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(watcher)) emit qt4Watcher->projectTypeChanged(this, oldType, projectType); } // // Add/Remove pri files, sub projects // QList existingProjectNodes = subProjectNodes(); QList newProjectFiles; QHash 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 toAdd; QList toRemove; QList::const_iterator existingNodeIter = existingProjectNodes.constBegin(); QList::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, *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(*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, this, *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 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(watcher)) emit qt4Watcher->variablesChanged(this, m_varValues, newVarValues); } updateUiFiles(); foreach (NodesWatcher *watcher, watchers()) if (Qt4NodesWatcher *qt4Watcher = qobject_cast(watcher)) emit qt4Watcher->proFileUpdated(this); delete reader; } 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 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() { // 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 uiFiles = uiFilesVisitor.uiFileNodes; // 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(); // Collect all existing generated files QList 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 QStringList newFilePaths; 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 toRemove; QList toAdd; // The list of files for which we call updateSourceFile QStringList toUpdate; qSort(newFilePaths); qSort(existingFileNodes.begin(), existingFileNodes.end(), ProjectNode::sortNodesByPath); QList::const_iterator existingNodeIter = existingFileNodes.constBegin(); QList::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::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; } } while (existingNodeIter != existingFileNodes.constEnd()) { toRemove << *existingNodeIter; ++existingNodeIter; } while (newPathIter != newFilePaths.constEnd()) { toAdd << new FileNode(*newPathIter, ProjectExplorer::HeaderType, true); ++newPathIter; } // Update project tree if (!toRemove.isEmpty()) { foreach (FileNode *file, toRemove) m_uitimestamps.remove(file->path()); removeFileNodes(toRemove, this); } CppTools::CppModelManagerInterface *modelManager = ExtensionSystem::PluginManager::instance()->getObject(); 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(); } } } addFileNodes(toAdd, this); } modelManager->updateSourceFiles(toUpdate); } ProFileReader *Qt4PriFileNode::createProFileReader() const { ProFileReader *reader = new ProFileReader(); 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); } reader->setOutputDir(m_qt4ProFileNode->buildDir()); 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 = QFileInfo(reader->value(subDirKey)).filePath(); else realDir = subDirVar; QFileInfo info(realDir); if (!info.isAbsolute()) realDir = 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 Qt4PriFileNode::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(); // change project type Qt4ProjectType oldType = m_projectType; m_projectType = InvalidProject; foreach (NodesWatcher *watcher, watchers()) if (Qt4NodesWatcher *qt4Watcher = qobject_cast(watcher)) emit qt4Watcher->projectTypeChanged(this, oldType, InvalidProject); } Qt4NodesWatcher::Qt4NodesWatcher(QObject *parent) : NodesWatcher(parent) { }