qtoptionspage.cpp 34.9 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 <qtconcurrent/runextensions.h>
53 54

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

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

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

65 66 67 68 69
///
// QtOptionsPage
///

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

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

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

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

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

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

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

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

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

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

123
//-----------------------------------------------------
124 125


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

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

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

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

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

    m_ui->debuggingHelperWidget->setWidget(debuggingHelperDetailsWidget);
153

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

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

179
    connect(m_versionUi->nameEdit, SIGNAL(textEdited(const QString &)),
180 181
            this, SLOT(updateCurrentQtName()));

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

185 186 187 188 189 190 191 192
    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 *)));

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

    connect(m_debuggingHelperUi->showLogButton, SIGNAL(clicked()),
205
            this, SLOT(slotShowDebuggingBuildLog()));
206

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

211 212
    connect(QtVersionManager::instance(), SIGNAL(dumpUpdatedFor(QString)),
            this, SLOT(qtVersionsDumpUpdated(QString)));
213 214 215

    connect(ProjectExplorer::ToolChainManager::instance(), SIGNAL(toolChainsChanged()),
            this, SLOT(toolChainsUpdated()));
216 217
}

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

238
int QtOptionsPageWidget::currentIndex() const
239
{
240 241 242 243
    if (QTreeWidgetItem *currentItem = m_ui->qtdirList->currentItem())
        return indexForTreeItem(currentItem);
    return -1;
}
244

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

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

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

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

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

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

    // Update bottom control if the selection is still the same
    if (index == currentIndex()) {
292
        updateDebuggingHelperUi();
293
    }
294

295 296
    if (!success)
        showDebuggingBuildLog(item);
297 298
}

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

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

342
void QtOptionsPageWidget::qtVersionsDumpUpdated(const QString &qmakeCommand)
343 344 345 346 347
{
    foreach (BaseQtVersion *version, m_versions) {
        if (version->qmakeCommand() == qmakeCommand)
            version->recheckDumper();
    }
348 349
    if (currentVersion()
            && currentVersion()->qmakeCommand() == qmakeCommand) {
350 351 352 353 354 355
        updateWidgets();
        updateDescriptionLabel();
        updateDebuggingHelperUi();
    }
}

356 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 383 384 385 386 387 388 389 390 391
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;
    }

    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;
    } 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
392
        info.toolTip = tr("The following ABIs are currently not supported:<ul><li>%1</li></ul>")
393 394 395 396 397 398
                       .arg(missingToolChains.join(QLatin1String("</li><li>")));
        info.icon = m_warningVersionIcon;
    }
    return info;
}

399
void QtOptionsPageWidget::buildDebuggingHelper(DebuggingHelperBuildTask::Tools tools)
400 401 402 403 404
{
    const int index = currentIndex();
    if (index < 0)
        return;

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

408 409 410 411
    DebuggingHelperBuildTask::Tools buildFlags
            = item->data(0, BuildRunningRole).value<DebuggingHelperBuildTask::Tools>();
    buildFlags |= tools;
    item->setData(0, BuildRunningRole, QVariant::fromValue(buildFlags));
412

dt's avatar
dt committed
413
    BaseQtVersion *version = m_versions.at(index);
414 415 416
    if (!version)
        return;

417
    updateDebuggingHelperUi();
418

419
    // Run a debugging helper build task in the background.
420
    DebuggingHelperBuildTask *buildTask = new DebuggingHelperBuildTask(version, tools);
421 422
    // Don't open General Messages pane with errors
    buildTask->showOutputOnError(false);
423 424
    connect(buildTask, SIGNAL(finished(int,QString,DebuggingHelperBuildTask::Tools)),
            this, SLOT(debuggingHelperBuildFinished(int,QString,DebuggingHelperBuildTask::Tools)),
425
            Qt::QueuedConnection);
426
    QFuture<void> task = QtConcurrent::run(&DebuggingHelperBuildTask::run, buildTask);
427
    const QString taskName = tr("Building helpers");
428

429
    Core::ICore::instance()->progressManager()->addTask(task, taskName,
430
                                                        QLatin1String("Qt4ProjectManager::BuildHelpers"));
431
}
432 433 434 435 436 437 438 439 440 441
void QtOptionsPageWidget::buildGdbHelper()
{
    buildDebuggingHelper(DebuggingHelperBuildTask::GdbDebugging);
}

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

442 443 444 445 446
void QtOptionsPageWidget::buildQmlDebuggingLibrary()
{
    buildDebuggingHelper(DebuggingHelperBuildTask::QmlDebugging);
}

447 448
void QtOptionsPageWidget::buildQmlObserver()
{
449
    DebuggingHelperBuildTask::Tools qmlDbgTools =
Kai Koehne's avatar
Kai Koehne committed
450 451
            DebuggingHelperBuildTask::QmlObserver;
    qmlDbgTools |= DebuggingHelperBuildTask::QmlDebugging;
452
    buildDebuggingHelper(qmlDbgTools);
453
}
454

455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471
// 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)
472
{
473 474 475 476
    m_ui.log->setPlainText(text); // Show and scroll to bottom
    m_ui.log->moveCursor(QTextCursor::End);
    m_ui.log->ensureCursorVisible();
}
477

478 479 480 481 482 483 484 485 486
void QtOptionsPageWidget::slotShowDebuggingBuildLog()
{
    if (const QTreeWidgetItem *currentItem = m_ui->qtdirList->currentItem())
        showDebuggingBuildLog(currentItem);
}

void QtOptionsPageWidget::showDebuggingBuildLog(const QTreeWidgetItem *currentItem)
{
    const int currentItemIndex = indexForTreeItem(currentItem);
487 488
    if (currentItemIndex < 0)
        return;
489
    BuildLogDialog *dialog = new BuildLogDialog(this->window());
490
    dialog->setWindowTitle(tr("Debugging Helper Build Log for '%1'").arg(currentItem->text(0)));
491
    dialog->setText(currentItem->data(0, BuildLogRole).toString());
492
    dialog->show();
493 494
}

495
QtOptionsPageWidget::~QtOptionsPageWidget()
496 497
{
    delete m_ui;
dt's avatar
dt committed
498 499
    delete m_versionUi;
    delete m_debuggingHelperUi;
dt's avatar
dt committed
500
    delete m_configurationWidget;
501
    qDeleteAll(m_versions);
502 503
}

dt_'s avatar
dt_ committed
504
static QString filterForQmakeFileDialog()
505
{
dt's avatar
dt committed
506 507
    QString filter("qmake (");
    foreach (const QString &s, Utils::BuildableHelperLibrary::possibleQMakeCommands()) {
508 509 510 511 512
#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
513
    }
514
    filter += QLatin1Char(')');
dt_'s avatar
dt_ committed
515 516
    return filter;
}
517

dt_'s avatar
dt_ committed
518 519
void QtOptionsPageWidget::addQtDir()
{
dt's avatar
dt committed
520
    QString qtVersion = QFileDialog::getOpenFileName(this,
dt_'s avatar
dt_ committed
521 522
                                                     tr("Select a qmake executable"),
                                                     QString(), filterForQmakeFileDialog());
dt's avatar
dt committed
523 524 525 526 527
    if (qtVersion.isNull())
        return;
    if (QtVersionManager::instance()->qtVersionForQMakeBinary(qtVersion)) {
        // Already exist
    }
528

dt's avatar
dt committed
529 530 531
    BaseQtVersion *version = QtVersionFactory::createQtVersionFromQMakePath(qtVersion);
    if (version) {
        m_versions.append(version);
532

dt's avatar
dt committed
533 534 535 536 537 538 539 540 541 542
        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());
        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();
543 544
}

545
void QtOptionsPageWidget::removeQtDir()
546 547
{
    QTreeWidgetItem *item = m_ui->qtdirList->currentItem();
548
    int index = indexForTreeItem(item);
549 550 551 552 553
    if (index < 0)
        return;

    delete item;

dt's avatar
dt committed
554
    BaseQtVersion *version = m_versions.at(index);
555
    m_versions.removeAt(index);
556
    delete version;
dt's avatar
dt committed
557
    updateCleanUpButton();
558 559
}

dt_'s avatar
dt_ committed
560 561
void QtOptionsPageWidget::editPath()
{
562 563
    BaseQtVersion *current = currentVersion();
    QString dir = QFileInfo(currentVersion()->qmakeCommand()).absolutePath();
dt_'s avatar
dt_ committed
564 565
    QString qtVersion = QFileDialog::getOpenFileName(this,
                                                     tr("Select a qmake executable"),
566
                                                     dir, filterForQmakeFileDialog());
dt_'s avatar
dt_ committed
567 568 569 570 571 572 573 574 575 576 577 578 579 580
    if (qtVersion.isNull())
        return;
    BaseQtVersion *version = QtVersionFactory::createQtVersionFromQMakePath(qtVersion);
    // 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
581 582
    if (current->displayName() != current->defaultDisplayName(current->qtVersionString(), current->qmakeCommand()))
        version->setDisplayName(current->displayName());
dt_'s avatar
dt_ committed
583 584 585 586 587 588 589 590 591 592 593 594
    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());
    item->setIcon(0, version->isValid()? m_validVersionIcon : m_invalidVersionIcon);
}

595
void QtOptionsPageWidget::updateDebuggingHelperUi()
596
{
dt's avatar
dt committed
597
    BaseQtVersion *version = currentVersion();
598
    const QTreeWidgetItem *currentItem = m_ui->qtdirList->currentItem();
599

600
    if (!version || !version->isValid()) {
601
        m_ui->debuggingHelperWidget->setVisible(false);
602
    } else {
603 604 605 606 607
        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;
608

609 610 611
        const bool hasGdbHelper = !version->gdbDebuggingHelperLibrary().isEmpty();
        const bool hasQmlDumper = version->hasQmlDump();
        const bool hasQmlDebuggingLib = version->hasQmlDebuggingLibrary();
612
        const bool needsQmlDebuggingLib = version->needsQmlDebuggingLibrary();
613
        const bool hasQmlObserver = !version->qmlObserverTool().isEmpty();
614

615 616
        bool isBuildingGdbHelper = false;
        bool isBuildingQmlDumper = false;
617
        bool isBuildingQmlDebuggingLib = false;
618 619 620 621 622 623 624
        bool isBuildingQmlObserver = false;

        if (currentItem) {
            DebuggingHelperBuildTask::Tools buildingTools
                    = currentItem->data(0, BuildRunningRole).value<DebuggingHelperBuildTask::Tools>();
            isBuildingGdbHelper = buildingTools & DebuggingHelperBuildTask::GdbDebugging;
            isBuildingQmlDumper = buildingTools & DebuggingHelperBuildTask::QmlDump;
625
            isBuildingQmlDebuggingLib = buildingTools & DebuggingHelperBuildTask::QmlDebugging;
626 627 628
            isBuildingQmlObserver = buildingTools & DebuggingHelperBuildTask::QmlObserver;
        }

629 630 631 632 633 634
        // get names of tools from labels
        QStringList helperNames;
        if (hasGdbHelper)
            helperNames << m_debuggingHelperUi->gdbHelperLabel->text().remove(':');
        if (hasQmlDumper)
            helperNames << m_debuggingHelperUi->qmlDumpLabel->text().remove(':');
635 636
        if (hasQmlDebuggingLib)
            helperNames << m_debuggingHelperUi->qmlDebuggingLibLabel->text().remove(':');
637 638 639 640 641
        if (hasQmlObserver)
            helperNames << m_debuggingHelperUi->qmlObserverLabel->text().remove(':');

        QString status;
        if (helperNames.isEmpty()) {
642
            status = tr("Helpers: None available");
643
        } else {
Friedemann Kleint's avatar
Friedemann Kleint committed
644
            //: %1 is list of tool names.
645
            status = tr("Helpers: %1.").arg(helperNames.join(QLatin1String(", ")));
646 647 648 649
        }

        m_ui->debuggingHelperWidget->setSummaryText(status);

650 651
        QString gdbHelperText;
        Qt::TextInteractionFlags gdbHelperTextFlags = Qt::NoTextInteraction;
652
        if (hasGdbHelper) {
653
            gdbHelperText = QDir::toNativeSeparators(version->gdbDebuggingHelperLibrary());
654
            gdbHelperTextFlags = Qt::TextSelectableByMouse;
655
        } else {
656 657 658 659 660
            if (canBuildGdbHelper) {
                gdbHelperText =  tr("<i>Not yet built.</i>");
            } else {
                gdbHelperText =  tr("<i>Not needed.</i>");
            }
661
        }
662 663
        m_debuggingHelperUi->gdbHelperStatus->setText(gdbHelperText);
        m_debuggingHelperUi->gdbHelperStatus->setTextInteractionFlags(gdbHelperTextFlags);
664
        m_debuggingHelperUi->gdbHelperBuildButton->setEnabled(canBuildGdbHelper && !isBuildingGdbHelper);
665

666
        QString qmlDumpStatusText, qmlDumpStatusToolTip;
667
        Qt::TextInteractionFlags qmlDumpStatusTextFlags = Qt::NoTextInteraction;
668
        if (hasQmlDumper) {
669 670
            qmlDumpStatusText = QDir::toNativeSeparators(version->qmlDumpTool(false));
            const QString debugQmlDumpPath = QDir::toNativeSeparators(version->qmlDumpTool(true));
671
            if (qmlDumpStatusText != debugQmlDumpPath) {
672 673
                if (!qmlDumpStatusText.isEmpty()
                        && !debugQmlDumpPath.isEmpty())
674 675 676
                    qmlDumpStatusText += QLatin1String("\n");
                qmlDumpStatusText += debugQmlDumpPath;
            }
677
            qmlDumpStatusTextFlags = Qt::TextSelectableByMouse;
678
        } else {
679 680 681 682
            if (canBuildQmlDumper) {
                qmlDumpStatusText = tr("<i>Not yet built.</i>");
            } else {
                qmlDumpStatusText = tr("<i>Cannot be compiled.</i>");
683
                QmlDumpTool::canBuild(version, &qmlDumpStatusToolTip);
684
            }
685
        }
686
        m_debuggingHelperUi->qmlDumpStatus->setText(qmlDumpStatusText);
687
        m_debuggingHelperUi->qmlDumpStatus->setTextInteractionFlags(qmlDumpStatusTextFlags);
688
        m_debuggingHelperUi->qmlDumpStatus->setToolTip(qmlDumpStatusToolTip);
689
        m_debuggingHelperUi->qmlDumpBuildButton->setEnabled(canBuildQmlDumper & !isBuildingQmlDumper);
690

691
        QString qmlDebuggingLibStatusText, qmlDebuggingLibToolTip;
692 693 694 695 696 697 698 699 700 701
        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()) {
702
                    qmlDebuggingLibStatusText += QLatin1Char('\n');
703 704 705 706
                }
                qmlDebuggingLibStatusText += debugPath;
            }
            qmlDebuggingLibStatusTextFlags = Qt::TextSelectableByMouse;
707 708 709 710
        } else {
            if (!needsQmlDebuggingLib) {
                qmlDebuggingLibStatusText = tr("<i>Not needed.</i>");
            } else if (canBuildQmlDebuggingLib) {
711 712 713
                qmlDebuggingLibStatusText = tr("<i>Not yet built.</i>");
            } else {
                qmlDebuggingLibStatusText = tr("<i>Cannot be compiled.</i>");
714
                QmlDebuggingLibrary::canBuild(version, &qmlDebuggingLibToolTip);
715 716 717 718
            }
        }
        m_debuggingHelperUi->qmlDebuggingLibStatus->setText(qmlDebuggingLibStatusText);
        m_debuggingHelperUi->qmlDebuggingLibStatus->setTextInteractionFlags(qmlDebuggingLibStatusTextFlags);
719
        m_debuggingHelperUi->qmlDebuggingLibStatus->setToolTip(qmlDebuggingLibToolTip);
720 721
        m_debuggingHelperUi->qmlDebuggingLibBuildButton->setEnabled(needsQmlDebuggingLib
                                                                    && canBuildQmlDebuggingLib
722 723
                                                                    && !isBuildingQmlDebuggingLib);

724
        QString qmlObserverStatusText, qmlObserverToolTip;
725
        Qt::TextInteractionFlags qmlObserverStatusTextFlags = Qt::NoTextInteraction;
726
        if (hasQmlObserver) {
727
            qmlObserverStatusText = QDir::toNativeSeparators(version->qmlObserverTool());
728
            qmlObserverStatusTextFlags = Qt::TextSelectableByMouse;
729
        }  else {
730 731 732
            if (!needsQmlDebuggingLib) {
                qmlObserverStatusText = tr("<i>Not needed.</i>");
            } else if (canBuildQmlObserver) {
733 734 735
                qmlObserverStatusText = tr("<i>Not yet built.</i>");
            } else {
                qmlObserverStatusText = tr("<i>Cannot be compiled.</i>");
736
                QmlObserverTool::canBuild(version, &qmlObserverToolTip);
737
            }
738
        }
739
        m_debuggingHelperUi->qmlObserverStatus->setText(qmlObserverStatusText);
740
        m_debuggingHelperUi->qmlObserverStatus->setTextInteractionFlags(qmlObserverStatusTextFlags);
741
        m_debuggingHelperUi->qmlObserverStatus->setToolTip(qmlObserverToolTip);
742 743
        m_debuggingHelperUi->qmlObserverBuildButton->setEnabled(canBuildQmlObserver
                                                                & !isBuildingQmlObserver);
744 745 746 747

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

748 749 750 751 752 753
        m_debuggingHelperUi->rebuildButton->setEnabled((!isBuildingGdbHelper
                                                        && !isBuildingQmlDumper
                                                        && !isBuildingQmlDebuggingLib
                                                        && !isBuildingQmlObserver)
                                                       && (canBuildGdbHelper
                                                           || canBuildQmlDumper
754
                                                           || (canBuildQmlDebuggingLib && needsQmlDebuggingLib)
755
                                                           || canBuildQmlObserver));
756

757
        m_ui->debuggingHelperWidget->setVisible(true);
758 759 760
    }
}

dt's avatar
dt committed
761 762
// To be called if a qt version was removed or added
void QtOptionsPageWidget::updateCleanUpButton()
763
{
764
    bool hasInvalidVersion = false;
765 766
    for (int i = 0; i < m_versions.count(); ++i) {
        if (!m_versions.at(i)->isValid()) {
767
            hasInvalidVersion = true;
dt's avatar
dt committed
768
            break;
769 770
        }
    }
771
    m_ui->cleanUpButton->setEnabled(hasInvalidVersion);
772
}
773

dt's avatar
dt committed
774
void QtOptionsPageWidget::userChangedCurrentVersion()
con's avatar
con committed
775
{
dt's avatar
dt committed
776 777 778
    updateWidgets();
    updateDescriptionLabel();
    updateDebuggingHelperUi();
779 780
}

dt's avatar
dt committed
781
void QtOptionsPageWidget::qtVersionChanged()
782
{
783
    updateDescriptionLabel();
dt's avatar
dt committed
784
    updateDebuggingHelperUi();
785 786 787 788
}

void QtOptionsPageWidget::updateDescriptionLabel()
{
789
    QTreeWidgetItem *item = m_ui->qtdirList->currentItem();
790 791 792 793 794
    const ValidityInfo info = validInformation(currentVersion());
    m_versionUi->errorLabel->setText(info.message);
    m_versionUi->errorLabel->setToolTip(info.toolTip);
    if (item)
        item->setIcon(0, info.icon);
795 796
}

797
int QtOptionsPageWidget::indexForTreeItem(const QTreeWidgetItem *item) const
798 799 800
{
    if (!item || !item->parent())
        return -1;
801
    const int uniqueId = item->data(0, VersionIdRole).toInt();
802 803 804 805 806 807 808
    for (int index = 0; index < m_versions.size(); ++index) {
        if (m_versions.at(index)->uniqueId() == uniqueId)
            return index;
    }
    return -1;
}

809 810
QTreeWidgetItem *QtOptionsPageWidget::treeItemForIndex(int index) const
{
811
    const int uniqueId = m_versions.at(index)->uniqueId();
812 813 814 815
    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);
816
            if (item->data(0, VersionIdRole).toInt() == uniqueId) {
817 818 819 820 821 822 823
                return item;
            }
        }
    }
    return 0;
}

dt's avatar
dt committed
824
void QtOptionsPageWidget::versionChanged(QTreeWidgetItem *newItem, QTreeWidgetItem *old)
825
{
dt's avatar
dt committed
826 827
    Q_UNUSED(newItem)
    if (old)
828
        fixQtVersionName(indexForTreeItem(old));
dt's avatar
dt committed
829 830 831 832 833 834 835 836 837 838 839 840 841 842
    userChangedCurrentVersion();
}

void QtOptionsPageWidget::updateWidgets()
{
    delete m_configurationWidget;
    m_configurationWidget = 0;
    BaseQtVersion *version = currentVersion();
    if (version) {
        m_versionUi->nameEdit->setText(version->displayName());
        m_versionUi->qmakePath->setText(QDir::toNativeSeparators(version->qmakeCommand()));
        m_configurationWidget = version->createConfigurationWidget();
        if (m_configurationWidget) {
            m_versionUi->formLayout->addRow(m_configurationWidget);
dt's avatar
dt committed
843
            m_configurationWidget->setEnabled(!version->isAutodetected());
dt's avatar
dt committed
844 845 846
            connect(m_configurationWidget, SIGNAL(changed()),
                    this, SLOT(qtVersionChanged()));
        }
847
    } else {
848
        m_versionUi->nameEdit->clear();
dt's avatar
dt committed
849
        m_versionUi->qmakePath->setText(QString()); // clear()
850 851
    }

dt's avatar
dt committed
852 853 854 855
    const bool enabled = version != 0;
    const bool isAutodetected = enabled && version->isAutodetected();
    m_ui->delButton->setEnabled(enabled && !isAutodetected);
    m_versionUi->nameEdit->setEnabled(enabled && !isAutodetected);
dt_'s avatar
dt_ committed
856
    m_versionUi->editPathPushButton->setEnabled(enabled && !isAutodetected);
857 858
}

859
void QtOptionsPageWidget::updateCurrentQtName()
860 861 862
{
    QTreeWidgetItem *currentItem = m_ui->qtdirList->currentItem();
    Q_ASSERT(currentItem);
863 864 865
    int currentItemIndex = indexForTreeItem(currentItem);
    if (currentItemIndex < 0)
        return;
866
    m_versions[currentItemIndex]->setDisplayName(m_versionUi->nameEdit->text());
867
    currentItem->setText(0, m_versions[currentItemIndex]->displayName());
868
    updateDescriptionLabel();
869 870 871
}


872
void QtOptionsPageWidget::finish()
873 874
{
    if (QTreeWidgetItem *item = m_ui->qtdirList->currentItem())
875
        fixQtVersionName(indexForTreeItem(item));
876 877 878 879 880 881
}

/* Checks that the qt version name is unique
 * and otherwise changes the name
 *
 */
882
void QtOptionsPageWidget::fixQtVersionName(int index)
883
{
884 885
    if (index < 0)
        return;
886
    int count = m_versions.count();
887
    QString name = m_versions.at(index)->displayName();
dt_'s avatar
dt_ committed
888 889
    if (name.isEmpty())
        return;