qtoptionspage.cpp 41.9 KB
Newer Older
Tobias Hunger's avatar
Tobias Hunger committed
1 2 3 4
/**************************************************************************
**
** This file is part of Qt Creator
**
hjk's avatar
hjk committed
5
** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
Tobias Hunger's avatar
Tobias Hunger committed
6
**
7
** Contact: Nokia Corporation (qt-info@nokia.com)
Tobias Hunger's avatar
Tobias Hunger committed
8 9 10 11
**
**
** GNU Lesser General Public License Usage
**
hjk's avatar
hjk committed
12 13 14 15 16 17
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this file.
** Please review the following information to ensure the GNU Lesser General
** Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
Tobias Hunger's avatar
Tobias Hunger committed
18
**
con's avatar
con committed
19
** In addition, as a special exception, Nokia gives you certain additional
hjk's avatar
hjk committed
20
** rights. These rights are described in the Nokia Qt LGPL Exception
con's avatar
con committed
21 22
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
23 24 25 26 27
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
con's avatar
con committed
28
** If you have questions regarding the use of this file, please contact
29
** Nokia at qt-info@nokia.com.
Tobias Hunger's avatar
Tobias Hunger committed
30 31 32
**
**************************************************************************/

33 34 35
#include "qtoptionspage.h"
#include "ui_showbuildlog.h"
#include "ui_qtversionmanager.h"
36 37
#include "ui_qtversioninfo.h"
#include "ui_debugginghelper.h"
38
#include "qtsupportconstants.h"
39
#include "qtversionmanager.h"
dt's avatar
dt committed
40
#include "qtversionfactory.h"
41 42 43
#include "qmldumptool.h"
#include "qmldebugginglibrary.h"
#include "qmlobservertool.h"
44 45 46

#include <coreplugin/icore.h>
#include <coreplugin/progressmanager/progressmanager.h>
47
#include <utils/treewidgetcolumnstretcher.h>
48
#include <utils/qtcassert.h>
dt's avatar
dt committed
49
#include <utils/buildablehelperlibrary.h>
50
#include <utils/pathchooser.h>
51
#include <projectexplorer/toolchainmanager.h>
52
#include <projectexplorer/toolchain.h>
53
#include <projectexplorer/projectexplorerconstants.h>
54
#include <utils/runextensions.h>
55

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

64
enum ModelRoles { VersionIdRole = Qt::UserRole, ToolChainIdRole, BuildLogRole, BuildRunningRole};
65

66 67
using namespace QtSupport;
using namespace QtSupport::Internal;
68

69 70 71 72 73
///
// QtOptionsPage
///

QtOptionsPage::QtOptionsPage()
dt's avatar
dt committed
74
    : m_widget(0)
75
{
76 77 78 79 80 81
    setId(QLatin1String(Constants::QTVERSION_SETTINGS_PAGE_ID));
    setDisplayName(QCoreApplication::translate("Qt4ProjectManager", Constants::QTVERSION_SETTINGS_PAGE_NAME));
    setCategory(QLatin1String(ProjectExplorer::Constants::PROJECTEXPLORER_SETTINGS_CATEGORY));
    setDisplayCategory(QCoreApplication::translate("ProjectExplorer",
        ProjectExplorer::Constants::PROJECTEXPLORER_SETTINGS_TR_CATEGORY));
    setCategoryIcon(QLatin1String(ProjectExplorer::Constants::PROJECTEXPLORER_SETTINGS_CATEGORY_ICON));
82 83
}

84 85
QWidget *QtOptionsPage::createPage(QWidget *parent)
{
86
    m_widget = new QtOptionsPageWidget(parent);
87 88
    if (m_searchKeywords.isEmpty())
        m_searchKeywords = m_widget->searchKeywords();
89 90 91 92 93
    return m_widget;
}

void QtOptionsPage::apply()
{
94 95
    if (!m_widget) // page was never shown
        return;
96 97
    m_widget->finish();

98
    m_widget->apply();
99 100
}

101 102 103 104 105
bool QtOptionsPage::matches(const QString &s) const
{
    return m_searchKeywords.contains(s, Qt::CaseInsensitive);
}

106
//-----------------------------------------------------
107 108


109
QtOptionsPageWidget::QtOptionsPageWidget(QWidget *parent)
110 111
    : QWidget(parent)
    , m_specifyNameString(tr("<specify a name>"))
112
    , m_ui(new Internal::Ui::QtVersionManager())
113 114
    , m_versionUi(new Internal::Ui::QtVersionInfo())
    , m_debuggingHelperUi(new Internal::Ui::DebuggingHelper())
115
    , m_infoBrowser(new QTextBrowser)
116 117
    , m_invalidVersionIcon(QLatin1String(":/projectexplorer/images/compile_error.png"))
    , m_warningVersionIcon(QLatin1String(":/projectexplorer/images/compile_warning.png"))
dt's avatar
dt committed
118
    , m_configurationWidget(0)
119 120
    , m_autoItem(0)
    , m_manualItem(0)
121
{
122 123
    QWidget *versionInfoWidget = new QWidget();
    m_versionUi->setupUi(versionInfoWidget);
124
    m_versionUi->editPathPushButton->setText(tr(Utils::PathChooser::browseButtonLabel));
125

126 127 128
    QWidget *debuggingHelperDetailsWidget = new QWidget();
    m_debuggingHelperUi->setupUi(debuggingHelperDetailsWidget);

129
    m_ui->setupUi(this);
130

131 132 133 134
    m_infoBrowser->setOpenLinks(false);
    m_infoBrowser->setTextInteractionFlags(Qt::TextBrowserInteraction);
    connect(m_infoBrowser, SIGNAL(anchorClicked(QUrl)), this, SLOT(infoAnchorClicked(QUrl)));
    m_ui->infoWidget->setWidget(m_infoBrowser);
135 136
    connect(m_ui->infoWidget, SIGNAL(expanded(bool)),
            this, SLOT(handleInfoWidgetExpanded(bool)));
137

138 139 140 141
    m_ui->versionInfoWidget->setWidget(versionInfoWidget);
    m_ui->versionInfoWidget->setState(Utils::DetailsWidget::NoSummary);

    m_ui->debuggingHelperWidget->setWidget(debuggingHelperDetailsWidget);
142 143
    connect(m_ui->debuggingHelperWidget, SIGNAL(expanded(bool)),
            this, SLOT(handleDebuggingHelperExpanded(bool)));
144

145 146
    // setup parent items for auto-detected and manual versions
    m_ui->qtdirList->header()->setResizeMode(QHeaderView::ResizeToContents);
dt_'s avatar
dt_ committed
147 148
    m_ui->qtdirList->header()->setStretchLastSection(false);
    m_ui->qtdirList->setTextElideMode(Qt::ElideNone);
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
    m_autoItem = new QTreeWidgetItem(m_ui->qtdirList);
    m_autoItem->setText(0, tr("Auto-detected"));
    m_autoItem->setFirstColumnSpanned(true);
    m_autoItem->setFlags(Qt::ItemIsEnabled);
    m_manualItem = new QTreeWidgetItem(m_ui->qtdirList);
    m_manualItem->setText(0, tr("Manual"));
    m_manualItem->setFirstColumnSpanned(true);
    m_manualItem->setFlags(Qt::ItemIsEnabled);

    QList<int> additions;
    QList<BaseQtVersion *> versions = QtVersionManager::instance()->versions();
    foreach (BaseQtVersion *v, versions)
        additions.append(v->uniqueId());

    updateQtVersions(additions, QList<int>(), QList<int>());
164 165

    m_ui->qtdirList->expandAll();
166

Robert Loehning's avatar
Robert Loehning committed
167
    connect(m_versionUi->nameEdit, SIGNAL(textEdited(QString)),
168 169
            this, SLOT(updateCurrentQtName()));

dt_'s avatar
dt_ committed
170 171 172
    connect(m_versionUi->editPathPushButton, SIGNAL(clicked()),
            this, SLOT(editPath()));

173 174 175 176 177
    connect(m_ui->addButton, SIGNAL(clicked()),
            this, SLOT(addQtDir()));
    connect(m_ui->delButton, SIGNAL(clicked()),
            this, SLOT(removeQtDir()));

Robert Loehning's avatar
Robert Loehning committed
178 179
    connect(m_ui->qtdirList, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)),
            this, SLOT(versionChanged(QTreeWidgetItem*,QTreeWidgetItem*)));
180

181
    connect(m_debuggingHelperUi->rebuildButton, SIGNAL(clicked()),
182
            this, SLOT(buildDebuggingHelper()));
183 184 185 186
    connect(m_debuggingHelperUi->gdbHelperBuildButton, SIGNAL(clicked()),
            this, SLOT(buildGdbHelper()));
    connect(m_debuggingHelperUi->qmlDumpBuildButton, SIGNAL(clicked()),
            this, SLOT(buildQmlDump()));
187 188
    connect(m_debuggingHelperUi->qmlDebuggingLibBuildButton, SIGNAL(clicked()),
            this, SLOT(buildQmlDebuggingLibrary()));
189 190 191 192
    connect(m_debuggingHelperUi->qmlObserverBuildButton, SIGNAL(clicked()),
            this, SLOT(buildQmlObserver()));

    connect(m_debuggingHelperUi->showLogButton, SIGNAL(clicked()),
193
            this, SLOT(slotShowDebuggingBuildLog()));
194 195
    connect(m_debuggingHelperUi->toolChainComboBox, SIGNAL(activated(int)),
            this, SLOT(selectedToolChainChanged(int)));
196

197
    connect(m_ui->cleanUpButton, SIGNAL(clicked()), this, SLOT(cleanUpQtVersions()));
dt's avatar
dt committed
198 199
    userChangedCurrentVersion();
    updateCleanUpButton();
200

201 202
    connect(QtVersionManager::instance(), SIGNAL(dumpUpdatedFor(Utils::FileName)),
            this, SLOT(qtVersionsDumpUpdated(Utils::FileName)));
203

204 205 206
    connect(QtVersionManager::instance(), SIGNAL(qtVersionsChanged(QList<int>,QList<int>,QList<int>)),
            this, SLOT(updateQtVersions(QList<int>,QList<int>,QList<int>)));

207 208
    connect(ProjectExplorer::ToolChainManager::instance(), SIGNAL(toolChainsChanged()),
            this, SLOT(toolChainsUpdated()));
209 210
}

211
int QtOptionsPageWidget::currentIndex() const
212
{
213 214 215 216
    if (QTreeWidgetItem *currentItem = m_ui->qtdirList->currentItem())
        return indexForTreeItem(currentItem);
    return -1;
}
217

dt's avatar
dt committed
218
BaseQtVersion *QtOptionsPageWidget::currentVersion() const
219 220 221
{
    const int currentItemIndex = currentIndex();
    if (currentItemIndex >= 0 && currentItemIndex < m_versions.size())
222
        return m_versions.at(currentItemIndex);
223 224
    return 0;
}
225

dt's avatar
dt committed
226
static inline int findVersionById(const QList<BaseQtVersion *> &l, int id)
227 228 229
{
    const int size = l.size();
    for (int i = 0; i < size; i++)
230
        if (l.at(i)->uniqueId() == id)
231 232 233
            return i;
    return -1;
}
234

235
// Update with results of terminated helper build
236
void QtOptionsPageWidget::debuggingHelperBuildFinished(int qtVersionId, const QString &output, DebuggingHelperBuildTask::Tools tools)
237
{
238
    const int index = findVersionById(m_versions, qtVersionId);
239 240
    if (index == -1)
        return; // Oops, somebody managed to delete the version
241

dt's avatar
dt committed
242
    BaseQtVersion *version = m_versions.at(index);
243

244 245
    // Update item view
    QTreeWidgetItem *item = treeItemForIndex(index);
246 247 248 249 250
    QTC_ASSERT(item, return);
    DebuggingHelperBuildTask::Tools buildFlags
            = item->data(0, BuildRunningRole).value<DebuggingHelperBuildTask::Tools>();
    buildFlags &= ~tools;
    item->setData(0, BuildRunningRole,  QVariant::fromValue(buildFlags));
251
    item->setData(0, BuildLogRole, output);
252

253 254
    bool success = true;
    if (tools & DebuggingHelperBuildTask::GdbDebugging)
dt's avatar
dt committed
255
        success &= version->hasGdbDebuggingHelper();
256
    if (tools & DebuggingHelperBuildTask::QmlDebugging)
dt's avatar
dt committed
257
        success &= version->hasQmlDebuggingLibrary();
258
    if (tools & DebuggingHelperBuildTask::QmlDump)
dt's avatar
dt committed
259
        success &= version->hasQmlDump();
260
    if (tools & DebuggingHelperBuildTask::QmlObserver)
dt's avatar
dt committed
261
        success &= version->hasQmlObserver();
262

263 264
    if (!success)
        showDebuggingBuildLog(item);
265 266

    updateDebuggingHelperUi();
267 268
}

269 270 271
void QtOptionsPageWidget::cleanUpQtVersions()
{
    QStringList toRemove;
dt's avatar
dt committed
272
    foreach (const BaseQtVersion *v, m_versions) {
273
        if (!v->isValid() && !v->isAutodetected())
274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295
            toRemove.append(v->displayName());
    }

    if (toRemove.isEmpty())
        return;

    if (QMessageBox::warning(0, tr("Remove invalid Qt Versions"),
                             tr("Do you want to remove all invalid Qt Versions?<br>"
                                "<ul><li>%1</li></ul><br>"
                                "will be removed.").arg(toRemove.join(QLatin1String("</li><li>"))),
                             QMessageBox::Yes, QMessageBox::No) == QMessageBox::No)
        return;

    for (int i = m_versions.count() - 1; i >= 0; --i) {
        if (!m_versions.at(i)->isValid()) {
            QTreeWidgetItem *item = treeItemForIndex(i);
            delete item;

            delete m_versions.at(i);
            m_versions.removeAt(i);
        }
    }
dt's avatar
dt committed
296
    updateCleanUpButton();
297 298
}

299 300 301 302
void QtOptionsPageWidget::toolChainsUpdated()
{
    for (int i = 0; i < m_versions.count(); ++i) {
        QTreeWidgetItem *item = treeItemForIndex(i);
303
        if (item == m_ui->qtdirList->currentItem()) {
304
            updateDescriptionLabel();
305 306
            updateDebuggingHelperUi();
        } else {
307 308 309 310 311 312
            const ValidityInfo info = validInformation(m_versions.at(i));
            item->setIcon(0, info.icon);
        }
    }
}

313 314 315 316 317 318 319 320 321 322 323 324 325 326
void QtOptionsPageWidget::selectedToolChainChanged(int comboIndex)
{
    const int index = currentIndex();
    if (index < 0)
        return;

    QTreeWidgetItem *item = treeItemForIndex(index);
    QTC_ASSERT(item, return);

    QString toolChainId = m_debuggingHelperUi->toolChainComboBox->itemData(comboIndex).toString();

    item->setData(0, ToolChainIdRole, toolChainId);
}

327
void QtOptionsPageWidget::qtVersionsDumpUpdated(const Utils::FileName &qmakeCommand)
328 329 330 331 332
{
    foreach (BaseQtVersion *version, m_versions) {
        if (version->qmakeCommand() == qmakeCommand)
            version->recheckDumper();
    }
333 334
    if (currentVersion()
            && currentVersion()->qmakeCommand() == qmakeCommand) {
335 336 337 338 339 340
        updateWidgets();
        updateDescriptionLabel();
        updateDebuggingHelperUi();
    }
}

341 342 343 344 345 346
void QtOptionsPageWidget::handleInfoWidgetExpanded(bool expanded)
{
    m_ui->versionInfoWidget->setVisible(!expanded);
    m_ui->debuggingHelperWidget->setVisible(!expanded);
}

347 348 349
void QtOptionsPageWidget::handleDebuggingHelperExpanded(bool expanded)
{
    m_ui->versionInfoWidget->setVisible(!expanded);
350
    m_ui->infoWidget->setVisible(!expanded);
351 352
}

353 354 355 356 357
void QtOptionsPageWidget::infoAnchorClicked(const QUrl &url)
{
    QDesktopServices::openUrl(url);
}

358 359 360 361 362 363 364 365
QtOptionsPageWidget::ValidityInfo QtOptionsPageWidget::validInformation(const BaseQtVersion *version)
{
    ValidityInfo info;
    info.icon = m_validVersionIcon;

    if (!version)
        return info;

366
    info.description = tr("Qt version %1 for %2").arg(version->qtVersionString(), version->description());
367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384
    if (!version->isValid()) {
        info.icon = m_invalidVersionIcon;
        info.message = version->invalidReason();
        return info;
    }

    // Do we have tool chain issues?
    QStringList missingToolChains;
    int abiCount = 0;
    foreach (const ProjectExplorer::Abi &a, version->qtAbis()) {
        // Ignore symbian emulator since we do not support it.
        if (a.osFlavor() == ProjectExplorer::Abi::SymbianEmulatorFlavor)
            continue;
        if (ProjectExplorer::ToolChainManager::instance()->findToolChains(a).isEmpty())
            missingToolChains.append(a.toString());
        ++abiCount;
    }

385
    bool useable = true;
386 387 388 389 390 391 392 393 394 395 396 397 398
    if (!missingToolChains.isEmpty()) {
        if (missingToolChains.count() == abiCount) {
            // Yes, this Qt version can't be used at all!
            info.message = tr("No tool chain can produce code for this Qt version. Please define one or more tool chains.");
            info.icon = m_invalidVersionIcon;
            useable = false;
        } else {
            // Yes, some ABIs are unsupported
            info.message = tr("Not all possible target environments can be supported due to missing tool chains.");
            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;
        }
399
    }
400 401 402 403 404

    if (useable) {
        QString warning = version->warningReason();
        if (!warning.isEmpty()) {
            if (!info.message.isEmpty())
405
                info.message.append(QLatin1Char('\n'));
406 407 408 409 410
            info.message += warning;
            info.icon = m_warningVersionIcon;
        }
    }

411 412 413
    return info;
}

414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432
QList<ProjectExplorer::ToolChain*> QtOptionsPageWidget::toolChains(const BaseQtVersion *version)
{
    QHash<QString,ProjectExplorer::ToolChain*> toolChains;
    if (!version)
        return toolChains.values();

    foreach (const ProjectExplorer::Abi &a, version->qtAbis()) {
        // Ignore symbian emulator since we do not support it.
        if (a.osFlavor() == ProjectExplorer::Abi::SymbianEmulatorFlavor)
            continue;
        foreach (ProjectExplorer::ToolChain *tc,
                 ProjectExplorer::ToolChainManager::instance()->findToolChains(a)) {
            toolChains.insert(tc->id(), tc);
        }
    }

    return toolChains.values();
}

433 434 435 436 437 438 439 440
QString QtOptionsPageWidget::defaultToolChainId(const BaseQtVersion *version)
{
    QList<ProjectExplorer::ToolChain*> possibleToolChains = toolChains(version);
    if (!possibleToolChains.isEmpty())
        return possibleToolChains.first()->id();
    return QString();
}

441
void QtOptionsPageWidget::buildDebuggingHelper(DebuggingHelperBuildTask::Tools tools)
442 443 444 445 446
{
    const int index = currentIndex();
    if (index < 0)
        return;

447 448 449
    // remove tools that cannot be build
    tools &= DebuggingHelperBuildTask::availableTools(currentVersion());

450 451
    QTreeWidgetItem *item = treeItemForIndex(index);
    QTC_ASSERT(item, return);
452

453 454 455 456
    DebuggingHelperBuildTask::Tools buildFlags
            = item->data(0, BuildRunningRole).value<DebuggingHelperBuildTask::Tools>();
    buildFlags |= tools;
    item->setData(0, BuildRunningRole, QVariant::fromValue(buildFlags));
457

dt's avatar
dt committed
458
    BaseQtVersion *version = m_versions.at(index);
459 460 461
    if (!version)
        return;

462
    updateDebuggingHelperUi();
463

464
    // Run a debugging helper build task in the background.
465 466 467 468 469 470 471 472
    QString toolChainId = m_debuggingHelperUi->toolChainComboBox->itemData(
                m_debuggingHelperUi->toolChainComboBox->currentIndex()).toString();
    ProjectExplorer::ToolChainManager *tcMgr = ProjectExplorer::ToolChainManager::instance();
    ProjectExplorer::ToolChain *toolChain = tcMgr->findToolChain(toolChainId);
    if (!toolChain)
        return;

    DebuggingHelperBuildTask *buildTask = new DebuggingHelperBuildTask(version, toolChain, tools);
473 474
    // Don't open General Messages pane with errors
    buildTask->showOutputOnError(false);
475 476
    connect(buildTask, SIGNAL(finished(int,QString,DebuggingHelperBuildTask::Tools)),
            this, SLOT(debuggingHelperBuildFinished(int,QString,DebuggingHelperBuildTask::Tools)),
477
            Qt::QueuedConnection);
478
    QFuture<void> task = QtConcurrent::run(&DebuggingHelperBuildTask::run, buildTask);
479
    const QString taskName = tr("Building helpers");
480

hjk's avatar
hjk committed
481
    Core::ICore::progressManager()->addTask(task, taskName,
482
                                                        QLatin1String("Qt4ProjectManager::BuildHelpers"));
483
}
484 485 486 487 488 489 490 491 492 493
void QtOptionsPageWidget::buildGdbHelper()
{
    buildDebuggingHelper(DebuggingHelperBuildTask::GdbDebugging);
}

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

494 495 496 497 498
void QtOptionsPageWidget::buildQmlDebuggingLibrary()
{
    buildDebuggingHelper(DebuggingHelperBuildTask::QmlDebugging);
}

499 500
void QtOptionsPageWidget::buildQmlObserver()
{
501
    DebuggingHelperBuildTask::Tools qmlDbgTools =
Kai Koehne's avatar
Kai Koehne committed
502 503
            DebuggingHelperBuildTask::QmlObserver;
    qmlDbgTools |= DebuggingHelperBuildTask::QmlDebugging;
504
    buildDebuggingHelper(qmlDbgTools);
505
}
506

507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523
// 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)
524
{
525 526 527 528
    m_ui.log->setPlainText(text); // Show and scroll to bottom
    m_ui.log->moveCursor(QTextCursor::End);
    m_ui.log->ensureCursorVisible();
}
529

530 531 532 533 534 535 536 537 538
void QtOptionsPageWidget::slotShowDebuggingBuildLog()
{
    if (const QTreeWidgetItem *currentItem = m_ui->qtdirList->currentItem())
        showDebuggingBuildLog(currentItem);
}

void QtOptionsPageWidget::showDebuggingBuildLog(const QTreeWidgetItem *currentItem)
{
    const int currentItemIndex = indexForTreeItem(currentItem);
539 540
    if (currentItemIndex < 0)
        return;
541
    BuildLogDialog *dialog = new BuildLogDialog(this->window());
542
    dialog->setWindowTitle(tr("Debugging Helper Build Log for '%1'").arg(currentItem->text(0)));
543
    dialog->setText(currentItem->data(0, BuildLogRole).toString());
544
    dialog->show();
545 546
}

547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613
void QtOptionsPageWidget::updateQtVersions(const QList<int> &additions, const QList<int> &removals,
                                           const QList<int> &changes)
{
    QtVersionManager *vm = QtVersionManager::instance();

    QList<QTreeWidgetItem *> toRemove;
    QList<int> toAdd = additions;

    // Generate list of all existing items:
    QList<QTreeWidgetItem *> itemList;
    for (int i = 0; i < m_autoItem->childCount(); ++i)
        itemList.append(m_autoItem->child(i));
    for (int i = 0; i < m_manualItem->childCount(); ++i)
        itemList.append(m_manualItem->child(i));

    // Find existing items to remove/change:
    foreach (QTreeWidgetItem *item, itemList) {
        int id = item->data(0, VersionIdRole).toInt();
        if (removals.contains(id)) {
            toRemove.append(item);
            continue;
        }

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

    // Remove changed/removed items:
    foreach (QTreeWidgetItem *item, toRemove) {
        int index = indexForTreeItem(item);
        delete m_versions.at(index);
        m_versions.removeAt(index);
        delete item;
    }

    // Add changed/added items:
    foreach (int a, toAdd) {
        BaseQtVersion *version = vm->version(a)->clone();
        m_versions.append(version);
        QTreeWidgetItem *item = new QTreeWidgetItem;

        item->setText(0, version->displayName());
        item->setText(1, version->qmakeCommand().toUserOutput());
        item->setData(0, VersionIdRole, version->uniqueId());
        item->setData(0, ToolChainIdRole, defaultToolChainId(version));
        const ValidityInfo info = validInformation(version);
        item->setIcon(0, info.icon);

        // Insert in the right place:
        QTreeWidgetItem *parent = version->isAutodetected()? m_autoItem : m_manualItem;
        for (int i = 0; i < parent->childCount(); ++i) {
            BaseQtVersion *currentVersion = m_versions.at(indexForTreeItem(parent->child(i)));
            if (currentVersion->qtVersion() > version->qtVersion())
                continue;
            parent->insertChild(i, item);
            parent = 0;
            break;
        }

        if (parent)
            parent->addChild(item);
    }
}

614
QtOptionsPageWidget::~QtOptionsPageWidget()
615 616
{
    delete m_ui;
dt's avatar
dt committed
617 618
    delete m_versionUi;
    delete m_debuggingHelperUi;
dt's avatar
dt committed
619
    delete m_configurationWidget;
620
    qDeleteAll(m_versions);
621 622
}

dt_'s avatar
dt_ committed
623
static QString filterForQmakeFileDialog()
624
{
625
    QString filter = QLatin1String("qmake (");
626 627 628 629
    const QStringList commands = Utils::BuildableHelperLibrary::possibleQMakeCommands();
    for (int i = 0; i < commands.size(); ++i) {
        if (i)
            filter += QLatin1Char(' ');
630
#ifdef Q_OS_MAC
631 632 633
        // work around QTBUG-7739 that prohibits filters that don't start with *
        filter += QLatin1Char('*');
#endif
634
        filter += commands.at(i);
dt's avatar
dt committed
635
    }
636
    filter += QLatin1Char(')');
dt_'s avatar
dt_ committed
637 638
    return filter;
}
639

dt_'s avatar
dt_ committed
640 641
void QtOptionsPageWidget::addQtDir()
{
642 643 644 645
    Utils::FileName qtVersion = Utils::FileName::fromString(
                QFileDialog::getOpenFileName(this,
                                             tr("Select a qmake executable"),
                                             QString(),
646 647 648
                                             filterForQmakeFileDialog(),
                                             0,
                                             QFileDialog::DontResolveSymlinks));
dt's avatar
dt committed
649 650 651 652 653
    if (qtVersion.isNull())
        return;
    if (QtVersionManager::instance()->qtVersionForQMakeBinary(qtVersion)) {
        // Already exist
    }
654

dt's avatar
dt committed
655 656 657
    BaseQtVersion *version = QtVersionFactory::createQtVersionFromQMakePath(qtVersion);
    if (version) {
        m_versions.append(version);
658

dt's avatar
dt committed
659 660
        QTreeWidgetItem *item = new QTreeWidgetItem(m_ui->qtdirList->topLevelItem(1));
        item->setText(0, version->displayName());
661
        item->setText(1, version->qmakeCommand().toUserOutput());
dt's avatar
dt committed
662
        item->setData(0, VersionIdRole, version->uniqueId());
663
        item->setData(0, ToolChainIdRole, defaultToolChainId(version));
dt's avatar
dt committed
664 665 666 667 668 669
        item->setIcon(0, version->isValid()? m_validVersionIcon : m_invalidVersionIcon);
        m_ui->qtdirList->setCurrentItem(item); // should update the rest of the ui
        m_versionUi->nameEdit->setFocus();
        m_versionUi->nameEdit->selectAll();
    }
    updateCleanUpButton();
670 671
}

672
void QtOptionsPageWidget::removeQtDir()
673 674
{
    QTreeWidgetItem *item = m_ui->qtdirList->currentItem();
675
    int index = indexForTreeItem(item);
676 677 678 679 680
    if (index < 0)
        return;

    delete item;

dt's avatar
dt committed
681
    BaseQtVersion *version = m_versions.at(index);
682
    m_versions.removeAt(index);
683
    delete version;
dt's avatar
dt committed
684
    updateCleanUpButton();
685 686
}

dt_'s avatar
dt_ committed
687 688
void QtOptionsPageWidget::editPath()
{
689
    BaseQtVersion *current = currentVersion();
690 691 692 693
    QString dir = currentVersion()->qmakeCommand().toFileInfo().absolutePath();
    Utils::FileName qtVersion = Utils::FileName::fromString(
                QFileDialog::getOpenFileName(this,
                                             tr("Select a qmake executable"),
694 695 696 697
                                             dir,
                                             filterForQmakeFileDialog(),
                                             0,
                                             QFileDialog::DontResolveSymlinks));
dt_'s avatar
dt_ committed
698 699 700
    if (qtVersion.isNull())
        return;
    BaseQtVersion *version = QtVersionFactory::createQtVersionFromQMakePath(qtVersion);
Daniel Teske's avatar
Daniel Teske committed
701 702
    if (!version)
        return;
dt_'s avatar
dt_ committed
703 704 705 706 707 708 709 710 711 712 713
    // Same type? then replace!
    if (current->type() != version->type()) {
        // not the same type, error out
        QMessageBox::critical(this, tr("Qt versions incompatible"),
                              tr("The qt version selected must be for the same target."),
                              QMessageBox::Ok);
        delete version;
        return;
    }
    // same type, replace
    version->setId(current->uniqueId());
dt_'s avatar
dt_ committed
714 715
    if (current->displayName() != current->defaultDisplayName(current->qtVersionString(), current->qmakeCommand()))
        version->setDisplayName(current->displayName());
dt_'s avatar
dt_ committed
716 717 718 719 720 721 722
    m_versions.replace(m_versions.indexOf(current), version);
    delete current;

    // Update ui
    userChangedCurrentVersion();
    QTreeWidgetItem *item = m_ui->qtdirList->currentItem();
    item->setText(0, version->displayName());
723
    item->setText(1, version->qmakeCommand().toUserOutput());
dt_'s avatar
dt_ committed
724
    item->setData(0, VersionIdRole, version->uniqueId());
725
    item->setData(0, ToolChainIdRole, defaultToolChainId(version));
dt_'s avatar
dt_ committed
726 727 728
    item->setIcon(0, version->isValid()? m_validVersionIcon : m_invalidVersionIcon);
}

729
void QtOptionsPageWidget::updateDebuggingHelperUi()
730
{
dt's avatar
dt committed
731
    BaseQtVersion *version = currentVersion();
732
    const QTreeWidgetItem *currentItem = m_ui->qtdirList->currentItem();
733

734 735 736
    QList<ProjectExplorer::ToolChain*> toolchains = toolChains(currentVersion());

    if (!version || !version->isValid() || toolchains.isEmpty()) {
737
        m_ui->debuggingHelperWidget->setVisible(false);
738
    } else {
739 740 741 742 743
        const DebuggingHelperBuildTask::Tools availableTools = DebuggingHelperBuildTask::availableTools(version);
        const bool canBuildGdbHelper = availableTools & DebuggingHelperBuildTask::GdbDebugging;
        const bool canBuildQmlDumper = availableTools & DebuggingHelperBuildTask::QmlDump;
        const bool canBuildQmlDebuggingLib = availableTools & DebuggingHelperBuildTask::QmlDebugging;
        const bool canBuildQmlObserver = availableTools & DebuggingHelperBuildTask::QmlObserver;
744

745 746
        const bool hasGdbHelper = !version->gdbDebuggingHelperLibrary().isEmpty();
        const bool hasQmlDumper = version->hasQmlDump();
747
        const bool needsQmlDumper = version->needsQmlDump();
748
        const bool hasQmlDebuggingLib = version->hasQmlDebuggingLibrary();
749
        const bool needsQmlDebuggingLib = version->needsQmlDebuggingLibrary();
750
        const bool hasQmlObserver = !version->qmlObserverTool().isEmpty();
751

752 753
        bool isBuildingGdbHelper = false;
        bool isBuildingQmlDumper = false;
754
        bool isBuildingQmlDebuggingLib = false;
755 756 757 758 759 760 761
        bool isBuildingQmlObserver = false;

        if (currentItem) {
            DebuggingHelperBuildTask::Tools buildingTools
                    = currentItem->data(0, BuildRunningRole).value<DebuggingHelperBuildTask::Tools>();
            isBuildingGdbHelper = buildingTools & DebuggingHelperBuildTask::GdbDebugging;
            isBuildingQmlDumper = buildingTools & DebuggingHelperBuildTask::QmlDump;
762
            isBuildingQmlDebuggingLib = buildingTools & DebuggingHelperBuildTask::QmlDebugging;
763 764 765
            isBuildingQmlObserver = buildingTools & DebuggingHelperBuildTask::QmlObserver;
        }

766 767
        // get names of tools from labels
        QStringList helperNames;
768
        const QChar colon = QLatin1Char(':');
769
        if (hasGdbHelper)
770
            helperNames << m_debuggingHelperUi->gdbHelperLabel->text().remove(colon);
771
        if (hasQmlDumper)
772
            helperNames << m_debuggingHelperUi->qmlDumpLabel->text().remove(colon);
773
        if (hasQmlDebuggingLib)
774
            helperNames << m_debuggingHelperUi->qmlDebuggingLibLabel->text().remove(colon);
775
        if (hasQmlObserver)
776
            helperNames << m_debuggingHelperUi->qmlObserverLabel->text().remove(colon);
777 778 779

        QString status;
        if (helperNames.isEmpty()) {
780
            status = tr("Helpers: None available");
781
        } else {
Friedemann Kleint's avatar
Friedemann Kleint committed
782
            //: %1 is list of tool names.
783
            status = tr("Helpers: %1.").arg(helperNames.join(QLatin1String(", ")));
784 785 786 787
        }

        m_ui->debuggingHelperWidget->setSummaryText(status);

788 789
        QString gdbHelperText;
        Qt::TextInteractionFlags gdbHelperTextFlags = Qt::NoTextInteraction;
790
        if (hasGdbHelper) {
791
            gdbHelperText = QDir::toNativeSeparators(version->gdbDebuggingHelperLibrary());
792
            gdbHelperTextFlags = Qt::TextSelectableByMouse;
793
        } else {
794 795 796 797 798
            if (canBuildGdbHelper) {
                gdbHelperText =  tr("<i>Not yet built.</i>");
            } else {
                gdbHelperText =  tr("<i>Not needed.</i>");
            }
799
        }
800 801
        m_debuggingHelperUi->gdbHelperStatus->setText(gdbHelperText);
        m_debuggingHelperUi->gdbHelperStatus->setTextInteractionFlags(gdbHelperTextFlags);
802
        m_debuggingHelperUi->gdbHelperBuildButton->setEnabled(canBuildGdbHelper && !isBuildingGdbHelper);
803

804
        QString qmlDumpStatusText, qmlDumpStatusToolTip;
805
        Qt::TextInteractionFlags qmlDumpStatusTextFlags = Qt::NoTextInteraction;
806
        if (hasQmlDumper) {
807 808
            qmlDumpStatusText = QDir::toNativeSeparators(version->qmlDumpTool(false));
            const QString debugQmlDumpPath = QDir::toNativeSeparators(version->qmlDumpTool(true));
809
            if (qmlDumpStatusText != debugQmlDumpPath) {
810 811
                if (!qmlDumpStatusText.isEmpty()
                        && !debugQmlDumpPath.isEmpty())
812 813 814
                    qmlDumpStatusText += QLatin1String("\n");
                qmlDumpStatusText += debugQmlDumpPath;
            }
815
            qmlDumpStatusTextFlags = Qt::TextSelectableByMouse;
816
        } else {
817 818 819
            if (!needsQmlDumper) {
                qmlDumpStatusText = tr("<i>Not needed.</i>");
            } else if (canBuildQmlDumper) {
820 821 822
                qmlDumpStatusText = tr("<i>Not yet built.</i>");
            } else {
                qmlDumpStatusText = tr("<i>Cannot be compiled.</i>");
823
                QmlDumpTool::canBuild(version, &qmlDumpStatusToolTip);
824
            }
825
        }
826
        m_debuggingHelperUi->qmlDumpStatus->setText(qmlDumpStatusText);
827
        m_debuggingHelperUi->qmlDumpStatus->setTextInteractionFlags(qmlDumpStatusTextFlags);
828
        m_debuggingHelperUi->qmlDumpStatus->setToolTip(qmlDumpStatusToolTip);
829
        m_debuggingHelperUi->qmlDumpBuildButton->setEnabled(canBuildQmlDumper & !isBuildingQmlDumper);
830

831
        QString qmlDebuggingLibStatusText, qmlDebuggingLibToolTip;
832 833 834 835 836 837 838 839 840 841
        Qt::TextInteractionFlags qmlDebuggingLibStatusTextFlags = Qt::NoTextInteraction;
        if (hasQmlDebuggingLib) {
            qmlDebuggingLibStatusText = QDir::toNativeSeparators(
                        version->qmlDebuggingHelperLibrary(false));
            const QString debugPath = QDir::toNativeSeparators(
                        version->qmlDebuggingHelperLibrary(true));

            if (qmlDebuggingLibStatusText != debugPath) {
                if (!qmlDebuggingLibStatusText.isEmpty()
                        && !debugPath.isEmpty()) {
842
                    qmlDebuggingLibStatusText += QLatin1Char('\n');
843 844 845 846
                }
                qmlDebuggingLibStatusText += debugPath;
            }
            qmlDebuggingLibStatusTextFlags = Qt::TextSelectableByMouse;
847 848 849 850
        } else {
            if (!needsQmlDebuggingLib) {
                qmlDebuggingLibStatusText = tr("<i>Not needed.</i>");
            } else if (canBuildQmlDebuggingLib) {
851 852 853
                qmlDebuggingLibStatusText = tr("<i>Not yet built.</i>");
            } else {
                qmlDebuggingLibStatusText = tr("<i>Cannot be compiled.</i>");
854
                QmlDebuggingLibrary::canBuild(version, &qmlDebuggingLibToolTip);
855 856 857 858
            }
        }
        m_debuggingHelperUi->qmlDebuggingLibStatus->setText(qmlDebuggingLibStatusText);
        m_debuggingHelperUi->qmlDebuggingLibStatus->setTextInteractionFlags(qmlDebuggingLibStatusTextFlags);
859
        m_debuggingHelperUi->qmlDebuggingLibStatus->setToolTip(qmlDebuggingLibToolTip);
860 861
        m_debuggingHelperUi->qmlDebuggingLibBuildButton->setEnabled(needsQmlDebuggingLib
                                                                    && canBuildQmlDebuggingLib
862 863
                                                                    && !isBuildingQmlDebuggingLib);

864
        QString qmlObserverStatusText, qmlObserverToolTip;
865
        Qt::TextInteractionFlags qmlObserverStatusTextFlags = Qt::NoTextInteraction;
866
        if (hasQmlObserver) {
867
            qmlObserverStatusText = QDir::toNativeSeparators(version->qmlObserverTool());
868
            qmlObserverStatusTextFlags = Qt::TextSelectableByMouse;
869
        }  else {
870 871 872
            if (!needsQmlDebuggingLib) {
                qmlObserverStatusText = tr("<i>Not needed.</i>");
            } else if (canBuildQmlObserver) {
873 874 875
                qmlObserverStatusText = tr("<i>Not yet built.</i>");
            } else {
                qmlObserverStatusText = tr("<i>Cannot be compiled.</i>");
876
                QmlObserverTool::canBuild(version, &qmlObserverToolTip);
877
            }
878
        }
879
        m_debuggingHelperUi->qmlObserverStatus->setText(qmlObserverStatusText);
880
        m_debuggingHelperUi->qmlObserverStatus->setTextInteractionFlags(qmlObserverStatusTextFlags);
881
        m_debuggingHelperUi->qmlObserverStatus->setToolTip(qmlObserverToolTip);
882 883
        m_debuggingHelperUi->qmlObserverBuildButton->setEnabled(canBuildQmlObserver
                                                                & !isBuildingQmlObserver);
884

885
        QList<ProjectExplorer::ToolChain*> toolchains = toolChains(currentVersion());
886
        QString selectedToolChainId = currentItem->data(0, ToolChainIdRole).toString();
887 888 889 890 891 892 893 894 895 896 897 898
        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->toolChainComboBox->insertItem(i, toolchains.at(i)->displayName(),
                                                                   toolchains.at(i)->id());
            }
            if (toolchains.at(i)->id() == selectedToolChainId)
                m_debuggingHelperUi->toolChainComboBox->setCurrentIndex(i);
        }

899 900 901
        const bool hasLog = currentItem && !currentItem->data(0, BuildLogRole).toString().isEmpty();
        m_debuggingHelperUi->showLogButton->setEnabled(hasLog);

902 903 904 905 906 907 908 909 910 911 912
        const bool canBuild = canBuildGdbHelper
                               || canBuildQmlDumper
                               || (canBuildQmlDebuggingLib && needsQmlDebuggingLib)
                               || canBuildQmlObserver;
        const bool isBuilding = isBuildingGdbHelper
                                 || isBuildingQmlDumper
                                 || isBuildingQmlDebuggingLib
                                 || isBuildingQmlObserver;

        m_debuggingHelperUi->rebuildButton->setEnabled(canBuild && !isBuilding);
        m_debuggingHelperUi->toolChainComboBox->setEnabled(canBuild && !isBuilding);
913

914
        m_ui->debuggingHelperWidget->setVisible(true);
915 916 917
    }
}

dt's avatar
dt committed
918 919
// To be called if a qt version was removed or added
void QtOptionsPageWidget::updateCleanUpButton()
920
{
921
    bool hasInvalidVersion = false;
922 923
    for (int i = 0; i < m_versions.count(); ++i) {
        if (!m_versions.at(i)->isValid()) {
924
            hasInvalidVersion = true;
dt's avatar
dt committed
925
            break;
926 927
        }
    }
928
    m_ui->cleanUpButton->setEnabled(hasInvalidVersion);
929
}
930

dt's avatar
dt committed
931
void QtOptionsPageWidget::userChangedCurrentVersion()
con's avatar
con committed
932
{
dt's avatar
dt committed
933 934 935
    updateWidgets();
    updateDescriptionLabel();
    updateDebuggingHelperUi();
936 937
}

dt's avatar
dt committed
938
void QtOptionsPageWidget::qtVersionChanged()
939
{
940
    updateDescriptionLabel();
dt's avatar
dt committed
941
    updateDebuggingHelperUi();
942 943 944 945
}

void QtOptionsPageWidget::updateDescriptionLabel()
{