qtoptionspage.cpp 42.2 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
Tobias Hunger's avatar
Tobias Hunger committed
2
**
3
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
hjk's avatar
hjk committed
4
** Contact: http://www.qt-project.org/legal
Tobias Hunger's avatar
Tobias Hunger committed
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
Tobias Hunger's avatar
Tobias Hunger committed
7
**
hjk's avatar
hjk committed
8 9 10 11 12 13 14
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
Tobias Hunger's avatar
Tobias Hunger committed
15 16
**
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17 18 19 20 21 22 23 24 25
** Alternatively, 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.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
con's avatar
con committed
26 27
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
28
****************************************************************************/
Tobias Hunger's avatar
Tobias Hunger committed
29

30
#include "qtoptionspage.h"
31
#include "qtconfigwidget.h"
32 33
#include "ui_showbuildlog.h"
#include "ui_qtversionmanager.h"
34 35
#include "ui_qtversioninfo.h"
#include "ui_debugginghelper.h"
36
#include "qtsupportconstants.h"
37
#include "qtversionmanager.h"
dt's avatar
dt committed
38
#include "qtversionfactory.h"
39 40 41
#include "qmldumptool.h"
#include "qmldebugginglibrary.h"
#include "qmlobservertool.h"
42 43 44

#include <coreplugin/icore.h>
#include <coreplugin/progressmanager/progressmanager.h>
45
#include <projectexplorer/toolchainmanager.h>
46
#include <projectexplorer/projectexplorerconstants.h>
47
#include <utils/hostosinfo.h>
hjk's avatar
hjk committed
48 49
#include <utils/pathchooser.h>
#include <utils/qtcassert.h>
50
#include <utils/runextensions.h>
51

52 53 54
#include <QDir>
#include <QMessageBox>
#include <QFileDialog>
55 56
#include <QTextBrowser>
#include <QDesktopServices>
57

hjk's avatar
hjk committed
58
using namespace ProjectExplorer;
59
using namespace Utils;
60

hjk's avatar
hjk committed
61 62 63 64 65
namespace QtSupport {
namespace Internal {

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

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

QtOptionsPage::QtOptionsPage()
dt's avatar
dt committed
71
    : m_widget(0)
72
{
hjk's avatar
hjk committed
73
    setId(Constants::QTVERSION_SETTINGS_PAGE_ID);
74
    setDisplayName(QCoreApplication::translate("Qt4ProjectManager", Constants::QTVERSION_SETTINGS_PAGE_NAME));
hjk's avatar
hjk committed
75
    setCategory(ProjectExplorer::Constants::PROJECTEXPLORER_SETTINGS_CATEGORY);
76 77 78
    setDisplayCategory(QCoreApplication::translate("ProjectExplorer",
        ProjectExplorer::Constants::PROJECTEXPLORER_SETTINGS_TR_CATEGORY));
    setCategoryIcon(QLatin1String(ProjectExplorer::Constants::PROJECTEXPLORER_SETTINGS_CATEGORY_ICON));
79 80
}

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

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

95
    m_widget->apply();
96 97
}

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

103
//-----------------------------------------------------
104 105


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

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

126
    m_ui->setupUi(this);
127

128 129 130 131
    m_infoBrowser->setOpenLinks(false);
    m_infoBrowser->setTextInteractionFlags(Qt::TextBrowserInteraction);
    connect(m_infoBrowser, SIGNAL(anchorClicked(QUrl)), this, SLOT(infoAnchorClicked(QUrl)));
    m_ui->infoWidget->setWidget(m_infoBrowser);
132
    connect(m_ui->infoWidget, SIGNAL(expanded(bool)),
133
            this, SLOT(setInfoWidgetVisibility()));
134

135
    m_ui->versionInfoWidget->setWidget(versionInfoWidget);
hjk's avatar
hjk committed
136
    m_ui->versionInfoWidget->setState(DetailsWidget::NoSummary);
137 138

    m_ui->debuggingHelperWidget->setWidget(debuggingHelperDetailsWidget);
139
    connect(m_ui->debuggingHelperWidget, SIGNAL(expanded(bool)),
140
            this, SLOT(setInfoWidgetVisibility()));
141

142 143
    // setup parent items for auto-detected and manual versions
    m_ui->qtdirList->header()->setResizeMode(QHeaderView::ResizeToContents);
dt_'s avatar
dt_ committed
144 145
    m_ui->qtdirList->header()->setStretchLastSection(false);
    m_ui->qtdirList->setTextElideMode(Qt::ElideNone);
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
    m_autoItem = new QTreeWidgetItem(m_ui->qtdirList);
    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>());
161 162

    m_ui->qtdirList->expandAll();
163

Robert Loehning's avatar
Robert Loehning committed
164
    connect(m_versionUi->nameEdit, SIGNAL(textEdited(QString)),
165 166
            this, SLOT(updateCurrentQtName()));

dt_'s avatar
dt_ committed
167 168 169
    connect(m_versionUi->editPathPushButton, SIGNAL(clicked()),
            this, SLOT(editPath()));

170 171 172 173 174
    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
175 176
    connect(m_ui->qtdirList, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)),
            this, SLOT(versionChanged(QTreeWidgetItem*,QTreeWidgetItem*)));
177

178
    connect(m_debuggingHelperUi->rebuildButton, SIGNAL(clicked()),
179
            this, SLOT(buildDebuggingHelper()));
180 181 182 183
    connect(m_debuggingHelperUi->gdbHelperBuildButton, SIGNAL(clicked()),
            this, SLOT(buildGdbHelper()));
    connect(m_debuggingHelperUi->qmlDumpBuildButton, SIGNAL(clicked()),
            this, SLOT(buildQmlDump()));
184 185
    connect(m_debuggingHelperUi->qmlDebuggingLibBuildButton, SIGNAL(clicked()),
            this, SLOT(buildQmlDebuggingLibrary()));
186 187 188 189
    connect(m_debuggingHelperUi->qmlObserverBuildButton, SIGNAL(clicked()),
            this, SLOT(buildQmlObserver()));

    connect(m_debuggingHelperUi->showLogButton, SIGNAL(clicked()),
190
            this, SLOT(slotShowDebuggingBuildLog()));
191 192
    connect(m_debuggingHelperUi->toolChainComboBox, SIGNAL(activated(int)),
            this, SLOT(selectedToolChainChanged(int)));
193

194
    connect(m_ui->cleanUpButton, SIGNAL(clicked()), this, SLOT(cleanUpQtVersions()));
dt's avatar
dt committed
195 196
    userChangedCurrentVersion();
    updateCleanUpButton();
197

198 199
    connect(QtVersionManager::instance(), SIGNAL(dumpUpdatedFor(Utils::FileName)),
            this, SLOT(qtVersionsDumpUpdated(Utils::FileName)));
200

201 202 203
    connect(QtVersionManager::instance(), SIGNAL(qtVersionsChanged(QList<int>,QList<int>,QList<int>)),
            this, SLOT(updateQtVersions(QList<int>,QList<int>,QList<int>)));

204 205
    connect(ProjectExplorer::ToolChainManager::instance(), SIGNAL(toolChainsChanged()),
            this, SLOT(toolChainsUpdated()));
206 207
}

208
int QtOptionsPageWidget::currentIndex() const
209
{
210 211 212 213
    if (QTreeWidgetItem *currentItem = m_ui->qtdirList->currentItem())
        return indexForTreeItem(currentItem);
    return -1;
}
214

dt's avatar
dt committed
215
BaseQtVersion *QtOptionsPageWidget::currentVersion() const
216 217 218
{
    const int currentItemIndex = currentIndex();
    if (currentItemIndex >= 0 && currentItemIndex < m_versions.size())
219
        return m_versions.at(currentItemIndex);
220 221
    return 0;
}
222

dt's avatar
dt committed
223
static inline int findVersionById(const QList<BaseQtVersion *> &l, int id)
224 225 226
{
    const int size = l.size();
    for (int i = 0; i < size; i++)
227
        if (l.at(i)->uniqueId() == id)
228 229 230
            return i;
    return -1;
}
231

232
// Update with results of terminated helper build
233
void QtOptionsPageWidget::debuggingHelperBuildFinished(int qtVersionId, const QString &output, DebuggingHelperBuildTask::Tools tools)
234
{
235
    const int index = findVersionById(m_versions, qtVersionId);
236 237
    if (index == -1)
        return; // Oops, somebody managed to delete the version
238

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

241 242
    // Update item view
    QTreeWidgetItem *item = treeItemForIndex(index);
243 244 245 246 247
    QTC_ASSERT(item, return);
    DebuggingHelperBuildTask::Tools buildFlags
            = item->data(0, BuildRunningRole).value<DebuggingHelperBuildTask::Tools>();
    buildFlags &= ~tools;
    item->setData(0, BuildRunningRole,  QVariant::fromValue(buildFlags));
248
    item->setData(0, BuildLogRole, output);
249

250 251
    bool success = true;
    if (tools & DebuggingHelperBuildTask::GdbDebugging)
dt's avatar
dt committed
252
        success &= version->hasGdbDebuggingHelper();
253
    if (tools & DebuggingHelperBuildTask::QmlDebugging)
dt's avatar
dt committed
254
        success &= version->hasQmlDebuggingLibrary();
255
    if (tools & DebuggingHelperBuildTask::QmlDump)
dt's avatar
dt committed
256
        success &= version->hasQmlDump();
257
    if (tools & DebuggingHelperBuildTask::QmlObserver)
dt's avatar
dt committed
258
        success &= version->hasQmlObserver();
259

260 261
    if (!success)
        showDebuggingBuildLog(item);
262 263

    updateDebuggingHelperUi();
264 265
}

266 267 268
void QtOptionsPageWidget::cleanUpQtVersions()
{
    QStringList toRemove;
dt's avatar
dt committed
269
    foreach (const BaseQtVersion *v, m_versions) {
270
        if (!v->isValid())
271 272 273 274 275 276
            toRemove.append(v->displayName());
    }

    if (toRemove.isEmpty())
        return;

Friedemann Kleint's avatar
Friedemann Kleint committed
277
    if (QMessageBox::warning(0, tr("Remove Invalid Qt Versions"),
278 279 280 281 282 283 284 285 286 287 288 289 290 291 292
                             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
293
    updateCleanUpButton();
294 295
}

296 297 298 299
void QtOptionsPageWidget::toolChainsUpdated()
{
    for (int i = 0; i < m_versions.count(); ++i) {
        QTreeWidgetItem *item = treeItemForIndex(i);
300
        if (item == m_ui->qtdirList->currentItem()) {
301
            updateDescriptionLabel();
302 303
            updateDebuggingHelperUi();
        } else {
304 305 306 307 308 309
            const ValidityInfo info = validInformation(m_versions.at(i));
            item->setIcon(0, info.icon);
        }
    }
}

310 311 312 313 314 315 316 317 318 319 320 321 322 323
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);
}

hjk's avatar
hjk committed
324
void QtOptionsPageWidget::qtVersionsDumpUpdated(const FileName &qmakeCommand)
325 326 327 328 329
{
    foreach (BaseQtVersion *version, m_versions) {
        if (version->qmakeCommand() == qmakeCommand)
            version->recheckDumper();
    }
330 331
    if (currentVersion()
            && currentVersion()->qmakeCommand() == qmakeCommand) {
332 333 334 335 336 337
        updateWidgets();
        updateDescriptionLabel();
        updateDebuggingHelperUi();
    }
}

338
void QtOptionsPageWidget::setInfoWidgetVisibility()
339
{
340 341 342 343
    m_ui->versionInfoWidget->setVisible((m_ui->infoWidget->state() == DetailsWidget::Collapsed)
                                        && (m_ui->debuggingHelperWidget->state() == DetailsWidget::Collapsed));
    m_ui->infoWidget->setVisible(m_ui->debuggingHelperWidget->state() == DetailsWidget::Collapsed);
    m_ui->debuggingHelperWidget->setVisible(m_ui->infoWidget->state() == DetailsWidget::Collapsed);
344 345
}

346 347 348 349 350
void QtOptionsPageWidget::infoAnchorClicked(const QUrl &url)
{
    QDesktopServices::openUrl(url);
}

351 352 353 354 355 356 357 358
QtOptionsPageWidget::ValidityInfo QtOptionsPageWidget::validInformation(const BaseQtVersion *version)
{
    ValidityInfo info;
    info.icon = m_validVersionIcon;

    if (!version)
        return info;

359
    info.description = tr("Qt version %1 for %2").arg(version->qtVersionString(), version->description());
360 361 362 363 364 365 366 367 368
    if (!version->isValid()) {
        info.icon = m_invalidVersionIcon;
        info.message = version->invalidReason();
        return info;
    }

    // Do we have tool chain issues?
    QStringList missingToolChains;
    int abiCount = 0;
hjk's avatar
hjk committed
369 370 371
    foreach (const Abi &abi, version->qtAbis()) {
        if (ToolChainManager::findToolChains(abi).isEmpty())
            missingToolChains.append(abi.toString());
372 373 374
        ++abiCount;
    }

375
    bool useable = true;
376
    QStringList warnings;
377 378 379
    if (!missingToolChains.isEmpty()) {
        if (missingToolChains.count() == abiCount) {
            // Yes, this Qt version can't be used at all!
380
            info.message = tr("No compiler can produce code for this Qt version. Please define one or more compilers.");
381 382 383 384
            info.icon = m_invalidVersionIcon;
            useable = false;
        } else {
            // Yes, some ABIs are unsupported
385
            warnings << tr("Not all possible target environments can be supported due to missing compilers.");
386 387 388 389
            info.toolTip = tr("The following ABIs are currently not supported:<ul><li>%1</li></ul>")
                    .arg(missingToolChains.join(QLatin1String("</li><li>")));
            info.icon = m_warningVersionIcon;
        }
390
    }
391 392

    if (useable) {
393 394 395
        warnings += version->warningReason();
        if (!warnings.isEmpty()) {
            info.message = warnings.join(QLatin1String("\n"));
396 397 398 399
            info.icon = m_warningVersionIcon;
        }
    }

400 401 402
    return info;
}

hjk's avatar
hjk committed
403
QList<ToolChain*> QtOptionsPageWidget::toolChains(const BaseQtVersion *version)
404
{
hjk's avatar
hjk committed
405
    QHash<QString,ToolChain*> toolChains;
406 407 408
    if (!version)
        return toolChains.values();

hjk's avatar
hjk committed
409 410
    foreach (const Abi &a, version->qtAbis())
        foreach (ToolChain *tc, ToolChainManager::findToolChains(a))
411 412 413 414 415
            toolChains.insert(tc->id(), tc);

    return toolChains.values();
}

416 417
QString QtOptionsPageWidget::defaultToolChainId(const BaseQtVersion *version)
{
hjk's avatar
hjk committed
418
    QList<ToolChain*> possibleToolChains = toolChains(version);
419 420 421 422 423
    if (!possibleToolChains.isEmpty())
        return possibleToolChains.first()->id();
    return QString();
}

424
void QtOptionsPageWidget::buildDebuggingHelper(DebuggingHelperBuildTask::Tools tools)
425 426 427 428 429
{
    const int index = currentIndex();
    if (index < 0)
        return;

430 431 432
    // remove tools that cannot be build
    tools &= DebuggingHelperBuildTask::availableTools(currentVersion());

433 434
    QTreeWidgetItem *item = treeItemForIndex(index);
    QTC_ASSERT(item, return);
435

436 437 438 439
    DebuggingHelperBuildTask::Tools buildFlags
            = item->data(0, BuildRunningRole).value<DebuggingHelperBuildTask::Tools>();
    buildFlags |= tools;
    item->setData(0, BuildRunningRole, QVariant::fromValue(buildFlags));
440

dt's avatar
dt committed
441
    BaseQtVersion *version = m_versions.at(index);
442 443 444
    if (!version)
        return;

445
    updateDebuggingHelperUi();
446

447
    // Run a debugging helper build task in the background.
448 449
    QString toolChainId = m_debuggingHelperUi->toolChainComboBox->itemData(
                m_debuggingHelperUi->toolChainComboBox->currentIndex()).toString();
hjk's avatar
hjk committed
450
    ToolChain *toolChain = ToolChainManager::findToolChain(toolChainId);
451 452 453 454
    if (!toolChain)
        return;

    DebuggingHelperBuildTask *buildTask = new DebuggingHelperBuildTask(version, toolChain, tools);
455 456
    // Don't open General Messages pane with errors
    buildTask->showOutputOnError(false);
457 458
    connect(buildTask, SIGNAL(finished(int,QString,DebuggingHelperBuildTask::Tools)),
            this, SLOT(debuggingHelperBuildFinished(int,QString,DebuggingHelperBuildTask::Tools)),
459
            Qt::QueuedConnection);
460
    QFuture<void> task = QtConcurrent::run(&DebuggingHelperBuildTask::run, buildTask);
461
    const QString taskName = tr("Building helpers");
462

hjk's avatar
hjk committed
463
    Core::ICore::progressManager()->addTask(task, taskName,
464
                                                        QLatin1String("Qt4ProjectManager::BuildHelpers"));
465
}
466 467 468 469 470 471 472 473 474 475
void QtOptionsPageWidget::buildGdbHelper()
{
    buildDebuggingHelper(DebuggingHelperBuildTask::GdbDebugging);
}

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

476 477 478 479 480
void QtOptionsPageWidget::buildQmlDebuggingLibrary()
{
    buildDebuggingHelper(DebuggingHelperBuildTask::QmlDebugging);
}

481 482
void QtOptionsPageWidget::buildQmlObserver()
{
483
    DebuggingHelperBuildTask::Tools qmlDbgTools =
Kai Koehne's avatar
Kai Koehne committed
484 485
            DebuggingHelperBuildTask::QmlObserver;
    qmlDbgTools |= DebuggingHelperBuildTask::QmlDebugging;
486
    buildDebuggingHelper(qmlDbgTools);
487
}
488

489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505
// 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)
506
{
507 508 509 510
    m_ui.log->setPlainText(text); // Show and scroll to bottom
    m_ui.log->moveCursor(QTextCursor::End);
    m_ui.log->ensureCursorVisible();
}
511

512 513 514 515 516 517 518 519 520
void QtOptionsPageWidget::slotShowDebuggingBuildLog()
{
    if (const QTreeWidgetItem *currentItem = m_ui->qtdirList->currentItem())
        showDebuggingBuildLog(currentItem);
}

void QtOptionsPageWidget::showDebuggingBuildLog(const QTreeWidgetItem *currentItem)
{
    const int currentItemIndex = indexForTreeItem(currentItem);
521 522
    if (currentItemIndex < 0)
        return;
523
    BuildLogDialog *dialog = new BuildLogDialog(this->window());
524
    dialog->setWindowTitle(tr("Debugging Helper Build Log for '%1'").arg(currentItem->text(0)));
525
    dialog->setText(currentItem->data(0, BuildLogRole).toString());
526
    dialog->show();
527 528
}

529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595
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);
    }
}

596
QtOptionsPageWidget::~QtOptionsPageWidget()
597 598
{
    delete m_ui;
dt's avatar
dt committed
599 600
    delete m_versionUi;
    delete m_debuggingHelperUi;
dt's avatar
dt committed
601
    delete m_configurationWidget;
602
    qDeleteAll(m_versions);
603 604
}

dt_'s avatar
dt_ committed
605
static QString filterForQmakeFileDialog()
606
{
607
    QString filter = QLatin1String("qmake (");
hjk's avatar
hjk committed
608
    const QStringList commands = BuildableHelperLibrary::possibleQMakeCommands();
609 610 611
    for (int i = 0; i < commands.size(); ++i) {
        if (i)
            filter += QLatin1Char(' ');
hjk's avatar
hjk committed
612
        if (HostOsInfo::isMacHost())
613 614
            // work around QTBUG-7739 that prohibits filters that don't start with *
            filter += QLatin1Char('*');
615
        filter += commands.at(i);
hjk's avatar
hjk committed
616
        if (HostOsInfo::isAnyUnixHost() && !HostOsInfo::isMacHost())
617 618 619
            // kde bug, we need at least one wildcard character
            // see QTCREATORBUG-7771
            filter += QLatin1Char('*');
dt's avatar
dt committed
620
    }
621
    filter += QLatin1Char(')');
dt_'s avatar
dt_ committed
622 623
    return filter;
}
624

dt_'s avatar
dt_ committed
625 626
void QtOptionsPageWidget::addQtDir()
{
hjk's avatar
hjk committed
627
    FileName qtVersion = FileName::fromString(
Tobias Hunger's avatar
Tobias Hunger committed
628
                QFileDialog::getOpenFileName(this,
Friedemann Kleint's avatar
Friedemann Kleint committed
629
                                             tr("Select a qmake Executable"),
Tobias Hunger's avatar
Tobias Hunger committed
630 631 632 633
                                             QString(),
                                             filterForQmakeFileDialog(),
                                             0,
                                             QFileDialog::DontResolveSymlinks));
dt's avatar
dt committed
634 635
    if (qtVersion.isNull())
        return;
636 637 638 639

    QFileInfo fi(qtVersion.toString());
    // should add all qt versions here ?
    if (BuildableHelperLibrary::isQtChooser(fi))
hjk's avatar
hjk committed
640
        qtVersion = FileName::fromString(BuildableHelperLibrary::qtChooserToQmakePath(fi.symLinkTarget()));
641

642 643 644 645 646 647 648
    BaseQtVersion *version = 0;
    foreach (BaseQtVersion *v, m_versions) {
        if (v->qmakeCommand() == qtVersion) {
            version = v;
            break;
        }
    }
649
    if (version) {
dt's avatar
dt committed
650
        // Already exist
Friedemann Kleint's avatar
Friedemann Kleint committed
651
        QMessageBox::warning(this, tr("Qt Version Already Known"),
652 653 654
                             tr("This Qt version was already registered as \"%1\".")
                             .arg(version->displayName()));
        return;
dt's avatar
dt committed
655
    }
656

657 658
    QString error;
    version = QtVersionFactory::createQtVersionFromQMakePath(qtVersion, false, QString(), &error);
dt's avatar
dt committed
659 660
    if (version) {
        m_versions.append(version);
661

dt's avatar
dt committed
662 663
        QTreeWidgetItem *item = new QTreeWidgetItem(m_ui->qtdirList->topLevelItem(1));
        item->setText(0, version->displayName());
664
        item->setText(1, version->qmakeCommand().toUserOutput());
dt's avatar
dt committed
665
        item->setData(0, VersionIdRole, version->uniqueId());
666
        item->setData(0, ToolChainIdRole, defaultToolChainId(version));
dt's avatar
dt committed
667 668 669 670
        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();
671
    } else {
672
        QMessageBox::warning(this, tr("Qmake Not Executable"),
Friedemann Kleint's avatar
Friedemann Kleint committed
673
                             tr("The qmake executable %1 could not be added: %2").arg(qtVersion.toUserOutput()).arg(error));
674
        return;
dt's avatar
dt committed
675 676
    }
    updateCleanUpButton();
677 678
}

679
void QtOptionsPageWidget::removeQtDir()
680 681
{
    QTreeWidgetItem *item = m_ui->qtdirList->currentItem();
682
    int index = indexForTreeItem(item);
683 684 685 686 687
    if (index < 0)
        return;

    delete item;

dt's avatar
dt committed
688
    BaseQtVersion *version = m_versions.at(index);
689
    m_versions.removeAt(index);
690
    delete version;
dt's avatar
dt committed
691
    updateCleanUpButton();
692 693
}

dt_'s avatar
dt_ committed
694 695
void QtOptionsPageWidget::editPath()
{
696
    BaseQtVersion *current = currentVersion();
697
    QString dir = currentVersion()->qmakeCommand().toFileInfo().absolutePath();
hjk's avatar
hjk committed
698
    FileName qtVersion = FileName::fromString(
699 700
                QFileDialog::getOpenFileName(this,
                                             tr("Select a qmake executable"),
701 702 703 704
                                             dir,
                                             filterForQmakeFileDialog(),
                                             0,
                                             QFileDialog::DontResolveSymlinks));
dt_'s avatar
dt_ committed
705 706 707
    if (qtVersion.isNull())
        return;
    BaseQtVersion *version = QtVersionFactory::createQtVersionFromQMakePath(qtVersion);
Daniel Teske's avatar
Daniel Teske committed
708 709
    if (!version)
        return;
dt_'s avatar
dt_ committed
710 711 712
    // Same type? then replace!
    if (current->type() != version->type()) {
        // not the same type, error out
Friedemann Kleint's avatar
Friedemann Kleint committed
713
        QMessageBox::critical(this, tr("Incompatible Qt Versions"),
Friedemann Kleint's avatar
Friedemann Kleint committed
714
                              tr("The Qt version selected must match the device type."),
dt_'s avatar
dt_ committed
715 716 717 718 719 720
                              QMessageBox::Ok);
        delete version;
        return;
    }
    // same type, replace
    version->setId(current->uniqueId());
dt_'s avatar
dt_ committed
721 722
    if (current->displayName() != current->defaultDisplayName(current->qtVersionString(), current->qmakeCommand()))
        version->setDisplayName(current->displayName());
dt_'s avatar
dt_ committed
723 724 725 726 727 728 729
    m_versions.replace(m_versions.indexOf(current), version);
    delete current;

    // Update ui
    userChangedCurrentVersion();
    QTreeWidgetItem *item = m_ui->qtdirList->currentItem();
    item->setText(0, version->displayName());
730
    item->setText(1, version->qmakeCommand().toUserOutput());
dt_'s avatar
dt_ committed
731
    item->setData(0, VersionIdRole, version->uniqueId());
732
    item->setData(0, ToolChainIdRole, defaultToolChainId(version));
dt_'s avatar
dt_ committed
733 734 735
    item->setIcon(0, version->isValid()? m_validVersionIcon : m_invalidVersionIcon);
}

736
void QtOptionsPageWidget::updateDebuggingHelperUi()
737
{
dt's avatar
dt committed
738
    BaseQtVersion *version = currentVersion();
739
    const QTreeWidgetItem *currentItem = m_ui->qtdirList->currentItem();
740

hjk's avatar
hjk committed
741
    QList<ToolChain*> toolchains = toolChains(currentVersion());
742 743

    if (!version || !version->isValid() || toolchains.isEmpty()) {
744
        m_ui->debuggingHelperWidget->setVisible(false);
745
    } else {
746 747 748 749 750
        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;
751

752 753
        const bool hasGdbHelper = !version->gdbDebuggingHelperLibrary().isEmpty();
        const bool hasQmlDumper = version->hasQmlDump();
754
        const bool needsQmlDumper = version->needsQmlDump();
755
        const bool hasQmlDebuggingLib = version->hasQmlDebuggingLibrary();
756
        const bool needsQmlDebuggingLib = version->needsQmlDebuggingLibrary();
757
        const bool hasQmlObserver = !version->qmlObserverTool().isEmpty();
758

759 760
        bool isBuildingGdbHelper = false;
        bool isBuildingQmlDumper = false;
761
        bool isBuildingQmlDebuggingLib = false;
762 763 764 765 766 767 768
        bool isBuildingQmlObserver = false;

        if (currentItem) {
            DebuggingHelperBuildTask::Tools buildingTools
                    = currentItem->data(0, BuildRunningRole).value<DebuggingHelperBuildTask::Tools>();
            isBuildingGdbHelper = buildingTools & DebuggingHelperBuildTask::GdbDebugging;
            isBuildingQmlDumper = buildingTools & DebuggingHelperBuildTask::QmlDump;
769
            isBuildingQmlDebuggingLib = buildingTools & DebuggingHelperBuildTask::QmlDebugging;
770 771 772
            isBuildingQmlObserver = buildingTools & DebuggingHelperBuildTask::QmlObserver;
        }

773 774
        // get names of tools from labels
        QStringList helperNames;
775
        const QChar colon = QLatin1Char(':');
776
        if (hasGdbHelper)
777
            helperNames << m_debuggingHelperUi->gdbHelperLabel->text().remove(colon);
778
        if (hasQmlDumper)
779
            helperNames << m_debuggingHelperUi->qmlDumpLabel->text().remove(colon);
780
        if (hasQmlDebuggingLib)
781
            helperNames << m_debuggingHelperUi->qmlDebuggingLibLabel->text().remove(colon);
782
        if (hasQmlObserver)
783
            helperNames << m_debuggingHelperUi->qmlObserverLabel->text().remove(colon);
784 785 786

        QString status;
        if (helperNames.isEmpty()) {
787
            status = tr("Helpers: None available");
788
        } else {
Friedemann Kleint's avatar
Friedemann Kleint committed
789
            //: %1 is list of tool names.
790
            status = tr("Helpers: %1.").arg(helperNames.join(QLatin1String(", ")));
791 792 793 794
        }

        m_ui->debuggingHelperWidget->setSummaryText(status);

795 796
        QString gdbHelperText;
        Qt::TextInteractionFlags gdbHelperTextFlags = Qt::NoTextInteraction;
797
        if (hasGdbHelper) {
798
            gdbHelperText = QDir::toNativeSeparators(version->gdbDebuggingHelperLibrary());
799
            gdbHelperTextFlags = Qt::TextSelectableByMouse;
800
        } else {
801
            if (canBuildGdbHelper)
802
                gdbHelperText =  tr("<i>Not yet built.</i>");
803
            else
804
                gdbHelperText =  tr("<i>Not needed.</i>");
805
        }
806 807
        m_debuggingHelperUi->gdbHelperStatus->setText(gdbHelperText);
        m_debuggingHelperUi->gdbHelperStatus->setTextInteractionFlags(gdbHelperTextFlags);
808
        m_debuggingHelperUi->gdbHelperBuildButton->setEnabled(canBuildGdbHelper && !isBuildingGdbHelper);
809

810
        QString qmlDumpStatusText, qmlDumpStatusToolTip;
811
        Qt::TextInteractionFlags qmlDumpStatusTextFlags = Qt::NoTextInteraction;
812
        if (hasQmlDumper) {
813 814
            qmlDumpStatusText = QDir::toNativeSeparators(version->qmlDumpTool(false));
            const QString debugQmlDumpPath = QDir::toNativeSeparators(version->qmlDumpTool(true));
815
            if (qmlDumpStatusText != debugQmlDumpPath) {
816 817
                if (!qmlDumpStatusText.isEmpty()
                        && !debugQmlDumpPath.isEmpty())
818 819 820
                    qmlDumpStatusText += QLatin1String("\n");
                qmlDumpStatusText += debugQmlDumpPath;
            }
821
            qmlDumpStatusTextFlags = Qt::TextSelectableByMouse;
822
        } else {
823 824 825
            if (!needsQmlDumper) {
                qmlDumpStatusText = tr("<i>Not needed.</i>");
            } else if (canBuildQmlDumper) {
826 827 828
                qmlDumpStatusText = tr("<i>Not yet built.</i>");
            } else {
                qmlDumpStatusText = tr("<i>Cannot be compiled.</i>");
829
                QmlDumpTool::canBuild(version, &qmlDumpStatusToolTip);
830
            }
831
        }
832
        m_debuggingHelperUi->qmlDumpStatus->setText(qmlDumpStatusText);
833
        m_debuggingHelperUi->qmlDumpStatus->setTextInteractionFlags(qmlDumpStatusTextFlags);
834
        m_debuggingHelperUi->qmlDumpStatus->setToolTip(qmlDumpStatusToolTip);
835
        m_debuggingHelperUi->qmlDumpBuildButton->setEnabled(canBuildQmlDumper & !isBuildingQmlDumper);
836

837
        QString qmlDebuggingLibStatusText, qmlDebuggingLibToolTip;
838 839 840 841 842 843 844 845 846 847
        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()) {
848
                    qmlDebuggingLibStatusText += QLatin1Char('\n');
849 850 851 852
                }
                qmlDebuggingLibStatusText += debugPath;
            }
            qmlDebuggingLibStatusTextFlags = Qt::TextSelectableByMouse;
853 854 855 856
        } else {
            if (!needsQmlDebuggingLib) {
                qmlDebuggingLibStatusText = tr("<i>Not needed.</i>");
            } else if (canBuildQmlDebuggingLib) {
857 858 859
                qmlDebuggingLibStatusText = tr("<i>Not yet built.</i>");
            } else {
                qmlDebuggingLibStatusText = tr("<i>Cannot be compiled.</i>");
860
                QmlDebuggingLibrary::canBuild(version, &qmlDebuggingLibToolTip);
861 862 863 864
            }
        }
        m_debuggingHelperUi->qmlDebuggingLibStatus->setText(qmlDebuggingLibStatusText);
        m_debuggingHelperUi->qmlDebuggingLibStatus->setTextInteractionFlags(qmlDebuggingLibStatusTextFlags);
865
        m_debuggingHelperUi->qmlDebuggingLibStatus->setToolTip(qmlDebuggingLibToolTip);
866 867
        m_debuggingHelperUi->qmlDebuggingLibBuildButton->setEnabled(needsQmlDebuggingLib
                                                                    && canBuildQmlDebuggingLib
868 869
                                                                    && !isBuildingQmlDebuggingLib);

870
        QString qmlObserverStatusText, qmlObserverToolTip;
871
        Qt::TextInteractionFlags qmlObserverStatusTextFlags = Qt::NoTextInteraction;
872
        if (hasQmlObserver) {
873
            qmlObserverStatusText = QDir::toNativeSeparators(version->qmlObserverTool());
874
            qmlObserverStatusTextFlags = Qt::TextSelectableByMouse;
875
        }  else {
876 877 878
            if (!needsQmlDebuggingLib) {
                qmlObserverStatusText = tr("<i>Not needed.</i>");
            } else if (canBuildQmlObserver) {
879 880 881
                qmlObserverStatusText = tr("<i>Not yet built.</i>");
            } else {
                qmlObserverStatusText = tr("<i>Cannot be compiled.</i>");
882
                QmlObserverTool::canBuild(version, &qmlObserverToolTip);
883
            }
Kai Koehne's avatar