qtoptionspage.cpp 34.6 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
Tobias Hunger's avatar
Tobias Hunger committed
2
**
3 4
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://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
12 13 14
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
Tobias Hunger's avatar
Tobias Hunger committed
15
**
16 17 18 19 20 21 22
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
con's avatar
con committed
23
**
hjk's avatar
hjk committed
24
****************************************************************************/
Tobias Hunger's avatar
Tobias Hunger committed
25

26
#include "qtoptionspage.h"
27
#include "qtconfigwidget.h"
28 29
#include "ui_showbuildlog.h"
#include "ui_qtversionmanager.h"
30 31
#include "ui_qtversioninfo.h"
#include "ui_debugginghelper.h"
32
#include "qtsupportconstants.h"
33
#include "qtversionmanager.h"
dt's avatar
dt committed
34
#include "qtversionfactory.h"
35
#include "qmldumptool.h"
36 37

#include <coreplugin/progressmanager/progressmanager.h>
38
#include <coreplugin/coreconstants.h>
39
#include <coreplugin/coreicons.h>
hjk's avatar
hjk committed
40
#include <coreplugin/variablechooser.h>
41
#include <projectexplorer/toolchain.h>
42
#include <projectexplorer/toolchainmanager.h>
43
#include <projectexplorer/projectexplorerconstants.h>
44
#include <utils/hostosinfo.h>
hjk's avatar
hjk committed
45 46
#include <utils/pathchooser.h>
#include <utils/qtcassert.h>
47
#include <utils/runextensions.h>
48
#include <utils/algorithm.h>
49
#include <utils/treemodel.h>
50

51 52 53
#include <QDir>
#include <QMessageBox>
#include <QFileDialog>
54 55
#include <QTextBrowser>
#include <QDesktopServices>
56 57 58
#include <QSortFilterProxyModel>

#include <utility>
59

hjk's avatar
hjk committed
60
using namespace ProjectExplorer;
61
using namespace Utils;
62

hjk's avatar
hjk committed
63 64 65
namespace QtSupport {
namespace Internal {

66 67 68 69 70 71 72 73 74 75 76 77 78 79
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
80
        delete m_version;
81 82 83 84 85 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
    }

    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
154
    BaseQtVersion *m_version = 0;
155 156 157 158 159
    QIcon m_icon;
    QString m_buildLog;
    QByteArray m_toolChainId;
    DebuggingHelperBuildTask::Tools m_buildFlags;
};
hjk's avatar
hjk committed
160

161 162 163 164 165
///
// QtOptionsPage
///

QtOptionsPage::QtOptionsPage()
dt's avatar
dt committed
166
    : m_widget(0)
167
{
hjk's avatar
hjk committed
168
    setId(Constants::QTVERSION_SETTINGS_PAGE_ID);
169
    setDisplayName(QCoreApplication::translate("Qt4ProjectManager", Constants::QTVERSION_SETTINGS_PAGE_NAME));
hjk's avatar
hjk committed
170
    setCategory(ProjectExplorer::Constants::PROJECTEXPLORER_SETTINGS_CATEGORY);
171 172 173
    setDisplayCategory(QCoreApplication::translate("ProjectExplorer",
        ProjectExplorer::Constants::PROJECTEXPLORER_SETTINGS_TR_CATEGORY));
    setCategoryIcon(QLatin1String(ProjectExplorer::Constants::PROJECTEXPLORER_SETTINGS_CATEGORY_ICON));
174 175
}

176
QWidget *QtOptionsPage::widget()
177
{
178 179
    if (!m_widget)
        m_widget = new QtOptionsPageWidget;
180 181 182 183 184
    return m_widget;
}

void QtOptionsPage::apply()
{
185 186
    if (!m_widget) // page was never shown
        return;
187
    m_widget->apply();
188 189
}

190
void QtOptionsPage::finish()
191
{
192
    delete m_widget;
193 194
}

195
//-----------------------------------------------------
196 197


198
QtOptionsPageWidget::QtOptionsPageWidget(QWidget *parent)
199 200
    : QWidget(parent)
    , m_specifyNameString(tr("<specify a name>"))
201
    , m_ui(new Internal::Ui::QtVersionManager())
202 203
    , m_versionUi(new Internal::Ui::QtVersionInfo())
    , m_debuggingHelperUi(new Internal::Ui::DebuggingHelper())
204
    , m_infoBrowser(new QTextBrowser)
205 206
    , m_invalidVersionIcon(Core::Icons::ERROR.icon())
    , m_warningVersionIcon(Core::Icons::WARNING.icon())
dt's avatar
dt committed
207
    , m_configurationWidget(0)
208 209
    , m_autoItem(0)
    , m_manualItem(0)
210
{
211 212
    QWidget *versionInfoWidget = new QWidget();
    m_versionUi->setupUi(versionInfoWidget);
213
    m_versionUi->editPathPushButton->setText(PathChooser::browseButtonLabel());
214

215 216 217
    QWidget *debuggingHelperDetailsWidget = new QWidget();
    m_debuggingHelperUi->setupUi(debuggingHelperDetailsWidget);

218
    m_ui->setupUi(this);
219

220 221
    m_infoBrowser->setOpenLinks(false);
    m_infoBrowser->setTextInteractionFlags(Qt::TextBrowserInteraction);
222 223
    connect(m_infoBrowser, &QTextBrowser::anchorClicked,
            this, &QtOptionsPageWidget::infoAnchorClicked);
224
    m_ui->infoWidget->setWidget(m_infoBrowser);
225 226
    connect(m_ui->infoWidget, &DetailsWidget::expanded,
            this, &QtOptionsPageWidget::setInfoWidgetVisibility);
227

228
    m_ui->versionInfoWidget->setWidget(versionInfoWidget);
hjk's avatar
hjk committed
229
    m_ui->versionInfoWidget->setState(DetailsWidget::NoSummary);
230 231

    m_ui->debuggingHelperWidget->setWidget(debuggingHelperDetailsWidget);
232 233
    connect(m_ui->debuggingHelperWidget, &DetailsWidget::expanded,
            this, &QtOptionsPageWidget::setInfoWidgetVisibility);
234

235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253
    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
254
    m_ui->qtdirList->header()->setStretchLastSection(false);
255 256
    m_ui->qtdirList->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
    m_ui->qtdirList->header()->setSectionResizeMode(1, QHeaderView::Stretch);
dt_'s avatar
dt_ committed
257
    m_ui->qtdirList->setTextElideMode(Qt::ElideNone);
258
    m_ui->qtdirList->sortByColumn(0, Qt::AscendingOrder);
259

260
    QList<int> additions = transform(QtVersionManager::versions(), &BaseQtVersion::uniqueId);
261 262

    updateQtVersions(additions, QList<int>(), QList<int>());
263 264

    m_ui->qtdirList->expandAll();
265

266 267
    connect(m_versionUi->nameEdit, &QLineEdit::textEdited,
            this, &QtOptionsPageWidget::updateCurrentQtName);
268

269 270
    connect(m_versionUi->editPathPushButton, &QAbstractButton::clicked,
            this, &QtOptionsPageWidget::editPath);
dt_'s avatar
dt_ committed
271

272 273 274 275
    connect(m_ui->addButton, &QAbstractButton::clicked,
            this, &QtOptionsPageWidget::addQtDir);
    connect(m_ui->delButton, &QAbstractButton::clicked,
            this, &QtOptionsPageWidget::removeQtDir);
276

277
    connect(m_ui->qtdirList->selectionModel(), &QItemSelectionModel::currentChanged,
278
            this, &QtOptionsPageWidget::versionChanged);
279

280 281 282 283
    connect(m_debuggingHelperUi->rebuildButton, &QAbstractButton::clicked,
            this, [this]() { buildDebuggingHelper(); });
    connect(m_debuggingHelperUi->qmlDumpBuildButton, &QAbstractButton::clicked,
            this, &QtOptionsPageWidget::buildQmlDump);
284

285 286 287 288
    connect(m_debuggingHelperUi->showLogButton, &QAbstractButton::clicked,
            this, &QtOptionsPageWidget::slotShowDebuggingBuildLog);
    connect(m_debuggingHelperUi->toolChainComboBox, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated),
            this, &QtOptionsPageWidget::selectedToolChainChanged);
289

290 291
    connect(m_ui->cleanUpButton, &QAbstractButton::clicked,
            this, &QtOptionsPageWidget::cleanUpQtVersions);
dt's avatar
dt committed
292 293
    userChangedCurrentVersion();
    updateCleanUpButton();
294

295 296
    connect(QtVersionManager::instance(), &QtVersionManager::dumpUpdatedFor,
            this, &QtOptionsPageWidget::qtVersionsDumpUpdated);
297

298 299
    connect(QtVersionManager::instance(), &QtVersionManager::qtVersionsChanged,
            this, &QtOptionsPageWidget::updateQtVersions);
300

301 302
    connect(ProjectExplorer::ToolChainManager::instance(), &ToolChainManager::toolChainsChanged,
            this, &QtOptionsPageWidget::toolChainsUpdated);
hjk's avatar
hjk committed
303 304

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

dt's avatar
dt committed
313
BaseQtVersion *QtOptionsPageWidget::currentVersion() const
314
{
315 316 317 318
    QtVersionItem *item = currentItem();
    if (!item)
        return 0;
    return item->version();
319
}
320

321
QtVersionItem *QtOptionsPageWidget::currentItem() const
322
{
323 324 325 326
    QModelIndex idx = m_ui->qtdirList->selectionModel()->currentIndex();
    QModelIndex sourceIdx = m_filterModel->mapToSource(idx);
    QtVersionItem *item = static_cast<QtVersionItem *>(m_model->itemForIndex(sourceIdx));
    return item;
327
}
328

329
// Update with results of terminated helper build
330
void QtOptionsPageWidget::debuggingHelperBuildFinished(int qtVersionId, const QString &output, DebuggingHelperBuildTask::Tools tools)
331
{
332 333 334 335 336 337 338 339
    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;
    };
340

341 342 343
    QtVersionItem *item = findItem(m_manualItem);
    if (!item)
        item = findItem(m_autoItem);
344

345 346 347 348 349
    if (!item)
        return;


    DebuggingHelperBuildTask::Tools buildFlags = item->buildFlags();
350
    buildFlags &= ~tools;
351 352
    item->setBuildFlags(buildFlags);
    item->setBuildLog(output);
353

354 355
    bool success = true;
    if (tools & DebuggingHelperBuildTask::QmlDump)
356
        success &= item->version()->hasQmlDump();
357

358 359
    if (!success)
        showDebuggingBuildLog(item);
360 361

    updateDebuggingHelperUi();
362 363
}

364 365
void QtOptionsPageWidget::cleanUpQtVersions()
{
366 367 368 369 370 371 372 373 374 375 376
    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());
        }
377 378 379 380 381
    }

    if (toRemove.isEmpty())
        return;

382

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

390 391 392
    foreach (QtVersionItem *item, toRemove) {
        m_model->takeItem(item);
        delete item;
393
    }
394

dt's avatar
dt committed
395
    updateCleanUpButton();
396 397
}

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

    update(m_autoItem);
    update(m_manualItem);
413 414
}

415 416
void QtOptionsPageWidget::selectedToolChainChanged(int comboIndex)
{
417 418
    QtVersionItem *item = currentItem();
    if (!item)
419 420
        return;

421
    QByteArray toolChainId = m_debuggingHelperUi->toolChainComboBox->itemData(comboIndex).toByteArray();
422
    item->setToolChainId(toolChainId);
423 424
}

hjk's avatar
hjk committed
425
void QtOptionsPageWidget::qtVersionsDumpUpdated(const FileName &qmakeCommand)
426
{
427 428 429 430 431 432 433 434 435 436 437
    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);

438 439
    if (currentVersion()
            && currentVersion()->qmakeCommand() == qmakeCommand) {
440 441 442 443 444 445
        updateWidgets();
        updateDescriptionLabel();
        updateDebuggingHelperUi();
    }
}

446
void QtOptionsPageWidget::setInfoWidgetVisibility()
447
{
448 449 450 451
    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);
452 453
}

454 455 456 457 458
void QtOptionsPageWidget::infoAnchorClicked(const QUrl &url)
{
    QDesktopServices::openUrl(url);
}

459 460 461 462 463 464 465 466
QtOptionsPageWidget::ValidityInfo QtOptionsPageWidget::validInformation(const BaseQtVersion *version)
{
    ValidityInfo info;
    info.icon = m_validVersionIcon;

    if (!version)
        return info;

467
    info.description = tr("Qt version %1 for %2").arg(version->qtVersionString(), version->description());
468 469 470 471 472 473 474 475 476
    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
477 478 479
    foreach (const Abi &abi, version->qtAbis()) {
        if (ToolChainManager::findToolChains(abi).isEmpty())
            missingToolChains.append(abi.toString());
480 481 482
        ++abiCount;
    }

483
    bool useable = true;
484
    QStringList warnings;
485 486 487
    if (!isNameUnique(version))
        warnings << tr("Display Name is not unique.");

488 489 490
    if (!missingToolChains.isEmpty()) {
        if (missingToolChains.count() == abiCount) {
            // Yes, this Qt version can't be used at all!
491
            info.message = tr("No compiler can produce code for this Qt version. Please define one or more compilers.");
492 493 494 495
            info.icon = m_invalidVersionIcon;
            useable = false;
        } else {
            // Yes, some ABIs are unsupported
496
            warnings << tr("Not all possible target environments can be supported due to missing compilers.");
497 498 499 500
            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;
        }
501
    }
502 503

    if (useable) {
504 505
        warnings += version->warningReason();
        if (!warnings.isEmpty()) {
hjk's avatar
hjk committed
506
            info.message = warnings.join(QLatin1Char('\n'));
507 508 509 510
            info.icon = m_warningVersionIcon;
        }
    }

511 512 513
    return info;
}

hjk's avatar
hjk committed
514
QList<ToolChain*> QtOptionsPageWidget::toolChains(const BaseQtVersion *version)
515
{
516
    QList<ToolChain*> toolChains;
517
    if (!version)
518 519
        return toolChains;

520
    QSet<QByteArray> ids;
521 522 523 524 525 526 527 528
    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);
        }
    }
529

530
    return toolChains;
531 532
}

533
QByteArray QtOptionsPageWidget::defaultToolChainId(const BaseQtVersion *version)
534
{
hjk's avatar
hjk committed
535
    QList<ToolChain*> possibleToolChains = toolChains(version);
536 537
    if (!possibleToolChains.isEmpty())
        return possibleToolChains.first()->id();
538
    return QByteArray();
539 540
}

541 542 543
bool QtOptionsPageWidget::isNameUnique(const BaseQtVersion *version)
{
    const QString name = version->displayName().trimmed();
544 545 546 547 548 549 550 551 552 553 554 555 556

    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);
557 558
}

559
void QtOptionsPageWidget::updateVersionItem(QtVersionItem *item)
560
{
561 562 563 564 565 566 567 568
    if (!item)
        return;
    if (!item->version())
        return;

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

571
void QtOptionsPageWidget::buildDebuggingHelper(DebuggingHelperBuildTask::Tools tools)
572
{
573 574 575 576 577
    QtVersionItem *item = currentItem();
    if (!item)
        return;

    if (!item->version())
578 579
        return;

580 581 582
    // remove tools that cannot be build
    tools &= DebuggingHelperBuildTask::availableTools(currentVersion());

583
    DebuggingHelperBuildTask::Tools buildFlags = item->buildFlags();
584
    buildFlags |= tools;
585
    item->setBuildFlags(buildFlags);
586

587
    updateDebuggingHelperUi();
588

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

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

605
    Core::ProgressManager::addTask(task, taskName, "QmakeProjectManager::BuildHelpers");
606
}
607 608 609 610 611 612

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

613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629
// 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)
630
{
631 632 633 634
    m_ui.log->setPlainText(text); // Show and scroll to bottom
    m_ui.log->moveCursor(QTextCursor::End);
    m_ui.log->ensureCursorVisible();
}
635

636 637
void QtOptionsPageWidget::slotShowDebuggingBuildLog()
{
638 639
    if (const QtVersionItem *item = currentItem())
        showDebuggingBuildLog(item);
640 641
}

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

653 654 655
void QtOptionsPageWidget::updateQtVersions(const QList<int> &additions, const QList<int> &removals,
                                           const QList<int> &changes)
{
656
    QList<QtVersionItem *> toRemove;
657 658 659
    QList<int> toAdd = additions;

    // Generate list of all existing items:
660
    QList<QtVersionItem *> itemList;
661
    for (int i = 0; i < m_autoItem->childCount(); ++i)
662
        itemList.append(static_cast<QtVersionItem *>(m_autoItem->child(i)));
663
    for (int i = 0; i < m_manualItem->childCount(); ++i)
664
        itemList.append(static_cast<QtVersionItem *>(m_manualItem->child(i)));
665 666

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

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

    // Remove changed/removed items:
682 683
    foreach (QtVersionItem *item, toRemove) {
        m_model->takeItem(item);
684 685 686 687 688
        delete item;
    }

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

692
        item->setToolChainId(defaultToolChainId(version));
693 694

        // Insert in the right place:
695 696
        Utils::TreeItem *parent = version->isAutodetected()? m_autoItem : m_manualItem;
        parent->appendChild(item);
697
    }
698

699 700 701 702 703 704 705
    auto update = [this](Utils::TreeItem *parent) {
        foreach (Utils::TreeItem *child, parent->children())
            updateVersionItem(static_cast<QtVersionItem *>(child));
    };

    update(m_autoItem);
    update(m_manualItem);
706 707
}

708
QtOptionsPageWidget::~QtOptionsPageWidget()
709 710
{
    delete m_ui;
dt's avatar
dt committed
711 712
    delete m_versionUi;
    delete m_debuggingHelperUi;
dt's avatar
dt committed
713
    delete m_configurationWidget;
714 715
}

dt_'s avatar
dt_ committed
716 717
void QtOptionsPageWidget::addQtDir()
{
hjk's avatar
hjk committed
718
    FileName qtVersion = FileName::fromString(
Tobias Hunger's avatar
Tobias Hunger committed
719
                QFileDialog::getOpenFileName(this,
Friedemann Kleint's avatar
Friedemann Kleint committed
720
                                             tr("Select a qmake Executable"),
Tobias Hunger's avatar
Tobias Hunger committed
721
                                             QString(),
722
                                             BuildableHelperLibrary::filterForQmakeFileDialog(),
Tobias Hunger's avatar
Tobias Hunger committed
723 724
                                             0,
                                             QFileDialog::DontResolveSymlinks));
dt's avatar
dt committed
725 726
    if (qtVersion.isNull())
        return;
727

728
    QFileInfo fi = qtVersion.toFileInfo();
729 730
    // should add all qt versions here ?
    if (BuildableHelperLibrary::isQtChooser(fi))
hjk's avatar
hjk committed
731
        qtVersion = FileName::fromString(BuildableHelperLibrary::qtChooserToQmakePath(fi.symLinkTarget()));
732

733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749
    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
750
        // Already exist
Friedemann Kleint's avatar
Friedemann Kleint committed
751
        QMessageBox::warning(this, tr("Qt Version Already Known"),
752
                             tr("This Qt version was already registered as \"%1\".")
753
                             .arg(otherName));
754
        return;
dt's avatar
dt committed
755
    }
756

757
    QString error;
758
    BaseQtVersion *version = QtVersionFactory::createQtVersionFromQMakePath(qtVersion, false, QString(), &error);
dt's avatar
dt committed
759
    if (version) {
760 761 762 763 764 765 766
        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
767 768
        m_versionUi->nameEdit->setFocus();
        m_versionUi->nameEdit->selectAll();
769
    } else {
770
        QMessageBox::warning(this, tr("Qmake Not Executable"),
Friedemann Kleint's avatar
Friedemann Kleint committed
771
                             tr("The qmake executable %1 could not be added: %2").arg(qtVersion.toUserOutput()).arg(error));
772
        return;
dt's avatar
dt committed
773 774
    }
    updateCleanUpButton();
775 776
}

777
void QtOptionsPageWidget::removeQtDir()
778
{
779 780
    QtVersionItem *item = currentItem();
    if (!item)
781 782
        return;

783
    m_model->takeItem(item);
784 785
    delete item;

dt's avatar
dt committed
786
    updateCleanUpButton();
787 788
}

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

    // Update ui
820 821 822 823
    QtVersionItem *item = currentItem();
    item->setVersion(version);
    item->setToolChainId(defaultToolChainId(version));
    item->setIcon(version->isValid()? m_validVersionIcon : m_invalidVersionIcon);
824 825 826
    userChangedCurrentVersion();

    delete current;
dt_'s avatar
dt_ committed
827 828
}

829
void QtOptionsPageWidget::updateDebuggingHelperUi()
830
{
dt's avatar
dt committed
831
    BaseQtVersion *version = currentVersion();
832
    const QtVersionItem *item = currentItem();
833

hjk's avatar
hjk committed
834
    QList<ToolChain*> toolchains = toolChains(currentVersion());
835 836

    if (!version || !version->isValid() || toolchains.isEmpty()) {
837
        m_ui->debuggingHelperWidget->setVisible(false);
838
    } else {
839 840
        const DebuggingHelperBuildTask::Tools availableTools = DebuggingHelperBuildTask::availableTools(version);
        const bool canBuildQmlDumper = availableTools & DebuggingHelperBuildTask::QmlDump;
841

842
        const bool hasQmlDumper = version->hasQmlDump();
843
        const bool needsQmlDumper = version->needsQmlDump();
844

845 846
        bool isBuildingQmlDumper = false;

847 848
        if (item) {
            DebuggingHelperBuildTask::Tools buildingTools = item->buildFlags();
849 850 851
            isBuildingQmlDumper = buildingTools & DebuggingHelperBuildTask::QmlDump;
        }

852 853
        // get names of tools from labels
        QStringList helperNames;
854
        const QChar colon = QLatin1Char(':');
855
        if (hasQmlDumper)
856
            helperNames << m_debuggingHelperUi->qmlDumpLabel->text().remove(colon);
857 858 859

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

        m_ui->debuggingHelperWidget->setSummaryText(status);

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

hjk's avatar
hjk committed
895
        QList<ToolChain*> toolchains = toolChains(currentVersion());
896
        QByteArray selectedToolChainId = item->toolChainId();
897 898 899