Skip to content
Snippets Groups Projects
qtversionmanager.cpp 53.2 KiB
Newer Older
/**************************************************************************
con's avatar
con committed
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
con's avatar
con committed
**
** Contact:  Qt Software Information (qt-info@nokia.com)
**
** Commercial Usage
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
** GNU Lesser General Public License Usage
** 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.
** If you are unsure which license is appropriate for your use, please
** contact the sales department at qt-sales@nokia.com.
con's avatar
con committed
**
**************************************************************************/
hjk's avatar
hjk committed

con's avatar
con committed
#include "qtversionmanager.h"
hjk's avatar
hjk committed

#include "projectexplorerconstants.h"
#include "ui_showbuildlog.h"
#include "ui_qtversionmanager.h"
con's avatar
con committed

con's avatar
con committed
#include <coreplugin/coreconstants.h>
#include <extensionsystem/pluginmanager.h>
dt's avatar
dt committed
#include <projectexplorer/cesdkhandler.h>
#include <projectexplorer/toolchain.h>
con's avatar
con committed
#include <help/helpplugin.h>
hjk's avatar
hjk committed
#include <utils/qtcassert.h>
con's avatar
con committed

hjk's avatar
hjk committed
#include <QtCore/QDebug>
#include <QtCore/QProcess>
con's avatar
con committed
#include <QtCore/QSettings>
#include <QtCore/QStringRef>
#include <QtCore/QTime>
con's avatar
con committed
#include <QtGui/QFileDialog>
hjk's avatar
hjk committed
#include <QtGui/QHeaderView>
con's avatar
con committed
#include <QtGui/QMessageBox>
#include <QtGui/QPushButton>
#include <QtGui/QToolButton>
#include <QtGui/QApplication>
#include <QtGui/QDesktopServices>
#include <QtGui/QHBoxLayout>
#include <QtGui/QLabel>
con's avatar
con committed

using namespace ProjectExplorer;
using namespace ProjectExplorer::Internal;
con's avatar
con committed

static const char *QtVersionsSectionName = "QtVersions";
static const char *defaultQtVersionKey = "DefaultQtVersion";
static const char *newQtVersionsKey = "NewQtVersions";

con's avatar
con committed
QtVersionManager::QtVersionManager()
    : m_emptyVersion(new QtVersion)
{
    QSettings *s = Core::ICore::instance()->settings();
con's avatar
con committed
    m_defaultVersion = s->value(defaultQtVersionKey, 0).toInt();

    m_idcount = 1;
    int size = s->beginReadArray(QtVersionsSectionName);
    for (int i = 0; i < size; ++i) {
        s->setArrayIndex(i);
        // Find the right id
        // Either something saved or something generated
        // Note: This code assumes that either all ids are read from the settings
        // or generated on the fly.
        int id = s->value("Id", -1).toInt();
        if (id == -1)
            id = getUniqueId();
        else if (id > m_idcount)
            m_idcount = id;
        QtVersion *version = new QtVersion(s->value("Name").toString(),
                                           s->value("Path").toString(),
                                           id,
                                           s->value("IsSystemVersion", false).toBool());
        version->setMingwDirectory(s->value("MingwDirectory").toString());
        version->setPrependPath(s->value("PrependPath").toString());
        version->setMsvcVersion(s->value("msvcVersion").toString());
        m_versions.append(version);
    }
    s->endArray();
    updateUniqueIdToIndexMap();

    ++m_idcount;
    addNewVersionsFromInstaller();
    updateSystemVersion();

    writeVersionsIntoSettings();
    updateDocumentation();
}

QtVersionManager::~QtVersionManager()
{
    qDeleteAll(m_versions);
    m_versions.clear();
    delete m_emptyVersion;
    m_emptyVersion = 0;
}

void QtVersionManager::addVersion(QtVersion *version)
{
    m_versions.append(version);
    emit qtVersionsChanged();
    writeVersionsIntoSettings();
con's avatar
con committed
}

void QtVersionManager::updateDocumentation()
{
    Help::HelpManager *helpManager
        = ExtensionSystem::PluginManager::instance()->getObject<Help::HelpManager>();
    Q_ASSERT(helpManager);
con's avatar
con committed
    QStringList fileEndings = QStringList() << "/qch/qt.qch" << "/qch/qmake.qch" << "/qch/designer.qch";
    QStringList files;
    foreach (QtVersion *version, m_versions) {
        QString docPath = version->versionInfo().value("QT_INSTALL_DOCS");
        foreach (const QString &fileEnding, fileEndings)
            files << docPath+fileEnding;
    }
    helpManager->registerDocumentation(files);
}

int QtVersionManager::getUniqueId()
{
    return m_idcount++;
}

QString QtVersionManager::id() const
{
    return QLatin1String(Constants::QTVERSION_PAGE);
}

QString QtVersionManager::trName() const
con's avatar
con committed
{
    return tr(Constants::QTVERSION_PAGE);
}

QString QtVersionManager::category() const
{
    return Constants::QT_CATEGORY;
}

QString QtVersionManager::trCategory() const
{
    return tr(Constants::QT_CATEGORY);
}

QWidget *QtVersionManager::createPage(QWidget *parent)
{
    if (m_widget)
        delete m_widget;
    m_widget = new QtDirWidget(parent, m_versions, m_defaultVersion);
    return m_widget;
}

void QtVersionManager::updateUniqueIdToIndexMap()
{
    m_uniqueIdToIndex.clear();
hjk's avatar
hjk committed
    for (int i = 0; i < m_versions.size(); ++i)
con's avatar
con committed
        m_uniqueIdToIndex.insert(m_versions.at(i)->uniqueId(), i);
}

void QtVersionManager::apply()
con's avatar
con committed
{
    m_widget->finish();
    QList<QtVersion*> newVersions = m_widget->versions();
    bool versionPathsChanged = m_versions.size() != newVersions.size();
    if (!versionPathsChanged) {
        for (int i = 0; i < m_versions.size(); ++i) {
            if (m_versions.at(i)->path() != newVersions.at(i)->path()) {
                versionPathsChanged = true;
                break;
            }
        }
    }
    qDeleteAll(m_versions);
    m_versions.clear();
    foreach(QtVersion *version, m_widget->versions())
        m_versions.append(new QtVersion(*version));
con's avatar
con committed
    if (versionPathsChanged)
        updateDocumentation();
    updateUniqueIdToIndexMap();

    bool emitDefaultChanged = false;
    if (m_defaultVersion != m_widget->defaultVersion()) {
        m_defaultVersion = m_widget->defaultVersion();
        emitDefaultChanged = true;
    }

    emit qtVersionsChanged();
    if (emitDefaultChanged)
        emit defaultQtVersionChanged();

    writeVersionsIntoSettings();
}

void QtVersionManager::writeVersionsIntoSettings()
{
    QSettings *s = Core::ICore::instance()->settings();
con's avatar
con committed
    s->setValue(defaultQtVersionKey, m_defaultVersion);
    s->beginWriteArray(QtVersionsSectionName);
con's avatar
con committed
    for (int i = 0; i < m_versions.size(); ++i) {
        s->setArrayIndex(i);
        s->setValue("Name", m_versions.at(i)->name());
        s->setValue("Path", m_versions.at(i)->path());
        s->setValue("Id", m_versions.at(i)->uniqueId());
        s->setValue("MingwDirectory", m_versions.at(i)->mingwDirectory());
        s->setValue("PrependPath", m_versions.at(i)->prependPath());
        s->setValue("msvcVersion", m_versions.at(i)->msvcVersion());
        s->setValue("IsSystemVersion", m_versions.at(i)->isSystemVersion());
    }
    s->endArray();
}

QList<QtVersion* > QtVersionManager::versions() const
{
    return m_versions;
}

QtVersion *QtVersionManager::version(int id) const
{
    int pos = m_uniqueIdToIndex.value(id, -1);
    if (pos != -1)
        return m_versions.at(pos);

hjk's avatar
hjk committed
    if (m_defaultVersion < m_versions.count())
con's avatar
con committed
        return m_versions.at(m_defaultVersion);
    else
        return m_emptyVersion;
}

void QtVersionManager::addNewVersionsFromInstaller()
{
    // Add new versions which may have been installed by the WB installer in the form:
    // NewQtVersions="qt 4.3.2=c:\\qt\\qt432;qt embedded=c:\\qtembedded;"
    // or NewQtVersions="qt 4.3.2=c:\\qt\\qt432=c:\\qtcreator\\mingw\\=prependToPath;
    // Duplicate entries are not added, the first new version is set as default.
    QSettings *settings = Core::ICore::instance()->settings();

    if (!settings->contains(newQtVersionsKey) &&
        !settings->contains(QLatin1String("Installer/")+newQtVersionsKey))
con's avatar
con committed
        return;

//    qDebug()<<"QtVersionManager::addNewVersionsFromInstaller()";

    QString newVersionsValue = settings->value(newQtVersionsKey).toString();
    if (newVersionsValue.isEmpty())
        newVersionsValue = settings->value(QLatin1String("Installer/")+newQtVersionsKey).toString();

con's avatar
con committed
    QStringList newVersionsList = newVersionsValue.split(';', QString::SkipEmptyParts);
    bool defaultVersionWasReset = false;
    foreach (QString newVersion, newVersionsList) {
        QStringList newVersionData = newVersion.split('=');
hjk's avatar
hjk committed
        if (newVersionData.count()>=2) {
con's avatar
con committed
            if (QDir(newVersionData[1]).exists()) {
                QtVersion *version = new QtVersion(newVersionData[0], newVersionData[1], m_idcount++ );
hjk's avatar
hjk committed
                if (newVersionData.count() >= 3)
con's avatar
con committed
                    version->setMingwDirectory(newVersionData[2]);
hjk's avatar
hjk committed
                if (newVersionData.count() >= 4)
con's avatar
con committed
                    version->setPrependPath(newVersionData[3]);

                bool versionWasAlreadyInList = false;
                foreach(const QtVersion * const it, m_versions) {
hjk's avatar
hjk committed
                    if (QDir(version->path()).canonicalPath() == QDir(it->path()).canonicalPath()) {
con's avatar
con committed
                        versionWasAlreadyInList = true;
                        break;
                    }
                }

                if (!versionWasAlreadyInList) {
                    m_versions.append(version);
                } else {
                    // clean up
                    delete version;
                }
                if (!defaultVersionWasReset) {
                    m_defaultVersion = versionWasAlreadyInList? m_defaultVersion : m_versions.count() - 1;
                    defaultVersionWasReset = true;
                }
            }
        }
    }
    settings->remove(newQtVersionsKey);
    settings->remove(QLatin1String("Installer/")+newQtVersionsKey);
con's avatar
con committed
    updateUniqueIdToIndexMap();
}

void QtVersionManager::updateSystemVersion()
{
    bool haveSystemVersion = false;
    foreach (QtVersion *version, m_versions) {
        if (version->isSystemVersion()) {
            version->setPath(findSystemQt());
            version->setName(tr("Auto-detected Qt"));
con's avatar
con committed
            haveSystemVersion = true;
        }
    }
    if (haveSystemVersion)
        return;
    QtVersion *version = new QtVersion(tr("Auto-detected Qt"),
con's avatar
con committed
                                       findSystemQt(),
                                       getUniqueId(),
                                       true);
    m_versions.prepend(version);
    updateUniqueIdToIndexMap();
    if (m_versions.size() > 1) // we had other versions before adding system version
        ++m_defaultVersion;
}

QStringList QtVersionManager::possibleQMakeCommands()
{
    // On windows noone has renamed qmake, right?
#ifdef Q_OS_WIN
    return QStringList() << "qmake.exe";
#endif
    // On unix some distributions renamed qmake to avoid clashes
    QStringList result;
    result << "qmake-qt4" << "qmake4" << "qmake";
    return result;
}

QString QtVersionManager::qtVersionForQMake(const QString &qmakePath)
con's avatar
con committed
{
    QProcess qmake;
    qmake.start(qmakePath, QStringList()<<"--version");
    if (!qmake.waitForFinished())
        return false;
    QString output = qmake.readAllStandardOutput();
    QRegExp regexp("(QMake version|Qmake version:)[\\s]*([\\d.]*)");
    regexp.indexIn(output);
    if (regexp.cap(2).startsWith("2.")) {
        QRegExp regexp2("Using Qt version[\\s]*([\\d\\.]*)");
        regexp2.indexIn(output);
        return regexp2.cap(1);
    }
    return QString();
con's avatar
con committed
}

QString QtVersionManager::findSystemQt() const
{
    Environment env = Environment::systemEnvironment();
    QStringList paths = env.path();
    foreach (const QString &path, paths) {
        foreach (const QString &possibleCommand, possibleQMakeCommands()) {
            QFileInfo qmake(path + "/" + possibleCommand);
            if (qmake.exists()) {
                if (!qtVersionForQMake(qmake.absoluteFilePath()).isNull()) {
con's avatar
con committed
                    QDir dir(qmake.absoluteDir());
                    dir.cdUp();
                    return dir.absolutePath();
                }
            }
        }
    }
    return tr("<not found>");
}

QtVersion *QtVersionManager::currentQtVersion() const
{
hjk's avatar
hjk committed
    if (m_defaultVersion < m_versions.count())
con's avatar
con committed
        return m_versions.at(m_defaultVersion);
    else
        return m_emptyVersion;
}

//-----------------------------------------------------
QtDirWidget::QtDirWidget(QWidget *parent, QList<QtVersion *> versions, int defaultVersion)
    : QWidget(parent)
    , m_defaultVersion(defaultVersion)
    , m_specifyNameString(tr("<specify a name>"))
    , m_specifyPathString(tr("<specify a path>"))
{
    // Initialize m_versions
    foreach(QtVersion *version, versions) {
        m_versions.append(new QtVersion(*version));
    }


    m_ui = new Internal::Ui::QtVersionManager();
    m_ui->setupUi(this);
    m_ui->qtPath->setExpectedKind(Core::Utils::PathChooser::Directory);
    m_ui->qtPath->setPromptDialogTitle(tr("Select QTDIR"));
    m_ui->mingwPath->setExpectedKind(Core::Utils::PathChooser::Directory);
    m_ui->qtPath->setPromptDialogTitle(tr("Select the Qt Directory"));
con's avatar
con committed

    m_ui->addButton->setIcon(QIcon(Core::Constants::ICON_PLUS));
    m_ui->delButton->setIcon(QIcon(Core::Constants::ICON_MINUS));
con's avatar
con committed

    for (int i = 0; i < m_versions.count(); ++i) {
        const QtVersion * const version = m_versions.at(i);
        QTreeWidgetItem *item = new QTreeWidgetItem(m_ui->qtdirList);
con's avatar
con committed
        item->setText(0, version->name());
        item->setText(1, QDir::toNativeSeparators(version->path()));
con's avatar
con committed
        item->setData(0, Qt::UserRole, version->uniqueId());
        if (version->isValid()) {
            if (version->hasDebuggingHelper())
                item->setData(2, Qt::DecorationRole, QIcon(":/extensionsystem/images/ok.png"));
                item->setData(2, Qt::DecorationRole, QIcon(":/extensionsystem/images/error.png"));
            item->setData(2, Qt::DecorationRole, QIcon());
        m_ui->defaultCombo->addItem(version->name());
con's avatar
con committed
        if (i == m_defaultVersion)
            m_ui->defaultCombo->setCurrentIndex(i);
con's avatar
con committed
    }

    connect(m_ui->nameEdit, SIGNAL(textEdited(const QString &)),
con's avatar
con committed
            this, SLOT(updateCurrentQtName()));
    connect(m_ui->qtPath, SIGNAL(changed()),
con's avatar
con committed
            this, SLOT(updateCurrentQtPath()));
    connect(m_ui->mingwPath, SIGNAL(changed()),
con's avatar
con committed
            this, SLOT(updateCurrentMingwDirectory()));

    connect(m_ui->addButton, SIGNAL(clicked()),
con's avatar
con committed
            this, SLOT(addQtDir()));
    connect(m_ui->delButton, SIGNAL(clicked()),
con's avatar
con committed
            this, SLOT(removeQtDir()));
    connect(m_ui->qtPath, SIGNAL(browsingFinished()),
            this, SLOT(onQtBrowsed()));
    connect(m_ui->mingwPath, SIGNAL(browsingFinished()),
            this, SLOT(onMingwBrowsed()));
con's avatar
con committed

    connect(m_ui->qtdirList, SIGNAL(currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)),
con's avatar
con committed
            this, SLOT(versionChanged(QTreeWidgetItem *, QTreeWidgetItem *)));
    connect(m_ui->defaultCombo, SIGNAL(currentIndexChanged(int)),
con's avatar
con committed
            this, SLOT(defaultChanged(int)));

    connect(m_ui->msvcComboBox, SIGNAL(currentIndexChanged(int)),
con's avatar
con committed
            this, SLOT(msvcVersionChanged()));

    connect(m_ui->rebuildButton, SIGNAL(clicked()),
            this, SLOT(buildDebuggingHelper()));
    connect(m_ui->showLogButton, SIGNAL(clicked()),
            this, SLOT(showDebuggingBuildLog()));

con's avatar
con committed
    showEnvironmentPage(0);
    updateState();
}

void QtDirWidget::buildDebuggingHelper()
{
    // Find the qt version for this button..
    QTreeWidgetItem *currentItem = m_ui->qtdirList->currentItem();
    if (!currentItem)
    int currentItemIndex = m_ui->qtdirList->indexOfTopLevelItem(currentItem);
    QtVersion *version = m_versions[currentItemIndex];
    QString result = m_versions.at(currentItemIndex)->buildDebuggingHelperLibrary();
    currentItem->setData(2, Qt::UserRole, result);
    if (version->hasDebuggingHelper()) {
        m_ui->debuggingHelperStateLabel->setPixmap(QPixmap(":/extensionsystem/images/ok.png"));
        currentItem->setData(2, Qt::DecorationRole, QIcon(":/extensionsystem/images/ok.png"));
    } else {        
        m_ui->debuggingHelperStateLabel->setPixmap(QPixmap(":/extensionsystem/images/error.png"));
        currentItem->setData(2, Qt::DecorationRole, QIcon(":/extensionsystem/images/error.png"));
    m_ui->showLogButton->setEnabled(true);
}

void QtDirWidget::showDebuggingBuildLog()
{
    QTreeWidgetItem *currentItem = m_ui->qtdirList->currentItem();
    if (!currentItem)
    int currentItemIndex = m_ui->qtdirList->indexOfTopLevelItem(currentItem);
hjk's avatar
hjk committed
    Ui_ShowBuildLog ui;
    ui.log->setPlainText(m_ui->qtdirList->topLevelItem(currentItemIndex)->data(2, Qt::UserRole).toString());
QtDirWidget::~QtDirWidget()
{
    qDeleteAll(m_versions);
con's avatar
con committed
void QtDirWidget::addQtDir()
{
    QtVersion *newVersion = new QtVersion(m_specifyNameString, m_specifyPathString);
    m_versions.append(newVersion);

    QTreeWidgetItem *item = new QTreeWidgetItem(m_ui->qtdirList);
con's avatar
con committed
    item->setText(0, newVersion->name());
    item->setText(1, QDir::toNativeSeparators(newVersion->path()));
con's avatar
con committed
    item->setData(0, Qt::UserRole, newVersion->uniqueId());
    item->setData(2, Qt::DecorationRole, QIcon());
    m_ui->qtdirList->setCurrentItem(item);
con's avatar
con committed

    m_ui->nameEdit->setText(newVersion->name());
    m_ui->qtPath->setPath(newVersion->path());
    m_ui->defaultCombo->addItem(newVersion->name());
    m_ui->nameEdit->setFocus();
    m_ui->nameEdit->selectAll();
con's avatar
con committed
}

void QtDirWidget::removeQtDir()
{
    QTreeWidgetItem *item = m_ui->qtdirList->currentItem();
    int index = m_ui->qtdirList->indexOfTopLevelItem(item);
con's avatar
con committed
    if (index < 0)
        return;

    for (int i = 0; i < m_ui->defaultCombo->count(); ++i) {
        if (m_ui->defaultCombo->itemText(i) == item->text(0)) {
            m_ui->defaultCombo->removeItem(i);
con's avatar
con committed
            break;
        }
    }

    delete item;

    delete m_versions.takeAt(index);
    updateState();
}

void QtDirWidget::updateState()
{
    bool enabled = (m_ui->qtdirList->currentItem() != 0);
con's avatar
con committed
    bool isSystemVersion = (enabled 
        && m_versions.at(m_ui->qtdirList->indexOfTopLevelItem(m_ui->qtdirList->currentItem()))->isSystemVersion());
    m_ui->delButton->setEnabled(enabled && !isSystemVersion);
    m_ui->nameEdit->setEnabled(enabled && !isSystemVersion);
    m_ui->qtPath->setEnabled(enabled && !isSystemVersion);
    m_ui->mingwPath->setEnabled(enabled);
    bool hasLog = enabled && !m_ui->qtdirList->currentItem()->data(2, Qt::UserRole).toString().isEmpty();
    m_ui->showLogButton->setEnabled(hasLog);

    QtVersion *version = 0;
    if (enabled)
        version = m_versions.at(m_ui->qtdirList->indexOfTopLevelItem(m_ui->qtdirList->currentItem()));
    if (version) {
        m_ui->rebuildButton->setEnabled(version->isValid());
        if (version->hasDebuggingHelper())
            m_ui->debuggingHelperStateLabel->setPixmap(QPixmap(":/extensionsystem/images/ok.png"));
            m_ui->debuggingHelperStateLabel->setPixmap(QPixmap(":/extensionsystem/images/error.png"));
        m_ui->rebuildButton->setEnabled(false);
        m_ui->debuggingHelperStateLabel->setPixmap(QPixmap());
void QtDirWidget::makeMingwVisible(bool visible)
{
    m_ui->mingwLabel->setVisible(visible);
    m_ui->mingwPath->setVisible(visible);
con's avatar
con committed
}

void QtDirWidget::showEnvironmentPage(QTreeWidgetItem *item)
{
    m_ui->msvcComboBox->setVisible(false);
hjk's avatar
hjk committed
    if (item) {
        int index = m_ui->qtdirList->indexOfTopLevelItem(item);
        m_ui->errorLabel->setText("");
dt's avatar
dt committed
        ProjectExplorer::ToolChain::ToolChainType t = m_versions.at(index)->toolchainType();
        if (t == ProjectExplorer::ToolChain::MinGW) {
            m_ui->msvcComboBox->setVisible(false);
            makeMingwVisible(true);
            m_ui->mingwPath->setPath(m_versions.at(index)->mingwDirectory());
dt's avatar
dt committed
        } else if (t == ProjectExplorer::ToolChain::MSVC || t == ProjectExplorer::ToolChain::WINCE){
            m_ui->msvcComboBox->setVisible(false);
            makeMingwVisible(false);
dt's avatar
dt committed
            QStringList msvcEnvironments = ProjectExplorer::ToolChain::availableMSVCVersions();
            if (msvcEnvironments.count() == 0) {
            } else if (msvcEnvironments.count() == 1) {
con's avatar
con committed
            } else {
                 m_ui->msvcComboBox->setVisible(true);
                 bool block = m_ui->msvcComboBox->blockSignals(true);
                 m_ui->msvcComboBox->clear();
dt's avatar
dt committed
                 foreach(const QString &msvcenv, msvcEnvironments) {
                     m_ui->msvcComboBox->addItem(msvcenv);
dt's avatar
dt committed
                     if (msvcenv == m_versions.at(index)->msvcVersion()) {
                         m_ui->msvcComboBox->setCurrentIndex(m_ui->msvcComboBox->count() - 1);
con's avatar
con committed
                     }
                 }
                 m_ui->msvcComboBox->blockSignals(block);
con's avatar
con committed
            }
dt's avatar
dt committed
        } else if (t == ProjectExplorer::ToolChain::INVALID) {
            m_ui->msvcComboBox->setVisible(false);
            makeMingwVisible(false);
con's avatar
con committed
            if (!m_versions.at(index)->isInstalled())
                m_ui->errorLabel->setText(tr("The Qt Version %1 is not installed. Run make install")
                                           .arg(QDir::toNativeSeparators(m_versions.at(index)->path())));
con's avatar
con committed
            else
                m_ui->errorLabel->setText(tr("%1 is not a valid qt directory").arg(QDir::toNativeSeparators(m_versions.at(index)->path())));
dt's avatar
dt committed
        } else { //ProjectExplorer::ToolChain::GCC
            m_ui->msvcComboBox->setVisible(false);
            makeMingwVisible(false);
            m_ui->errorLabel->setText(tr("Found Qt version %1, using mkspec %2")
                                     .arg(m_versions.at(index)->qtVersionString(),
                                          m_versions.at(index)->mkspec()));
con's avatar
con committed
        }
    } else {
        m_ui->msvcComboBox->setVisible(false);
        makeMingwVisible(false);
con's avatar
con committed
    }
}

void QtDirWidget::versionChanged(QTreeWidgetItem *item, QTreeWidgetItem *old)
{
hjk's avatar
hjk committed
    if (old) {
        fixQtVersionName(m_ui->qtdirList->indexOfTopLevelItem(old));
con's avatar
con committed
    }
    if (item) {
        m_ui->nameEdit->setText(item->text(0));
        m_ui->qtPath->setPath(item->text(1));
con's avatar
con committed
    } else {
        m_ui->nameEdit->clear();
        m_ui->qtPath->setPath(""); // clear()
con's avatar
con committed
    }
    showEnvironmentPage(item);
    updateState();
}

void QtDirWidget::onQtBrowsed()
con's avatar
con committed
{
    const QString dir = m_ui->qtPath->path();
con's avatar
con committed
    if (dir.isEmpty())
        return;

    updateCurrentQtPath();
    if (m_ui->nameEdit->text().isEmpty() || m_ui->nameEdit->text() == m_specifyNameString) {
con's avatar
con committed
        QStringList dirSegments = dir.split(QDir::separator(), QString::SkipEmptyParts);
        if (!dirSegments.isEmpty())
            m_ui->nameEdit->setText(dirSegments.last());
con's avatar
con committed
        updateCurrentQtName();
    }
    updateState();
}

void QtDirWidget::onMingwBrowsed()
con's avatar
con committed
{
    const QString dir = m_ui->mingwPath->path();
con's avatar
con committed
    if (dir.isEmpty())
        return;

    updateCurrentMingwDirectory();
    updateState();
}

void QtDirWidget::defaultChanged(int)
{
    for (int i=0; i<m_ui->defaultCombo->count(); ++i) {
        if (m_versions.at(i)->name() == m_ui->defaultCombo->currentText()) {
con's avatar
con committed
            m_defaultVersion = i;
            return;
        }
    }

    m_defaultVersion = 0;
}

void QtDirWidget::updateCurrentQtName()
{
    QTreeWidgetItem *currentItem = m_ui->qtdirList->currentItem();
    Q_ASSERT(currentItem);
    int currentItemIndex = m_ui->qtdirList->indexOfTopLevelItem(currentItem);
    m_versions[currentItemIndex]->setName(m_ui->nameEdit->text());
con's avatar
con committed
    currentItem->setText(0, m_versions[currentItemIndex]->name());

    m_ui->defaultCombo->setItemText(currentItemIndex, m_versions[currentItemIndex]->name());
con's avatar
con committed
}


void QtDirWidget::finish()
{
    if (QTreeWidgetItem *item = m_ui->qtdirList->currentItem())
        fixQtVersionName(m_ui->qtdirList->indexOfTopLevelItem(item));
con's avatar
con committed
}

/* Checks that the qt version name is unique
 * and otherwise changes the name
 *
 */
void QtDirWidget::fixQtVersionName(int index)
{
    int count = m_versions.count();
hjk's avatar
hjk committed
    for (int i = 0; i < count; ++i) {
        if (i != index) {
            if (m_versions.at(i)->name() == m_versions.at(index)->name()) {
con's avatar
con committed
                // Same name, find new name
                QString name = m_versions.at(index)->name();
                QRegExp regexp("^(.*)\\((\\d)\\)$");
                if (regexp.exactMatch(name)) {
                    // Alreay in Name (#) format
                    name = regexp.cap(1) + "(" + QString().setNum(regexp.cap(2).toInt() + 1) + ")";
                } else {
                    name = name + " (2)";
                }
                // set new name
                m_versions[index]->setName(name);
                m_ui->qtdirList->topLevelItem(index)->setText(0, name);
                m_ui->defaultCombo->setItemText(index, name);
con's avatar
con committed

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

void QtDirWidget::updateCurrentQtPath()
{
    QTreeWidgetItem *currentItem = m_ui->qtdirList->currentItem();
    Q_ASSERT(currentItem);
    int currentItemIndex = m_ui->qtdirList->indexOfTopLevelItem(currentItem);
    if (m_versions[currentItemIndex]->path() == m_ui->qtPath->path())
    m_versions[currentItemIndex]->setPath(m_ui->qtPath->path());
    currentItem->setText(1, QDir::toNativeSeparators(m_versions[currentItemIndex]->path()));
con's avatar
con committed

    showEnvironmentPage(currentItem);

    if (m_versions[currentItemIndex]->isValid()) {
        bool hasLog = !currentItem->data(2, Qt::UserRole).toString().isEmpty();
        bool hasHelper = m_versions[currentItemIndex]->hasDebuggingHelper();
        if (hasHelper) {
            currentItem->setData(2, Qt::DecorationRole, QIcon(":/extensionsystem/images/ok.png"));
            m_ui->debuggingHelperStateLabel->setPixmap(QPixmap(":/extensionsystem/images/ok.png"));
        } else {
            currentItem->setData(2, Qt::DecorationRole, QIcon(":/extensionsystem/images/error.png"));
            m_ui->debuggingHelperStateLabel->setPixmap(QPixmap(":/extensionsystem/images/error.png"));
        m_ui->showLogButton->setEnabled(hasLog);
        currentItem->setData(2, Qt::DecorationRole, QIcon());
        m_ui->debuggingHelperStateLabel->setPixmap(QPixmap());
con's avatar
con committed
}

void QtDirWidget::updateCurrentMingwDirectory()
{
    QTreeWidgetItem *currentItem = m_ui->qtdirList->currentItem();
    Q_ASSERT(currentItem);
    int currentItemIndex = m_ui->qtdirList->indexOfTopLevelItem(currentItem);
    m_versions[currentItemIndex]->setMingwDirectory(m_ui->mingwPath->path());
con's avatar
con committed
}

void QtDirWidget::msvcVersionChanged()
{
    const QString &msvcVersion = m_ui->msvcComboBox->currentText();
    QTreeWidgetItem *currentItem = m_ui->qtdirList->currentItem();
    Q_ASSERT(currentItem);
    int currentItemIndex = m_ui->qtdirList->indexOfTopLevelItem(currentItem);
con's avatar
con committed
    m_versions[currentItemIndex]->setMsvcVersion(msvcVersion);
}

QList<QtVersion *> QtDirWidget::versions() const
{
    return m_versions;
}

int QtDirWidget::defaultVersion() const
{
    return m_defaultVersion;
}

///
/// QtVersion
///

QtVersion::QtVersion(const QString &name, const QString &path, int id, bool isSystemVersion)
    : m_name(name),
    m_isSystemVersion(isSystemVersion),
    m_notInstalled(false),
    m_defaultConfigIsDebug(true),
    m_defaultConfigIsDebugAndRelease(true),
    m_hasDebuggingHelper(false)
con's avatar
con committed
{
    setPath(path);
hjk's avatar
hjk committed
    if (id == -1)
con's avatar
con committed
        m_id = getUniqueId();
    else
        m_id = id;
}

QtVersion::QtVersion(const QString &name, const QString &path)
    : m_name(name),
    m_versionInfoUpToDate(false),
    m_mkspecUpToDate(false),
    m_isSystemVersion(false),
    m_hasDebuggingHelper(false)
con's avatar
con committed
{
    setPath(path);
    m_id = getUniqueId();
}

QString QtVersion::name() const
{
    return m_name;
}

QString QtVersion::path() const
{
    return m_path;
}

QString QtVersion::sourcePath() const
{
    return m_sourcePath;
}

QString QtVersion::mkspec() const
{
    updateMkSpec();
    return m_mkspec;
}

dt's avatar
dt committed
QString QtVersion::mkspecPath() const
{
    updateMkSpec();
    return m_mkspecFullPath;
}

QString QtVersion::qtVersionString() const
{
    qmakeCommand();
    return m_qtVersionString;
}

con's avatar
con committed
QHash<QString,QString> QtVersion::versionInfo() const
{
    updateVersionInfo();
    return m_versionInfo;
}

void QtVersion::setName(const QString &name)
{
    m_name = name;
}

void QtVersion::setPath(const QString &path)
{
    m_path = QDir::cleanPath(path);
    updateSourcePath();
    m_versionInfoUpToDate = false;
    m_mkspecUpToDate = false;
    m_qmakeCommand = QString::null;
// TODO do i need to optimize this?
    m_hasDebuggingHelper = !dumperLibrary().isEmpty();
}

QString QtVersion::dumperLibrary() const
{
    uint hash = qHash(path());
    QString qtInstallData = versionInfo().value("QT_INSTALL_DATA");
    if (qtInstallData.isEmpty())
        qtInstallData = path();
    QStringList directories;
    directories
            << (qtInstallData + "/qtc-debugging-helper/")
            << (QApplication::applicationDirPath() + "/../qtc-debugging-helper/" + QString::number(hash)) + "/"
            << (QDesktopServices::storageLocation(QDesktopServices::DataLocation) + "/qtc-debugging-helper/" + QString::number(hash)) + "/";
    foreach(const QString &directory, directories) {
#if defined(Q_OS_WIN)
        QFileInfo fi(directory + "debug/gdbmacros.dll");
        QFileInfo fi(directory + "libgdbmacros.dylib");
        QFileInfo fi(directory + "libgdbmacros.so");
#endif
        if (fi.exists())
            return fi.filePath();
    }
    return QString();
con's avatar
con committed
}

void QtVersion::updateSourcePath()
{
    m_sourcePath = m_path;
    QFile qmakeCache(m_path + QLatin1String("/.qmake.cache"));
    if (qmakeCache.exists()) {
        qmakeCache.open(QIODevice::ReadOnly | QIODevice::Text);
        QTextStream stream(&qmakeCache);
        while (!stream.atEnd()) {
            QString line = stream.readLine().trimmed();
            if (line.startsWith(QLatin1String("QT_SOURCE_TREE"))) {
                m_sourcePath = line.split(QLatin1Char('=')).at(1).trimmed();
                if (m_sourcePath.startsWith(QLatin1String("$$quote("))) {
                    m_sourcePath.remove(0, 8);
                    m_sourcePath.chop(1);
                }
                break;
            }
        }
    }
}

// Returns the version that was used to build the project in that directory
// That is returns the directory
// To find out wheter we already have a qtversion for that directory call
// QtVersion *QtVersionManager::qtVersionForDirectory(const QString directory);
QString QtVersionManager::findQtVersionFromMakefile(const QString &directory)
{
    QString result = QString::null;
    bool debugAdding = false;
    QFile makefile(directory + "/Makefile" );
    if (makefile.exists() && makefile.open(QFile::ReadOnly)) {
        QTextStream ts(&makefile);
        while (!ts.atEnd()) {
            QString line = ts.readLine();
            QRegExp r1("QMAKE\\s*=(.*)");
            if (r1.exactMatch(line)) {
                if (debugAdding)
                    qDebug()<<"#~~ QMAKE is:"<<r1.cap(1).trimmed();
                QFileInfo qmake(r1.cap(1).trimmed());
                QFileInfo binDir(qmake.absolutePath());
                QString qtDir = binDir.absolutePath();
                if (debugAdding)
                    qDebug() << "#~~ QtDir:"<<qtDir;
                // Now we have the qtDir
                // look through the qtversions wheter we already have that qt version setup
                return qtDir;
            }
        }
        makefile.close();
    }
    return result;
}

QtVersion *QtVersionManager::qtVersionForDirectory(const QString &directory)
{
   foreach(QtVersion *v, versions()) {
        if (v->path() == directory) {
            return v;
            break;
        }
    }
   return 0;
}

QtVersion::QmakeBuildConfig QtVersionManager::scanMakefileForQmakeConfig(const QString &directory, QtVersion::QmakeBuildConfig defaultBuildConfig)
{
    bool debugScan = false;
    QtVersion::QmakeBuildConfig result = QtVersion::NoBuild;
    QFile makefile(directory + "/Makefile" );
    if (makefile.exists() && makefile.open(QFile::ReadOnly)) {
        QTextStream ts(&makefile);
        while (!ts.atEnd()) {
            QString line = ts.readLine();
            if (line.startsWith("# Command:")) {
                // if nothing is specified
                result = defaultBuildConfig;

                // Actually parsing that line is not trivial in the general case
                // There might things like this
                // # Command: /home/dteske/git/bqt-45/bin/qmake -unix CONFIG+=debug\ release CONFIG\ +=\ debug_and_release\ debug -o Makefile test.pro
                // which sets debug_and_release and debug
                // or something like this:
                //[...] CONFIG+=debug\ release CONFIG\ +=\ debug_and_release\ debug CONFIG\ -=\ debug_and_release CONFIG\ -=\ debug -o Makefile test.pro
                // which sets -build_all and release

                // To parse that, we search for the first CONFIG, then look for " " which is not after a "\" or the end
                // And then look at each config individually
                // we then remove all "\ " with just " "
                // += sets adding flags
                // -= sets removing flags
                // and then split the string after the =
                // and go over each item separetly
                // debug sets/removes the flag DebugBuild
                // release removes/sets the flag DebugBuild
                // debug_and_release sets/removes the flag BuildAll
                int pos = line.indexOf("CONFIG");
                if (pos != -1) {
                    // Chopped of anything that is not interesting
                    line = line.mid(pos);
                    line = line.trimmed();
                    if (debugScan)
                        qDebug()<<"chopping line :"<<line;

                    //Now chop into parts that are intresting
                    QStringList parts;