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
    QStringList warnings;
387 388 389 390 391 392 393 394
    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
395
            warnings << tr("Not all possible target environments can be supported due to missing tool chains.");
396 397 398 399
            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;
        }
400
    }
401 402

    if (useable) {
403 404 405
        warnings += version->warningReason();
        if (!warnings.isEmpty()) {
            info.message = warnings.join(QLatin1String("\n"));
406 407 408 409
            info.icon = m_warningVersionIcon;
        }
    }

410 411 412
    return info;
}

413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431
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();
}

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

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

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

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

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

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

461
    updateDebuggingHelperUi();
462

463
    // Run a debugging helper build task in the background.
464 465 466 467 468 469 470 471
    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);
472 473
    // Don't open General Messages pane with errors
    buildTask->showOutputOnError(false);
474 475
    connect(buildTask, SIGNAL(finished(int,QString,DebuggingHelperBuildTask::Tools)),
            this, SLOT(debuggingHelperBuildFinished(int,QString,DebuggingHelperBuildTask::Tools)),
476
            Qt::QueuedConnection);
477
    QFuture<void> task = QtConcurrent::run(&DebuggingHelperBuildTask::run, buildTask);
478
    const QString taskName = tr("Building helpers");
479

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

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

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

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

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

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

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

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

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

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

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

dt's avatar
dt committed
658 659
        QTreeWidgetItem *item = new QTreeWidgetItem(m_ui->qtdirList->topLevelItem(1));
        item->setText(0, version->displayName());
660
        item->setText(1, version->qmakeCommand().toUserOutput());
dt's avatar
dt committed
661
        item->setData(0, VersionIdRole, version->uniqueId());
662
        item->setData(0, ToolChainIdRole, defaultToolChainId(version));
dt's avatar
dt committed
663 664 665 666 667 668
        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();
669 670
}

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

    delete item;

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

dt_'s avatar
dt_ committed
686 687
void QtOptionsPageWidget::editPath()
{
688
    BaseQtVersion *current = currentVersion();
689 690 691 692
    QString dir = currentVersion()->qmakeCommand().toFileInfo().absolutePath();
    Utils::FileName qtVersion = Utils::FileName::fromString(
                QFileDialog::getOpenFileName(this,
                                             tr("Select a qmake executable"),
693 694 695 696
                                             dir,
                                             filterForQmakeFileDialog(),
                                             0,
                                             QFileDialog::DontResolveSymlinks));
dt_'s avatar
dt_ committed
697 698 699
    if (qtVersion.isNull())
        return;
    BaseQtVersion *version = QtVersionFactory::createQtVersionFromQMakePath(qtVersion);
Daniel Teske's avatar
Daniel Teske committed
700 701
    if (!version)
        return;
dt_'s avatar
dt_ committed
702 703 704 705 706 707 708 709 710 711 712
    // 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
713 714
    if (current->displayName() != current->defaultDisplayName(current->qtVersionString(), current->qmakeCommand()))
        version->setDisplayName(current->displayName());
dt_'s avatar
dt_ committed
715 716 717 718 719 720 721
    m_versions.replace(m_versions.indexOf(current), version);
    delete current;

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

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

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

    if (!version || !version->isValid() || toolchains.isEmpty()) {
736
        m_ui->debuggingHelperWidget->setVisible(false);
737
    } else {
738 739 740 741 742
        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;
743

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

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

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

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

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

        m_ui->debuggingHelperWidget->setSummaryText(status);

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

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

830
        QString qmlDebuggingLibStatusText, qmlDebuggingLibToolTip;
831 832 833 834 835 836 837 838 839 840
        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()) {
841
                    qmlDebuggingLibStatusText += QLatin1Char('\n');
842 843 844 845
                }
                qmlDebuggingLibStatusText += debugPath;
            }
            qmlDebuggingLibStatusTextFlags = Qt::TextSelectableByMouse;
846 847 848 849
        } else {
            if (!needsQmlDebuggingLib) {
                qmlDebuggingLibStatusText = tr("<i>Not needed.</i>");
            } else if (canBuildQmlDebuggingLib) {
850 851 852
                qmlDebuggingLibStatusText = tr("<i>Not yet built.</i>");
            } else {
                qmlDebuggingLibStatusText = tr("<i>Cannot be compiled.</i>");
853
                QmlDebuggingLibrary::canBuild(version, &qmlDebuggingLibToolTip);
854 855 856 857
            }
        }
        m_debuggingHelperUi->qmlDebuggingLibStatus->setText(qmlDebuggingLibStatusText);
        m_debuggingHelperUi->qmlDebuggingLibStatus->setTextInteractionFlags(qmlDebuggingLibStatusTextFlags);
858
        m_debuggingHelperUi->qmlDebuggingLibStatus->setToolTip(qmlDebuggingLibToolTip);
859 860
        m_debuggingHelperUi->qmlDebuggingLibBuildButton->setEnabled(needsQmlDebuggingLib
                                                                    && canBuildQmlDebuggingLib
861 862
                                                                    && !isBuildingQmlDebuggingLib);

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

884
        QList<ProjectExplorer::ToolChain*> toolchains = toolChains(currentVersion());
885
        QString selectedToolChainId = currentItem->data(0, ToolChainIdRole).toString();
886 887 888 889 890 891 892 893 894 895 896 897
        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);
        }

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

901 902 903 904 905 906 907 908 909 910 911
        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);
912

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

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

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

dt's avatar
dt committed
937
void QtOptionsPageWidget::qtVersionChanged()
938
{
939
    updateDescriptionLabel();
dt's avatar
dt committed
940
    updateDebuggingHelperUi();