qtoptionspage.cpp 24.8 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
#include "ui_qtversioninfo.h"
31
#include "qtsupportconstants.h"
32
#include "qtversionmanager.h"
dt's avatar
dt committed
33
#include "qtversionfactory.h"
34 35

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

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

#include <utility>
58

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

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

65 66 67
class QtVersionItem : public TreeItem
{
public:
hjk's avatar
hjk committed
68 69
    explicit QtVersionItem(BaseQtVersion *version)
        : m_version(version)
70 71 72 73
    {}

    ~QtVersionItem()
    {
Daniel Teske's avatar
Daniel Teske committed
74
        delete m_version;
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
    }

    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();
        }

105 106 107 108 109 110
        if (role == Qt::FontRole && m_changed) {
            QFont font;
            font.setBold(true);
            return font;
         }

111 112 113 114 115 116 117 118
        if (role == Qt::DecorationRole && column == 0)
            return m_icon;

        return QVariant();
    }

    void setIcon(const QIcon &icon)
    {
119 120
        if (m_icon.cacheKey() == icon.cacheKey())
            return;
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
        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;
    }

145 146 147 148 149 150 151 152
    void setChanged(bool changed)
    {
        if (changed == m_changed)
            return;
        m_changed = changed;
        update();
    }

153
private:
Daniel Teske's avatar
Daniel Teske committed
154
    BaseQtVersion *m_version = 0;
155 156 157
    QIcon m_icon;
    QString m_buildLog;
    QByteArray m_toolChainId;
158
    bool m_changed = false;
159
};
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("QtSupport", Constants::QTVERSION_SETTINGS_PAGE_NAME));
hjk's avatar
hjk committed
170
    setCategory(ProjectExplorer::Constants::PROJECTEXPLORER_SETTINGS_CATEGORY);
171 172
    setDisplayCategory(QCoreApplication::translate("ProjectExplorer",
        ProjectExplorer::Constants::PROJECTEXPLORER_SETTINGS_TR_CATEGORY));
173
    setCategoryIcon(Utils::Icon(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
    , m_versionUi(new Internal::Ui::QtVersionInfo())
203
    , m_infoBrowser(new QTextBrowser)
Ulf Hermann's avatar
Ulf Hermann committed
204 205
    , m_invalidVersionIcon(Utils::Icons::ERROR.icon())
    , m_warningVersionIcon(Utils::Icons::WARNING.icon())
dt's avatar
dt committed
206
    , m_configurationWidget(0)
207
{
208 209
    QWidget *versionInfoWidget = new QWidget();
    m_versionUi->setupUi(versionInfoWidget);
210
    m_versionUi->editPathPushButton->setText(PathChooser::browseButtonLabel());
211

212
    m_ui->setupUi(this);
213

214 215
    m_infoBrowser->setOpenLinks(false);
    m_infoBrowser->setTextInteractionFlags(Qt::TextBrowserInteraction);
216 217
    connect(m_infoBrowser, &QTextBrowser::anchorClicked,
            this, &QtOptionsPageWidget::infoAnchorClicked);
218
    m_ui->infoWidget->setWidget(m_infoBrowser);
219 220
    connect(m_ui->infoWidget, &DetailsWidget::expanded,
            this, &QtOptionsPageWidget::setInfoWidgetVisibility);
221

222
    m_ui->versionInfoWidget->setWidget(versionInfoWidget);
hjk's avatar
hjk committed
223
    m_ui->versionInfoWidget->setState(DetailsWidget::NoSummary);
224

225 226
    m_autoItem = new StaticTreeItem(tr("Auto-detected"));
    m_manualItem = new StaticTreeItem(tr("Manual"));
227

hjk's avatar
hjk committed
228
    m_model = new TreeModel<Utils::TreeItem, Utils::TreeItem, QtVersionItem>();
229
    m_model->setHeader({tr("Name"), tr("qmake Location")});
hjk's avatar
hjk committed
230 231 232
    m_model->rootItem()->appendChild(m_autoItem);
    m_model->rootItem()->appendChild(m_manualItem);

233 234 235 236 237 238 239 240 241 242
    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
243
    m_ui->qtdirList->header()->setStretchLastSection(false);
244 245
    m_ui->qtdirList->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
    m_ui->qtdirList->header()->setSectionResizeMode(1, QHeaderView::Stretch);
246
    m_ui->qtdirList->setTextElideMode(Qt::ElideMiddle);
247
    m_ui->qtdirList->sortByColumn(0, Qt::AscendingOrder);
248

249
    QList<int> additions = transform(QtVersionManager::versions(), &BaseQtVersion::uniqueId);
250 251

    updateQtVersions(additions, QList<int>(), QList<int>());
252 253

    m_ui->qtdirList->expandAll();
254

255 256
    connect(m_versionUi->nameEdit, &QLineEdit::textEdited,
            this, &QtOptionsPageWidget::updateCurrentQtName);
257

258 259
    connect(m_versionUi->editPathPushButton, &QAbstractButton::clicked,
            this, &QtOptionsPageWidget::editPath);
dt_'s avatar
dt_ committed
260

261 262 263 264
    connect(m_ui->addButton, &QAbstractButton::clicked,
            this, &QtOptionsPageWidget::addQtDir);
    connect(m_ui->delButton, &QAbstractButton::clicked,
            this, &QtOptionsPageWidget::removeQtDir);
265

266
    connect(m_ui->qtdirList->selectionModel(), &QItemSelectionModel::currentChanged,
267
            this, &QtOptionsPageWidget::versionChanged);
268

269 270
    connect(m_ui->cleanUpButton, &QAbstractButton::clicked,
            this, &QtOptionsPageWidget::cleanUpQtVersions);
dt's avatar
dt committed
271 272
    userChangedCurrentVersion();
    updateCleanUpButton();
273

274 275
    connect(QtVersionManager::instance(), &QtVersionManager::dumpUpdatedFor,
            this, &QtOptionsPageWidget::qtVersionsDumpUpdated);
276

277 278
    connect(QtVersionManager::instance(), &QtVersionManager::qtVersionsChanged,
            this, &QtOptionsPageWidget::updateQtVersions);
279

280 281
    connect(ProjectExplorer::ToolChainManager::instance(), &ToolChainManager::toolChainsChanged,
            this, &QtOptionsPageWidget::toolChainsUpdated);
hjk's avatar
hjk committed
282 283

    auto chooser = new Core::VariableChooser(this);
284
    chooser->addSupportedWidget(m_versionUi->nameEdit, "Qt:Name");
hjk's avatar
hjk committed
285 286 287 288 289
    chooser->addMacroExpanderProvider(
        [this]() -> Utils::MacroExpander * {
            BaseQtVersion *version = currentVersion();
            return version ? version->macroExpander() : 0;
        });
290 291
}

dt's avatar
dt committed
292
BaseQtVersion *QtOptionsPageWidget::currentVersion() const
293
{
294 295 296 297
    QtVersionItem *item = currentItem();
    if (!item)
        return 0;
    return item->version();
298
}
299

300
QtVersionItem *QtOptionsPageWidget::currentItem() const
301
{
302 303
    QModelIndex idx = m_ui->qtdirList->selectionModel()->currentIndex();
    QModelIndex sourceIdx = m_filterModel->mapToSource(idx);
hjk's avatar
hjk committed
304
    return m_model->itemForIndexAtLevel<2>(sourceIdx);
305
}
306

307 308
void QtOptionsPageWidget::cleanUpQtVersions()
{
309 310 311 312 313 314 315 316 317 318 319
    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());
        }
320 321 322 323 324
    }

    if (toRemove.isEmpty())
        return;

325

Friedemann Kleint's avatar
Friedemann Kleint committed
326
    if (QMessageBox::warning(0, tr("Remove Invalid Qt Versions"),
327 328
                             tr("Do you want to remove all invalid Qt Versions?<br>"
                                "<ul><li>%1</li></ul><br>"
329
                                "will be removed.").arg(text),
330 331 332
                             QMessageBox::Yes, QMessageBox::No) == QMessageBox::No)
        return;

333 334
    foreach (QtVersionItem *item, toRemove)
        m_model->destroyItem(item);
335

dt's avatar
dt committed
336
    updateCleanUpButton();
337 338
}

339 340
void QtOptionsPageWidget::toolChainsUpdated()
{
hjk's avatar
hjk committed
341
    m_model->forItemsAtLevel<2>([this](QtVersionItem *item) {
342 343 344 345 346
        if (item == currentItem())
            updateDescriptionLabel();
        else
            updateVersionItem(item);
    });
347 348
}

hjk's avatar
hjk committed
349
void QtOptionsPageWidget::qtVersionsDumpUpdated(const FileName &qmakeCommand)
350
{
hjk's avatar
hjk committed
351
    m_model->forItemsAtLevel<2>([this, qmakeCommand](QtVersionItem *item) {
352 353 354
        if (item->version()->qmakeCommand() == qmakeCommand)
            item->version()->recheckDumper();
    });
355

356
    if (currentVersion() && currentVersion()->qmakeCommand() == qmakeCommand) {
357 358 359 360 361
        updateWidgets();
        updateDescriptionLabel();
    }
}

362
void QtOptionsPageWidget::setInfoWidgetVisibility()
363
{
364 365
    m_ui->versionInfoWidget->setVisible(m_ui->infoWidget->state() == DetailsWidget::Collapsed);
    m_ui->infoWidget->setVisible(true);
366 367
}

368 369 370 371 372
void QtOptionsPageWidget::infoAnchorClicked(const QUrl &url)
{
    QDesktopServices::openUrl(url);
}

373 374 375 376 377 378 379 380 381 382 383 384
static QString formatAbiHtmlList(const QList<Abi> &abis)
{
    QString result = QStringLiteral("<ul><li>");
    for (int i = 0, count = abis.size(); i < count; ++i) {
        if (i)
            result += QStringLiteral("</li><li>");
        result += abis.at(i).toString();
    }
    result += QStringLiteral("</li></ul>");
    return result;
}

385 386 387 388 389 390 391 392
QtOptionsPageWidget::ValidityInfo QtOptionsPageWidget::validInformation(const BaseQtVersion *version)
{
    ValidityInfo info;
    info.icon = m_validVersionIcon;

    if (!version)
        return info;

393
    info.description = tr("Qt version %1 for %2").arg(version->qtVersionString(), version->description());
394 395 396 397 398 399 400
    if (!version->isValid()) {
        info.icon = m_invalidVersionIcon;
        info.message = version->invalidReason();
        return info;
    }

    // Do we have tool chain issues?
401 402 403
    QList<Abi> missingToolChains;
    const QList<Abi> qtAbis = version->qtAbis();
    for (const Abi &abi : qtAbis) {
hjk's avatar
hjk committed
404
        if (ToolChainManager::findToolChains(abi).isEmpty())
405
            missingToolChains.append(abi);
406 407
    }

408
    bool useable = true;
409
    QStringList warnings;
410 411 412
    if (!isNameUnique(version))
        warnings << tr("Display Name is not unique.");

413
    if (!missingToolChains.isEmpty()) {
414
        if (missingToolChains.count() == qtAbis.size()) {
415
            // Yes, this Qt version can't be used at all!
416 417 418
            info.message =
                tr("No compiler can produce code for this Qt version."
                   " Please define one or more compilers for: %1").arg(formatAbiHtmlList(qtAbis));
419 420 421 422
            info.icon = m_invalidVersionIcon;
            useable = false;
        } else {
            // Yes, some ABIs are unsupported
423
            warnings << tr("Not all possible target environments can be supported due to missing compilers.");
424 425
            info.toolTip = tr("The following ABIs are currently not supported: %1")
                    .arg(formatAbiHtmlList(missingToolChains));
426 427
            info.icon = m_warningVersionIcon;
        }
428
    }
429 430

    if (useable) {
431 432
        warnings += version->warningReason();
        if (!warnings.isEmpty()) {
hjk's avatar
hjk committed
433
            info.message = warnings.join(QLatin1Char('\n'));
434 435 436 437
            info.icon = m_warningVersionIcon;
        }
    }

438 439 440
    return info;
}

hjk's avatar
hjk committed
441
QList<ToolChain*> QtOptionsPageWidget::toolChains(const BaseQtVersion *version)
442
{
443
    QList<ToolChain*> toolChains;
444
    if (!version)
445 446
        return toolChains;

447
    QSet<QByteArray> ids;
448 449 450 451 452 453 454 455
    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);
        }
    }
456

457
    return toolChains;
458 459
}

460
QByteArray QtOptionsPageWidget::defaultToolChainId(const BaseQtVersion *version)
461
{
hjk's avatar
hjk committed
462
    QList<ToolChain*> possibleToolChains = toolChains(version);
463 464
    if (!possibleToolChains.isEmpty())
        return possibleToolChains.first()->id();
465
    return QByteArray();
466 467
}

468 469 470
bool QtOptionsPageWidget::isNameUnique(const BaseQtVersion *version)
{
    const QString name = version->displayName().trimmed();
471

hjk's avatar
hjk committed
472
    return !m_model->findItemAtLevel<2>([name, version](QtVersionItem *item) {
473 474 475
        BaseQtVersion *v = item->version();
        return v != version && v->displayName().trimmed() == name;
    });
476 477
}

478
void QtOptionsPageWidget::updateVersionItem(QtVersionItem *item)
479
{
480 481 482 483 484 485 486 487
    if (!item)
        return;
    if (!item->version())
        return;

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

490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506
// 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)
507
{
508 509 510 511
    m_ui.log->setPlainText(text); // Show and scroll to bottom
    m_ui.log->moveCursor(QTextCursor::End);
    m_ui.log->ensureCursorVisible();
}
512

513
void QtOptionsPageWidget::showDebuggingBuildLog(const QtVersionItem *item)
514
{
515 516
    BaseQtVersion *version = item->version();
    if (!version)
517
        return;
518
    BuildLogDialog *dialog = new BuildLogDialog(this->window());
519 520
    dialog->setWindowTitle(tr("Debugging Helper Build Log for \"%1\"").arg(version->displayName()));
    dialog->setText(item->buildLog());
521
    dialog->show();
522 523
}

524 525 526
void QtOptionsPageWidget::updateQtVersions(const QList<int> &additions, const QList<int> &removals,
                                           const QList<int> &changes)
{
527
    QList<QtVersionItem *> toRemove;
528 529 530
    QList<int> toAdd = additions;

    // Find existing items to remove/change:
hjk's avatar
hjk committed
531
    m_model->forItemsAtLevel<2>([&](QtVersionItem *item) {
532
        int id = item->uniqueId();
533 534
        if (removals.contains(id)) {
            toRemove.append(item);
535
        } else if (changes.contains(id)) {
536 537 538
            toAdd.append(id);
            toRemove.append(item);
        }
539
    });
540 541

    // Remove changed/removed items:
542 543
    foreach (QtVersionItem *item, toRemove)
        m_model->destroyItem(item);
544 545 546

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

550
        item->setToolChainId(defaultToolChainId(version));
551 552

        // Insert in the right place:
553 554
        Utils::TreeItem *parent = version->isAutodetected()? m_autoItem : m_manualItem;
        parent->appendChild(item);
555
    }
556

hjk's avatar
hjk committed
557
    m_model->forItemsAtLevel<2>([this](QtVersionItem *item) { updateVersionItem(item); });
558 559
}

560
QtOptionsPageWidget::~QtOptionsPageWidget()
561 562
{
    delete m_ui;
dt's avatar
dt committed
563
    delete m_versionUi;
dt's avatar
dt committed
564
    delete m_configurationWidget;
565 566
}

dt_'s avatar
dt_ committed
567 568
void QtOptionsPageWidget::addQtDir()
{
hjk's avatar
hjk committed
569
    FileName qtVersion = FileName::fromString(
Tobias Hunger's avatar
Tobias Hunger committed
570
                QFileDialog::getOpenFileName(this,
Friedemann Kleint's avatar
Friedemann Kleint committed
571
                                             tr("Select a qmake Executable"),
Tobias Hunger's avatar
Tobias Hunger committed
572
                                             QString(),
573
                                             BuildableHelperLibrary::filterForQmakeFileDialog(),
Tobias Hunger's avatar
Tobias Hunger committed
574 575
                                             0,
                                             QFileDialog::DontResolveSymlinks));
dt's avatar
dt committed
576 577
    if (qtVersion.isNull())
        return;
578

579
    QFileInfo fi = qtVersion.toFileInfo();
580 581
    // should add all qt versions here ?
    if (BuildableHelperLibrary::isQtChooser(fi))
hjk's avatar
hjk committed
582
        qtVersion = FileName::fromString(BuildableHelperLibrary::qtChooserToQmakePath(fi.symLinkTarget()));
583

584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600
    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
601
        // Already exist
Friedemann Kleint's avatar
Friedemann Kleint committed
602
        QMessageBox::warning(this, tr("Qt Version Already Known"),
603
                             tr("This Qt version was already registered as \"%1\".")
604
                             .arg(otherName));
605
        return;
dt's avatar
dt committed
606
    }
607

608
    QString error;
609
    BaseQtVersion *version = QtVersionFactory::createQtVersionFromQMakePath(qtVersion, false, QString(), &error);
dt's avatar
dt committed
610
    if (version) {
611 612 613 614 615 616 617
        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
618 619
        m_versionUi->nameEdit->setFocus();
        m_versionUi->nameEdit->selectAll();
620
    } else {
621
        QMessageBox::warning(this, tr("Qmake Not Executable"),
Friedemann Kleint's avatar
Friedemann Kleint committed
622
                             tr("The qmake executable %1 could not be added: %2").arg(qtVersion.toUserOutput()).arg(error));
623
        return;
dt's avatar
dt committed
624 625
    }
    updateCleanUpButton();
626 627
}

628
void QtOptionsPageWidget::removeQtDir()
629
{
630 631
    QtVersionItem *item = currentItem();
    if (!item)
632 633
        return;

634
    m_model->destroyItem(item);
635

dt's avatar
dt committed
636
    updateCleanUpButton();
637 638
}

dt_'s avatar
dt_ committed
639 640
void QtOptionsPageWidget::editPath()
{
641
    BaseQtVersion *current = currentVersion();
642
    QString dir = currentVersion()->qmakeCommand().toFileInfo().absolutePath();
hjk's avatar
hjk committed
643
    FileName qtVersion = FileName::fromString(
644
                QFileDialog::getOpenFileName(this,
Robert Loehning's avatar
Robert Loehning committed
645
                                             tr("Select a qmake Executable"),
646
                                             dir,
647
                                             BuildableHelperLibrary::filterForQmakeFileDialog(),
648 649
                                             0,
                                             QFileDialog::DontResolveSymlinks));
dt_'s avatar
dt_ committed
650 651 652
    if (qtVersion.isNull())
        return;
    BaseQtVersion *version = QtVersionFactory::createQtVersionFromQMakePath(qtVersion);
Daniel Teske's avatar
Daniel Teske committed
653 654
    if (!version)
        return;
dt_'s avatar
dt_ committed
655 656 657
    // Same type? then replace!
    if (current->type() != version->type()) {
        // not the same type, error out
Friedemann Kleint's avatar
Friedemann Kleint committed
658
        QMessageBox::critical(this, tr("Incompatible Qt Versions"),
Friedemann Kleint's avatar
Friedemann Kleint committed
659
                              tr("The Qt version selected must match the device type."),
dt_'s avatar
dt_ committed
660 661 662 663 664 665
                              QMessageBox::Ok);
        delete version;
        return;
    }
    // same type, replace
    version->setId(current->uniqueId());
666 667
    if (current->unexpandedDisplayName() != current->defaultUnexpandedDisplayName(current->qmakeCommand()))
        version->setUnexpandedDisplayName(current->displayName());
dt_'s avatar
dt_ committed
668 669

    // Update ui
670 671 672 673 674
    if (QtVersionItem *item = currentItem()) {
        item->setVersion(version);
        item->setToolChainId(defaultToolChainId(version));
        item->setIcon(version->isValid()? m_validVersionIcon : m_invalidVersionIcon);
    }
675 676 677
    userChangedCurrentVersion();

    delete current;
dt_'s avatar
dt_ committed
678 679
}

Friedemann Kleint's avatar
Friedemann Kleint committed
680
// To be called if a Qt version was removed or added
dt's avatar
dt committed
681
void QtOptionsPageWidget::updateCleanUpButton()
682
{
683
    bool hasInvalidVersion = false;
684 685 686
    foreach (Utils::TreeItem *child, m_manualItem->children()) {
        auto item = static_cast<QtVersionItem *>(child);
        if (item->version() && !item->version()->isValid()) {
687
            hasInvalidVersion = true;
dt's avatar
dt committed
688
            break;
689 690
        }
    }
691

692
    m_ui->cleanUpButton->setEnabled(hasInvalidVersion);
693
}
694

dt's avatar
dt committed
695
void QtOptionsPageWidget::userChangedCurrentVersion()
con's avatar
con committed
696
{
dt's avatar
dt committed
697 698
    updateWidgets();
    updateDescriptionLabel();
699 700
}

701 702
void QtOptionsPageWidget::updateDescriptionLabel()
{
703
    QtVersionItem *item = currentItem();
704
    const BaseQtVersion *version = item ? item->version() : 0;
705 706 707 708 709 710 711 712 713
    const ValidityInfo info = validInformation(version);
    if (info.message.isEmpty()) {
        m_versionUi->errorLabel->setVisible(false);
    } else {
        m_versionUi->errorLabel->setVisible(true);
        m_versionUi->errorLabel->setText(info.message);
        m_versionUi->errorLabel->setToolTip(info.toolTip);
    }
    m_ui->infoWidget->setSummaryText(info.description);
714 715
    if (item)
        item->setIcon(info.icon);
716 717 718

    if (version) {
        m_infoBrowser->setHtml(version->toHtml(true));
719
        setInfoWidgetVisibility();
720
    } else {
721
        m_infoBrowser->clear();
722
        m_ui->versionInfoWidget->setVisible(false);
723 724
        m_ui->infoWidget->setVisible(false);
    }
725 726
}

727
void QtOptionsPageWidget::versionChanged(const QModelIndex &current, const QModelIndex &previous)
728
{
729 730
    Q_UNUSED(current);
    Q_UNUSED(previous);
dt's avatar
dt committed
731 732 733 734 735 736 737 738 739
    userChangedCurrentVersion();
}

void QtOptionsPageWidget::updateWidgets()
{
    delete m_configurationWidget;
    m_configurationWidget = 0;
    BaseQtVersion *version = currentVersion();
    if (version) {
740
        m_versionUi->nameEdit->setText(version->unexpandedDisplayName());
741
        m_versionUi->qmakePath->setText(version->qmakeCommand().toUserOutput());
dt's avatar
dt committed
742 743 744
        m_configurationWidget = version->createConfigurationWidget();
        if (m_configurationWidget) {
            m_versionUi->formLayout->addRow(m_configurationWidget);
dt's avatar
dt committed
745
            m_configurationWidget->setEnabled(!version->isAutodetected());
746
            connect(m_configurationWidget, &QtConfigWidget::changed,
747
                    this, &QtOptionsPageWidget::updateDescriptionLabel);
dt's avatar
dt committed
748
        }
749
    } else {
750
        m_versionUi->nameEdit->clear();
751
        m_versionUi->qmakePath->clear();
752 753
    }

dt's avatar
dt committed
754 755 756
    const bool enabled = version != 0;
    const bool isAutodetected = enabled && version->isAutodetected();
    m_ui->delButton->setEnabled(enabled && !isAutodetected);
757
    m_versionUi->nameEdit->setEnabled(enabled);
dt_'s avatar
dt_ committed
758
    m_versionUi->editPathPushButton->setEnabled(enabled && !isAutodetected);
759 760
}

761
void QtOptionsPageWidget::updateCurrentQtName()
762
{
763 764
    QtVersionItem *item = currentItem();
    if (!item || !item->version())
765
        return;
766

767
    item->setChanged(true);
768 769
    item->version()->setUnexpandedDisplayName(m_versionUi->nameEdit->text());

770
    updateDescriptionLabel();
hjk's avatar
hjk committed
771
    m_model->forItemsAtLevel<2>([this](QtVersionItem *item) { updateVersionItem(item); });
772 773
}

774 775
void QtOptionsPageWidget::apply()
{
776 777
    disconnect(QtVersionManager::instance(), &QtVersionManager::qtVersionsChanged,
            this, &QtOptionsPageWidget::updateQtVersions);
778

779
    QList<BaseQtVersion *> versions;
780

hjk's avatar
hjk committed
781
    m_model->forItemsAtLevel<2>([this, &versions](QtVersionItem *item) {
782 783 784
        item->setChanged(false);
        versions.append(item->version()->clone());
    });
785

786
    QtVersionManager::setNewQtVersions(versions);
787 788


789 790
    connect(QtVersionManager::instance(), &QtVersionManager::qtVersionsChanged,
            this, &QtOptionsPageWidget::updateQtVersions);
791 792
}

hjk's avatar
hjk committed
793 794
} // namespace Internal
} // namespace QtSupport