qbsnodes.cpp 20.7 KB
Newer Older
Tobias Hunger's avatar
Tobias Hunger committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** 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.
**
** GNU Lesser General Public License Usage
** 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
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/

#include "qbsnodes.h"

#include "qbsproject.h"
33
#include "qbsrunconfiguration.h"
Tobias Hunger's avatar
Tobias Hunger committed
34

35
#include <coreplugin/fileiconprovider.h>
36
#include <coreplugin/idocument.h>
37
#include <projectexplorer/projectexplorerconstants.h>
38
#include <projectexplorer/target.h>
39
#include <qtsupport/qtsupportconstants.h>
40
#include <utils/hostosinfo.h>
Tobias Hunger's avatar
Tobias Hunger committed
41
42
43
44
45
#include <utils/qtcassert.h>

#include <qbs.h>

#include <QDir>
46
#include <QStyle>
Tobias Hunger's avatar
Tobias Hunger committed
47
48
49
50
51

// ----------------------------------------------------------------------
// Helpers:
// ----------------------------------------------------------------------

52
53
54
55
56
57
58
static QString displayNameFromPath(const QString &path, const QString &base)
{
    QString dir = base;
    if (!base.endsWith(QLatin1Char('/')))
        dir.append(QLatin1Char('/'));

    QString name = path;
59
    if (name.startsWith(dir)) {
60
        name = name.mid(dir.count());
61
62
63
64
65
    } else {
        QFileInfo fi = QFileInfo(path);
        name = QCoreApplication::translate("Qbs::QbsProjectNode", "%1 in %2")
                .arg(fi.fileName(), fi.absolutePath());
    }
66
67
68

    return name;
}
Tobias Hunger's avatar
Tobias Hunger committed
69

70
static QIcon generateIcon()
71
72
73
74
75
76
77
78
79
80
81
82
83
{
    const QSize desiredSize = QSize(16, 16);
    const QIcon projectBaseIcon(QString::fromLatin1(QtSupport::Constants::ICON_QT_PROJECT));
    const QPixmap projectPixmap = Core::FileIconProvider::overlayIcon(QStyle::SP_DirIcon,
                                                                      projectBaseIcon,
                                                                      desiredSize);

    QIcon result;
    result.addPixmap(projectPixmap);

    return result;
}

84
85
86
namespace QbsProjectManager {
namespace Internal {

87
QIcon QbsProjectNode::m_projectIcon = generateIcon();
88
89
QIcon QbsProductNode::m_productIcon = QIcon(QString::fromLatin1(ProjectExplorer::Constants::ICON_REBUILD_SMALL));
QIcon QbsGroupNode::m_groupIcon = QIcon(QString::fromLatin1(ProjectExplorer::Constants::ICON_BUILD_SMALL));
90

Tobias Hunger's avatar
Tobias Hunger committed
91
92
class FileTreeNode {
public:
Tobias Hunger's avatar
Tobias Hunger committed
93
94
    explicit FileTreeNode(const QString &n = QString(), FileTreeNode *p = 0, bool f = false) :
        parent(p), name(n), m_isFile(f)
Tobias Hunger's avatar
Tobias Hunger committed
95
96
97
98
99
100
101
102
103
104
    {
        if (p)
            p->children.append(this);
    }

    ~FileTreeNode()
    {
        qDeleteAll(children);
    }

Tobias Hunger's avatar
Tobias Hunger committed
105
    FileTreeNode *addPart(const QString &n, bool isFile)
Tobias Hunger's avatar
Tobias Hunger committed
106
107
108
109
110
    {
        foreach (FileTreeNode *c, children) {
            if (c->name == n)
                return c;
        }
Tobias Hunger's avatar
Tobias Hunger committed
111
        return new FileTreeNode(n, this, isFile);
Tobias Hunger's avatar
Tobias Hunger committed
112
113
    }

Tobias Hunger's avatar
Tobias Hunger committed
114
    bool isFile() { return m_isFile; }
Tobias Hunger's avatar
Tobias Hunger committed
115

116
    static FileTreeNode *moveChildrenUp(FileTreeNode *node)
117
    {
118
        QTC_ASSERT(node, return 0);
119
120

        FileTreeNode *newParent = node->parent;
121
122
        if (!newParent)
            return 0;
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141

        // disconnect node and parent:
        node->parent = 0;
        newParent->children.removeOne(node);

        foreach (FileTreeNode *c, node->children) {
            // update path, make sure there will be no / before "C:" on windows:
            if (Utils::HostOsInfo::isWindowsHost() && node->name.isEmpty())
                c->name = node->name;
            else
                c->name = node->name + QLatin1Char('/') + c->name;

            newParent->children.append(c);
            c->parent = newParent;
        }

        // Delete node
        node->children.clear();
        delete node;
142
143
144
145
146
147
148
        return newParent;
    }

    // Moves the children of the node pointing to basedir to the root of the tree.
    static void reorder(FileTreeNode *node, const QString &basedir)
    {
        QTC_CHECK(!basedir.isEmpty());
Tobias Hunger's avatar
Tobias Hunger committed
149
150
151
152
        QString prefix = basedir;
        if (basedir.startsWith(QLatin1Char('/')))
            prefix = basedir.mid(1);
        prefix.append(QLatin1Char('/'));
153
154
155
156
157
158
159
160
161

        if (node->path() == basedir) {
            // Find root node:
            FileTreeNode *root = node;
            while (root->parent)
                root = root->parent;

            foreach (FileTreeNode *c, node->children) {
                // Update children names by prepending basedir
Tobias Hunger's avatar
Tobias Hunger committed
162
                c->name = prefix + c->name;
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
                // Update parent information:
                c->parent = root;

                root->children.append(c);
            }

            // Clean up node:
            node->children.clear();
            node->parent->children.removeOne(node);
            node->parent = 0;
            delete node;

            return;
        }

        foreach (FileTreeNode *n, node->children)
            reorder(n, basedir);
180
181
    }

Tobias Hunger's avatar
Tobias Hunger committed
182
183
184
185
186
    static void simplify(FileTreeNode *node)
    {
        foreach (FileTreeNode *c, node->children)
            simplify(c);

187
188
189
        if (!node->parent)
            return;

Tobias Hunger's avatar
Tobias Hunger committed
190
        if (node->children.isEmpty() && !node->isFile()) {
191
192
193
194
195
196
197
            // Clean up empty folder nodes:
            node->parent->children.removeOne(node);
            node->parent = 0;
            delete node;
        } else if (node->children.count() == 1 && !node->children.at(0)->isFile()) {
            // Compact folder nodes with one child only:
            moveChildrenUp(node);
Tobias Hunger's avatar
Tobias Hunger committed
198
199
200
201
202
203
204
205
        }
    }

    QString path()
    {
        QString p = name;
        FileTreeNode *node = parent;
        while (node) {
206
207
            if (!Utils::HostOsInfo::isWindowsHost() || !node->name.isEmpty())
                p = node->name + QLatin1Char('/') + p;
Tobias Hunger's avatar
Tobias Hunger committed
208
209
210
211
212
213
214
215
            node = node->parent;
        }
        return p;
    }

    QList<FileTreeNode *> children;
    FileTreeNode *parent;
    QString name;
Tobias Hunger's avatar
Tobias Hunger committed
216
    bool m_isFile;
Tobias Hunger's avatar
Tobias Hunger committed
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
};

// ----------------------------------------------------------------------
// QbsFileNode:
// ----------------------------------------------------------------------

QbsFileNode::QbsFileNode(const QString &filePath, const ProjectExplorer::FileType fileType,
                         bool generated, int line) :
    ProjectExplorer::FileNode(filePath, fileType, generated),
    m_line(line)
{ }

void QbsFileNode::setLine(int l)
{
    m_line = l;
}

234
235
236
237
238
QString QbsFileNode::displayName() const
{
    return ProjectExplorer::FileNode::displayName() + QLatin1Char(':') + QString::number(m_line);
}

239
240
241
242
243
244
245
246
247
248
bool QbsFileNode::update(const qbs::CodeLocation &loc)
{
    const QString oldPath = path();
    const int oldLine = line();

    setPath(loc.fileName());
    setLine(loc.line());
    return (line() != oldLine || path() != oldPath);
}

Tobias Hunger's avatar
Tobias Hunger committed
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
// ---------------------------------------------------------------------------
// QbsBaseProjectNode:
// ---------------------------------------------------------------------------

QbsBaseProjectNode::QbsBaseProjectNode(const QString &path) :
    ProjectExplorer::ProjectNode(path)
{ }

bool QbsBaseProjectNode::hasBuildTargets() const
{
    foreach (ProjectNode *n, subProjectNodes())
        if (n->hasBuildTargets())
            return true;
    return false;
}

QList<ProjectExplorer::ProjectNode::ProjectAction> QbsBaseProjectNode::supportedActions(ProjectExplorer::Node *node) const
{
    Q_UNUSED(node);
    return QList<ProjectExplorer::ProjectNode::ProjectAction>();
}

bool QbsBaseProjectNode::canAddSubProject(const QString &proFilePath) const
{
    Q_UNUSED(proFilePath);
    return false;
}

bool QbsBaseProjectNode::addSubProjects(const QStringList &proFilePaths)
{
    Q_UNUSED(proFilePaths);
    return false;
}

bool QbsBaseProjectNode::removeSubProjects(const QStringList &proFilePaths)
{
    Q_UNUSED(proFilePaths);
    return false;
}

289
bool QbsBaseProjectNode::addFiles(const QStringList &filePaths, QStringList *notAdded)
Tobias Hunger's avatar
Tobias Hunger committed
290
291
292
293
294
295
{
    Q_UNUSED(filePaths);
    Q_UNUSED(notAdded);
    return false;
}

296
bool QbsBaseProjectNode::removeFiles(const QStringList &filePaths, QStringList *notRemoved)
Tobias Hunger's avatar
Tobias Hunger committed
297
298
299
300
301
302
{
    Q_UNUSED(filePaths);
    Q_UNUSED(notRemoved);
    return false;
}

303
bool QbsBaseProjectNode::deleteFiles(const QStringList &filePaths)
Tobias Hunger's avatar
Tobias Hunger committed
304
305
306
307
308
{
    Q_UNUSED(filePaths);
    return false;
}

309
bool QbsBaseProjectNode::renameFile(const QString &filePath, const QString &newFilePath)
Tobias Hunger's avatar
Tobias Hunger committed
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
{
    Q_UNUSED(filePath);
    Q_UNUSED(newFilePath);
    return false;
}

QList<ProjectExplorer::RunConfiguration *> QbsBaseProjectNode::runConfigurationsFor(ProjectExplorer::Node *node)
{
    Q_UNUSED(node);
    return QList<ProjectExplorer::RunConfiguration *>();
}

// --------------------------------------------------------------------
// QbsGroupNode:
// --------------------------------------------------------------------

326
QbsGroupNode::QbsGroupNode(const qbs::GroupData *grp, const QString &productPath) :
Tobias Hunger's avatar
Tobias Hunger committed
327
    QbsBaseProjectNode(QString()),
328
    m_qbsGroupData(0)
Tobias Hunger's avatar
Tobias Hunger committed
329
{
330
    setIcon(m_groupIcon);
331

332
    QbsFileNode *idx = new QbsFileNode(grp->location().fileName(),
333
                                       ProjectExplorer::ProjectFileType, false,
334
                                       grp->location().line());
335
336
    addFileNodes(QList<ProjectExplorer::FileNode *>() << idx, this);

337
    updateQbsGroupData(grp, productPath, true, true);
Tobias Hunger's avatar
Tobias Hunger committed
338
339
340
341
}

bool QbsGroupNode::isEnabled() const
{
342
343
344
345
    if (!parentFolderNode() || !m_qbsGroupData)
        return false;
    return static_cast<QbsProductNode *>(parentFolderNode())->isEnabled()
            && m_qbsGroupData->isEnabled();
Tobias Hunger's avatar
Tobias Hunger committed
346
347
}

348
349
void QbsGroupNode::updateQbsGroupData(const qbs::GroupData *grp, const QString &productPath,
                                      bool productWasEnabled, bool productIsEnabled)
Tobias Hunger's avatar
Tobias Hunger committed
350
{
351
352
    Q_ASSERT(grp);

353
    if (grp == m_qbsGroupData && productPath == m_productPath)
Tobias Hunger's avatar
Tobias Hunger committed
354
355
        return;

356
357
358
359
    bool groupWasEnabled = productWasEnabled && m_qbsGroupData && m_qbsGroupData->isEnabled();
    bool groupIsEnabled = productIsEnabled && grp->isEnabled();
    bool updateExisting = groupWasEnabled != groupIsEnabled;

360
    m_productPath = productPath;
361
    m_qbsGroupData = grp;
362

363
    setPath(grp->location().fileName());
364
365
366
367
368
369
370
    setDisplayName(grp->name());

    QbsFileNode *idx = 0;
    foreach (ProjectExplorer::FileNode *fn, fileNodes()) {
        idx = qobject_cast<QbsFileNode *>(fn);
        if (idx)
            break;
Tobias Hunger's avatar
Tobias Hunger committed
371
    }
372
373
    if (idx->update(grp->location()) || updateExisting)
        idx->emitNodeUpdated();
Tobias Hunger's avatar
Tobias Hunger committed
374

375
    setupFiles(this, grp->allFilePaths(), productPath, updateExisting);
376

377
378
    if (updateExisting)
        emitNodeUpdated();
379
380
}

381
void QbsGroupNode::setupFiles(QbsBaseProjectNode *root, const QStringList &files,
382
                              const QString &productPath, bool updateExisting)
383
{
Tobias Hunger's avatar
Tobias Hunger committed
384
    // Build up a tree of nodes:
385
    FileTreeNode tree;
Tobias Hunger's avatar
Tobias Hunger committed
386

387
    foreach (const QString &path, files) {
Tobias Hunger's avatar
Tobias Hunger committed
388
389
        QStringList pathSegments = path.split(QLatin1Char('/'), QString::SkipEmptyParts);

390
        FileTreeNode *root = &tree;
Tobias Hunger's avatar
Tobias Hunger committed
391
392
393
394
        while (!pathSegments.isEmpty()) {
            bool isFile = pathSegments.count() == 1;
            root = root->addPart(pathSegments.takeFirst(), isFile);
        }
Tobias Hunger's avatar
Tobias Hunger committed
395
396
    }

397
    FileTreeNode::reorder(&tree, productPath);
398
399
    FileTreeNode::simplify(&tree);

400
    setupFolder(root, &tree, productPath, updateExisting);
Tobias Hunger's avatar
Tobias Hunger committed
401
402
}

403
void QbsGroupNode::setupFolder(ProjectExplorer::FolderNode *root,
404
405
                               const FileTreeNode *fileTree, const QString &baseDir,
                               bool updateExisting)
Tobias Hunger's avatar
Tobias Hunger committed
406
{
407
408
409
410
411
    // We only need to care about FileNodes and FolderNodes here. Everything else is
    // handled elsewhere.
    // QbsGroupNodes are managed by the QbsProductNode.
    // The buildsystem file is either managed by QbsProductNode or by updateQbsGroupData(...).

412
413
    QList<ProjectExplorer::FileNode *> filesToRemove;
    foreach (ProjectExplorer::FileNode *fn, root->fileNodes()) {
414
415
        if (!qobject_cast<QbsFileNode *>(fn))
            filesToRemove << fn;
Tobias Hunger's avatar
Tobias Hunger committed
416
417
418
    }
    QList<ProjectExplorer::FileNode *> filesToAdd;

419
420
    QList<ProjectExplorer::FolderNode *> foldersToRemove;
    foreach (ProjectExplorer::FolderNode *fn, root->subFolderNodes()) {
421
422
423
        if (fn->nodeType() == ProjectExplorer::ProjectNodeType)
            continue; // Skip ProjectNodes mixed into the folders...
        foldersToRemove.append(fn);
424
    }
Tobias Hunger's avatar
Tobias Hunger committed
425

426
    foreach (FileTreeNode *c, fileTree->children) {
Tobias Hunger's avatar
Tobias Hunger committed
427
        QString path = c->path();
428
429

        // Handle files:
Tobias Hunger's avatar
Tobias Hunger committed
430
431
432
433
        if (c->isFile()) {
            ProjectExplorer::FileNode *fn = root->findFile(path);
            if (fn) {
                filesToRemove.removeOne(fn);
434
435
                if (updateExisting)
                    fn->emitNodeUpdated();
Tobias Hunger's avatar
Tobias Hunger committed
436
437
438
439
            } else {
                fn = new ProjectExplorer::FileNode(path, ProjectExplorer::UnknownFileType, false);
                filesToAdd.append(fn);
            }
440
            continue;
Tobias Hunger's avatar
Tobias Hunger committed
441
        } else {
442
443
444
445
446
447
            FolderNode *fn = root->findSubFolder(c->path());
            if (!fn) {
                fn = new FolderNode(c->path());
                root->projectNode()->addFolderNodes(QList<FolderNode *>() << fn, root);
            } else {
                foldersToRemove.removeOne(fn);
448
449
                if (updateExisting)
                    fn->emitNodeUpdated();
450
451
452
            }
            fn->setDisplayName(displayNameFromPath(c->path(), baseDir));

453
            setupFolder(fn, c, c->path(), updateExisting);
Tobias Hunger's avatar
Tobias Hunger committed
454
455
        }
    }
456
457
458
    root->projectNode()->removeFileNodes(filesToRemove, root);
    root->projectNode()->removeFolderNodes(foldersToRemove, root);
    root->projectNode()->addFileNodes(filesToAdd, root);
Tobias Hunger's avatar
Tobias Hunger committed
459
460
461
462
463
464
}

// --------------------------------------------------------------------
// QbsProductNode:
// --------------------------------------------------------------------

465
466
QbsProductNode::QbsProductNode(const qbs::ProductData &prd) :
    QbsBaseProjectNode(prd.location().fileName())
Tobias Hunger's avatar
Tobias Hunger committed
467
{
468
    setIcon(m_productIcon);
469

470
    ProjectExplorer::FileNode *idx = new QbsFileNode(prd.location().fileName(),
471
                                                     ProjectExplorer::ProjectFileType, false,
472
                                                     prd.location().line());
473
474
    addFileNodes(QList<ProjectExplorer::FileNode *>() << idx, this);

475
    setQbsProductData(prd);
Tobias Hunger's avatar
Tobias Hunger committed
476
477
478
479
}

bool QbsProductNode::isEnabled() const
{
480
    return m_qbsProductData.isEnabled();
Tobias Hunger's avatar
Tobias Hunger committed
481
482
}

483
void QbsProductNode::setQbsProductData(const qbs::ProductData prd)
Tobias Hunger's avatar
Tobias Hunger committed
484
{
485
    if (m_qbsProductData == prd)
Tobias Hunger's avatar
Tobias Hunger committed
486
487
        return;

488
489
    bool productWasEnabled = m_qbsProductData.isEnabled();
    bool productIsEnabled = prd.isEnabled();
490
491
    bool updateExisting = productWasEnabled != productIsEnabled;

492
493
494
    setDisplayName(prd.name());
    setPath(prd.location().fileName());
    const QString &productPath = QFileInfo(prd.location().fileName()).absolutePath();
Tobias Hunger's avatar
Tobias Hunger committed
495

496
    // Find the QbsFileNode we added earlier:
497
    QbsFileNode *idx = 0;
498
499
500
501
    foreach (ProjectExplorer::FileNode *fn, fileNodes()) {
        idx = qobject_cast<QbsFileNode *>(fn);
        if (idx)
            break;
Tobias Hunger's avatar
Tobias Hunger committed
502
    }
503
    if (idx->update(prd.location()) || updateExisting)
504
        idx->emitNodeUpdated();
Tobias Hunger's avatar
Tobias Hunger committed
505
506
507
508

    QList<ProjectExplorer::ProjectNode *> toAdd;
    QList<ProjectExplorer::ProjectNode *> toRemove = subProjectNodes();

509
510
    foreach (const qbs::GroupData &grp, prd.groups()) {
        if (grp.name() == prd.name() && grp.location() == prd.location()) {
511
            // Set implicit product group right onto this node:
512
            QbsGroupNode::setupFiles(this, grp.allFilePaths(), productPath, updateExisting);
513
514
            continue;
        }
515
516
517
        QbsGroupNode *gn = findGroupNode(grp.name());
        if (gn) {
            toRemove.removeOne(gn);
518
            gn->updateQbsGroupData(&grp, productPath, productWasEnabled, productIsEnabled);
Tobias Hunger's avatar
Tobias Hunger committed
519
        } else {
520
521
            gn = new QbsGroupNode(&grp, productPath);
            toAdd.append(gn);
Tobias Hunger's avatar
Tobias Hunger committed
522
523
524
525
526
527
        }
    }

    addProjectNodes(toAdd);
    removeProjectNodes(toRemove);

528
    m_qbsProductData = prd;
529
530
    if (updateExisting)
        emitNodeUpdated();
Tobias Hunger's avatar
Tobias Hunger committed
531
532
}

533
534
535
536
537
QList<ProjectExplorer::RunConfiguration *> QbsProductNode::runConfigurationsFor(ProjectExplorer::Node *node)
{
    Q_UNUSED(node);
    QList<ProjectExplorer::RunConfiguration *> result;
    QbsProjectNode *pn = qobject_cast<QbsProjectNode *>(projectNode());
538
    if (!isEnabled() || !pn || pn->qbsProject()->targetExecutable(m_qbsProductData,
539
                                                                  qbs::InstallOptions()).isEmpty()) {
540
        return result;
541
    }
542
543
544
545
546

    foreach (ProjectExplorer::RunConfiguration *rc, pn->project()->activeTarget()->runConfigurations()) {
        QbsRunConfiguration *qbsRc = qobject_cast<QbsRunConfiguration *>(rc);
        if (!qbsRc)
            continue;
547
        if (qbsRc->qbsProduct() == qbsProductData().name())
548
549
550
551
552
553
            result << qbsRc;
    }

    return result;
}

Tobias Hunger's avatar
Tobias Hunger committed
554
555
556
557
QbsGroupNode *QbsProductNode::findGroupNode(const QString &name)
{
    foreach (ProjectExplorer::ProjectNode *n, subProjectNodes()) {
        QbsGroupNode *qn = static_cast<QbsGroupNode *>(n);
558
        if (qn->qbsGroupData()->name() == name)
Tobias Hunger's avatar
Tobias Hunger committed
559
560
561
562
563
564
565
566
567
            return qn;
    }
    return 0;
}

// --------------------------------------------------------------------
// QbsProjectNode:
// --------------------------------------------------------------------

568
569
QbsProjectNode::QbsProjectNode(QbsProject *project) :
    QbsBaseProjectNode(project->document()->fileName()),
570
    m_project(project), m_qbsProject(0)
Tobias Hunger's avatar
Tobias Hunger committed
571
{
Tobias Hunger's avatar
Tobias Hunger committed
572
573
574
575
576
    ctor();
}

QbsProjectNode::QbsProjectNode(const QString &path) :
    QbsBaseProjectNode(path),
577
    m_project(0), m_qbsProject(0)
Tobias Hunger's avatar
Tobias Hunger committed
578
579
{
    ctor();
Tobias Hunger's avatar
Tobias Hunger committed
580
581
582
583
}

QbsProjectNode::~QbsProjectNode()
{
584
    // do not delete m_project
585
    delete m_qbsProject;
Tobias Hunger's avatar
Tobias Hunger committed
586
587
588
}

void QbsProjectNode::update(const qbs::Project *prj)
Tobias Hunger's avatar
Tobias Hunger committed
589
{
590
    update(prj ? prj->projectData() : qbs::ProjectData());
Tobias Hunger's avatar
Tobias Hunger committed
591
592
593
594
595
596

    delete m_qbsProject;
    m_qbsProject = prj;
}

void QbsProjectNode::update(const qbs::ProjectData &prjData)
Tobias Hunger's avatar
Tobias Hunger committed
597
598
599
600
{
    QList<ProjectExplorer::ProjectNode *> toAdd;
    QList<ProjectExplorer::ProjectNode *> toRemove = subProjectNodes();

Tobias Hunger's avatar
Tobias Hunger committed
601
602
603
604
605
606
607
608
609
610
611
    foreach (const qbs::ProjectData &subData, prjData.subProjects()) {
        QbsProjectNode *qn = findProjectNode(subData.name());
        if (!qn) {
            QbsProjectNode *subProject = new QbsProjectNode(prjData.location().fileName());
            subProject->update(subData);
            toAdd << subProject;
        } else {
            qn->update(subData);
            toRemove.removeOne(qn);
        }
    }
Tobias Hunger's avatar
Tobias Hunger committed
612

Tobias Hunger's avatar
Tobias Hunger committed
613
614
615
    foreach (const qbs::ProductData &prd, prjData.products()) {
        QbsProductNode *qn = findProductNode(prd.name());
        if (!qn) {
616
            toAdd << new QbsProductNode(prd);
Tobias Hunger's avatar
Tobias Hunger committed
617
        } else {
618
            qn->setQbsProductData(prd);
Tobias Hunger's avatar
Tobias Hunger committed
619
            toRemove.removeOne(qn);
Tobias Hunger's avatar
Tobias Hunger committed
620
621
622
        }
    }

Tobias Hunger's avatar
Tobias Hunger committed
623
    setDisplayName(prjData.name());
Tobias Hunger's avatar
Tobias Hunger committed
624
625
626

    removeProjectNodes(toRemove);
    addProjectNodes(toAdd);
627
628

    m_qbsProjectData = prjData;
Tobias Hunger's avatar
Tobias Hunger committed
629
630
}

631
632
QbsProject *QbsProjectNode::project() const
{
Tobias Hunger's avatar
Tobias Hunger committed
633
634
    if (!m_project && projectNode())
        return static_cast<QbsProjectNode *>(projectNode())->project();
635
636
637
    return m_project;
}

638
const qbs::Project *QbsProjectNode::qbsProject() const
Tobias Hunger's avatar
Tobias Hunger committed
639
{
Tobias Hunger's avatar
Tobias Hunger committed
640
641
642
    QbsProjectNode *parent = qobject_cast<QbsProjectNode *>(projectNode());
    if (!m_qbsProject && parent != this)
        return parent->qbsProject();
643
    return m_qbsProject;
Tobias Hunger's avatar
Tobias Hunger committed
644
645
}

646
const qbs::ProjectData QbsProjectNode::qbsProjectData() const
Tobias Hunger's avatar
Tobias Hunger committed
647
{
648
    return m_qbsProjectData;
Tobias Hunger's avatar
Tobias Hunger committed
649
650
}

Tobias Hunger's avatar
Tobias Hunger committed
651
652
653
654
655
656
657
void QbsProjectNode::ctor()
{
    setIcon(m_projectIcon);
    addFileNodes(QList<ProjectExplorer::FileNode *>()
                 << new ProjectExplorer::FileNode(path(), ProjectExplorer::ProjectFileType, false), this);
}

Tobias Hunger's avatar
Tobias Hunger committed
658
659
660
QbsProductNode *QbsProjectNode::findProductNode(const QString &name)
{
    foreach (ProjectExplorer::ProjectNode *n, subProjectNodes()) {
Tobias Hunger's avatar
Tobias Hunger committed
661
        QbsProductNode *qn = qobject_cast<QbsProductNode *>(n);
662
        if (qn && qn->qbsProductData().name() == name)
Tobias Hunger's avatar
Tobias Hunger committed
663
664
665
666
667
668
669
670
671
            return qn;
    }
    return 0;
}

QbsProjectNode *QbsProjectNode::findProjectNode(const QString &name)
{
    foreach (ProjectExplorer::ProjectNode *n, subProjectNodes()) {
        QbsProjectNode *qn = qobject_cast<QbsProjectNode *>(n);
672
        if (qn && qn->qbsProjectData().name() == name)
Tobias Hunger's avatar
Tobias Hunger committed
673
674
675
676
677
678
679
            return qn;
    }
    return 0;
}

} // namespace Internal
} // namespace QbsProjectManager