qtoptionspage.cpp 32.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 <qtconcurrent/runextensions.h>
51 52

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

58
enum ModelRoles { VersionIdRole = Qt::UserRole, BuildLogRole, BuildRunningRole};
59

60 61
using namespace QtSupport;
using namespace QtSupport::Internal;
62

63 64 65 66 67
///
// QtOptionsPage
///

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

QString QtOptionsPage::id() const
{
74
    return QLatin1String(Constants::QTVERSION_SETTINGS_PAGE_ID);
75 76
}

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

QString QtOptionsPage::category() const
{
84
    return QLatin1String(Constants::QT_SETTINGS_CATEGORY);
85 86
}

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

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

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

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

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

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

121
//-----------------------------------------------------
122 123


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

137 138
    QWidget *versionInfoWidget = new QWidget();
    m_versionUi->setupUi(versionInfoWidget);
139

140 141 142
    QWidget *debuggingHelperDetailsWidget = new QWidget();
    m_debuggingHelperUi->setupUi(debuggingHelperDetailsWidget);

143
    m_ui->setupUi(this);
144 145 146 147 148

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

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

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

164
    for (int i = 0; i < m_versions.count(); ++i) {
dt's avatar
dt committed
165
        BaseQtVersion *version = m_versions.at(i);
166
        QTreeWidgetItem *item = new QTreeWidgetItem(version->isAutodetected()? autoItem : manualItem);
167
        item->setText(0, version->displayName());
168
        item->setText(1, QDir::toNativeSeparators(version->qmakeCommand()));
169
        item->setData(0, VersionIdRole, version->uniqueId());
dt's avatar
dt committed
170
        item->setIcon(0, version->isValid()? m_validVersionIcon : m_invalidVersionIcon);
171
    }
172
    m_ui->qtdirList->expandAll();
173

174
    connect(m_versionUi->nameEdit, SIGNAL(textEdited(const QString &)),
175 176
            this, SLOT(updateCurrentQtName()));

dt_'s avatar
dt_ committed
177 178 179
    connect(m_versionUi->editPathPushButton, SIGNAL(clicked()),
            this, SLOT(editPath()));

180 181 182 183 184 185 186 187
    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 *)));

188
    connect(m_debuggingHelperUi->rebuildButton, SIGNAL(clicked()),
189
            this, SLOT(buildDebuggingHelper()));
190 191 192 193
    connect(m_debuggingHelperUi->gdbHelperBuildButton, SIGNAL(clicked()),
            this, SLOT(buildGdbHelper()));
    connect(m_debuggingHelperUi->qmlDumpBuildButton, SIGNAL(clicked()),
            this, SLOT(buildQmlDump()));
194 195
    connect(m_debuggingHelperUi->qmlDebuggingLibBuildButton, SIGNAL(clicked()),
            this, SLOT(buildQmlDebuggingLibrary()));
196 197 198 199
    connect(m_debuggingHelperUi->qmlObserverBuildButton, SIGNAL(clicked()),
            this, SLOT(buildQmlObserver()));

    connect(m_debuggingHelperUi->showLogButton, SIGNAL(clicked()),
200
            this, SLOT(slotShowDebuggingBuildLog()));
201

202
    connect(m_ui->cleanUpButton, SIGNAL(clicked()), this, SLOT(cleanUpQtVersions()));
dt's avatar
dt committed
203 204
    userChangedCurrentVersion();
    updateCleanUpButton();
205

206 207
    connect(QtVersionManager::instance(), SIGNAL(dumpUpdatedFor(QString)),
            this, SLOT(qtVersionsDumpUpdated(QString)));
208 209
}

210 211 212 213 214
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)
215
        return false;
216 217 218 219 220 221 222 223
    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;
224
    const QString tooltip = m_versions.at(index)->toHtml(true);
225 226 227 228 229
    QToolTip::showText(helpEvent->globalPos(), tooltip, m_ui->qtdirList);
    helpEvent->accept();
    return true;
}

230
int QtOptionsPageWidget::currentIndex() const
231
{
232 233 234 235
    if (QTreeWidgetItem *currentItem = m_ui->qtdirList->currentItem())
        return indexForTreeItem(currentItem);
    return -1;
}
236

dt's avatar
dt committed
237
BaseQtVersion *QtOptionsPageWidget::currentVersion() const
238 239 240
{
    const int currentItemIndex = currentIndex();
    if (currentItemIndex >= 0 && currentItemIndex < m_versions.size())
241
        return m_versions.at(currentItemIndex);
242 243
    return 0;
}
244

dt's avatar
dt committed
245
static inline int findVersionById(const QList<BaseQtVersion *> &l, int id)
246 247 248
{
    const int size = l.size();
    for (int i = 0; i < size; i++)
249
        if (l.at(i)->uniqueId() == id)
250 251 252
            return i;
    return -1;
}
253

254
// Update with results of terminated helper build
255
void QtOptionsPageWidget::debuggingHelperBuildFinished(int qtVersionId, const QString &output, DebuggingHelperBuildTask::Tools tools)
256
{
257
    const int index = findVersionById(m_versions, qtVersionId);
258 259
    if (index == -1)
        return; // Oops, somebody managed to delete the version
260

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

263 264
    // Update item view
    QTreeWidgetItem *item = treeItemForIndex(index);
265 266 267 268 269
    QTC_ASSERT(item, return);
    DebuggingHelperBuildTask::Tools buildFlags
            = item->data(0, BuildRunningRole).value<DebuggingHelperBuildTask::Tools>();
    buildFlags &= ~tools;
    item->setData(0, BuildRunningRole,  QVariant::fromValue(buildFlags));
270
    item->setData(0, BuildLogRole, output);
271

272 273
    bool success = true;
    if (tools & DebuggingHelperBuildTask::GdbDebugging)
dt's avatar
dt committed
274
        success &= version->hasGdbDebuggingHelper();
275
    if (tools & DebuggingHelperBuildTask::QmlDebugging)
dt's avatar
dt committed
276
        success &= version->hasQmlDebuggingLibrary();
277
    if (tools & DebuggingHelperBuildTask::QmlDump)
dt's avatar
dt committed
278
        success &= version->hasQmlDump();
279
    if (tools & DebuggingHelperBuildTask::QmlObserver)
dt's avatar
dt committed
280
        success &= version->hasQmlObserver();
281 282 283

    // Update bottom control if the selection is still the same
    if (index == currentIndex()) {
284
        updateDebuggingHelperUi();
285
    }
286

287 288
    if (!success)
        showDebuggingBuildLog(item);
289 290
}

291 292 293
void QtOptionsPageWidget::cleanUpQtVersions()
{
    QStringList toRemove;
dt's avatar
dt committed
294
    foreach (const BaseQtVersion *v, m_versions) {
295
        if (!v->isValid() && !v->isAutodetected())
296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317
            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
318
    updateCleanUpButton();
319 320
}

321
void QtOptionsPageWidget::qtVersionsDumpUpdated(const QString &qmakeCommand)
322 323 324 325 326
{
    foreach (BaseQtVersion *version, m_versions) {
        if (version->qmakeCommand() == qmakeCommand)
            version->recheckDumper();
    }
327 328
    if (currentVersion()
            && currentVersion()->qmakeCommand() == qmakeCommand) {
329 330 331 332 333 334
        updateWidgets();
        updateDescriptionLabel();
        updateDebuggingHelperUi();
    }
}

335
void QtOptionsPageWidget::buildDebuggingHelper(DebuggingHelperBuildTask::Tools tools)
336 337 338 339 340
{
    const int index = currentIndex();
    if (index < 0)
        return;

341 342
    QTreeWidgetItem *item = treeItemForIndex(index);
    QTC_ASSERT(item, return);
343

344 345 346 347
    DebuggingHelperBuildTask::Tools buildFlags
            = item->data(0, BuildRunningRole).value<DebuggingHelperBuildTask::Tools>();
    buildFlags |= tools;
    item->setData(0, BuildRunningRole, QVariant::fromValue(buildFlags));
348

dt's avatar
dt committed
349
    BaseQtVersion *version = m_versions.at(index);
350 351 352
    if (!version)
        return;

353
    updateDebuggingHelperUi();
354

355
    // Run a debugging helper build task in the background.
356
    DebuggingHelperBuildTask *buildTask = new DebuggingHelperBuildTask(version, tools);
357 358
    // Don't open General Messages pane with errors
    buildTask->showOutputOnError(false);
359 360
    connect(buildTask, SIGNAL(finished(int,QString,DebuggingHelperBuildTask::Tools)),
            this, SLOT(debuggingHelperBuildFinished(int,QString,DebuggingHelperBuildTask::Tools)),
361
            Qt::QueuedConnection);
362
    QFuture<void> task = QtConcurrent::run(&DebuggingHelperBuildTask::run, buildTask);
363
    const QString taskName = tr("Building helpers");
364

365
    Core::ICore::instance()->progressManager()->addTask(task, taskName,
366
                                                        QLatin1String("Qt4ProjectManager::BuildHelpers"));
367
}
368 369 370 371 372 373 374 375 376 377
void QtOptionsPageWidget::buildGdbHelper()
{
    buildDebuggingHelper(DebuggingHelperBuildTask::GdbDebugging);
}

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

378 379 380 381 382
void QtOptionsPageWidget::buildQmlDebuggingLibrary()
{
    buildDebuggingHelper(DebuggingHelperBuildTask::QmlDebugging);
}

383 384
void QtOptionsPageWidget::buildQmlObserver()
{
385
    DebuggingHelperBuildTask::Tools qmlDbgTools =
Kai Koehne's avatar
Kai Koehne committed
386 387
            DebuggingHelperBuildTask::QmlObserver;
    qmlDbgTools |= DebuggingHelperBuildTask::QmlDebugging;
388
    buildDebuggingHelper(qmlDbgTools);
389
}
390

391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407
// 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)
408
{
409 410 411 412
    m_ui.log->setPlainText(text); // Show and scroll to bottom
    m_ui.log->moveCursor(QTextCursor::End);
    m_ui.log->ensureCursorVisible();
}
413

414 415 416 417 418 419 420 421 422
void QtOptionsPageWidget::slotShowDebuggingBuildLog()
{
    if (const QTreeWidgetItem *currentItem = m_ui->qtdirList->currentItem())
        showDebuggingBuildLog(currentItem);
}

void QtOptionsPageWidget::showDebuggingBuildLog(const QTreeWidgetItem *currentItem)
{
    const int currentItemIndex = indexForTreeItem(currentItem);
423 424
    if (currentItemIndex < 0)
        return;
425 426
    BuildLogDialog *dialog = new BuildLogDialog(this);
    dialog->setWindowTitle(tr("Debugging Helper Build Log for '%1'").arg(currentItem->text(0)));
427
    dialog->setText(currentItem->data(0, BuildLogRole).toString());
428
    dialog->show();
429 430
}

431
QtOptionsPageWidget::~QtOptionsPageWidget()
432 433
{
    delete m_ui;
dt's avatar
dt committed
434 435
    delete m_versionUi;
    delete m_debuggingHelperUi;
dt's avatar
dt committed
436
    delete m_configurationWidget;
437
    qDeleteAll(m_versions);
438 439
}

dt_'s avatar
dt_ committed
440
static QString filterForQmakeFileDialog()
441
{
dt's avatar
dt committed
442 443 444 445 446
    QString filter("qmake (");
    foreach (const QString &s, Utils::BuildableHelperLibrary::possibleQMakeCommands()) {
        filter += s + " ";
    }
    filter += ")";
dt_'s avatar
dt_ committed
447 448
    return filter;
}
449

dt_'s avatar
dt_ committed
450 451
void QtOptionsPageWidget::addQtDir()
{
dt's avatar
dt committed
452
    QString qtVersion = QFileDialog::getOpenFileName(this,
dt_'s avatar
dt_ committed
453 454
                                                     tr("Select a qmake executable"),
                                                     QString(), filterForQmakeFileDialog());
dt's avatar
dt committed
455 456 457 458 459
    if (qtVersion.isNull())
        return;
    if (QtVersionManager::instance()->qtVersionForQMakeBinary(qtVersion)) {
        // Already exist
    }
460

dt's avatar
dt committed
461 462 463
    BaseQtVersion *version = QtVersionFactory::createQtVersionFromQMakePath(qtVersion);
    if (version) {
        m_versions.append(version);
464

dt's avatar
dt committed
465 466 467 468 469 470 471 472 473 474
        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();
475 476
}

477
void QtOptionsPageWidget::removeQtDir()
478 479
{
    QTreeWidgetItem *item = m_ui->qtdirList->currentItem();
480
    int index = indexForTreeItem(item);
481 482 483 484 485
    if (index < 0)
        return;

    delete item;

dt's avatar
dt committed
486
    BaseQtVersion *version = m_versions.at(index);
487
    m_versions.removeAt(index);
488
    delete version;
dt's avatar
dt committed
489
    updateCleanUpButton();
490 491
}

dt_'s avatar
dt_ committed
492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524
void QtOptionsPageWidget::editPath()
{
   // TODO Here be dragons
    QString qtVersion = QFileDialog::getOpenFileName(this,
                                                     tr("Select a qmake executable"),
                                                     QString(), filterForQmakeFileDialog());
    if (qtVersion.isNull())
        return;
    BaseQtVersion *version = QtVersionFactory::createQtVersionFromQMakePath(qtVersion);
    BaseQtVersion *current = currentVersion();
    // 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());
    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);
}

525
void QtOptionsPageWidget::updateDebuggingHelperUi()
526
{
dt's avatar
dt committed
527
    BaseQtVersion *version = currentVersion();
528
    const QTreeWidgetItem *currentItem = m_ui->qtdirList->currentItem();
529

530
    if (!version || !version->isValid()) {
531
        m_ui->debuggingHelperWidget->setVisible(false);
532
    } else {
533 534 535 536 537
        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;
538

539 540 541
        const bool hasGdbHelper = !version->gdbDebuggingHelperLibrary().isEmpty();
        const bool hasQmlDumper = version->hasQmlDump();
        const bool hasQmlDebuggingLib = version->hasQmlDebuggingLibrary();
542
        const bool needsQmlDebuggingLib = version->needsQmlDebuggingLibrary();
543
        const bool hasQmlObserver = !version->qmlObserverTool().isEmpty();
544

545 546
        bool isBuildingGdbHelper = false;
        bool isBuildingQmlDumper = false;
547
        bool isBuildingQmlDebuggingLib = false;
548 549 550 551 552 553 554
        bool isBuildingQmlObserver = false;

        if (currentItem) {
            DebuggingHelperBuildTask::Tools buildingTools
                    = currentItem->data(0, BuildRunningRole).value<DebuggingHelperBuildTask::Tools>();
            isBuildingGdbHelper = buildingTools & DebuggingHelperBuildTask::GdbDebugging;
            isBuildingQmlDumper = buildingTools & DebuggingHelperBuildTask::QmlDump;
555
            isBuildingQmlDebuggingLib = buildingTools & DebuggingHelperBuildTask::QmlDebugging;
556 557 558
            isBuildingQmlObserver = buildingTools & DebuggingHelperBuildTask::QmlObserver;
        }

559 560 561 562 563 564
        // get names of tools from labels
        QStringList helperNames;
        if (hasGdbHelper)
            helperNames << m_debuggingHelperUi->gdbHelperLabel->text().remove(':');
        if (hasQmlDumper)
            helperNames << m_debuggingHelperUi->qmlDumpLabel->text().remove(':');
565 566
        if (hasQmlDebuggingLib)
            helperNames << m_debuggingHelperUi->qmlDebuggingLibLabel->text().remove(':');
567 568 569 570 571
        if (hasQmlObserver)
            helperNames << m_debuggingHelperUi->qmlObserverLabel->text().remove(':');

        QString status;
        if (helperNames.isEmpty()) {
572
            status = tr("Helpers: None available");
573
        } else {
Friedemann Kleint's avatar
Friedemann Kleint committed
574
            //: %1 is list of tool names.
575
            status = tr("Helpers: %1.").arg(helperNames.join(QLatin1String(", ")));
576 577 578 579
        }

        m_ui->debuggingHelperWidget->setSummaryText(status);

580 581
        QString gdbHelperText;
        Qt::TextInteractionFlags gdbHelperTextFlags = Qt::NoTextInteraction;
582
        if (hasGdbHelper) {
583
            gdbHelperText = QDir::toNativeSeparators(version->gdbDebuggingHelperLibrary());
584
            gdbHelperTextFlags = Qt::TextSelectableByMouse;
585
        } else {
586 587 588 589 590
            if (canBuildGdbHelper) {
                gdbHelperText =  tr("<i>Not yet built.</i>");
            } else {
                gdbHelperText =  tr("<i>Not needed.</i>");
            }
591
        }
592 593
        m_debuggingHelperUi->gdbHelperStatus->setText(gdbHelperText);
        m_debuggingHelperUi->gdbHelperStatus->setTextInteractionFlags(gdbHelperTextFlags);
594
        m_debuggingHelperUi->gdbHelperBuildButton->setEnabled(canBuildGdbHelper && !isBuildingGdbHelper);
595

596
        QString qmlDumpStatusText, qmlDumpStatusToolTip;
597
        Qt::TextInteractionFlags qmlDumpStatusTextFlags = Qt::NoTextInteraction;
598
        if (hasQmlDumper) {
599 600
            qmlDumpStatusText = QDir::toNativeSeparators(version->qmlDumpTool(false));
            const QString debugQmlDumpPath = QDir::toNativeSeparators(version->qmlDumpTool(true));
601
            if (qmlDumpStatusText != debugQmlDumpPath) {
602 603
                if (!qmlDumpStatusText.isEmpty()
                        && !debugQmlDumpPath.isEmpty())
604 605 606
                    qmlDumpStatusText += QLatin1String("\n");
                qmlDumpStatusText += debugQmlDumpPath;
            }
607
            qmlDumpStatusTextFlags = Qt::TextSelectableByMouse;
608
        } else {
609 610 611 612
            if (canBuildQmlDumper) {
                qmlDumpStatusText = tr("<i>Not yet built.</i>");
            } else {
                qmlDumpStatusText = tr("<i>Cannot be compiled.</i>");
613
                QmlDumpTool::canBuild(version, &qmlDumpStatusToolTip);
614
            }
615
        }
616
        m_debuggingHelperUi->qmlDumpStatus->setText(qmlDumpStatusText);
617
        m_debuggingHelperUi->qmlDumpStatus->setTextInteractionFlags(qmlDumpStatusTextFlags);
618
        m_debuggingHelperUi->qmlDumpStatus->setToolTip(qmlDumpStatusToolTip);
619
        m_debuggingHelperUi->qmlDumpBuildButton->setEnabled(canBuildQmlDumper & !isBuildingQmlDumper);
620

621
        QString qmlDebuggingLibStatusText, qmlDebuggingLibToolTip;
622 623 624 625 626 627 628 629 630 631
        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()) {
632
                    qmlDebuggingLibStatusText += QLatin1Char('\n');
633 634 635 636
                }
                qmlDebuggingLibStatusText += debugPath;
            }
            qmlDebuggingLibStatusTextFlags = Qt::TextSelectableByMouse;
637 638 639 640
        } else {
            if (!needsQmlDebuggingLib) {
                qmlDebuggingLibStatusText = tr("<i>Not needed.</i>");
            } else if (canBuildQmlDebuggingLib) {
641 642 643
                qmlDebuggingLibStatusText = tr("<i>Not yet built.</i>");
            } else {
                qmlDebuggingLibStatusText = tr("<i>Cannot be compiled.</i>");
644
                QmlDebuggingLibrary::canBuild(version, &qmlDebuggingLibToolTip);
645 646 647 648
            }
        }
        m_debuggingHelperUi->qmlDebuggingLibStatus->setText(qmlDebuggingLibStatusText);
        m_debuggingHelperUi->qmlDebuggingLibStatus->setTextInteractionFlags(qmlDebuggingLibStatusTextFlags);
649
        m_debuggingHelperUi->qmlDebuggingLibStatus->setToolTip(qmlDebuggingLibToolTip);
650 651
        m_debuggingHelperUi->qmlDebuggingLibBuildButton->setEnabled(needsQmlDebuggingLib
                                                                    && canBuildQmlDebuggingLib
652 653
                                                                    && !isBuildingQmlDebuggingLib);

654
        QString qmlObserverStatusText, qmlObserverToolTip;
655
        Qt::TextInteractionFlags qmlObserverStatusTextFlags = Qt::NoTextInteraction;
656
        if (hasQmlObserver) {
657
            qmlObserverStatusText = QDir::toNativeSeparators(version->qmlObserverTool());
658
            qmlObserverStatusTextFlags = Qt::TextSelectableByMouse;
659
        }  else {
660 661 662
            if (!needsQmlDebuggingLib) {
                qmlObserverStatusText = tr("<i>Not needed.</i>");
            } else if (canBuildQmlObserver) {
663 664 665
                qmlObserverStatusText = tr("<i>Not yet built.</i>");
            } else {
                qmlObserverStatusText = tr("<i>Cannot be compiled.</i>");
666
                QmlObserverTool::canBuild(version, &qmlObserverToolTip);
667
            }
668
        }
669
        m_debuggingHelperUi->qmlObserverStatus->setText(qmlObserverStatusText);
670
        m_debuggingHelperUi->qmlObserverStatus->setTextInteractionFlags(qmlObserverStatusTextFlags);
671
        m_debuggingHelperUi->qmlObserverStatus->setToolTip(qmlObserverToolTip);
672 673
        m_debuggingHelperUi->qmlObserverBuildButton->setEnabled(canBuildQmlObserver
                                                                & !isBuildingQmlObserver);
674 675 676 677

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

678 679 680 681 682 683
        m_debuggingHelperUi->rebuildButton->setEnabled((!isBuildingGdbHelper
                                                        && !isBuildingQmlDumper
                                                        && !isBuildingQmlDebuggingLib
                                                        && !isBuildingQmlObserver)
                                                       && (canBuildGdbHelper
                                                           || canBuildQmlDumper
684
                                                           || (canBuildQmlDebuggingLib && needsQmlDebuggingLib)
685
                                                           || canBuildQmlObserver));
686

687
        m_ui->debuggingHelperWidget->setVisible(true);
688 689 690
    }
}

dt's avatar
dt committed
691 692
// To be called if a qt version was removed or added
void QtOptionsPageWidget::updateCleanUpButton()
693
{
694
    bool hasInvalidVersion = false;
695 696
    for (int i = 0; i < m_versions.count(); ++i) {
        if (!m_versions.at(i)->isValid()) {
697
            hasInvalidVersion = true;
dt's avatar
dt committed
698
            break;
699 700
        }
    }
701
    m_ui->cleanUpButton->setEnabled(hasInvalidVersion);
702
}
703

dt's avatar
dt committed
704
void QtOptionsPageWidget::userChangedCurrentVersion()
con's avatar
con committed
705
{
dt's avatar
dt committed
706 707 708
    updateWidgets();
    updateDescriptionLabel();
    updateDebuggingHelperUi();
709 710
}

dt's avatar
dt committed
711
void QtOptionsPageWidget::qtVersionChanged()
712
{
713
    updateDescriptionLabel();
dt's avatar
dt committed
714
    updateDebuggingHelperUi();
715 716 717 718
}

void QtOptionsPageWidget::updateDescriptionLabel()
{
719 720 721 722 723
    QTreeWidgetItem *item = m_ui->qtdirList->currentItem();
    const BaseQtVersion *version = currentVersion();
    if (!version) {
        m_versionUi->errorLabel->setText(QString());
    } else if (version->isValid()) {
dt's avatar
dt committed
724 725
        m_versionUi->errorLabel->setText( tr("Qt version %1 for %2").arg(version->qtVersionString(),
                                                                         version->description()));
726 727
        item->setIcon(0, m_validVersionIcon);
    } else {
728
        m_versionUi->errorLabel->setText(version->invalidReason());
729 730
        item->setIcon(0, m_invalidVersionIcon);
    }
731 732
}

733
int QtOptionsPageWidget::indexForTreeItem(const QTreeWidgetItem *item) const
734 735 736
{
    if (!item || !item->parent())
        return -1;
737
    const int uniqueId = item->data(0, VersionIdRole).toInt();
738 739 740 741 742 743 744
    for (int index = 0; index < m_versions.size(); ++index) {
        if (m_versions.at(index)->uniqueId() == uniqueId)
            return index;
    }
    return -1;
}

745 746
QTreeWidgetItem *QtOptionsPageWidget::treeItemForIndex(int index) const
{
747
    const int uniqueId = m_versions.at(index)->uniqueId();
748 749 750 751
    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);
752
            if (item->data(0, VersionIdRole).toInt() == uniqueId) {
753 754 755 756 757 758 759
                return item;
            }
        }
    }
    return 0;
}

dt's avatar
dt committed
760
void QtOptionsPageWidget::versionChanged(QTreeWidgetItem *newItem, QTreeWidgetItem *old)
761
{
dt's avatar
dt committed
762 763
    Q_UNUSED(newItem)
    if (old)
764
        fixQtVersionName(indexForTreeItem(old));
dt's avatar
dt committed
765 766 767 768 769 770 771 772 773 774 775 776 777 778
    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
779
            m_configurationWidget->setEnabled(!version->isAutodetected());
dt's avatar
dt committed
780 781 782
            connect(m_configurationWidget, SIGNAL(changed()),
                    this, SLOT(qtVersionChanged()));
        }
783
    } else {
784
        m_versionUi->nameEdit->clear();
dt's avatar
dt committed
785
        m_versionUi->qmakePath->setText(QString()); // clear()
786 787
    }

dt's avatar
dt committed
788 789 790 791
    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
792
    m_versionUi->editPathPushButton->setEnabled(enabled && !isAutodetected);
793 794
}

795
void QtOptionsPageWidget::updateCurrentQtName()
796 797 798
{
    QTreeWidgetItem *currentItem = m_ui->qtdirList->currentItem();
    Q_ASSERT(currentItem);
799 800 801
    int currentItemIndex = indexForTreeItem(currentItem);
    if (currentItemIndex < 0)
        return;
802
    m_versions[currentItemIndex]->setDisplayName(m_versionUi->nameEdit->text());
803
    currentItem->setText(0, m_versions[currentItemIndex]->displayName());
804
    updateDescriptionLabel();
805 806 807
}


808
void QtOptionsPageWidget::finish()
809 810
{
    if (QTreeWidgetItem *item = m_ui->qtdirList->currentItem())
811
        fixQtVersionName(indexForTreeItem(item));
812 813 814 815 816 817
}

/* Checks that the qt version name is unique
 * and otherwise changes the name
 *
 */
818
void QtOptionsPageWidget::fixQtVersionName(int index)
819
{
820 821
    if (index < 0)
        return;
822
    int count = m_versions.count();
823
    QString name = m_versions.at(index)->displayName();
dt_'s avatar
dt_ committed
824 825
    if (name.isEmpty())
        return;
826 827
    for (int i = 0; i < count; ++i) {
        if (i != index) {
828
            if (m_versions.at(i)->displayName() == m_versions.at(index)->displayName()) {
829 830 831
                // Same name, find new name
                QRegExp regexp("^(.*)\\((\\d)\\)$");
                if (regexp.exactMatch(name)) {
Tobias Hunger's avatar
Tobias Hunger committed
832
                    // Already in Name (#) format
833 834 835 836
                    name = regexp.cap(1);
                    name += QLatin1Char('(');
                    name += QString::number(regexp.cap(2).toInt() + 1);
                    name += QLatin1Char(')');
837
                } else {
838
                    name +=  QLatin1String(" (2)");
839 840
                }
                // set new name
841
                m_versions[index]->setDisplayName(name);
842
                treeItemForIndex(index)->setText(0, name);
843 844 845 846 847 848 849 850

                // Now check again...
                fixQtVersionName(index);
            }
        }
    }
}

dt's avatar
dt committed
851
QList<BaseQtVersion *> QtOptionsPageWidget::versions() const
852
{
dt's avatar
dt committed
853
    QList<BaseQtVersion *> result;
854
    for (int i = 0; i < m_versions.count(); ++i)
dt's avatar
dt committed
855
        result.append(m_versions.at(i)->clone());
856
    return result;
857 858
}

859 860 861
QString QtOptionsPageWidget::searchKeywords() const
{
    QString rc;
862
    QLatin1Char sep(' ');
dt's avatar
dt committed
863 864 865 866 867 868 869 870 871 872 873 874 875 876
    QTextStream ts(&rc);
    ts << sep << m_versionUi->versionNameLabel->text()
       << sep << m_versionUi->pathLabel->text()
       << sep << m_debuggingHelperUi->gdbHelperLabel->text()
       << sep << m_debuggingHelperUi->qmlDumpLabel->text()
       << sep << m_debuggingHelperUi->qmlObserverLabel->text();

    // Symbian specific, could be factored out to the factory
    // checking m_configurationWidget is not enough, we want them to be a keyword
    // regardless of which qt versions configuration widget is currently active
    ts << sep << tr("S60 SDK:")
       << sep << tr("SBS v2 directory:");