qtoptionspage.cpp 38.4 KB
Newer Older
Tobias Hunger's avatar
Tobias Hunger committed
1 2 3 4
/**************************************************************************
**
** This file is part of Qt Creator
**
con's avatar
con committed
5
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
Tobias Hunger's avatar
Tobias Hunger committed
6
**
hjk's avatar
hjk committed
7
** Contact: Nokia Corporation (info@qt.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
Tobias Hunger's avatar
Tobias Hunger committed
29
** Nokia at info@qt.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 <qtconcurrent/runextensions.h>
54 55

#include <QtCore/QDir>
56
#include <QtGui/QToolTip>
57
#include <QtGui/QMessageBox>
dt's avatar
dt committed
58 59
#include <QtGui/QFileDialog>
#include <QtGui/QMainWindow>
60

61
enum ModelRoles { VersionIdRole = Qt::UserRole, ToolChainIdRole, BuildLogRole, BuildRunningRole};
62

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

66 67 68 69 70
///
// QtOptionsPage
///

QtOptionsPage::QtOptionsPage()
dt's avatar
dt committed
71
    : m_widget(0)
72 73 74 75 76
{
}

QString QtOptionsPage::id() const
{
77
    return QLatin1String(Constants::QTVERSION_SETTINGS_PAGE_ID);
78 79
}

80
QString QtOptionsPage::displayName() const
81
{
82
    return QCoreApplication::translate("Qt4ProjectManager", Constants::QTVERSION_SETTINGS_PAGE_NAME);
83 84 85 86
}

QString QtOptionsPage::category() const
{
87
    return QLatin1String(Constants::QT_SETTINGS_CATEGORY);
88 89
}

90
QString QtOptionsPage::displayCategory() const
91
{
92
    return QCoreApplication::translate("Qt4ProjectManager", Constants::QT_SETTINGS_TR_CATEGORY);
93 94
}

95 96 97 98 99
QIcon QtOptionsPage::categoryIcon() const
{
    return QIcon(QLatin1String(Constants::QT_SETTINGS_CATEGORY_ICON));
}

100 101 102
QWidget *QtOptionsPage::createPage(QWidget *parent)
{
    QtVersionManager *vm = QtVersionManager::instance();
Tobias Hunger's avatar
Tobias Hunger committed
103
    m_widget = new QtOptionsPageWidget(parent, vm->versions());
104 105
    if (m_searchKeywords.isEmpty())
        m_searchKeywords = m_widget->searchKeywords();
106 107 108 109 110
    return m_widget;
}

void QtOptionsPage::apply()
{
111 112
    if (!m_widget) // page was never shown
        return;
113 114 115
    m_widget->finish();

    QtVersionManager *vm = QtVersionManager::instance();
116
    vm->setNewQtVersions(m_widget->versions());
117 118
}

119 120 121 122 123
bool QtOptionsPage::matches(const QString &s) const
{
    return m_searchKeywords.contains(s, Qt::CaseInsensitive);
}

124
//-----------------------------------------------------
125 126


dt's avatar
dt committed
127
QtOptionsPageWidget::QtOptionsPageWidget(QWidget *parent, QList<BaseQtVersion *> versions)
128 129
    : QWidget(parent)
    , m_specifyNameString(tr("<specify a name>"))
130
    , m_ui(new Internal::Ui::QtVersionManager())
131 132
    , m_versionUi(new Internal::Ui::QtVersionInfo())
    , m_debuggingHelperUi(new Internal::Ui::DebuggingHelper())
133
    , m_invalidVersionIcon(":/projectexplorer/images/compile_error.png")
134
    , m_warningVersionIcon(":/projectexplorer/images/compile_warning.png")
dt's avatar
dt committed
135
    , m_configurationWidget(0)
136 137
{
    // Initialize m_versions
dt's avatar
dt committed
138
    foreach (BaseQtVersion *version, versions)
dt's avatar
dt committed
139
        m_versions.push_back(version->clone());
140

141 142
    QWidget *versionInfoWidget = new QWidget();
    m_versionUi->setupUi(versionInfoWidget);
143
    m_versionUi->editPathPushButton->setText(tr(Utils::PathChooser::browseButtonLabel));
144

145 146 147
    QWidget *debuggingHelperDetailsWidget = new QWidget();
    m_debuggingHelperUi->setupUi(debuggingHelperDetailsWidget);

148
    m_ui->setupUi(this);
149 150 151 152 153

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

    m_ui->debuggingHelperWidget->setWidget(debuggingHelperDetailsWidget);
154 155
    connect(m_ui->debuggingHelperWidget, SIGNAL(expanded(bool)),
            this, SLOT(handleDebuggingHelperExpanded(bool)));
156

157 158
    // setup parent items for auto-detected and manual versions
    m_ui->qtdirList->header()->setResizeMode(QHeaderView::ResizeToContents);
dt_'s avatar
dt_ committed
159 160
    m_ui->qtdirList->header()->setStretchLastSection(false);
    m_ui->qtdirList->setTextElideMode(Qt::ElideNone);
161
    QTreeWidgetItem *autoItem = new QTreeWidgetItem(m_ui->qtdirList);
162
    m_ui->qtdirList->installEventFilter(this);
163 164
    autoItem->setText(0, tr("Auto-detected"));
    autoItem->setFirstColumnSpanned(true);
165
    autoItem->setFlags(Qt::ItemIsEnabled);
166 167 168
    QTreeWidgetItem *manualItem = new QTreeWidgetItem(m_ui->qtdirList);
    manualItem->setText(0, tr("Manual"));
    manualItem->setFirstColumnSpanned(true);
169
    manualItem->setFlags(Qt::ItemIsEnabled);
170

171
    for (int i = 0; i < m_versions.count(); ++i) {
dt's avatar
dt committed
172
        BaseQtVersion *version = m_versions.at(i);
173
        QTreeWidgetItem *item = new QTreeWidgetItem(version->isAutodetected()? autoItem : manualItem);
174
        item->setText(0, version->displayName());
175
        item->setText(1, QDir::toNativeSeparators(version->qmakeCommand()));
176
        item->setData(0, VersionIdRole, version->uniqueId());
177
        item->setData(0, ToolChainIdRole, defaultToolChainId(version));
178 179
        const ValidityInfo info = validInformation(version);
        item->setIcon(0, info.icon);
180
    }
181
    m_ui->qtdirList->expandAll();
182

183
    connect(m_versionUi->nameEdit, SIGNAL(textEdited(const QString &)),
184 185
            this, SLOT(updateCurrentQtName()));

dt_'s avatar
dt_ committed
186 187 188
    connect(m_versionUi->editPathPushButton, SIGNAL(clicked()),
            this, SLOT(editPath()));

189 190 191 192 193 194 195 196
    connect(m_ui->addButton, SIGNAL(clicked()),
            this, SLOT(addQtDir()));
    connect(m_ui->delButton, SIGNAL(clicked()),
            this, SLOT(removeQtDir()));

    connect(m_ui->qtdirList, SIGNAL(currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)),
            this, SLOT(versionChanged(QTreeWidgetItem *, QTreeWidgetItem *)));

197
    connect(m_debuggingHelperUi->rebuildButton, SIGNAL(clicked()),
198
            this, SLOT(buildDebuggingHelper()));
199 200 201 202
    connect(m_debuggingHelperUi->gdbHelperBuildButton, SIGNAL(clicked()),
            this, SLOT(buildGdbHelper()));
    connect(m_debuggingHelperUi->qmlDumpBuildButton, SIGNAL(clicked()),
            this, SLOT(buildQmlDump()));
203 204
    connect(m_debuggingHelperUi->qmlDebuggingLibBuildButton, SIGNAL(clicked()),
            this, SLOT(buildQmlDebuggingLibrary()));
205 206 207 208
    connect(m_debuggingHelperUi->qmlObserverBuildButton, SIGNAL(clicked()),
            this, SLOT(buildQmlObserver()));

    connect(m_debuggingHelperUi->showLogButton, SIGNAL(clicked()),
209
            this, SLOT(slotShowDebuggingBuildLog()));
210 211
    connect(m_debuggingHelperUi->toolChainComboBox, SIGNAL(activated(int)),
            this, SLOT(selectedToolChainChanged(int)));
212

213
    connect(m_ui->cleanUpButton, SIGNAL(clicked()), this, SLOT(cleanUpQtVersions()));
dt's avatar
dt committed
214 215
    userChangedCurrentVersion();
    updateCleanUpButton();
216

217 218
    connect(QtVersionManager::instance(), SIGNAL(dumpUpdatedFor(QString)),
            this, SLOT(qtVersionsDumpUpdated(QString)));
219 220 221

    connect(ProjectExplorer::ToolChainManager::instance(), SIGNAL(toolChainsChanged()),
            this, SLOT(toolChainsUpdated()));
222 223
}

224 225 226 227 228
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)
229
        return false;
230 231 232 233 234 235 236 237
    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;
238
    const QString tooltip = m_versions.at(index)->toHtml(true);
239 240 241 242 243
    QToolTip::showText(helpEvent->globalPos(), tooltip, m_ui->qtdirList);
    helpEvent->accept();
    return true;
}

244
int QtOptionsPageWidget::currentIndex() const
245
{
246 247 248 249
    if (QTreeWidgetItem *currentItem = m_ui->qtdirList->currentItem())
        return indexForTreeItem(currentItem);
    return -1;
}
250

dt's avatar
dt committed
251
BaseQtVersion *QtOptionsPageWidget::currentVersion() const
252 253 254
{
    const int currentItemIndex = currentIndex();
    if (currentItemIndex >= 0 && currentItemIndex < m_versions.size())
255
        return m_versions.at(currentItemIndex);
256 257
    return 0;
}
258

dt's avatar
dt committed
259
static inline int findVersionById(const QList<BaseQtVersion *> &l, int id)
260 261 262
{
    const int size = l.size();
    for (int i = 0; i < size; i++)
263
        if (l.at(i)->uniqueId() == id)
264 265 266
            return i;
    return -1;
}
267

268
// Update with results of terminated helper build
269
void QtOptionsPageWidget::debuggingHelperBuildFinished(int qtVersionId, const QString &output, DebuggingHelperBuildTask::Tools tools)
270
{
271
    const int index = findVersionById(m_versions, qtVersionId);
272 273
    if (index == -1)
        return; // Oops, somebody managed to delete the version
274

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

277 278
    // Update item view
    QTreeWidgetItem *item = treeItemForIndex(index);
279 280 281 282 283
    QTC_ASSERT(item, return);
    DebuggingHelperBuildTask::Tools buildFlags
            = item->data(0, BuildRunningRole).value<DebuggingHelperBuildTask::Tools>();
    buildFlags &= ~tools;
    item->setData(0, BuildRunningRole,  QVariant::fromValue(buildFlags));
284
    item->setData(0, BuildLogRole, output);
285

286 287
    bool success = true;
    if (tools & DebuggingHelperBuildTask::GdbDebugging)
dt's avatar
dt committed
288
        success &= version->hasGdbDebuggingHelper();
289
    if (tools & DebuggingHelperBuildTask::QmlDebugging)
dt's avatar
dt committed
290
        success &= version->hasQmlDebuggingLibrary();
291
    if (tools & DebuggingHelperBuildTask::QmlDump)
dt's avatar
dt committed
292
        success &= version->hasQmlDump();
293
    if (tools & DebuggingHelperBuildTask::QmlObserver)
dt's avatar
dt committed
294
        success &= version->hasQmlObserver();
295

296 297
    if (!success)
        showDebuggingBuildLog(item);
298 299

    updateDebuggingHelperUi();
300 301
}

302 303 304
void QtOptionsPageWidget::cleanUpQtVersions()
{
    QStringList toRemove;
dt's avatar
dt committed
305
    foreach (const BaseQtVersion *v, m_versions) {
306
        if (!v->isValid() && !v->isAutodetected())
307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328
            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
329
    updateCleanUpButton();
330 331
}

332 333 334 335
void QtOptionsPageWidget::toolChainsUpdated()
{
    for (int i = 0; i < m_versions.count(); ++i) {
        QTreeWidgetItem *item = treeItemForIndex(i);
336
        if (item == m_ui->qtdirList->currentItem()) {
337
            updateDescriptionLabel();
338 339
            updateDebuggingHelperUi();
        } else {
340 341 342 343 344 345
            const ValidityInfo info = validInformation(m_versions.at(i));
            item->setIcon(0, info.icon);
        }
    }
}

346 347 348 349 350 351 352 353 354 355 356 357 358 359
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);
}

360
void QtOptionsPageWidget::qtVersionsDumpUpdated(const QString &qmakeCommand)
361 362 363 364 365
{
    foreach (BaseQtVersion *version, m_versions) {
        if (version->qmakeCommand() == qmakeCommand)
            version->recheckDumper();
    }
366 367
    if (currentVersion()
            && currentVersion()->qmakeCommand() == qmakeCommand) {
368 369 370 371 372 373
        updateWidgets();
        updateDescriptionLabel();
        updateDebuggingHelperUi();
    }
}

374 375 376 377 378
void QtOptionsPageWidget::handleDebuggingHelperExpanded(bool expanded)
{
    m_ui->versionInfoWidget->setVisible(!expanded);
}

379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404
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;
    }

405
    bool useable = true;
406 407 408 409 410 411 412
    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;
413
        useable = false;
414 415 416
    } 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
417
        info.toolTip = tr("The following ABIs are currently not supported:<ul><li>%1</li></ul>")
418 419 420
                       .arg(missingToolChains.join(QLatin1String("</li><li>")));
        info.icon = m_warningVersionIcon;
    }
421 422 423 424 425 426 427 428 429 430 431

    if (useable) {
        QString warning = version->warningReason();
        if (!warning.isEmpty()) {
            if (!info.message.isEmpty())
                info.message.append('\n');
            info.message += warning;
            info.icon = m_warningVersionIcon;
        }
    }

432 433 434
    return info;
}

435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453
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();
}

454 455 456 457 458 459 460 461
QString QtOptionsPageWidget::defaultToolChainId(const BaseQtVersion *version)
{
    QList<ProjectExplorer::ToolChain*> possibleToolChains = toolChains(version);
    if (!possibleToolChains.isEmpty())
        return possibleToolChains.first()->id();
    return QString();
}

462
void QtOptionsPageWidget::buildDebuggingHelper(DebuggingHelperBuildTask::Tools tools)
463 464 465 466 467
{
    const int index = currentIndex();
    if (index < 0)
        return;

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

471 472 473 474
    DebuggingHelperBuildTask::Tools buildFlags
            = item->data(0, BuildRunningRole).value<DebuggingHelperBuildTask::Tools>();
    buildFlags |= tools;
    item->setData(0, BuildRunningRole, QVariant::fromValue(buildFlags));
475

dt's avatar
dt committed
476
    BaseQtVersion *version = m_versions.at(index);
477 478 479
    if (!version)
        return;

480
    updateDebuggingHelperUi();
481

482
    // Run a debugging helper build task in the background.
483 484 485 486 487 488 489 490
    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);
491 492
    // Don't open General Messages pane with errors
    buildTask->showOutputOnError(false);
493 494
    connect(buildTask, SIGNAL(finished(int,QString,DebuggingHelperBuildTask::Tools)),
            this, SLOT(debuggingHelperBuildFinished(int,QString,DebuggingHelperBuildTask::Tools)),
495
            Qt::QueuedConnection);
496
    QFuture<void> task = QtConcurrent::run(&DebuggingHelperBuildTask::run, buildTask);
497
    const QString taskName = tr("Building helpers");
498

499
    Core::ICore::instance()->progressManager()->addTask(task, taskName,
500
                                                        QLatin1String("Qt4ProjectManager::BuildHelpers"));
501
}
502 503 504 505 506 507 508 509 510 511
void QtOptionsPageWidget::buildGdbHelper()
{
    buildDebuggingHelper(DebuggingHelperBuildTask::GdbDebugging);
}

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

512 513 514 515 516
void QtOptionsPageWidget::buildQmlDebuggingLibrary()
{
    buildDebuggingHelper(DebuggingHelperBuildTask::QmlDebugging);
}

517 518
void QtOptionsPageWidget::buildQmlObserver()
{
519
    DebuggingHelperBuildTask::Tools qmlDbgTools =
Kai Koehne's avatar
Kai Koehne committed
520 521
            DebuggingHelperBuildTask::QmlObserver;
    qmlDbgTools |= DebuggingHelperBuildTask::QmlDebugging;
522
    buildDebuggingHelper(qmlDbgTools);
523
}
524

525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541
// 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)
542
{
543 544 545 546
    m_ui.log->setPlainText(text); // Show and scroll to bottom
    m_ui.log->moveCursor(QTextCursor::End);
    m_ui.log->ensureCursorVisible();
}
547

548 549 550 551 552 553 554 555 556
void QtOptionsPageWidget::slotShowDebuggingBuildLog()
{
    if (const QTreeWidgetItem *currentItem = m_ui->qtdirList->currentItem())
        showDebuggingBuildLog(currentItem);
}

void QtOptionsPageWidget::showDebuggingBuildLog(const QTreeWidgetItem *currentItem)
{
    const int currentItemIndex = indexForTreeItem(currentItem);
557 558
    if (currentItemIndex < 0)
        return;
559
    BuildLogDialog *dialog = new BuildLogDialog(this->window());
560
    dialog->setWindowTitle(tr("Debugging Helper Build Log for '%1'").arg(currentItem->text(0)));
561
    dialog->setText(currentItem->data(0, BuildLogRole).toString());
562
    dialog->show();
563 564
}

565
QtOptionsPageWidget::~QtOptionsPageWidget()
566 567
{
    delete m_ui;
dt's avatar
dt committed
568 569
    delete m_versionUi;
    delete m_debuggingHelperUi;
dt's avatar
dt committed
570
    delete m_configurationWidget;
571
    qDeleteAll(m_versions);
572 573
}

dt_'s avatar
dt_ committed
574
static QString filterForQmakeFileDialog()
575
{
dt's avatar
dt committed
576 577
    QString filter("qmake (");
    foreach (const QString &s, Utils::BuildableHelperLibrary::possibleQMakeCommands()) {
578 579 580 581 582
#ifdef Q_WS_MAC
        // work around QTBUG-7739 that prohibits filters that don't start with *
        filter += QLatin1Char('*');
#endif
        filter += s + QLatin1Char(' ');
dt's avatar
dt committed
583
    }
584
    filter += QLatin1Char(')');
dt_'s avatar
dt_ committed
585 586
    return filter;
}
587

dt_'s avatar
dt_ committed
588 589
void QtOptionsPageWidget::addQtDir()
{
dt's avatar
dt committed
590
    QString qtVersion = QFileDialog::getOpenFileName(this,
dt_'s avatar
dt_ committed
591 592
                                                     tr("Select a qmake executable"),
                                                     QString(), filterForQmakeFileDialog());
dt's avatar
dt committed
593 594 595 596 597
    if (qtVersion.isNull())
        return;
    if (QtVersionManager::instance()->qtVersionForQMakeBinary(qtVersion)) {
        // Already exist
    }
598

dt's avatar
dt committed
599 600 601
    BaseQtVersion *version = QtVersionFactory::createQtVersionFromQMakePath(qtVersion);
    if (version) {
        m_versions.append(version);
602

dt's avatar
dt committed
603 604 605 606
        QTreeWidgetItem *item = new QTreeWidgetItem(m_ui->qtdirList->topLevelItem(1));
        item->setText(0, version->displayName());
        item->setText(1, QDir::toNativeSeparators(version->qmakeCommand()));
        item->setData(0, VersionIdRole, version->uniqueId());
607
        item->setData(0, ToolChainIdRole, defaultToolChainId(version));
dt's avatar
dt committed
608 609 610 611 612 613
        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();
614 615
}

616
void QtOptionsPageWidget::removeQtDir()
617 618
{
    QTreeWidgetItem *item = m_ui->qtdirList->currentItem();
619
    int index = indexForTreeItem(item);
620 621 622 623 624
    if (index < 0)
        return;

    delete item;

dt's avatar
dt committed
625
    BaseQtVersion *version = m_versions.at(index);
626
    m_versions.removeAt(index);
627
    delete version;
dt's avatar
dt committed
628
    updateCleanUpButton();
629 630
}

dt_'s avatar
dt_ committed
631 632
void QtOptionsPageWidget::editPath()
{
633 634
    BaseQtVersion *current = currentVersion();
    QString dir = QFileInfo(currentVersion()->qmakeCommand()).absolutePath();
dt_'s avatar
dt_ committed
635 636
    QString qtVersion = QFileDialog::getOpenFileName(this,
                                                     tr("Select a qmake executable"),
637
                                                     dir, filterForQmakeFileDialog());
dt_'s avatar
dt_ committed
638 639 640
    if (qtVersion.isNull())
        return;
    BaseQtVersion *version = QtVersionFactory::createQtVersionFromQMakePath(qtVersion);
Daniel Teske's avatar
Daniel Teske committed
641 642
    if (!version)
        return;
dt_'s avatar
dt_ committed
643 644 645 646 647 648 649 650 651 652 653
    // 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
654 655
    if (current->displayName() != current->defaultDisplayName(current->qtVersionString(), current->qmakeCommand()))
        version->setDisplayName(current->displayName());
dt_'s avatar
dt_ committed
656 657 658 659 660 661 662 663 664
    m_versions.replace(m_versions.indexOf(current), version);
    delete current;

    // Update ui
    userChangedCurrentVersion();
    QTreeWidgetItem *item = m_ui->qtdirList->currentItem();
    item->setText(0, version->displayName());
    item->setText(1, QDir::toNativeSeparators(version->qmakeCommand()));
    item->setData(0, VersionIdRole, version->uniqueId());
665
    item->setData(0, ToolChainIdRole, defaultToolChainId(version));
dt_'s avatar
dt_ committed
666 667 668
    item->setIcon(0, version->isValid()? m_validVersionIcon : m_invalidVersionIcon);
}

669
void QtOptionsPageWidget::updateDebuggingHelperUi()
670
{
dt's avatar
dt committed
671
    BaseQtVersion *version = currentVersion();
672
    const QTreeWidgetItem *currentItem = m_ui->qtdirList->currentItem();
673

674 675 676
    QList<ProjectExplorer::ToolChain*> toolchains = toolChains(currentVersion());

    if (!version || !version->isValid() || toolchains.isEmpty()) {
677
        m_ui->debuggingHelperWidget->setVisible(false);
678
    } else {
679 680 681 682 683
        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;
684

685 686 687
        const bool hasGdbHelper = !version->gdbDebuggingHelperLibrary().isEmpty();
        const bool hasQmlDumper = version->hasQmlDump();
        const bool hasQmlDebuggingLib = version->hasQmlDebuggingLibrary();
688
        const bool needsQmlDebuggingLib = version->needsQmlDebuggingLibrary();
689
        const bool hasQmlObserver = !version->qmlObserverTool().isEmpty();
690

691 692
        bool isBuildingGdbHelper = false;
        bool isBuildingQmlDumper = false;
693
        bool isBuildingQmlDebuggingLib = false;
694 695 696 697 698 699 700
        bool isBuildingQmlObserver = false;

        if (currentItem) {
            DebuggingHelperBuildTask::Tools buildingTools
                    = currentItem->data(0, BuildRunningRole).value<DebuggingHelperBuildTask::Tools>();
            isBuildingGdbHelper = buildingTools & DebuggingHelperBuildTask::GdbDebugging;
            isBuildingQmlDumper = buildingTools & DebuggingHelperBuildTask::QmlDump;
701
            isBuildingQmlDebuggingLib = buildingTools & DebuggingHelperBuildTask::QmlDebugging;
702 703 704
            isBuildingQmlObserver = buildingTools & DebuggingHelperBuildTask::QmlObserver;
        }

705 706 707 708 709 710
        // get names of tools from labels
        QStringList helperNames;
        if (hasGdbHelper)
            helperNames << m_debuggingHelperUi->gdbHelperLabel->text().remove(':');
        if (hasQmlDumper)
            helperNames << m_debuggingHelperUi->qmlDumpLabel->text().remove(':');
711 712
        if (hasQmlDebuggingLib)
            helperNames << m_debuggingHelperUi->qmlDebuggingLibLabel->text().remove(':');
713 714 715 716 717
        if (hasQmlObserver)
            helperNames << m_debuggingHelperUi->qmlObserverLabel->text().remove(':');

        QString status;
        if (helperNames.isEmpty()) {
718
            status = tr("Helpers: None available");
719
        } else {
Friedemann Kleint's avatar
Friedemann Kleint committed
720
            //: %1 is list of tool names.
721
            status = tr("Helpers: %1.").arg(helperNames.join(QLatin1String(", ")));
722 723 724 725
        }

        m_ui->debuggingHelperWidget->setSummaryText(status);

726 727
        QString gdbHelperText;
        Qt::TextInteractionFlags gdbHelperTextFlags = Qt::NoTextInteraction;
728
        if (hasGdbHelper) {
729
            gdbHelperText = QDir::toNativeSeparators(version->gdbDebuggingHelperLibrary());
730
            gdbHelperTextFlags = Qt::TextSelectableByMouse;
731
        } else {
732 733 734 735 736
            if (canBuildGdbHelper) {
                gdbHelperText =  tr("<i>Not yet built.</i>");
            } else {
                gdbHelperText =  tr("<i>Not needed.</i>");
            }
737
        }
738 739
        m_debuggingHelperUi->gdbHelperStatus->setText(gdbHelperText);
        m_debuggingHelperUi->gdbHelperStatus->setTextInteractionFlags(gdbHelperTextFlags);
740
        m_debuggingHelperUi->gdbHelperBuildButton->setEnabled(canBuildGdbHelper && !isBuildingGdbHelper);
741

742
        QString qmlDumpStatusText, qmlDumpStatusToolTip;
743
        Qt::TextInteractionFlags qmlDumpStatusTextFlags = Qt::NoTextInteraction;
744
        if (hasQmlDumper) {
745 746
            qmlDumpStatusText = QDir::toNativeSeparators(version->qmlDumpTool(false));
            const QString debugQmlDumpPath = QDir::toNativeSeparators(version->qmlDumpTool(true));
747
            if (qmlDumpStatusText != debugQmlDumpPath) {
748 749
                if (!qmlDumpStatusText.isEmpty()
                        && !debugQmlDumpPath.isEmpty())
750 751 752
                    qmlDumpStatusText += QLatin1String("\n");
                qmlDumpStatusText += debugQmlDumpPath;
            }
753
            qmlDumpStatusTextFlags = Qt::TextSelectableByMouse;
754
        } else {
755 756 757 758
            if (canBuildQmlDumper) {
                qmlDumpStatusText = tr("<i>Not yet built.</i>");
            } else {
                qmlDumpStatusText = tr("<i>Cannot be compiled.</i>");
759
                QmlDumpTool::canBuild(version, &qmlDumpStatusToolTip);
760
            }
761
        }
762
        m_debuggingHelperUi->qmlDumpStatus->setText(qmlDumpStatusText);
763
        m_debuggingHelperUi->qmlDumpStatus->setTextInteractionFlags(qmlDumpStatusTextFlags);
764
        m_debuggingHelperUi->qmlDumpStatus->setToolTip(qmlDumpStatusToolTip);
765
        m_debuggingHelperUi->qmlDumpBuildButton->setEnabled(canBuildQmlDumper & !isBuildingQmlDumper);
766

767
        QString qmlDebuggingLibStatusText, qmlDebuggingLibToolTip;
768 769 770 771 772 773 774 775 776 777
        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()) {
778
                    qmlDebuggingLibStatusText += QLatin1Char('\n');
779 780 781 782
                }
                qmlDebuggingLibStatusText += debugPath;
            }
            qmlDebuggingLibStatusTextFlags = Qt::TextSelectableByMouse;
783 784 785 786
        } else {
            if (!needsQmlDebuggingLib) {
                qmlDebuggingLibStatusText = tr("<i>Not needed.</i>");
            } else if (canBuildQmlDebuggingLib) {
787 788 789
                qmlDebuggingLibStatusText = tr("<i>Not yet built.</i>");
            } else {
                qmlDebuggingLibStatusText = tr("<i>Cannot be compiled.</i>");
790
                QmlDebuggingLibrary::canBuild(version, &qmlDebuggingLibToolTip);
791 792 793 794
            }
        }
        m_debuggingHelperUi->qmlDebuggingLibStatus->setText(qmlDebuggingLibStatusText);
        m_debuggingHelperUi->qmlDebuggingLibStatus->setTextInteractionFlags(qmlDebuggingLibStatusTextFlags);
795
        m_debuggingHelperUi->qmlDebuggingLibStatus->setToolTip(qmlDebuggingLibToolTip);
796 797
        m_debuggingHelperUi->qmlDebuggingLibBuildButton->setEnabled(needsQmlDebuggingLib
                                                                    && canBuildQmlDebuggingLib
798 799
                                                                    && !isBuildingQmlDebuggingLib);

800
        QString qmlObserverStatusText, qmlObserverToolTip;
801
        Qt::TextInteractionFlags qmlObserverStatusTextFlags = Qt::NoTextInteraction;
802
        if (hasQmlObserver) {
803
            qmlObserverStatusText = QDir::toNativeSeparators(version->qmlObserverTool());
804
            qmlObserverStatusTextFlags = Qt::TextSelectableByMouse;
805
        }  else {
806 807 808
            if (!needsQmlDebuggingLib) {
                qmlObserverStatusText = tr("<i>Not needed.</i>");
            } else if (canBuildQmlObserver) {
809 810 811
                qmlObserverStatusText = tr("<i>Not yet built.</i>");
            } else {
                qmlObserverStatusText = tr("<i>Cannot be compiled.</i>");
812
                QmlObserverTool::canBuild(version, &qmlObserverToolTip);
813
            }
814
        }
815
        m_debuggingHelperUi->qmlObserverStatus->setText(qmlObserverStatusText);
816
        m_debuggingHelperUi->qmlObserverStatus->setTextInteractionFlags(qmlObserverStatusTextFlags);
817
        m_debuggingHelperUi->qmlObserverStatus->setToolTip(qmlObserverToolTip);
818 819
        m_debuggingHelperUi->qmlObserverBuildButton->setEnabled(canBuildQmlObserver
                                                                & !isBuildingQmlObserver);
820

821
        QList<ProjectExplorer::ToolChain*> toolchains = toolChains(currentVersion());
822
        QString selectedToolChainId = currentItem->data(0, ToolChainIdRole).toString();
823 824 825 826 827 828 829 830 831 832 833 834
        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);
        }

835 836 837
        const bool hasLog = currentItem && !currentItem->data(0, BuildLogRole).toString().isEmpty();
        m_debuggingHelperUi->showLogButton->setEnabled(hasLog);

838 839 840 841 842 843 844 845 846 847 848
        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);
849

850
        m_ui->debuggingHelperWidget->setVisible(true);
851 852 853
    }
}

dt's avatar
dt committed
854 855
// To be called if a qt version was removed or added
void QtOptionsPageWidget::updateCleanUpButton()
856
{
857
    bool hasInvalidVersion = false;
858 859
    for (int i = 0; i < m_versions.count(); ++i) {
        if (!m_versions.at(i)->isValid()) {
860
            hasInvalidVersion = true;
dt's avatar
dt committed
861
            break;
862 863
        }
    }
864
    m_ui->cleanUpButton->setEnabled(hasInvalidVersion);
865
}
866

dt's avatar
dt committed
867
void QtOptionsPageWidget::userChangedCurrentVersion()
con's avatar
con committed
868
{
dt's avatar
dt committed
869 870 871
    updateWidgets();
    updateDescriptionLabel();
    updateDebuggingHelperUi();
872 873
}

dt's avatar
dt committed
874
void QtOptionsPageWidget::qtVersionChanged()
875
{
876
    updateDescriptionLabel();
dt's avatar
dt committed
877
    updateDebuggingHelperUi();
878 879 880 881
}

void QtOptionsPageWidget::updateDescriptionLabel()
{
882
    QTreeWidgetItem *item = m_ui->qtdirList->currentItem();
883 884 885 886 887
    const ValidityInfo info = validInformation(currentVersion());
    m_versionUi->errorLabel->setText(info.message);
    m_versionUi->errorLabel->setToolTip(info.toolTip);
    if (item)
        item->setIcon(0, info.icon);
888 889
}

890
int QtOptionsPageWidget::indexForTreeItem(const QTreeWidgetItem *item) const
891 892 893
{
    if (!item || !item->parent())
        return -1;
894
    const int uniqueId = item->data(0, VersionIdRole).toInt();
895 896 897 898 899 900 901
    for (int index = 0; index < m_versions.size(); ++index) {
        if (m_versions.at(index)->uniqueId() == uniqueId)
            return index;
    }
    return -1;
}

902 903
QTreeWidgetItem *QtOptionsPageWidget::treeItemForIndex(int index) const
{
904
    const int uniqueId = m_versions.at(index)->uniqueId();
905 906 907 908
    for (int i = 0; i < m_ui->qtdirList->topLevelItemCount(); ++i) {
        QTreeWidgetItem *toplevelItem = m_ui->qtdirList->topLevelItem(i);
        for (int j = 0; j < toplevelItem->childCount(); ++j) {
            QTreeWidgetItem *item = toplevelItem->child(j);
909
            if (item->data(0, VersionIdRole).toInt() == uniqueId) {