qt4nodes.cpp 84.7 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
con's avatar
con committed
2
**
hjk's avatar
hjk committed
3 4
** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
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 12 13 14
** 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
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
15
**
hjk's avatar
hjk committed
16
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17 18 19 20 21 22 23 24 25
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
con's avatar
con committed
26 27
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
28
****************************************************************************/
hjk's avatar
hjk committed
29

con's avatar
con committed
30 31 32
#include "qt4nodes.h"
#include "qt4project.h"
#include "qt4projectmanager.h"
33
#include "qt4projectmanagerconstants.h"
dt's avatar
dt committed
34
#include "qtuicodemodelsupport.h"
35
#include "qmakestep.h"
36
#include "qt4buildconfiguration.h"
Tobias Hunger's avatar
Tobias Hunger committed
37
#include "qmakerunconfigurationfactory.h"
con's avatar
con committed
38 39

#include <projectexplorer/nodesvisitor.h>
40
#include <projectexplorer/runconfiguration.h>
con's avatar
con committed
41 42

#include <coreplugin/editormanager/editormanager.h>
43
#include <coreplugin/editormanager/ieditor.h>
Jens Bache-Wiig's avatar
Jens Bache-Wiig committed
44
#include <coreplugin/fileiconprovider.h>
45
#include <coreplugin/documentmanager.h>
con's avatar
con committed
46 47 48 49
#include <coreplugin/icore.h>
#include <coreplugin/iversioncontrol.h>
#include <coreplugin/vcsmanager.h>

50
#include <cpptools/ModelManagerInterface.h>
51
#include <cplusplus/CppDocument.h>
52
#include <extensionsystem/pluginmanager.h>
53
#include <projectexplorer/buildmanager.h>
Tobias Hunger's avatar
Tobias Hunger committed
54 55
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/target.h>
56
#include <qtsupport/profilereader.h>
Tobias Hunger's avatar
Tobias Hunger committed
57
#include <qtsupport/qtkitinformation.h>
Tobias Hunger's avatar
Tobias Hunger committed
58
#include <qtsupport/qtsupportconstants.h>
con's avatar
con committed
59

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

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

74 75 76
#include <QPainter>
#include <QMessageBox>
#include <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"),
104
      ":/qt4projectmanager/images/qml.ico" },
105
    { ProjectExplorer::UnknownFileType,
Daniel Teske's avatar
Daniel Teske committed
106
      QT_TRANSLATE_NOOP("Qt4ProjectManager::Qt4PriFileNode", "Other files"),
107 108 109
      ":/qt4projectmanager/images/unknown.png" }
};

110 111 112 113 114
bool sortNodesByPath(ProjectExplorer::Node *a, ProjectExplorer::Node *b)
{
    return a->path() < b->path();
}

115 116 117 118
class Qt4NodeStaticData {
public:
    class FileTypeData {
    public:
119 120 121 122
        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
123

124 125 126 127 128
        ProjectExplorer::FileType type;
        QString typeName;
        QIcon icon;
    };

129 130
    Qt4NodeStaticData();

131 132 133 134 135 136
    QVector<FileTypeData> fileTypeData;
    QIcon projectIcon;
};

static void clearQt4NodeStaticData();

137 138
Qt4NodeStaticData::Qt4NodeStaticData()
{
139 140
    // File type data
    const unsigned count = sizeof(fileTypeDataStorage)/sizeof(FileTypeDataStorage);
141
    fileTypeData.reserve(count);
142 143 144 145

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

Friedemann Kleint's avatar
Friedemann Kleint committed
146
    for (unsigned i = 0 ; i < count; ++i) {
147 148 149 150 151 152
        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
153
        const QString desc = Qt4ProjectManager::Qt4PriFileNode::tr(fileTypeDataStorage[i].typeName);
154 155
        fileTypeData.push_back(Qt4NodeStaticData::FileTypeData(fileTypeDataStorage[i].type,
                                                               desc, folderIcon));
156 157 158 159 160 161
    }
    // Project icon
    const QIcon projectBaseIcon(QLatin1String(":/qt4projectmanager/images/qt_project.png"));
    const QPixmap projectPixmap = Core::FileIconProvider::overlayIcon(QStyle::SP_DirIcon,
                                                                      projectBaseIcon,
                                                                      desiredSize);
162
    projectIcon.addPixmap(projectPixmap);
163 164

    qAddPostRoutine(clearQt4NodeStaticData);
165 166 167
}

Q_GLOBAL_STATIC(Qt4NodeStaticData, qt4NodeStaticData)
168 169 170 171 172

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

175 176
enum { debug = 0 };

Daniel Teske's avatar
Daniel Teske committed
177 178
using namespace Qt4ProjectManager;
using namespace Qt4ProjectManager::Internal;
179

180
Qt4PriFile::Qt4PriFile(Qt4ProjectManager::Qt4PriFileNode *qt4PriFile)
181
    : IDocument(qt4PriFile), m_priFile(qt4PriFile)
182 183 184 185
{

}

186
bool Qt4PriFile::save(QString *errorString, const QString &fileName, bool autoSave)
187
{
188
    Q_UNUSED(errorString);
189
    Q_UNUSED(fileName);
190
    Q_UNUSED(autoSave);
191 192 193
    return false;
}

dt's avatar
dt committed
194 195 196 197 198 199 200
void Qt4PriFile::rename(const QString &newName)
{
    // Can't happen
    Q_ASSERT(false);
    Q_UNUSED(newName);
}

201 202 203 204 205 206 207
QString Qt4PriFile::fileName() const
{
    return m_priFile->path();
}

QString Qt4PriFile::defaultPath() const
{
208
    return QString();
209 210 211 212
}

QString Qt4PriFile::suggestedFileName() const
{
213
    return QString();
214 215 216 217
}

QString Qt4PriFile::mimeType() const
{
218
    return QLatin1String(Qt4ProjectManager::Constants::PROFILE_MIMETYPE);
219 220 221 222 223 224 225 226 227 228 229 230
}

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

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

231
Core::IDocument::ReloadBehavior Qt4PriFile::reloadBehavior(ChangeTrigger state, ChangeType type) const
232
{
233 234 235
    Q_UNUSED(state)
    Q_UNUSED(type)
    return BehaviorSilent;
236 237
}

238
bool Qt4PriFile::reload(QString *errorString, ReloadFlag flag, ChangeType type)
239
{
240
    Q_UNUSED(errorString)
241 242
    Q_UNUSED(flag)
    if (type == TypePermissions)
243
        return true;
244
    m_priFile->scheduleUpdate();
245
    return true;
246
}
247

con's avatar
con committed
248 249 250 251 252
/*!
  \class Qt4PriFileNode
  Implements abstract ProjectNode class
  */

253 254
namespace Qt4ProjectManager {

255
Qt4PriFileNode::Qt4PriFileNode(Qt4Project *project, Qt4ProFileNode* qt4ProFileNode, const QString &filePath)
con's avatar
con committed
256 257
        : ProjectNode(filePath),
          m_project(project),
258
          m_qt4ProFileNode(qt4ProFileNode),
259
          m_projectFilePath(QDir::fromNativeSeparators(filePath)),
260 261
          m_projectDir(QFileInfo(filePath).absolutePath()),
          m_includedInExactParse(true)
con's avatar
con committed
262
{
dt's avatar
dt committed
263
    Q_ASSERT(project);
264
    m_qt4PriFile = new Qt4PriFile(this);
265
    Core::DocumentManager::addDocument(m_qt4PriFile);
266

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

269
    setIcon(qt4NodeStaticData()->projectIcon);
270 271
}

272 273 274 275 276
Qt4PriFileNode::~Qt4PriFileNode()
{
    watchFolders(QSet<QString>());
}

277 278
void Qt4PriFileNode::scheduleUpdate()
{
279
    QtSupport::ProFileCacheManager::instance()->discardFile(m_projectFilePath);
280
    m_qt4ProFileNode->scheduleUpdate();
con's avatar
con committed
281 282
}

Daniel Teske's avatar
Daniel Teske committed
283
namespace Internal {
284
struct InternalNode
285
{
286 287
    QList<InternalNode *> virtualfolders;
    QMap<QString, InternalNode *> subnodes;
288 289
    QStringList files;
    ProjectExplorer::FileType type;
290
    QString displayName;
291 292
    QString typeName;
    int priority;
293 294
    QString fullPath;
    QIcon icon;
295

296 297 298
    InternalNode()
    {
        type = ProjectExplorer::UnknownFileType;
299
        priority = 0;
300
    }
301

302 303
    ~InternalNode()
    {
304
        qDeleteAll(virtualfolders);
305 306
        qDeleteAll(subnodes);
    }
307

308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325
    // 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
326
    void create(const QString &projectDir, const QSet<Utils::FileName> &newFilePaths, ProjectExplorer::FileType type)
327
    {
328
        static const QChar separator = QLatin1Char('/');
329
        const Utils::FileName projectDirFileName = Utils::FileName::fromString(projectDir);
330 331
        foreach (const Utils::FileName &file, newFilePaths) {
            Utils::FileName fileWithoutPrefix;
332
            bool isRelative;
333
            if (file.isChildOf(projectDirFileName)) {
334
                isRelative = true;
335
                fileWithoutPrefix = file.relativeChildPath(projectDirFileName);
336 337 338 339
            } else {
                isRelative = false;
                fileWithoutPrefix = file;
            }
340
            QStringList parts = fileWithoutPrefix.toString().split(separator, QString::SkipEmptyParts);
341
            if (!Utils::HostOsInfo::isWindowsHost() && !isRelative && parts.count() > 0)
342 343 344
                parts[0].prepend(separator);
            QStringListIterator it(parts);
            InternalNode *currentNode = this;
345
            QString path = (isRelative ? (projectDirFileName.toString() + QLatin1Char('/')) : QString());
346 347 348 349
            while (it.hasNext()) {
                const QString &key = it.next();
                if (it.hasNext()) { // key is directory
                    path += key;
350
                    if (!currentNode->subnodes.contains(path)) {
351 352 353
                        InternalNode *val = new InternalNode;
                        val->type = type;
                        val->fullPath = path;
354 355
                        val->displayName = key;
                        currentNode->subnodes.insert(path, val);
356 357
                        currentNode = val;
                    } else {
358
                        currentNode = currentNode->subnodes.value(path);
359
                    }
360 361
                    path += separator;
                } else { // key is filename
362
                    currentNode->files.append(file.toString());
363 364 365
                }
            }
        }
366 367
        this->compress();
    }
368

369 370 371 372 373 374 375 376 377
    // 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) {
378
                // replace i.value() by i.value()->subnodes.begin()
379
                QString key = i.value()->subnodes.begin().key();
380
                InternalNode *keep = i.value()->subnodes.value(key);
381
                keep->displayName = i.value()->displayName + QLatin1Char('/') + keep->displayName;
382
                newSubnodes.insert(key, keep);
383 384 385 386
                i.value()->subnodes.clear();
                delete i.value();
            } else {
                newSubnodes.insert(i.key(), i.value());
387 388
            }
        }
389 390
        subnodes = newSubnodes;
    }
391

392 393 394 395 396 397 398 399 400 401 402 403 404 405
    FolderNode *createFolderNode(InternalNode *node)
    {
        FolderNode *newNode = 0;
        if (node->typeName.isEmpty())
            newNode = new FolderNode(node->fullPath);
        else
            newNode = new ProVirtualFolderNode(node->fullPath, node->priority, node->typeName);

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

406
    // Makes the projectNode's subtree below the given folder match this internal node's subtree
407
    void updateSubFolders(Qt4ProjectManager::Qt4PriFileNode *projectNode, ProjectExplorer::FolderNode *folder)
408 409
    {
        updateFiles(projectNode, folder, type);
410

411 412 413
        // updateFolders
        QMultiMap<QString, FolderNode *> existingFolderNodes;
        foreach (FolderNode *node, folder->subFolderNodes())
414
            if (node->nodeType() != ProjectNodeType)
415
                existingFolderNodes.insert(node->path(), node);
416

417 418 419 420 421
        QList<FolderNode *> foldersToRemove;
        QList<FolderNode *> foldersToAdd;
        typedef QPair<InternalNode *, FolderNode *> NodePair;
        QList<NodePair> nodesToUpdate;

422 423 424 425 426 427 428 429 430
        // Check virtual
        {
            QList<InternalNode *>::const_iterator it = virtualfolders.constBegin();
            QList<InternalNode *>::const_iterator end = virtualfolders.constEnd();
            for ( ; it != end; ++it) {
                bool found = false;
                QString path = (*it)->fullPath;
                QMultiMap<QString, FolderNode *>::const_iterator oldit
                        = existingFolderNodes.constFind(path);
431
                while (oldit != existingFolderNodes.constEnd() && oldit.key() == path) {
432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448
                    if (oldit.value()->nodeType() == ProjectExplorer::VirtualFolderNodeType) {
                        ProjectExplorer::VirtualFolderNode *vfn
                                = qobject_cast<ProjectExplorer::VirtualFolderNode *>(oldit.value());
                        if (vfn->priority() == (*it)->priority) {
                            found = true;
                            break;
                        }
                    }
                    ++oldit;
                }
                if (found) {
                    nodesToUpdate << NodePair(*it, *oldit);
                } else {
                    FolderNode *newNode = createFolderNode(*it);
                    foldersToAdd << newNode;
                    nodesToUpdate << NodePair(*it, newNode);
                }
449
            }
450
        }
451 452 453 454 455 456 457 458 459 460
        // Check subnodes
        {
            QMap<QString, InternalNode *>::const_iterator it = subnodes.constBegin();
            QMap<QString, InternalNode *>::const_iterator end = subnodes.constEnd();

            for ( ; it != end; ++it) {
                bool found = false;
                QString path = it.value()->fullPath;
                QMultiMap<QString, FolderNode *>::const_iterator oldit
                        = existingFolderNodes.constFind(path);
461
                while (oldit != existingFolderNodes.constEnd() && oldit.key() == path) {
462 463 464 465 466 467 468 469 470 471 472 473 474 475
                    if (oldit.value()->nodeType() == ProjectExplorer::FolderNodeType) {
                        found = true;
                        break;
                    }
                    ++oldit;
                }
                if (found) {
                    nodesToUpdate << NodePair(it.value(), *oldit);
                } else {
                    FolderNode *newNode = createFolderNode(it.value());
                    foldersToAdd << newNode;
                    nodesToUpdate << NodePair(it.value(), newNode);
                }
            }
476
        }
477

478 479 480 481 482 483 484 485 486 487
        QSet<FolderNode *> toKeep;
        foreach (const NodePair &np, nodesToUpdate)
            toKeep << np.second;

        QMultiMap<QString, FolderNode *>::const_iterator jit = existingFolderNodes.constBegin();
        QMultiMap<QString, FolderNode *>::const_iterator jend = existingFolderNodes.constEnd();
        for ( ; jit != jend; ++jit)
            if (!toKeep.contains(jit.value()))
                foldersToRemove << jit.value();

488 489 490 491
        if (!foldersToRemove.isEmpty())
            projectNode->removeFolderNodes(foldersToRemove, folder);
        if (!foldersToAdd.isEmpty())
            projectNode->addFolderNodes(foldersToAdd, folder);
492

493 494 495 496 497
        foreach (const NodePair &np, nodesToUpdate)
            np.first->updateSubFolders(projectNode, np.second);
    }

    // Makes the folder's files match this internal node's file list
498
    void updateFiles(Qt4ProjectManager::Qt4PriFileNode *projectNode, FolderNode *folder, FileType type)
499 500 501 502 503
    {
        QList<FileNode*> existingFileNodes;
        foreach (FileNode *fileNode, folder->fileNodes()) {
            if (fileNode->fileType() == type && !fileNode->isGenerated())
                existingFileNodes << fileNode;
504 505
        }

506 507
        QList<FileNode*> filesToRemove;
        QList<FileNode*> filesToAdd;
508

509
        qSort(files);
510
        qSort(existingFileNodes.begin(), existingFileNodes.end(), sortNodesByPath);
511 512 513 514 515 516

        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) {
517 518
                filesToRemove << *existingNodeIter;
                ++existingNodeIter;
519
            } else if ((*existingNodeIter)->path() > *newPathIter) {
520
                filesToAdd << new ProjectExplorer::FileNode(*newPathIter, type, false);
521
                ++newPathIter;
522 523 524
            } else { // *existingNodeIter->path() == *newPathIter
                ++existingNodeIter;
                ++newPathIter;
525 526
            }
        }
527 528 529 530 531
        while (existingNodeIter != existingFileNodes.constEnd()) {
            filesToRemove << *existingNodeIter;
            ++existingNodeIter;
        }
        while (newPathIter != files.constEnd()) {
532
            filesToAdd << new ProjectExplorer::FileNode(*newPathIter, type, false);
533 534 535 536 537 538 539 540 541
            ++newPathIter;
        }

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

544
QStringList Qt4PriFileNode::baseVPaths(QtSupport::ProFileReader *reader, const QString &projectDir) const
con's avatar
con committed
545
{
dt's avatar
dt committed
546 547 548
    QStringList result;
    if (!reader)
        return result;
549
    result += reader->absolutePathValues(QLatin1String("VPATH"), projectDir);
dt's avatar
dt committed
550
    result << projectDir; // QMAKE_ABSOLUTE_SOURCE_PATH
551
    result += reader->absolutePathValues(QLatin1String("DEPENDPATH"), projectDir);
dt's avatar
dt committed
552 553 554
    result.removeDuplicates();
    return result;
}
con's avatar
con committed
555

556 557
QStringList Qt4PriFileNode::fullVPaths(const QStringList &baseVPaths, QtSupport::ProFileReader *reader,
                                       FileType type, const QString &qmakeVariable, const QString &projectDir) const
dt's avatar
dt committed
558 559 560 561 562
{
    QStringList vPaths;
    if (!reader)
        return vPaths;
    if (type == ProjectExplorer::SourceType)
563
        vPaths = reader->absolutePathValues(QLatin1String("VPATH_") + qmakeVariable, projectDir);
dt's avatar
dt committed
564 565
    vPaths += baseVPaths;
    if (type == ProjectExplorer::HeaderType)
566
        vPaths += reader->absolutePathValues(QLatin1String("INCLUDEPATH"), projectDir);
dt's avatar
dt committed
567 568 569 570
    vPaths.removeDuplicates();
    return vPaths;
}

571
static QSet<Utils::FileName> recursiveEnumerate(const QString &folder)
572
{
573
    QSet<Utils::FileName> result;
574 575 576 577 578 579
    QFileInfo fi(folder);
    if (fi.isDir()) {
        QDir dir(folder);
        dir.setFilter(dir.filter() | QDir::NoDotAndDotDot);

        foreach (const QFileInfo &file, dir.entryInfoList()) {
580
            if (file.isDir() && !file.isSymLink())
581 582
                result += recursiveEnumerate(file.absoluteFilePath());
            else
583
                result += Utils::FileName(file);
584 585
        }
    } else if (fi.exists()) {
586
        result << Utils::FileName(fi);
587 588 589
    }
    return result;
}
dt's avatar
dt committed
590

591
void Qt4PriFileNode::update(ProFile *includeFileExact, QtSupport::ProFileReader *readerExact, ProFile *includeFileCumlative, QtSupport::ProFileReader *readerCumulative)
dt's avatar
dt committed
592
{
con's avatar
con committed
593 594
    // add project file node
    if (m_fileNodes.isEmpty())
595
        addFileNodes(QList<FileNode*>() << new ProjectExplorer::FileNode(m_projectFilePath, ProjectExplorer::ProjectFileType, false), this);
con's avatar
con committed
596

597 598
    const QString &projectDir = m_qt4ProFileNode->m_projectDir;

599 600
    InternalNode contents;

601 602 603 604
    ProjectExplorer::Target *t = m_project->activeTarget();
    ProjectExplorer::Kit *k = t ? t->kit() : ProjectExplorer::KitManager::instance()->defaultKit();
    QtSupport::BaseQtVersion *qtVersion = QtSupport::QtKitInformation::qtVersion(k);

605 606
    // Figure out DEPLOYMENT and INSTALL folders
    QStringList folders;
607
    QStringList dynamicVariables = dynamicVarNames(readerExact, readerCumulative, qtVersion);
608 609 610 611 612 613 614 615
    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
        }
616 617 618


    for (int i=0; i < folders.size(); ++i) {
619
        const QFileInfo fi(folders.at(i));
620
        if (fi.isRelative())
621
            folders[i] = QDir::cleanPath(projectDir + QLatin1Char('/') + folders.at(i));
622 623
    }

dt's avatar
dt committed
624 625

    m_recursiveEnumerateFiles.clear();
626
    // Remove non existing items and non folders
dt's avatar
dt committed
627 628 629
    QStringList::iterator it = folders.begin();
    while (it != folders.end()) {
        QFileInfo fi(*it);
dt's avatar
dt committed
630 631 632 633 634 635
        if (fi.exists()) {
            if (fi.isDir()) {
                // keep directories
                ++it;
            } else {
                // move files directly to m_recursiveEnumerateFiles
636
                m_recursiveEnumerateFiles << Utils::FileName::fromString(*it);
dt's avatar
dt committed
637 638 639 640
                it = folders.erase(it);
            }
        } else {
            // do remove non exsting stuff
dt's avatar
dt committed
641
            it = folders.erase(it);
dt's avatar
dt committed
642
        }
dt's avatar
dt committed
643 644
    }

645 646 647 648 649 650
    folders.removeDuplicates();
    watchFolders(folders.toSet());

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

653 654 655 656 657 658 659 660 661
    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
662
    // update files
663
    for (int i = 0; i < fileTypes.size(); ++i) {
664
        FileType type = fileTypes.at(i).type;
665
        QStringList qmakeVariables = varNames(type);
con's avatar
con committed
666

667
        QSet<Utils::FileName> newFilePaths;
668
        foreach (const QString &qmakeVariable, qmakeVariables) {
669 670
            if (includeFileExact) {
                QStringList vPathsExact = fullVPaths(baseVPathsExact, readerExact, type, qmakeVariable, projectDir);
671 672 673
                QStringList tmp = readerExact->absoluteFileValues(qmakeVariable, projectDir, vPathsExact, includeFileExact);
                foreach (const QString &t, tmp)
                    newFilePaths += Utils::FileName::fromString(t);
674 675 676
            }
            if (includeFileCumlative) {
                QStringList vPathsCumulative = fullVPaths(baseVPathsCumulative, readerCumulative, type, qmakeVariable, projectDir);
677 678 679
                QStringList tmp = readerCumulative->absoluteFileValues(qmakeVariable, projectDir, vPathsCumulative, includeFileCumlative);
                foreach (const QString &t, tmp)
                    newFilePaths += Utils::FileName::fromString(t);
680
            }
681
        }
con's avatar
con committed
682

dt's avatar
dt committed
683 684 685 686 687 688
        foundFiles[type] = newFilePaths;
        m_recursiveEnumerateFiles.subtract(newFilePaths);
    }

    for (int i = 0; i < fileTypes.size(); ++i) {
        FileType type = fileTypes.at(i).type;
689
        QSet<Utils::FileName> newFilePaths = filterFilesProVariables(type, foundFiles[type]);
690
        newFilePaths += filterFilesRecursiveEnumerata(type, m_recursiveEnumerateFiles);
691 692 693 694 695 696 697 698

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

699 700 701
        if (!newFilePaths.isEmpty()) {
            InternalNode *subfolder = new InternalNode;
            subfolder->type = type;
702
            subfolder->icon = fileTypes.at(i).icon;
703 704 705
            subfolder->fullPath = m_projectDir;
            subfolder->typeName = fileTypes.at(i).typeName;
            subfolder->priority = -i;
706
            subfolder->displayName = fileTypes.at(i).typeName;
707
            contents.virtualfolders.append(subfolder);
708
            // create the hierarchy with subdirectories
709
            subfolder->create(m_projectDir, newFilePaths, type);
con's avatar
con committed
710 711
        }
    }
712 713 714 715 716 717 718 719 720 721 722 723 724

    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())
725
        m_project->unwatchFolders(toUnwatch.toList(), this);
726
    if (!toWatch.isEmpty())
727
        m_project->watchFolders(toWatch.toList(), this);
728 729 730 731

    m_watchedFolders = folders;
}

732
void Qt4PriFileNode::folderChanged(const QString &changedFolder)
733 734 735 736 737
{
    //qDebug()<<"########## Qt4PriFileNode::folderChanged";
    // So, we need to figure out which files changed.

    // Collect all the files
738
    QSet<Utils::FileName> newFiles;
739 740
    newFiles += recursiveEnumerate(changedFolder);

741
    foreach (const Utils::FileName &file, m_recursiveEnumerateFiles) {
742
        if (!file.isChildOf(Utils::FileName::fromString(changedFolder)))
743
            newFiles.insert(file);
744 745
    }

746
    QSet<Utils::FileName> addedFiles = newFiles;
747 748
    addedFiles.subtract(m_recursiveEnumerateFiles);

749
    QSet<Utils::FileName> removedFiles = m_recursiveEnumerateFiles;
750 751 752 753 754 755 756 757 758 759 760 761
    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;
762 763
        QSet<Utils::FileName> add = filterFilesRecursiveEnumerata(type, addedFiles);
        QSet<Utils::FileName> remove = filterFilesRecursiveEnumerata(type, removedFiles);
764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783

        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;
784 785 786
            subfolder->fullPath = m_projectDir;
            subfolder->typeName = fileTypes.at(i).typeName;
            subfolder->priority = -i;
787
            subfolder->displayName = fileTypes.at(i).typeName;
788
            contents.virtualfolders.append(subfolder);
789
            // create the hierarchy with subdirectories
790
            subfolder->create(m_projectDir, m_files[type], type);
791 792 793
        }
    }

794
    contents.updateSubFolders(this, this);
795
    m_project->updateFileList();
796
    m_project->updateCodeModels();
con's avatar
con committed
797 798
}

dt's avatar
dt committed
799 800 801
bool Qt4PriFileNode::deploysFolder(const QString &folder) const
{
    QString f = folder;
802 803 804
    const QChar slash = QLatin1Char('/');
    if (!f.endsWith(slash))
        f.append(slash);
dt's avatar
dt committed
805 806
    foreach (const QString &wf, m_watchedFolders) {
        if (f.startsWith(wf)
807 808
            && (wf.endsWith(slash)
                || (wf.length() < f.length() && f.at(wf.length()) == slash)))
dt's avatar
dt committed
809 810 811 812 813
            return true;
    }
    return false;
}

dt's avatar
dt committed
814 815
QList<ProjectExplorer::RunConfiguration *> Qt4PriFileNode::runConfigurationsFor(Node *node)
{
Tobias Hunger's avatar
Tobias Hunger committed
816 817 818
    QmakeRunConfigurationFactory *factory = QmakeRunConfigurationFactory::find(m_project->activeTarget());
    if (factory)
        return factory->runConfigurationsForNode(m_project->activeTarget(), node);
819
    return QList<ProjectExplorer::RunConfiguration *>();
dt's avatar
dt committed
820 821
}

822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842
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
843
QList<ProjectNode::ProjectAction> Qt4PriFileNode::supportedActions(Node *node) const
con's avatar
con committed
844 845
{
    QList<ProjectAction> actions;
846 847 848 849 850

    const FolderNode *folderNode = this;
    const Qt4ProFileNode *proFileNode;
    while (!(proFileNode = qobject_cast<const Qt4ProFileNode*>(folderNode)))
        folderNode = folderNode->parentFolderNode();
dt's avatar
dt committed
851
    Q_ASSERT(proFileNode);
852 853 854

    switch (proFileNode->projectType()) {
    case ApplicationTemplate:
855 856 857 858 859 860
    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
861
        actions << AddNewFile;
862
        if (m_recursiveEnumerateFiles.contains(Utils::FileName::fromString(node->path()))) {
dt's avatar
dt committed
863
            actions << EraseFile;
dt's avatar
dt committed
864
        } else {
865
            actions << RemoveFile;
dt's avatar
dt committed
866 867
        }

868
        bool addExistingFiles = true;
869
        if (node->nodeType() == ProjectExplorer::VirtualFolderNodeType) {
870 871 872 873 874
            // A virtual folder, we do what the projectexplorer does
            FolderNode *folder = qobject_cast<FolderNode *>(node);
            if (folder) {
                QStringList list;
                foreach (FolderNode *f, folder->subFolderNodes())
875
                    list << f->path() + QLatin1Char('/');
876 877 878 879 880
                if (deploysFolder(Utils::commonPath(list)))
                    addExistingFiles = false;
            }
        }

dt's avatar
dt committed