OSDN Git Service

Merge remote branch 'origin/2.0'
[qt-creator-jp/qt-creator-jp.git] / src / plugins / qt4projectmanager / qt4nodes.cpp
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
8 **
9 ** Commercial Usage
10 **
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.
15 **
16 ** GNU Lesser General Public License Usage
17 **
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.
24 **
25 ** If you are unsure which license is appropriate for your use, please
26 ** contact the sales department at http://qt.nokia.com/contact.
27 **
28 **************************************************************************/
29
30 #include "profilereader.h"
31 #include "prowriter.h"
32 #include "qt4nodes.h"
33 #include "qt4project.h"
34 #include "qt4projectmanager.h"
35 #include "qt4projectmanagerconstants.h"
36 #include "qtuicodemodelsupport.h"
37 #include "qt4buildconfiguration.h"
38
39 #include <projectexplorer/nodesvisitor.h>
40
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>
48
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>
54
55 #include <utils/qtcassert.h>
56
57 #include <QtCore/QDebug>
58 #include <QtCore/QDir>
59 #include <QtCore/QFile>
60 #include <QtCore/QFileInfo>
61 #include <QtCore/QCoreApplication>
62 #include <QtCore/QXmlStreamReader>
63
64 #include <QtGui/QPainter>
65 #include <QtGui/QMainWindow>
66 #include <QtGui/QMessageBox>
67 #include <QtGui/QPushButton>
68 #include <qtconcurrent/QtConcurrentTools>
69
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.
73
74 struct FileTypeDataStorage {
75     ProjectExplorer::FileType type;
76     const char *typeName;
77     const char *icon;
78 };
79
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" }
96 };
97
98 struct Qt4NodeStaticData {
99     struct FileTypeData {
100         FileTypeData(ProjectExplorer::FileType t = ProjectExplorer::UnknownFileType,
101                      const QString &tN = QString(),
102                      const QIcon &i = QIcon()) :
103         type(t), typeName(tN), icon(i) { }
104
105         ProjectExplorer::FileType type;
106         QString typeName;
107         QIcon icon;
108     };
109
110     QVector<FileTypeData> fileTypeData;
111     QIcon projectIcon;
112 };
113
114 static void clearQt4NodeStaticData();
115
116 Q_GLOBAL_STATIC_WITH_INITIALIZER(Qt4NodeStaticData, qt4NodeStaticData, {
117     // File type data
118     const unsigned count = sizeof(fileTypeDataStorage)/sizeof(FileTypeDataStorage);
119     x->fileTypeData.reserve(count);
120
121     // Overlay the SP_DirIcon with the custom icons
122     const QSize desiredSize = QSize(16, 16);
123
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);
129         QIcon folderIcon;
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,
133                                                                   desc, folderIcon));
134     }
135     // Project icon
136     const QIcon projectBaseIcon(QLatin1String(":/qt4projectmanager/images/qt_project.png"));
137     const QPixmap projectPixmap = Core::FileIconProvider::overlayIcon(QStyle::SP_DirIcon,
138                                                                       projectBaseIcon,
139                                                                       desiredSize);
140     x->projectIcon.addPixmap(projectPixmap);
141
142     qAddPostRoutine(clearQt4NodeStaticData);
143 });
144
145 static void clearQt4NodeStaticData()
146 {
147     qt4NodeStaticData()->fileTypeData.clear();
148     qt4NodeStaticData()->projectIcon = QIcon();
149 }
150
151 enum { debug = 0 };
152
153 namespace {
154     // sorting helper function
155     bool sortProjectFilesByPath(ProFile *f1, ProFile *f2)
156     {
157         return f1->fileName() < f2->fileName();
158     }
159 }
160
161 namespace Qt4ProjectManager {
162 namespace Internal {
163
164 Qt4PriFile::Qt4PriFile(Qt4PriFileNode *qt4PriFile)
165     : IFile(qt4PriFile), m_priFile(qt4PriFile)
166 {
167
168 }
169
170 bool Qt4PriFile::save(const QString &fileName)
171 {
172     Q_UNUSED(fileName);
173     return false;
174 }
175
176 void Qt4PriFile::rename(const QString &newName)
177 {
178     // Can't happen
179     Q_ASSERT(false);
180     Q_UNUSED(newName);
181 }
182
183 QString Qt4PriFile::fileName() const
184 {
185     return m_priFile->path();
186 }
187
188 QString Qt4PriFile::defaultPath() const
189 {
190     return QString();
191 }
192
193 QString Qt4PriFile::suggestedFileName() const
194 {
195     return QString();
196 }
197
198 QString Qt4PriFile::mimeType() const
199 {
200     return Qt4ProjectManager::Constants::PROFILE_MIMETYPE;
201 }
202
203 bool Qt4PriFile::isModified() const
204 {
205     return false;
206 }
207
208 bool Qt4PriFile::isReadOnly() const
209 {
210     return false;
211 }
212
213 bool Qt4PriFile::isSaveAsAllowed() const
214 {
215     return false;
216 }
217
218 Core::IFile::ReloadBehavior Qt4PriFile::reloadBehavior(ChangeTrigger state, ChangeType type) const
219 {
220     Q_UNUSED(state)
221     Q_UNUSED(type)
222     return BehaviorSilent;
223 }
224
225 void Qt4PriFile::reload(ReloadFlag flag, ChangeType type)
226 {
227     Q_UNUSED(flag)
228     Q_UNUSED(type)
229     if (type == TypePermissions)
230         return;
231     m_priFile->scheduleUpdate();
232 }
233
234 /*!
235   \class Qt4PriFileNode
236   Implements abstract ProjectNode class
237   */
238
239 Qt4PriFileNode::Qt4PriFileNode(Qt4Project *project, Qt4ProFileNode* qt4ProFileNode, const QString &filePath)
240         : ProjectNode(filePath),
241           m_project(project),
242           m_qt4ProFileNode(qt4ProFileNode),
243           m_projectFilePath(QDir::fromNativeSeparators(filePath)),
244           m_projectDir(QFileInfo(filePath).absolutePath())
245 {
246     Q_ASSERT(project);
247     m_qt4PriFile = new Qt4PriFile(this);
248     Core::ICore::instance()->fileManager()->addFile(m_qt4PriFile);
249
250     setDisplayName(QFileInfo(filePath).completeBaseName());
251
252     setIcon(qt4NodeStaticData()->projectIcon);
253 }
254
255 void Qt4PriFileNode::scheduleUpdate()
256 {
257     ProFileCacheManager::instance()->discardFile(m_projectFilePath);
258     m_qt4ProFileNode->scheduleUpdate();
259 }
260
261 struct InternalNode
262 {
263     QMap<QString, InternalNode*> subnodes;
264     QStringList files;
265     ProjectExplorer::FileType type;
266     QString fullPath;
267     QIcon icon;
268
269     InternalNode()
270     {
271         type = ProjectExplorer::UnknownFileType;
272     }
273
274     ~InternalNode()
275     {
276         qDeleteAll(subnodes);
277     }
278
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.
281     // * project
282     //    * /absolute/path
283     //       * file1
284     //    * relative
285     //       * path1
286     //          * file1
287     //          * file2
288     //       * path2
289     //          * file1
290     // The method first creates a tree that looks like the directory structure, i.e.
291     //    * /
292     //       * absolute
293     //          * path
294     // ...
295     // and afterwards calls compress() which merges directory nodes with single children, i.e. to
296     //    * /absolute/path
297     void create(const QString &projectDir, const QStringList &newFilePaths, ProjectExplorer::FileType type)
298     {
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;
304             bool isRelative;
305             if (file.startsWith(projectDirWithSeparator)) {
306                 isRelative = true;
307                 fileWithoutPrefix = file.mid(projectDirWithSeparatorLength);
308             } else {
309                 isRelative = false;
310                 fileWithoutPrefix = file;
311             }
312             QStringList parts = fileWithoutPrefix.split(separator, QString::SkipEmptyParts);
313 #ifndef Q_OS_WIN
314             if (!isRelative && parts.count() > 0)
315                 parts[0].prepend(separator);
316 #endif
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
323                     path += key;
324                     if (!currentNode->subnodes.contains(key)) {
325                         InternalNode *val = new InternalNode;
326                         val->type = type;
327                         val->fullPath = path;
328                         currentNode->subnodes.insert(key, val);
329                         currentNode = val;
330                     } else {
331                         currentNode = currentNode->subnodes.value(key);
332                     }
333                     path += separator;
334                 } else { // key is filename
335                     currentNode->files.append(file);
336                 }
337             }
338         }
339         this->compress();
340     }
341
342     // Removes folder nodes with only a single sub folder in it
343     void compress()
344     {
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()) {
349             i.next();
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();
355                 delete i.value();
356             } else {
357                 newSubnodes.insert(i.key(), i.value());
358             }
359         }
360         subnodes = newSubnodes;
361     }
362
363     // Makes the projectNode's subtree below the given folder match this internal node's subtree
364     void updateSubFolders(Qt4PriFileNode *projectNode, ProjectExplorer::FolderNode *folder)
365     {
366         updateFiles(projectNode, folder, type);
367
368         // update folders
369         QList<FolderNode *> existingFolderNodes;
370         foreach (FolderNode *node, folder->subFolderNodes()) {
371             if (node->nodeType() != ProjectNodeType)
372                 existingFolderNodes << node;
373         }
374
375         QList<FolderNode *> foldersToRemove;
376         QList<FolderNode *> foldersToAdd;
377         typedef QPair<InternalNode *, FolderNode *> NodePair;
378         QList<NodePair> nodesToUpdate;
379
380         // newFolders is already sorted
381         qSort(existingFolderNodes.begin(), existingFolderNodes.end(), ProjectNode::sortFolderNodesByName);
382
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;
389                 ++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);
397                 ++newNodeIter;
398             } else { // *existingNodeIter->path() == *newPathIter
399                 nodesToUpdate << NodePair(newNodeIter.value(), *existingNodeIter);
400                 ++existingNodeIter;
401                 ++newNodeIter;
402             }
403         }
404
405         while (existingNodeIter != existingFolderNodes.constEnd()) {
406             foldersToRemove << *existingNodeIter;
407             ++existingNodeIter;
408         }
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);
416             ++newNodeIter;
417         }
418
419         if (!foldersToRemove.isEmpty())
420             projectNode->removeFolderNodes(foldersToRemove, folder);
421         if (!foldersToAdd.isEmpty())
422             projectNode->addFolderNodes(foldersToAdd, folder);
423
424         foreach (const NodePair &np, nodesToUpdate)
425             np.first->updateSubFolders(projectNode, np.second);
426     }
427
428     // Makes the folder's files match this internal node's file list
429     void updateFiles(Qt4PriFileNode *projectNode, FolderNode *folder, FileType type)
430     {
431         QList<FileNode*> existingFileNodes;
432         foreach (FileNode *fileNode, folder->fileNodes()) {
433             if (fileNode->fileType() == type && !fileNode->isGenerated())
434                 existingFileNodes << fileNode;
435         }
436
437         QList<FileNode*> filesToRemove;
438         QList<FileNode*> filesToAdd;
439
440         qSort(files);
441         qSort(existingFileNodes.begin(), existingFileNodes.end(), ProjectNode::sortNodesByPath);
442
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;
449                 ++existingNodeIter;
450             } else if ((*existingNodeIter)->path() > *newPathIter) {
451                 filesToAdd << new FileNode(*newPathIter, type, false);
452                 ++newPathIter;
453             } else { // *existingNodeIter->path() == *newPathIter
454                 ++existingNodeIter;
455                 ++newPathIter;
456             }
457         }
458         while (existingNodeIter != existingFileNodes.constEnd()) {
459             filesToRemove << *existingNodeIter;
460             ++existingNodeIter;
461         }
462         while (newPathIter != files.constEnd()) {
463             filesToAdd << new FileNode(*newPathIter, type, false);
464             ++newPathIter;
465         }
466
467         if (!filesToRemove.isEmpty())
468             projectNode->removeFileNodes(filesToRemove, folder);
469         if (!filesToAdd.isEmpty())
470             projectNode->addFileNodes(filesToAdd, folder);
471     }
472 };
473
474
475 QStringList Qt4PriFileNode::baseVPaths(ProFileReader *reader, const QString &projectDir)
476 {
477     QStringList result;
478     if (!reader)
479         return result;
480     result += reader->absolutePathValues("VPATH", projectDir);
481     result << projectDir; // QMAKE_ABSOLUTE_SOURCE_PATH
482     result += reader->absolutePathValues("DEPENDPATH", projectDir);
483     result.removeDuplicates();
484     return result;
485 }
486
487 QStringList Qt4PriFileNode::fullVPaths(const QStringList &baseVPaths, ProFileReader *reader, FileType type, const QString &qmakeVariable, const QString &projectDir)
488 {
489     QStringList vPaths;
490     if (!reader)
491         return vPaths;
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();
498     return vPaths;
499 }
500
501
502 void Qt4PriFileNode::update(ProFile *includeFileExact, ProFileReader *readerExact, ProFile *includeFileCumlative, ProFileReader *readerCumulative)
503 {
504     // add project file node
505     if (m_fileNodes.isEmpty())
506         addFileNodes(QList<FileNode*>() << new FileNode(m_projectFilePath, ProjectFileType, false), this);
507
508     const QString &projectDir = m_qt4ProFileNode->m_projectDir;
509
510     QStringList baseVPathsExact = baseVPaths(readerExact, projectDir);
511     QStringList baseVPathsCumulative = baseVPaths(readerCumulative, projectDir);
512
513     const QVector<Qt4NodeStaticData::FileTypeData> &fileTypes = qt4NodeStaticData()->fileTypeData;
514
515     InternalNode contents;
516
517     // update files
518     for (int i = 0; i < fileTypes.size(); ++i) {
519         FileType type = fileTypes.at(i).type;
520         const QStringList qmakeVariables = varNames(type);
521
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);
526
527
528             newFilePaths += readerExact->absoluteFileValues(qmakeVariable, projectDir, vPathsExact, includeFileExact);
529             if (readerCumulative)
530                 newFilePaths += readerCumulative->absoluteFileValues(qmakeVariable, projectDir, vPathsCumulative, includeFileCumlative);
531
532         }
533
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);
543         }
544     }
545     contents.updateSubFolders(this, this);
546 }
547
548 QList<ProjectNode::ProjectAction> Qt4PriFileNode::supportedActions(Node *node) const
549 {
550     QList<ProjectAction> actions;
551
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);
557
558     switch (proFileNode->projectType()) {
559     case ApplicationTemplate:
560     case LibraryTemplate:
561         actions << AddFile << RemoveFile;
562         break;
563     case SubDirsTemplate:
564         actions << AddSubProject << RemoveSubProject;
565         break;
566     default:
567         break;
568     }
569
570     FileNode *fileNode = qobject_cast<FileNode *>(node);
571     if (fileNode && fileNode->fileType() != ProjectExplorer::ProjectFileType)
572         actions << Rename;
573
574     return actions;
575 }
576
577 bool Qt4PriFileNode::addSubProjects(const QStringList &proFilePaths)
578 {
579     Q_UNUSED(proFilePaths)
580     return false; //changeIncludes(m_includeFile, proFilePaths, AddToProFile);
581 }
582
583 bool Qt4PriFileNode::removeSubProjects(const QStringList &proFilePaths)
584 {
585     Q_UNUSED(proFilePaths)
586     return false; //changeIncludes(m_includeFile, proFilePaths, RemoveFromProFile);
587 }
588
589 bool Qt4PriFileNode::addFiles(const FileType fileType, const QStringList &filePaths,
590                            QStringList *notAdded)
591 {
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.
596
597     ProjectExplorer::FindAllFilesVisitor visitor;
598     accept(&visitor);
599     const QStringList &allFiles = visitor.filePaths();
600
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);
608         }
609     }
610
611     QStringList uniqueQrcFiles;
612     foreach (const QString &file, qrcFiles) {
613         if (!allFiles.contains(file))
614             uniqueQrcFiles.append(file);
615     }
616
617     QStringList uniqueFilePaths;
618     foreach (const QString &file, filePaths) {
619         if (!allFiles.contains(file))
620             uniqueFilePaths.append(file);
621     }
622
623     QStringList failedFiles;
624     changeFiles(fileType, uniqueFilePaths, &failedFiles, AddToProFile);
625     if (notAdded)
626         *notAdded = failedFiles;
627     changeFiles(ProjectExplorer::ResourceType, uniqueQrcFiles, &failedFiles, AddToProFile);
628     if (notAdded)
629         *notAdded += failedFiles;
630     return failedFiles.isEmpty();
631 }
632
633 bool Qt4PriFileNode::removeFiles(const FileType fileType, const QStringList &filePaths,
634                               QStringList *notRemoved)
635 {
636     QStringList failedFiles;
637     changeFiles(fileType, filePaths, &failedFiles, RemoveFromProFile);
638     if (notRemoved)
639         *notRemoved = failedFiles;
640     return failedFiles.isEmpty();
641 }
642
643 bool Qt4PriFileNode::renameFile(const FileType fileType, const QString &filePath,
644                              const QString &newFilePath)
645 {
646     if (newFilePath.isEmpty())
647         return false;
648
649     QStringList dummy;
650     changeFiles(fileType, QStringList() << filePath, &dummy, RemoveFromProFile);
651     if (!dummy.isEmpty())
652         return false;
653     changeFiles(fileType, QStringList() << newFilePath, &dummy, AddToProFile);
654     if (!dummy.isEmpty())
655         return false;
656     return true;
657 }
658
659 bool Qt4PriFileNode::changeIncludes(ProFile *includeFile, const QStringList &proFilePaths,
660                                     ChangeType change)
661 {
662     Q_UNUSED(includeFile)
663     Q_UNUSED(proFilePaths)
664     Q_UNUSED(change)
665     // TODO
666     return false;
667 }
668
669 bool Qt4PriFileNode::priFileWritable(const QString &path)
670 {
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."));
678             return false;
679         }
680         break;
681     case Core::EditorManager::RO_MakeWriteable: {
682         const bool permsOk = QFile::setPermissions(path, QFile::permissions(path) | QFile::WriteUser);
683         if (!permsOk) {
684             QMessageBox::warning(core->mainWindow(), tr("Failed!"),  tr("Could not set permissions to writable."));
685             return false;
686         }
687         break;
688     }
689     case Core::EditorManager::RO_SaveAs:
690     case Core::EditorManager::RO_Cancel:
691         return false;
692     }
693     return true;
694 }
695
696 bool Qt4PriFileNode::saveModifiedEditors()
697 {
698     QList<Core::IFile*> modifiedFileHandles;
699
700     Core::ICore *core = Core::ICore::instance();
701
702     foreach (Core::IEditor *editor, core->editorManager()->editorsForFileName(m_projectFilePath)) {
703         if (Core::IFile *editorFile = editor->file()) {
704             if (editorFile->isModified())
705                 modifiedFileHandles << editorFile;
706         }
707     }
708
709     if (!modifiedFileHandles.isEmpty()) {
710         bool cancelled;
711         core->fileManager()->saveModifiedFiles(modifiedFileHandles, &cancelled,
712                                          tr("There are unsaved changes for project file %1.").arg(m_projectFilePath));
713         if (cancelled)
714             return false;
715         // force instant reload of ourselves
716         ProFileCacheManager::instance()->discardFile(m_projectFilePath);
717         m_project->qt4ProjectManager()->notifyChanged(m_projectFilePath);
718     }
719     return true;
720 }
721
722 QStringList Qt4PriFileNode::formResources(const QString &formFile) const
723 {
724     QStringList resourceFiles;
725     QFile file(formFile);
726     file.open(QIODevice::ReadOnly);
727     QXmlStreamReader reader(&file);
728
729     QFileInfo fi(formFile);
730     QDir formDir = fi.absoluteDir();
731     while (!reader.atEnd()) {
732         reader.readNext();
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())));
744
745             }
746         }
747     }
748
749     if (reader.hasError())
750         qWarning() << "Could not read form file:" << formFile;
751
752     return resourceFiles;
753 }
754
755 void Qt4PriFileNode::changeFiles(const FileType fileType,
756                                  const QStringList &filePaths,
757                                  QStringList *notChanged,
758                                  ChangeType change)
759 {
760     if (filePaths.isEmpty())
761         return;
762
763     *notChanged = filePaths;
764
765     // Check for modified editors
766     if (!saveModifiedEditors())
767         return;
768
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);
777             if (!makeWritable) {
778                 QMessageBox::warning(Core::ICore::instance()->mainWindow(),
779                                      tr("Failed!"),
780                                      tr("Could not write project file %1.").arg(m_projectFilePath));
781                 return;
782             }
783         }
784     }
785
786     QStringList lines;
787     ProFile *includeFile;
788     {
789         QString contents;
790         {
791             QFile qfile(m_projectFilePath);
792             if (qfile.open(QIODevice::ReadOnly | QIODevice::Text)) {
793                 contents = QString::fromLocal8Bit(qfile.readAll());
794                 qfile.close();
795                 lines = contents.split(QLatin1Char('\n'));
796                 while (!lines.isEmpty() && lines.last().isEmpty())
797                     lines.removeLast();
798             } else {
799                 m_project->proFileParseError(tr("Error while reading PRO file %1: %2")
800                                              .arg(m_projectFilePath, qfile.errorString()));
801                 return;
802             }
803         }
804
805         ProMessageHandler handler;
806         ProFileParser parser(0, &handler);
807         includeFile = parser.parsedProFile(m_projectFilePath, false, contents);
808     }
809
810     const QStringList vars = varNames(fileType);
811     QDir priFileDir = QDir(m_qt4ProFileNode->m_projectDir);
812
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());
817         notChanged->clear();
818     } else { // RemoveFromProFile
819         *notChanged = ProWriter::removeFiles(includeFile, &lines, priFileDir, filePaths, vars);
820     }
821
822     // save file
823     save(lines);
824
825     // This is a hack.
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);
833         }
834     }
835
836     includeFile->deref();
837 }
838
839 void Qt4PriFileNode::save(const QStringList &lines)
840 {
841     QFile qfile(m_projectFilePath);
842     if (qfile.open(QIODevice::WriteOnly | QIODevice::Text)) {
843         foreach (const QString &str, lines) {
844             qfile.write(str.toLocal8Bit());
845             qfile.write("\n");
846         }
847         qfile.close();
848     }
849
850     m_project->qt4ProjectManager()->notifyChanged(m_projectFilePath);
851 }
852
853 /*
854   Deletes all subprojects/files/virtual folders
855   */
856 void Qt4PriFileNode::clear()
857 {
858     // delete files && folders && projects
859     removeFileNodes(fileNodes(), this);
860     removeProjectNodes(subProjectNodes());
861     removeFolderNodes(subFolderNodes(), this);
862 }
863
864 QStringList Qt4PriFileNode::varNames(FileType type)
865 {
866     QStringList vars;
867     switch (type) {
868     case ProjectExplorer::HeaderType:
869         vars << QLatin1String("HEADERS");
870         vars << QLatin1String("OBJECTIVE_HEADERS");
871         break;
872     case ProjectExplorer::SourceType:
873         vars << QLatin1String("SOURCES");
874         vars << QLatin1String("OBJECTIVE_SOURCES");
875         vars << QLatin1String("LEXSOURCES");
876         vars << QLatin1String("YACCSOURCES");
877         break;
878     case ProjectExplorer::ResourceType:
879         vars << QLatin1String("RESOURCES");
880         break;
881     case ProjectExplorer::FormType:
882         vars << QLatin1String("FORMS");
883         break;
884     default:
885         vars << QLatin1String("OTHER_FILES");
886         break;
887     }
888     return vars;
889 }
890
891 Qt4ProFileNode *Qt4ProFileNode::findProFileFor(const QString &fileName)
892 {
893     if (fileName == path())
894         return this;
895     foreach (ProjectNode *pn, subProjectNodes())
896         if (Qt4ProFileNode *qt4ProFileNode = qobject_cast<Qt4ProFileNode *>(pn))
897             if (Qt4ProFileNode *result = qt4ProFileNode->findProFileFor(fileName))
898                 return result;
899     return 0;
900 }
901
902 TargetInformation Qt4ProFileNode::targetInformation(const QString &fileName)
903 {
904     TargetInformation result;
905     Qt4ProFileNode *qt4ProFileNode = findProFileFor(fileName);
906     if (!qt4ProFileNode)
907         return result;
908
909     return qt4ProFileNode->targetInformation();
910 }
911
912 /*!
913   \class Qt4ProFileNode
914   Implements abstract ProjectNode class
915   */
916 Qt4ProFileNode::Qt4ProFileNode(Qt4Project *project,
917                                const QString &filePath,
918                                QObject *parent)
919         : Qt4PriFileNode(project, this, filePath),
920           m_projectType(InvalidProject),
921           m_readerExact(0),
922           m_readerCumulative(0)
923 {
924
925     if (parent)
926         setParent(parent);
927
928     connect(ProjectExplorer::ProjectExplorerPlugin::instance()->buildManager(), SIGNAL(buildStateChanged(ProjectExplorer::Project*)),
929             this, SLOT(buildStateChanged(ProjectExplorer::Project*)));
930
931     connect(&m_parseFutureWatcher, SIGNAL(finished()),
932             this, SLOT(applyAsyncEvaluate()));
933 }
934
935 Qt4ProFileNode::~Qt4ProFileNode()
936 {
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());
943         delete it.value();
944     }
945     m_parseFutureWatcher.waitForFinished();
946     if (m_readerExact) {
947         // Oh we need to clean up
948         applyEvaluate(true, true);
949         m_project->decrementPendingEvaluateFutures();
950     }
951 }
952
953 bool Qt4ProFileNode::isParent(Qt4ProFileNode *node)
954 {
955     while ((node = qobject_cast<Qt4ProFileNode *>(node->parentFolderNode()))) {
956         if (node == this)
957             return true;
958     }
959     return false;
960 }
961
962 void Qt4ProFileNode::buildStateChanged(ProjectExplorer::Project *project)
963 {
964     if (project == m_project && !ProjectExplorer::ProjectExplorerPlugin::instance()->buildManager()->isBuilding(m_project)) {
965         QStringList filesToUpdate = updateUiFiles();
966         updateCodeModelSupportFromBuild(filesToUpdate);
967     }
968 }
969
970 bool Qt4ProFileNode::hasBuildTargets() const
971 {
972     return (projectType() == ApplicationTemplate) || (projectType() == LibraryTemplate);
973 }
974
975 Qt4ProjectType Qt4ProFileNode::projectType() const
976 {
977     return m_projectType;
978 }
979
980 QStringList Qt4ProFileNode::variableValue(const Qt4Variable var) const
981 {
982     return m_varValues.value(var);
983 }
984
985 void Qt4ProFileNode::scheduleUpdate()
986 {
987     m_project->scheduleAsyncUpdate(this);
988 }
989
990 void Qt4ProFileNode::asyncUpdate()
991 {
992     m_project->incrementPendingEvaluateFutures();
993     setupReader();
994     QFuture<bool> future = QtConcurrent::run(&Qt4ProFileNode::asyncEvaluate, this);
995     m_parseFutureWatcher.setFuture(future);
996 }
997
998 void Qt4ProFileNode::update()
999 {
1000     setupReader();
1001     bool parserError = evaluate();
1002     applyEvaluate(!parserError, false);
1003 }
1004
1005 void Qt4ProFileNode::setupReader()
1006 {
1007     Q_ASSERT(!m_readerExact);
1008     Q_ASSERT(!m_readerCumulative);
1009
1010     m_readerExact = m_project->createProFileReader(this);
1011     m_readerExact->setCumulative(false);
1012
1013     m_readerCumulative = m_project->createProFileReader(this);
1014
1015     // Find out what flags we pass on to qmake
1016     QStringList addedUserConfigArguments;
1017     QStringList removedUserConfigArguments;
1018     m_project->activeTarget()->activeBuildConfiguration()->getConfigCommandLineArguments(&addedUserConfigArguments, &removedUserConfigArguments);
1019
1020     m_readerExact->setConfigCommandLineArguments(addedUserConfigArguments, removedUserConfigArguments);
1021     m_readerCumulative->setConfigCommandLineArguments(addedUserConfigArguments, removedUserConfigArguments);
1022 }
1023
1024 bool Qt4ProFileNode::evaluate()
1025 {
1026     bool parserError = false;
1027     if (ProFile *pro = m_readerExact->parsedProFile(m_projectFilePath)) {
1028         if (!m_readerExact->accept(pro, ProFileEvaluator::LoadAll))
1029             parserError = true;
1030         if (!m_readerCumulative->accept(pro, ProFileEvaluator::LoadPreFiles))
1031             parserError = true;
1032         pro->deref();
1033     } else {
1034         parserError = true;
1035     }
1036     return parserError;
1037 }
1038
1039 void Qt4ProFileNode::asyncEvaluate(QFutureInterface<bool> &fi)
1040 {
1041     bool parserError = evaluate();
1042     fi.reportResult(!parserError);
1043 }
1044
1045 void Qt4ProFileNode::applyAsyncEvaluate()
1046 {
1047     applyEvaluate(m_parseFutureWatcher.result(), true);
1048     m_project->decrementPendingEvaluateFutures();
1049 }
1050
1051 static Qt4ProjectType proFileTemplateTypeToProjectType(ProFileEvaluator::TemplateType type)
1052 {
1053     switch (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;
1063     default:
1064         return InvalidProject;
1065     }
1066 }
1067
1068 void Qt4ProFileNode::applyEvaluate(bool parseResult, bool async)
1069 {
1070     if (!m_readerExact)
1071         return;
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;
1077         if (!parseResult) {
1078             m_project->proFileParseError(tr("Error while parsing file %1. Giving up.").arg(m_projectFilePath));
1079             invalidate();
1080         }
1081         return;
1082     }
1083
1084     if (debug)
1085         qDebug() << "Qt4ProFileNode - updating files for file " << m_projectFilePath;
1086
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;
1096     }
1097
1098     projectType = proFileTemplateTypeToProjectType(m_readerExact->templateType());
1099
1100     if (projectType != m_projectType) {
1101         Qt4ProjectType oldType = m_projectType;
1102         // probably all subfiles/projects have changed anyway ...
1103         clear();
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);
1110     }
1111
1112     //
1113     // Add/Remove pri files, sub projects
1114     //
1115
1116     QList<ProjectNode*> existingProjectNodes = subProjectNodes();
1117
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;
1126         } else {
1127             newProjectFilesExact << includeFile->fileName();
1128             includeFilesExact.insert(includeFile->fileName(), includeFile);
1129         }
1130     }
1131
1132
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;
1142             } else {
1143                 newProjectFilesCumlative << includeFile->fileName();
1144                 includeFilesCumlative.insert(includeFile->fileName(), includeFile);
1145             }
1146         }
1147     }
1148
1149     qSort(existingProjectNodes.begin(), existingProjectNodes.end(),
1150           sortNodesByPath);
1151     qSort(newProjectFilesExact);
1152     qSort(newProjectFilesCumlative);
1153
1154     QList<ProjectNode*> toAdd;
1155     QList<ProjectNode*> toRemove;
1156
1157     QList<ProjectNode*>::const_iterator existingIt = existingProjectNodes.constBegin();
1158     QStringList::const_iterator newExactIt = newProjectFilesExact.constBegin();
1159     QStringList::const_iterator newCumlativeIt = newProjectFilesCumlative.constBegin();
1160
1161     forever {
1162         bool existingAtEnd = (existingIt == existingProjectNodes.constEnd());
1163         bool newExactAtEnd = (newExactIt == newProjectFilesExact.constEnd());
1164         bool newCumlativeAtEnd = (newCumlativeIt == newProjectFilesCumlative.constEnd());
1165
1166         if (existingAtEnd && newExactAtEnd && newCumlativeAtEnd)
1167             break; // we are done, hurray!
1168
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
1174
1175         QString nodeToAdd;
1176         if (! existingAtEnd
1177             && (newExactAtEnd || (*existingIt)->path() < *newExactIt)
1178             && (newCumlativeAtEnd || (*existingIt)->path() < *newCumlativeIt)) {
1179             // Remove case
1180             toRemove << *existingIt;
1181             ++existingIt;
1182         } else if(! newExactAtEnd
1183                   && (existingAtEnd || *newExactIt < (*existingIt)->path())
1184                   && (newCumlativeAtEnd || *newExactIt < *newCumlativeIt)) {
1185             // Mark node from exact for adding
1186             nodeToAdd = *newExactIt;
1187             ++newExactIt;
1188         } else if (! newCumlativeAtEnd
1189                    && (existingAtEnd ||  *newCumlativeIt < (*existingIt)->path())
1190                    && (newExactAtEnd || *newCumlativeIt < *newExactIt)) {
1191             // Mark node from cumalative for adding
1192             nodeToAdd = *newCumlativeIt;
1193             ++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;
1200             ++newExactIt;
1201             ++newCumlativeIt;
1202         } else {
1203             Q_ASSERT(!newExactAtEnd || !newCumlativeAtEnd);
1204             // update case, figure out which case exactly
1205             if (newExactAtEnd) {
1206                 ++newCumlativeIt;
1207             } else if (newCumlativeAtEnd) {
1208                 ++newExactIt;
1209             } else if(*newExactIt < *newCumlativeIt) {
1210                 ++newExactIt;
1211             } else if (*newCumlativeIt < *newExactIt) {
1212                 ++newCumlativeIt;
1213             } else {
1214                 ++newExactIt;
1215                 ++newCumlativeIt;
1216             }
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);
1222             } else {
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
1226                 if (async)
1227                     static_cast<Qt4ProFileNode *>(*existingIt)->asyncUpdate();
1228                 else
1229                     static_cast<Qt4ProFileNode *>(*existingIt)->update();
1230             }
1231             ++existingIt;
1232             // newCumalativeIt and newExactIt are already incremented
1233
1234         }
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);
1239
1240             // Loop preventation, make sure that exact same node is not in our parent chain
1241             bool loop = false;
1242             ProjectExplorer::Node *n = this;
1243             while ((n = n->parentFolderNode())) {
1244                 if (qobject_cast<Qt4PriFileNode *>(n) && n->path() == nodeToAdd) {
1245                     loop = true;
1246                     break;
1247                 }
1248             }
1249
1250             if (loop) {
1251                 // Do nothing
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;
1257             } else {
1258                 Qt4ProFileNode *qt4ProFileNode = new Qt4ProFileNode(m_project, nodeToAdd);
1259                 qt4ProFileNode->setParentFolderNode(this); // Needed for loop detection
1260                 if (async)
1261                     qt4ProFileNode->asyncUpdate();
1262                 else
1263                     qt4ProFileNode->update();
1264                 toAdd << qt4ProFileNode;
1265             }
1266         }
1267     } // for
1268
1269     if (!toRemove.isEmpty())
1270         removeProjectNodes(toRemove);
1271     if (!toAdd.isEmpty())
1272         addProjectNodes(toAdd);
1273
1274     Qt4PriFileNode::update(fileForCurrentProjectExact, m_readerExact, fileForCurrentProjectCumlative, m_readerCumulative);
1275
1276     // update TargetInformation
1277     m_qt4targetInformation = targetInformation(m_readerExact);
1278
1279     // update other variables
1280     QHash<Qt4Variable, QStringList> newVarValues;
1281
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"),
1289                                               m_projectDir,
1290                                               QStringList() << m_projectDir,
1291                                               0);
1292     newVarValues[LibDirectoriesVar] = libDirectories(m_readerExact);
1293
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);
1299     }
1300
1301     createUiCodeModelSupport();
1302     updateUiFiles();
1303
1304     foreach (NodesWatcher *watcher, watchers())
1305         if (Qt4NodesWatcher *qt4Watcher = qobject_cast<Qt4NodesWatcher*>(watcher))
1306             emit qt4Watcher->proFileUpdated(this);
1307
1308     m_project->destroyProFileReader(m_readerExact);
1309     if (m_readerCumulative)
1310         m_project->destroyProFileReader(m_readerCumulative);
1311
1312     m_readerExact = 0;
1313     m_readerCumulative = 0;
1314 }
1315
1316 namespace {
1317     // find all ui files in project
1318     class FindUiFileNodesVisitor : public ProjectExplorer::NodesVisitor {
1319     public:
1320         void visitProjectNode(ProjectNode *projectNode)
1321         {
1322             visitFolderNode(projectNode);
1323         }
1324         void visitFolderNode(FolderNode *folderNode)
1325         {
1326             foreach (FileNode *fileNode, folderNode->fileNodes()) {
1327                 if (fileNode->fileType() == ProjectExplorer::FormType)
1328                     uiFileNodes << fileNode;
1329             }
1330         }
1331         QList<FileNode*> uiFileNodes;
1332     };
1333 }
1334
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.
1337
1338 // TODO this function should also be called if the build directory is changed
1339 QStringList Qt4ProFileNode::updateUiFiles()
1340 {
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();
1346
1347     // Find all ui files
1348     FindUiFileNodesVisitor uiFilesVisitor;
1349     this->accept(&uiFilesVisitor);
1350     const QList<FileNode*> uiFiles = uiFilesVisitor.uiFileNodes;
1351
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();
1357
1358     // Collect all existing generated files
1359     QList<FileNode*> existingFileNodes;
1360     foreach (FileNode *file, fileNodes()) {
1361         if (file->isGenerated())
1362             existingFileNodes << file;
1363     }
1364
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;
1373     }
1374
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;
1380
1381     qSort(newFilePaths);
1382     qSort(existingFileNodes.begin(), existingFileNodes.end(), ProjectNode::sortNodesByPath);
1383
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;
1390             ++existingNodeIter;
1391         } else if ((*existingNodeIter)->path() > *newPathIter) {
1392             toAdd << new FileNode(*newPathIter, ProjectExplorer::HeaderType, true);
1393             ++newPathIter;
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;
1401             }
1402             ++existingNodeIter;
1403             ++newPathIter;
1404         }
1405     }
1406     while (existingNodeIter != existingFileNodes.constEnd()) {
1407         toRemove << *existingNodeIter;
1408         ++existingNodeIter;
1409     }
1410     while (newPathIter != newFilePaths.constEnd()) {
1411         toAdd << new FileNode(*newPathIter, ProjectExplorer::HeaderType, true);
1412         ++newPathIter;
1413     }
1414
1415     // Update project tree
1416     if (!toRemove.isEmpty()) {
1417         foreach (FileNode *file, toRemove)
1418             m_uitimestamps.remove(file->path());
1419         removeFileNodes(toRemove, this);
1420     }
1421
1422     CppTools::CppModelManagerInterface *modelManager =
1423         ExtensionSystem::PluginManager::instance()->getObject<CppTools::CppModelManagerInterface>();
1424
1425     if (!toAdd.isEmpty()) {
1426         foreach (FileNode *file, toAdd) {
1427             m_uitimestamps.insert(file->path(), QFileInfo(file->path()).lastModified());
1428             toUpdate << file->path();
1429
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();
1437                 }
1438             }
1439         }
1440         addFileNodes(toAdd, this);
1441     }
1442     return toUpdate;
1443 }
1444
1445 QString Qt4ProFileNode::uiDirPath(ProFileReader *reader) const
1446 {
1447     QString path = reader->value("UI_DIR");
1448     if (QFileInfo(path).isRelative())
1449         path = QDir::cleanPath(buildDir() + "/" + path);
1450     return path;
1451 }
1452
1453 QString Qt4ProFileNode::mocDirPath(ProFileReader *reader) const
1454 {
1455     QString path = reader->value("MOC_DIR");
1456     if (QFileInfo(path).isRelative())
1457         path = QDir::cleanPath(buildDir() + "/" + path);
1458     return path;
1459 }
1460
1461 QStringList Qt4ProFileNode::includePaths(ProFileReader *reader) const
1462 {
1463     QStringList paths;
1464     foreach (const QString &cxxflags, m_readerExact->values("QMAKE_CXXFLAGS")) {
1465         if (cxxflags.startsWith("-I"))
1466             paths.append(cxxflags.mid(2));
1467     }
1468
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();
1475     return paths;
1476 }
1477
1478 QStringList Qt4ProFileNode::libDirectories(ProFileReader *reader) const
1479 {
1480     QStringList result;
1481     foreach (const QString &str, reader->values(QLatin1String("LIBS"))) {
1482         if (str.startsWith("-L")) {
1483             result.append(str.mid(2));
1484         }
1485     }
1486     return result;
1487 }
1488
1489 QStringList Qt4ProFileNode::subDirsPaths(ProFileReader *reader) const
1490 {
1491     QStringList subProjectPaths;
1492
1493     const QStringList subDirVars = reader->values(QLatin1String("SUBDIRS"));
1494
1495     foreach (const QString &subDirVar, subDirVars) {
1496         // Special case were subdir is just an identifier:
1497         //   "SUBDIR = subid
1498         //    subid.subdir = realdir"
1499         // or
1500         //   "SUBDIR = subid
1501         //    subid.file = realdir/realfile.pro"
1502
1503         QString realDir;
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);
1510         else
1511             realDir = subDirVar;
1512         QFileInfo info(realDir);
1513         if (!info.isAbsolute())
1514             info.setFile(m_projectDir + QLatin1Char('/') + realDir);
1515         realDir = info.filePath();
1516
1517         QString realFile;
1518         if (info.isDir()) {
1519             realFile = QString::fromLatin1("%1/%2.pro").arg(realDir, info.fileName());
1520         } else {
1521             realFile = realDir;
1522         }
1523
1524         if (QFile::exists(realFile)) {
1525             subProjectPaths << realFile;
1526         } else {
1527             m_project->proFileParseError(tr("Could not find .pro file for sub dir '%1' in '%2'")
1528                                          .arg(subDirVar).arg(realDir));
1529         }
1530     }
1531
1532     subProjectPaths.removeDuplicates();
1533     return subProjectPaths;
1534 }
1535
1536 TargetInformation Qt4ProFileNode::targetInformation(ProFileReader *reader) const
1537 {
1538     TargetInformation result;
1539     if (!reader)
1540         return result;
1541
1542     result.buildDir = buildDir();
1543     const QString baseDir = result.buildDir;
1544     // qDebug() << "base build dir is:"<<baseDir;
1545
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;
1553         }
1554     } else {
1555         //qDebug() << "reader didn't contain DESTDIR, setting to " << baseDir;
1556         result.workingDir = baseDir;
1557     }
1558
1559     result.target = reader->value("TARGET");
1560     if (result.target.isEmpty())
1561         result.target = QFileInfo(m_projectFilePath).baseName();
1562
1563 #if defined (Q_OS_MAC)
1564     if (reader->values("CONFIG").contains("app_bundle")) {
1565         result.workingDir += QLatin1Char('/')
1566                            + result.target
1567                            + QLatin1String(".app/Contents/MacOS");
1568     }
1569 #endif
1570
1571     result.workingDir = QDir::cleanPath(result.workingDir);
1572
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";
1580
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;
1587     }
1588
1589     result.executable = QDir::cleanPath(wd + QLatin1Char('/') + result.target);
1590     //qDebug() << "##### updateTarget sets:" << result.workingDir << result.executable;
1591
1592 #if defined (Q_OS_WIN)
1593     result.executable += QLatin1String(".exe");
1594 #endif
1595     result.valid = true;
1596     return result;
1597 }
1598
1599 TargetInformation Qt4ProFileNode::targetInformation()
1600 {
1601     return m_qt4targetInformation;
1602 }
1603
1604 QString Qt4ProFileNode::buildDir() const
1605 {
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);
1609 }
1610
1611 /*
1612   Sets project type to InvalidProject & deletes all subprojects/files/virtual folders
1613   */
1614 void Qt4ProFileNode::invalidate()
1615 {
1616     if (m_projectType == InvalidProject)
1617         return;
1618
1619     clear();
1620
1621     // change project type
1622     Qt4ProjectType oldType = m_projectType;
1623     m_projectType = InvalidProject;
1624
1625
1626     foreach (NodesWatcher *watcher, watchers())
1627         if (Qt4NodesWatcher *qt4Watcher = qobject_cast<Qt4NodesWatcher*>(watcher))
1628             emit qt4Watcher->projectTypeChanged(this, oldType, InvalidProject);
1629 }
1630
1631 void Qt4ProFileNode::updateCodeModelSupportFromBuild(const QStringList &files)
1632 {
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();
1639         }
1640     }
1641 }
1642
1643 void Qt4ProFileNode::updateCodeModelSupportFromEditor(const QString &uiFileName,
1644                                                       const QString &contents)
1645 {
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);
1653 }
1654
1655 QString Qt4ProFileNode::uiDirectory() const
1656 {
1657     const Qt4VariablesHash::const_iterator it = m_varValues.constFind(UiDirVar);
1658     if (it != m_varValues.constEnd() && !it.value().isEmpty())
1659         return it.value().front();
1660     return buildDir();
1661 }
1662
1663 QString Qt4ProFileNode::uiHeaderFile(const QString &uiDir, const QString &formFile)
1664 {
1665     QString uiHeaderFilePath = uiDir;
1666     uiHeaderFilePath += QLatin1String("/ui_");
1667     uiHeaderFilePath += QFileInfo(formFile).completeBaseName();
1668     uiHeaderFilePath += QLatin1String(".h");
1669     return QDir::cleanPath(uiHeaderFilePath);
1670 }
1671
1672 void Qt4ProFileNode::createUiCodeModelSupport()
1673 {
1674 //    qDebug()<<"creatUiCodeModelSupport()";
1675     CppTools::CppModelManagerInterface *modelManager
1676             = ExtensionSystem::PluginManager::instance()->getObject<CppTools::CppModelManagerInterface>();
1677
1678     // First move all to
1679     QMap<QString, Qt4UiCodeModelSupport *> oldCodeModelSupport;
1680     oldCodeModelSupport = m_uiCodeModelSupport;
1681     m_uiCodeModelSupport.clear();
1682
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;
1689
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);
1702             } else {
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);
1707             }
1708         }
1709     }
1710     // Remove old
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());
1715         delete it.value();
1716     }
1717 }
1718
1719 Qt4NodesWatcher::Qt4NodesWatcher(QObject *parent)
1720         : NodesWatcher(parent)
1721 {
1722 }
1723
1724 } // namespace Internal
1725 } // namespace Qt4ProjectManager