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>
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
// 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::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) { }
105
106
107
108
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
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);
}
279
280
281
282
283
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
// 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(key)) {
InternalNode *val = new InternalNode;
val->type = type;
val->fullPath = path;
currentNode->subnodes.insert(key, val);
currentNode = val;
} else {
currentNode = currentNode->subnodes.value(key);
path += separator;
} else { // key is filename
currentNode->files.append(file);
}
}
}
// Removes folder nodes with only a single sub folder in it
void compress()
{
static const QChar separator = QDir::separator(); // it is used for the *keys* which will become display name
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) {
QString key = i.value()->subnodes.begin().key();
newSubnodes.insert(i.key()+separator+key, i.value()->subnodes.value(key));
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;
}
QList<FolderNode *> foldersToRemove;
QList<FolderNode *> foldersToAdd;
typedef QPair<InternalNode *, FolderNode *> NodePair;
QList<NodePair> nodesToUpdate;
// newFolders is already sorted
qSort(existingFolderNodes.begin(), existingFolderNodes.end(), ProjectNode::sortFolderNodesByName);
QList<FolderNode*>::const_iterator existingNodeIter = existingFolderNodes.constBegin();
QMap<QString, InternalNode*>::const_iterator newNodeIter = subnodes.constBegin();;
while (existingNodeIter != existingFolderNodes.constEnd()
&& newNodeIter != subnodes.constEnd()) {
if ((*existingNodeIter)->displayName() < newNodeIter.key()) {
foldersToRemove << *existingNodeIter;
++existingNodeIter;
} else if ((*existingNodeIter)->displayName() > newNodeIter.key()) {
FolderNode *newNode = new FolderNode(newNodeIter.value()->fullPath);
newNode->setDisplayName(newNodeIter.key());
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.key());
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;
}
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;
for (int i = 0; i < fileTypes.size(); ++i) {
FileType type = fileTypes.at(i).type;
const QStringList qmakeVariables = varNames(type);
QStringList newFilePaths;
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);
if (readerCumulative)
newFilePaths += readerCumulative->absoluteFileValues(qmakeVariable, projectDir, vPathsCumulative, includeFileCumlative);
if (!newFilePaths.isEmpty()) {
InternalNode *subfolder = new InternalNode;
subfolder->type = type;
subfolder->icon = fileTypes.at(i).icon;
subfolder->fullPath = m_projectDir + "/#" + fileTypes.at(i).typeName;
contents.subnodes.insert(fileTypes.at(i).typeName, subfolder);
// create the hierarchy with subdirectories
subfolder->create(m_projectDir, newFilePaths, type);
contents.updateSubFolders(this, this);
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 << AddFile << RemoveFile;
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::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);
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
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(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;
default:
vars << QLatin1String("OTHER_FILES");
break;
}
return vars;
}
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);