diff options
author | Friedemann Kleint <Friedemann.Kleint@nokia.com> | 2010-03-12 11:20:32 +0100 |
---|---|---|
committer | Friedemann Kleint <Friedemann.Kleint@nokia.com> | 2010-03-12 11:20:32 +0100 |
commit | 1606cf1b330269dba769f299f5446108ef062ff3 (patch) | |
tree | e0a01697b272df7dc3de11a5258ff70294304354 | |
parent | e82219b344ee471dbb5d8e7f1351404ff9616d6c (diff) |
Wizards/Custom Wizards: Add code.
Task-number: QTCREATORBUG-423
15 files changed, 1484 insertions, 5 deletions
diff --git a/src/plugins/coreplugin/basefilewizard.cpp b/src/plugins/coreplugin/basefilewizard.cpp index 18e4285b72..4b04d72ad7 100644 --- a/src/plugins/coreplugin/basefilewizard.cpp +++ b/src/plugins/coreplugin/basefilewizard.cpp @@ -185,6 +185,7 @@ class BaseFileWizardParameterData : public QSharedData { public: explicit BaseFileWizardParameterData(IWizard::WizardKind kind = IWizard::FileWizard); + void clear(); IWizard::WizardKind kind; QIcon icon; @@ -200,6 +201,17 @@ BaseFileWizardParameterData::BaseFileWizardParameterData(IWizard::WizardKind k) { } +void BaseFileWizardParameterData::clear() +{ + kind = IWizard::FileWizard; + icon = QIcon(); + description.clear(); + displayName.clear(); + id.clear(); + category.clear(); + displayCategory.clear(); +} + BaseFileWizardParameters::BaseFileWizardParameters(IWizard::WizardKind kind) : m_d(new BaseFileWizardParameterData(kind)) { @@ -221,6 +233,21 @@ BaseFileWizardParameters::~BaseFileWizardParameters() { } +void BaseFileWizardParameters::clear() +{ + m_d->clear(); +} + +CORE_EXPORT QDebug operator<<(QDebug d, const BaseFileWizardParameters &p) +{ + d.nospace() << "Kind: " << p.kind() << " Id: " << p.id() + << " Category: " << p.category() + << " DisplayName: " << p.displayName() + << " Description: " << p.description() + << " DisplayCategory: " << p.displayCategory(); + return d; +} + IWizard::WizardKind BaseFileWizardParameters::kind() const { return m_d->kind; diff --git a/src/plugins/coreplugin/basefilewizard.h b/src/plugins/coreplugin/basefilewizard.h index 4f4efc043c..268b54f543 100644 --- a/src/plugins/coreplugin/basefilewizard.h +++ b/src/plugins/coreplugin/basefilewizard.h @@ -42,6 +42,7 @@ QT_BEGIN_NAMESPACE class QWizard; class QWizardPage; +class QDebug; QT_END_NAMESPACE namespace Core { @@ -105,6 +106,8 @@ public: BaseFileWizardParameters &operator=(const BaseFileWizardParameters&); ~BaseFileWizardParameters(); + void clear(); + IWizard::WizardKind kind() const; void setKind(IWizard::WizardKind k); @@ -130,6 +133,8 @@ private: QSharedDataPointer<BaseFileWizardParameterData> m_d; }; +CORE_EXPORT QDebug operator<<(QDebug d, const BaseFileWizardParameters &); + /* A generic wizard for creating files. * * The abstract methods: diff --git a/src/plugins/projectexplorer/baseprojectwizarddialog.h b/src/plugins/projectexplorer/baseprojectwizarddialog.h index 8bd5b1dbe9..f4f40c2781 100644 --- a/src/plugins/projectexplorer/baseprojectwizarddialog.h +++ b/src/plugins/projectexplorer/baseprojectwizarddialog.h @@ -51,12 +51,13 @@ class PROJECTEXPLORER_EXPORT BaseProjectWizardDialog : public QWizard Q_OBJECT protected: - explicit BaseProjectWizardDialog(QWidget *parent = 0); explicit BaseProjectWizardDialog(Utils::ProjectIntroPage *introPage, int introId = -1, QWidget *parent = 0); public: + explicit BaseProjectWizardDialog(QWidget *parent = 0); + virtual ~BaseProjectWizardDialog(); QString projectName() const; diff --git a/src/plugins/projectexplorer/customwizard/customwizard.cpp b/src/plugins/projectexplorer/customwizard/customwizard.cpp new file mode 100644 index 0000000000..7f36a7a450 --- /dev/null +++ b/src/plugins/projectexplorer/customwizard/customwizard.cpp @@ -0,0 +1,396 @@ +/************************************************************************** +** +** 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 "customwizard.h" +#include "customwizardparameters.h" +#include "customwizardpage.h" +#include "projectexplorer.h" +#include "baseprojectwizarddialog.h" + +#include <coreplugin/icore.h> +#include <coreplugin/mimedatabase.h> +#include <cpptools/cpptoolsconstants.h> +#include <utils/qtcassert.h> + +#include <QtCore/QDebug> +#include <QtCore/QFile> +#include <QtCore/QMap> +#include <QtCore/QDir> +#include <QtCore/QFileInfo> + +enum { debug = 0 }; + +static const char templatePathC[] = "templates/wizards"; +static const char configFileC[] = "wizard.xml"; + +namespace ProjectExplorer { + +struct CustomWizardPrivate { + QSharedPointer<Internal::CustomWizardParameters> m_parameters; +}; + +CustomWizard::CustomWizard(const Core::BaseFileWizardParameters& baseFileParameters, + QObject *parent) : + Core::BaseFileWizard(baseFileParameters, parent), + d(new CustomWizardPrivate) +{ +} + +CustomWizard::~CustomWizard() +{ + delete d; +} + +void CustomWizard::setParameters(const CustomWizardParametersPtr &p) +{ + d->m_parameters = p; +} + +// Add a wizard page with an id, visibly warn if something goes wrong. +static inline void addWizardPage(QWizard *w, QWizardPage *p, int id) +{ + if (id == -1) { + w->addPage(p); + } else { + if (w->pageIds().contains(id)) { + qWarning("Page %d already present in custom wizard dialog, defaulting to add.", id); + w->addPage(p); + } else { + w->setPage(id, p); + } + } +} + +// Initialize a wizard with a custom file page. +void CustomWizard::initWizardDialog(QWizard *wizard, const QString &defaultPath, + const WizardPageList &extensionPages) const +{ + QTC_ASSERT(!parameters().isNull(), return); + Internal::CustomWizardPage *customPage = new Internal::CustomWizardPage(parameters()->fields); + customPage->setPath(defaultPath); + addWizardPage(wizard, customPage, parameters()->firstPageId); + if (!parameters()->fieldPageTitle.isEmpty()) + customPage->setTitle(parameters()->fieldPageTitle); + foreach(QWizardPage *ep, extensionPages) + wizard->addPage(ep); + Core::BaseFileWizard::setupWizard(wizard); + if (debug) + qDebug() << "initWizardDialog" << wizard << wizard->pageIds(); +} + +QWizard *CustomWizard::createWizardDialog(QWidget *parent, + const QString &defaultPath, + const WizardPageList &extensionPages) const +{ + QTC_ASSERT(!d->m_parameters.isNull(), return 0); + QWizard *wizard = new QWizard(parent); + initWizardDialog(wizard, defaultPath, extensionPages); + return wizard; +} + +// Replace field values delimited by '%' with special modifiers: +// %Field% -> simple replacement +// %Field:l% -> lower case replacement, 'u' for upper case and so on. +static void replaceFields(const CustomProjectWizard::FieldReplacementMap &fm, QString *s) +{ + const QChar delimiter = QLatin1Char('%'); + const QChar modifierDelimiter = QLatin1Char(':'); + int pos = 0; + while (pos < s->size()) { + pos = s->indexOf(delimiter, pos); + if (pos < 0) + break; + int nextPos = s->indexOf(delimiter, pos + 1); + if (nextPos == -1) + break; + nextPos++; // Point past 2nd delimiter + if (nextPos == pos + 2) { + pos = nextPos; // Skip '%%' + continue; + } + // Evaluate field specification for modifiers + // "%field:l%" + QString fieldSpec = s->mid(pos + 1, nextPos - pos - 2); + const int fieldSpecSize = fieldSpec.size(); + char modifier = '\0'; + if (fieldSpecSize >= 3 && fieldSpec.at(fieldSpecSize - 2) == modifierDelimiter) { + modifier = fieldSpec.at(fieldSpecSize - 1).toLatin1(); + fieldSpec.truncate(fieldSpecSize - 2); + } + const CustomProjectWizard::FieldReplacementMap::const_iterator it = fm.constFind(fieldSpec); + if (it == fm.constEnd()) { + pos = nextPos; // Not found, skip + continue; + } + // Assign + QString replacement = it.value(); + switch (modifier) { + case 'l': + replacement = it.value().toLower(); + break; + case 'u': + replacement = it.value().toUpper(); + break; + default: + break; + } + s->replace(pos, nextPos - pos, replacement); + pos += replacement.size(); + } +} + +// Read out files and store contents with field contents replaced. +static inline bool createFile(Internal::CustomWizardFile cwFile, + const QString &sourceDirectory, + const QString &targetDirectory, + const CustomProjectWizard::FieldReplacementMap &fm, + Core::GeneratedFiles *files, + QString *errorMessage) +{ + const QChar slash = QLatin1Char('/'); + const QString sourcePath = sourceDirectory + slash + cwFile.source; + replaceFields(fm, &cwFile.target); + const QString targetPath = QDir::toNativeSeparators(targetDirectory + slash + cwFile.target); + if (debug) + qDebug() << "generating " << targetPath << sourcePath << fm; + QFile file(sourcePath); + if (!file.open(QIODevice::ReadOnly|QIODevice::Text)) { + *errorMessage = QString::fromLatin1("Cannot open %1: %2").arg(sourcePath, file.errorString()); + return false; + } + QString contents = QString::fromLocal8Bit(file.readAll()); + if (!contents.isEmpty() && !fm.isEmpty()) + replaceFields(fm, &contents); + Core::GeneratedFile generatedFile; + generatedFile.setContents(contents); + generatedFile.setPath(targetPath); + files->push_back(generatedFile); + return true; +} + +// Helper to find a specific wizard page of a wizard by type. +template <class WizardPage> + WizardPage *findWizardPage(const QWizard *w) +{ + foreach (int pageId, w->pageIds()) + if (WizardPage *wp = qobject_cast<WizardPage *>(w->page(pageId))) + return wp; + return 0; +} + +Core::GeneratedFiles CustomWizard::generateFiles(const QWizard *dialog, QString *errorMessage) const +{ + // Look for the Custom field page to find the path + const Internal::CustomWizardPage *cwp = findWizardPage<Internal::CustomWizardPage>(dialog); + QTC_ASSERT(cwp, return Core::GeneratedFiles()) + QString path = cwp->path(); + const FieldReplacementMap fieldMap = defaultReplacementMap(dialog); + if (debug) + qDebug() << "CustomWizard::generateFiles" << dialog << path << fieldMap; + return generateWizardFiles(path, fieldMap, errorMessage); +} + +Core::GeneratedFiles CustomWizard::generateWizardFiles(const QString &targetPath, + const FieldReplacementMap &fieldReplacementMap, + QString *errorMessage) const +{ + if (debug) + qDebug() << "Replacements" << fieldReplacementMap; + // Create files + Core::GeneratedFiles rc; + foreach(const Internal::CustomWizardFile &file, d->m_parameters->files) + if (!createFile(file, d->m_parameters->directory, targetPath, fieldReplacementMap, &rc, errorMessage)) + return Core::GeneratedFiles(); + return rc; +} + +// Create a default replacement map from the wizard dialog via fields +// and add some useful fields. +CustomWizard::FieldReplacementMap CustomWizard::defaultReplacementMap(const QWizard *w) const +{ + FieldReplacementMap fieldReplacementMap; + foreach(const Internal::CustomWizardField &field, d->m_parameters->fields) { + const QString value = w->field(field.name).toString(); + fieldReplacementMap.insert(field.name, value); + } + const Core::MimeDatabase *mdb = Core::ICore::instance()->mimeDatabase(); + fieldReplacementMap.insert(QLatin1String("CppSourceSuffix"), + mdb->preferredSuffixByType(QLatin1String(CppTools::Constants::CPP_SOURCE_MIMETYPE))); + fieldReplacementMap.insert(QLatin1String("CppHeaderSuffix"), + mdb->preferredSuffixByType(QLatin1String(CppTools::Constants::CPP_HEADER_MIMETYPE))); + return fieldReplacementMap; +} + +CustomWizard::CustomWizardParametersPtr CustomWizard::parameters() const +{ + return d->m_parameters; +} + +// Static factory map +typedef QMap<QString, QSharedPointer<ICustomWizardFactory> > CustomWizardFactoryMap; +Q_GLOBAL_STATIC(CustomWizardFactoryMap, customWizardFactoryMap) + +void CustomWizard::registerFactory(const QString &name, const ICustomWizardFactoryPtr &f) +{ + customWizardFactoryMap()->insert(name, f); +} + +CustomWizard *CustomWizard::createWizard(const CustomWizardParametersPtr &p, const Core::BaseFileWizardParameters &b) +{ + CustomWizard * rc = 0; + if (p->klass.isEmpty()) { + // Use defaults for empty class names + switch (b.kind()) { + case Core::IWizard::ProjectWizard: + rc = new CustomProjectWizard(b); + break; + case Core::IWizard::FileWizard: + case Core::IWizard::ClassWizard: + rc = new CustomWizard(b); + break; + } + } else { + // Look up class name in map + const CustomWizardFactoryMap::const_iterator it = customWizardFactoryMap()->constFind(p->klass); + if (it != customWizardFactoryMap()->constEnd()) + rc = it.value()->create(b); + } + if (!rc) { + qWarning("Unable to create custom wizard for class %s.", qPrintable(p->klass)); + return 0; + } + rc->setParameters(p); + return rc; +} + +// Scan the subdirectories of the template directory for directories +// containing valid configuration files and parse them into wizards. +QList<CustomWizard*> CustomWizard::createWizards() +{ + QList<CustomWizard*> rc; + QString errorMessage; + const QString templateDirName = Core::ICore::instance()->resourcePath() + + QLatin1Char('/') + QLatin1String(templatePathC); + const QDir templateDir(templateDirName); + if (!templateDir.exists()) { + if (debug) + qWarning("Custom project template path %s does not exist.", qPrintable(templateDir.absolutePath())); + return rc; + } + + const QList<QFileInfo> dirs = templateDir.entryInfoList(QDir::Dirs|QDir::Readable|QDir::NoDotAndDotDot, + QDir::Name|QDir::IgnoreCase); + const QString configFile = QLatin1String(configFileC); + // Check and parse config file in each directory. + + foreach(const QFileInfo &dirFi, dirs) { + const QDir dir(dirFi.absoluteFilePath()); + if (dir.exists(configFile)) { + CustomWizardParametersPtr parameters(new Internal::CustomWizardParameters); + Core::BaseFileWizardParameters baseFileParameters; + if (parameters->parse(dir.absoluteFilePath(configFile), &baseFileParameters, &errorMessage)) { + parameters->directory = dir.absolutePath(); + if (debug) + qDebug() << (*parameters); + if (CustomWizard *w = createWizard(parameters, baseFileParameters)) + rc.push_back(w); + } else { + qWarning("Failed to initialize custom project wizard in %s: %s", + qPrintable(dir.absolutePath()), qPrintable(errorMessage)); + } + } + } + return rc; +} + +// --------------- CustomProjectWizard + +CustomProjectWizard::CustomProjectWizard(const Core::BaseFileWizardParameters& baseFileParameters, + QObject *parent) : + CustomWizard(baseFileParameters, parent) +{ +} + +QWizard *CustomProjectWizard::createWizardDialog(QWidget *parent, + const QString &defaultPath, + const WizardPageList &extensionPages) const +{ + QTC_ASSERT(!parameters().isNull(), return 0); + BaseProjectWizardDialog *projectDialog = new BaseProjectWizardDialog(parent); + initProjectWizardDialog(projectDialog, defaultPath, extensionPages); + return projectDialog; +} + +void CustomProjectWizard::initProjectWizardDialog(BaseProjectWizardDialog *w, + const QString &defaultPath, + const WizardPageList &extensionPages) const +{ + QTC_ASSERT(!parameters().isNull(), return); + if (!parameters()->fields.isEmpty()) { + Internal::CustomWizardFieldPage *cp = new Internal::CustomWizardFieldPage(parameters()->fields); + addWizardPage(w, cp, parameters()->firstPageId); + if (!parameters()->fieldPageTitle.isEmpty()) + cp->setTitle(parameters()->fieldPageTitle); + } + foreach(QWizardPage *ep, extensionPages) + w->addPage(ep); + w->setPath(defaultPath); + w->setProjectName(BaseProjectWizardDialog::uniqueProjectName(defaultPath)); + if (debug) + qDebug() << "initProjectWizardDialog" << w << w->pageIds(); +} + +Core::GeneratedFiles CustomProjectWizard::generateFiles(const QWizard *w, QString *errorMessage) const +{ + const BaseProjectWizardDialog *dialog = qobject_cast<const BaseProjectWizardDialog *>(w); + QTC_ASSERT(dialog, return Core::GeneratedFiles()) + const QString targetPath = dialog->path() + QLatin1Char('/') + dialog->projectName(); + // Add project name as macro. + FieldReplacementMap fieldReplacementMap = defaultReplacementMap(dialog); + fieldReplacementMap.insert(QLatin1String("ProjectName"), dialog->projectName()); + if (debug) + qDebug() << "CustomProjectWizard::generateFiles" << dialog << targetPath << fieldReplacementMap; + return generateWizardFiles(targetPath, fieldReplacementMap, errorMessage); +} + +bool CustomProjectWizard::postGenerateFiles(const QWizard *, const Core::GeneratedFiles &l, QString *errorMessage) +{ + // Post-Generate: Open the project + const QString proFileName = l.back().path(); + const bool opened = ProjectExplorer::ProjectExplorerPlugin::instance()->openProject(proFileName); + if (debug) + qDebug() << "CustomProjectWizard::postGenerateFiles: opened " << proFileName << opened; + if (opened) { + *errorMessage = tr("The project %1 could not be opened.").arg(proFileName); + return false; + } + return true; +} + +} // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/customwizard/customwizard.h b/src/plugins/projectexplorer/customwizard/customwizard.h new file mode 100644 index 0000000000..2b14e40435 --- /dev/null +++ b/src/plugins/projectexplorer/customwizard/customwizard.h @@ -0,0 +1,153 @@ +/************************************************************************** +** +** 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 CUSTOMPROJECTWIZARD_H +#define CUSTOMPROJECTWIZARD_H + +#include "../projectexplorer_export.h" + +#include <coreplugin/basefilewizard.h> + +#include <QtCore/QSharedPointer> +#include <QtCore/QList> + +QT_BEGIN_NAMESPACE +class QDir; +QT_END_NAMESPACE + +namespace ProjectExplorer { +class CustomWizard; +struct CustomWizardPrivate; +class BaseProjectWizardDialog; + +namespace Internal { + struct CustomWizardParameters; +} + +// Factory for creating wizard. Can be registered under a name +// in CustomWizard. +class ICustomWizardFactory { +public: + virtual CustomWizard *create(const Core::BaseFileWizardParameters& baseFileParameters, + QObject *parent = 0) const = 0; + virtual ~ICustomWizardFactory() {} +}; + +// Convenience template to create wizard classes. +template <class Wizard> class CustomWizardFactory : public ICustomWizardFactory { + virtual CustomWizard *create(const Core::BaseFileWizardParameters& baseFileParameters, + QObject *parent = 0) const + { return new Wizard(baseFileParameters, parent); } +}; + +// A custom wizard presenting CustomWizardDialog (fields page containing +// path control) for wizards of type "class" or "file". Serves as base class +// for project wizards. + +class PROJECTEXPLORER_EXPORT CustomWizard : public Core::BaseFileWizard +{ + Q_OBJECT +public: + typedef QMap<QString, QString> FieldReplacementMap; + typedef QSharedPointer<ICustomWizardFactory> ICustomWizardFactoryPtr; + + explicit CustomWizard(const Core::BaseFileWizardParameters& baseFileParameters, + QObject *parent = 0); + virtual ~CustomWizard(); + + // Can be reimplemented to create custom wizards. initWizardDialog() needs to be + // called. + virtual QWizard *createWizardDialog(QWidget *parent, + const QString &defaultPath, + const WizardPageList &extensionPages) const; + + virtual Core::GeneratedFiles generateFiles(const QWizard *w, QString *errorMessage) const; + + + // Register a factory for a derived custom widget + static void registerFactory(const QString &name, const ICustomWizardFactoryPtr &f); + template <class Wizard> static void registerFactory(const QString &name) + { registerFactory(name, ICustomWizardFactoryPtr(new CustomWizardFactory<Wizard>)); } + + // Create all wizards. As other plugins might register factories for derived + // classes, call it in extensionsInitialized(). + static QList<CustomWizard*> createWizards(); + +protected: + typedef QSharedPointer<Internal::CustomWizardParameters> CustomWizardParametersPtr; + + void initWizardDialog(QWizard *w, const QString &defaultPath, + const WizardPageList &extensionPages) const; + + // generate files in path + Core::GeneratedFiles generateWizardFiles(const QString &path, + const FieldReplacementMap &defaultFields, + QString *errorMessage) const; + // Create replacement map from QWizard fields with additional useful fields. + FieldReplacementMap defaultReplacementMap(const QWizard *w) const; + + CustomWizardParametersPtr parameters() const; + +private: + void setParameters(const CustomWizardParametersPtr &p); + + static CustomWizard *createWizard(const CustomWizardParametersPtr &p, const Core::BaseFileWizardParameters &b); + CustomWizardPrivate *d; +}; + +// A custom project wizard presenting CustomProjectWizardDialog +// (Project intro page and fields page) for wizards of type "project". +// Overwrites postGenerateFiles() to open the project file which is the +// last one by convention. + +class PROJECTEXPLORER_EXPORT CustomProjectWizard : public CustomWizard +{ + Q_OBJECT +public: + explicit CustomProjectWizard(const Core::BaseFileWizardParameters& baseFileParameters, + QObject *parent = 0); + + // Can be reimplemented to create custom project wizards. + // initProjectWizardDialog() needs to be called. + virtual QWizard *createWizardDialog(QWidget *parent, + const QString &defaultPath, + const WizardPageList &extensionPages) const; + + virtual Core::GeneratedFiles generateFiles(const QWizard *w, QString *errorMessage) const; + +protected: + virtual bool postGenerateFiles(const QWizard *w, const Core::GeneratedFiles &l, QString *errorMessage); + + void initProjectWizardDialog(BaseProjectWizardDialog *w, const QString &defaultPath, + const WizardPageList &extensionPages) const; +}; + +} // namespace ProjectExplorer + +#endif // CUSTOMPROJECTWIZARD_H diff --git a/src/plugins/projectexplorer/customwizard/customwizard.pri b/src/plugins/projectexplorer/customwizard/customwizard.pri new file mode 100644 index 0000000000..61475f3ae4 --- /dev/null +++ b/src/plugins/projectexplorer/customwizard/customwizard.pri @@ -0,0 +1,7 @@ +INCLUDEPATH *= $$PWD +HEADERS += $$PWD/customwizard.h \ + $$PWD/customwizardparameters.h \ + $$PWD/customwizardpage.h +SOURCES += $$PWD/customwizard.cpp \ + $$PWD/customwizardparameters.cpp \ + $$PWD/customwizardpage.cpp diff --git a/src/plugins/projectexplorer/customwizard/customwizardpage.cpp b/src/plugins/projectexplorer/customwizard/customwizardpage.cpp new file mode 100644 index 0000000000..5c63f30717 --- /dev/null +++ b/src/plugins/projectexplorer/customwizard/customwizardpage.cpp @@ -0,0 +1,160 @@ +/************************************************************************** +** +** 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 "customwizardpage.h" +#include "customwizardparameters.h" + +#include <utils/pathchooser.h> + +#include <QtCore/QRegExp> +#include <QtCore/QDebug> + +#include <QtGui/QWizardPage> +#include <QtGui/QFormLayout> +#include <QtGui/QLineEdit> +#include <QtGui/QLabel> +#include <QtGui/QRegExpValidator> +#include <QtGui/QComboBox> + +enum { debug = 0 }; + +namespace ProjectExplorer { +namespace Internal { + +TextFieldComboBox::TextFieldComboBox(QWidget *parent) : + QComboBox(parent) +{ + setEditable(false); + connect(this, SIGNAL(currentIndexChanged(QString)), this, SIGNAL(textChanged(QString))); +} + +void TextFieldComboBox::setText(const QString &s) +{ + const int index = findText(s); + if (index != -1 && index != currentIndex()) + setCurrentIndex(index); +} + +// --------------- CustomWizardFieldPage +CustomWizardFieldPage::CustomWizardFieldPage(const FieldList &fields, + QWidget *parent) : + QWizardPage(parent), + m_formLayout(new QFormLayout) +{ + if (debug) + qDebug() << Q_FUNC_INFO << fields; + foreach(const CustomWizardField &f, fields) + addField(f); + setLayout(m_formLayout); +} + +void CustomWizardFieldPage::addRow(const QString &name, QWidget *w) +{ + m_formLayout->addRow(name, w); +} + +// Create widget a control based on the control attributes map +// and register it with the QWizard. +QWidget *CustomWizardFieldPage::registerControl(const CustomWizardField &field) +{ + // Register field, Indicate mandatory by '*' (only when registering) + QString fieldName = field.name; + if (field.mandatory) + fieldName += QLatin1Char('*'); + // Check known classes: QComboBox + const QString className = field.controlAttributes.value(QLatin1String("class")); + if (className == QLatin1String("QComboBox")) { + TextFieldComboBox *combo = new TextFieldComboBox; + // Set up items + do { + const QString choices = field.controlAttributes.value(QLatin1String("combochoices")); + if (choices.isEmpty()) + break; + combo->addItems(choices.split(QLatin1Char(','))); + bool ok; + const QString currentIndexS = field.controlAttributes.value(QLatin1String("defaultindex")); + if (currentIndexS.isEmpty()) + break; + const int currentIndex = currentIndexS.toInt(&ok); + if (!ok || currentIndex < 0 || currentIndex >= combo->count()) + break; + combo->setCurrentIndex(currentIndex); + } while (false); + registerField(fieldName, combo, "text", SIGNAL(text4Changed(QString))); + return combo; + } // QComboBox + // Default to QLineEdit + QLineEdit *lineEdit = new QLineEdit; + const QString validationRegExp = field.controlAttributes.value(QLatin1String("validator")); + if (!validationRegExp.isEmpty()) { + QRegExp re(validationRegExp); + if (re.isValid()) { + lineEdit->setValidator(new QRegExpValidator(re, lineEdit)); + } else { + qWarning("Invalid custom wizard field validator regular expression %s.", qPrintable(validationRegExp)); + } + } + lineEdit->setText(field.controlAttributes.value(QLatin1String("defaulttext"))); + registerField(fieldName, lineEdit, "text", SIGNAL(textEdited(QString))); + return lineEdit; +} + +void CustomWizardFieldPage::addField(const CustomWizardField &field) +{ + addRow(field.description, registerControl(field)); +} + +// --------------- CustomWizardPage + +CustomWizardPage::CustomWizardPage(const FieldList &f, + QWidget *parent) : + CustomWizardFieldPage(f, parent), + m_pathChooser(new Utils::PathChooser) +{ + addRow(tr("Path:"), m_pathChooser); + connect(m_pathChooser, SIGNAL(validChanged()), this, SIGNAL(completeChanged())); +} + +QString CustomWizardPage::path() const +{ + return m_pathChooser->path(); +} + +void CustomWizardPage::setPath(const QString &path) +{ + m_pathChooser->setPath(path); +} + +bool CustomWizardPage::isComplete() const +{ + return m_pathChooser->isValid(); +} + +} // namespace Internal +} // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/customwizard/customwizardpage.h b/src/plugins/projectexplorer/customwizard/customwizardpage.h new file mode 100644 index 0000000000..26ec8e9910 --- /dev/null +++ b/src/plugins/projectexplorer/customwizard/customwizardpage.h @@ -0,0 +1,108 @@ +/************************************************************************** +** +** 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 CUSTOMPROJECTWIZARDDIALOG_H +#define CUSTOMPROJECTWIZARDDIALOG_H + +#include <QtGui/QComboBox> +#include <QtGui/QWizardPage> +#include <QtCore/QSharedPointer> + +QT_BEGIN_NAMESPACE +class QFormLayout; +QT_END_NAMESPACE + +namespace Utils { + class PathChooser; +} + +namespace ProjectExplorer { +namespace Internal { + +struct CustomWizardField; +struct CustomWizardParameters; + +// A non-editable combo for text editing purposes that plays +// with QWizard::registerField (providing a settable text property). +class TextFieldComboBox : public QComboBox { + Q_PROPERTY(QString text READ text WRITE setText) + Q_OBJECT +public: + explicit TextFieldComboBox(QWidget *parent = 0); + + QString text() const { return currentText(); } + void setText(const QString &s); + +signals: + void text4Changed(const QString &); // Do not conflict with Qt 3 compat signal. +}; + +// A simple custom wizard page presenting the fields to be used +// as page 2 of a BaseProjectWizardDialog if there are any fields. +// Uses the 'field' functionality of QWizard. +class CustomWizardFieldPage : public QWizardPage { + Q_OBJECT +public: + typedef QList<CustomWizardField> FieldList; + + explicit CustomWizardFieldPage(const FieldList &f, + QWidget *parent = 0); +protected: + inline void addRow(const QString &name, QWidget *w); + +private: + QWidget *registerControl(const CustomWizardField &f); + + void addField(const CustomWizardField &f); + QFormLayout *m_formLayout; +}; + +// A custom wizard page presenting the fields to be used and a path chooser +// at the bottom (for use by "class"/"file" wizards). Does validation on +// the Path chooser only (as the other fields can by validated by regexps). + +class CustomWizardPage : public CustomWizardFieldPage { + Q_OBJECT +public: + explicit CustomWizardPage(const FieldList &f, + QWidget *parent = 0); + + QString path() const; + void setPath(const QString &path); + + virtual bool isComplete() const; + +private: + Utils::PathChooser *m_pathChooser; +}; + +} // namespace Internal +} // namespace ProjectExplorer + +#endif // CUSTOMPROJECTWIZARDDIALOG_H diff --git a/src/plugins/projectexplorer/customwizard/customwizardparameters.cpp b/src/plugins/projectexplorer/customwizard/customwizardparameters.cpp new file mode 100644 index 0000000000..bc9bcc2acb --- /dev/null +++ b/src/plugins/projectexplorer/customwizard/customwizardparameters.cpp @@ -0,0 +1,472 @@ +/************************************************************************** +** +** 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 "customwizardparameters.h" + +#include <QtCore/QDebug> +#include <QtCore/QLocale> +#include <QtCore/QFile> +#include <QtCore/QFileInfo> +#include <QtCore/QXmlStreamReader> +#include <QtCore/QXmlStreamAttribute> +#include <QtGui/QIcon> + +enum { debug = 0 }; + +static const char customWizardElementC[] = "wizard"; +static const char iconElementC[] = "icon"; +static const char descriptionElementC[] = "description"; +static const char displayNameElementC[] = "displayName"; +static const char idAttributeC[] = "id"; +static const char kindAttributeC[] = "kind"; +static const char klassAttributeC[] = "class"; +static const char firstPageAttributeC[] = "firstpage"; +static const char langAttributeC[] = "xml:lang"; +static const char categoryAttributeC[] = "category"; +static const char displayCategoryElementC[] = "displayCategory"; +static const char fieldPageTitleElementC[] = "fieldpagetitle"; +static const char fieldsElementC[] = "fields"; +static const char fieldElementC[] = "field"; + +static const char fieldDescriptionElementC[] = "fielddescription"; +static const char fieldNameAttributeC[] = "name"; +static const char fieldMandatoryAttributeC[] = "mandatory"; +static const char fieldControlElementC[] = "fieldcontrol"; + +static const char filesElementC[] = "files"; +static const char fileElementC[] = "file"; +static const char fileNameSourceAttributeC[] = "source"; +static const char fileNameTargetAttributeC[] = "target"; + +enum ParseState { + ParseBeginning, + ParseWithinWizard, + ParseWithinFields, + ParseWithinField, + ParseWithinFiles, + ParseWithinFile, + ParseError +}; + +namespace ProjectExplorer { +namespace Internal { + +CustomWizardField::CustomWizardField() : + mandatory(false) +{ +} + +void CustomWizardField::clear() +{ + mandatory = false; + name.clear(); + description.clear(); + controlAttributes.clear(); +} + +CustomWizardParameters::CustomWizardParameters() : + firstPageId(-1) +{ +} + +void CustomWizardParameters::clear() +{ + directory.clear(); + files.clear(); + fields.clear(); + firstPageId = -1; +} + +// Resolve icon file path relative to config file directory. +static inline QIcon wizardIcon(const QString &configFileFullPath, + const QString &xmlIconFileName) +{ + const QFileInfo fi(xmlIconFileName); + if (fi.isFile()) + return QIcon(fi.absoluteFilePath()); + if (!fi.isRelative()) + return QIcon(); + // Expand by config path + const QFileInfo absFi(QFileInfo(configFileFullPath).absolutePath() + + QLatin1Char('/') + xmlIconFileName); + if (absFi.isFile()) + return QIcon(absFi.absoluteFilePath()); + return QIcon(); +} + +// Forward a reader over element text +static inline bool skipOverElementText(QXmlStreamReader &reader) +{ + QXmlStreamReader::TokenType next = QXmlStreamReader::EndElement; + do { + next = reader.readNext(); + } while (next == QXmlStreamReader::Characters || next == QXmlStreamReader::EntityReference + || next == QXmlStreamReader::ProcessingInstruction || next == QXmlStreamReader::Comment); + return next == QXmlStreamReader::EndElement; +} + +// Helper for reading text element depending on a language. +// Assign the element text to the string passed on if the language matches, +// that is, the element has no language attribute or there is an exact match. +// If there is no match, skip over the element text. +static inline void assignLanguageElementText(QXmlStreamReader &reader, + const QString &desiredLanguage, + QString *target) +{ + const QStringRef elementLanguage = reader.attributes().value(langAttributeC); + if (elementLanguage.isEmpty() || elementLanguage == desiredLanguage) { + *target = reader.readElementText(); + } else { + // Language mismatch: forward to end element. + skipOverElementText(reader); + } +} + +// Copy&paste from above to call a setter of BaseFileParameters. +// Implementation of a sophisticated mem_fun pattern is left +// as an exercise to the reader. +static inline void assignLanguageElementText(QXmlStreamReader &reader, + const QString &desiredLanguage, + Core::BaseFileWizardParameters *bp, + void (Core::BaseFileWizardParameters::*setter)(const QString &)) +{ + const QStringRef elementLanguage = reader.attributes().value(langAttributeC); + if (elementLanguage.isEmpty() || elementLanguage == desiredLanguage) { + (bp->*setter)(reader.readElementText()); + } else { + // Language mismatch: forward to end element. + skipOverElementText(reader); + } +} + +// Read level sub-elements of "wizard" +static bool parseCustomProjectElement(QXmlStreamReader &reader, + const QString &configFileFullPath, + const QString &language, + CustomWizardParameters *p, + Core::BaseFileWizardParameters *bp) +{ + const QStringRef elementName = reader.name(); + if (elementName == QLatin1String(iconElementC)) { + const QString path = reader.readElementText(); + const QIcon icon = wizardIcon(configFileFullPath, path); + if (icon.availableSizes().isEmpty()) { + qWarning("Invalid icon path '%s' encountered in custom project template %s.", + qPrintable(path), qPrintable(configFileFullPath)); + } else { + bp->setIcon(icon); + } + return true; + } + if (elementName == QLatin1String(descriptionElementC)) { + assignLanguageElementText(reader, language, bp, + &Core::BaseFileWizardParameters::setDescription); + return true; + } + if (elementName == QLatin1String(displayNameElementC)) { + assignLanguageElementText(reader, language, bp, + &Core::BaseFileWizardParameters::setDisplayName); + return true; + } + if (elementName == QLatin1String(displayCategoryElementC)) { + assignLanguageElementText(reader, language, bp, + &Core::BaseFileWizardParameters::setDisplayCategory); + return true; + } + if (elementName == QLatin1String(fieldPageTitleElementC)) { + assignLanguageElementText(reader, language, &p->fieldPageTitle); + return true; + } + return false; +} + +// Read sub-elements of "fields" +static bool parseFieldElement(QXmlStreamReader &reader, + const QString &language, + CustomWizardField *m) +{ + const QStringRef elementName = reader.name(); + if (elementName == QLatin1String(fieldDescriptionElementC)) { + assignLanguageElementText(reader, language, &m->description); + return true; + } + // Copy widget control attributes + if (elementName == QLatin1String(fieldControlElementC)) { + foreach(const QXmlStreamAttribute &attribute, reader.attributes()) + m->controlAttributes.insert(attribute.name().toString(), attribute.value().toString()); + skipOverElementText(reader); + return true; + } + return false; +} + +// Switch parser state depending on opening element name. +static ParseState nextOpeningState(ParseState in, const QStringRef &name) +{ + switch (in) { + case ParseBeginning: + if (name == QLatin1String(customWizardElementC)) + return ParseWithinWizard; + break; + case ParseWithinWizard: + if (name == QLatin1String(fieldsElementC)) + return ParseWithinFields; + if (name == QLatin1String(filesElementC)) + return ParseWithinFiles; + break; + case ParseWithinFields: + if (name == QLatin1String(fieldElementC)) + return ParseWithinField; + break; + case ParseWithinFiles: + if (name == QLatin1String(fileElementC)) + return ParseWithinFile; + break; + case ParseWithinField: + case ParseWithinFile: + case ParseError: + break; + } + return ParseError; +}; + +// Switch parser state depending on closing element name. +static ParseState nextClosingState(ParseState in, const QStringRef &name) +{ + switch (in) { + case ParseBeginning: + break; + case ParseWithinWizard: + if (name == QLatin1String(customWizardElementC)) + return ParseBeginning; + break; + case ParseWithinFields: + if (name == QLatin1String(fieldsElementC)) + return ParseWithinWizard; + break; + case ParseWithinField: + if (name == QLatin1String(fieldElementC)) + return ParseWithinFields; + break; + case ParseWithinFiles: + if (name == QLatin1String(filesElementC)) + return ParseWithinWizard; + break; + case ParseWithinFile: + if (name == QLatin1String(fileElementC)) + return ParseWithinFiles; + break; + case ParseError: + break; + } + return ParseError; +}; + +// Parse kind attribute +static inline Core::IWizard::WizardKind kindAttribute(const QXmlStreamReader &r) +{ + const QStringRef value = r.attributes().value(QLatin1String(kindAttributeC)); + if (!value.isEmpty()) { + if (value == QLatin1String("file")) + return Core::IWizard::FileWizard; + if (value == QLatin1String("class")) + return Core::IWizard::ClassWizard; + } + return Core::IWizard::ProjectWizard; +} + +static inline QString msgError(const QXmlStreamReader &reader, + const QString &fileName, + const QString &what) +{ + return QString::fromLatin1("Error in %1 at line %2, column %3: %4"). + arg(fileName).arg(reader.lineNumber()).arg(reader.columnNumber()).arg(what); +} + +static inline bool booleanAttributeValue(const QXmlStreamReader &r, const char *name) +{ + return r.attributes().value(QLatin1String(name)) == QLatin1String("true"); +} + +static inline int integerAttributeValue(const QXmlStreamReader &r, const char *name, int defaultValue) +{ + const QString sValue = r.attributes().value(QLatin1String(name)).toString(); + if (sValue.isEmpty()) + return defaultValue; + bool ok; + const int value = sValue.toInt(&ok); + if (!ok) { + qWarning("Invalid integer value specification '%s' for attribute '%s'.", + qPrintable(sValue), name); + return defaultValue; + } + return value; +} + +static inline QString attributeValue(const QXmlStreamReader &r, const char *name) +{ + return r.attributes().value(QLatin1String(name)).toString(); +} + +// Return locale language attribute "de_UTF8" -> "de", empty string for "C" +static inline QString localeLanguage() +{ + QLocale loc; + QString name = loc.name(); + const int underScorePos = name.indexOf(QLatin1Char('_')); + if (underScorePos != -1) + name.truncate(underScorePos); + if (name.compare(QLatin1String("C"), Qt::CaseInsensitive) == 0) + name.clear(); + return name; +} + +// Main parsing routine +bool CustomWizardParameters::parse(QIODevice &device, + const QString &configFileFullPath, + Core::BaseFileWizardParameters *bp, + QString *errorMessage) +{ + QXmlStreamReader reader(&device); + QXmlStreamReader::TokenType token = QXmlStreamReader::EndDocument; + ParseState state = ParseBeginning; + clear(); + bp->clear(); + bp->setKind(Core::IWizard::ProjectWizard); + const QString language = localeLanguage(); + CustomWizardField field; + do { + token = reader.readNext(); + switch (token) { + case QXmlStreamReader::Invalid: + *errorMessage = msgError(reader, configFileFullPath, reader.errorString()); + return false; + case QXmlStreamReader::StartElement: + do { + // Read out subelements applicable to current state + if (state == ParseWithinWizard && parseCustomProjectElement(reader, configFileFullPath, language, this, bp)) + break; + if (state == ParseWithinField && parseFieldElement(reader, language, &field)) + break; + // switch to next state + state = nextOpeningState(state, reader.name()); + // Read attributes post state-switching + switch (state) { + case ParseError: + *errorMessage = msgError(reader, configFileFullPath, + QString::fromLatin1("Unexpected start element %1").arg(reader.name().toString())); + return false; + case ParseWithinWizard: + bp->setId(attributeValue(reader, idAttributeC)); + bp->setCategory(attributeValue(reader, categoryAttributeC)); + bp->setKind(kindAttribute(reader)); + klass = attributeValue(reader, klassAttributeC); + firstPageId = integerAttributeValue(reader, firstPageAttributeC, -1); + break; + case ParseWithinField: // field attribute + field.name = attributeValue(reader, fieldNameAttributeC); + field.mandatory = booleanAttributeValue(reader, fieldMandatoryAttributeC); + break; + case ParseWithinFile: { // file attribute + CustomWizardFile file; + file.source = attributeValue(reader, fileNameSourceAttributeC); + file.target = attributeValue(reader, fileNameTargetAttributeC); + if (file.target.isEmpty()) + file.target = file.source; + if (file.source.isEmpty()) { + qWarning("Skipping empty file name in custom project."); + } else { + files.push_back(file); + } + } + break; + default: + break; + } + } while (false); + break; + case QXmlStreamReader::EndElement: + state = nextClosingState(state, reader.name()); + if (state == ParseError) { + *errorMessage = msgError(reader, configFileFullPath, + QString::fromLatin1("Unexpected end element %1").arg(reader.name().toString())); + return false; + } + if (state == ParseWithinFields) { // Leaving a field element + fields.push_back(field); + field.clear(); + } + break; + default: + break; + } + } while (token != QXmlStreamReader::EndDocument); + return true; +} + +bool CustomWizardParameters::parse(const QString &configFileFullPath, + Core::BaseFileWizardParameters *bp, + QString *errorMessage) +{ + QFile configFile(configFileFullPath); + if (!configFile.open(QIODevice::ReadOnly|QIODevice::Text)) { + *errorMessage = QString::fromLatin1("Cannot open %1: %2").arg(configFileFullPath, configFile.errorString()); + return false; + } + return parse(configFile, configFileFullPath, bp, errorMessage); +} + +QDebug operator<<(QDebug d, const CustomWizardField &m) +{ + QDebug nsp = d.nospace(); + nsp << "Name: " << m.name; + if (m.mandatory) + nsp << '*'; + nsp << " Desc: " << m.description << " Control: " << m.controlAttributes; + return d; +} + +QDebug operator<<(QDebug d, const CustomWizardFile &f) +{ + d.nospace() << "source: " << f.source << " target: " << f.target; + return d; +} + +QDebug operator<<(QDebug d, const CustomWizardParameters &p) +{ + QDebug nsp = d.nospace(); + nsp << "Dir: " << p.directory << " klass:" << p.klass << " Files: " << p.files; + if (!p.fields.isEmpty()) + nsp << " Fields: " << p.fields; + nsp << "First page: " << p.firstPageId; + return d; +} + +} // namespace Internal +} // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/customwizard/customwizardparameters.h b/src/plugins/projectexplorer/customwizard/customwizardparameters.h new file mode 100644 index 0000000000..e526da0d0f --- /dev/null +++ b/src/plugins/projectexplorer/customwizard/customwizardparameters.h @@ -0,0 +1,88 @@ +/************************************************************************** +** +** 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 CUSTOMPROJECTWIZARDPARAMETERS_H +#define CUSTOMPROJECTWIZARDPARAMETERS_H + +#include <coreplugin/basefilewizard.h> + +#include <QtCore/QList> +#include <QtCore/QMap> + +QT_BEGIN_NAMESPACE +class QIODevice; +class QDebug; +QT_END_NAMESPACE + +namespace ProjectExplorer { +namespace Internal { + +struct CustomWizardField { + // Parameters of the widget control are stored as map + typedef QMap<QString, QString> ControlAttributeMap; + CustomWizardField(); + void clear(); + + QString description; + QString name; + ControlAttributeMap controlAttributes; + bool mandatory; +}; + +struct CustomWizardFile { + QString source; + QString target; +}; + +struct CustomWizardParameters +{ +public: + CustomWizardParameters(); + void clear(); + bool parse(QIODevice &device, const QString &configFileFullPath, + Core::BaseFileWizardParameters *bp, QString *errorMessage); + bool parse(const QString &configFileFullPath, + Core::BaseFileWizardParameters *bp, QString *errorMessage); + + QString directory; + QString klass; + QList<CustomWizardFile> files; + QString fieldPageTitle; + QList<CustomWizardField> fields; + int firstPageId; +}; + +QDebug operator<<(QDebug d, const CustomWizardField &); +QDebug operator<<(QDebug d, const CustomWizardFile &); +QDebug operator<<(QDebug d, const CustomWizardParameters &); + +} // namespace Internal +} // namespace ProjectExplorer + +#endif // CUSTOMPROJECTWIZARDPARAMETERS_H diff --git a/src/plugins/projectexplorer/projectexplorer.cpp b/src/plugins/projectexplorer/projectexplorer.cpp index d34d3fa663..5e89c9742c 100644 --- a/src/plugins/projectexplorer/projectexplorer.cpp +++ b/src/plugins/projectexplorer/projectexplorer.cpp @@ -52,6 +52,7 @@ #include "pluginfilefactory.h" #include "processstep.h" #include "projectexplorerconstants.h" +#include "customwizard.h" #include "projectfilewizardextension.h" #include "projecttreewidget.h" #include "projectwindow.h" @@ -902,6 +903,10 @@ void ProjectExplorerPlugin::extensionsInitialized() d->m_profileMimeTypes += pf->mimeTypes(); addAutoReleasedObject(pf); } + // Add custom wizards, for which other plugins might have registered + // class factories + foreach(Core::IWizard *cpw, ProjectExplorer::CustomWizard::createWizards()) + addAutoReleasedObject(cpw); } void ProjectExplorerPlugin::shutdown() diff --git a/src/plugins/projectexplorer/projectexplorer.pro b/src/plugins/projectexplorer/projectexplorer.pro index c912a1985d..26001ffaa9 100644 --- a/src/plugins/projectexplorer/projectexplorer.pro +++ b/src/plugins/projectexplorer/projectexplorer.pro @@ -6,6 +6,7 @@ include(../../qtcreatorplugin.pri) include(projectexplorer_dependencies.pri) include(../../shared/scriptwrapper/scriptwrapper.pri) include(../../libs/utils/utils.pri) +include(customwizard/customwizard.pri) INCLUDEPATH += $$PWD/../../libs/utils HEADERS += projectexplorer.h \ projectexplorer_export.h \ diff --git a/src/plugins/qt4projectmanager/qt4projectmanagerplugin.cpp b/src/plugins/qt4projectmanager/qt4projectmanagerplugin.cpp index 81940970b5..1783d4f5fb 100644 --- a/src/plugins/qt4projectmanager/qt4projectmanagerplugin.cpp +++ b/src/plugins/qt4projectmanager/qt4projectmanagerplugin.cpp @@ -133,6 +133,8 @@ bool Qt4ProjectManagerPlugin::initialize(const QStringList &arguments, QString * addAutoReleasedObject(new TestWizard); addAutoReleasedObject(new CustomWidgetWizard); + CustomQt4ProjectWizard::registerSelf(); + addAutoReleasedObject(new QMakeStepFactory); addAutoReleasedObject(new MakeStepFactory); diff --git a/src/plugins/qt4projectmanager/wizards/qtwizard.cpp b/src/plugins/qt4projectmanager/wizards/qtwizard.cpp index 1113597ed0..6eb0abe27c 100644 --- a/src/plugins/qt4projectmanager/wizards/qtwizard.cpp +++ b/src/plugins/qt4projectmanager/wizards/qtwizard.cpp @@ -38,7 +38,6 @@ #include <coreplugin/icore.h> #include <cpptools/cpptoolsconstants.h> #include <extensionsystem/pluginmanager.h> - #include <QtCore/QCoreApplication> #include <QtCore/QVariant> @@ -104,6 +103,11 @@ QString QtWizard::profileSuffix() bool QtWizard::postGenerateFiles(const QWizard *w, const Core::GeneratedFiles &l, QString *errorMessage) { + return QtWizard::qt4ProjectPostGenerateFiles(w, l, errorMessage); +} + +bool QtWizard::qt4ProjectPostGenerateFiles(const QWizard *w, const Core::GeneratedFiles &l, QString *errorMessage) +{ const QString proFileName = l.back().path(); const BaseQt4ProjectWizardDialog *dialog = qobject_cast<const BaseQt4ProjectWizardDialog *>(w); @@ -144,6 +148,35 @@ bool QtWizard::showModulesPageForLibraries() return true; } +// ------------ CustomQt4ProjectWizard +CustomQt4ProjectWizard::CustomQt4ProjectWizard(const Core::BaseFileWizardParameters& baseFileParameters, + QObject *parent) : + ProjectExplorer::CustomProjectWizard(baseFileParameters, parent) +{ +} + +QWizard *CustomQt4ProjectWizard::createWizardDialog(QWidget *parent, + const QString &defaultPath, + const WizardPageList &extensionPages) const +{ + BaseQt4ProjectWizardDialog *wizard = new BaseQt4ProjectWizardDialog(false, parent); + initProjectWizardDialog(wizard, defaultPath, extensionPages); + if (wizard->pageIds().contains(targetPageId)) + qWarning("CustomQt4ProjectWizard: Unable to insert target page at %d", int(targetPageId)); + wizard->addTargetsPage(QSet<QString>(), targetPageId); + return wizard; +} + +bool CustomQt4ProjectWizard::postGenerateFiles(const QWizard *w, const Core::GeneratedFiles &l, QString *errorMessage) +{ + return QtWizard::qt4ProjectPostGenerateFiles(w, l, errorMessage); +} + +void CustomQt4ProjectWizard::registerSelf() +{ + ProjectExplorer::CustomWizard::registerFactory<CustomQt4ProjectWizard>(QLatin1String("qt4project")); +} + // ----------------- BaseQt4ProjectWizardDialog BaseQt4ProjectWizardDialog::BaseQt4ProjectWizardDialog(bool showModulesPage, QWidget *parent) : ProjectExplorer::BaseProjectWizardDialog(parent), diff --git a/src/plugins/qt4projectmanager/wizards/qtwizard.h b/src/plugins/qt4projectmanager/wizards/qtwizard.h index e305324a5a..760a72095d 100644 --- a/src/plugins/qt4projectmanager/wizards/qtwizard.h +++ b/src/plugins/qt4projectmanager/wizards/qtwizard.h @@ -32,6 +32,7 @@ #include "qtprojectparameters.h" #include <projectexplorer/baseprojectwizarddialog.h> +#include <projectexplorer/customwizard/customwizard.h> #include <coreplugin/basefilewizard.h> @@ -77,6 +78,8 @@ public: // Query CppTools settings for the class wizard settings static bool lowerCaseFiles(); + static bool qt4ProjectPostGenerateFiles(const QWizard *w, const Core::GeneratedFiles &l, QString *errorMessage); + protected: static bool showModulesPageForApplications(); static bool showModulesPageForLibraries(); @@ -85,6 +88,25 @@ private: bool postGenerateFiles(const QWizard *w, const Core::GeneratedFiles &l, QString *errorMessage); }; +// A custom wizard with an additional Qt 4 target page +class CustomQt4ProjectWizard : public ProjectExplorer::CustomProjectWizard { + Q_OBJECT +public: + explicit CustomQt4ProjectWizard(const Core::BaseFileWizardParameters& baseFileParameters, + QObject *parent = 0); + + virtual QWizard *createWizardDialog(QWidget *parent, + const QString &defaultPath, + const WizardPageList &extensionPages) const; + static void registerSelf(); + +protected: + virtual bool postGenerateFiles(const QWizard *, const Core::GeneratedFiles &l, QString *errorMessage); + +private: + enum { targetPageId = 2 }; +}; + /* BaseQt4ProjectWizardDialog: Additionally offers modules page * and getter/setter for blank-delimited modules list, transparently * handling the visibility of the modules page list as well as a page @@ -93,13 +115,13 @@ private: class BaseQt4ProjectWizardDialog : public ProjectExplorer::BaseProjectWizardDialog { Q_OBJECT - protected: - explicit BaseQt4ProjectWizardDialog(bool showModulesPage, QWidget *parent = 0); explicit BaseQt4ProjectWizardDialog(bool showModulesPage, Utils::ProjectIntroPage *introPage, int introId = -1, QWidget *parent = 0); +public: + explicit BaseQt4ProjectWizardDialog(bool showModulesPage, QWidget *parent = 0); virtual ~BaseQt4ProjectWizardDialog(); int addModulesPage(int id = -1); @@ -107,7 +129,6 @@ protected: static QSet<QString> desktopTarget(); -public: QString selectedModules() const; void setSelectedModules(const QString &, bool lock = false); |