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/treemodel.h>
54

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

#include <utility>
63

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

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

70 71 72 73 74 75 76 77 78 79 80 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 154 155 156 157 158 159 160 161 162 163
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()
    {

    }

    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:
    BaseQtVersion *m_version = 0; // not owned
    QIcon m_icon;
    QString m_buildLog;
    QByteArray m_toolChainId;
    DebuggingHelperBuildTask::Tools m_buildFlags;
};
hjk's avatar
hjk committed
164

165 166 167 168 169
///
// QtOptionsPage
///

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

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

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

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

199
//-----------------------------------------------------
200 201


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

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

222
    m_ui->setupUi(this);
223

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

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

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

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

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

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

    m_ui->qtdirList->expandAll();
269

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

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

    updateDebuggingHelperUi();
366 367
}

368 369
void QtOptionsPageWidget::cleanUpQtVersions()
{
370 371 372 373 374 375 376 377 378 379 380
    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());
        }
381 382 383 384 385
    }

    if (toRemove.isEmpty())
        return;

386

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

394 395 396 397
    foreach (QtVersionItem *item, toRemove) {
        m_model->takeItem(item);
        delete item->version();
        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 689
    foreach (QtVersionItem *item, toRemove) {
        m_model->takeItem(item);
        delete item->version();
690 691 692 693 694
        delete item;
    }

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

699
        item->setToolChainId(defaultToolChainId(version));
700 701

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

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

    update(m_autoItem);
    update(m_manualItem);
713 714
}

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

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

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

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

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

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

790 791
    m_model->takeItem(item);
    delete item->version();
792 793
    delete item;

dt's avatar
dt committed
794
    updateCleanUpButton();
795 796
}

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

    // Update ui
    userChangedCurrentVersion();
830 831 832 833
    QtVersionItem *item = currentItem();
    item->setVersion(version);
    item->setToolChainId(defaultToolChainId(version));
    item->setIcon(version->isValid()? m_validVersionIcon : m_invalidVersionIcon);
dt_'s avatar
dt_ committed
834 835
}

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

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

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

849
        const bool hasQmlDumper = version->hasQmlDump();
850
        const bool needsQmlDumper = version->needsQmlDump();
851

852 853
        bool isBuildingQmlDumper = false;

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

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

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

        m_ui->debuggingHelperWidget->setSummaryText(status);

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

hjk's avatar
hjk committed
902
        QList<ToolChain*> toolchains = toolChains(currentVersion());
903
        QByteArray selectedToolChainId = item->toolChainId();
904 905 906 907 908 909 910 911 912 913 914 915
        m_debuggingHelperUi->toolChainComboBox->clear();
        for (int i = 0; i < toolchains.size(); ++i) {
            if (!toolchains.at(i)->isValid())
                continue;
            if (i >= m_debuggingHelperUi->toolChainComboBox->count()) {
                m_debuggingHelperUi