1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
11 ** Licensees holding valid Qt Commercial licenses may use this file in
12 ** accordance with the Qt Commercial License Agreement provided with the
13 ** Software or, alternatively, in accordance with the terms contained in
14 ** a written agreement between you and Nokia.
16 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** If you are unsure which license is appropriate for your use, please
26 ** contact the sales department at http://qt.nokia.com/contact.
28 **************************************************************************/
30 #include "profilereader.h"
31 #include "prowriter.h"
33 #include "qt4project.h"
34 #include "qt4projectmanager.h"
35 #include "qt4projectmanagerconstants.h"
36 #include "qtuicodemodelsupport.h"
37 #include "qt4buildconfiguration.h"
39 #include <projectexplorer/nodesvisitor.h>
41 #include <coreplugin/editormanager/editormanager.h>
42 #include <coreplugin/editormanager/ieditor.h>
43 #include <coreplugin/fileiconprovider.h>
44 #include <coreplugin/filemanager.h>
45 #include <coreplugin/icore.h>
46 #include <coreplugin/iversioncontrol.h>
47 #include <coreplugin/vcsmanager.h>
49 #include <cpptools/cppmodelmanagerinterface.h>
50 #include <cplusplus/CppDocument.h>
51 #include <extensionsystem/pluginmanager.h>
52 #include <projectexplorer/projectexplorer.h>
53 #include <projectexplorer/buildmanager.h>
55 #include <utils/qtcassert.h>
57 #include <QtCore/QDebug>
58 #include <QtCore/QDir>
59 #include <QtCore/QFile>
60 #include <QtCore/QFileInfo>
61 #include <QtCore/QCoreApplication>
62 #include <QtCore/QXmlStreamReader>
64 #include <QtGui/QPainter>
65 #include <QtGui/QMainWindow>
66 #include <QtGui/QMessageBox>
67 #include <QtGui/QPushButton>
68 #include <qtconcurrent/QtConcurrentTools>
70 // Static cached data in struct Qt4NodeStaticData providing information and icons
71 // for file types and the project. Do some magic via qAddPostRoutine()
72 // to make sure the icons do not outlive QApplication, triggering warnings on X11.
74 struct FileTypeDataStorage {
75 ProjectExplorer::FileType type;
80 static const FileTypeDataStorage fileTypeDataStorage[] = {
81 { ProjectExplorer::HeaderType,
82 QT_TRANSLATE_NOOP("Qt4ProjectManager::Internal::Qt4PriFileNode", "Headers"),
83 ":/qt4projectmanager/images/headers.png" },
84 { ProjectExplorer::SourceType,
85 QT_TRANSLATE_NOOP("Qt4ProjectManager::Internal::Qt4PriFileNode", "Sources"),
86 ":/qt4projectmanager/images/sources.png" },
87 { ProjectExplorer::FormType,
88 QT_TRANSLATE_NOOP("Qt4ProjectManager::Internal::Qt4PriFileNode", "Forms"),
89 ":/qt4projectmanager/images/forms.png" },
90 { ProjectExplorer::ResourceType,
91 QT_TRANSLATE_NOOP("Qt4ProjectManager::Internal::Qt4PriFileNode", "Resources"),
92 ":/qt4projectmanager/images/qt_qrc.png" },
93 { ProjectExplorer::UnknownFileType,
94 QT_TRANSLATE_NOOP("Qt4ProjectManager::Internal::Qt4PriFileNode", "Other files"),
95 ":/qt4projectmanager/images/unknown.png" }
98 struct Qt4NodeStaticData {
100 FileTypeData(ProjectExplorer::FileType t = ProjectExplorer::UnknownFileType,
101 const QString &tN = QString(),
102 const QIcon &i = QIcon()) :
103 type(t), typeName(tN), icon(i) { }
105 ProjectExplorer::FileType type;
110 QVector<FileTypeData> fileTypeData;
114 static void clearQt4NodeStaticData();
116 Q_GLOBAL_STATIC_WITH_INITIALIZER(Qt4NodeStaticData, qt4NodeStaticData, {
118 const unsigned count = sizeof(fileTypeDataStorage)/sizeof(FileTypeDataStorage);
119 x->fileTypeData.reserve(count);
121 // Overlay the SP_DirIcon with the custom icons
122 const QSize desiredSize = QSize(16, 16);
124 for (unsigned i = 0 ; i < count; i++) {
125 const QIcon overlayIcon = QIcon(QLatin1String(fileTypeDataStorage[i].icon));
126 const QPixmap folderPixmap =
127 Core::FileIconProvider::overlayIcon(QStyle::SP_DirIcon,
128 overlayIcon, desiredSize);
130 folderIcon.addPixmap(folderPixmap);
131 const QString desc = Qt4ProjectManager::Internal::Qt4PriFileNode::tr(fileTypeDataStorage[i].typeName);
132 x->fileTypeData.push_back(Qt4NodeStaticData::FileTypeData(fileTypeDataStorage[i].type,
136 const QIcon projectBaseIcon(QLatin1String(":/qt4projectmanager/images/qt_project.png"));
137 const QPixmap projectPixmap = Core::FileIconProvider::overlayIcon(QStyle::SP_DirIcon,
140 x->projectIcon.addPixmap(projectPixmap);
142 qAddPostRoutine(clearQt4NodeStaticData);
145 static void clearQt4NodeStaticData()
147 qt4NodeStaticData()->fileTypeData.clear();
148 qt4NodeStaticData()->projectIcon = QIcon();
154 // sorting helper function
155 bool sortProjectFilesByPath(ProFile *f1, ProFile *f2)
157 return f1->fileName() < f2->fileName();
161 namespace Qt4ProjectManager {
164 Qt4PriFile::Qt4PriFile(Qt4PriFileNode *qt4PriFile)
165 : IFile(qt4PriFile), m_priFile(qt4PriFile)
170 bool Qt4PriFile::save(const QString &fileName)
176 void Qt4PriFile::rename(const QString &newName)
183 QString Qt4PriFile::fileName() const
185 return m_priFile->path();
188 QString Qt4PriFile::defaultPath() const
193 QString Qt4PriFile::suggestedFileName() const
198 QString Qt4PriFile::mimeType() const
200 return Qt4ProjectManager::Constants::PROFILE_MIMETYPE;
203 bool Qt4PriFile::isModified() const
208 bool Qt4PriFile::isReadOnly() const
213 bool Qt4PriFile::isSaveAsAllowed() const
218 Core::IFile::ReloadBehavior Qt4PriFile::reloadBehavior(ChangeTrigger state, ChangeType type) const
222 return BehaviorSilent;
225 void Qt4PriFile::reload(ReloadFlag flag, ChangeType type)
229 if (type == TypePermissions)
231 m_priFile->scheduleUpdate();
235 \class Qt4PriFileNode
236 Implements abstract ProjectNode class
239 Qt4PriFileNode::Qt4PriFileNode(Qt4Project *project, Qt4ProFileNode* qt4ProFileNode, const QString &filePath)
240 : ProjectNode(filePath),
242 m_qt4ProFileNode(qt4ProFileNode),
243 m_projectFilePath(QDir::fromNativeSeparators(filePath)),
244 m_projectDir(QFileInfo(filePath).absolutePath())
247 m_qt4PriFile = new Qt4PriFile(this);
248 Core::ICore::instance()->fileManager()->addFile(m_qt4PriFile);
250 setDisplayName(QFileInfo(filePath).completeBaseName());
252 setIcon(qt4NodeStaticData()->projectIcon);
255 void Qt4PriFileNode::scheduleUpdate()
257 ProFileCacheManager::instance()->discardFile(m_projectFilePath);
258 m_qt4ProFileNode->scheduleUpdate();
263 QMap<QString, InternalNode*> subnodes;
265 ProjectExplorer::FileType type;
271 type = ProjectExplorer::UnknownFileType;
276 qDeleteAll(subnodes);
279 // Creates a tree structure from a list of absolute file paths.
280 // Empty directories are compressed into a single entry with a longer path.
290 // The method first creates a tree that looks like the directory structure, i.e.
295 // and afterwards calls compress() which merges directory nodes with single children, i.e. to
297 void create(const QString &projectDir, const QStringList &newFilePaths, ProjectExplorer::FileType type)
299 static const QChar separator = QChar('/');
300 const QString projectDirWithSeparator = projectDir + separator;
301 int projectDirWithSeparatorLength = projectDirWithSeparator.length();
302 foreach (const QString &file, newFilePaths) {
303 QString fileWithoutPrefix;
305 if (file.startsWith(projectDirWithSeparator)) {
307 fileWithoutPrefix = file.mid(projectDirWithSeparatorLength);
310 fileWithoutPrefix = file;
312 QStringList parts = fileWithoutPrefix.split(separator, QString::SkipEmptyParts);
314 if (!isRelative && parts.count() > 0)
315 parts[0].prepend(separator);
317 QStringListIterator it(parts);
318 InternalNode *currentNode = this;
319 QString path = (isRelative ? projectDirWithSeparator : "");
320 while (it.hasNext()) {
321 const QString &key = it.next();
322 if (it.hasNext()) { // key is directory
324 if (!currentNode->subnodes.contains(key)) {
325 InternalNode *val = new InternalNode;
327 val->fullPath = path;
328 currentNode->subnodes.insert(key, val);
331 currentNode = currentNode->subnodes.value(key);
334 } else { // key is filename
335 currentNode->files.append(file);
342 // Removes folder nodes with only a single sub folder in it
345 static const QChar separator = QDir::separator(); // it is used for the *keys* which will become display name
346 QMap<QString, InternalNode*> newSubnodes;
347 QMapIterator<QString, InternalNode*> i(subnodes);
348 while (i.hasNext()) {
350 i.value()->compress();
351 if (i.value()->files.isEmpty() && i.value()->subnodes.size() == 1) {
352 QString key = i.value()->subnodes.begin().key();
353 newSubnodes.insert(i.key()+separator+key, i.value()->subnodes.value(key));
354 i.value()->subnodes.clear();
357 newSubnodes.insert(i.key(), i.value());
360 subnodes = newSubnodes;
363 // Makes the projectNode's subtree below the given folder match this internal node's subtree
364 void updateSubFolders(Qt4PriFileNode *projectNode, ProjectExplorer::FolderNode *folder)
366 updateFiles(projectNode, folder, type);
369 QList<FolderNode *> existingFolderNodes;
370 foreach (FolderNode *node, folder->subFolderNodes()) {
371 if (node->nodeType() != ProjectNodeType)
372 existingFolderNodes << node;
375 QList<FolderNode *> foldersToRemove;
376 QList<FolderNode *> foldersToAdd;
377 typedef QPair<InternalNode *, FolderNode *> NodePair;
378 QList<NodePair> nodesToUpdate;
380 // newFolders is already sorted
381 qSort(existingFolderNodes.begin(), existingFolderNodes.end(), ProjectNode::sortFolderNodesByName);
383 QList<FolderNode*>::const_iterator existingNodeIter = existingFolderNodes.constBegin();
384 QMap<QString, InternalNode*>::const_iterator newNodeIter = subnodes.constBegin();;
385 while (existingNodeIter != existingFolderNodes.constEnd()
386 && newNodeIter != subnodes.constEnd()) {
387 if ((*existingNodeIter)->displayName() < newNodeIter.key()) {
388 foldersToRemove << *existingNodeIter;
390 } else if ((*existingNodeIter)->displayName() > newNodeIter.key()) {
391 FolderNode *newNode = new FolderNode(newNodeIter.value()->fullPath);
392 newNode->setDisplayName(newNodeIter.key());
393 if (!newNodeIter.value()->icon.isNull())
394 newNode->setIcon(newNodeIter.value()->icon);
395 foldersToAdd << newNode;
396 nodesToUpdate << NodePair(newNodeIter.value(), newNode);
398 } else { // *existingNodeIter->path() == *newPathIter
399 nodesToUpdate << NodePair(newNodeIter.value(), *existingNodeIter);
405 while (existingNodeIter != existingFolderNodes.constEnd()) {
406 foldersToRemove << *existingNodeIter;
409 while (newNodeIter != subnodes.constEnd()) {
410 FolderNode *newNode = new FolderNode(newNodeIter.value()->fullPath);
411 newNode->setDisplayName(newNodeIter.key());
412 if (!newNodeIter.value()->icon.isNull())
413 newNode->setIcon(newNodeIter.value()->icon);
414 foldersToAdd << newNode;
415 nodesToUpdate << NodePair(newNodeIter.value(), newNode);
419 if (!foldersToRemove.isEmpty())
420 projectNode->removeFolderNodes(foldersToRemove, folder);
421 if (!foldersToAdd.isEmpty())
422 projectNode->addFolderNodes(foldersToAdd, folder);
424 foreach (const NodePair &np, nodesToUpdate)
425 np.first->updateSubFolders(projectNode, np.second);
428 // Makes the folder's files match this internal node's file list
429 void updateFiles(Qt4PriFileNode *projectNode, FolderNode *folder, FileType type)
431 QList<FileNode*> existingFileNodes;
432 foreach (FileNode *fileNode, folder->fileNodes()) {
433 if (fileNode->fileType() == type && !fileNode->isGenerated())
434 existingFileNodes << fileNode;
437 QList<FileNode*> filesToRemove;
438 QList<FileNode*> filesToAdd;
441 qSort(existingFileNodes.begin(), existingFileNodes.end(), ProjectNode::sortNodesByPath);
443 QList<FileNode*>::const_iterator existingNodeIter = existingFileNodes.constBegin();
444 QList<QString>::const_iterator newPathIter = files.constBegin();
445 while (existingNodeIter != existingFileNodes.constEnd()
446 && newPathIter != files.constEnd()) {
447 if ((*existingNodeIter)->path() < *newPathIter) {
448 filesToRemove << *existingNodeIter;
450 } else if ((*existingNodeIter)->path() > *newPathIter) {
451 filesToAdd << new FileNode(*newPathIter, type, false);
453 } else { // *existingNodeIter->path() == *newPathIter
458 while (existingNodeIter != existingFileNodes.constEnd()) {
459 filesToRemove << *existingNodeIter;
462 while (newPathIter != files.constEnd()) {
463 filesToAdd << new FileNode(*newPathIter, type, false);
467 if (!filesToRemove.isEmpty())
468 projectNode->removeFileNodes(filesToRemove, folder);
469 if (!filesToAdd.isEmpty())
470 projectNode->addFileNodes(filesToAdd, folder);
475 QStringList Qt4PriFileNode::baseVPaths(ProFileReader *reader, const QString &projectDir)
480 result += reader->absolutePathValues("VPATH", projectDir);
481 result << projectDir; // QMAKE_ABSOLUTE_SOURCE_PATH
482 result += reader->absolutePathValues("DEPENDPATH", projectDir);
483 result.removeDuplicates();
487 QStringList Qt4PriFileNode::fullVPaths(const QStringList &baseVPaths, ProFileReader *reader, FileType type, const QString &qmakeVariable, const QString &projectDir)
492 if (type == ProjectExplorer::SourceType)
493 vPaths = reader->absolutePathValues("VPATH_" + qmakeVariable, projectDir);
494 vPaths += baseVPaths;
495 if (type == ProjectExplorer::HeaderType)
496 vPaths += reader->absolutePathValues("INCLUDEPATH", projectDir);
497 vPaths.removeDuplicates();
502 void Qt4PriFileNode::update(ProFile *includeFileExact, ProFileReader *readerExact, ProFile *includeFileCumlative, ProFileReader *readerCumulative)
504 // add project file node
505 if (m_fileNodes.isEmpty())
506 addFileNodes(QList<FileNode*>() << new FileNode(m_projectFilePath, ProjectFileType, false), this);
508 const QString &projectDir = m_qt4ProFileNode->m_projectDir;
510 QStringList baseVPathsExact = baseVPaths(readerExact, projectDir);
511 QStringList baseVPathsCumulative = baseVPaths(readerCumulative, projectDir);
513 const QVector<Qt4NodeStaticData::FileTypeData> &fileTypes = qt4NodeStaticData()->fileTypeData;
515 InternalNode contents;
518 for (int i = 0; i < fileTypes.size(); ++i) {
519 FileType type = fileTypes.at(i).type;
520 const QStringList qmakeVariables = varNames(type);
522 QStringList newFilePaths;
523 foreach (const QString &qmakeVariable, qmakeVariables) {
524 QStringList vPathsExact = fullVPaths(baseVPathsExact, readerExact, type, qmakeVariable, projectDir);
525 QStringList vPathsCumulative = fullVPaths(baseVPathsCumulative, readerCumulative, type, qmakeVariable, projectDir);
528 newFilePaths += readerExact->absoluteFileValues(qmakeVariable, projectDir, vPathsExact, includeFileExact);
529 if (readerCumulative)
530 newFilePaths += readerCumulative->absoluteFileValues(qmakeVariable, projectDir, vPathsCumulative, includeFileCumlative);
534 if (!newFilePaths.isEmpty()) {
535 newFilePaths.removeDuplicates();
536 InternalNode *subfolder = new InternalNode;
537 subfolder->type = type;
538 subfolder->icon = fileTypes.at(i).icon;
539 subfolder->fullPath = m_projectDir + "/#" + fileTypes.at(i).typeName;
540 contents.subnodes.insert(fileTypes.at(i).typeName, subfolder);
541 // create the hierarchy with subdirectories
542 subfolder->create(m_projectDir, newFilePaths, type);
545 contents.updateSubFolders(this, this);
548 QList<ProjectNode::ProjectAction> Qt4PriFileNode::supportedActions(Node *node) const
550 QList<ProjectAction> actions;
552 const FolderNode *folderNode = this;
553 const Qt4ProFileNode *proFileNode;
554 while (!(proFileNode = qobject_cast<const Qt4ProFileNode*>(folderNode)))
555 folderNode = folderNode->parentFolderNode();
556 Q_ASSERT(proFileNode);
558 switch (proFileNode->projectType()) {
559 case ApplicationTemplate:
560 case LibraryTemplate:
561 actions << AddFile << RemoveFile;
563 case SubDirsTemplate:
564 actions << AddSubProject << RemoveSubProject;
570 FileNode *fileNode = qobject_cast<FileNode *>(node);
571 if (fileNode && fileNode->fileType() != ProjectExplorer::ProjectFileType)
577 bool Qt4PriFileNode::addSubProjects(const QStringList &proFilePaths)
579 Q_UNUSED(proFilePaths)
580 return false; //changeIncludes(m_includeFile, proFilePaths, AddToProFile);
583 bool Qt4PriFileNode::removeSubProjects(const QStringList &proFilePaths)
585 Q_UNUSED(proFilePaths)
586 return false; //changeIncludes(m_includeFile, proFilePaths, RemoveFromProFile);
589 bool Qt4PriFileNode::addFiles(const FileType fileType, const QStringList &filePaths,
590 QStringList *notAdded)
592 // If a file is already referenced in the .pro file then we don't add them.
593 // That ignores scopes and which variable was used to reference the file
594 // So it's obviously a bit limited, but in those cases you need to edit the
595 // project files manually anyway.
597 ProjectExplorer::FindAllFilesVisitor visitor;
599 const QStringList &allFiles = visitor.filePaths();
601 QStringList qrcFiles; // the list of qrc files referenced from ui files
602 if (fileType == ProjectExplorer::FormType) {
603 foreach (const QString &formFile, filePaths) {
604 QStringList resourceFiles = formResources(formFile);
605 foreach (const QString &resourceFile, resourceFiles)
606 if (!qrcFiles.contains(resourceFile))
607 qrcFiles.append(resourceFile);
611 QStringList uniqueQrcFiles;
612 foreach (const QString &file, qrcFiles) {
613 if (!allFiles.contains(file))
614 uniqueQrcFiles.append(file);
617 QStringList uniqueFilePaths;
618 foreach (const QString &file, filePaths) {
619 if (!allFiles.contains(file))
620 uniqueFilePaths.append(file);
623 QStringList failedFiles;
624 changeFiles(fileType, uniqueFilePaths, &failedFiles, AddToProFile);
626 *notAdded = failedFiles;
627 changeFiles(ProjectExplorer::ResourceType, uniqueQrcFiles, &failedFiles, AddToProFile);
629 *notAdded += failedFiles;
630 return failedFiles.isEmpty();
633 bool Qt4PriFileNode::removeFiles(const FileType fileType, const QStringList &filePaths,
634 QStringList *notRemoved)
636 QStringList failedFiles;
637 changeFiles(fileType, filePaths, &failedFiles, RemoveFromProFile);
639 *notRemoved = failedFiles;
640 return failedFiles.isEmpty();
643 bool Qt4PriFileNode::renameFile(const FileType fileType, const QString &filePath,
644 const QString &newFilePath)
646 if (newFilePath.isEmpty())
650 changeFiles(fileType, QStringList() << filePath, &dummy, RemoveFromProFile);
651 if (!dummy.isEmpty())
653 changeFiles(fileType, QStringList() << newFilePath, &dummy, AddToProFile);
654 if (!dummy.isEmpty())
659 bool Qt4PriFileNode::changeIncludes(ProFile *includeFile, const QStringList &proFilePaths,
662 Q_UNUSED(includeFile)
663 Q_UNUSED(proFilePaths)
669 bool Qt4PriFileNode::priFileWritable(const QString &path)
671 const QString dir = QFileInfo(path).dir().path();
672 Core::ICore *core = Core::ICore::instance();
673 Core::IVersionControl *versionControl = core->vcsManager()->findVersionControlForDirectory(dir);
674 switch (Core::EditorManager::promptReadOnlyFile(path, versionControl, core->mainWindow(), false)) {
675 case Core::EditorManager::RO_OpenVCS:
676 if (!versionControl->vcsOpen(path)) {
677 QMessageBox::warning(core->mainWindow(), tr("Failed!"), tr("Could not open the file for edit with SCC."));
681 case Core::EditorManager::RO_MakeWriteable: {
682 const bool permsOk = QFile::setPermissions(path, QFile::permissions(path) | QFile::WriteUser);
684 QMessageBox::warning(core->mainWindow(), tr("Failed!"), tr("Could not set permissions to writable."));
689 case Core::EditorManager::RO_SaveAs:
690 case Core::EditorManager::RO_Cancel:
696 bool Qt4PriFileNode::saveModifiedEditors()
698 QList<Core::IFile*> modifiedFileHandles;
700 Core::ICore *core = Core::ICore::instance();
702 foreach (Core::IEditor *editor, core->editorManager()->editorsForFileName(m_projectFilePath)) {
703 if (Core::IFile *editorFile = editor->file()) {
704 if (editorFile->isModified())
705 modifiedFileHandles << editorFile;
709 if (!modifiedFileHandles.isEmpty()) {
711 core->fileManager()->saveModifiedFiles(modifiedFileHandles, &cancelled,
712 tr("There are unsaved changes for project file %1.").arg(m_projectFilePath));
715 // force instant reload of ourselves
716 ProFileCacheManager::instance()->discardFile(m_projectFilePath);
717 m_project->qt4ProjectManager()->notifyChanged(m_projectFilePath);
722 QStringList Qt4PriFileNode::formResources(const QString &formFile) const
724 QStringList resourceFiles;
725 QFile file(formFile);
726 file.open(QIODevice::ReadOnly);
727 QXmlStreamReader reader(&file);
729 QFileInfo fi(formFile);
730 QDir formDir = fi.absoluteDir();
731 while (!reader.atEnd()) {
733 if (reader.isStartElement()) {
734 if (reader.name() == QLatin1String("iconset")) {
735 const QXmlStreamAttributes attributes = reader.attributes();
736 if (attributes.hasAttribute(QLatin1String("resource")))
737 resourceFiles.append(QDir::cleanPath(formDir.absoluteFilePath(
738 attributes.value(QLatin1String("resource")).toString())));
739 } else if (reader.name() == QLatin1String("include")) {
740 const QXmlStreamAttributes attributes = reader.attributes();
741 if (attributes.hasAttribute(QLatin1String("location")))
742 resourceFiles.append(QDir::cleanPath(formDir.absoluteFilePath(
743 attributes.value(QLatin1String("location")).toString())));
749 if (reader.hasError())
750 qWarning() << "Could not read form file:" << formFile;
752 return resourceFiles;
755 void Qt4PriFileNode::changeFiles(const FileType fileType,
756 const QStringList &filePaths,
757 QStringList *notChanged,
760 if (filePaths.isEmpty())
763 *notChanged = filePaths;
765 // Check for modified editors
766 if (!saveModifiedEditors())
769 // Ensure that the file is not read only
770 QFileInfo fi(m_projectFilePath);
771 if (!fi.isWritable()) {
772 // Try via vcs manager
773 Core::VCSManager *vcsManager = Core::ICore::instance()->vcsManager();
774 Core::IVersionControl *versionControl = vcsManager->findVersionControlForDirectory(fi.absolutePath());
775 if (!versionControl || versionControl->vcsOpen(m_projectFilePath)) {
776 bool makeWritable = QFile::setPermissions(m_projectFilePath, fi.permissions() | QFile::WriteUser);
778 QMessageBox::warning(Core::ICore::instance()->mainWindow(),
780 tr("Could not write project file %1.").arg(m_projectFilePath));
787 ProFile *includeFile;
791 QFile qfile(m_projectFilePath);
792 if (qfile.open(QIODevice::ReadOnly | QIODevice::Text)) {
793 contents = QString::fromLocal8Bit(qfile.readAll());
795 lines = contents.split(QLatin1Char('\n'));
796 while (!lines.isEmpty() && lines.last().isEmpty())
799 m_project->proFileParseError(tr("Error while reading PRO file %1: %2")
800 .arg(m_projectFilePath, qfile.errorString()));
805 ProMessageHandler handler;
806 ProFileParser parser(0, &handler);
807 includeFile = parser.parsedProFile(m_projectFilePath, false, contents);
810 const QStringList vars = varNames(fileType);
811 QDir priFileDir = QDir(m_qt4ProFileNode->m_projectDir);
813 if (change == AddToProFile) {
814 // Use the first variable for adding.
815 // Yes, that's broken for adding objective c sources or other stuff.
816 ProWriter::addFiles(includeFile, &lines, priFileDir, filePaths, vars.first());
818 } else { // RemoveFromProFile
819 *notChanged = ProWriter::removeFiles(includeFile, &lines, priFileDir, filePaths, vars);
826 // We are saving twice in a very short timeframe, once the editor and once the ProFile.
827 // So the modification time might not change between those two saves.
828 // We manually tell each editor to reload it's file.
829 // (The .pro files are notified by the file system watcher.)
830 foreach (Core::IEditor *editor, Core::ICore::instance()->editorManager()->editorsForFileName(m_projectFilePath)) {
831 if (Core::IFile *editorFile = editor->file()) {
832 editorFile->reload(Core::IFile::FlagReload, Core::IFile::TypeContents);
836 includeFile->deref();
839 void Qt4PriFileNode::save(const QStringList &lines)
841 QFile qfile(m_projectFilePath);
842 if (qfile.open(QIODevice::WriteOnly | QIODevice::Text)) {
843 foreach (const QString &str, lines) {
844 qfile.write(str.toLocal8Bit());
850 m_project->qt4ProjectManager()->notifyChanged(m_projectFilePath);
854 Deletes all subprojects/files/virtual folders
856 void Qt4PriFileNode::clear()
858 // delete files && folders && projects
859 removeFileNodes(fileNodes(), this);
860 removeProjectNodes(subProjectNodes());
861 removeFolderNodes(subFolderNodes(), this);
864 QStringList Qt4PriFileNode::varNames(FileType type)
868 case ProjectExplorer::HeaderType:
869 vars << QLatin1String("HEADERS");
870 vars << QLatin1String("OBJECTIVE_HEADERS");
872 case ProjectExplorer::SourceType:
873 vars << QLatin1String("SOURCES");
874 vars << QLatin1String("OBJECTIVE_SOURCES");
875 vars << QLatin1String("LEXSOURCES");
876 vars << QLatin1String("YACCSOURCES");
878 case ProjectExplorer::ResourceType:
879 vars << QLatin1String("RESOURCES");
881 case ProjectExplorer::FormType:
882 vars << QLatin1String("FORMS");
885 vars << QLatin1String("OTHER_FILES");
891 Qt4ProFileNode *Qt4ProFileNode::findProFileFor(const QString &fileName)
893 if (fileName == path())
895 foreach (ProjectNode *pn, subProjectNodes())
896 if (Qt4ProFileNode *qt4ProFileNode = qobject_cast<Qt4ProFileNode *>(pn))
897 if (Qt4ProFileNode *result = qt4ProFileNode->findProFileFor(fileName))
902 TargetInformation Qt4ProFileNode::targetInformation(const QString &fileName)
904 TargetInformation result;
905 Qt4ProFileNode *qt4ProFileNode = findProFileFor(fileName);
909 return qt4ProFileNode->targetInformation();
913 \class Qt4ProFileNode
914 Implements abstract ProjectNode class
916 Qt4ProFileNode::Qt4ProFileNode(Qt4Project *project,
917 const QString &filePath,
919 : Qt4PriFileNode(project, this, filePath),
920 m_projectType(InvalidProject),
922 m_readerCumulative(0)
928 connect(ProjectExplorer::ProjectExplorerPlugin::instance()->buildManager(), SIGNAL(buildStateChanged(ProjectExplorer::Project*)),
929 this, SLOT(buildStateChanged(ProjectExplorer::Project*)));
931 connect(&m_parseFutureWatcher, SIGNAL(finished()),
932 this, SLOT(applyAsyncEvaluate()));
935 Qt4ProFileNode::~Qt4ProFileNode()
937 CppTools::CppModelManagerInterface *modelManager
938 = ExtensionSystem::PluginManager::instance()->getObject<CppTools::CppModelManagerInterface>();
939 QMap<QString, Qt4UiCodeModelSupport *>::const_iterator it, end;
940 end = m_uiCodeModelSupport.constEnd();
941 for (it = m_uiCodeModelSupport.constBegin(); it != end; ++it) {
942 modelManager->removeEditorSupport(it.value());
945 m_parseFutureWatcher.waitForFinished();
947 // Oh we need to clean up
948 applyEvaluate(true, true);
949 m_project->decrementPendingEvaluateFutures();
953 bool Qt4ProFileNode::isParent(Qt4ProFileNode *node)
955 while ((node = qobject_cast<Qt4ProFileNode *>(node->parentFolderNode()))) {
962 void Qt4ProFileNode::buildStateChanged(ProjectExplorer::Project *project)
964 if (project == m_project && !ProjectExplorer::ProjectExplorerPlugin::instance()->buildManager()->isBuilding(m_project)) {
965 QStringList filesToUpdate = updateUiFiles();
966 updateCodeModelSupportFromBuild(filesToUpdate);
970 bool Qt4ProFileNode::hasBuildTargets() const
972 return (projectType() == ApplicationTemplate) || (projectType() == LibraryTemplate);
975 Qt4ProjectType Qt4ProFileNode::projectType() const
977 return m_projectType;
980 QStringList Qt4ProFileNode::variableValue(const Qt4Variable var) const
982 return m_varValues.value(var);
985 void Qt4ProFileNode::scheduleUpdate()
987 m_project->scheduleAsyncUpdate(this);
990 void Qt4ProFileNode::asyncUpdate()
992 m_project->incrementPendingEvaluateFutures();
994 QFuture<bool> future = QtConcurrent::run(&Qt4ProFileNode::asyncEvaluate, this);
995 m_parseFutureWatcher.setFuture(future);
998 void Qt4ProFileNode::update()
1001 bool parserError = evaluate();
1002 applyEvaluate(!parserError, false);
1005 void Qt4ProFileNode::setupReader()
1007 Q_ASSERT(!m_readerExact);
1008 Q_ASSERT(!m_readerCumulative);
1010 m_readerExact = m_project->createProFileReader(this);
1011 m_readerExact->setCumulative(false);
1013 m_readerCumulative = m_project->createProFileReader(this);
1015 // Find out what flags we pass on to qmake
1016 QStringList addedUserConfigArguments;
1017 QStringList removedUserConfigArguments;
1018 m_project->activeTarget()->activeBuildConfiguration()->getConfigCommandLineArguments(&addedUserConfigArguments, &removedUserConfigArguments);
1020 m_readerExact->setConfigCommandLineArguments(addedUserConfigArguments, removedUserConfigArguments);
1021 m_readerCumulative->setConfigCommandLineArguments(addedUserConfigArguments, removedUserConfigArguments);
1024 bool Qt4ProFileNode::evaluate()
1026 bool parserError = false;
1027 if (ProFile *pro = m_readerExact->parsedProFile(m_projectFilePath)) {
1028 if (!m_readerExact->accept(pro, ProFileEvaluator::LoadAll))
1030 if (!m_readerCumulative->accept(pro, ProFileEvaluator::LoadPreFiles))
1039 void Qt4ProFileNode::asyncEvaluate(QFutureInterface<bool> &fi)
1041 bool parserError = evaluate();
1042 fi.reportResult(!parserError);
1045 void Qt4ProFileNode::applyAsyncEvaluate()
1047 applyEvaluate(m_parseFutureWatcher.result(), true);
1048 m_project->decrementPendingEvaluateFutures();
1051 static Qt4ProjectType proFileTemplateTypeToProjectType(ProFileEvaluator::TemplateType type)
1054 case ProFileEvaluator::TT_Unknown:
1055 case ProFileEvaluator::TT_Application:
1056 return ApplicationTemplate;
1057 case ProFileEvaluator::TT_Library:
1058 return LibraryTemplate;
1059 case ProFileEvaluator::TT_Script:
1060 return ScriptTemplate;
1061 case ProFileEvaluator::TT_Subdirs:
1062 return SubDirsTemplate;
1064 return InvalidProject;
1068 void Qt4ProFileNode::applyEvaluate(bool parseResult, bool async)
1072 if (!parseResult || m_project->wasEvaluateCanceled()) {
1073 m_project->destroyProFileReader(m_readerExact);
1074 if (m_readerCumulative)
1075 m_project->destroyProFileReader(m_readerCumulative);
1076 m_readerExact = m_readerCumulative = 0;
1078 m_project->proFileParseError(tr("Error while parsing file %1. Giving up.").arg(m_projectFilePath));
1085 qDebug() << "Qt4ProFileNode - updating files for file " << m_projectFilePath;
1087 Qt4ProjectType projectType = InvalidProject;
1088 // Check that both are the same if we have both
1089 if (m_readerExact->templateType() != m_readerCumulative->templateType()) {
1090 // Now what. The only thing which could be reasonable is that someone
1091 // changes between template app and library.
1092 // Well, we are conservative here for now.
1093 // Let's wait until someone complains and look at what they are doing.
1094 m_project->destroyProFileReader(m_readerCumulative);
1095 m_readerCumulative = 0;
1098 projectType = proFileTemplateTypeToProjectType(m_readerExact->templateType());
1100 if (projectType != m_projectType) {
1101 Qt4ProjectType oldType = m_projectType;
1102 // probably all subfiles/projects have changed anyway ...
1104 m_projectType = projectType;
1105 // really emit here? or at the end? Noone is connected to this signal at the moment
1106 // so we kind of can ignore that question for now
1107 foreach (NodesWatcher *watcher, watchers())
1108 if (Qt4NodesWatcher *qt4Watcher = qobject_cast<Qt4NodesWatcher*>(watcher))
1109 emit qt4Watcher->projectTypeChanged(this, oldType, projectType);
1113 // Add/Remove pri files, sub projects
1116 QList<ProjectNode*> existingProjectNodes = subProjectNodes();
1118 QStringList newProjectFilesExact;
1119 QHash<QString, ProFile*> includeFilesExact;
1120 ProFile *fileForCurrentProjectExact = 0;
1121 if (m_projectType == SubDirsTemplate)
1122 newProjectFilesExact = subDirsPaths(m_readerExact);
1123 foreach (ProFile *includeFile, m_readerExact->includeFiles()) {
1124 if (includeFile->fileName() == m_projectFilePath) { // this file
1125 fileForCurrentProjectExact = includeFile;
1127 newProjectFilesExact << includeFile->fileName();
1128 includeFilesExact.insert(includeFile->fileName(), includeFile);
1133 QStringList newProjectFilesCumlative;
1134 QHash<QString, ProFile*> includeFilesCumlative;
1135 ProFile *fileForCurrentProjectCumlative = 0;
1136 if (m_readerCumulative) {
1137 if (m_projectType == SubDirsTemplate)
1138 newProjectFilesCumlative = subDirsPaths(m_readerCumulative);
1139 foreach (ProFile *includeFile, m_readerCumulative->includeFiles()) {
1140 if (includeFile->fileName() == m_projectFilePath) {
1141 fileForCurrentProjectCumlative = includeFile;
1143 newProjectFilesCumlative << includeFile->fileName();
1144 includeFilesCumlative.insert(includeFile->fileName(), includeFile);
1149 qSort(existingProjectNodes.begin(), existingProjectNodes.end(),
1151 qSort(newProjectFilesExact);
1152 qSort(newProjectFilesCumlative);
1154 QList<ProjectNode*> toAdd;
1155 QList<ProjectNode*> toRemove;
1157 QList<ProjectNode*>::const_iterator existingIt = existingProjectNodes.constBegin();
1158 QStringList::const_iterator newExactIt = newProjectFilesExact.constBegin();
1159 QStringList::const_iterator newCumlativeIt = newProjectFilesCumlative.constBegin();
1162 bool existingAtEnd = (existingIt == existingProjectNodes.constEnd());
1163 bool newExactAtEnd = (newExactIt == newProjectFilesExact.constEnd());
1164 bool newCumlativeAtEnd = (newCumlativeIt == newProjectFilesCumlative.constEnd());
1166 if (existingAtEnd && newExactAtEnd && newCumlativeAtEnd)
1167 break; // we are done, hurray!
1169 // So this is one giant loop comparing 3 lists at once and sorting the comparision
1170 // into mainly 2 buckets: toAdd and toRemove
1171 // We need to distinguish between nodes that came from exact and cumalative
1172 // parsing, since the update call is diffrent for them
1173 // I believe this code to be correct, be careful in changing it
1177 && (newExactAtEnd || (*existingIt)->path() < *newExactIt)
1178 && (newCumlativeAtEnd || (*existingIt)->path() < *newCumlativeIt)) {
1180 toRemove << *existingIt;
1182 } else if(! newExactAtEnd
1183 && (existingAtEnd || *newExactIt < (*existingIt)->path())
1184 && (newCumlativeAtEnd || *newExactIt < *newCumlativeIt)) {
1185 // Mark node from exact for adding
1186 nodeToAdd = *newExactIt;
1188 } else if (! newCumlativeAtEnd
1189 && (existingAtEnd || *newCumlativeIt < (*existingIt)->path())
1190 && (newExactAtEnd || *newCumlativeIt < *newExactIt)) {
1191 // Mark node from cumalative for adding
1192 nodeToAdd = *newCumlativeIt;
1194 } else if (!newExactAtEnd
1195 && !newCumlativeAtEnd
1196 && (existingAtEnd || *newExactIt < (*existingIt)->path())
1197 && (existingAtEnd || *newCumlativeIt < (*existingIt)->path())) {
1198 // Mark node from both for adding
1199 nodeToAdd = *newExactIt;
1203 Q_ASSERT(!newExactAtEnd || !newCumlativeAtEnd);
1204 // update case, figure out which case exactly
1205 if (newExactAtEnd) {
1207 } else if (newCumlativeAtEnd) {
1209 } else if(*newExactIt < *newCumlativeIt) {
1211 } else if (*newCumlativeIt < *newExactIt) {
1217 // Update existingNodeIte
1218 ProFile *fileExact = includeFilesCumlative.value((*existingIt)->path());
1219 ProFile *fileCumlative = includeFilesCumlative.value((*existingIt)->path());
1220 if (fileExact || fileCumlative) {
1221 static_cast<Qt4PriFileNode *>(*existingIt)->update(fileExact, m_readerExact, fileCumlative, m_readerCumulative);
1223 // We always parse exactly, because we later when async parsing don't know whether
1224 // the .pro file is included in this .pro file
1225 // So to compare that later parse with the sync one
1227 static_cast<Qt4ProFileNode *>(*existingIt)->asyncUpdate();
1229 static_cast<Qt4ProFileNode *>(*existingIt)->update();
1232 // newCumalativeIt and newExactIt are already incremented
1235 // If we found something to add, do it
1236 if (!nodeToAdd.isEmpty()) {
1237 ProFile *fileExact = includeFilesCumlative.value(nodeToAdd);
1238 ProFile *fileCumlative = includeFilesCumlative.value(nodeToAdd);
1240 // Loop preventation, make sure that exact same node is not in our parent chain
1242 ProjectExplorer::Node *n = this;
1243 while ((n = n->parentFolderNode())) {
1244 if (qobject_cast<Qt4PriFileNode *>(n) && n->path() == nodeToAdd) {
1252 } else if (fileExact || fileCumlative) {
1253 Qt4PriFileNode *qt4PriFileNode = new Qt4PriFileNode(m_project, this, nodeToAdd);
1254 qt4PriFileNode->setParentFolderNode(this); // Needed for loop detection
1255 qt4PriFileNode->update(fileExact, m_readerExact, fileCumlative, m_readerCumulative);
1256 toAdd << qt4PriFileNode;
1258 Qt4ProFileNode *qt4ProFileNode = new Qt4ProFileNode(m_project, nodeToAdd);
1259 qt4ProFileNode->setParentFolderNode(this); // Needed for loop detection
1261 qt4ProFileNode->asyncUpdate();
1263 qt4ProFileNode->update();
1264 toAdd << qt4ProFileNode;
1269 if (!toRemove.isEmpty())
1270 removeProjectNodes(toRemove);
1271 if (!toAdd.isEmpty())
1272 addProjectNodes(toAdd);
1274 Qt4PriFileNode::update(fileForCurrentProjectExact, m_readerExact, fileForCurrentProjectCumlative, m_readerCumulative);
1276 // update TargetInformation
1277 m_qt4targetInformation = targetInformation(m_readerExact);
1279 // update other variables
1280 QHash<Qt4Variable, QStringList> newVarValues;
1282 newVarValues[DefinesVar] = m_readerExact->values(QLatin1String("DEFINES"));
1283 newVarValues[IncludePathVar] = includePaths(m_readerExact);
1284 newVarValues[UiDirVar] = QStringList() << uiDirPath(m_readerExact);
1285 newVarValues[MocDirVar] = QStringList() << mocDirPath(m_readerExact);
1286 newVarValues[PkgConfigVar] = m_readerExact->values(QLatin1String("PKGCONFIG"));
1287 newVarValues[PrecompiledHeaderVar] =
1288 m_readerExact->absoluteFileValues(QLatin1String("PRECOMPILED_HEADER"),
1290 QStringList() << m_projectDir,
1292 newVarValues[LibDirectoriesVar] = libDirectories(m_readerExact);
1294 if (m_varValues != newVarValues) {
1295 m_varValues = newVarValues;
1296 foreach (NodesWatcher *watcher, watchers())
1297 if (Qt4NodesWatcher *qt4Watcher = qobject_cast<Qt4NodesWatcher*>(watcher))
1298 emit qt4Watcher->variablesChanged(this, m_varValues, newVarValues);
1301 createUiCodeModelSupport();
1304 foreach (NodesWatcher *watcher, watchers())
1305 if (Qt4NodesWatcher *qt4Watcher = qobject_cast<Qt4NodesWatcher*>(watcher))
1306 emit qt4Watcher->proFileUpdated(this);
1308 m_project->destroyProFileReader(m_readerExact);
1309 if (m_readerCumulative)
1310 m_project->destroyProFileReader(m_readerCumulative);
1313 m_readerCumulative = 0;
1317 // find all ui files in project
1318 class FindUiFileNodesVisitor : public ProjectExplorer::NodesVisitor {
1320 void visitProjectNode(ProjectNode *projectNode)
1322 visitFolderNode(projectNode);
1324 void visitFolderNode(FolderNode *folderNode)
1326 foreach (FileNode *fileNode, folderNode->fileNodes()) {
1327 if (fileNode->fileType() == ProjectExplorer::FormType)
1328 uiFileNodes << fileNode;
1331 QList<FileNode*> uiFileNodes;
1335 // This function is triggered after a build, and updates the state ui files
1336 // It does so by storing a modification time for each ui file we know about.
1338 // TODO this function should also be called if the build directory is changed
1339 QStringList Qt4ProFileNode::updateUiFiles()
1341 // qDebug()<<"Qt4ProFileNode::updateUiFiles()";
1342 // Only those two project types can have ui files for us
1343 if (m_projectType != ApplicationTemplate
1344 && m_projectType != LibraryTemplate)
1345 return QStringList();
1347 // Find all ui files
1348 FindUiFileNodesVisitor uiFilesVisitor;
1349 this->accept(&uiFilesVisitor);
1350 const QList<FileNode*> uiFiles = uiFilesVisitor.uiFileNodes;
1352 // Find the UiDir, there can only ever be one
1353 QString uiDir = buildDir();
1354 QStringList tmp = m_varValues[UiDirVar];
1355 if (tmp.size() != 0)
1356 uiDir = tmp.first();
1358 // Collect all existing generated files
1359 QList<FileNode*> existingFileNodes;
1360 foreach (FileNode *file, fileNodes()) {
1361 if (file->isGenerated())
1362 existingFileNodes << file;
1365 // Convert uiFile to uiHeaderFilePath, find all headers that correspond
1366 // and try to find them in uiDir
1367 QStringList newFilePaths;
1368 foreach (FileNode *uiFile, uiFiles) {
1369 const QString uiHeaderFilePath
1370 = QString("%1/ui_%2.h").arg(uiDir, QFileInfo(uiFile->path()).completeBaseName());
1371 if (QFileInfo(uiHeaderFilePath).exists())
1372 newFilePaths << uiHeaderFilePath;
1375 // Create a diff between those lists
1376 QList<FileNode*> toRemove;
1377 QList<FileNode*> toAdd;
1378 // The list of files for which we call updateSourceFile
1379 QStringList toUpdate;
1381 qSort(newFilePaths);
1382 qSort(existingFileNodes.begin(), existingFileNodes.end(), ProjectNode::sortNodesByPath);
1384 QList<FileNode*>::const_iterator existingNodeIter = existingFileNodes.constBegin();
1385 QList<QString>::const_iterator newPathIter = newFilePaths.constBegin();
1386 while (existingNodeIter != existingFileNodes.constEnd()
1387 && newPathIter != newFilePaths.constEnd()) {
1388 if ((*existingNodeIter)->path() < *newPathIter) {
1389 toRemove << *existingNodeIter;
1391 } else if ((*existingNodeIter)->path() > *newPathIter) {
1392 toAdd << new FileNode(*newPathIter, ProjectExplorer::HeaderType, true);
1394 } else { // *existingNodeIter->path() == *newPathIter
1395 QString fileName = (*existingNodeIter)->path();
1396 QMap<QString, QDateTime>::const_iterator it = m_uitimestamps.find(fileName);
1397 QDateTime lastModified = QFileInfo(fileName).lastModified();
1398 if (it == m_uitimestamps.constEnd() || it.value() < lastModified) {
1399 toUpdate << fileName;
1400 m_uitimestamps[fileName] = lastModified;
1406 while (existingNodeIter != existingFileNodes.constEnd()) {
1407 toRemove << *existingNodeIter;
1410 while (newPathIter != newFilePaths.constEnd()) {
1411 toAdd << new FileNode(*newPathIter, ProjectExplorer::HeaderType, true);
1415 // Update project tree
1416 if (!toRemove.isEmpty()) {
1417 foreach (FileNode *file, toRemove)
1418 m_uitimestamps.remove(file->path());
1419 removeFileNodes(toRemove, this);
1422 CppTools::CppModelManagerInterface *modelManager =
1423 ExtensionSystem::PluginManager::instance()->getObject<CppTools::CppModelManagerInterface>();
1425 if (!toAdd.isEmpty()) {
1426 foreach (FileNode *file, toAdd) {
1427 m_uitimestamps.insert(file->path(), QFileInfo(file->path()).lastModified());
1428 toUpdate << file->path();
1430 // Also adding files depending on that
1431 // We only need to do that for files that were newly created
1432 QString fileName = QFileInfo(file->path()).fileName();
1433 foreach (CPlusPlus::Document::Ptr doc, modelManager->snapshot()) {
1434 if (doc->includedFiles().contains(fileName)) {
1435 if (!toUpdate.contains(doc->fileName()))
1436 toUpdate << doc->fileName();
1440 addFileNodes(toAdd, this);
1445 QString Qt4ProFileNode::uiDirPath(ProFileReader *reader) const
1447 QString path = reader->value("UI_DIR");
1448 if (QFileInfo(path).isRelative())
1449 path = QDir::cleanPath(buildDir() + "/" + path);
1453 QString Qt4ProFileNode::mocDirPath(ProFileReader *reader) const
1455 QString path = reader->value("MOC_DIR");
1456 if (QFileInfo(path).isRelative())
1457 path = QDir::cleanPath(buildDir() + "/" + path);
1461 QStringList Qt4ProFileNode::includePaths(ProFileReader *reader) const
1464 foreach (const QString &cxxflags, m_readerExact->values("QMAKE_CXXFLAGS")) {
1465 if (cxxflags.startsWith("-I"))
1466 paths.append(cxxflags.mid(2));
1469 paths.append(reader->absolutePathValues(QLatin1String("INCLUDEPATH"), m_projectDir));
1470 // paths already contains moc dir and ui dir, due to corrrectly parsing uic.prf and moc.prf
1471 // except if those directories don't exist at the time of parsing
1472 // thus we add those directories manually (without checking for existance)
1473 paths << mocDirPath(reader) << uiDirPath(reader);
1474 paths.removeDuplicates();
1478 QStringList Qt4ProFileNode::libDirectories(ProFileReader *reader) const
1481 foreach (const QString &str, reader->values(QLatin1String("LIBS"))) {
1482 if (str.startsWith("-L")) {
1483 result.append(str.mid(2));
1489 QStringList Qt4ProFileNode::subDirsPaths(ProFileReader *reader) const
1491 QStringList subProjectPaths;
1493 const QStringList subDirVars = reader->values(QLatin1String("SUBDIRS"));
1495 foreach (const QString &subDirVar, subDirVars) {
1496 // Special case were subdir is just an identifier:
1498 // subid.subdir = realdir"
1501 // subid.file = realdir/realfile.pro"
1504 const QString subDirKey = subDirVar + QLatin1String(".subdir");
1505 const QString subDirFileKey = subDirVar + QLatin1String(".file");
1506 if (reader->contains(subDirKey))
1507 realDir = reader->value(subDirKey);
1508 else if (reader->contains(subDirFileKey))
1509 realDir = reader->value(subDirFileKey);
1511 realDir = subDirVar;
1512 QFileInfo info(realDir);
1513 if (!info.isAbsolute())
1514 info.setFile(m_projectDir + QLatin1Char('/') + realDir);
1515 realDir = info.filePath();
1519 realFile = QString::fromLatin1("%1/%2.pro").arg(realDir, info.fileName());
1524 if (QFile::exists(realFile)) {
1525 subProjectPaths << realFile;
1527 m_project->proFileParseError(tr("Could not find .pro file for sub dir '%1' in '%2'")
1528 .arg(subDirVar).arg(realDir));
1532 subProjectPaths.removeDuplicates();
1533 return subProjectPaths;
1536 TargetInformation Qt4ProFileNode::targetInformation(ProFileReader *reader) const
1538 TargetInformation result;
1542 result.buildDir = buildDir();
1543 const QString baseDir = result.buildDir;
1544 // qDebug() << "base build dir is:"<<baseDir;
1546 // Working Directory
1547 if (reader->contains("DESTDIR")) {
1548 //qDebug() << "reader contains destdir:" << reader->value("DESTDIR");
1549 result.workingDir = reader->value("DESTDIR");
1550 if (QDir::isRelativePath(result.workingDir)) {
1551 result.workingDir = baseDir + QLatin1Char('/') + result.workingDir;
1552 //qDebug() << "was relative and expanded to" << result.workingDir;
1555 //qDebug() << "reader didn't contain DESTDIR, setting to " << baseDir;
1556 result.workingDir = baseDir;
1559 result.target = reader->value("TARGET");
1560 if (result.target.isEmpty())
1561 result.target = QFileInfo(m_projectFilePath).baseName();
1563 #if defined (Q_OS_MAC)
1564 if (reader->values("CONFIG").contains("app_bundle")) {
1565 result.workingDir += QLatin1Char('/')
1567 + QLatin1String(".app/Contents/MacOS");
1571 result.workingDir = QDir::cleanPath(result.workingDir);
1573 QString wd = result.workingDir;
1574 if (!reader->contains("DESTDIR")
1575 && reader->values("CONFIG").contains("debug_and_release")
1576 && reader->values("CONFIG").contains("debug_and_release_target")) {
1577 // If we don't have a destdir and debug and release is set
1578 // then the executable is in a debug/release folder
1579 //qDebug() << "reader has debug_and_release_target";
1581 // Hmm can we find out whether it's debug or release in a saner way?
1582 // Theoretically it's in CONFIG
1583 QString qmakeBuildConfig = "release";
1584 if (m_project->activeTarget()->activeBuildConfiguration()->qmakeBuildConfiguration() & QtVersion::DebugBuild)
1585 qmakeBuildConfig = "debug";
1586 wd += QLatin1Char('/') + qmakeBuildConfig;
1589 result.executable = QDir::cleanPath(wd + QLatin1Char('/') + result.target);
1590 //qDebug() << "##### updateTarget sets:" << result.workingDir << result.executable;
1592 #if defined (Q_OS_WIN)
1593 result.executable += QLatin1String(".exe");
1595 result.valid = true;
1599 TargetInformation Qt4ProFileNode::targetInformation()
1601 return m_qt4targetInformation;
1604 QString Qt4ProFileNode::buildDir() const
1606 const QDir srcDirRoot = QFileInfo(m_project->rootProjectNode()->path()).absoluteDir();
1607 const QString relativeDir = srcDirRoot.relativeFilePath(m_projectDir);
1608 return QDir(m_project->activeTarget()->activeBuildConfiguration()->buildDirectory()).absoluteFilePath(relativeDir);
1612 Sets project type to InvalidProject & deletes all subprojects/files/virtual folders
1614 void Qt4ProFileNode::invalidate()
1616 if (m_projectType == InvalidProject)
1621 // change project type
1622 Qt4ProjectType oldType = m_projectType;
1623 m_projectType = InvalidProject;
1626 foreach (NodesWatcher *watcher, watchers())
1627 if (Qt4NodesWatcher *qt4Watcher = qobject_cast<Qt4NodesWatcher*>(watcher))
1628 emit qt4Watcher->projectTypeChanged(this, oldType, InvalidProject);
1631 void Qt4ProFileNode::updateCodeModelSupportFromBuild(const QStringList &files)
1633 foreach (const QString &file, files) {
1634 QMap<QString, Qt4UiCodeModelSupport *>::const_iterator it, end;
1635 end = m_uiCodeModelSupport.constEnd();
1636 for (it = m_uiCodeModelSupport.constBegin(); it != end; ++it) {
1637 if (it.value()->fileName() == file)
1638 it.value()->updateFromBuild();
1643 void Qt4ProFileNode::updateCodeModelSupportFromEditor(const QString &uiFileName,
1644 const QString &contents)
1646 const QMap<QString, Qt4UiCodeModelSupport *>::const_iterator it =
1647 m_uiCodeModelSupport.constFind(uiFileName);
1648 if (it != m_uiCodeModelSupport.constEnd())
1649 it.value()->updateFromEditor(contents);
1650 foreach (ProjectExplorer::ProjectNode *pro, subProjectNodes())
1651 if (Qt4ProFileNode *qt4proFileNode = qobject_cast<Qt4ProFileNode *>(pro))
1652 qt4proFileNode->updateCodeModelSupportFromEditor(uiFileName, contents);
1655 QString Qt4ProFileNode::uiDirectory() const
1657 const Qt4VariablesHash::const_iterator it = m_varValues.constFind(UiDirVar);
1658 if (it != m_varValues.constEnd() && !it.value().isEmpty())
1659 return it.value().front();
1663 QString Qt4ProFileNode::uiHeaderFile(const QString &uiDir, const QString &formFile)
1665 QString uiHeaderFilePath = uiDir;
1666 uiHeaderFilePath += QLatin1String("/ui_");
1667 uiHeaderFilePath += QFileInfo(formFile).completeBaseName();
1668 uiHeaderFilePath += QLatin1String(".h");
1669 return QDir::cleanPath(uiHeaderFilePath);
1672 void Qt4ProFileNode::createUiCodeModelSupport()
1674 // qDebug()<<"creatUiCodeModelSupport()";
1675 CppTools::CppModelManagerInterface *modelManager
1676 = ExtensionSystem::PluginManager::instance()->getObject<CppTools::CppModelManagerInterface>();
1678 // First move all to
1679 QMap<QString, Qt4UiCodeModelSupport *> oldCodeModelSupport;
1680 oldCodeModelSupport = m_uiCodeModelSupport;
1681 m_uiCodeModelSupport.clear();
1683 // Only those two project types can have ui files for us
1684 if (m_projectType == ApplicationTemplate || m_projectType == LibraryTemplate) {
1685 // Find all ui files
1686 FindUiFileNodesVisitor uiFilesVisitor;
1687 this->accept(&uiFilesVisitor);
1688 const QList<FileNode*> uiFiles = uiFilesVisitor.uiFileNodes;
1690 // Find the UiDir, there can only ever be one
1691 const QString uiDir = uiDirectory();
1692 foreach (const FileNode *uiFile, uiFiles) {
1693 const QString uiHeaderFilePath = uiHeaderFile(uiDir, uiFile->path());
1694 // qDebug()<<"code model support for "<<uiFile->path()<<" "<<uiHeaderFilePath;
1695 QMap<QString, Qt4UiCodeModelSupport *>::iterator it = oldCodeModelSupport.find(uiFile->path());
1696 if (it != oldCodeModelSupport.end()) {
1697 // qDebug()<<"updated old codemodelsupport";
1698 Qt4UiCodeModelSupport *cms = it.value();
1699 cms->setFileName(uiHeaderFilePath);
1700 m_uiCodeModelSupport.insert(it.key(), cms);
1701 oldCodeModelSupport.erase(it);
1703 // qDebug()<<"adding new codemodelsupport";
1704 Qt4UiCodeModelSupport *cms = new Qt4UiCodeModelSupport(modelManager, m_project, uiFile->path(), uiHeaderFilePath);
1705 m_uiCodeModelSupport.insert(uiFile->path(), cms);
1706 modelManager->addEditorSupport(cms);
1711 QMap<QString, Qt4UiCodeModelSupport *>::const_iterator it, end;
1712 end = oldCodeModelSupport.constEnd();
1713 for (it = oldCodeModelSupport.constBegin(); it!=end; ++it) {
1714 modelManager->removeEditorSupport(it.value());
1719 Qt4NodesWatcher::Qt4NodesWatcher(QObject *parent)
1720 : NodesWatcher(parent)
1724 } // namespace Internal
1725 } // namespace Qt4ProjectManager