Newer
Older
/**************************************************************************
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
** Contact: Nokia Corporation (qt-info@nokia.com)
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
** If you are unsure which license is appropriate for your use, please
**************************************************************************/
#include "qt4nodes.h"
#include "qt4project.h"
#include "qt4projectmanager.h"
#include "qt4projectmanagerconstants.h"
#include "qt4buildconfiguration.h"
#include <projectexplorer/nodesvisitor.h>
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/editormanager/ieditor.h>
#include <coreplugin/filemanager.h>
#include <coreplugin/icore.h>
#include <coreplugin/iversioncontrol.h>
#include <coreplugin/vcsmanager.h>
#include <cpptools/cppmodelmanagerinterface.h>
#include <extensionsystem/pluginmanager.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/buildmanager.h>
#include <QtCore/QDebug>
#include <QtCore/QDir>
#include <QtCore/QFile>
#include <QtCore/QFileInfo>
#include <QtCore/QCoreApplication>
#include <QtCore/QXmlStreamReader>
#include <QtGui/QMainWindow>
#include <QtGui/QMessageBox>
#include <QtGui/QPushButton>
// Static cached data in struct Qt4NodeStaticData providing information and icons
// for file types and the project. Do some magic via qAddPostRoutine()
// to make sure the icons do not outlive QApplication, triggering warnings on X11.
struct FileTypeDataStorage {
ProjectExplorer::FileType type;
const char *typeName;
const char *icon;
};
static const FileTypeDataStorage fileTypeDataStorage[] = {
{ ProjectExplorer::HeaderType,
QT_TRANSLATE_NOOP("Qt4ProjectManager::Internal::Qt4PriFileNode", "Headers"),
":/qt4projectmanager/images/headers.png" },
{ ProjectExplorer::SourceType,
QT_TRANSLATE_NOOP("Qt4ProjectManager::Internal::Qt4PriFileNode", "Sources"),
":/qt4projectmanager/images/sources.png" },
{ ProjectExplorer::FormType,
QT_TRANSLATE_NOOP("Qt4ProjectManager::Internal::Qt4PriFileNode", "Forms"),
":/qt4projectmanager/images/forms.png" },
{ ProjectExplorer::ResourceType,
QT_TRANSLATE_NOOP("Qt4ProjectManager::Internal::Qt4PriFileNode", "Resources"),
":/qt4projectmanager/images/qt_qrc.png" },
{ ProjectExplorer::QMLType,
QT_TRANSLATE_NOOP("Qt4ProjectManager::Internal::Qt4PriFileNode", "QML"),
{ ProjectExplorer::UnknownFileType,
QT_TRANSLATE_NOOP("Qt4ProjectManager::Internal::Qt4PriFileNode", "Other files"),
":/qt4projectmanager/images/unknown.png" }
};
struct Qt4NodeStaticData {
struct FileTypeData {
FileTypeData(ProjectExplorer::FileType t = ProjectExplorer::UnknownFileType,
const QString &tN = QString(),
const QIcon &i = QIcon()) :
type(t), typeName(tN), icon(i) { }
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
ProjectExplorer::FileType type;
QString typeName;
QIcon icon;
};
QVector<FileTypeData> fileTypeData;
QIcon projectIcon;
};
static void clearQt4NodeStaticData();
Q_GLOBAL_STATIC_WITH_INITIALIZER(Qt4NodeStaticData, qt4NodeStaticData, {
// File type data
const unsigned count = sizeof(fileTypeDataStorage)/sizeof(FileTypeDataStorage);
x->fileTypeData.reserve(count);
// Overlay the SP_DirIcon with the custom icons
const QSize desiredSize = QSize(16, 16);
for (unsigned i = 0 ; i < count; i++) {
const QIcon overlayIcon = QIcon(QLatin1String(fileTypeDataStorage[i].icon));
const QPixmap folderPixmap =
Core::FileIconProvider::overlayIcon(QStyle::SP_DirIcon,
overlayIcon, desiredSize);
QIcon folderIcon;
folderIcon.addPixmap(folderPixmap);
const QString desc = Qt4ProjectManager::Internal::Qt4PriFileNode::tr(fileTypeDataStorage[i].typeName);
x->fileTypeData.push_back(Qt4NodeStaticData::FileTypeData(fileTypeDataStorage[i].type,
desc, folderIcon));
}
// Project icon
const QIcon projectBaseIcon(QLatin1String(":/qt4projectmanager/images/qt_project.png"));
const QPixmap projectPixmap = Core::FileIconProvider::overlayIcon(QStyle::SP_DirIcon,
projectBaseIcon,
desiredSize);
x->projectIcon.addPixmap(projectPixmap);
qAddPostRoutine(clearQt4NodeStaticData);
});
static void clearQt4NodeStaticData()
{
qt4NodeStaticData()->fileTypeData.clear();
qt4NodeStaticData()->projectIcon = QIcon();
enum { debug = 0 };
namespace {
// sorting helper function
bool sortProjectFilesByPath(ProFile *f1, ProFile *f2)
{
return f1->fileName() < f2->fileName();
}
}
namespace Qt4ProjectManager {
namespace Internal {
Qt4PriFile::Qt4PriFile(Qt4PriFileNode *qt4PriFile)
: IFile(qt4PriFile), m_priFile(qt4PriFile)
{
}
bool Qt4PriFile::save(const QString &fileName)
{
Q_UNUSED(fileName);
return false;
}
void Qt4PriFile::rename(const QString &newName)
{
// Can't happen
Q_ASSERT(false);
Q_UNUSED(newName);
}
QString Qt4PriFile::fileName() const
{
return m_priFile->path();
}
QString Qt4PriFile::defaultPath() const
{
}
QString Qt4PriFile::suggestedFileName() const
{
QString Qt4PriFile::mimeType() const
{
return Qt4ProjectManager::Constants::PROFILE_MIMETYPE;
}
bool Qt4PriFile::isModified() const
{
return false;
}
bool Qt4PriFile::isReadOnly() const
{
return false;
}
bool Qt4PriFile::isSaveAsAllowed() const
{
return false;
}
Core::IFile::ReloadBehavior Qt4PriFile::reloadBehavior(ChangeTrigger state, ChangeType type) const
Q_UNUSED(state)
Q_UNUSED(type)
return BehaviorSilent;
void Qt4PriFile::reload(ReloadFlag flag, ChangeType type)
{
Q_UNUSED(flag)
Q_UNUSED(type)
if (type == TypePermissions)
return;
m_priFile->scheduleUpdate();
}
/*!
\class Qt4PriFileNode
Implements abstract ProjectNode class
*/
Qt4PriFileNode::Qt4PriFileNode(Qt4Project *project, Qt4ProFileNode* qt4ProFileNode, const QString &filePath)
m_projectFilePath(QDir::fromNativeSeparators(filePath)),
m_projectDir(QFileInfo(filePath).absolutePath())
m_qt4PriFile = new Qt4PriFile(this);
Core::ICore::instance()->fileManager()->addFile(m_qt4PriFile);
setDisplayName(QFileInfo(filePath).completeBaseName());
setIcon(qt4NodeStaticData()->projectIcon);
}
void Qt4PriFileNode::scheduleUpdate()
{
ProFileCacheManager::instance()->discardFile(m_projectFilePath);
m_qt4ProFileNode->scheduleUpdate();
struct InternalNode
{
QMap<QString, InternalNode*> subnodes;
QStringList files;
ProjectExplorer::FileType type;
QString fullPath;
QIcon icon;
InternalNode()
{
type = ProjectExplorer::UnknownFileType;
}
~InternalNode()
{
qDeleteAll(subnodes);
}
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
// Creates a tree structure from a list of absolute file paths.
// Empty directories are compressed into a single entry with a longer path.
// * project
// * /absolute/path
// * file1
// * relative
// * path1
// * file1
// * file2
// * path2
// * file1
// The method first creates a tree that looks like the directory structure, i.e.
// * /
// * absolute
// * path
// ...
// and afterwards calls compress() which merges directory nodes with single children, i.e. to
// * /absolute/path
void create(const QString &projectDir, const QStringList &newFilePaths, ProjectExplorer::FileType type)
{
static const QChar separator = QChar('/');
const QString projectDirWithSeparator = projectDir + separator;
int projectDirWithSeparatorLength = projectDirWithSeparator.length();
foreach (const QString &file, newFilePaths) {
QString fileWithoutPrefix;
bool isRelative;
if (file.startsWith(projectDirWithSeparator)) {
isRelative = true;
fileWithoutPrefix = file.mid(projectDirWithSeparatorLength);
} else {
isRelative = false;
fileWithoutPrefix = file;
}
QStringList parts = fileWithoutPrefix.split(separator, QString::SkipEmptyParts);
if (!isRelative && parts.count() > 0)
parts[0].prepend(separator);
QStringListIterator it(parts);
InternalNode *currentNode = this;
QString path = (isRelative ? projectDirWithSeparator : "");
while (it.hasNext()) {
const QString &key = it.next();
if (it.hasNext()) { // key is directory
path += key;
if (!currentNode->subnodes.contains(path)) {
InternalNode *val = new InternalNode;
val->type = type;
val->fullPath = path;
val->displayName = key;
currentNode->subnodes.insert(path, val);
currentNode = currentNode->subnodes.value(path);
path += separator;
} else { // key is filename
currentNode->files.append(file);
}
}
}
// Removes folder nodes with only a single sub folder in it
void compress()
{
QMap<QString, InternalNode*> newSubnodes;
QMapIterator<QString, InternalNode*> i(subnodes);
while (i.hasNext()) {
i.next();
i.value()->compress();
if (i.value()->files.isEmpty() && i.value()->subnodes.size() == 1) {
// replace i.value() by i.value()->subnodes.begin()
QString key = i.value()->subnodes.begin().key();
InternalNode *keep = i.value()->subnodes.value(key);
keep->displayName = i.value()->displayName + "/" + keep->displayName;
newSubnodes.insert(key, keep);
i.value()->subnodes.clear();
delete i.value();
} else {
newSubnodes.insert(i.key(), i.value());
}
}
// Makes the projectNode's subtree below the given folder match this internal node's subtree
void updateSubFolders(Qt4PriFileNode *projectNode, ProjectExplorer::FolderNode *folder)
{
updateFiles(projectNode, folder, type);
// update folders
QList<FolderNode *> existingFolderNodes;
foreach (FolderNode *node, folder->subFolderNodes()) {
if (node->nodeType() != ProjectNodeType)
existingFolderNodes << node;
}
qSort(existingFolderNodes.begin(), existingFolderNodes.end(), ProjectNode::sortNodesByPath);
QList<FolderNode *> foldersToRemove;
QList<FolderNode *> foldersToAdd;
typedef QPair<InternalNode *, FolderNode *> NodePair;
QList<NodePair> nodesToUpdate;
// Both lists should be already sorted...
QList<FolderNode*>::const_iterator existingNodeIter = existingFolderNodes.constBegin();
QMap<QString, InternalNode*>::const_iterator newNodeIter = subnodes.constBegin();;
while (existingNodeIter != existingFolderNodes.constEnd()
&& newNodeIter != subnodes.constEnd()) {
if ((*existingNodeIter)->path() < newNodeIter.value()->fullPath) {
foldersToRemove << *existingNodeIter;
++existingNodeIter;
} else if ((*existingNodeIter)->path() > newNodeIter.value()->fullPath) {
FolderNode *newNode = new FolderNode(newNodeIter.value()->fullPath);
newNode->setDisplayName(newNodeIter.value()->displayName);
if (!newNodeIter.value()->icon.isNull())
newNode->setIcon(newNodeIter.value()->icon);
foldersToAdd << newNode;
nodesToUpdate << NodePair(newNodeIter.value(), newNode);
++newNodeIter;
} else { // *existingNodeIter->path() == *newPathIter
nodesToUpdate << NodePair(newNodeIter.value(), *existingNodeIter);
++existingNodeIter;
++newNodeIter;
}
while (existingNodeIter != existingFolderNodes.constEnd()) {
foldersToRemove << *existingNodeIter;
++existingNodeIter;
}
while (newNodeIter != subnodes.constEnd()) {
FolderNode *newNode = new FolderNode(newNodeIter.value()->fullPath);
newNode->setDisplayName(newNodeIter.value()->displayName);
if (!newNodeIter.value()->icon.isNull())
newNode->setIcon(newNodeIter.value()->icon);
foldersToAdd << newNode;
nodesToUpdate << NodePair(newNodeIter.value(), newNode);
++newNodeIter;
}
if (!foldersToRemove.isEmpty())
projectNode->removeFolderNodes(foldersToRemove, folder);
if (!foldersToAdd.isEmpty())
projectNode->addFolderNodes(foldersToAdd, folder);
foreach (const NodePair &np, nodesToUpdate)
np.first->updateSubFolders(projectNode, np.second);
}
// Makes the folder's files match this internal node's file list
void updateFiles(Qt4PriFileNode *projectNode, FolderNode *folder, FileType type)
{
QList<FileNode*> existingFileNodes;
foreach (FileNode *fileNode, folder->fileNodes()) {
if (fileNode->fileType() == type && !fileNode->isGenerated())
existingFileNodes << fileNode;
QList<FileNode*> filesToRemove;
QList<FileNode*> filesToAdd;
qSort(files);
qSort(existingFileNodes.begin(), existingFileNodes.end(), ProjectNode::sortNodesByPath);
QList<FileNode*>::const_iterator existingNodeIter = existingFileNodes.constBegin();
QList<QString>::const_iterator newPathIter = files.constBegin();
while (existingNodeIter != existingFileNodes.constEnd()
&& newPathIter != files.constEnd()) {
if ((*existingNodeIter)->path() < *newPathIter) {
filesToRemove << *existingNodeIter;
++existingNodeIter;
} else if ((*existingNodeIter)->path() > *newPathIter) {
filesToAdd << new FileNode(*newPathIter, type, false);
++newPathIter;
} else { // *existingNodeIter->path() == *newPathIter
++existingNodeIter;
++newPathIter;
}
}
while (existingNodeIter != existingFileNodes.constEnd()) {
filesToRemove << *existingNodeIter;
++existingNodeIter;
}
while (newPathIter != files.constEnd()) {
filesToAdd << new FileNode(*newPathIter, type, false);
++newPathIter;
}
if (!filesToRemove.isEmpty())
projectNode->removeFileNodes(filesToRemove, folder);
if (!filesToAdd.isEmpty())
projectNode->addFileNodes(filesToAdd, folder);
}
};
QStringList Qt4PriFileNode::baseVPaths(ProFileReader *reader, const QString &projectDir)
QStringList result;
if (!reader)
return result;
result += reader->absolutePathValues("VPATH", projectDir);
result << projectDir; // QMAKE_ABSOLUTE_SOURCE_PATH
result += reader->absolutePathValues("DEPENDPATH", projectDir);
result.removeDuplicates();
return result;
}
QStringList Qt4PriFileNode::fullVPaths(const QStringList &baseVPaths, ProFileReader *reader, FileType type, const QString &qmakeVariable, const QString &projectDir)
{
QStringList vPaths;
if (!reader)
return vPaths;
if (type == ProjectExplorer::SourceType)
vPaths = reader->absolutePathValues("VPATH_" + qmakeVariable, projectDir);
vPaths += baseVPaths;
if (type == ProjectExplorer::HeaderType)
vPaths += reader->absolutePathValues("INCLUDEPATH", projectDir);
vPaths.removeDuplicates();
return vPaths;
}
static QSet<QString> recursiveEnumerate(const QString &folder)
{
QSet<QString> result;
QFileInfo fi(folder);
if (fi.isDir()) {
QDir dir(folder);
dir.setFilter(dir.filter() | QDir::NoDotAndDotDot);
foreach (const QFileInfo &file, dir.entryInfoList()) {
if (file.isDir())
result += recursiveEnumerate(file.absoluteFilePath());
else
result += file.absoluteFilePath();
}
} else if (fi.exists()) {
result << folder;
}
return result;
}
void Qt4PriFileNode::update(ProFile *includeFileExact, ProFileReader *readerExact, ProFile *includeFileCumlative, ProFileReader *readerCumulative)
{
// add project file node
if (m_fileNodes.isEmpty())
addFileNodes(QList<FileNode*>() << new FileNode(m_projectFilePath, ProjectFileType, false), this);
const QString &projectDir = m_qt4ProFileNode->m_projectDir;
QStringList baseVPathsExact = baseVPaths(readerExact, projectDir);
QStringList baseVPathsCumulative = baseVPaths(readerCumulative, projectDir);
const QVector<Qt4NodeStaticData::FileTypeData> &fileTypes = qt4NodeStaticData()->fileTypeData;
InternalNode contents;
// Figure out DEPLOYMENT and INSTALL folders
QStringList folders;
QStringList dynamicVariables = dynamicVarNames(readerExact, readerCumulative);
foreach (const QString &dynamicVar, dynamicVariables) {
folders += readerExact->values(dynamicVar, includeFileExact);
folders += readerCumulative->values(dynamicVar, includeFileCumlative);
}
for (int i=0; i < folders.size(); ++i) {
QFileInfo fi(folders.at(i));
if (fi.isRelative())
folders[i] = projectDir + "/" + folders.at(i);
}
// Remove non existing items and non folders
// todo fix files in INSTALL rules
QStringList::iterator it = folders.begin();
while (it != folders.end()) {
QFileInfo fi(*it);
if (fi.exists()) {
if (fi.isDir()) {
// keep directories
++it;
} else {
// move files directly to m_recursiveEnumerateFiles
m_recursiveEnumerateFiles << *it;
it = folders.erase(it);
}
} else {
// do remove non exsting stuff
folders.removeDuplicates();
watchFolders(folders.toSet());
foreach (const QString &folder, folders) {
m_recursiveEnumerateFiles += recursiveEnumerate(folder);
}
for (int i = 0; i < fileTypes.size(); ++i) {
FileType type = fileTypes.at(i).type;
QStringList qmakeVariables = varNames(type);
foreach (const QString &qmakeVariable, qmakeVariables) {
QStringList vPathsExact = fullVPaths(baseVPathsExact, readerExact, type, qmakeVariable, projectDir);
QStringList vPathsCumulative = fullVPaths(baseVPathsCumulative, readerCumulative, type, qmakeVariable, projectDir);
newFilePaths += readerExact->absoluteFileValues(qmakeVariable, projectDir, vPathsExact, includeFileExact).toSet();
newFilePaths += readerCumulative->absoluteFileValues(qmakeVariable, projectDir, vPathsCumulative, includeFileCumlative).toSet();
newFilePaths += filterFiles(type, m_recursiveEnumerateFiles);
// We only need to save this information if
// we are watching folders
if (!folders.isEmpty())
m_files[type] = newFilePaths;
else
m_files[type].clear();
if (!newFilePaths.isEmpty()) {
InternalNode *subfolder = new InternalNode;
subfolder->type = type;
subfolder->icon = fileTypes.at(i).icon;
subfolder->fullPath = m_projectDir + "/#" + QString::number(i) + fileTypes.at(i).typeName;
subfolder->displayName = fileTypes.at(i).typeName;
contents.subnodes.insert(subfolder->fullPath, subfolder);
// create the hierarchy with subdirectories
subfolder->create(m_projectDir, newFilePaths.toList(), type);
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
contents.updateSubFolders(this, this);
}
void Qt4PriFileNode::watchFolders(const QSet<QString> &folders)
{
QSet<QString> toUnwatch = m_watchedFolders;
toUnwatch.subtract(folders);
QSet<QString> toWatch = folders;
toWatch.subtract(m_watchedFolders);
if (!toUnwatch.isEmpty())
m_project->centralizedFolderWatcher()->unwatchFolders(toUnwatch.toList(), this);
if (!toWatch.isEmpty())
m_project->centralizedFolderWatcher()->watchFolders(toWatch.toList(), this);
m_watchedFolders = folders;
}
void Qt4PriFileNode::folderChanged(const QString &)
{
//qDebug()<<"########## Qt4PriFileNode::folderChanged";
// So, we need to figure out which files changed.
// Collect all the files
QSet<QString> newFiles;
foreach (const QString &folder, m_watchedFolders) {
newFiles += recursiveEnumerate(folder);
}
QSet<QString> addedFiles = newFiles;
addedFiles.subtract(m_recursiveEnumerateFiles);
QSet<QString> removedFiles = m_recursiveEnumerateFiles;
removedFiles.subtract(newFiles);
if (addedFiles.isEmpty() && removedFiles.isEmpty())
return;
m_recursiveEnumerateFiles = newFiles;
// Apply the differences
// per file type
const QVector<Qt4NodeStaticData::FileTypeData> &fileTypes = qt4NodeStaticData()->fileTypeData;
for (int i = 0; i < fileTypes.size(); ++i) {
FileType type = fileTypes.at(i).type;
QSet<QString> add = filterFiles(type, addedFiles);
QSet<QString> remove = filterFiles(type, removedFiles);
if (!add.isEmpty() || !remove.isEmpty()) {
// Scream :)
// qDebug()<<"For type"<<fileTypes.at(i).typeName<<"\n"
// <<"added files"<<add<<"\n"
// <<"removed files"<<remove;
m_files[type].unite(add);
m_files[type].subtract(remove);
}
}
// Now apply stuff
InternalNode contents;
for (int i = 0; i < fileTypes.size(); ++i) {
FileType type = fileTypes.at(i).type;
if (!m_files[type].isEmpty()) {
InternalNode *subfolder = new InternalNode;
subfolder->type = type;
subfolder->icon = fileTypes.at(i).icon;
subfolder->fullPath = m_projectDir + "/#" + QString::number(i) + fileTypes.at(i).typeName;
subfolder->displayName = fileTypes.at(i).typeName;
contents.subnodes.insert(subfolder->fullPath, subfolder);
// create the hierarchy with subdirectories
subfolder->create(m_projectDir, m_files[type].toList(), type);
}
}
contents.updateSubFolders(this, this);
bool Qt4PriFileNode::deploysFolder(const QString &folder) const
{
QString f = folder;
if (!f.endsWith('/'))
f.append('/');
foreach (const QString &wf, m_watchedFolders) {
if (f.startsWith(wf)
&& (wf.endsWith('/')
|| (wf.length() < f.length() && f.at(wf.length()) == '/')))
return true;
}
return false;
}
QList<ProjectNode::ProjectAction> Qt4PriFileNode::supportedActions(Node *node) const
const FolderNode *folderNode = this;
const Qt4ProFileNode *proFileNode;
while (!(proFileNode = qobject_cast<const Qt4ProFileNode*>(folderNode)))
folderNode = folderNode->parentFolderNode();
switch (proFileNode->projectType()) {
case ApplicationTemplate:
case LibraryTemplate:
actions << AddNewFile;
if (m_recursiveEnumerateFiles.contains(node->path())) {
}
// Only enable 'add existing file' if we don't deploy the folder
if (!deploysFolder(node->path()))
actions << AddExistingFile;
break;
case SubDirsTemplate:
actions << AddSubProject << RemoveSubProject;
break;
default:
break;
FileNode *fileNode = qobject_cast<FileNode *>(node);
if (fileNode && fileNode->fileType() != ProjectExplorer::ProjectFileType)
actions << Rename;
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)
{
// If a file is already referenced in the .pro file then we don't add them.
// That ignores scopes and which variable was used to reference the file
// So it's obviously a bit limited, but in those cases you need to edit the
// project files manually anyway.
ProjectExplorer::FindAllFilesVisitor visitor;
accept(&visitor);
const QStringList &allFiles = visitor.filePaths();
QStringList qrcFiles; // the list of qrc files referenced from ui files
if (fileType == ProjectExplorer::FormType) {
foreach (const QString &formFile, filePaths) {
QStringList resourceFiles = formResources(formFile);
foreach (const QString &resourceFile, resourceFiles)
if (!qrcFiles.contains(resourceFile))
qrcFiles.append(resourceFile);
}
}
QStringList uniqueQrcFiles;
foreach (const QString &file, qrcFiles) {
if (!allFiles.contains(file))
uniqueQrcFiles.append(file);
}
QStringList uniqueFilePaths;
foreach (const QString &file, filePaths) {
if (!allFiles.contains(file))
uniqueFilePaths.append(file);
}
QStringList failedFiles;
changeFiles(fileType, uniqueFilePaths, &failedFiles, AddToProFile);
changeFiles(ProjectExplorer::ResourceType, uniqueQrcFiles, &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::deleteFiles(const FileType fileType, const QStringList &filePaths)
{
QStringList failedFiles;
changeFiles(fileType, filePaths, &failedFiles, RemoveFromProFile);
return true;
}
bool Qt4PriFileNode::renameFile(const FileType fileType, const QString &filePath,
const QString &newFilePath)
{
return false;
QStringList dummy;
changeFiles(fileType, QStringList() << filePath, &dummy, RemoveFromProFile);
if (!dummy.isEmpty())
return false;
changeFiles(fileType, QStringList() << newFilePath, &dummy, AddToProFile);
if (!dummy.isEmpty())
return false;
return true;
}
bool Qt4PriFileNode::changeIncludes(ProFile *includeFile, const QStringList &proFilePaths,
ChangeType change)
{
Q_UNUSED(includeFile)
Q_UNUSED(proFilePaths)
Q_UNUSED(change)
// TODO
return false;
}
bool Qt4PriFileNode::priFileWritable(const QString &path)
{
const QString dir = QFileInfo(path).dir().path();
Core::ICore *core = Core::ICore::instance();
Core::IVersionControl *versionControl = core->vcsManager()->findVersionControlForDirectory(dir);
switch (Core::EditorManager::promptReadOnlyFile(path, versionControl, core->mainWindow(), false)) {
case Core::EditorManager::RO_OpenVCS:
QMessageBox::warning(core->mainWindow(), tr("Failed!"), tr("Could not open the file for edit with VCS."));
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."));
case Core::EditorManager::RO_SaveAs:
case Core::EditorManager::RO_Cancel:
bool Qt4PriFileNode::saveModifiedEditors()
Core::ICore *core = Core::ICore::instance();
foreach (Core::IEditor *editor, core->editorManager()->editorsForFileName(m_projectFilePath)) {
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(m_projectFilePath));
// force instant reload of ourselves
ProFileCacheManager::instance()->discardFile(m_projectFilePath);
m_project->qt4ProjectManager()->notifyChanged(m_projectFilePath);
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
QStringList Qt4PriFileNode::formResources(const QString &formFile) const
{
QStringList resourceFiles;
QFile file(formFile);
file.open(QIODevice::ReadOnly);
QXmlStreamReader reader(&file);
QFileInfo fi(formFile);
QDir formDir = fi.absoluteDir();
while (!reader.atEnd()) {
reader.readNext();
if (reader.isStartElement()) {
if (reader.name() == QLatin1String("iconset")) {
const QXmlStreamAttributes attributes = reader.attributes();
if (attributes.hasAttribute(QLatin1String("resource")))
resourceFiles.append(QDir::cleanPath(formDir.absoluteFilePath(
attributes.value(QLatin1String("resource")).toString())));
} else if (reader.name() == QLatin1String("include")) {
const QXmlStreamAttributes attributes = reader.attributes();
if (attributes.hasAttribute(QLatin1String("location")))
resourceFiles.append(QDir::cleanPath(formDir.absoluteFilePath(
attributes.value(QLatin1String("location")).toString())));
}
}
}
if (reader.hasError())
qWarning() << "Could not read form file:" << formFile;
return resourceFiles;
}
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())
return;
// Ensure that the file is not read only
QFileInfo fi(m_projectFilePath);
if (!fi.isWritable()) {
// Try via vcs manager
Core::VCSManager *vcsManager = Core::ICore::instance()->vcsManager();
Core::IVersionControl *versionControl = vcsManager->findVersionControlForDirectory(fi.absolutePath());
if (!versionControl || versionControl->vcsOpen(m_projectFilePath)) {
bool makeWritable = QFile::setPermissions(m_projectFilePath, fi.permissions() | QFile::WriteUser);
if (!makeWritable) {
QMessageBox::warning(Core::ICore::instance()->mainWindow(),
tr("Failed!"),
tr("Could not write project file %1.").arg(m_projectFilePath));
return;
}
}
}
QStringList lines;
ProFile *includeFile;
{
QString contents;
{
QFile qfile(m_projectFilePath);
if (qfile.open(QIODevice::ReadOnly | QIODevice::Text)) {
contents = QString::fromLocal8Bit(qfile.readAll());
qfile.close();
lines = contents.split(QLatin1Char('\n'));
while (!lines.isEmpty() && lines.last().isEmpty())
lines.removeLast();
} else {
m_project->proFileParseError(tr("Error while reading PRO file %1: %2")
.arg(m_projectFilePath, qfile.errorString()));
return;
}
}
ProMessageHandler handler;
ProFileParser parser(0, &handler);
includeFile = parser.parsedProFile(m_projectFilePath, false, contents);
QDir priFileDir = QDir(m_qt4ProFileNode->m_projectDir);
// Use the first variable for adding.
// Yes, that's broken for adding objective c sources or other stuff.
ProWriter::addFiles(includeFile, &lines, priFileDir, filePaths, vars.first());
notChanged->clear();
*notChanged = ProWriter::removeFiles(includeFile, &lines, priFileDir, filePaths, vars);
save(lines);
// This is a hack.
// We are saving twice in a very short timeframe, once the editor and once the ProFile.
// So the modification time might not change between those two saves.
// We manually tell each editor to reload it's file.
// (The .pro files are notified by the file system watcher.)
foreach (Core::IEditor *editor, Core::ICore::instance()->editorManager()->editorsForFileName(m_projectFilePath)) {
if (Core::IFile *editorFile = editor->file()) {
editorFile->reload(Core::IFile::FlagReload, Core::IFile::TypeContents);
}
}
includeFile->deref();
void Qt4PriFileNode::save(const QStringList &lines)
QFile qfile(m_projectFilePath);
if (qfile.open(QIODevice::WriteOnly | QIODevice::Text)) {
foreach (const QString &str, lines) {
qfile.write(str.toLocal8Bit());
qfile.write("\n");
}
qfile.close();
}
m_project->qt4ProjectManager()->notifyChanged(m_projectFilePath);
}
/*
Deletes all subprojects/files/virtual folders
*/
void Qt4PriFileNode::clear()
{
// delete files && folders && projects
removeFileNodes(fileNodes(), this);
removeProjectNodes(subProjectNodes());
removeFolderNodes(subFolderNodes(), this);
QStringList Qt4PriFileNode::varNames(ProjectExplorer::FileType type)
{
QStringList vars;
switch (type) {
case ProjectExplorer::HeaderType:
vars << QLatin1String("HEADERS");
vars << QLatin1String("OBJECTIVE_HEADERS");
break;
case ProjectExplorer::SourceType:
vars << QLatin1String("SOURCES");
vars << QLatin1String("OBJECTIVE_SOURCES");
vars << QLatin1String("LEXSOURCES");
vars << QLatin1String("YACCSOURCES");
break;
case ProjectExplorer::ResourceType:
vars << QLatin1String("RESOURCES");
break;
case ProjectExplorer::FormType:
vars << QLatin1String("FORMS");
break;
case ProjectExplorer::QMLType:
break;
default:
vars << QLatin1String("OTHER_FILES");
break;
}
return vars;
}
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
QStringList Qt4PriFileNode::dynamicVarNames(ProFileReader *readerExact, ProFileReader *readerCumulative)
{
QStringList result;
// Figure out DEPLOYMENT and INSTALLS
QStringList listOfVars = readerExact->values("DEPLOYMENT");
foreach (const QString &var, listOfVars) {
result << (var + ".sources");
}
if (readerCumulative) {
QStringList listOfVars = readerCumulative->values("DEPLOYMENT");
foreach (const QString &var, listOfVars) {
result << (var + ".sources");
}
}
listOfVars = readerExact->values("INSTALLS");
foreach (const QString &var, listOfVars) {
result << (var + ".files");
}
if (readerCumulative) {
QStringList listOfVars = readerCumulative->values("INSTALLS");
foreach (const QString &var, listOfVars) {
result << (var + ".files");
}
}
return result;
}
QSet<QString> Qt4PriFileNode::filterFiles(ProjectExplorer::FileType fileType, const QSet<QString> &files)
{
QSet<QString> result;
if (fileType != ProjectExplorer::QMLType && fileType != ProjectExplorer::UnknownFileType)
return result;
if(fileType == ProjectExplorer::QMLType) {
foreach (const QString &file, files)
if (file.endsWith(".qml"))
result << file;
} else {
foreach (const QString &file, files)
if (!file.endsWith(".qml"))
result << file;
}
return result;
}
const Qt4ProFileNode *Qt4ProFileNode::findProFileFor(const QString &fileName) const
{
if (fileName == path())
return this;
foreach (ProjectNode *pn, subProjectNodes())
if (Qt4ProFileNode *qt4ProFileNode = qobject_cast<Qt4ProFileNode *>(pn))
if (const Qt4ProFileNode *result = qt4ProFileNode->findProFileFor(fileName))
return result;
return 0;
}
TargetInformation Qt4ProFileNode::targetInformation(const QString &fileName) const
const Qt4ProFileNode *qt4ProFileNode = findProFileFor(fileName);
if (!qt4ProFileNode)
return result;
return qt4ProFileNode->targetInformation();
}
/*!
\class Qt4ProFileNode
Implements abstract ProjectNode class
*/
Qt4ProFileNode::Qt4ProFileNode(Qt4Project *project,
const QString &filePath,
QObject *parent)
: Qt4PriFileNode(project, this, filePath),
m_projectType(InvalidProject),
m_readerExact(0),
m_readerCumulative(0)
connect(ProjectExplorer::ProjectExplorerPlugin::instance()->buildManager(), SIGNAL(buildStateChanged(ProjectExplorer::Project*)),
this, SLOT(buildStateChanged(ProjectExplorer::Project*)));
connect(&m_parseFutureWatcher, SIGNAL(finished()),
this, SLOT(applyAsyncEvaluate()));
CppTools::CppModelManagerInterface *modelManager
= ExtensionSystem::PluginManager::instance()->getObject<CppTools::CppModelManagerInterface>();
QMap<QString, Qt4UiCodeModelSupport *>::const_iterator it, end;
end = m_uiCodeModelSupport.constEnd();
for (it = m_uiCodeModelSupport.constBegin(); it != end; ++it) {
modelManager->removeEditorSupport(it.value());
delete it.value();
}
if (m_readerExact) {
// Oh we need to clean up
applyEvaluate(true, true);
m_project->decrementPendingEvaluateFutures();
}
}
bool Qt4ProFileNode::isParent(Qt4ProFileNode *node)
{
while ((node = qobject_cast<Qt4ProFileNode *>(node->parentFolderNode()))) {
if (node == this)
return true;
}
return false;
void Qt4ProFileNode::buildStateChanged(ProjectExplorer::Project *project)
{
if (project == m_project && !ProjectExplorer::ProjectExplorerPlugin::instance()->buildManager()->isBuilding(m_project)) {
QStringList filesToUpdate = updateUiFiles();
updateCodeModelSupportFromBuild(filesToUpdate);
}
bool Qt4ProFileNode::hasBuildTargets() 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_project->scheduleAsyncUpdate(this);
}
void Qt4ProFileNode::asyncUpdate()
{
m_project->incrementPendingEvaluateFutures();
setupReader();
QFuture<bool> future = QtConcurrent::run(&Qt4ProFileNode::asyncEvaluate, this);
m_parseFutureWatcher.setFuture(future);
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
setupReader();
bool parserError = evaluate();
applyEvaluate(!parserError, false);
}
void Qt4ProFileNode::setupReader()
{
Q_ASSERT(!m_readerExact);
Q_ASSERT(!m_readerCumulative);
m_readerExact = m_project->createProFileReader(this);
m_readerExact->setCumulative(false);
m_readerCumulative = m_project->createProFileReader(this);
// Find out what flags we pass on to qmake
QStringList addedUserConfigArguments;
QStringList removedUserConfigArguments;
m_project->activeTarget()->activeBuildConfiguration()->getConfigCommandLineArguments(&addedUserConfigArguments, &removedUserConfigArguments);
m_readerExact->setConfigCommandLineArguments(addedUserConfigArguments, removedUserConfigArguments);
m_readerCumulative->setConfigCommandLineArguments(addedUserConfigArguments, removedUserConfigArguments);
}
bool Qt4ProFileNode::evaluate()
{
bool parserError = false;
if (ProFile *pro = m_readerExact->parsedProFile(m_projectFilePath)) {
if (!m_readerExact->accept(pro, ProFileEvaluator::LoadAll))
parserError = true;
if (!m_readerCumulative->accept(pro, ProFileEvaluator::LoadPreFiles))
parserError = true;
pro->deref();
} else {
}
return parserError;
}
void Qt4ProFileNode::asyncEvaluate(QFutureInterface<bool> &fi)
{
bool parserError = evaluate();
fi.reportResult(!parserError);
}
void Qt4ProFileNode::applyAsyncEvaluate()
{
applyEvaluate(m_parseFutureWatcher.result(), true);
m_project->decrementPendingEvaluateFutures();
}
static Qt4ProjectType proFileTemplateTypeToProjectType(ProFileEvaluator::TemplateType type)
{
switch (type) {
case ProFileEvaluator::TT_Unknown:
case ProFileEvaluator::TT_Application:
return ApplicationTemplate;
case ProFileEvaluator::TT_Library:
return LibraryTemplate;
case ProFileEvaluator::TT_Script:
return ScriptTemplate;
case ProFileEvaluator::TT_Subdirs:
return SubDirsTemplate;
default:
return InvalidProject;
}
}
void Qt4ProFileNode::applyEvaluate(bool parseResult, bool async)
{
if (!m_readerExact)
return;
if (!parseResult || m_project->wasEvaluateCanceled()) {
m_project->destroyProFileReader(m_readerExact);
if (m_readerCumulative)
m_project->destroyProFileReader(m_readerCumulative);
m_readerExact = m_readerCumulative = 0;
if (!parseResult) {
m_project->proFileParseError(tr("Error while parsing file %1. Giving up.").arg(m_projectFilePath));
return;
}
if (debug)
qDebug() << "Qt4ProFileNode - updating files for file " << m_projectFilePath;
Qt4ProjectType projectType = InvalidProject;
// Check that both are the same if we have both
if (m_readerExact->templateType() != m_readerCumulative->templateType()) {
// Now what. The only thing which could be reasonable is that someone
// changes between template app and library.
// Well, we are conservative here for now.
// Let's wait until someone complains and look at what they are doing.
m_project->destroyProFileReader(m_readerCumulative);
m_readerCumulative = 0;
projectType = proFileTemplateTypeToProjectType(m_readerExact->templateType());
if (projectType != m_projectType) {
Qt4ProjectType oldType = m_projectType;
// probably all subfiles/projects have changed anyway ...
clear();
m_projectType = projectType;
// really emit here? or at the end? Noone is connected to this signal at the moment
// so we kind of can ignore that question for now
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();
QStringList newProjectFilesExact;
QHash<QString, ProFile*> includeFilesExact;
ProFile *fileForCurrentProjectExact = 0;
if (m_projectType == SubDirsTemplate)
newProjectFilesExact = subDirsPaths(m_readerExact);
foreach (ProFile *includeFile, m_readerExact->includeFiles()) {
if (includeFile->fileName() == m_projectFilePath) { // this file
fileForCurrentProjectExact = includeFile;
} else {
newProjectFilesExact << includeFile->fileName();
includeFilesExact.insert(includeFile->fileName(), includeFile);
QStringList newProjectFilesCumlative;
QHash<QString, ProFile*> includeFilesCumlative;
ProFile *fileForCurrentProjectCumlative = 0;
if (m_readerCumulative) {
if (m_projectType == SubDirsTemplate)
newProjectFilesCumlative = subDirsPaths(m_readerCumulative);
foreach (ProFile *includeFile, m_readerCumulative->includeFiles()) {
if (includeFile->fileName() == m_projectFilePath) {
fileForCurrentProjectCumlative = includeFile;
newProjectFilesCumlative << includeFile->fileName();
includeFilesCumlative.insert(includeFile->fileName(), includeFile);
}
}
}
qSort(existingProjectNodes.begin(), existingProjectNodes.end(),
sortNodesByPath);
qSort(newProjectFilesExact);
qSort(newProjectFilesCumlative);
QList<ProjectNode*> toAdd;
QList<ProjectNode*> toRemove;
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
QList<ProjectNode*>::const_iterator existingIt = existingProjectNodes.constBegin();
QStringList::const_iterator newExactIt = newProjectFilesExact.constBegin();
QStringList::const_iterator newCumlativeIt = newProjectFilesCumlative.constBegin();
forever {
bool existingAtEnd = (existingIt == existingProjectNodes.constEnd());
bool newExactAtEnd = (newExactIt == newProjectFilesExact.constEnd());
bool newCumlativeAtEnd = (newCumlativeIt == newProjectFilesCumlative.constEnd());
if (existingAtEnd && newExactAtEnd && newCumlativeAtEnd)
break; // we are done, hurray!
// So this is one giant loop comparing 3 lists at once and sorting the comparision
// into mainly 2 buckets: toAdd and toRemove
// We need to distinguish between nodes that came from exact and cumalative
// parsing, since the update call is diffrent for them
// I believe this code to be correct, be careful in changing it
QString nodeToAdd;
if (! existingAtEnd
&& (newExactAtEnd || (*existingIt)->path() < *newExactIt)
&& (newCumlativeAtEnd || (*existingIt)->path() < *newCumlativeIt)) {
// Remove case
toRemove << *existingIt;
++existingIt;
} else if(! newExactAtEnd
&& (existingAtEnd || *newExactIt < (*existingIt)->path())
&& (newCumlativeAtEnd || *newExactIt < *newCumlativeIt)) {
// Mark node from exact for adding
nodeToAdd = *newExactIt;
++newExactIt;
} else if (! newCumlativeAtEnd
&& (existingAtEnd || *newCumlativeIt < (*existingIt)->path())
&& (newExactAtEnd || *newCumlativeIt < *newExactIt)) {
// Mark node from cumalative for adding
nodeToAdd = *newCumlativeIt;
++newCumlativeIt;
} else if (!newExactAtEnd
&& !newCumlativeAtEnd
&& (existingAtEnd || *newExactIt < (*existingIt)->path())
&& (existingAtEnd || *newCumlativeIt < (*existingIt)->path())) {
// Mark node from both for adding
nodeToAdd = *newExactIt;
++newExactIt;
++newCumlativeIt;
} else {
Q_ASSERT(!newExactAtEnd || !newCumlativeAtEnd);
// update case, figure out which case exactly
if (newExactAtEnd) {
++newCumlativeIt;
} else if (newCumlativeAtEnd) {
++newExactIt;
} else if(*newExactIt < *newCumlativeIt) {
++newExactIt;
} else if (*newCumlativeIt < *newExactIt) {
++newCumlativeIt;
// Update existingNodeIte
ProFile *fileExact = includeFilesCumlative.value((*existingIt)->path());
ProFile *fileCumlative = includeFilesCumlative.value((*existingIt)->path());
if (fileExact || fileCumlative) {
static_cast<Qt4PriFileNode *>(*existingIt)->update(fileExact, m_readerExact, fileCumlative, m_readerCumulative);
} else {
// We always parse exactly, because we later when async parsing don't know whether
// the .pro file is included in this .pro file
// So to compare that later parse with the sync one
if (async)
static_cast<Qt4ProFileNode *>(*existingIt)->asyncUpdate();
else
static_cast<Qt4ProFileNode *>(*existingIt)->update();
++existingIt;
// newCumalativeIt and newExactIt are already incremented
if (!nodeToAdd.isEmpty()) {
ProFile *fileExact = includeFilesCumlative.value(nodeToAdd);
ProFile *fileCumlative = includeFilesCumlative.value(nodeToAdd);
// Loop preventation, make sure that exact same node is not in our parent chain
bool loop = false;
ProjectExplorer::Node *n = this;
while ((n = n->parentFolderNode())) {
if (qobject_cast<Qt4PriFileNode *>(n) && n->path() == nodeToAdd) {
loop = true;
break;
}
}
if (loop) {
// Do nothing
} else if (fileExact || fileCumlative) {
Qt4PriFileNode *qt4PriFileNode = new Qt4PriFileNode(m_project, this, nodeToAdd);
qt4PriFileNode->setParentFolderNode(this); // Needed for loop detection
qt4PriFileNode->update(fileExact, m_readerExact, fileCumlative, m_readerCumulative);
toAdd << qt4PriFileNode;
} else {
Qt4ProFileNode *qt4ProFileNode = new Qt4ProFileNode(m_project, nodeToAdd);
qt4ProFileNode->setParentFolderNode(this); // Needed for loop detection
if (async)
qt4ProFileNode->asyncUpdate();
else
qt4ProFileNode->update();
toAdd << qt4ProFileNode;
}
if (!toRemove.isEmpty())
removeProjectNodes(toRemove);
if (!toAdd.isEmpty())
addProjectNodes(toAdd);
Qt4PriFileNode::update(fileForCurrentProjectExact, m_readerExact, fileForCurrentProjectCumlative, m_readerCumulative);
// update TargetInformation
m_qt4targetInformation = targetInformation(m_readerExact);
// update other variables
QHash<Qt4Variable, QStringList> newVarValues;
newVarValues[DefinesVar] = m_readerExact->values(QLatin1String("DEFINES"));
newVarValues[IncludePathVar] = includePaths(m_readerExact);
newVarValues[UiDirVar] = QStringList() << uiDirPath(m_readerExact);
newVarValues[MocDirVar] = QStringList() << mocDirPath(m_readerExact);
newVarValues[PkgConfigVar] = m_readerExact->values(QLatin1String("PKGCONFIG"));
newVarValues[PrecompiledHeaderVar] =
m_readerExact->absoluteFileValues(QLatin1String("PRECOMPILED_HEADER"),
m_projectDir,
QStringList() << m_projectDir,
0);
newVarValues[LibDirectoriesVar] = libDirectories(m_readerExact);
newVarValues[ConfigVar] = m_readerExact->values(QLatin1String("CONFIG"));
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);
}
foreach (NodesWatcher *watcher, watchers())
if (Qt4NodesWatcher *qt4Watcher = qobject_cast<Qt4NodesWatcher*>(watcher))
emit qt4Watcher->proFileUpdated(this);
m_project->destroyProFileReader(m_readerExact);
if (m_readerCumulative)
m_project->destroyProFileReader(m_readerCumulative);
m_readerExact = 0;
m_readerCumulative = 0;
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
}
namespace {
// find all ui files in project
class FindUiFileNodesVisitor : public ProjectExplorer::NodesVisitor {
public:
void visitProjectNode(ProjectNode *projectNode)
{
visitFolderNode(projectNode);
}
void visitFolderNode(FolderNode *folderNode)
{
foreach (FileNode *fileNode, folderNode->fileNodes()) {
if (fileNode->fileType() == ProjectExplorer::FormType)
uiFileNodes << fileNode;
}
}
QList<FileNode*> uiFileNodes;
};
}
// This function is triggered after a build, and updates the state ui files
// 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
// Only those two project types can have ui files for us
if (m_projectType != ApplicationTemplate
&& m_projectType != LibraryTemplate)
FindUiFileNodesVisitor uiFilesVisitor;
this->accept(&uiFilesVisitor);
const QList<FileNode*> uiFiles = uiFilesVisitor.uiFileNodes;
// Find the UiDir, there can only ever be one
QString uiDir = buildDir();
QStringList tmp = m_varValues[UiDirVar];
if (tmp.size() != 0)
uiDir = tmp.first();
// Collect all existing generated files
QList<FileNode*> existingFileNodes;
foreach (FileNode *file, fileNodes()) {
if (file->isGenerated())
existingFileNodes << file;
}
// Convert uiFile to uiHeaderFilePath, find all headers that correspond
foreach (FileNode *uiFile, uiFiles) {
const QString uiHeaderFilePath
= QString("%1/ui_%2.h").arg(uiDir, QFileInfo(uiFile->path()).completeBaseName());
if (QFileInfo(uiHeaderFilePath).exists())
newFilePaths << uiHeaderFilePath;
// The list of files for which we call updateSourceFile
QStringList toUpdate;
qSort(newFilePaths);
qSort(existingFileNodes.begin(), existingFileNodes.end(), ProjectNode::sortNodesByPath);
QList<FileNode*>::const_iterator existingNodeIter = existingFileNodes.constBegin();
QList<QString>::const_iterator newPathIter = newFilePaths.constBegin();
while (existingNodeIter != existingFileNodes.constEnd()
&& newPathIter != newFilePaths.constEnd()) {
if ((*existingNodeIter)->path() < *newPathIter) {
toRemove << *existingNodeIter;
++existingNodeIter;
} else if ((*existingNodeIter)->path() > *newPathIter) {
toAdd << new FileNode(*newPathIter, ProjectExplorer::HeaderType, true);
++newPathIter;
} else { // *existingNodeIter->path() == *newPathIter
QString fileName = (*existingNodeIter)->path();
QMap<QString, QDateTime>::const_iterator it = m_uitimestamps.find(fileName);
QDateTime lastModified = QFileInfo(fileName).lastModified();
if (it == m_uitimestamps.constEnd() || it.value() < lastModified) {
toUpdate << fileName;
m_uitimestamps[fileName] = lastModified;
}
++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)
CppTools::CppModelManagerInterface *modelManager =
ExtensionSystem::PluginManager::instance()->getObject<CppTools::CppModelManagerInterface>();
foreach (FileNode *file, toAdd) {
m_uitimestamps.insert(file->path(), QFileInfo(file->path()).lastModified());
toUpdate << file->path();
// Also adding files depending on that
// We only need to do that for files that were newly created
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();
}
}
}
QString Qt4ProFileNode::uiDirPath(ProFileReader *reader) const
QString path = reader->value("UI_DIR");
if (QFileInfo(path).isRelative())
path = QDir::cleanPath(buildDir() + "/" + path);
return path;
QString Qt4ProFileNode::mocDirPath(ProFileReader *reader) const
QString path = reader->value("MOC_DIR");
if (QFileInfo(path).isRelative())
path = QDir::cleanPath(buildDir() + "/" + path);
return path;
}
QStringList Qt4ProFileNode::includePaths(ProFileReader *reader) const
{
QStringList paths;
foreach (const QString &cxxflags, m_readerExact->values("QMAKE_CXXFLAGS")) {
if (cxxflags.startsWith("-I"))
paths.append(cxxflags.mid(2));
}
paths.append(reader->absolutePathValues(QLatin1String("INCLUDEPATH"), m_projectDir));
// paths already contains moc dir and ui dir, due to corrrectly parsing uic.prf and moc.prf
// except if those directories don't exist at the time of parsing
// thus we add those directories manually (without checking for existance)
paths << mocDirPath(reader) << uiDirPath(reader);
QStringList Qt4ProFileNode::libDirectories(ProFileReader *reader) const
{
QStringList result;
foreach (const QString &str, reader->values(QLatin1String("LIBS"))) {
if (str.startsWith("-L")) {
result.append(str.mid(2));
}
}
return result;
}
QStringList Qt4ProFileNode::subDirsPaths(ProFileReader *reader) const
{
QStringList subProjectPaths;
const QStringList subDirVars = reader->values(QLatin1String("SUBDIRS"));
foreach (const QString &subDirVar, subDirVars) {
// Special case were subdir is just an identifier:
// "SUBDIR = subid
// subid.subdir = realdir"
// or
// "SUBDIR = subid
// subid.file = realdir/realfile.pro"
QString realDir;
const QString subDirKey = subDirVar + QLatin1String(".subdir");
const QString subDirFileKey = subDirVar + QLatin1String(".file");
realDir = reader->value(subDirKey);
realDir = reader->value(subDirFileKey);
info.setFile(m_projectDir + QLatin1Char('/') + realDir);
QString realFile;
if (info.isDir()) {
realFile = QString::fromLatin1("%1/%2.pro").arg(realDir, info.fileName());
if (QFile::exists(realFile)) {
} else {
m_project->proFileParseError(tr("Could not find .pro file for sub dir '%1' in '%2'")
.arg(subDirVar).arg(realDir));
}
TargetInformation Qt4ProFileNode::targetInformation(ProFileReader *reader) const
{
TargetInformation result;
if (!reader)
return result;
result.buildDir = buildDir();
const QString baseDir = result.buildDir;
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
// qDebug() << "base build dir is:"<<baseDir;
// Working Directory
if (reader->contains("DESTDIR")) {
//qDebug() << "reader contains destdir:" << reader->value("DESTDIR");
result.workingDir = reader->value("DESTDIR");
if (QDir::isRelativePath(result.workingDir)) {
result.workingDir = baseDir + QLatin1Char('/') + result.workingDir;
//qDebug() << "was relative and expanded to" << result.workingDir;
}
} else {
//qDebug() << "reader didn't contain DESTDIR, setting to " << baseDir;
result.workingDir = baseDir;
}
result.target = reader->value("TARGET");
if (result.target.isEmpty())
result.target = QFileInfo(m_projectFilePath).baseName();
#if defined (Q_OS_MAC)
if (reader->values("CONFIG").contains("app_bundle")) {
result.workingDir += QLatin1Char('/')
+ result.target
+ QLatin1String(".app/Contents/MacOS");
}
#endif
result.workingDir = QDir::cleanPath(result.workingDir);
QString wd = result.workingDir;
if ( (!reader->contains("DESTDIR") || reader->value("DESTDIR") == ".")
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
&& reader->values("CONFIG").contains("debug_and_release")
&& reader->values("CONFIG").contains("debug_and_release_target")) {
// If we don't have a destdir and debug and release is set
// then the executable is in a debug/release folder
//qDebug() << "reader has debug_and_release_target";
// Hmm can we find out whether it's debug or release in a saner way?
// Theoretically it's in CONFIG
QString qmakeBuildConfig = "release";
if (m_project->activeTarget()->activeBuildConfiguration()->qmakeBuildConfiguration() & QtVersion::DebugBuild)
qmakeBuildConfig = "debug";
wd += QLatin1Char('/') + qmakeBuildConfig;
}
result.executable = QDir::cleanPath(wd + QLatin1Char('/') + result.target);
//qDebug() << "##### updateTarget sets:" << result.workingDir << result.executable;
#if defined (Q_OS_WIN)
result.executable += QLatin1String(".exe");
#endif
result.valid = true;
return result;
}
TargetInformation Qt4ProFileNode::targetInformation() const
{
return m_qt4targetInformation;
}
QString Qt4ProFileNode::buildDir() const
{
const QDir srcDirRoot = QFileInfo(m_project->rootProjectNode()->path()).absoluteDir();
const QString relativeDir = srcDirRoot.relativeFilePath(m_projectDir);
return QDir(m_project->activeTarget()->activeBuildConfiguration()->buildDirectory()).absoluteFilePath(relativeDir);
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
}
/*
Sets project type to InvalidProject & deletes all subprojects/files/virtual folders
*/
void Qt4ProFileNode::invalidate()
{
if (m_projectType == InvalidProject)
return;
clear();
// change project type
Qt4ProjectType oldType = m_projectType;
m_projectType = InvalidProject;
foreach (NodesWatcher *watcher, watchers())
if (Qt4NodesWatcher *qt4Watcher = qobject_cast<Qt4NodesWatcher*>(watcher))
emit qt4Watcher->projectTypeChanged(this, oldType, InvalidProject);
}
void Qt4ProFileNode::updateCodeModelSupportFromBuild(const QStringList &files)
{
foreach (const QString &file, files) {
QMap<QString, Qt4UiCodeModelSupport *>::const_iterator it, end;
end = m_uiCodeModelSupport.constEnd();
for (it = m_uiCodeModelSupport.constBegin(); it != end; ++it) {
if (it.value()->fileName() == file)
it.value()->updateFromBuild();
}
}
}
void Qt4ProFileNode::updateCodeModelSupportFromEditor(const QString &uiFileName,
const QString &contents)
const QMap<QString, Qt4UiCodeModelSupport *>::const_iterator it =
m_uiCodeModelSupport.constFind(uiFileName);
if (it != m_uiCodeModelSupport.constEnd())
it.value()->updateFromEditor(contents);
foreach (ProjectExplorer::ProjectNode *pro, subProjectNodes())
if (Qt4ProFileNode *qt4proFileNode = qobject_cast<Qt4ProFileNode *>(pro))
qt4proFileNode->updateCodeModelSupportFromEditor(uiFileName, contents);
QString Qt4ProFileNode::uiDirectory() const
{
const Qt4VariablesHash::const_iterator it = m_varValues.constFind(UiDirVar);
if (it != m_varValues.constEnd() && !it.value().isEmpty())
return it.value().front();
return buildDir();
}
QString Qt4ProFileNode::uiHeaderFile(const QString &uiDir, const QString &formFile)
{
QString uiHeaderFilePath = uiDir;
uiHeaderFilePath += QLatin1String("/ui_");
uiHeaderFilePath += QFileInfo(formFile).completeBaseName();
uiHeaderFilePath += QLatin1String(".h");
return QDir::cleanPath(uiHeaderFilePath);
}
void Qt4ProFileNode::createUiCodeModelSupport()
{
CppTools::CppModelManagerInterface *modelManager
= ExtensionSystem::PluginManager::instance()->getObject<CppTools::CppModelManagerInterface>();
// First move all to
QMap<QString, Qt4UiCodeModelSupport *> oldCodeModelSupport;
oldCodeModelSupport = m_uiCodeModelSupport;
m_uiCodeModelSupport.clear();
// Only those two project types can have ui files for us
if (m_projectType == ApplicationTemplate || m_projectType == LibraryTemplate) {
// Find all ui files
FindUiFileNodesVisitor uiFilesVisitor;
this->accept(&uiFilesVisitor);
const QList<FileNode*> uiFiles = uiFilesVisitor.uiFileNodes;
// Find the UiDir, there can only ever be one
const QString uiDir = uiDirectory();
foreach (const FileNode *uiFile, uiFiles) {
const QString uiHeaderFilePath = uiHeaderFile(uiDir, uiFile->path());
// qDebug()<<"code model support for "<<uiFile->path()<<" "<<uiHeaderFilePath;
QMap<QString, Qt4UiCodeModelSupport *>::iterator it = oldCodeModelSupport.find(uiFile->path());
if (it != oldCodeModelSupport.end()) {
Qt4UiCodeModelSupport *cms = it.value();
cms->setFileName(uiHeaderFilePath);
m_uiCodeModelSupport.insert(it.key(), cms);
oldCodeModelSupport.erase(it);
} else {
Qt4UiCodeModelSupport *cms = new Qt4UiCodeModelSupport(modelManager, m_project, uiFile->path(), uiHeaderFilePath);
m_uiCodeModelSupport.insert(uiFile->path(), cms);
modelManager->addEditorSupport(cms);
}
}
}
// Remove old
QMap<QString, Qt4UiCodeModelSupport *>::const_iterator it, end;
end = oldCodeModelSupport.constEnd();
for (it = oldCodeModelSupport.constBegin(); it!=end; ++it) {
modelManager->removeEditorSupport(it.value());
delete it.value();
}
}
Qt4NodesWatcher::Qt4NodesWatcher(QObject *parent)
: NodesWatcher(parent)
{
}
} // namespace Internal
} // namespace Qt4ProjectManager