From 14eeb631a6bdbe4d2f44f521f02610dea83be22d Mon Sep 17 00:00:00 2001
From: Tobias Hunger <tobias.hunger@nokia.com>
Date: Mon, 29 Mar 2010 13:14:01 +0200
Subject: [PATCH] Add target setup page for the wizards to use

 * This page differs from the targetspage as it supports
   importing of existing builds and will replace the
   targetspage in time.

Reviewed-by: dt
---
 .../qt4projectmanager/qt4projectmanager.pro   |   2 +
 .../wizards/targetsetuppage.cpp               | 370 ++++++++++++++++++
 .../wizards/targetsetuppage.h                 | 124 ++++++
 3 files changed, 496 insertions(+)
 create mode 100644 src/plugins/qt4projectmanager/wizards/targetsetuppage.cpp
 create mode 100644 src/plugins/qt4projectmanager/wizards/targetsetuppage.h

diff --git a/src/plugins/qt4projectmanager/qt4projectmanager.pro b/src/plugins/qt4projectmanager/qt4projectmanager.pro
index d11288f2db6..eb3b98f9893 100644
--- a/src/plugins/qt4projectmanager/qt4projectmanager.pro
+++ b/src/plugins/qt4projectmanager/qt4projectmanager.pro
@@ -28,6 +28,7 @@ HEADERS += qt4projectmanagerplugin.h \
     wizards/filespage.h \
     wizards/qtwizard.h \
     wizards/targetspage.h \
+    wizards/targetsetuppage.h \
     qt4projectmanagerconstants.h \
     makestep.h \
     qmakestep.h \
@@ -69,6 +70,7 @@ SOURCES += qt4projectmanagerplugin.cpp \
     wizards/filespage.cpp \
     wizards/qtwizard.cpp \
     wizards/targetspage.cpp \
+    wizards/targetsetuppage.cpp \
     makestep.cpp \
     qmakestep.cpp \
     qt4runconfiguration.cpp \
diff --git a/src/plugins/qt4projectmanager/wizards/targetsetuppage.cpp b/src/plugins/qt4projectmanager/wizards/targetsetuppage.cpp
new file mode 100644
index 00000000000..db6c0299b23
--- /dev/null
+++ b/src/plugins/qt4projectmanager/wizards/targetsetuppage.cpp
@@ -0,0 +1,370 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (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 http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#include "targetsetuppage.h"
+
+#include "qt4project.h"
+#include "qt4target.h"
+
+#include <utils/pathchooser.h>
+
+#include <QtGui/QHeaderView>
+#include <QtGui/QLabel>
+#include <QtGui/QLayout>
+#include <QtGui/QTreeWidget>
+
+using namespace Qt4ProjectManager::Internal;
+
+TargetSetupPage::TargetSetupPage(QWidget *parent) :
+    QWizardPage(parent)
+{
+    resize(500, 400);
+    setTitle(tr("Set up targets for your project"));
+
+    QVBoxLayout *vbox = new QVBoxLayout(this);
+
+    QLabel * importLabel = new QLabel(this);
+    importLabel->setText(tr("Qt Creator can set up the following targets:"));
+    importLabel->setWordWrap(true);
+
+    vbox->addWidget(importLabel);
+
+    m_treeWidget = new QTreeWidget(this);
+    m_treeWidget->setColumnCount(3);
+    m_treeWidget->header()->setResizeMode(0, QHeaderView::ResizeToContents);
+    m_treeWidget->header()->setResizeMode(1, QHeaderView::ResizeToContents);
+    m_treeWidget->setHeaderLabels(QStringList() << tr("Qt Version") << tr("Status") << tr("Directory"));
+    vbox->addWidget(m_treeWidget);
+
+    QHBoxLayout *hbox = new QHBoxLayout;
+    QLabel *directoryLabel = new QLabel(this);
+    directoryLabel->setText(tr("Scan for builds"));
+    hbox->addWidget(directoryLabel);
+
+    m_directoryChooser = new Utils::PathChooser(this);
+    m_directoryChooser->setPromptDialogTitle(tr("Directory to import builds from"));
+    m_directoryChooser->setExpectedKind(Utils::PathChooser::Directory);
+    hbox->addWidget(m_directoryChooser);
+    vbox->addLayout(hbox);
+
+    connect(m_directoryChooser, SIGNAL(changed(QString)),
+            this, SLOT(importDirectoryAdded(QString)));
+}
+
+TargetSetupPage::~TargetSetupPage()
+{
+    resetInfos();
+}
+
+void TargetSetupPage::setImportInfos(const QList<ImportInfo> &infos)
+{
+    disconnect(m_treeWidget, SIGNAL(itemChanged(QTreeWidgetItem*,int)),
+               this, SLOT(itemWasChanged()));
+
+    // Clean up!
+    resetInfos();
+
+    // Find possible targets:
+    QStringList targets;
+    foreach (const ImportInfo &i, infos) {
+        // Make sure we have no duplicate directories/version pairs:
+        bool skip = false;
+        foreach (const ImportInfo &j, m_infos) {
+            if ((j.directory == i.directory) &&
+                (j.version == i.version)) {
+                skip = true;
+                break;
+            }
+        }
+        if (skip)
+            continue;
+
+        m_infos.append(i);
+
+        QSet<QString> versionTargets = i.version->supportedTargetIds();
+        foreach (const QString &t, versionTargets) {
+            if (!targets.contains(t))
+                targets.append(t);
+        }
+    }
+    qSort(targets.begin(), targets.end());
+
+    Qt4TargetFactory factory;
+    foreach (const QString &t, targets) {
+        QTreeWidgetItem *targetItem = new QTreeWidgetItem(m_treeWidget);
+        targetItem->setText(0, factory.displayNameForId(t));
+        targetItem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
+        targetItem->setData(0, Qt::UserRole, t);
+        targetItem->setExpanded(true);
+
+        int pos = -1;
+        foreach (const ImportInfo &i, infos) {
+            ++pos;
+
+            if (!i.version->supportsTargetId(t))
+                continue;
+            QTreeWidgetItem *versionItem = new QTreeWidgetItem(targetItem);
+            // Column 0:
+            versionItem->setText(0, i.version->displayName());
+            versionItem->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable);
+            versionItem->setData(0, Qt::UserRole, pos);
+            // Prefer imports to creating new builds, but precheck any
+            // Qt that exists (if there is no import with that version)
+            if (!i.isExistingBuild) {
+                bool haveExistingBuildForQtVersion = false;
+                foreach (const ImportInfo &j, m_infos) {
+                    if (j.isExistingBuild && j.version == i.version) {
+                        haveExistingBuildForQtVersion = true;
+                        break;
+                    }
+                }
+                versionItem->setCheckState(0, haveExistingBuildForQtVersion ? Qt::Unchecked : Qt::Checked);
+            } else {
+                versionItem->setCheckState(0, Qt::Checked);
+            }
+
+            // Column 1 (status):
+            versionItem->setText(1, i.isExistingBuild ? tr("Import", "Is this an import of an existing build or a new one?") :
+                                                        tr("New", "Is this an import of an existing build or a new one?"));
+
+            // Column 2 (directory):
+            versionItem->setText(2, QDir::toNativeSeparators(i.directory));
+        }
+    }
+
+    connect(m_treeWidget, SIGNAL(itemChanged(QTreeWidgetItem*,int)),
+            this, SLOT(itemWasChanged()));
+
+    emit completeChanged();
+}
+
+QList<TargetSetupPage::ImportInfo> TargetSetupPage::importInfos() const
+{
+    return m_infos;
+}
+
+bool TargetSetupPage::hasSelection() const
+{
+    for (int i = 0; i < m_treeWidget->topLevelItemCount(); ++i) {
+        QTreeWidgetItem * current = m_treeWidget->topLevelItem(i);
+        for (int j = 0; j < current->childCount(); ++j) {
+            QTreeWidgetItem * child = current->child(j);
+            if (child->checkState(0) != Qt::Checked)
+                return true;
+        }
+    }
+    return false;
+}
+
+bool TargetSetupPage::isTargetSelected(const QString &targetid) const
+{
+    for (int i = 0; i < m_treeWidget->topLevelItemCount(); ++i) {
+        QTreeWidgetItem * current = m_treeWidget->topLevelItem(i);
+        if (current->data(0, Qt::UserRole).toString() != targetid)
+            continue;
+        for (int j = 0; j < current->childCount(); ++j) {
+            QTreeWidgetItem * child = current->child(j);
+            if (child->checkState(0) == Qt::Checked)
+                return true;
+        }
+    }
+    return false;
+}
+
+bool TargetSetupPage::setupProject(Qt4ProjectManager::Qt4Project *project)
+{
+    Q_ASSERT(project->targets().isEmpty());
+
+    for (int i = 0; i < m_treeWidget->topLevelItemCount(); ++i) {
+        QTreeWidgetItem *current = m_treeWidget->topLevelItem(i);
+        QString targetId = current->data(0, Qt::UserRole).toString();
+
+        QList<BuildConfigurationInfo> targetInfos;
+        for (int j = 0; j < current->childCount(); ++j) {
+            QTreeWidgetItem *child = current->child(j);
+            if (child->checkState(0) != Qt::Checked)
+                continue;
+
+            const ImportInfo &info = m_infos.at(child->data(0, Qt::UserRole).toInt());
+
+            targetInfos.append(BuildConfigurationInfo(info.version, QtVersion::QmakeBuildConfigs(info.buildConfig | QtVersion::DebugBuild),
+                                                      info.additionalArguments, info.directory));
+            targetInfos.append(BuildConfigurationInfo(info.version, info.buildConfig,
+                                                      info.additionalArguments, info.directory));
+        }
+
+        // create the target:
+        Qt4Target *target = 0;
+        if (targetInfos.isEmpty())
+            target = project->targetFactory()->create(project, targetId);
+        else
+            target = project->targetFactory()->create(project, targetId, targetInfos);
+
+        if (target)
+            project->addTarget(target);
+    }
+
+    return !project->targets().isEmpty();
+}
+
+void TargetSetupPage::itemWasChanged()
+{
+    emit completeChanged();
+}
+
+bool TargetSetupPage::isComplete() const
+{
+    return hasSelection();
+}
+
+void TargetSetupPage::setImportDirectoryBrowsingEnabled(bool browsing)
+{
+    m_directoryChooser->setEnabled(browsing);
+    m_directoryChooser->setVisible(browsing);
+}
+
+void TargetSetupPage::setImportDirectoryBrowsingLocation(const QString &directory)
+{
+    m_directoryChooser->setInitialBrowsePathBackup(directory);
+}
+
+void TargetSetupPage::setShowLocationInformation(bool location)
+{
+    m_treeWidget->setColumnCount(location ? 3 : 1);
+}
+
+QList<TargetSetupPage::ImportInfo>
+TargetSetupPage::importInfosForKnownQtVersions(Qt4ProjectManager::Qt4Project *project)
+{
+    QList<ImportInfo> results;
+    QtVersionManager * vm = QtVersionManager::instance();
+    QList<QtVersion *> validVersions = vm->validVersions();
+    foreach (QtVersion *v, validVersions) {
+        ImportInfo info;
+        // ToDo: Check whether shadowbuilding is possible and use sourcedir if not:
+        //       This needs a shadowbuilding patch to land
+        if (project)
+            info.directory = project->defaultTopLevelBuildDirectory();
+        info.isExistingBuild = false;
+        info.isTemporary = false;
+        info.version = v;
+        results.append(info);
+    }
+    return results;
+}
+
+QList<TargetSetupPage::ImportInfo> TargetSetupPage::filterImportInfos(const QSet<QString> &validTargets,
+                                                                      const QList<ImportInfo> &infos)
+{
+    QList<ImportInfo> results;
+    foreach (const ImportInfo &info, infos) {
+        Q_ASSERT(info.version);
+        foreach (const QString &target, validTargets) {
+            if (info.version->supportsTargetId(target))
+                results.append(info);
+        }
+    }
+    return results;
+}
+
+QList<TargetSetupPage::ImportInfo>
+TargetSetupPage::recursivelyCheckDirectoryForBuild(const QString &directory, int maxdepth)
+{
+    QList<ImportInfo> results;
+
+    if (maxdepth <= 0)
+        return results;
+
+    // Check for in-source builds first:
+    QString qmakeBinary =  QtVersionManager::findQMakeBinaryFromMakefile(directory);
+
+    // Recurse into subdirectories:
+    if (qmakeBinary.isNull()) {
+        QStringList subDirs = QDir(directory).entryList(QDir::Dirs | QDir::NoDotAndDotDot);
+        foreach (QString subDir, subDirs)
+            results.append(recursivelyCheckDirectoryForBuild(directory + QChar('/') + subDir, maxdepth - 1));
+        return results;
+    }
+
+    // Shiny fresh directory with a Makefile...
+    QtVersionManager * vm = QtVersionManager::instance();
+    TargetSetupPage::ImportInfo info;
+    info.directory = directory;
+
+    // This also means we have a build in there
+    // First get the qt version
+    info.version = vm->qtVersionForQMakeBinary(qmakeBinary);
+    info.isExistingBuild = true;
+
+    // Okay does not yet exist, create
+    if (!info.version) {
+        info.version = new QtVersion(qmakeBinary);
+        info.isTemporary = true;
+    }
+
+    QPair<QtVersion::QmakeBuildConfigs, QStringList> result =
+            QtVersionManager::scanMakeFile(directory, info.version->defaultBuildConfig());
+    info.buildConfig = result.first;
+    info.additionalArguments = Qt4BuildConfiguration::removeSpecFromArgumentList(result.second);
+
+    QString parsedSpec = Qt4BuildConfiguration::extractSpecFromArgumentList(result.second, directory, info.version);
+    QString versionSpec = info.version->mkspec();
+
+    // Compare mkspecs and add to additional arguments
+    if (parsedSpec.isEmpty() || parsedSpec == versionSpec || parsedSpec == "default") {
+        // using the default spec, don't modify additional arguments
+    } else {
+        info.additionalArguments.prepend(parsedSpec);
+        info.additionalArguments.prepend("-spec");
+    }
+
+    results.append(info);
+    return results;
+}
+
+void TargetSetupPage::importDirectoryAdded(const QString &directory)
+{
+    QFileInfo dir(directory);
+    if (!dir.exists() || !dir.isDir())
+        return;
+    m_directoryChooser->setPath(QString());
+    QList<ImportInfo> tmp = m_infos;
+    tmp.append(recursivelyCheckDirectoryForBuild(directory));
+    setImportInfos(tmp);
+}
+
+void TargetSetupPage::resetInfos()
+{
+    m_treeWidget->clear();
+    foreach (const ImportInfo &info, m_infos) {
+        if (info.isTemporary)
+            delete info.version;
+    }
+    m_infos.clear();
+}
diff --git a/src/plugins/qt4projectmanager/wizards/targetsetuppage.h b/src/plugins/qt4projectmanager/wizards/targetsetuppage.h
new file mode 100644
index 00000000000..3ecd7c2b037
--- /dev/null
+++ b/src/plugins/qt4projectmanager/wizards/targetsetuppage.h
@@ -0,0 +1,124 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (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 http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#ifndef TARGETSETUPPAGE_H
+#define TARGETSETUPPAGE_H
+
+#include "qtversionmanager.h"
+
+#include <QtCore/QList>
+#include <QtCore/QSet>
+#include <QtCore/QString>
+
+#include <QtGui/QWizard>
+
+QT_BEGIN_NAMESPACE
+class QTreeWidget;
+QT_END_NAMESPACE
+
+namespace Utils {
+class PathChooser;
+}
+
+namespace Qt4ProjectManager {
+class Qt4Project;
+
+namespace Internal {
+
+class TargetSetupPage : public QWizardPage
+{
+    Q_OBJECT
+
+public:
+    struct ImportInfo {
+        ImportInfo() :
+            version(0),
+            isTemporary(false),
+            buildConfig(QtVersion::QmakeBuildConfig(0)),
+            isExistingBuild(false)
+        {
+            if (version && version->isValid())
+                buildConfig = version->defaultBuildConfig();
+        }
+
+        ImportInfo(const ImportInfo &other) :
+            version(other.version),
+            isTemporary(other.isTemporary),
+            buildConfig(other.buildConfig),
+            additionalArguments(other.additionalArguments),
+            directory(other.directory),
+            isExistingBuild(other.isExistingBuild)
+        { }
+
+        QtVersion *version;
+        bool isTemporary;
+        QtVersion::QmakeBuildConfigs buildConfig;
+        QStringList additionalArguments;
+        QString directory;
+        bool isExistingBuild;
+    };
+
+    explicit TargetSetupPage(QWidget* parent = 0);
+    ~TargetSetupPage();
+
+    void setImportInfos(const QList<ImportInfo> &infos);
+    QList<ImportInfo> importInfos() const;
+
+    void setImportDirectoryBrowsingEnabled(bool browsing);
+    void setImportDirectoryBrowsingLocation(const QString &directory);
+    void setShowLocationInformation(bool location);
+
+    static QList<ImportInfo> importInfosForKnownQtVersions(Qt4ProjectManager::Qt4Project *project);
+    static QList<ImportInfo> filterImportInfos(const QSet<QString> &validTargets,
+                                               const QList<ImportInfo> &infos);
+
+    static QList<ImportInfo> recursivelyCheckDirectoryForBuild(const QString &directory, int maxdepth = 3);
+
+    bool hasSelection() const;
+    bool isTargetSelected(const QString &targetid) const;
+    bool isComplete() const;
+
+    bool setupProject(Qt4Project *project);
+
+private slots:
+    void itemWasChanged();
+    void importDirectoryAdded(const QString &);
+
+private:
+    void resetInfos();
+
+    QList<ImportInfo> m_infos;
+    QTreeWidget *m_treeWidget;
+    Utils::PathChooser *m_directoryChooser;
+};
+
+} // namespace Internal
+} // namespace Qt4ProjectManager
+
+#endif // TARGETSETUPPAGE_H
-- 
GitLab