qtoptionspage.cpp 41.5 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
enum ModelRoles { VersionIdRole = Qt::UserRole, ToolChainIdRole, BuildLogRole, BuildRunningRole};
63

64 65
using namespace QtSupport;
using namespace QtSupport::Internal;
66

67 68 69 70 71
///
// QtOptionsPage
///

QtOptionsPage::QtOptionsPage()
dt's avatar
dt committed
72
    : m_widget(0)
73
{
74 75 76 77 78 79
    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));
80 81
}

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

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

96
    m_widget->apply();
97 98
}

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

104
//-----------------------------------------------------
105 106


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

123 124 125
    QWidget *debuggingHelperDetailsWidget = new QWidget();
    m_debuggingHelperUi->setupUi(debuggingHelperDetailsWidget);

126
    m_ui->setupUi(this);
127 128 129 130 131

    m_ui->versionInfoWidget->setWidget(versionInfoWidget);
    m_ui->versionInfoWidget->setState(Utils::DetailsWidget::NoSummary);

    m_ui->debuggingHelperWidget->setWidget(debuggingHelperDetailsWidget);
132 133
    connect(m_ui->debuggingHelperWidget, SIGNAL(expanded(bool)),
            this, SLOT(handleDebuggingHelperExpanded(bool)));
134

135 136
    // setup parent items for auto-detected and manual versions
    m_ui->qtdirList->header()->setResizeMode(QHeaderView::ResizeToContents);
dt_'s avatar
dt_ committed
137 138
    m_ui->qtdirList->header()->setStretchLastSection(false);
    m_ui->qtdirList->setTextElideMode(Qt::ElideNone);
139
    m_autoItem = new QTreeWidgetItem(m_ui->qtdirList);
140
    m_ui->qtdirList->installEventFilter(this);
141 142 143 144 145 146 147 148 149 150 151 152 153 154
    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>());
155 156

    m_ui->qtdirList->expandAll();
157

Robert Loehning's avatar
Robert Loehning committed
158
    connect(m_versionUi->nameEdit, SIGNAL(textEdited(QString)),
159 160
            this, SLOT(updateCurrentQtName()));

dt_'s avatar
dt_ committed
161 162 163
    connect(m_versionUi->editPathPushButton, SIGNAL(clicked()),
            this, SLOT(editPath()));

164 165 166 167 168
    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
169 170
    connect(m_ui->qtdirList, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)),
            this, SLOT(versionChanged(QTreeWidgetItem*,QTreeWidgetItem*)));
171

172
    connect(m_debuggingHelperUi->rebuildButton, SIGNAL(clicked()),
173
            this, SLOT(buildDebuggingHelper()));
174 175 176 177
    connect(m_debuggingHelperUi->gdbHelperBuildButton, SIGNAL(clicked()),
            this, SLOT(buildGdbHelper()));
    connect(m_debuggingHelperUi->qmlDumpBuildButton, SIGNAL(clicked()),
            this, SLOT(buildQmlDump()));
178 179
    connect(m_debuggingHelperUi->qmlDebuggingLibBuildButton, SIGNAL(clicked()),
            this, SLOT(buildQmlDebuggingLibrary()));
180 181 182 183
    connect(m_debuggingHelperUi->qmlObserverBuildButton, SIGNAL(clicked()),
            this, SLOT(buildQmlObserver()));

    connect(m_debuggingHelperUi->showLogButton, SIGNAL(clicked()),
184
            this, SLOT(slotShowDebuggingBuildLog()));
185 186
    connect(m_debuggingHelperUi->toolChainComboBox, SIGNAL(activated(int)),
            this, SLOT(selectedToolChainChanged(int)));
187

188
    connect(m_ui->cleanUpButton, SIGNAL(clicked()), this, SLOT(cleanUpQtVersions()));
dt's avatar
dt committed
189 190
    userChangedCurrentVersion();
    updateCleanUpButton();
191

192 193
    connect(QtVersionManager::instance(), SIGNAL(dumpUpdatedFor(Utils::FileName)),
            this, SLOT(qtVersionsDumpUpdated(Utils::FileName)));
194

195 196 197
    connect(QtVersionManager::instance(), SIGNAL(qtVersionsChanged(QList<int>,QList<int>,QList<int>)),
            this, SLOT(updateQtVersions(QList<int>,QList<int>,QList<int>)));

198 199
    connect(ProjectExplorer::ToolChainManager::instance(), SIGNAL(toolChainsChanged()),
            this, SLOT(toolChainsUpdated()));
200 201
}

202 203 204 205 206
bool QtOptionsPageWidget::eventFilter(QObject *o, QEvent *e)
{
    // Set the items tooltip, which may cause costly initialization
    // of QtVersion and must be up-to-date
    if (o != m_ui->qtdirList || e->type() != QEvent::ToolTip)
207
        return false;
208 209 210 211 212 213 214 215
    QHelpEvent *helpEvent = static_cast<QHelpEvent *>(e);
    const QPoint treePos = helpEvent->pos() - QPoint(0, m_ui->qtdirList->header()->height());
    QTreeWidgetItem *item = m_ui->qtdirList->itemAt(treePos);
    if (!item)
        return false;
    const int index = indexForTreeItem(item);
    if (index == -1)
        return false;
216
    const QString tooltip = m_versions.at(index)->toHtml(true);
217 218 219 220 221
    QToolTip::showText(helpEvent->globalPos(), tooltip, m_ui->qtdirList);
    helpEvent->accept();
    return true;
}

222
int QtOptionsPageWidget::currentIndex() const
223
{
224 225 226 227
    if (QTreeWidgetItem *currentItem = m_ui->qtdirList->currentItem())
        return indexForTreeItem(currentItem);
    return -1;
}
228

dt's avatar
dt committed
229
BaseQtVersion *QtOptionsPageWidget::currentVersion() const
230 231 232
{
    const int currentItemIndex = currentIndex();
    if (currentItemIndex >= 0 && currentItemIndex < m_versions.size())
233
        return m_versions.at(currentItemIndex);
234 235
    return 0;
}
236

dt's avatar
dt committed
237
static inline int findVersionById(const QList<BaseQtVersion *> &l, int id)
238 239 240
{
    const int size = l.size();
    for (int i = 0; i < size; i++)
241
        if (l.at(i)->uniqueId() == id)
242 243 244
            return i;
    return -1;
}
245

246
// Update with results of terminated helper build
247
void QtOptionsPageWidget::debuggingHelperBuildFinished(int qtVersionId, const QString &output, DebuggingHelperBuildTask::Tools tools)
248
{
249
    const int index = findVersionById(m_versions, qtVersionId);
250 251
    if (index == -1)
        return; // Oops, somebody managed to delete the version
252

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

255 256
    // Update item view
    QTreeWidgetItem *item = treeItemForIndex(index);
257 258 259 260 261
    QTC_ASSERT(item, return);
    DebuggingHelperBuildTask::Tools buildFlags
            = item->data(0, BuildRunningRole).value<DebuggingHelperBuildTask::Tools>();
    buildFlags &= ~tools;
    item->setData(0, BuildRunningRole,  QVariant::fromValue(buildFlags));
262
    item->setData(0, BuildLogRole, output);
263

264 265
    bool success = true;
    if (tools & DebuggingHelperBuildTask::GdbDebugging)
dt's avatar
dt committed
266
        success &= version->hasGdbDebuggingHelper();
267
    if (tools & DebuggingHelperBuildTask::QmlDebugging)
dt's avatar
dt committed
268
        success &= version->hasQmlDebuggingLibrary();
269
    if (tools & DebuggingHelperBuildTask::QmlDump)
dt's avatar
dt committed
270
        success &= version->hasQmlDump();
271
    if (tools & DebuggingHelperBuildTask::QmlObserver)
dt's avatar
dt committed
272
        success &= version->hasQmlObserver();
273

274 275
    if (!success)
        showDebuggingBuildLog(item);
276 277

    updateDebuggingHelperUi();
278 279
}

280 281 282
void QtOptionsPageWidget::cleanUpQtVersions()
{
    QStringList toRemove;
dt's avatar
dt committed
283
    foreach (const BaseQtVersion *v, m_versions) {
284
        if (!v->isValid() && !v->isAutodetected())
285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306
            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
307
    updateCleanUpButton();
308 309
}

310 311 312 313
void QtOptionsPageWidget::toolChainsUpdated()
{
    for (int i = 0; i < m_versions.count(); ++i) {
        QTreeWidgetItem *item = treeItemForIndex(i);
314
        if (item == m_ui->qtdirList->currentItem()) {
315
            updateDescriptionLabel();
316 317
            updateDebuggingHelperUi();
        } else {
318 319 320 321 322 323
            const ValidityInfo info = validInformation(m_versions.at(i));
            item->setIcon(0, info.icon);
        }
    }
}

324 325 326 327 328 329 330 331 332 333 334 335 336 337
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);
}

338
void QtOptionsPageWidget::qtVersionsDumpUpdated(const Utils::FileName &qmakeCommand)
339 340 341 342 343
{
    foreach (BaseQtVersion *version, m_versions) {
        if (version->qmakeCommand() == qmakeCommand)
            version->recheckDumper();
    }
344 345
    if (currentVersion()
            && currentVersion()->qmakeCommand() == qmakeCommand) {
346 347 348 349 350 351
        updateWidgets();
        updateDescriptionLabel();
        updateDebuggingHelperUi();
    }
}

352 353 354 355 356
void QtOptionsPageWidget::handleDebuggingHelperExpanded(bool expanded)
{
    m_ui->versionInfoWidget->setVisible(!expanded);
}

357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382
QtOptionsPageWidget::ValidityInfo QtOptionsPageWidget::validInformation(const BaseQtVersion *version)
{
    ValidityInfo info;
    info.icon = m_validVersionIcon;

    if (!version)
        return info;

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

383
    bool useable = true;
384 385 386 387 388 389 390
    if (missingToolChains.isEmpty()) {
        // No:
        info.message = tr("Qt version %1 for %2").arg(version->qtVersionString(), version->description());
    } else 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;
391
        useable = false;
392 393 394
    } else {
        // Yes, some ABIs are unsupported
        info.message = tr("Not all possible target environments can be supported due to missing tool chains.");
Jarek Kobus's avatar
Jarek Kobus committed
395
        info.toolTip = tr("The following ABIs are currently not supported:<ul><li>%1</li></ul>")
396 397 398
                       .arg(missingToolChains.join(QLatin1String("</li><li>")));
        info.icon = m_warningVersionIcon;
    }
399 400 401 402 403

    if (useable) {
        QString warning = version->warningReason();
        if (!warning.isEmpty()) {
            if (!info.message.isEmpty())
404
                info.message.append(QLatin1Char('\n'));
405 406 407 408 409
            info.message += warning;
            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();
941 942 943 944
}

void QtOptionsPageWidget::updateDescriptionLabel()
{
945
    QTreeWidgetItem *item = m_ui->qtdirList->currentItem();
946 947 948