qt4nodes.cpp 85.9 KB
Newer Older
hjk's avatar
hjk committed
1
/**************************************************************************
con's avatar
con committed
2
3
4
**
** This file is part of Qt Creator
**
hjk's avatar
hjk committed
5
** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
con's avatar
con committed
6
**
7
** Contact: Nokia Corporation (qt-info@nokia.com)
con's avatar
con committed
8
**
9
**
hjk's avatar
hjk committed
10
** GNU Lesser General Public License Usage
11
**
hjk's avatar
hjk committed
12
13
14
15
16
17
** 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.
18
**
con's avatar
con committed
19
** In addition, as a special exception, Nokia gives you certain additional
hjk's avatar
hjk committed
20
** rights. These rights are described in the Nokia Qt LGPL Exception
con's avatar
con committed
21
22
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
23
24
25
26
27
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
con's avatar
con committed
28
** If you have questions regarding the use of this file, please contact
29
** Nokia at qt-info@nokia.com.
con's avatar
con committed
30
**
hjk's avatar
hjk committed
31
**************************************************************************/
hjk's avatar
hjk committed
32

con's avatar
con committed
33
34
#include "qt4nodes.h"
#include "qt4project.h"
35
#include "qt4target.h"
con's avatar
con committed
36
#include "qt4projectmanager.h"
37
#include "qt4projectmanagerconstants.h"
dt's avatar
dt committed
38
#include "qtuicodemodelsupport.h"
39
#include "qmakestep.h"
40
#include "qt4buildconfiguration.h"
con's avatar
con committed
41
42

#include <projectexplorer/nodesvisitor.h>
43
#include <projectexplorer/runconfiguration.h>
con's avatar
con committed
44
45

#include <coreplugin/editormanager/editormanager.h>
46
#include <coreplugin/editormanager/ieditor.h>
Jens Bache-Wiig's avatar
Jens Bache-Wiig committed
47
#include <coreplugin/fileiconprovider.h>
con's avatar
con committed
48
49
50
51
52
#include <coreplugin/filemanager.h>
#include <coreplugin/icore.h>
#include <coreplugin/iversioncontrol.h>
#include <coreplugin/vcsmanager.h>

53
#include <cplusplus/ModelManagerInterface.h>
54
#include <cplusplus/CppDocument.h>
55
#include <extensionsystem/pluginmanager.h>
56
57
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/buildmanager.h>
58
#include <qtsupport/profilereader.h>
con's avatar
con committed
59

hjk's avatar
hjk committed
60
#include <utils/qtcassert.h>
61
#include <utils/stringutils.h>
62
#include <utils/fileutils.h>
63
#include <proparser/prowriter.h>
64
#include <algorithm>
hjk's avatar
hjk committed
65

con's avatar
con committed
66
67
68
69
#include <QtCore/QDebug>
#include <QtCore/QDir>
#include <QtCore/QFile>
#include <QtCore/QFileInfo>
70
#include <QtCore/QCoreApplication>
71
#include <QtCore/QXmlStreamReader>
hjk's avatar
hjk committed
72

Jens Bache-Wiig's avatar
Jens Bache-Wiig committed
73
#include <QtGui/QPainter>
con's avatar
con committed
74
75
76
#include <QtGui/QMainWindow>
#include <QtGui/QMessageBox>
#include <QtGui/QPushButton>
77
#include <utils/QtConcurrentTools>
con's avatar
con committed
78

79
80
81
82
83
84
85
86
87
88
89
90
// 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,
Daniel Teske's avatar
Daniel Teske committed
91
      QT_TRANSLATE_NOOP("Qt4ProjectManager::Qt4PriFileNode", "Headers"),
92
93
      ":/qt4projectmanager/images/headers.png" },
    { ProjectExplorer::SourceType,
Daniel Teske's avatar
Daniel Teske committed
94
      QT_TRANSLATE_NOOP("Qt4ProjectManager::Qt4PriFileNode", "Sources"),
95
96
      ":/qt4projectmanager/images/sources.png" },
    { ProjectExplorer::FormType,
Daniel Teske's avatar
Daniel Teske committed
97
      QT_TRANSLATE_NOOP("Qt4ProjectManager::Qt4PriFileNode", "Forms"),
98
99
      ":/qt4projectmanager/images/forms.png" },
    { ProjectExplorer::ResourceType,
Daniel Teske's avatar
Daniel Teske committed
100
      QT_TRANSLATE_NOOP("Qt4ProjectManager::Qt4PriFileNode", "Resources"),
101
      ":/qt4projectmanager/images/qt_qrc.png" },
102
    { ProjectExplorer::QMLType,
Daniel Teske's avatar
Daniel Teske committed
103
      QT_TRANSLATE_NOOP("Qt4ProjectManager::Qt4PriFileNode", "QML"),
dt's avatar
dt committed
104
      ":/qt4projectmanager/images/qml.ico" }, // TODO icon
105
    { ProjectExplorer::UnknownFileType,
Daniel Teske's avatar
Daniel Teske committed
106
      QT_TRANSLATE_NOOP("Qt4ProjectManager::Qt4PriFileNode", "Other files"),
107
108
109
110
111
112
113
114
115
      ":/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) { }
con's avatar
con committed
116

117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
        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);

Friedemann Kleint's avatar
Friedemann Kleint committed
136
    for (unsigned i = 0 ; i < count; ++i) {
137
138
139
140
141
142
        const QIcon overlayIcon = QIcon(QLatin1String(fileTypeDataStorage[i].icon));
        const QPixmap folderPixmap =
                Core::FileIconProvider::overlayIcon(QStyle::SP_DirIcon,
                                                    overlayIcon, desiredSize);
        QIcon folderIcon;
        folderIcon.addPixmap(folderPixmap);
Daniel Teske's avatar
Daniel Teske committed
143
        const QString desc = Qt4ProjectManager::Qt4PriFileNode::tr(fileTypeDataStorage[i].typeName);
144
145
146
147
148
149
150
151
152
153
154
        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);
155
})
156
157
158
159
160

static void clearQt4NodeStaticData()
{
    qt4NodeStaticData()->fileTypeData.clear();
    qt4NodeStaticData()->projectIcon = QIcon();
con's avatar
con committed
161
162
}

163
164
enum { debug = 0 };

Daniel Teske's avatar
Daniel Teske committed
165
166
using namespace Qt4ProjectManager;
using namespace Qt4ProjectManager::Internal;
167

168
Qt4PriFile::Qt4PriFile(Qt4ProjectManager::Qt4PriFileNode *qt4PriFile)
169
170
171
172
173
    : IFile(qt4PriFile), m_priFile(qt4PriFile)
{

}

174
bool Qt4PriFile::save(QString *errorString, const QString &fileName, bool autoSave)
175
{
176
    Q_UNUSED(errorString);
177
    Q_UNUSED(fileName);
178
    Q_UNUSED(autoSave);
179
180
181
    return false;
}

dt's avatar
dt committed
182
183
184
185
186
187
188
void Qt4PriFile::rename(const QString &newName)
{
    // Can't happen
    Q_ASSERT(false);
    Q_UNUSED(newName);
}

189
190
191
192
193
194
195
QString Qt4PriFile::fileName() const
{
    return m_priFile->path();
}

QString Qt4PriFile::defaultPath() const
{
196
    return QString();
197
198
199
200
}

QString Qt4PriFile::suggestedFileName() const
{
201
    return QString();
202
203
204
205
}

QString Qt4PriFile::mimeType() const
{
206
    return QLatin1String(Qt4ProjectManager::Constants::PROFILE_MIMETYPE);
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
}

bool Qt4PriFile::isModified() const
{
    return false;
}

bool Qt4PriFile::isReadOnly() const
{
    return false;
}

bool Qt4PriFile::isSaveAsAllowed() const
{
    return false;
}

224
Core::IFile::ReloadBehavior Qt4PriFile::reloadBehavior(ChangeTrigger state, ChangeType type) const
225
{
226
227
228
    Q_UNUSED(state)
    Q_UNUSED(type)
    return BehaviorSilent;
229
230
}

231
bool Qt4PriFile::reload(QString *errorString, ReloadFlag flag, ChangeType type)
232
{
233
    Q_UNUSED(errorString)
234
235
    Q_UNUSED(flag)
    if (type == TypePermissions)
236
        return true;
237
    m_priFile->scheduleUpdate();
238
    return true;
239
}
240

con's avatar
con committed
241
242
243
244
245
/*!
  \class Qt4PriFileNode
  Implements abstract ProjectNode class
  */

246
247
namespace Qt4ProjectManager {

248
Qt4PriFileNode::Qt4PriFileNode(Qt4Project *project, Qt4ProFileNode* qt4ProFileNode, const QString &filePath)
con's avatar
con committed
249
250
        : ProjectNode(filePath),
          m_project(project),
251
          m_qt4ProFileNode(qt4ProFileNode),
252
          m_projectFilePath(QDir::fromNativeSeparators(filePath)),
253
254
          m_projectDir(QFileInfo(filePath).absolutePath()),
          m_includedInExactParse(true)
con's avatar
con committed
255
{
dt's avatar
dt committed
256
    Q_ASSERT(project);
257
    m_qt4PriFile = new Qt4PriFile(this);
258
    Core::FileManager::addFile(m_qt4PriFile);
259

260
    setDisplayName(QFileInfo(filePath).completeBaseName());
Jens Bache-Wiig's avatar
Jens Bache-Wiig committed
261

262
    setIcon(qt4NodeStaticData()->projectIcon);
263
264
265
266
}

void Qt4PriFileNode::scheduleUpdate()
{
267
    QtSupport::ProFileCacheManager::instance()->discardFile(m_projectFilePath);
268
    m_qt4ProFileNode->scheduleUpdate();
con's avatar
con committed
269
270
}

Daniel Teske's avatar
Daniel Teske committed
271
namespace Internal {
272
struct InternalNode
273
274
275
276
{
    QMap<QString, InternalNode*> subnodes;
    QStringList files;
    ProjectExplorer::FileType type;
277
    QString displayName;
278
279
    QString fullPath;
    QIcon icon;
280

281
282
283
284
    InternalNode()
    {
        type = ProjectExplorer::UnknownFileType;
    }
285

286
287
288
289
    ~InternalNode()
    {
        qDeleteAll(subnodes);
    }
290

291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
    // 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
309
    void create(const QString &projectDir, const QSet<Utils::FileName> &newFilePaths, ProjectExplorer::FileType type)
310
    {
311
        static const QChar separator = QLatin1Char('/');
312
        const Utils::FileName projectDirFileName = Utils::FileName::fromString(projectDir);
313
314
        foreach (const Utils::FileName &file, newFilePaths) {
            Utils::FileName fileWithoutPrefix;
315
            bool isRelative;
316
            if (file.isChildOf(projectDirFileName)) {
317
                isRelative = true;
318
                fileWithoutPrefix = file.relativeChildPath(projectDirFileName);
319
320
321
322
            } else {
                isRelative = false;
                fileWithoutPrefix = file;
            }
323
            QStringList parts = fileWithoutPrefix.toString().split(separator, QString::SkipEmptyParts);
324
#ifndef Q_OS_WIN
325
326
            if (!isRelative && parts.count() > 0)
                parts[0].prepend(separator);
327
#endif
328
329
            QStringListIterator it(parts);
            InternalNode *currentNode = this;
330
            QString path = (isRelative ? (projectDirFileName.toString() + QLatin1Char('/')) : QString());
331
332
333
334
            while (it.hasNext()) {
                const QString &key = it.next();
                if (it.hasNext()) { // key is directory
                    path += key;
335
                    if (!currentNode->subnodes.contains(path)) {
336
337
338
                        InternalNode *val = new InternalNode;
                        val->type = type;
                        val->fullPath = path;
339
340
                        val->displayName = key;
                        currentNode->subnodes.insert(path, val);
341
342
                        currentNode = val;
                    } else {
343
                        currentNode = currentNode->subnodes.value(path);
344
                    }
345
346
                    path += separator;
                } else { // key is filename
347
                    currentNode->files.append(file.toString());
348
349
350
                }
            }
        }
351
352
        this->compress();
    }
353

354
355
356
357
358
359
360
361
362
    // Removes folder nodes with only a single sub folder in it
    void compress()
    {
        QMap<QString, InternalNode*> newSubnodes;
        QMapIterator<QString, InternalNode*> i(subnodes);
        while (i.hasNext()) {
            i.next();
            i.value()->compress();
            if (i.value()->files.isEmpty() && i.value()->subnodes.size() == 1) {
363
                // replace i.value() by i.value()->subnodes.begin()
364
                QString key = i.value()->subnodes.begin().key();
365
                InternalNode *keep = i.value()->subnodes.value(key);
366
                keep->displayName = i.value()->displayName + QLatin1Char('/') + keep->displayName;
367
                newSubnodes.insert(key, keep);
368
369
370
371
                i.value()->subnodes.clear();
                delete i.value();
            } else {
                newSubnodes.insert(i.key(), i.value());
372
373
            }
        }
374
375
        subnodes = newSubnodes;
    }
376

377
    // Makes the projectNode's subtree below the given folder match this internal node's subtree
378
    void updateSubFolders(Qt4ProjectManager::Qt4PriFileNode *projectNode, ProjectExplorer::FolderNode *folder)
379
380
    {
        updateFiles(projectNode, folder, type);
381

382
383
384
385
386
387
        // update folders
        QList<FolderNode *> existingFolderNodes;
        foreach (FolderNode *node, folder->subFolderNodes()) {
            if (node->nodeType() != ProjectNodeType)
                existingFolderNodes << node;
        }
388

389
390
        qSort(existingFolderNodes.begin(), existingFolderNodes.end(), ProjectNode::sortNodesByPath);

391
392
393
394
395
        QList<FolderNode *> foldersToRemove;
        QList<FolderNode *> foldersToAdd;
        typedef QPair<InternalNode *, FolderNode *> NodePair;
        QList<NodePair> nodesToUpdate;

396
        // Both lists should be already sorted...
397
398
399
400
        QList<FolderNode*>::const_iterator existingNodeIter = existingFolderNodes.constBegin();
        QMap<QString, InternalNode*>::const_iterator newNodeIter = subnodes.constBegin();;
        while (existingNodeIter != existingFolderNodes.constEnd()
               && newNodeIter != subnodes.constEnd()) {
401
            if ((*existingNodeIter)->path() < newNodeIter.value()->fullPath) {
402
403
                foldersToRemove << *existingNodeIter;
                ++existingNodeIter;
404
            } else if ((*existingNodeIter)->path() > newNodeIter.value()->fullPath) {
405
                FolderNode *newNode = new FolderNode(newNodeIter.value()->fullPath);
406
                newNode->setDisplayName(newNodeIter.value()->displayName);
407
408
409
                if (!newNodeIter.value()->icon.isNull())
                    newNode->setIcon(newNodeIter.value()->icon);
                foldersToAdd << newNode;
410
                nodesToUpdate << NodePair(newNodeIter.value(), newNode);
411
                ++newNodeIter;
412
413
414
415
            } else { // *existingNodeIter->path() == *newPathIter
                nodesToUpdate << NodePair(newNodeIter.value(), *existingNodeIter);
                ++existingNodeIter;
                ++newNodeIter;
416
            }
417
418
419
420
421
422
423
424
        }

        while (existingNodeIter != existingFolderNodes.constEnd()) {
            foldersToRemove << *existingNodeIter;
            ++existingNodeIter;
        }
        while (newNodeIter != subnodes.constEnd()) {
            FolderNode *newNode = new FolderNode(newNodeIter.value()->fullPath);
425
            newNode->setDisplayName(newNodeIter.value()->displayName);
426
427
428
429
430
431
            if (!newNodeIter.value()->icon.isNull())
                newNode->setIcon(newNodeIter.value()->icon);
            foldersToAdd << newNode;
            nodesToUpdate << NodePair(newNodeIter.value(), newNode);
            ++newNodeIter;
        }
432

433
434
435
436
        if (!foldersToRemove.isEmpty())
            projectNode->removeFolderNodes(foldersToRemove, folder);
        if (!foldersToAdd.isEmpty())
            projectNode->addFolderNodes(foldersToAdd, folder);
437

438
439
440
441
442
        foreach (const NodePair &np, nodesToUpdate)
            np.first->updateSubFolders(projectNode, np.second);
    }

    // Makes the folder's files match this internal node's file list
443
    void updateFiles(Qt4ProjectManager::Qt4PriFileNode *projectNode, FolderNode *folder, FileType type)
444
445
446
447
448
    {
        QList<FileNode*> existingFileNodes;
        foreach (FileNode *fileNode, folder->fileNodes()) {
            if (fileNode->fileType() == type && !fileNode->isGenerated())
                existingFileNodes << fileNode;
449
450
        }

451
452
        QList<FileNode*> filesToRemove;
        QList<FileNode*> filesToAdd;
453

454
455
456
457
458
459
460
461
        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) {
462
463
                filesToRemove << *existingNodeIter;
                ++existingNodeIter;
464
            } else if ((*existingNodeIter)->path() > *newPathIter) {
465
                filesToAdd << new ProjectExplorer::FileNode(*newPathIter, type, false);
466
                ++newPathIter;
467
468
469
            } else { // *existingNodeIter->path() == *newPathIter
                ++existingNodeIter;
                ++newPathIter;
470
471
            }
        }
472
473
474
475
476
        while (existingNodeIter != existingFileNodes.constEnd()) {
            filesToRemove << *existingNodeIter;
            ++existingNodeIter;
        }
        while (newPathIter != files.constEnd()) {
477
            filesToAdd << new ProjectExplorer::FileNode(*newPathIter, type, false);
478
479
480
481
482
483
484
485
486
            ++newPathIter;
        }

        if (!filesToRemove.isEmpty())
            projectNode->removeFileNodes(filesToRemove, folder);
        if (!filesToAdd.isEmpty())
            projectNode->addFileNodes(filesToAdd, folder);
    }
};
Daniel Teske's avatar
Daniel Teske committed
487
}
dt's avatar
dt committed
488

489
QStringList Qt4PriFileNode::baseVPaths(QtSupport::ProFileReader *reader, const QString &projectDir)
con's avatar
con committed
490
{
dt's avatar
dt committed
491
492
493
    QStringList result;
    if (!reader)
        return result;
494
    result += reader->absolutePathValues(QLatin1String("VPATH"), projectDir);
dt's avatar
dt committed
495
    result << projectDir; // QMAKE_ABSOLUTE_SOURCE_PATH
496
    result += reader->absolutePathValues(QLatin1String("DEPENDPATH"), projectDir);
dt's avatar
dt committed
497
498
499
    result.removeDuplicates();
    return result;
}
con's avatar
con committed
500

501
QStringList Qt4PriFileNode::fullVPaths(const QStringList &baseVPaths, QtSupport::ProFileReader *reader, FileType type, const QString &qmakeVariable, const QString &projectDir)
dt's avatar
dt committed
502
503
504
505
506
{
    QStringList vPaths;
    if (!reader)
        return vPaths;
    if (type == ProjectExplorer::SourceType)
507
        vPaths = reader->absolutePathValues(QLatin1String("VPATH_") + qmakeVariable, projectDir);
dt's avatar
dt committed
508
509
    vPaths += baseVPaths;
    if (type == ProjectExplorer::HeaderType)
510
        vPaths += reader->absolutePathValues(QLatin1String("INCLUDEPATH"), projectDir);
dt's avatar
dt committed
511
512
513
514
    vPaths.removeDuplicates();
    return vPaths;
}

515
static QSet<Utils::FileName> recursiveEnumerate(const QString &folder)
516
{
517
    QSet<Utils::FileName> result;
518
519
520
521
522
523
    QFileInfo fi(folder);
    if (fi.isDir()) {
        QDir dir(folder);
        dir.setFilter(dir.filter() | QDir::NoDotAndDotDot);

        foreach (const QFileInfo &file, dir.entryInfoList()) {
524
            if (file.isDir() && !file.isSymLink())
525
526
                result += recursiveEnumerate(file.absoluteFilePath());
            else
527
                result += Utils::FileName(file);
528
529
        }
    } else if (fi.exists()) {
530
        result << Utils::FileName(fi);
531
532
533
    }
    return result;
}
dt's avatar
dt committed
534

535
void Qt4PriFileNode::update(ProFile *includeFileExact, QtSupport::ProFileReader *readerExact, ProFile *includeFileCumlative, QtSupport::ProFileReader *readerCumulative)
dt's avatar
dt committed
536
{
con's avatar
con committed
537
538
    // add project file node
    if (m_fileNodes.isEmpty())
539
        addFileNodes(QList<FileNode*>() << new ProjectExplorer::FileNode(m_projectFilePath, ProjectExplorer::ProjectFileType, false), this);
con's avatar
con committed
540

541
542
    const QString &projectDir = m_qt4ProFileNode->m_projectDir;

543
544
    InternalNode contents;

545
546
547
    // Figure out DEPLOYMENT and INSTALL folders
    QStringList folders;
    QStringList dynamicVariables = dynamicVarNames(readerExact, readerCumulative);
548
549
550
551
552
553
554
555
    if (includeFileExact)
        foreach (const QString &dynamicVar, dynamicVariables) {
            folders += readerExact->values(dynamicVar, includeFileExact);
            // Ignore stuff from cumulative parse
            // we are recursively enumerating all the files from those folders
            // and add watchers for them, that's too dangerous if we get the foldrs
            // wrong and enumerate the whole project tree multiple times
        }
556
557
558


    for (int i=0; i < folders.size(); ++i) {
559
        const QFileInfo fi(folders.at(i));
560
        if (fi.isRelative())
561
            folders[i] = QDir::cleanPath(projectDir + QLatin1Char('/') + folders.at(i));
562
563
    }

dt's avatar
dt committed
564
565

    m_recursiveEnumerateFiles.clear();
566
567
    // Remove non existing items and non folders
    // todo fix files in INSTALL rules
dt's avatar
dt committed
568
569
570
    QStringList::iterator it = folders.begin();
    while (it != folders.end()) {
        QFileInfo fi(*it);
dt's avatar
dt committed
571
572
573
574
575
576
        if (fi.exists()) {
            if (fi.isDir()) {
                // keep directories
                ++it;
            } else {
                // move files directly to m_recursiveEnumerateFiles
577
                m_recursiveEnumerateFiles << Utils::FileName::fromString(*it);
dt's avatar
dt committed
578
579
580
581
                it = folders.erase(it);
            }
        } else {
            // do remove non exsting stuff
dt's avatar
dt committed
582
            it = folders.erase(it);
dt's avatar
dt committed
583
        }
dt's avatar
dt committed
584
585
    }

586
587
588
589
590
591
    folders.removeDuplicates();
    watchFolders(folders.toSet());

    foreach (const QString &folder, folders) {
        m_recursiveEnumerateFiles += recursiveEnumerate(folder);
    }
592
    QMap<FileType, QSet<Utils::FileName> > foundFiles;
dt's avatar
dt committed
593

594
595
596
597
598
599
600
601
602
    QStringList baseVPathsExact;
    if (includeFileExact)
        baseVPathsExact = baseVPaths(readerExact, projectDir);
    QStringList baseVPathsCumulative;
    if (includeFileCumlative)
        baseVPathsCumulative = baseVPaths(readerCumulative, projectDir);

    const QVector<Qt4NodeStaticData::FileTypeData> &fileTypes = qt4NodeStaticData()->fileTypeData;

con's avatar
con committed
603
    // update files
604
    for (int i = 0; i < fileTypes.size(); ++i) {
605
        FileType type = fileTypes.at(i).type;
606
        QStringList qmakeVariables = varNames(type);
con's avatar
con committed
607

608
        QSet<Utils::FileName> newFilePaths;
609
        foreach (const QString &qmakeVariable, qmakeVariables) {
610
611
            if (includeFileExact) {
                QStringList vPathsExact = fullVPaths(baseVPathsExact, readerExact, type, qmakeVariable, projectDir);
612
613
614
                QStringList tmp = readerExact->absoluteFileValues(qmakeVariable, projectDir, vPathsExact, includeFileExact);
                foreach (const QString &t, tmp)
                    newFilePaths += Utils::FileName::fromString(t);
615
616
617
            }
            if (includeFileCumlative) {
                QStringList vPathsCumulative = fullVPaths(baseVPathsCumulative, readerCumulative, type, qmakeVariable, projectDir);
618
619
620
                QStringList tmp = readerCumulative->absoluteFileValues(qmakeVariable, projectDir, vPathsCumulative, includeFileCumlative);
                foreach (const QString &t, tmp)
                    newFilePaths += Utils::FileName::fromString(t);
621
            }
622
        }
con's avatar
con committed
623

dt's avatar
dt committed
624
625
626
627
628
629
        foundFiles[type] = newFilePaths;
        m_recursiveEnumerateFiles.subtract(newFilePaths);
    }

    for (int i = 0; i < fileTypes.size(); ++i) {
        FileType type = fileTypes.at(i).type;
630
        QSet<Utils::FileName> newFilePaths = filterFilesProVariables(type, foundFiles[type]);
631
        newFilePaths += filterFilesRecursiveEnumerata(type, m_recursiveEnumerateFiles);
632
633
634
635
636
637
638
639

        // We only need to save this information if
        // we are watching folders
        if (!folders.isEmpty())
            m_files[type] = newFilePaths;
        else
            m_files[type].clear();

640
641
642
        if (!newFilePaths.isEmpty()) {
            InternalNode *subfolder = new InternalNode;
            subfolder->type = type;
643
            subfolder->icon = fileTypes.at(i).icon;
644
645
            subfolder->fullPath = m_projectDir + QLatin1String("/#")
                                  + QString::number(i) + fileTypes.at(i).typeName;
646
647
            subfolder->displayName = fileTypes.at(i).typeName;
            contents.subnodes.insert(subfolder->fullPath, subfolder);
648
            // create the hierarchy with subdirectories
649
            subfolder->create(m_projectDir, newFilePaths, type);
con's avatar
con committed
650
651
        }
    }
652
653
654
655
656
657
658
659
660
661
662
663
664

    contents.updateSubFolders(this, this);
}

void Qt4PriFileNode::watchFolders(const QSet<QString> &folders)
{
    QSet<QString> toUnwatch = m_watchedFolders;
    toUnwatch.subtract(folders);

    QSet<QString> toWatch = folders;
    toWatch.subtract(m_watchedFolders);

    if (!toUnwatch.isEmpty())
665
        m_project->unwatchFolders(toUnwatch.toList(), this);
666
    if (!toWatch.isEmpty())
667
        m_project->watchFolders(toWatch.toList(), this);
668
669
670
671

    m_watchedFolders = folders;
}

672
void Qt4PriFileNode::folderChanged(const QString &folder)
673
674
675
676
{
    //qDebug()<<"########## Qt4PriFileNode::folderChanged";
    // So, we need to figure out which files changed.

677
678
679
680
    QString changedFolder = folder;
    if (!changedFolder.endsWith(QLatin1Char('/')))
        changedFolder.append(QLatin1Char('/'));

681
    // Collect all the files
682
    QSet<Utils::FileName> newFiles;
683
684
    newFiles += recursiveEnumerate(changedFolder);

685
    foreach (const Utils::FileName &file, m_recursiveEnumerateFiles) {
686
        if (!file.isChildOf(Utils::FileName::fromString(changedFolder)))
687
            newFiles.insert(file);
688
689
    }

690
    QSet<Utils::FileName> addedFiles = newFiles;
691
692
    addedFiles.subtract(m_recursiveEnumerateFiles);

693
    QSet<Utils::FileName> removedFiles = m_recursiveEnumerateFiles;
694
695
696
697
698
699
700
701
702
703
704
705
    removedFiles.subtract(newFiles);

    if (addedFiles.isEmpty() && removedFiles.isEmpty())
        return;

    m_recursiveEnumerateFiles = newFiles;

    // Apply the differences
    // per file type
    const QVector<Qt4NodeStaticData::FileTypeData> &fileTypes = qt4NodeStaticData()->fileTypeData;
    for (int i = 0; i < fileTypes.size(); ++i) {
        FileType type = fileTypes.at(i).type;
706
707
        QSet<Utils::FileName> add = filterFilesRecursiveEnumerata(type, addedFiles);
        QSet<Utils::FileName> remove = filterFilesRecursiveEnumerata(type, removedFiles);
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727

        if (!add.isEmpty() || !remove.isEmpty()) {
            // Scream :)
//            qDebug()<<"For type"<<fileTypes.at(i).typeName<<"\n"
//                    <<"added files"<<add<<"\n"
//                    <<"removed files"<<remove;

            m_files[type].unite(add);
            m_files[type].subtract(remove);
        }
    }

    // Now apply stuff
    InternalNode contents;
    for (int i = 0; i < fileTypes.size(); ++i) {
        FileType type = fileTypes.at(i).type;
        if (!m_files[type].isEmpty()) {
            InternalNode *subfolder = new InternalNode;
            subfolder->type = type;
            subfolder->icon = fileTypes.at(i).icon;
728
729
            subfolder->fullPath = m_projectDir + QLatin1String("/#")
                                  + QString::number(i) + fileTypes.at(i).typeName;
730
731
732
            subfolder->displayName = fileTypes.at(i).typeName;
            contents.subnodes.insert(subfolder->fullPath, subfolder);
            // create the hierarchy with subdirectories
733
            subfolder->create(m_projectDir, m_files[type], type);
734
735
736
        }
    }

737
    contents.updateSubFolders(this, this);
738
    m_project->updateFileList();
739
    m_project->updateCodeModels();
740
741
742
743
744
745

    // The files to be packaged are listed inside the symbian build system.
    // We need to regenerate that list by running qmake
    // Other platforms do not have a explicit list of files to package, but package
    // directories
    foreach (ProjectExplorer::Target *target, m_project->targets()) {
746
        if (target->id() == QLatin1String(Constants::S60_DEVICE_TARGET_ID)) {
747
748
749
750
751
752
753
754
755
756
757
            foreach (ProjectExplorer::BuildConfiguration *bc, target->buildConfigurations()) {
                Qt4BuildConfiguration *qt4bc = qobject_cast<Qt4BuildConfiguration *>(bc);
                if (qt4bc) {
                    QMakeStep *qmakeStep = qt4bc->qmakeStep();
                    if (qmakeStep)
                        qmakeStep->setForced(true);
                }
            }
        }
    }

con's avatar
con committed
758
759
}

dt's avatar
dt committed
760
761
762
bool Qt4PriFileNode::deploysFolder(const QString &folder) const
{
    QString f = folder;
763
764
765
    const QChar slash = QLatin1Char('/');
    if (!f.endsWith(slash))
        f.append(slash);
dt's avatar
dt committed
766
767
    foreach (const QString &wf, m_watchedFolders) {
        if (f.startsWith(wf)
768
769
            && (wf.endsWith(slash)
                || (wf.length() < f.length() && f.at(wf.length()) == slash)))
dt's avatar
dt committed
770
771
772
773
774
            return true;
    }
    return false;
}

dt's avatar
dt committed
775
776
QList<ProjectExplorer::RunConfiguration *> Qt4PriFileNode::runConfigurationsFor(Node *node)
{
777
778
779
780
    Qt4BaseTarget *target = m_project->activeTarget();
    if (target)
        return target->runConfigurationsForNode(node);
    return QList<ProjectExplorer::RunConfiguration *>();
dt's avatar
dt committed
781
782
}

783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
QList<Qt4PriFileNode *> Qt4PriFileNode::subProjectNodesExact() const
{
    QList<Qt4PriFileNode *> nodes;
    foreach (ProjectNode *node, subProjectNodes()) {
        Qt4PriFileNode *n = qobject_cast<Qt4PriFileNode *>(node);
        if (n && n->includedInExactParse())
            nodes << n;
    }
    return nodes;
}

bool Qt4PriFileNode::includedInExactParse() const
{
    return m_includedInExactParse;
}

void Qt4PriFileNode::setIncludedInExactParse(bool b)
{
    m_includedInExactParse = b;
}

dt's avatar
dt committed
804
QList<ProjectNode::ProjectAction> Qt4PriFileNode::supportedActions(Node *node) const
con's avatar
con committed
805
806
{
    QList<ProjectAction> actions;
807
808
809
810
811

    const FolderNode *folderNode = this;
    const Qt4ProFileNode *proFileNode;
    while (!(proFileNode = qobject_cast<const Qt4ProFileNode*>(folderNode)))
        folderNode = folderNode->parentFolderNode();
dt's avatar
dt committed
812
    Q_ASSERT(proFileNode);
813
814
815

    switch (proFileNode->projectType()) {
    case ApplicationTemplate:
816
817
818
819
820
821
    case LibraryTemplate:
    case AuxTemplate: {
        // TODO: Some of the file types don't make much sense for aux
        // projects (e.g. cpp). It'd be nice if the "add" action could
        // work on a subset of the file types according to project type.

dt's avatar
dt committed
822
        actions << AddNewFile;
823
        if (m_recursiveEnumerateFiles.contains(Utils::FileName::fromString(node->path()))) {
dt's avatar
dt committed
824
            actions << EraseFile;
dt's avatar
dt committed
825
        } else {
826
            actions << RemoveFile;
dt's avatar
dt committed
827
828
        }

829
        bool addExistingFiles = true;
830
        if (node->path().contains(QLatin1Char('#'))) {
831
832
833
834
835
            // A virtual folder, we do what the projectexplorer does
            FolderNode *folder = qobject_cast<FolderNode *>(node);
            if (folder) {
                QStringList list;
                foreach (FolderNode *f, folder->subFolderNodes())
836
                    list << f->path() + QLatin1Char('/');
837
838
839
840
841
                if (deploysFolder(Utils::commonPath(list)))
                    addExistingFiles = false;
            }
        }

dt's avatar
dt committed
842
        addExistingFiles = addExistingFiles && !deploysFolder(node->path());
843
844

        if (addExistingFiles)
dt's avatar
dt committed
845
846
            actions << AddExistingFile;

847
        break;
848
    }
849
850
851
852
853
    case SubDirsTemplate:
        actions << AddSubProject << RemoveSubProject;
        break;
    default:
        break;
con's avatar
con committed
854
    }
dt's avatar
dt committed
855

856
    ProjectExplorer::FileNode *fileNode = qobject_cast<FileNode *>(node);
dt's avatar
dt committed
857
858
859
    if (fileNode && fileNode->fileType() != ProjectExplorer::ProjectFileType)
        actions << Rename;

860
861
862

    Qt4BaseTarget *target = m_project->activeTarget();
    if (target && !target->runConfigurationsForNode(node).isEmpty())
dt's avatar
dt committed
863
864
        actions << HasSubProjectRunConfigurations;

con's avatar
con committed
865
866
867
    return actions;
}

868
869
870
871
872
873
874
875
876
bool Qt4PriFileNode::canAddSubProject(const QString &proFilePath) const
{
    QFileInfo fi(proFilePath);
    if (fi.suffix() == QLatin1String("pro")
        || fi.suffix() == QLatin1String("pri"))
        return true;
    return false;
}

877
878
879
880
881
882
883
884
885
886
887
888
static QString simplifyProFilePath(const QString &proFilePath)
{
    // if proFilePath is like: _path_/projectName/projectName.pro
    // we simplify it to: _path_/projectName
    QFileInfo fi(proFilePath);
    const QString parentPath = fi.absolutePath();
    QFileInfo parentFi(parentPath);
    if (parentFi.fileName() == fi.completeBaseName())
        return parentPath;
    return proFilePath;
}

con's avatar
con committed
889
890
bool Qt4PriFileNode::addSubProjects(const QStringList &proFilePaths)
{
891
892
893
894
895
    ProjectExplorer::FindAllFilesVisitor visitor;
    accept(&visitor);
    const QStringList &allFiles = visitor.filePaths();

    QStringList uniqueProFilePaths;
896
897
898
    foreach (const QString &proFile, proFilePaths)
        if (!allFiles.contains(proFile))
            uniqueProFilePaths.append(simplifyProFilePath(proFile));
899
900
901
902
903

    QStringList failedFiles;
    changeFiles(ProjectExplorer::ProjectFileType, uniqueProFilePaths, &failedFiles, AddToProFile);

    return failedFiles.isEmpty();
con's avatar
con committed
904
905
906
907
}

bool Qt4PriFileNode::removeSubProjects(const QStringList &proFilePaths)
{
908
909
910
911
912
913
914
915
916
917
918
    QStringList failedOriginalFiles;
    changeFiles(ProjectExplorer::ProjectFileType, proFilePaths, &failedOriginalFiles, RemoveFromProFile);

    QStringList simplifiedProFiles;
    foreach (const QString &proFile, failedOriginalFiles)
        simplifiedProFiles.append(simplifyProFilePath(proFile));

    QStringList failedSimplifiedFiles;
    changeFiles(ProjectExplorer::ProjectFileType, simplifiedProFiles, &failedSimplifiedFiles, RemoveFromProFile);

    return failedSimplifiedFiles.isEmpty();
con's avatar
con committed
919
920
921
922
923
}

bool Qt4PriFileNode::addFiles(const FileType fileType, const QStringList &filePaths,
                           QStringList *notAdded)
{
924
925
926
927
928
929
930
931
932
    // 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();

933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
    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);
    }

949
950
    QStringList uniqueFilePaths;
    foreach (const QString &file, filePaths) {
951
        if (!allFiles.contains(file))
952
953
            uniqueFilePaths.append(file);
    }
con's avatar
con committed
954

955
956
    QStringList failedFiles;
    changeFiles(fileType, uniqueFilePaths, &failedFiles, AddToProFile);
con's avatar
con committed
957
958
    if (notAdded)
        *notAdded = failedFiles;
959
960
961
    changeFiles(ProjectExplorer::ResourceType, uniqueQrcFiles, &failedFiles, AddToProFile);
    if (notAdded)
        *notAdded += failedFiles;
con's avatar
con committed
962
963
964
965
966
967
968
969
970
971
972
973
974
    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();
}

975
976
977
978
979
980
981
bool Qt4PriFileNode::deleteFiles(const FileType fileType, const QStringList &filePaths)
{
    QStringList failedFiles;
    changeFiles(fileType, filePaths, &failedFiles, RemoveFromProFile);
    return true;
}

con's avatar
con committed
982
983
984
bool Qt4PriFileNode::renameFile(const FileType fileType, const QString &filePath,
                             const QString &newFilePath)
{
985
    if (newFilePath.isEmpty())
con's avatar
con committed
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
        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)
{
1001
1002
1003
    Q_UNUSED(includeFile)
    Q_UNUSED(proFilePaths)
    Q_UNUSED(change)
con's avatar
con committed
1004
1005
1006
1007
1008
1009
1010
    // TODO
    return false;
}

bool Qt4PriFileNode::priFileWritable(const QString &path)
{
    const QString dir = QFileInfo(path).dir().path();
hjk's avatar
hjk committed
1011
1012
    Core::IVersionControl *versionControl = Core::ICore::vcsManager()->findVersionControlForDirectory(dir);
    switch (Core::FileManager::promptReadOnlyFile(path, versionControl, Core::ICore::mainWindow(), false)) {
1013
    case Core::FileManager::RO_OpenVCS:
con's avatar
con committed
1014
        if (!versionControl->vcsOpen(path)) {
hjk's avatar
hjk committed
1015
            QMessageBox::warning(Core::ICore::mainWindow(), tr("Cannot Open File"), tr("Cannot open the file for editing with VCS."));
con's avatar
con committed
1016
1017
1018
            return false;
        }
        break;
1019
    case Core::FileManager::RO_MakeWriteable: {
con's avatar
con committed
1020
1021
        const bool permsOk = QFile::setPermissions(path, QFile::permissions(path) | QFile::WriteUser);
        if (!permsOk) {
hjk's avatar
hjk committed
1022
            QMessageBox::warning(Core::ICore::mainWindow(), tr("Cannot Set Permissions"),  tr("Cannot set permissions to writable."));
con's avatar
con committed
1023
1024
1025
1026
            return false;
        }
        break;
    }
1027
1028
    case Core::FileManager::RO_SaveAs:
    case Core::FileManager::RO_Cancel:
con's avatar
con committed
1029
1030
1031
1032
1033
        return false;
    }
    return true;
}

1034
bool Qt4PriFileNode::saveModifiedEditors()
con's avatar
con committed
1035
1036
1037
{
    QList<Core::IFile*> modifiedFileHandles;

hjk's avatar
hjk committed
1038
    foreach (Core::IEditor *editor, Core::ICore::editorManager()->editorsForFileName(m_projectFilePath)) {
con's avatar
con committed
1039
1040
1041
1042
1043
1044
1045
1046
        if (Core::IFile *editorFile = editor->file()) {
            if (editorFile->isModified())
                modifiedFileHandles << editorFile;
        }
    }

    if (!modifiedFileHandles.isEmpty()) {
        bool cancelled;
1047
        Core::FileManager::saveModifiedFiles(modifiedFileHandles, &cancelled,
1048
                                         tr("There are unsaved changes for project file %1.").arg(m_projectFilePath));
con's avatar
con committed
1049
1050
        if (cancelled)
            return false;
1051
        // force instant reload of ourselves
1052
        QtSupport::ProFileCacheManager::instance()->discardFile(m_projectFilePath);
1053
        m_project->qt4ProjectManager()->notifyChanged(m_projectFilePath);
con's avatar
con committed
1054
1055
1056
1057
    }
    return true;
}

1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
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")) {