qmakenodes.cpp 86.2 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
con's avatar
con committed
2
**
3 4
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
con's avatar
con committed
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
con's avatar
con committed
7
**
hjk's avatar
hjk committed
8 9 10 11
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
12 13 14
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
15
**
16 17 18 19 20 21 22
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
con's avatar
con committed
23
**
hjk's avatar
hjk committed
24
****************************************************************************/
hjk's avatar
hjk committed
25

26
#include "qmakenodes.h"
27
#include "qmakeparsernodes.h"
28 29 30 31
#include "qmakeproject.h"
#include "qmakeprojectmanager.h"
#include "qmakeprojectmanagerconstants.h"
#include "qmakebuildconfiguration.h"
Tobias Hunger's avatar
Tobias Hunger committed
32
#include "qmakerunconfigurationfactory.h"
con's avatar
con committed
33 34

#include <projectexplorer/nodesvisitor.h>
35
#include <projectexplorer/projectexplorer.h>
Alessandro Portale's avatar
Alessandro Portale committed
36
#include <projectexplorer/projectexplorerconstants.h>
con's avatar
con committed
37
#include <coreplugin/editormanager/editormanager.h>
38
#include <coreplugin/editormanager/ieditor.h>
Jens Bache-Wiig's avatar
Jens Bache-Wiig committed
39
#include <coreplugin/fileiconprovider.h>
40
#include <coreplugin/documentmanager.h>
con's avatar
con committed
41 42 43
#include <coreplugin/icore.h>
#include <coreplugin/iversioncontrol.h>
#include <coreplugin/vcsmanager.h>
44
#include <coreplugin/dialogs/readonlyfilesdialog.h>
con's avatar
con committed
45

46 47
#include <extensionsystem/pluginmanager.h>

48
#include <projectexplorer/buildmanager.h>
Tobias Hunger's avatar
Tobias Hunger committed
49 50
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/target.h>
51
#include <projectexplorer/projecttree.h>
52
#include <qtsupport/profilereader.h>
Tobias Hunger's avatar
Tobias Hunger committed
53
#include <qtsupport/qtkitinformation.h>
54
#include <cpptools/generatedcodemodelsupport.h>
con's avatar
con committed
55

Daniel Teske's avatar
Daniel Teske committed
56 57
#include <resourceeditor/resourcenode.h>

58
#include <cpptools/cppmodelmanager.h>
59
#include <cpptools/cpptoolsconstants.h>
Friedemann Kleint's avatar
Friedemann Kleint committed
60

61
#include <utils/algorithm.h>
62
#include <utils/fileutils.h>
63
#include <utils/hostosinfo.h>
64
#include <utils/qtcprocess.h>
Eike Ziller's avatar
Eike Ziller committed
65
#include <utils/mimetypes/mimedatabase.h>
66
#include <utils/stringutils.h>
67
#include <utils/theme/theme.h>
68
#include <proparser/prowriter.h>
69
#include <proparser/qmakevfs.h>
70
#include <proparser/ioutils.h>
hjk's avatar
hjk committed
71

72
#include <QApplication>
73 74 75 76
#include <QDebug>
#include <QDir>
#include <QFile>
#include <QFileInfo>
77
#include <QTextCodec>
78
#include <QXmlStreamReader>
hjk's avatar
hjk committed
79

80
#include <QMessageBox>
81
#include <utils/QtConcurrentTools>
con's avatar
con committed
82

hjk's avatar
hjk committed
83
using namespace Core;
84
using namespace ProjectExplorer;
85
using namespace Utils;
86
using namespace QMakeInternal;
hjk's avatar
hjk committed
87

88
// Static cached data in struct QmakeNodeStaticData providing information and icons
89 90 91 92
// 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 {
93
    FileType type;
94 95
    const char *typeName;
    const char *icon;
96
    const char *addFileFilter;
97 98 99
};

static const FileTypeDataStorage fileTypeDataStorage[] = {
100
    { FileType::Header, QT_TRANSLATE_NOOP("QmakeProjectManager::QmakePriFileNode", "Headers"),
Alessandro Portale's avatar
Alessandro Portale committed
101
      ProjectExplorer::Constants::FILEOVERLAY_H, "*.h; *.hh; *.hpp; *.hxx;"},
102
    { FileType::Source, QT_TRANSLATE_NOOP("QmakeProjectManager::QmakePriFileNode", "Sources"),
Alessandro Portale's avatar
Alessandro Portale committed
103
      ProjectExplorer::Constants::FILEOVERLAY_CPP, "*.c; *.cc; *.cpp; *.cp; *.cxx; *.c++;" },
104
    { FileType::Form, QT_TRANSLATE_NOOP("QmakeProjectManager::QmakePriFileNode", "Forms"),
Alessandro Portale's avatar
Alessandro Portale committed
105
      Constants::FILEOVERLAY_UI, "*.ui;" },
106
    { FileType::StateChart, QT_TRANSLATE_NOOP("QmakeProjectManager::QmakePriFileNode", "State charts"),
Alessandro Portale's avatar
Alessandro Portale committed
107
      ProjectExplorer::Constants::FILEOVERLAY_SCXML, "*.scxml;" },
108
    { FileType::Resource, QT_TRANSLATE_NOOP("QmakeProjectManager::QmakePriFileNode", "Resources"),
Alessandro Portale's avatar
Alessandro Portale committed
109
      ProjectExplorer::Constants::FILEOVERLAY_QRC, "*.qrc;" },
110
    { FileType::QML, QT_TRANSLATE_NOOP("QmakeProjectManager::QmakePriFileNode", "QML"),
Alessandro Portale's avatar
Alessandro Portale committed
111
      ProjectExplorer::Constants::FILEOVERLAY_QML, "*.qml;" },
112
    { FileType::Unknown, QT_TRANSLATE_NOOP("QmakeProjectManager::QmakePriFileNode", "Other files"),
Alessandro Portale's avatar
Alessandro Portale committed
113
      ProjectExplorer::Constants::FILEOVERLAY_UNKNOWN, "*;" }
114 115
};

116
class QmakeNodeStaticData {
117 118 119
public:
    class FileTypeData {
    public:
120
        FileTypeData(FileType t = FileType::Unknown,
121
                     const QString &tN = QString(),
122
                     const QString &aff = QString(),
123
                     const QIcon &i = QIcon()) :
124
        type(t), typeName(tN), addFileFilter(aff), icon(i) { }
con's avatar
con committed
125

126
        FileType type;
127
        QString typeName;
128
        QString addFileFilter;
129 130 131
        QIcon icon;
    };

132
    QmakeNodeStaticData();
133

134 135 136 137
    QVector<FileTypeData> fileTypeData;
    QIcon projectIcon;
};

138
static void clearQmakeNodeStaticData();
139

140
QmakeNodeStaticData::QmakeNodeStaticData()
141
{
142 143
    // File type data
    const unsigned count = sizeof(fileTypeDataStorage)/sizeof(FileTypeDataStorage);
144
    fileTypeData.reserve(count);
145 146 147 148

    // Overlay the SP_DirIcon with the custom icons
    const QSize desiredSize = QSize(16, 16);

149
    const QPixmap dirPixmap = qApp->style()->standardIcon(QStyle::SP_DirIcon).pixmap(desiredSize);
Friedemann Kleint's avatar
Friedemann Kleint committed
150
    for (unsigned i = 0 ; i < count; ++i) {
151
        const QIcon overlayIcon(QLatin1String(fileTypeDataStorage[i].icon));
152
        QIcon folderIcon;
153
        folderIcon.addPixmap(FileIconProvider::overlayIcon(dirPixmap, overlayIcon));
154
        const QString desc = QCoreApplication::translate("QmakeProjectManager::QmakePriFileNode", fileTypeDataStorage[i].typeName);
155
        const QString filter = QString::fromUtf8(fileTypeDataStorage[i].addFileFilter);
156
        fileTypeData.push_back(QmakeNodeStaticData::FileTypeData(fileTypeDataStorage[i].type,
157
                                                                 desc, filter, folderIcon));
158 159
    }
    // Project icon
160
    const QIcon projectBaseIcon(ProjectExplorer::Constants::FILEOVERLAY_QT);
161
    const QPixmap projectPixmap = FileIconProvider::overlayIcon(dirPixmap, projectBaseIcon);
162
    projectIcon.addPixmap(projectPixmap);
163

164
    qAddPostRoutine(clearQmakeNodeStaticData);
165 166
}

167
Q_GLOBAL_STATIC(QmakeNodeStaticData, qmakeNodeStaticData)
168

169
static void clearQmakeNodeStaticData()
170
{
171 172
    qmakeNodeStaticData()->fileTypeData.clear();
    qmakeNodeStaticData()->projectIcon = QIcon();
con's avatar
con committed
173 174
}

175 176
enum { debug = 0 };

177 178
using namespace QmakeProjectManager;
using namespace QmakeProjectManager::Internal;
179

180 181 182 183 184 185
namespace QmakeProjectManager {
namespace Internal {
class EvalInput
{
public:
    QString projectDir;
186
    FileName projectFilePath;
187
    QString buildDirectory;
188
    QString sysroot;
189 190
    QtSupport::ProFileReader *readerExact;
    QtSupport::ProFileReader *readerCumulative;
191
    QMakeGlobals *qmakeGlobals;
192 193 194 195 196 197 198
    QMakeVfs *qmakeVfs;
};

class PriFileEvalResult
{
public:
    QStringList folders;
199
    QSet<FileName> recursiveEnumerateFiles;
Tobias Hunger's avatar
Tobias Hunger committed
200
    QMap<FileType, QSet<FileName>> foundFiles;
201 202
};

203 204 205
class IncludedPriFile
{
public:
206
    ProFile *proFile;
207 208 209 210 211 212 213 214 215 216
    Utils::FileName name;
    PriFileEvalResult result;
    QMap<Utils::FileName, IncludedPriFile *> children;

    ~IncludedPriFile()
    {
        qDeleteAll(children);
    }
};

217 218 219 220 221
class EvalResult
{
public:
    enum EvalResultState { EvalAbort, EvalFail, EvalPartial, EvalOk };
    EvalResultState state;
222
    ProjectType projectType;
223 224

    QStringList subProjectsNotToDeploy;
225
    QSet<FileName> exactSubdirs;
226
    IncludedPriFile includedFiles;
227 228
    TargetInformation targetInformation;
    InstallsList installsList;
229
    QHash<Variable, QStringList> newVarValues;
230 231
    QStringList errors;
};
232

233
class QmakePriFileNodeDocument : public Core::IDocument
234
{
hjk's avatar
hjk committed
235
public:
236
    QmakePriFileNodeDocument(QmakePriFileNode *qmakePriFile)
hjk's avatar
hjk committed
237 238 239 240 241 242
        : IDocument(nullptr), m_priFile(qmakePriFile)
    {
        setId("Qmake.PriFile");
        setMimeType(QLatin1String(QmakeProjectManager::Constants::PROFILE_MIMETYPE));
        setFilePath(m_priFile->filePath());
    }
243

hjk's avatar
hjk committed
244 245 246 247 248 249 250 251 252 253 254 255 256
    ReloadBehavior reloadBehavior(ChangeTrigger state, ChangeType type) const override
    {
        Q_UNUSED(state)
        Q_UNUSED(type)
        return BehaviorSilent;
    }
    bool reload(QString *errorString, ReloadFlag flag, ChangeType type) override
    {
        Q_UNUSED(errorString)
        Q_UNUSED(flag)
        if (type == TypePermissions)
            return true;
        m_priFile->scheduleUpdate();
257
        return true;
hjk's avatar
hjk committed
258
    }
con's avatar
con committed
259

hjk's avatar
hjk committed
260 261 262
private:
    QmakePriFileNode *m_priFile;
};
263

hjk's avatar
hjk committed
264
class ProVirtualFolderNode : public VirtualFolderNode
con's avatar
con committed
265
{
hjk's avatar
hjk committed
266
public:
267
    ProVirtualFolderNode(InternalNode *node);
268

hjk's avatar
hjk committed
269 270 271
    QString displayName() const final { return m_typeName; }
    QString addFileFilter() const final { return m_addFileFilter; }
    QString tooltip() const final { return QString(); }
272

hjk's avatar
hjk committed
273 274 275 276
private:
    QString m_typeName;
    QString m_addFileFilter;
};
con's avatar
con committed
277

278
struct InternalNode
279
{
280 281
    QList<InternalNode *> virtualfolders;
    QMap<QString, InternalNode *> subnodes;
282
    FileNameList files;
283
    FileType type = FileType::Unknown;
284
    int priority = Node::DefaultVirtualFolderPriority;
285
    QString displayName;
286
    QString typeName;
287
    QString addFileFilter;
288 289
    QString fullPath;
    QIcon icon;
290

291 292
    ~InternalNode()
    {
293
        qDeleteAll(virtualfolders);
294 295
        qDeleteAll(subnodes);
    }
296

297
    // Creates: a tree structure from a list of absolute file paths.
298 299 300 301 302 303 304 305 306 307
    // Empty directories are compressed into a single entry with a longer path.
    // * project
    //    * /absolute/path
    //       * file1
    //    * relative
    //       * path1
    //          * file1
    //          * file2
    //       * path2
    //          * file1
308
    // The function first creates a tree that looks like the directory structure, i.e.
309 310 311 312 313 314
    //    * /
    //       * absolute
    //          * path
    // ...
    // and afterwards calls compress() which merges directory nodes with single children, i.e. to
    //    * /absolute/path
315
    void create(const QString &projectDir, const QSet<FileName> &newFilePaths, FileType type)
316
    {
317
        static const QChar separator = QLatin1Char('/');
318 319 320
        const FileName projectDirFileName = FileName::fromString(projectDir);
        foreach (const FileName &file, newFilePaths) {
            FileName fileWithoutPrefix;
321
            bool isRelative;
322
            if (file.isChildOf(projectDirFileName)) {
323
                isRelative = true;
324
                fileWithoutPrefix = file.relativeChildPath(projectDirFileName);
325 326 327 328
            } else {
                isRelative = false;
                fileWithoutPrefix = file;
            }
329
            QStringList parts = fileWithoutPrefix.toString().split(separator, QString::SkipEmptyParts);
330
            if (!HostOsInfo::isWindowsHost() && !isRelative && parts.count() > 0)
331 332 333
                parts[0].prepend(separator);
            QStringListIterator it(parts);
            InternalNode *currentNode = this;
334
            QString path = (isRelative ? (projectDirFileName.toString() + QLatin1Char('/')) : QString());
335 336 337 338
            while (it.hasNext()) {
                const QString &key = it.next();
                if (it.hasNext()) { // key is directory
                    path += key;
339
                    if (!currentNode->subnodes.contains(path)) {
340 341 342
                        InternalNode *val = new InternalNode;
                        val->type = type;
                        val->fullPath = path;
343 344
                        val->displayName = key;
                        currentNode->subnodes.insert(path, val);
345 346
                        currentNode = val;
                    } else {
347
                        currentNode = currentNode->subnodes.value(path);
348
                    }
349 350
                    path += separator;
                } else { // key is filename
351
                    currentNode->files.append(file);
352 353 354
                }
            }
        }
355 356
        this->compress();
    }
357

358 359 360 361 362 363 364 365 366
    // 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) {
367
                // replace i.value() by i.value()->subnodes.begin()
368
                QString key = i.value()->subnodes.begin().key();
369
                InternalNode *keep = i.value()->subnodes.value(key);
370
                keep->displayName = i.value()->displayName + QDir::separator() + keep->displayName;
371
                newSubnodes.insert(key, keep);
372 373 374 375
                i.value()->subnodes.clear();
                delete i.value();
            } else {
                newSubnodes.insert(i.key(), i.value());
376 377
            }
        }
378 379
        subnodes = newSubnodes;
    }
380

381 382
    FolderNode *createFolderNode(InternalNode *node)
    {
383
        FolderNode *newNode = nullptr;
384
        if (node->typeName.isEmpty())
385
            newNode = new FolderNode(FileName::fromString(node->fullPath));
386 387
        else
            newNode = new ProVirtualFolderNode(node);
388 389 390 391 392 393 394

        newNode->setDisplayName(node->displayName);
        if (!node->icon.isNull())
            newNode->setIcon(node->icon);
        return newNode;
    }

395
    // Makes the projectNode's subtree below the given folder match this internal node's subtree
396
    void addSubFolderContents(FolderNode *folder)
397
    {
398 399 400 401 402 403 404 405 406 407
        if (type == FileType::Resource) {
            for (const FileName &file : files) {
                auto vfs = static_cast<QmakePriFileNode *>(folder->parentProjectNode())->m_project->qmakeVfs();
                QString contents;
                // Prefer the cumulative file if it's non-empty, based on the assumption
                // that it contains more "stuff".
                vfs->readVirtualFile(file.toString(), QMakeVfs::VfsCumulative, &contents);
                // If the cumulative evaluation botched the file too much, try the exact one.
                if (contents.isEmpty())
                    vfs->readVirtualFile(file.toString(), QMakeVfs::VfsExact, &contents);
408
                auto resourceNode = new ResourceEditor::ResourceTopLevelNode(file, contents, folder);
409
                folder->addNode(resourceNode);
410
                resourceNode->addInternalNodes();
411 412 413
            }
        } else {
            for (const FileName &file : files)
414
                folder->addNode(new FileNode(file, type, false));
415
        }
416

417
        // Virtual
418
        {
419 420
            for (InternalNode *node : virtualfolders) {
                FolderNode *newNode = createFolderNode(node);
421
                folder->addNode(newNode);
422
                node->addSubFolderContents(newNode);
423
            }
424
        }
425
        // Subnodes
426 427 428 429
        {
            QMap<QString, InternalNode *>::const_iterator it = subnodes.constBegin();
            QMap<QString, InternalNode *>::const_iterator end = subnodes.constEnd();
            for ( ; it != end; ++it) {
430
                FolderNode *newNode = createFolderNode(it.value());
431
                folder->addNode(newNode);
432
                it.value()->addSubFolderContents(newNode);
433
            }
434
        }
Daniel Teske's avatar
Daniel Teske committed
435
    }
436
};
hjk's avatar
hjk committed
437

438 439 440 441 442
ProVirtualFolderNode::ProVirtualFolderNode(InternalNode *node)
    : VirtualFolderNode(FileName::fromString(node->fullPath), node->priority),
      m_typeName(node->typeName),
      m_addFileFilter(node->addFileFilter)
{}
hjk's avatar
hjk committed
443

444
} // Internal
dt's avatar
dt committed
445

hjk's avatar
hjk committed
446 447 448 449 450 451 452 453 454 455 456 457 458 459
/*!
  \class QmakePriFileNode
  Implements abstract ProjectNode class
  */

QmakePriFileNode::QmakePriFileNode(QmakeProject *project, QmakeProFileNode *qmakeProFileNode,
                                   const FileName &filePath)
    : ProjectNode(filePath),
      m_project(project),
      m_qmakeProFileNode(qmakeProFileNode),
      m_projectFilePath(filePath),
      m_projectDir(filePath.toFileInfo().absolutePath())
{
    Q_ASSERT(project);
460
    m_qmakePriFile = new QmakePriFileNodeDocument(this);
hjk's avatar
hjk committed
461 462 463 464 465 466 467 468 469 470 471 472 473 474 475
    Core::DocumentManager::addDocument(m_qmakePriFile);

    setDisplayName(filePath.toFileInfo().completeBaseName());
    setIcon(qmakeNodeStaticData()->projectIcon);
}

QmakePriFileNode::~QmakePriFileNode()
{
    watchFolders(QSet<QString>());
    delete m_qmakePriFile;
}

void QmakePriFileNode::scheduleUpdate()
{
    QtSupport::ProFileCacheManager::instance()->discardFile(m_projectFilePath.toString());
476
    m_qmakeProFileNode->scheduleUpdate(QmakeProFile::ParseLater);
hjk's avatar
hjk committed
477 478
}

479
QStringList QmakePriFileNode::baseVPaths(QtSupport::ProFileReader *reader, const QString &projectDir, const QString &buildDir)
con's avatar
con committed
480
{
dt's avatar
dt committed
481 482 483
    QStringList result;
    if (!reader)
        return result;
484
    result += reader->absolutePathValues(QLatin1String("VPATH"), projectDir);
dt's avatar
dt committed
485
    result << projectDir; // QMAKE_ABSOLUTE_SOURCE_PATH
486
    result << buildDir;
dt's avatar
dt committed
487 488 489
    result.removeDuplicates();
    return result;
}
con's avatar
con committed
490

491
QStringList QmakePriFileNode::fullVPaths(const QStringList &baseVPaths, QtSupport::ProFileReader *reader,
492
                                       const QString &qmakeVariable, const QString &projectDir)
dt's avatar
dt committed
493 494 495 496
{
    QStringList vPaths;
    if (!reader)
        return vPaths;
497
    vPaths = reader->absolutePathValues(QLatin1String("VPATH_") + qmakeVariable, projectDir);
dt's avatar
dt committed
498 499 500 501 502
    vPaths += baseVPaths;
    vPaths.removeDuplicates();
    return vPaths;
}

503
QSet<FileName> QmakePriFileNode::recursiveEnumerate(const QString &folder)
504
{
505
    QSet<FileName> result;
506 507 508 509 510 511 512
    QDir dir(folder);
    dir.setFilter(dir.filter() | QDir::NoDotAndDotDot);
    foreach (const QFileInfo &file, dir.entryInfoList()) {
        if (file.isDir() && !file.isSymLink())
            result += recursiveEnumerate(file.absoluteFilePath());
        else if (!Core::EditorManager::isAutoSaveFile(file.fileName()))
            result += FileName(file);
513 514 515
    }
    return result;
}
dt's avatar
dt committed
516

hjk's avatar
hjk committed
517
static QStringList fileListForVar(
518 519
        const QHash<QString, QVector<ProFileEvaluator::SourceFile> > &sourceFiles,
        const QString &varName)
dt's avatar
dt committed
520
{
521 522 523 524 525 526 527
    const QVector<ProFileEvaluator::SourceFile> &sources = sourceFiles[varName];
    QStringList result;
    result.reserve(sources.size());
    foreach (const ProFileEvaluator::SourceFile &sf, sources)
        result << sf.fileName;
    return result;
}
528

529 530 531 532 533 534 535 536 537 538 539 540
void QmakePriFileNode::extractSources(
        QHash<const ProFile *, PriFileEvalResult *> proToResult, PriFileEvalResult *fallback,
        QVector<ProFileEvaluator::SourceFile> sourceFiles, FileType type)
{
    foreach (const ProFileEvaluator::SourceFile &source, sourceFiles) {
        PriFileEvalResult *result = proToResult.value(source.proFile);
        if (!result)
            result = fallback;
        result->foundFiles[type].insert(FileName::fromString(source.fileName));
    }
}

541 542 543 544 545 546 547 548 549 550 551
void QmakePriFileNode::extractInstalls(
        QHash<const ProFile *, PriFileEvalResult *> proToResult, PriFileEvalResult *fallback,
        const InstallsList &installList)
{
    for (const InstallsItem &item : installList.items) {
        for (const ProFileEvaluator::SourceFile &source : item.files) {
            auto *result = proToResult.value(source.proFile);
            if (!result)
                result = fallback;
            result->folders << source.fileName;
        }
552
    }
553
}
554

555 556
void QmakePriFileNode::processValues(PriFileEvalResult &result)
{
557
    result.folders.removeDuplicates();
dt's avatar
dt committed
558

559
    // Remove non existing items and non folders
560 561
    QStringList::iterator it = result.folders.begin();
    while (it != result.folders.end()) {
dt's avatar
dt committed
562
        QFileInfo fi(*it);
dt's avatar
dt committed
563 564 565 566 567
        if (fi.exists()) {
            if (fi.isDir()) {
                // keep directories
                ++it;
            } else {
568
                // move files directly to recursiveEnumerateFiles
569
                result.recursiveEnumerateFiles << FileName::fromString(*it);
570
                it = result.folders.erase(it);
dt's avatar
dt committed
571 572 573
            }
        } else {
            // do remove non exsting stuff
574
            it = result.folders.erase(it);
dt's avatar
dt committed
575
        }
dt's avatar
dt committed
576 577
    }

578 579
    foreach (const QString &folder, result.folders)
        result.recursiveEnumerateFiles += recursiveEnumerate(folder);
580

581
    const QVector<QmakeNodeStaticData::FileTypeData> &fileTypes = qmakeNodeStaticData()->fileTypeData;
dt's avatar
dt committed
582 583
    for (int i = 0; i < fileTypes.size(); ++i) {
        FileType type = fileTypes.at(i).type;
584 585 586
        QSet<FileName> &foundFiles = result.foundFiles[type];
        result.recursiveEnumerateFiles.subtract(foundFiles);
        QSet<FileName> newFilePaths = filterFilesProVariables(type, foundFiles);
587
        newFilePaths += filterFilesRecursiveEnumerata(type, result.recursiveEnumerateFiles);
588
        foundFiles = newFilePaths;
589 590 591 592 593 594
    }
}

void QmakePriFileNode::update(const Internal::PriFileEvalResult &result)
{
    // add project file node
595
    addNode(new FileNode(m_projectFilePath, FileType::Project, false));
596

597 598 599 600 601 602 603
    m_recursiveEnumerateFiles = result.recursiveEnumerateFiles;
    watchFolders(result.folders.toSet());

    InternalNode contents;
    const QVector<QmakeNodeStaticData::FileTypeData> &fileTypes = qmakeNodeStaticData()->fileTypeData;
    for (int i = 0; i < fileTypes.size(); ++i) {
        FileType type = fileTypes.at(i).type;
604
        const QSet<FileName> &newFilePaths = result.foundFiles.value(type);
605 606
        // We only need to save this information if
        // we are watching folders
607
        if (!result.folders.isEmpty())
608 609 610 611
            m_files[type] = newFilePaths;
        else
            m_files[type].clear();

612 613 614
        if (!newFilePaths.isEmpty()) {
            InternalNode *subfolder = new InternalNode;
            subfolder->type = type;
615
            subfolder->icon = fileTypes.at(i).icon;
616 617
            subfolder->fullPath = m_projectDir;
            subfolder->typeName = fileTypes.at(i).typeName;
618
            subfolder->addFileFilter = fileTypes.at(i).addFileFilter;
619
            subfolder->priority = Node::DefaultVirtualFolderPriority - i;
620
            subfolder->displayName = fileTypes.at(i).typeName;
621
            contents.virtualfolders.append(subfolder);
622
            // create the hierarchy with subdirectories
623
            subfolder->create(m_projectDir, newFilePaths, type);
con's avatar
con committed
624 625
        }
    }
626

627
    contents.addSubFolderContents(this);
628 629
}

630
void QmakePriFileNode::watchFolders(const QSet<QString> &folders)
631 632 633 634 635 636 637 638
{
    QSet<QString> toUnwatch = m_watchedFolders;
    toUnwatch.subtract(folders);

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

    if (!toUnwatch.isEmpty())
639
        m_project->unwatchFolders(toUnwatch.toList(), this);
640
    if (!toWatch.isEmpty())
641
        m_project->watchFolders(toWatch.toList(), this);
642 643 644 645

    m_watchedFolders = folders;
}

646
bool QmakePriFileNode::folderChanged(const QString &changedFolder, const QSet<FileName> &newFiles)
647
{
648
    //qDebug()<<"########## QmakePriFileNode::folderChanged";
649 650
    // So, we need to figure out which files changed.

651
    QSet<FileName> addedFiles = newFiles;
652 653
    addedFiles.subtract(m_recursiveEnumerateFiles);

654
    QSet<FileName> removedFiles = m_recursiveEnumerateFiles;
655 656
    removedFiles.subtract(newFiles);

657 658
    foreach (const FileName &file, removedFiles) {
        if (!file.isChildOf(FileName::fromString(changedFolder)))
659 660 661 662 663 664 665 666 667 668
            removedFiles.remove(file);
    }

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

    m_recursiveEnumerateFiles = newFiles;

    // Apply the differences
    // per file type
669
    const QVector<QmakeNodeStaticData::FileTypeData> &fileTypes = qmakeNodeStaticData()->fileTypeData;
670 671
    for (int i = 0; i < fileTypes.size(); ++i) {
        FileType type = fileTypes.at(i).type;
672 673
        QSet<FileName> add = filterFilesRecursiveEnumerata(type, addedFiles);
        QSet<FileName> remove = filterFilesRecursiveEnumerata(type, removedFiles);
674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695

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

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

    // Now apply stuff
    InternalNode contents;
    for (int i = 0; i < fileTypes.size(); ++i) {
        FileType type = fileTypes.at(i).type;
        if (!m_files[type].isEmpty()) {
            InternalNode *subfolder = new InternalNode;
            subfolder->type = type;
            subfolder->icon = fileTypes.at(i).icon;
            subfolder->fullPath = m_projectDir;
            subfolder->typeName = fileTypes.at(i).typeName;
696
            subfolder->priority = Node::DefaultVirtualFolderPriority - i;
697 698 699 700 701 702 703
            subfolder->displayName = fileTypes.at(i).typeName;
            contents.virtualfolders.append(subfolder);
            // create the hierarchy with subdirectories
            subfolder->create(m_projectDir, m_files[type], type);
        }
    }

704
    contents.addSubFolderContents(this);
705
    return true;
con's avatar
con committed
706 707
}

708
bool QmakePriFileNode::deploysFolder(const QString &folder) const
dt's avatar
dt committed
709 710
{
    QString f = folder;
711 712 713
    const QChar slash = QLatin1Char('/');
    if (!f.endsWith(slash))
        f.append(slash);
714

dt's avatar
dt committed
715 716
    foreach (const QString &wf, m_watchedFolders) {
        if (f.startsWith(wf)
717 718
            && (wf.endsWith(slash)
                || (wf.length() < f.length() && f.at(wf.length()) == slash)))
dt's avatar
dt committed
719 720 721 722 723
            return true;
    }
    return false;
}

724
QList<RunConfiguration *> QmakePriFileNode::runConfigurations() const
dt's avatar
dt committed
725
{
Tobias Hunger's avatar
Tobias Hunger committed
726 727
    QmakeRunConfigurationFactory *factory = QmakeRunConfigurationFactory::find(m_project->activeTarget());
    if (factory)
728
        return factory->runConfigurationsForNode(m_project->activeTarget(), this);
729
    return QList<RunConfiguration *>();
dt's avatar
dt committed
730 731
}

732
QList<QmakePriFileNode *> QmakePriFileNode::subProjectNodesExact() const
733
{
734
    QList<QmakePriFileNode *> nodes;
735
    foreach (ProjectNode *node, projectNodes()) {
736
        QmakePriFileNode *n = dynamic_cast<QmakePriFileNode *>(node);
737 738 739 740 741 742
        if (n && n->includedInExactParse())
            nodes << n;
    }
    return nodes;
}

743
QmakeProFileNode *QmakePriFileNode::proFileNode() const
744
{
745
    return m_qmakeProFileNode;
746 747
}

748
bool QmakePriFileNode::includedInExactParse() const
749 750 751 752
{
    return m_includedInExactParse;
}

753
void QmakePriFileNode::setIncludedInExactParse(bool b)
754 755 756 757
{
    m_includedInExactParse = b;
}

758
QList<ProjectAction> QmakePriFileNode::supportedActions(Node *node) const
con's avatar
con committed
759
{
760
    QList<ProjectAction> actions;
761 762

    const FolderNode *folderNode = this;
763
    const QmakeProFileNode *proFileNode;
764
    while (!(proFileNode = dynamic_cast<const QmakeProFileNode*>(folderNode)))
765
        folderNode = folderNode->parentFolderNode();
dt's avatar
dt committed
766
    Q_ASSERT(proFileNode);
767 768

    switch (proFileNode->projectType()) {
769 770 771 772
    case ProjectType::ApplicationTemplate:
    case ProjectType::StaticLibraryTemplate:
    case ProjectType::SharedLibraryTemplate:
    case ProjectType::AuxTemplate: {
773 774 775 776
        // 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.

777
        actions << AddNewFile;
778
        if (m_recursiveEnumerateFiles.contains(node->filePath()))
779
            actions << EraseFile;
780
        else
781
            actions << RemoveFile;
dt's avatar
dt committed
782

783
        bool addExistingFiles = true;
784
        if (node->nodeType() == NodeType::VirtualFolder) {
785
            // A virtual folder, we do what the projectexplorer does
786
            FolderNode *folder = node->asFolderNode();
787 788
            if (folder) {
                QStringList list;
789
                foreach (FolderNode *f, folder->folderNodes())
790
                    list << f->filePath().toString() + QLatin1Char('/');
791 792 793 794 795
                if (deploysFolder(Utils::commonPath(list)))
                    addExistingFiles = false;
            }
        }

796
        addExistingFiles = addExistingFiles && !deploysFolder(node->filePath().toString());
797 798

        if (addExistingFiles)
799
            actions << AddExistingFile << AddExistingDirectory;
dt's avatar
dt committed
800

801
        break;
802
    }
803
    case ProjectType::SubDirsTemplate:
804
        actions << AddSubProject << RemoveSubProject;
805 806 807
        break;
    default:
        break;
con's avatar
con committed
808
    }
dt's avatar
dt committed
809

810
    FileNode *fileNode = node->asFileNode();
811
    if ((fileNode && fileNode->fileType() != FileType::Project)
812
            || dynamic_cast<ResourceEditor::ResourceTopLevelNode *>(node)) {
813
        actions << Rename;
814 815
        actions << DuplicateFile;
    }
dt's avatar
dt committed
816

817

818
    Target *target = m_project->activeTarget();
Tobias Hunger's avatar
Tobias Hunger committed