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

31
#include "qtoptionspage.h"
32
#include "qtconfigwidget.h"
33 34
#include "ui_showbuildlog.h"
#include "ui_qtversionmanager.h"
35 36
#include "ui_qtversioninfo.h"
#include "ui_debugginghelper.h"
37
#include "qtsupportconstants.h"
38
#include "qtversionmanager.h"
dt's avatar
dt committed
39
#include "qtversionfactory.h"
40
#include "qmldumptool.h"
41 42

#include <coreplugin/progressmanager/progressmanager.h>
43
#include <coreplugin/coreconstants.h>
hjk's avatar
hjk committed
44
#include <coreplugin/variablechooser.h>
45
#include <projectexplorer/toolchain.h>
46
#include <projectexplorer/toolchainmanager.h>
47
#include <projectexplorer/projectexplorerconstants.h>
48
#include <utils/hostosinfo.h>
hjk's avatar
hjk committed
49 50
#include <utils/pathchooser.h>
#include <utils/qtcassert.h>
51
#include <utils/runextensions.h>
52
#include <utils/algorithm.h>
53
#include <utils/themehelper.h>
54
#include <utils/treemodel.h>
55

56 57 58
#include <QDir>
#include <QMessageBox>
#include <QFileDialog>
59 60
#include <QTextBrowser>
#include <QDesktopServices>
61 62 63
#include <QSortFilterProxyModel>

#include <utility>
64

hjk's avatar
hjk committed
65
using namespace ProjectExplorer;
66
using namespace Utils;
67

hjk's avatar
hjk committed
68 69 70
namespace QtSupport {
namespace Internal {

71 72 73 74 75 76 77 78 79 80 81 82 83 84
class QtVersionItem : public TreeItem
{
public:
    QtVersionItem(const QString &name) // for auto/manual node
        : TreeItem({name})
    {}

    QtVersionItem(BaseQtVersion *version) // for versions
        : TreeItem(),
          m_version(version)
    {}

    ~QtVersionItem()
    {
Daniel Teske's avatar
Daniel Teske committed
85
        delete m_version;
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
    }

    void setVersion(BaseQtVersion *version)
    {
        m_version = version;
        update();
    }

    int uniqueId() const
    {
        return m_version ? m_version->uniqueId() : -1;
    }

    BaseQtVersion *version() const
    {
        return m_version;
    }

    QVariant data(int column, int role) const
    {
        if (!m_version)
            return TreeItem::data(column, role);

        if (role == Qt::DisplayRole) {
            if (column == 0)
                return m_version->displayName();
            if (column == 1)
                return m_version->qmakeCommand().toUserOutput();
        }

        if (role == Qt::DecorationRole && column == 0)
            return m_icon;

        return QVariant();
    }

    void setIcon(const QIcon &icon)
    {
        m_icon = icon;
        update();
    }

    QString buildLog() const
    {
        return m_buildLog;
    }

    void setBuildLog(const QString &buildLog)
    {
        m_buildLog = buildLog;
    }

    QByteArray toolChainId() const
    {
        return m_toolChainId;
    }

    void setToolChainId(const QByteArray &id)
    {
        m_toolChainId = id;
    }

    DebuggingHelperBuildTask::Tools buildFlags() const
    {
        return m_buildFlags;
    }

    void setBuildFlags(DebuggingHelperBuildTask::Tools flags)
    {
        m_buildFlags = flags;
    }

private:
Daniel Teske's avatar
Daniel Teske committed
159
    BaseQtVersion *m_version = 0;
160 161 162 163 164
    QIcon m_icon;
    QString m_buildLog;
    QByteArray m_toolChainId;
    DebuggingHelperBuildTask::Tools m_buildFlags;
};
hjk's avatar
hjk committed
165

166 167 168 169 170
///
// QtOptionsPage
///

QtOptionsPage::QtOptionsPage()
dt's avatar
dt committed
171
    : m_widget(0)
172
{
hjk's avatar
hjk committed
173
    setId(Constants::QTVERSION_SETTINGS_PAGE_ID);
174
    setDisplayName(QCoreApplication::translate("Qt4ProjectManager", Constants::QTVERSION_SETTINGS_PAGE_NAME));
hjk's avatar
hjk committed
175
    setCategory(ProjectExplorer::Constants::PROJECTEXPLORER_SETTINGS_CATEGORY);
176 177 178
    setDisplayCategory(QCoreApplication::translate("ProjectExplorer",
        ProjectExplorer::Constants::PROJECTEXPLORER_SETTINGS_TR_CATEGORY));
    setCategoryIcon(QLatin1String(ProjectExplorer::Constants::PROJECTEXPLORER_SETTINGS_CATEGORY_ICON));
179 180
}

181
QWidget *QtOptionsPage::widget()
182
{
183 184
    if (!m_widget)
        m_widget = new QtOptionsPageWidget;
185 186 187 188 189
    return m_widget;
}

void QtOptionsPage::apply()
{
190 191
    if (!m_widget) // page was never shown
        return;
192
    m_widget->apply();
193 194
}

195
void QtOptionsPage::finish()
196
{
197
    delete m_widget;
198 199
}

200
//-----------------------------------------------------
201 202


203
QtOptionsPageWidget::QtOptionsPageWidget(QWidget *parent)
204 205
    : QWidget(parent)
    , m_specifyNameString(tr("<specify a name>"))
206
    , m_ui(new Internal::Ui::QtVersionManager())
207 208
    , m_versionUi(new Internal::Ui::QtVersionInfo())
    , m_debuggingHelperUi(new Internal::Ui::DebuggingHelper())
209
    , m_infoBrowser(new QTextBrowser)
210 211
    , m_invalidVersionIcon(Utils::ThemeHelper::themedIcon(QLatin1String(Core::Constants::ICON_ERROR)))
    , m_warningVersionIcon(Utils::ThemeHelper::themedIcon(QLatin1String(Core::Constants::ICON_WARNING)))
dt's avatar
dt committed
212
    , m_configurationWidget(0)
213 214
    , m_autoItem(0)
    , m_manualItem(0)
215
{
216 217
    QWidget *versionInfoWidget = new QWidget();
    m_versionUi->setupUi(versionInfoWidget);
218
    m_versionUi->editPathPushButton->setText(PathChooser::browseButtonLabel());
219

220 221 222
    QWidget *debuggingHelperDetailsWidget = new QWidget();
    m_debuggingHelperUi->setupUi(debuggingHelperDetailsWidget);

223
    m_ui->setupUi(this);
224

225 226
    m_infoBrowser->setOpenLinks(false);
    m_infoBrowser->setTextInteractionFlags(Qt::TextBrowserInteraction);
227 228
    connect(m_infoBrowser, &QTextBrowser::anchorClicked,
            this, &QtOptionsPageWidget::infoAnchorClicked);
229
    m_ui->infoWidget->setWidget(m_infoBrowser);
230 231
    connect(m_ui->infoWidget, &DetailsWidget::expanded,
            this, &QtOptionsPageWidget::setInfoWidgetVisibility);
232

233
    m_ui->versionInfoWidget->setWidget(versionInfoWidget);
hjk's avatar
hjk committed
234
    m_ui->versionInfoWidget->setState(DetailsWidget::NoSummary);
235 236

    m_ui->debuggingHelperWidget->setWidget(debuggingHelperDetailsWidget);
237 238
    connect(m_ui->debuggingHelperWidget, &DetailsWidget::expanded,
            this, &QtOptionsPageWidget::setInfoWidgetVisibility);
239

240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258
    auto rootItem = new QtVersionItem(QLatin1String("root"));
    m_autoItem = new QtVersionItem(tr("Auto-detected"));
    rootItem->appendChild(m_autoItem);
    m_manualItem = new QtVersionItem(tr("Manual"));
    rootItem->appendChild(m_manualItem);

    m_model = new TreeModel(rootItem);
    m_model->setHeader({tr("Name"), tr("qmake Location"), tr("Type")});

    m_filterModel = new QSortFilterProxyModel(this);
    m_filterModel->setSourceModel(m_model);
    m_filterModel->setSortCaseSensitivity(Qt::CaseInsensitive);

    m_ui->qtdirList->setModel(m_filterModel);
    m_ui->qtdirList->setSortingEnabled(true);

    m_ui->qtdirList->setFirstColumnSpanned(0, QModelIndex(), true);
    m_ui->qtdirList->setFirstColumnSpanned(1, QModelIndex(), true);

dt_'s avatar
dt_ committed
259
    m_ui->qtdirList->header()->setStretchLastSection(false);
260 261
    m_ui->qtdirList->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
    m_ui->qtdirList->header()->setSectionResizeMode(1, QHeaderView::Stretch);
dt_'s avatar
dt_ committed
262
    m_ui->qtdirList->setTextElideMode(Qt::ElideNone);
263
    m_ui->qtdirList->sortByColumn(0, Qt::AscendingOrder);
264

265
    QList<int> additions = transform(QtVersionManager::versions(), &BaseQtVersion::uniqueId);
266 267

    updateQtVersions(additions, QList<int>(), QList<int>());
268 269

    m_ui->qtdirList->expandAll();
270

271 272
    connect(m_versionUi->nameEdit, &QLineEdit::textEdited,
            this, &QtOptionsPageWidget::updateCurrentQtName);
273

274 275
    connect(m_versionUi->editPathPushButton, &QAbstractButton::clicked,
            this, &QtOptionsPageWidget::editPath);
dt_'s avatar
dt_ committed
276

277 278 279 280
    connect(m_ui->addButton, &QAbstractButton::clicked,
            this, &QtOptionsPageWidget::addQtDir);
    connect(m_ui->delButton, &QAbstractButton::clicked,
            this, &QtOptionsPageWidget::removeQtDir);
281

282
    connect(m_ui->qtdirList->selectionModel(), &QItemSelectionModel::currentChanged,
283
            this, &QtOptionsPageWidget::versionChanged);
284

285 286 287 288
    connect(m_debuggingHelperUi->rebuildButton, &QAbstractButton::clicked,
            this, [this]() { buildDebuggingHelper(); });
    connect(m_debuggingHelperUi->qmlDumpBuildButton, &QAbstractButton::clicked,
            this, &QtOptionsPageWidget::buildQmlDump);
289

290 291 292 293
    connect(m_debuggingHelperUi->showLogButton, &QAbstractButton::clicked,
            this, &QtOptionsPageWidget::slotShowDebuggingBuildLog);
    connect(m_debuggingHelperUi->toolChainComboBox, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated),
            this, &QtOptionsPageWidget::selectedToolChainChanged);
294

295 296
    connect(m_ui->cleanUpButton, &QAbstractButton::clicked,
            this, &QtOptionsPageWidget::cleanUpQtVersions);
dt's avatar
dt committed
297 298
    userChangedCurrentVersion();
    updateCleanUpButton();
299

300 301
    connect(QtVersionManager::instance(), &QtVersionManager::dumpUpdatedFor,
            this, &QtOptionsPageWidget::qtVersionsDumpUpdated);
302

303 304
    connect(QtVersionManager::instance(), &QtVersionManager::qtVersionsChanged,
            this, &QtOptionsPageWidget::updateQtVersions);
305

306 307
    connect(ProjectExplorer::ToolChainManager::instance(), &ToolChainManager::toolChainsChanged,
            this, &QtOptionsPageWidget::toolChainsUpdated);
hjk's avatar
hjk committed
308 309

    auto chooser = new Core::VariableChooser(this);
310
    chooser->addSupportedWidget(m_versionUi->nameEdit, "Qt:Name");
hjk's avatar
hjk committed
311 312 313 314 315
    chooser->addMacroExpanderProvider(
        [this]() -> Utils::MacroExpander * {
            BaseQtVersion *version = currentVersion();
            return version ? version->macroExpander() : 0;
        });
316 317
}

dt's avatar
dt committed
318
BaseQtVersion *QtOptionsPageWidget::currentVersion() const
319
{
320 321 322 323
    QtVersionItem *item = currentItem();
    if (!item)
        return 0;
    return item->version();
324
}
325

326
QtVersionItem *QtOptionsPageWidget::currentItem() const
327
{
328 329 330 331
    QModelIndex idx = m_ui->qtdirList->selectionModel()->currentIndex();
    QModelIndex sourceIdx = m_filterModel->mapToSource(idx);
    QtVersionItem *item = static_cast<QtVersionItem *>(m_model->itemForIndex(sourceIdx));
    return item;
332
}
333

334
// Update with results of terminated helper build
335
void QtOptionsPageWidget::debuggingHelperBuildFinished(int qtVersionId, const QString &output, DebuggingHelperBuildTask::Tools tools)
336
{
337 338 339 340 341 342 343 344
    auto findItem = [qtVersionId](Utils::TreeItem *parent) {
        foreach (Utils::TreeItem *child, parent->children()) {
            auto item = static_cast<QtVersionItem *>(child);
            if (item->version()->uniqueId() == qtVersionId)
                return item;
        }
        return (QtVersionItem *)nullptr;
    };
345

346 347 348
    QtVersionItem *item = findItem(m_manualItem);
    if (!item)
        item = findItem(m_autoItem);
349

350 351 352 353 354
    if (!item)
        return;


    DebuggingHelperBuildTask::Tools buildFlags = item->buildFlags();
355
    buildFlags &= ~tools;
356 357
    item->setBuildFlags(buildFlags);
    item->setBuildLog(output);
358

359 360
    bool success = true;
    if (tools & DebuggingHelperBuildTask::QmlDump)
361
        success &= item->version()->hasQmlDump();
362

363 364
    if (!success)
        showDebuggingBuildLog(item);
365 366

    updateDebuggingHelperUi();
367 368
}

369 370
void QtOptionsPageWidget::cleanUpQtVersions()
{
371 372 373 374 375 376 377 378 379 380 381
    QVector<QtVersionItem *> toRemove;
    QString text;

    foreach (Utils::TreeItem *child, m_manualItem->children()) {
        auto item = static_cast<QtVersionItem *>(child);
        if (item->version() && !item->version()->isValid()) {
            toRemove.append(item);
            if (!text.isEmpty())
                text.append(QLatin1String("</li><li>"));
            text.append(item->version()->displayName());
        }
382 383 384 385 386
    }

    if (toRemove.isEmpty())
        return;

387

Friedemann Kleint's avatar
Friedemann Kleint committed
388
    if (QMessageBox::warning(0, tr("Remove Invalid Qt Versions"),
389 390
                             tr("Do you want to remove all invalid Qt Versions?<br>"
                                "<ul><li>%1</li></ul><br>"
391
                                "will be removed.").arg(text),
392 393 394
                             QMessageBox::Yes, QMessageBox::No) == QMessageBox::No)
        return;

395 396 397
    foreach (QtVersionItem *item, toRemove) {
        m_model->takeItem(item);
        delete item;
398
    }
399

dt's avatar
dt committed
400
    updateCleanUpButton();
401 402
}

403 404
void QtOptionsPageWidget::toolChainsUpdated()
{
405 406 407 408 409 410 411 412
    auto update = [this](Utils::TreeItem *parent) {
        foreach (Utils::TreeItem *child, parent->children()) {
            if (child == currentItem()) {
                updateDescriptionLabel();
                updateDebuggingHelperUi();
            } else {
                updateVersionItem(static_cast<QtVersionItem *>(child));
            }
413
        }
414 415 416 417
    };

    update(m_autoItem);
    update(m_manualItem);
418 419
}

420 421
void QtOptionsPageWidget::selectedToolChainChanged(int comboIndex)
{
422 423
    QtVersionItem *item = currentItem();
    if (!item)
424 425
        return;

426
    QByteArray toolChainId = m_debuggingHelperUi->toolChainComboBox->itemData(comboIndex).toByteArray();
427
    item->setToolChainId(toolChainId);
428 429
}

hjk's avatar
hjk committed
430
void QtOptionsPageWidget::qtVersionsDumpUpdated(const FileName &qmakeCommand)
431
{
432 433 434 435 436 437 438 439 440 441 442
    auto recheck = [qmakeCommand](Utils::TreeItem *parent) {
        foreach (Utils::TreeItem *child, parent->children()) {
            auto item = static_cast<QtVersionItem *>(child);
            if (item->version()->qmakeCommand() == qmakeCommand)
                item->version()->recheckDumper();
        }
    };

    recheck(m_autoItem);
    recheck(m_manualItem);

443 444
    if (currentVersion()
            && currentVersion()->qmakeCommand() == qmakeCommand) {
445 446 447 448 449 450
        updateWidgets();
        updateDescriptionLabel();
        updateDebuggingHelperUi();
    }
}

451
void QtOptionsPageWidget::setInfoWidgetVisibility()
452
{
453 454 455 456
    m_ui->versionInfoWidget->setVisible((m_ui->infoWidget->state() == DetailsWidget::Collapsed)
                                        && (m_ui->debuggingHelperWidget->state() == DetailsWidget::Collapsed));
    m_ui->infoWidget->setVisible(m_ui->debuggingHelperWidget->state() == DetailsWidget::Collapsed);
    m_ui->debuggingHelperWidget->setVisible(m_ui->infoWidget->state() == DetailsWidget::Collapsed);
457 458
}

459 460 461 462 463
void QtOptionsPageWidget::infoAnchorClicked(const QUrl &url)
{
    QDesktopServices::openUrl(url);
}

464 465 466 467 468 469 470 471
QtOptionsPageWidget::ValidityInfo QtOptionsPageWidget::validInformation(const BaseQtVersion *version)
{
    ValidityInfo info;
    info.icon = m_validVersionIcon;

    if (!version)
        return info;

472
    info.description = tr("Qt version %1 for %2").arg(version->qtVersionString(), version->description());
473 474 475 476 477 478 479 480 481
    if (!version->isValid()) {
        info.icon = m_invalidVersionIcon;
        info.message = version->invalidReason();
        return info;
    }

    // Do we have tool chain issues?
    QStringList missingToolChains;
    int abiCount = 0;
hjk's avatar
hjk committed
482 483 484
    foreach (const Abi &abi, version->qtAbis()) {
        if (ToolChainManager::findToolChains(abi).isEmpty())
            missingToolChains.append(abi.toString());
485 486 487
        ++abiCount;
    }

488
    bool useable = true;
489
    QStringList warnings;
490 491 492
    if (!isNameUnique(version))
        warnings << tr("Display Name is not unique.");

493 494 495
    if (!missingToolChains.isEmpty()) {
        if (missingToolChains.count() == abiCount) {
            // Yes, this Qt version can't be used at all!
496
            info.message = tr("No compiler can produce code for this Qt version. Please define one or more compilers.");
497 498 499 500
            info.icon = m_invalidVersionIcon;
            useable = false;
        } else {
            // Yes, some ABIs are unsupported
501
            warnings << tr("Not all possible target environments can be supported due to missing compilers.");
502 503 504 505
            info.toolTip = tr("The following ABIs are currently not supported:<ul><li>%1</li></ul>")
                    .arg(missingToolChains.join(QLatin1String("</li><li>")));
            info.icon = m_warningVersionIcon;
        }
506
    }
507 508

    if (useable) {
509 510
        warnings += version->warningReason();
        if (!warnings.isEmpty()) {
hjk's avatar
hjk committed
511
            info.message = warnings.join(QLatin1Char('\n'));
512 513 514 515
            info.icon = m_warningVersionIcon;
        }
    }

516 517 518
    return info;
}

hjk's avatar
hjk committed
519
QList<ToolChain*> QtOptionsPageWidget::toolChains(const BaseQtVersion *version)
520
{
521
    QList<ToolChain*> toolChains;
522
    if (!version)
523 524
        return toolChains;

525
    QSet<QByteArray> ids;
526 527 528 529 530 531 532 533
    foreach (const Abi &a, version->qtAbis()) {
        foreach (ToolChain *tc, ToolChainManager::findToolChains(a)) {
            if (ids.contains(tc->id()))
                continue;
            ids.insert(tc->id());
            toolChains.append(tc);
        }
    }
534

535
    return toolChains;
536 537
}

538
QByteArray QtOptionsPageWidget::defaultToolChainId(const BaseQtVersion *version)
539
{
hjk's avatar
hjk committed
540
    QList<ToolChain*> possibleToolChains = toolChains(version);
541 542
    if (!possibleToolChains.isEmpty())
        return possibleToolChains.first()->id();
543
    return QByteArray();
544 545
}

546 547 548
bool QtOptionsPageWidget::isNameUnique(const BaseQtVersion *version)
{
    const QString name = version->displayName().trimmed();
549 550 551 552 553 554 555 556 557 558 559 560 561

    auto isUnique = [name, version](Utils::TreeItem *parent) {
        foreach (Utils::TreeItem *child, parent->children()) {
            auto item = static_cast<QtVersionItem *>(child);
            if (item->version() == version)
                continue;
            if (item->version()->displayName().trimmed() == name)
                return false;
        }
        return true;
    };

    return isUnique(m_manualItem) && isUnique(m_autoItem);
562 563
}

564
void QtOptionsPageWidget::updateVersionItem(QtVersionItem *item)
565
{
566 567 568 569 570 571 572 573
    if (!item)
        return;
    if (!item->version())
        return;

    const ValidityInfo info = validInformation(item->version());
    item->update();
    item->setIcon(info.icon);
574 575
}

576
void QtOptionsPageWidget::buildDebuggingHelper(DebuggingHelperBuildTask::Tools tools)
577
{
578 579 580 581 582
    QtVersionItem *item = currentItem();
    if (!item)
        return;

    if (!item->version())
583 584
        return;

585 586 587
    // remove tools that cannot be build
    tools &= DebuggingHelperBuildTask::availableTools(currentVersion());

588
    DebuggingHelperBuildTask::Tools buildFlags = item->buildFlags();
589
    buildFlags |= tools;
590
    item->setBuildFlags(buildFlags);
591

592
    updateDebuggingHelperUi();
593

594
    // Run a debugging helper build task in the background.
595 596
    QByteArray toolChainId = m_debuggingHelperUi->toolChainComboBox->itemData(
                m_debuggingHelperUi->toolChainComboBox->currentIndex()).toByteArray();
hjk's avatar
hjk committed
597
    ToolChain *toolChain = ToolChainManager::findToolChain(toolChainId);
598 599 600
    if (!toolChain)
        return;

601
    DebuggingHelperBuildTask *buildTask = new DebuggingHelperBuildTask(item->version(), toolChain, tools);
602 603
    // Don't open General Messages pane with errors
    buildTask->showOutputOnError(false);
604 605
    connect(buildTask, SIGNAL(finished(int,QString,DebuggingHelperBuildTask::Tools)),
            this, SLOT(debuggingHelperBuildFinished(int,QString,DebuggingHelperBuildTask::Tools)),
606
            Qt::QueuedConnection);
607
    QFuture<void> task = QtConcurrent::run(&DebuggingHelperBuildTask::run, buildTask);
608
    const QString taskName = tr("Building Helpers");
609

610
    Core::ProgressManager::addTask(task, taskName, "QmakeProjectManager::BuildHelpers");
611
}
612 613 614 615 616 617

void QtOptionsPageWidget::buildQmlDump()
{
    buildDebuggingHelper(DebuggingHelperBuildTask::QmlDump);
}

618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634
// Non-modal dialog
class BuildLogDialog : public QDialog {
public:
    explicit BuildLogDialog(QWidget *parent = 0);
    void setText(const QString &text);

private:
    Ui_ShowBuildLog m_ui;
};

BuildLogDialog::BuildLogDialog(QWidget *parent) : QDialog(parent)
{
    m_ui.setupUi(this);
    setAttribute(Qt::WA_DeleteOnClose, true);
}

void BuildLogDialog::setText(const QString &text)
635
{
636 637 638 639
    m_ui.log->setPlainText(text); // Show and scroll to bottom
    m_ui.log->moveCursor(QTextCursor::End);
    m_ui.log->ensureCursorVisible();
}
640

641 642
void QtOptionsPageWidget::slotShowDebuggingBuildLog()
{
643 644
    if (const QtVersionItem *item = currentItem())
        showDebuggingBuildLog(item);
645 646
}

647
void QtOptionsPageWidget::showDebuggingBuildLog(const QtVersionItem *item)
648
{
649 650
    BaseQtVersion *version = item->version();
    if (!version)
651
        return;
652
    BuildLogDialog *dialog = new BuildLogDialog(this->window());
653 654
    dialog->setWindowTitle(tr("Debugging Helper Build Log for \"%1\"").arg(version->displayName()));
    dialog->setText(item->buildLog());
655
    dialog->show();
656 657
}

658 659 660
void QtOptionsPageWidget::updateQtVersions(const QList<int> &additions, const QList<int> &removals,
                                           const QList<int> &changes)
{
661
    QList<QtVersionItem *> toRemove;
662 663 664
    QList<int> toAdd = additions;

    // Generate list of all existing items:
665
    QList<QtVersionItem *> itemList;
666
    for (int i = 0; i < m_autoItem->childCount(); ++i)
667
        itemList.append(static_cast<QtVersionItem *>(m_autoItem->child(i)));
668
    for (int i = 0; i < m_manualItem->childCount(); ++i)
669
        itemList.append(static_cast<QtVersionItem *>(m_manualItem->child(i)));
670 671

    // Find existing items to remove/change:
672 673
    foreach (QtVersionItem *item, itemList) {
        int id = item->uniqueId();
674 675 676 677 678 679 680 681 682 683 684 685 686
        if (removals.contains(id)) {
            toRemove.append(item);
            continue;
        }

        if (changes.contains(id)) {
            toAdd.append(id);
            toRemove.append(item);
            continue;
        }
    }

    // Remove changed/removed items:
687 688
    foreach (QtVersionItem *item, toRemove) {
        m_model->takeItem(item);
689 690 691 692 693
        delete item;
    }

    // Add changed/added items:
    foreach (int a, toAdd) {
hjk's avatar
hjk committed
694
        BaseQtVersion *version = QtVersionManager::version(a)->clone();
695
        auto *item = new QtVersionItem(version);
696

697
        item->setToolChainId(defaultToolChainId(version));
698 699

        // Insert in the right place:
700 701
        Utils::TreeItem *parent = version->isAutodetected()? m_autoItem : m_manualItem;
        parent->appendChild(item);
702
    }
703

704 705 706 707 708 709 710
    auto update = [this](Utils::TreeItem *parent) {
        foreach (Utils::TreeItem *child, parent->children())
            updateVersionItem(static_cast<QtVersionItem *>(child));
    };

    update(m_autoItem);
    update(m_manualItem);
711 712
}

713
QtOptionsPageWidget::~QtOptionsPageWidget()
714 715
{
    delete m_ui;
dt's avatar
dt committed
716 717
    delete m_versionUi;
    delete m_debuggingHelperUi;
dt's avatar
dt committed
718
    delete m_configurationWidget;
719 720
}

dt_'s avatar
dt_ committed
721 722
void QtOptionsPageWidget::addQtDir()
{
hjk's avatar
hjk committed
723
    FileName qtVersion = FileName::fromString(
Tobias Hunger's avatar
Tobias Hunger committed
724
                QFileDialog::getOpenFileName(this,
Friedemann Kleint's avatar
Friedemann Kleint committed
725
                                             tr("Select a qmake Executable"),
Tobias Hunger's avatar
Tobias Hunger committed
726
                                             QString(),
727
                                             BuildableHelperLibrary::filterForQmakeFileDialog(),
Tobias Hunger's avatar
Tobias Hunger committed
728 729
                                             0,
                                             QFileDialog::DontResolveSymlinks));
dt's avatar
dt committed
730 731
    if (qtVersion.isNull())
        return;
732 733 734 735

    QFileInfo fi(qtVersion.toString());
    // should add all qt versions here ?
    if (BuildableHelperLibrary::isQtChooser(fi))
hjk's avatar
hjk committed
736
        qtVersion = FileName::fromString(BuildableHelperLibrary::qtChooserToQmakePath(fi.symLinkTarget()));
737

738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754
    auto checkAlreadyExists = [qtVersion](Utils::TreeItem *parent) {
        for (int i = 0; i < parent->childCount(); ++i) {
            auto item = static_cast<QtVersionItem *>(parent->childAt(i));
            if (item->version()->qmakeCommand() == qtVersion) {
                return std::make_pair(true, item->version()->displayName());
            }
        }
        return std::make_pair(false, QString());
    };

    bool alreadyExists;
    QString otherName;
    std::tie(alreadyExists, otherName) = checkAlreadyExists(m_autoItem);
    if (!alreadyExists)
        std::tie(alreadyExists, otherName) = checkAlreadyExists(m_manualItem);

    if (alreadyExists) {
dt's avatar
dt committed
755
        // Already exist
Friedemann Kleint's avatar
Friedemann Kleint committed
756
        QMessageBox::warning(this, tr("Qt Version Already Known"),
757
                             tr("This Qt version was already registered as \"%1\".")
758
                             .arg(otherName));
759
        return;
dt's avatar
dt committed
760
    }
761

762
    QString error;
763
    BaseQtVersion *version = QtVersionFactory::createQtVersionFromQMakePath(qtVersion, false, QString(), &error);
dt's avatar
dt committed
764
    if (version) {
765 766 767 768 769 770 771
        auto item = new QtVersionItem(version);
        item->setIcon(version->isValid()? m_validVersionIcon : m_invalidVersionIcon);
        m_manualItem->appendChild(item);
        item->setToolChainId(defaultToolChainId(version));

        QModelIndex source = m_model->indexForItem(item);
        m_ui->qtdirList->setCurrentIndex(m_filterModel->mapFromSource(source)); // should update the rest of the ui
dt's avatar
dt committed
772 773
        m_versionUi->nameEdit->setFocus();
        m_versionUi->nameEdit->selectAll();
774
    } else {
775
        QMessageBox::warning(this, tr("Qmake Not Executable"),
Friedemann Kleint's avatar
Friedemann Kleint committed
776
                             tr("The qmake executable %1 could not be added: %2").arg(qtVersion.toUserOutput()).arg(error));
777
        return;
dt's avatar
dt committed
778 779
    }
    updateCleanUpButton();
780 781
}

782
void QtOptionsPageWidget::removeQtDir()
783
{
784 785
    QtVersionItem *item = currentItem();
    if (!item)
786 787
        return;

788
    m_model->takeItem(item);
789 790
    delete item;

dt's avatar
dt committed
791
    updateCleanUpButton();
792 793
}

dt_'s avatar
dt_ committed
794 795
void QtOptionsPageWidget::editPath()
{
796
    BaseQtVersion *current = currentVersion();
797
    QString dir = currentVersion()->qmakeCommand().toFileInfo().absolutePath();
hjk's avatar
hjk committed
798
    FileName qtVersion = FileName::fromString(
799
                QFileDialog::getOpenFileName(this,
Robert Loehning's avatar
Robert Loehning committed
800
                                             tr("Select a qmake Executable"),
801
                                             dir,
802
                                             BuildableHelperLibrary::filterForQmakeFileDialog(),
803 804
                                             0,
                                             QFileDialog::DontResolveSymlinks));
dt_'s avatar
dt_ committed
805 806 807
    if (qtVersion.isNull())
        return;
    BaseQtVersion *version = QtVersionFactory::createQtVersionFromQMakePath(qtVersion);
Daniel Teske's avatar
Daniel Teske committed
808 809
    if (!version)
        return;
dt_'s avatar
dt_ committed
810 811 812
    // Same type? then replace!
    if (current->type() != version->type()) {
        // not the same type, error out
Friedemann Kleint's avatar
Friedemann Kleint committed
813
        QMessageBox::critical(this, tr("Incompatible Qt Versions"),
Friedemann Kleint's avatar
Friedemann Kleint committed
814
                              tr("The Qt version selected must match the device type."),
dt_'s avatar
dt_ committed
815 816 817 818 819 820
                              QMessageBox::Ok);
        delete version;
        return;
    }
    // same type, replace
    version->setId(current->uniqueId());
821 822
    if (current->unexpandedDisplayName() != current->defaultUnexpandedDisplayName(current->qmakeCommand()))
        version->setUnexpandedDisplayName(current->displayName());
dt_'s avatar
dt_ committed
823 824

    // Update ui
825 826 827 828
    QtVersionItem *item = currentItem();
    item->setVersion(version);
    item->setToolChainId(defaultToolChainId(version));
    item->setIcon(version->isValid()? m_validVersionIcon : m_invalidVersionIcon);
829 830 831
    userChangedCurrentVersion();

    delete current;
dt_'s avatar
dt_ committed
832 833
}

834
void QtOptionsPageWidget::updateDebuggingHelperUi()
835
{
dt's avatar
dt committed
836
    BaseQtVersion *version = currentVersion();
837
    const QtVersionItem *item = currentItem();
838

hjk's avatar
hjk committed
839
    QList<ToolChain*> toolchains = toolChains(currentVersion());
840 841

    if (!version || !version->isValid() || toolchains.isEmpty()) {
842
        m_ui->debuggingHelperWidget->setVisible(false);
843
    } else {
844 845
        const DebuggingHelperBuildTask::Tools availableTools = DebuggingHelperBuildTask::availableTools(version);
        const bool canBuildQmlDumper = availableTools & DebuggingHelperBuildTask::QmlDump;
846

847
        const bool hasQmlDumper = version->hasQmlDump();
848
        const bool needsQmlDumper = version->needsQmlDump();
849

850 851
        bool isBuildingQmlDumper = false;

852 853
        if (item) {
            DebuggingHelperBuildTask::Tools buildingTools = item->buildFlags();
854 855 856
            isBuildingQmlDumper = buildingTools & DebuggingHelperBuildTask::QmlDump;
        }

857 858
        // get names of tools from labels
        QStringList helperNames;
859
        const QChar colon = QLatin1Char(':');
860
        if (hasQmlDumper)
861
            helperNames << m_debuggingHelperUi->qmlDumpLabel->text().remove(colon);
862 863 864

        QString status;
        if (helperNames.isEmpty()) {
865
            status = tr("Helpers: None available");
866
        } else {
Friedemann Kleint's avatar
Friedemann Kleint committed
867
            //: %1 is list of tool names.
868
            status = tr("Helpers: %1.").arg(helperNames.join(QLatin1String(", ")));
869 870 871 872
        }

        m_ui->debuggingHelperWidget->setSummaryText(status);

873
        QString qmlDumpStatusText, qmlDumpStatusToolTip;
874
        Qt::TextInteractionFlags qmlDumpStatusTextFlags = Qt::NoTextInteraction;
875
        if (hasQmlDumper) {
876 877
            qmlDumpStatusText = QDir::toNativeSeparators(version->qmlDumpTool(false));
            const QString debugQmlDumpPath = QDir::toNativeSeparators(version->qmlDumpTool(true));
878
            if (qmlDumpStatusText != debugQmlDumpPath) {
879 880
                if (!qmlDumpStatusText.isEmpty()
                        && !debugQmlDumpPath.isEmpty())
881 882 883
                    qmlDumpStatusText += QLatin1String("\n");
                qmlDumpStatusText += debugQmlDumpPath;
            }
884
            qmlDumpStatusTextFlags = Qt::TextSelectableByMouse;
885
        } else {
886 887 888
            if (!needsQmlDumper) {
                qmlDumpStatusText = tr("<i>Not needed.</i>");
            } else if (canBuildQmlDumper) {
889 890 891
                qmlDumpStatusText = tr("<i>Not yet built.</i>");
            } else {
                qmlDumpStatusText = tr("<i>Cannot be compiled.</i>");
892
                QmlDumpTool::canBuild(version, &qmlDumpStatusToolTip);
893
            }
894
        }
895
        m_debuggingHelperUi->qmlDumpStatus->setText(qmlDumpStatusText);
896
        m_debuggingHelperUi->qmlDumpStatus->setTextInteractionFlags(qmlDumpStatusTextFlags);
897
        m_debuggingHelperUi->qmlDumpStatus->setToolTip(qmlDumpStatusToolTip);
898
        m_debuggingHelperUi->qmlDumpBuildButton->setEnabled(canBuildQmlDumper & !isBuildingQmlDumper);
899

hjk's avatar
hjk committed