qtoptionspage.cpp 41.7 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
{
}

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

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

QString QtOptionsPage::category() const
{
88
    return QLatin1String(ProjectExplorer::Constants::PROJECTEXPLORER_SETTINGS_CATEGORY);
89 90
}

91
QString QtOptionsPage::displayCategory() const
92
{
93 94
    return QCoreApplication::translate("ProjectExplorer",
                                       ProjectExplorer::Constants::PROJECTEXPLORER_SETTINGS_TR_CATEGORY);
95 96
}

97 98
QIcon QtOptionsPage::categoryIcon() const
{
99
    return QIcon(QLatin1String(ProjectExplorer::Constants::PROJECTEXPLORER_SETTINGS_CATEGORY_ICON));
100 101
}

102 103
QWidget *QtOptionsPage::createPage(QWidget *parent)
{
104
    m_widget = new QtOptionsPageWidget(parent);
105 106
    if (m_searchKeywords.isEmpty())
        m_searchKeywords = m_widget->searchKeywords();
107 108 109 110 111
    return m_widget;
}

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

116
    m_widget->apply();
117 118
}

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

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


127
QtOptionsPageWidget::QtOptionsPageWidget(QWidget *parent)
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 134
    , m_invalidVersionIcon(QLatin1String(":/projectexplorer/images/compile_error.png"))
    , m_warningVersionIcon(QLatin1String(":/projectexplorer/images/compile_warning.png"))
dt's avatar
dt committed
135
    , m_configurationWidget(0)
136 137
    , m_autoItem(0)
    , m_manualItem(0)
138
{
139 140
    QWidget *versionInfoWidget = new QWidget();
    m_versionUi->setupUi(versionInfoWidget);
141
    m_versionUi->editPathPushButton->setText(tr(Utils::PathChooser::browseButtonLabel));
142

143 144 145
    QWidget *debuggingHelperDetailsWidget = new QWidget();
    m_debuggingHelperUi->setupUi(debuggingHelperDetailsWidget);

146
    m_ui->setupUi(this);
147 148 149 150 151

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

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

155 156
    // setup parent items for auto-detected and manual versions
    m_ui->qtdirList->header()->setResizeMode(QHeaderView::ResizeToContents);
dt_'s avatar
dt_ committed
157 158
    m_ui->qtdirList->header()->setStretchLastSection(false);
    m_ui->qtdirList->setTextElideMode(Qt::ElideNone);
159
    m_autoItem = new QTreeWidgetItem(m_ui->qtdirList);
160
    m_ui->qtdirList->installEventFilter(this);
161 162 163 164 165 166 167 168 169 170 171 172 173 174
    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>());
175 176

    m_ui->qtdirList->expandAll();
177

Robert Loehning's avatar
Robert Loehning committed
178
    connect(m_versionUi->nameEdit, SIGNAL(textEdited(QString)),
179 180
            this, SLOT(updateCurrentQtName()));

dt_'s avatar
dt_ committed
181 182 183
    connect(m_versionUi->editPathPushButton, SIGNAL(clicked()),
            this, SLOT(editPath()));

184 185 186 187 188
    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
189 190
    connect(m_ui->qtdirList, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)),
            this, SLOT(versionChanged(QTreeWidgetItem*,QTreeWidgetItem*)));
191

192
    connect(m_debuggingHelperUi->rebuildButton, SIGNAL(clicked()),
193
            this, SLOT(buildDebuggingHelper()));
194 195 196 197
    connect(m_debuggingHelperUi->gdbHelperBuildButton, SIGNAL(clicked()),
            this, SLOT(buildGdbHelper()));
    connect(m_debuggingHelperUi->qmlDumpBuildButton, SIGNAL(clicked()),
            this, SLOT(buildQmlDump()));
198 199
    connect(m_debuggingHelperUi->qmlDebuggingLibBuildButton, SIGNAL(clicked()),
            this, SLOT(buildQmlDebuggingLibrary()));
200 201 202 203
    connect(m_debuggingHelperUi->qmlObserverBuildButton, SIGNAL(clicked()),
            this, SLOT(buildQmlObserver()));

    connect(m_debuggingHelperUi->showLogButton, SIGNAL(clicked()),
204
            this, SLOT(slotShowDebuggingBuildLog()));
205 206
    connect(m_debuggingHelperUi->toolChainComboBox, SIGNAL(activated(int)),
            this, SLOT(selectedToolChainChanged(int)));
207

208
    connect(m_ui->cleanUpButton, SIGNAL(clicked()), this, SLOT(cleanUpQtVersions()));
dt's avatar
dt committed
209 210
    userChangedCurrentVersion();
    updateCleanUpButton();
211

212 213
    connect(QtVersionManager::instance(), SIGNAL(dumpUpdatedFor(Utils::FileName)),
            this, SLOT(qtVersionsDumpUpdated(Utils::FileName)));
214

215 216 217
    connect(QtVersionManager::instance(), SIGNAL(qtVersionsChanged(QList<int>,QList<int>,QList<int>)),
            this, SLOT(updateQtVersions(QList<int>,QList<int>,QList<int>)));

218 219
    connect(ProjectExplorer::ToolChainManager::instance(), SIGNAL(toolChainsChanged()),
            this, SLOT(toolChainsUpdated()));
220 221
}

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

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

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

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

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

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

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

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

294 295
    if (!success)
        showDebuggingBuildLog(item);
296 297

    updateDebuggingHelperUi();
298 299
}

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

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

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

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

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

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

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

    if (useable) {
        QString warning = version->warningReason();
        if (!warning.isEmpty()) {
            if (!info.message.isEmpty())
424
                info.message.append(QLatin1Char('\n'));
425 426 427 428 429
            info.message += warning;
            info.icon = m_warningVersionIcon;
        }
    }

430 431 432
    return info;
}

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

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

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

466 467 468
    // remove tools that cannot be build
    tools &= DebuggingHelperBuildTask::availableTools(currentVersion());

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

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

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

481
    updateDebuggingHelperUi();
482

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

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

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

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

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

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

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

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

566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632
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);
    }
}

633
QtOptionsPageWidget::~QtOptionsPageWidget()
634 635
{
    delete m_ui;
dt's avatar
dt committed
636 637
    delete m_versionUi;
    delete m_debuggingHelperUi;
dt's avatar
dt committed
638
    delete m_configurationWidget;
639
    qDeleteAll(m_versions);
640 641
}

dt_'s avatar
dt_ committed
642
static QString filterForQmakeFileDialog()
643
{
644
    QString filter = QLatin1String("qmake (");
645 646 647 648
    const QStringList commands = Utils::BuildableHelperLibrary::possibleQMakeCommands();
    for (int i = 0; i < commands.size(); ++i) {
        if (i)
            filter += QLatin1Char(' ');
649
#ifdef Q_OS_MAC
650 651 652
        // work around QTBUG-7739 that prohibits filters that don't start with *
        filter += QLatin1Char('*');
#endif
653
        filter += commands.at(i);
dt's avatar
dt committed
654
    }
655
    filter += QLatin1Char(')');
dt_'s avatar
dt_ committed
656 657
    return filter;
}
658

dt_'s avatar
dt_ committed
659 660
void QtOptionsPageWidget::addQtDir()
{
661 662 663 664
    Utils::FileName qtVersion = Utils::FileName::fromString(
                QFileDialog::getOpenFileName(this,
                                             tr("Select a qmake executable"),
                                             QString(),
665 666 667
                                             filterForQmakeFileDialog(),
                                             0,
                                             QFileDialog::DontResolveSymlinks));
dt's avatar
dt committed
668 669 670 671 672
    if (qtVersion.isNull())
        return;
    if (QtVersionManager::instance()->qtVersionForQMakeBinary(qtVersion)) {
        // Already exist
    }
673

dt's avatar
dt committed
674 675 676
    BaseQtVersion *version = QtVersionFactory::createQtVersionFromQMakePath(qtVersion);
    if (version) {
        m_versions.append(version);
677

dt's avatar
dt committed
678 679
        QTreeWidgetItem *item = new QTreeWidgetItem(m_ui->qtdirList->topLevelItem(1));
        item->setText(0, version->displayName());
680
        item->setText(1, version->qmakeCommand().toUserOutput());
dt's avatar
dt committed
681
        item->setData(0, VersionIdRole, version->uniqueId());
682
        item->setData(0, ToolChainIdRole, defaultToolChainId(version));
dt's avatar
dt committed
683 684 685 686 687 688
        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();
689 690
}

691
void QtOptionsPageWidget::removeQtDir()
692 693
{
    QTreeWidgetItem *item = m_ui->qtdirList->currentItem();
694
    int index = indexForTreeItem(item);
695 696 697 698 699
    if (index < 0)
        return;

    delete item;

dt's avatar
dt committed
700
    BaseQtVersion *version = m_versions.at(index);
701
    m_versions.removeAt(index);
702
    delete version;
dt's avatar
dt committed
703
    updateCleanUpButton();
704 705
}

dt_'s avatar
dt_ committed
706 707
void QtOptionsPageWidget::editPath()
{
708
    BaseQtVersion *current = currentVersion();
709 710 711 712
    QString dir = currentVersion()->qmakeCommand().toFileInfo().absolutePath();
    Utils::FileName qtVersion = Utils::FileName::fromString(
                QFileDialog::getOpenFileName(this,
                                             tr("Select a qmake executable"),
713 714 715 716
                                             dir,
                                             filterForQmakeFileDialog(),
                                             0,
                                             QFileDialog::DontResolveSymlinks));
dt_'s avatar
dt_ committed
717 718 719
    if (qtVersion.isNull())
        return;
    BaseQtVersion *version = QtVersionFactory::createQtVersionFromQMakePath(qtVersion);
Daniel Teske's avatar
Daniel Teske committed
720 721
    if (!version)
        return;
dt_'s avatar
dt_ committed
722 723 724 725 726 727 728 729 730 731 732
    // 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
733 734
    if (current->displayName() != current->defaultDisplayName(current->qtVersionString(), current->qmakeCommand()))
        version->setDisplayName(current->displayName());
dt_'s avatar
dt_ committed
735 736 737 738 739 740 741
    m_versions.replace(m_versions.indexOf(current), version);
    delete current;

    // Update ui
    userChangedCurrentVersion();
    QTreeWidgetItem *item = m_ui->qtdirList->currentItem();
    item->setText(0, version->displayName());
742
    item->setText(1, version->qmakeCommand().toUserOutput());
dt_'s avatar
dt_ committed
743
    item->setData(0, VersionIdRole, version->uniqueId());
744
    item->setData(0, ToolChainIdRole, defaultToolChainId(version));
dt_'s avatar
dt_ committed
745 746 747
    item->setIcon(0, version->isValid()? m_validVersionIcon : m_invalidVersionIcon);
}

748
void QtOptionsPageWidget::updateDebuggingHelperUi()
749
{
dt's avatar
dt committed
750
    BaseQtVersion *version = currentVersion();
751
    const QTreeWidgetItem *currentItem = m_ui->qtdirList->currentItem();
752

753 754 755
    QList<ProjectExplorer::ToolChain*> toolchains = toolChains(currentVersion());

    if (!version || !version->isValid() || toolchains.isEmpty()) {
756
        m_ui->debuggingHelperWidget->setVisible(false);
757
    } else {
758 759 760 761 762
        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;
763

764 765
        const bool hasGdbHelper = !version->gdbDebuggingHelperLibrary().isEmpty();
        const bool hasQmlDumper = version->hasQmlDump();
766
        const bool needsQmlDumper = version->needsQmlDump();
767
        const bool hasQmlDebuggingLib = version->hasQmlDebuggingLibrary();
768
        const bool needsQmlDebuggingLib = version->needsQmlDebuggingLibrary();
769
        const bool hasQmlObserver = !version->qmlObserverTool().isEmpty();
770

771 772
        bool isBuildingGdbHelper = false;
        bool isBuildingQmlDumper = false;
773
        bool isBuildingQmlDebuggingLib = false;
774 775 776 777 778 779 780
        bool isBuildingQmlObserver = false;

        if (currentItem) {
            DebuggingHelperBuildTask::Tools buildingTools
                    = currentItem->data(0, BuildRunningRole).value<DebuggingHelperBuildTask::Tools>();
            isBuildingGdbHelper = buildingTools & DebuggingHelperBuildTask::GdbDebugging;
            isBuildingQmlDumper = buildingTools & DebuggingHelperBuildTask::QmlDump;
781
            isBuildingQmlDebuggingLib = buildingTools & DebuggingHelperBuildTask::QmlDebugging;
782 783 784
            isBuildingQmlObserver = buildingTools & DebuggingHelperBuildTask::QmlObserver;
        }

785 786
        // get names of tools from labels
        QStringList helperNames;
787
        const QChar colon = QLatin1Char(':');
788
        if (hasGdbHelper)
789
            helperNames << m_debuggingHelperUi->gdbHelperLabel->text().remove(colon);
790
        if (hasQmlDumper)
791
            helperNames << m_debuggingHelperUi->qmlDumpLabel->text().remove(colon);
792
        if (hasQmlDebuggingLib)
793
            helperNames << m_debuggingHelperUi->qmlDebuggingLibLabel->text().remove(colon);
794
        if (hasQmlObserver)
795
            helperNames << m_debuggingHelperUi->qmlObserverLabel->text().remove(colon);
796 797 798

        QString status;
        if (helperNames.isEmpty()) {
799
            status = tr("Helpers: None available");
800
        } else {
Friedemann Kleint's avatar
Friedemann Kleint committed
801
            //: %1 is list of tool names.
802
            status = tr("Helpers: %1.").arg(helperNames.join(QLatin1String(", ")));
803 804 805 806
        }

        m_ui->debuggingHelperWidget->setSummaryText(status);

807 808
        QString gdbHelperText;
        Qt::TextInteractionFlags gdbHelperTextFlags = Qt::NoTextInteraction;
809
        if (hasGdbHelper) {
810
            gdbHelperText = QDir::toNativeSeparators(version->gdbDebuggingHelperLibrary());
811
            gdbHelperTextFlags = Qt::TextSelectableByMouse;
812
        } else {
813 814 815 816 817
            if (canBuildGdbHelper) {
                gdbHelperText =  tr("<i>Not yet built.</i>");
            } else {
                gdbHelperText =  tr("<i>Not needed.</i>");
            }
818
        }
819 820
        m_debuggingHelperUi->gdbHelperStatus->setText(gdbHelperText);
        m_debuggingHelperUi->gdbHelperStatus->setTextInteractionFlags(gdbHelperTextFlags);
821
        m_debuggingHelperUi->gdbHelperBuildButton->setEnabled(canBuildGdbHelper && !isBuildingGdbHelper);
822

823
        QString qmlDumpStatusText, qmlDumpStatusToolTip;
824
        Qt::TextInteractionFlags qmlDumpStatusTextFlags = Qt::NoTextInteraction;
825
        if (hasQmlDumper) {
826 827
            qmlDumpStatusText = QDir::toNativeSeparators(version->qmlDumpTool(false));
            const QString debugQmlDumpPath = QDir::toNativeSeparators(version->qmlDumpTool(true));
828
            if (qmlDumpStatusText != debugQmlDumpPath) {
829 830
                if (!qmlDumpStatusText.isEmpty()
                        && !debugQmlDumpPath.isEmpty())
831 832 833
                    qmlDumpStatusText += QLatin1String("\n");
                qmlDumpStatusText += debugQmlDumpPath;
            }
834
            qmlDumpStatusTextFlags = Qt::TextSelectableByMouse;
835
        } else {
836 837 838
            if (!needsQmlDumper) {
                qmlDumpStatusText = tr("<i>Not needed.</i>");
            } else if (canBuildQmlDumper) {
839 840 841
                qmlDumpStatusText = tr("<i>Not yet built.</i>");
            } else {
                qmlDumpStatusText = tr("<i>Cannot be compiled.</i>");
842
                QmlDumpTool::canBuild(version, &qmlDumpStatusToolTip);
843
            }
844
        }
845
        m_debuggingHelperUi->qmlDumpStatus->setText(qmlDumpStatusText);
846
        m_debuggingHelperUi->qmlDumpStatus->setTextInteractionFlags(qmlDumpStatusTextFlags);
847
        m_debuggingHelperUi->qmlDumpStatus->setToolTip(qmlDumpStatusToolTip);
848
        m_debuggingHelperUi->qmlDumpBuildButton->setEnabled(canBuildQmlDumper & !isBuildingQmlDumper);
849

850
        QString qmlDebuggingLibStatusText, qmlDebuggingLibToolTip;
851 852 853 854 855 856 857 858 859 860
        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()) {
861
                    qmlDebuggingLibStatusText += QLatin1Char('\n');
862 863 864 865
                }
                qmlDebuggingLibStatusText += debugPath;
            }
            qmlDebuggingLibStatusTextFlags = Qt::TextSelectableByMouse;
866 867 868 869
        } else {
            if (!needsQmlDebuggingLib) {
                qmlDebuggingLibStatusText = tr("<i>Not needed.</i>");
            } else if (canBuildQmlDebuggingLib) {
870 871 872
                qmlDebuggingLibStatusText = tr("<i>Not yet built.</i>");
            } else {
                qmlDebuggingLibStatusText = tr("<i>Cannot be compiled.</i>");
873
                QmlDebuggingLibrary::canBuild(version, &qmlDebuggingLibToolTip);
874 875 876 877
            }
        }
        m_debuggingHelperUi->qmlDebuggingLibStatus->setText(qmlDebuggingLibStatusText);
        m_debuggingHelperUi->qmlDebuggingLibStatus->setTextInteractionFlags(qmlDebuggingLibStatusTextFlags);
878
        m_debuggingHelperUi->qmlDebuggingLibStatus->setToolTip(qmlDebuggingLibToolTip);
879 880
        m_debuggingHelperUi->qmlDebuggingLibBuildButton->setEnabled(needsQmlDebuggingLib
                                                                    && canBuildQmlDebuggingLib
881 882
                                                                    && !isBuildingQmlDebuggingLib);

883
        QString qmlObserverStatusText, qmlObserverToolTip;
884
        Qt::TextInteractionFlags qmlObserverStatusTextFlags = Qt::NoTextInteraction;
885
        if (hasQmlObserver) {
886
            qmlObserverStatusText = QDir::toNativeSeparators(version->qmlObserverTool());
887
            qmlObserverStatusTextFlags = Qt::TextSelectableByMouse;
888
        }  else {
889 890 891
            if (!needsQmlDebuggingLib) {
                qmlObserverStatusText = tr("<i>Not needed.</i>");
            } else if (canBuildQmlObserver) {
892 893 894
                qmlObserverStatusText = tr("<i>Not yet built.</i>");
            } else {
                qmlObserverStatusText = tr("<i>Cannot be compiled.</i>");
895
                QmlObserverTool::canBuild(version, &qmlObserverToolTip);
896
            }
897
        }
898
        m_debuggingHelperUi->qmlObserverStatus->setText(qmlObserverStatusText);
899
        m_debuggingHelperUi->qmlObserverStatus->setTextInteractionFlags(qmlObserverStatusTextFlags);
900
        m_debuggingHelperUi->qmlObserverStatus->setToolTip(qmlObserverToolTip);
901 902
        m_debuggingHelperUi->qmlObserverBuildButton->setEnabled(canBuildQmlObserver
                                                                & !isBuildingQmlObserver);
903

904
        QList<ProjectExplorer::ToolChain*> toolchains = toolChains(currentVersion());
905
        QString selectedToolChainId = currentItem->data(0, ToolChainIdRole).toString();
906 907 908 909 910 911 912 913 914 915 916 917
        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);
        }

918 919 920
        const bool hasLog = currentItem && !currentItem->data(0, BuildLogRole).toString().isEmpty();
        m_debuggingHelperUi->showLogButton->setEnabled(hasLog);

921 922 923 924 925 926 927 928 929 930 931
        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);
932

933
        m_ui->debuggingHelperWidget->setVisible(true);
934 935 936
    }
}

dt's avatar
dt committed
937 938
// To be called if a qt version was removed or added
void QtOptionsPageWidget::updateCleanUpButton()
939
{
940
    bool hasInvalidVersion = false;
941 942
    for (int i = 0; i < m_versions.count(); ++i) {
        if (!m_versions.at(i)->isValid()) {
943
            hasInvalidVersion = true;
dt's avatar
dt committed
944
            break;
945 946
        }
    }
947
    m_ui->cleanUpButton->setEnabled(hasInvalidVersion);