From 5bc886c5b643d69c841005777538e24cb223d75d Mon Sep 17 00:00:00 2001 From: Friedemann Kleint <Friedemann.Kleint@nokia.com> Date: Mon, 22 Mar 2010 15:33:33 +0100 Subject: [PATCH] Custom wizards: Make it possible to use fields in default texts. Pass around shared context containing basic replacement map. CustomProjectWizard adds %ProjectName% to it obtained from signal BaseProjectWizardDialog::introPageLeft(). Move replacement code into context. Add new modifier 'c' for capitalizing words. --- .../wizards/helloworld/wizard_sample.xml | 3 +- .../baseprojectwizarddialog.cpp | 27 +++- .../projectexplorer/baseprojectwizarddialog.h | 4 + .../customwizard/customwizard.cpp | 122 +++++++----------- .../customwizard/customwizard.h | 14 +- .../customwizard/customwizardpage.cpp | 112 ++++++++++------ .../customwizard/customwizardpage.h | 24 +++- .../customwizard/customwizardparameters.cpp | 74 +++++++++++ .../customwizard/customwizardparameters.h | 22 ++++ 9 files changed, 279 insertions(+), 123 deletions(-) diff --git a/share/qtcreator/templates/wizards/helloworld/wizard_sample.xml b/share/qtcreator/templates/wizards/helloworld/wizard_sample.xml index 58e7748bccc..23af3d6802b 100644 --- a/share/qtcreator/templates/wizards/helloworld/wizard_sample.xml +++ b/share/qtcreator/templates/wizards/helloworld/wizard_sample.xml @@ -53,7 +53,8 @@ leave room for the Qt 4 target page. <fieldpagetitle xml:lang="de">Hallo Welt Parameter</fieldpagetitle> <fields> <field mandatory="true" name="MESSAGE"> - <fieldcontrol class="QLineEdit" validator='^[^"]+$' defaulttext="Hello world!" /> + <fieldcontrol class="QLineEdit" validator='^[^"]+$' + defaulttext="Hello world from project '%ProjectName:c%'!" /> <fielddescription>Hello world message:</fielddescription> <fielddescription xml:lang="de">Hallo-Welt-Nachricht:</fielddescription> </field> diff --git a/src/plugins/projectexplorer/baseprojectwizarddialog.cpp b/src/plugins/projectexplorer/baseprojectwizarddialog.cpp index 166d2eb6222..a2324054709 100644 --- a/src/plugins/projectexplorer/baseprojectwizarddialog.cpp +++ b/src/plugins/projectexplorer/baseprojectwizarddialog.cpp @@ -41,13 +41,17 @@ namespace ProjectExplorer { struct BaseProjectWizardDialogPrivate { explicit BaseProjectWizardDialogPrivate(Utils::ProjectIntroPage *page, int id = -1); - const int introId; + const int desiredIntroPageId; Utils::ProjectIntroPage *introPage; + int introPageId; + int lastId; }; BaseProjectWizardDialogPrivate::BaseProjectWizardDialogPrivate(Utils::ProjectIntroPage *page, int id) : - introId(id), - introPage(page) + desiredIntroPageId(id), + introPage(page), + introPageId(-1), + lastId(-1) { } @@ -70,8 +74,14 @@ BaseProjectWizardDialog::BaseProjectWizardDialog(Utils::ProjectIntroPage *introP void BaseProjectWizardDialog::init() { Core::BaseFileWizard::setupWizard(this); - addPage(d->introPage); + if (d->introPageId == -1) { + d->introPageId = addPage(d->introPage); + } else { + d->introPageId = d->desiredIntroPageId; + setPage(d->desiredIntroPageId, d->introPage); + } connect(this, SIGNAL(accepted()), this, SLOT(slotAccepted())); + connect(this, SIGNAL(currentIdChanged(int)), this, SLOT(slotBaseCurrentIdChanged(int))); } BaseProjectWizardDialog::~BaseProjectWizardDialog() @@ -107,12 +117,21 @@ void BaseProjectWizardDialog::setProjectName(const QString &name) void BaseProjectWizardDialog::slotAccepted() { if (d->introPage->useAsDefaultPath()) { + // Store the path as default path for new projects if desired. Core::FileManager *fm = Core::ICore::instance()->fileManager(); fm->setProjectsDirectory(path()); fm->setUseProjectsDirectory(true); } } +void BaseProjectWizardDialog::slotBaseCurrentIdChanged(int id) +{ + if (d->lastId == d->introPageId) { + emit introPageLeft(d->introPage->projectName(), d->introPage->path()); + } + d->lastId = id; +} + Utils::ProjectIntroPage *BaseProjectWizardDialog::introPage() const { return d->introPage; diff --git a/src/plugins/projectexplorer/baseprojectwizarddialog.h b/src/plugins/projectexplorer/baseprojectwizarddialog.h index f4f40c27813..a0b669644ae 100644 --- a/src/plugins/projectexplorer/baseprojectwizarddialog.h +++ b/src/plugins/projectexplorer/baseprojectwizarddialog.h @@ -71,11 +71,15 @@ public slots: void setPath(const QString &path); void setProjectName(const QString &name); +signals: + void introPageLeft(const QString &projectName, const QString &path); + protected: Utils::ProjectIntroPage *introPage() const; private slots: void slotAccepted(); + void slotBaseCurrentIdChanged(int); private: void init(); diff --git a/src/plugins/projectexplorer/customwizard/customwizard.cpp b/src/plugins/projectexplorer/customwizard/customwizard.cpp index ff468835af0..87dfbb2f2b1 100644 --- a/src/plugins/projectexplorer/customwizard/customwizard.cpp +++ b/src/plugins/projectexplorer/customwizard/customwizard.cpp @@ -35,8 +35,6 @@ #include <coreplugin/icore.h> #include <coreplugin/messagemanager.h> -#include <coreplugin/mimedatabase.h> -#include <cpptools/cpptoolsconstants.h> #include <extensionsystem/pluginmanager.h> #include <utils/qtcassert.h> @@ -52,7 +50,10 @@ static const char configFileC[] = "wizard.xml"; namespace ProjectExplorer { struct CustomWizardPrivate { + CustomWizardPrivate() : m_context(new Internal::CustomWizardContext) {} + QSharedPointer<Internal::CustomWizardParameters> m_parameters; + QSharedPointer<Internal::CustomWizardContext> m_context; static int verbose; }; @@ -105,7 +106,9 @@ 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); + + d->m_context->reset(); + Internal::CustomWizardPage *customPage = new Internal::CustomWizardPage(d->m_context, parameters()->fields); customPage->setPath(defaultPath); addWizardPage(wizard, customPage, parameters()->firstPageId); if (!parameters()->fieldPageTitle.isEmpty()) @@ -127,57 +130,6 @@ QWizard *CustomWizard::createWizardDialog(QWidget *parent, 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, @@ -188,7 +140,8 @@ static inline bool createFile(Internal::CustomWizardFile cwFile, { const QChar slash = QLatin1Char('/'); const QString sourcePath = sourceDirectory + slash + cwFile.source; - replaceFields(fm, &cwFile.target); + // Field replacement on target path + Internal::CustomWizardContext::replaceFields(fm, &cwFile.target); const QString targetPath = QDir::toNativeSeparators(targetDirectory + slash + cwFile.target); if (CustomWizardPrivate::verbose) qDebug() << "generating " << targetPath << sourcePath << fm; @@ -197,9 +150,10 @@ static inline bool createFile(Internal::CustomWizardFile cwFile, *errorMessage = QString::fromLatin1("Cannot open %1: %2").arg(sourcePath, file.errorString()); return false; } + // Field replacement on contents QString contents = QString::fromLocal8Bit(file.readAll()); if (!contents.isEmpty() && !fm.isEmpty()) - replaceFields(fm, &contents); + Internal::CustomWizardContext::replaceFields(fm, &contents); Core::GeneratedFile generatedFile; generatedFile.setContents(contents); generatedFile.setPath(targetPath); @@ -223,9 +177,16 @@ Core::GeneratedFiles CustomWizard::generateFiles(const QWizard *dialog, QString const Internal::CustomWizardPage *cwp = findWizardPage<Internal::CustomWizardPage>(dialog); QTC_ASSERT(cwp, return Core::GeneratedFiles()) QString path = cwp->path(); - const FieldReplacementMap fieldMap = defaultReplacementMap(dialog); - if (CustomWizardPrivate::verbose) - qDebug() << "CustomWizard::generateFiles" << dialog << path << fieldMap; + const FieldReplacementMap fieldMap = replacementMap(dialog); + if (CustomWizardPrivate::verbose) { + QString logText; + QTextStream str(&logText); + str << "CustomWizard::generateFiles: " << path << '\n'; + const FieldReplacementMap::const_iterator cend = fieldMap.constEnd(); + for (FieldReplacementMap::const_iterator it = fieldMap.constBegin(); it != cend; ++it) + str << " '" << it.key() << "' -> '" << it.value() << "'\n"; + qWarning("%s", qPrintable(logText)); + } return generateWizardFiles(path, fieldMap, errorMessage); } @@ -243,20 +204,14 @@ Core::GeneratedFiles CustomWizard::generateWizardFiles(const QString &targetPath 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 +// Create a replacement map of static base fields + wizard dialog fields +CustomWizard::FieldReplacementMap CustomWizard::replacementMap(const QWizard *w) const { - FieldReplacementMap fieldReplacementMap; + FieldReplacementMap fieldReplacementMap = d->m_context->baseReplacements; 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; } @@ -265,6 +220,11 @@ CustomWizard::CustomWizardParametersPtr CustomWizard::parameters() const return d->m_parameters; } +CustomWizard::CustomWizardContextPtr CustomWizard::context() const +{ + return d->m_context; +} + // Static factory map typedef QMap<QString, QSharedPointer<ICustomWizardFactory> > CustomWizardFactoryMap; Q_GLOBAL_STATIC(CustomWizardFactoryMap, customWizardFactoryMap) @@ -407,17 +367,25 @@ 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); + const CustomWizardParametersPtr pa = parameters(); + QTC_ASSERT(!pa.isNull(), return); + + const CustomWizardContextPtr ctx = context(); + ctx->reset(); + + if (!pa->fields.isEmpty()) { + Internal::CustomWizardFieldPage *cp = new Internal::CustomWizardFieldPage(ctx, pa->fields); addWizardPage(w, cp, parameters()->firstPageId); - if (!parameters()->fieldPageTitle.isEmpty()) - cp->setTitle(parameters()->fieldPageTitle); + if (!pa->fieldPageTitle.isEmpty()) + cp->setTitle(pa->fieldPageTitle); } foreach(QWizardPage *ep, extensionPages) w->addPage(ep); w->setPath(defaultPath); w->setProjectName(BaseProjectWizardDialog::uniqueProjectName(defaultPath)); + + connect(w, SIGNAL(introPageLeft(QString,QString)), this, SLOT(introPageLeft(QString,QString))); + if (CustomWizardPrivate::verbose) qDebug() << "initProjectWizardDialog" << w << w->pageIds(); } @@ -428,7 +396,7 @@ Core::GeneratedFiles CustomProjectWizard::generateFiles(const QWizard *w, QStrin QTC_ASSERT(dialog, return Core::GeneratedFiles()) const QString targetPath = dialog->path() + QLatin1Char('/') + dialog->projectName(); // Add project name as macro. - FieldReplacementMap fieldReplacementMap = defaultReplacementMap(dialog); + FieldReplacementMap fieldReplacementMap = replacementMap(dialog); fieldReplacementMap.insert(QLatin1String("ProjectName"), dialog->projectName()); if (CustomWizardPrivate::verbose) qDebug() << "CustomProjectWizard::generateFiles" << dialog << targetPath << fieldReplacementMap; @@ -449,4 +417,10 @@ bool CustomProjectWizard::postGenerateFiles(const QWizard *, const Core::Generat return true; } +void CustomProjectWizard::introPageLeft(const QString &project, const QString & /* path */) +{ + // Make '%ProjectName%' available in base replacements. + context()->baseReplacements.insert(QLatin1String("ProjectName"), project); +} + } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/customwizard/customwizard.h b/src/plugins/projectexplorer/customwizard/customwizard.h index b294a7bef3d..842afd85f51 100644 --- a/src/plugins/projectexplorer/customwizard/customwizard.h +++ b/src/plugins/projectexplorer/customwizard/customwizard.h @@ -49,6 +49,7 @@ class BaseProjectWizardDialog; namespace Internal { struct CustomWizardParameters; + struct CustomWizardContext; } // Factory for creating wizard. Can be registered under a name @@ -105,6 +106,7 @@ public: protected: typedef QSharedPointer<Internal::CustomWizardParameters> CustomWizardParametersPtr; + typedef QSharedPointer<Internal::CustomWizardContext> CustomWizardContextPtr; void initWizardDialog(QWizard *w, const QString &defaultPath, const WizardPageList &extensionPages) const; @@ -113,10 +115,11 @@ protected: 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; + // Create replacement map as static base fields + QWizard fields + FieldReplacementMap replacementMap(const QWizard *w) const; CustomWizardParametersPtr parameters() const; + CustomWizardContextPtr context() const; private: void setParameters(const CustomWizardParametersPtr &p); @@ -128,7 +131,9 @@ private: // 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. +// last one by convention. Also inserts '%ProjectName%' into the base +// replacement map once the intro page is left to have it available +// for QLineEdit-type fields' default text. class PROJECTEXPLORER_EXPORT CustomProjectWizard : public CustomWizard { @@ -150,6 +155,9 @@ protected: void initProjectWizardDialog(BaseProjectWizardDialog *w, const QString &defaultPath, const WizardPageList &extensionPages) const; + +private slots: + void introPageLeft(const QString &project, const QString &path); }; } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/customwizard/customwizardpage.cpp b/src/plugins/projectexplorer/customwizard/customwizardpage.cpp index 290aba4884e..bb33911f04f 100644 --- a/src/plugins/projectexplorer/customwizard/customwizardpage.cpp +++ b/src/plugins/projectexplorer/customwizard/customwizardpage.cpp @@ -63,9 +63,17 @@ void TextFieldComboBox::setText(const QString &s) } // --------------- CustomWizardFieldPage -CustomWizardFieldPage::CustomWizardFieldPage(const FieldList &fields, + +CustomWizardFieldPage::LineEditData::LineEditData(QLineEdit* le, const QString &defText) : + lineEdit(le), defaultText(defText) +{ +} + +CustomWizardFieldPage::CustomWizardFieldPage(const QSharedPointer<CustomWizardContext> &ctx, + const FieldList &fields, QWidget *parent) : QWizardPage(parent), + m_context(ctx), m_formLayout(new QFormLayout) { if (debug) @@ -75,6 +83,10 @@ CustomWizardFieldPage::CustomWizardFieldPage(const FieldList &fields, setLayout(m_formLayout); } +CustomWizardFieldPage::~CustomWizardFieldPage() +{ +} + void CustomWizardFieldPage::addRow(const QString &name, QWidget *w) { m_formLayout->addRow(name, w); @@ -82,36 +94,50 @@ void CustomWizardFieldPage::addRow(const QString &name, QWidget *w) // Create widget a control based on the control attributes map // and register it with the QWizard. -QWidget *CustomWizardFieldPage::registerControl(const CustomWizardField &field) +void CustomWizardFieldPage::addField(const CustomWizardField &field)\ { - // Register field, Indicate mandatory by '*' (only when registering) + // 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")); + QWidget *fieldWidget = 0; 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 + fieldWidget = registerComboBox(fieldName, field); + } else { + fieldWidget = registerLineEdit(fieldName, field); + } + addRow(field.description, fieldWidget); +} + +QWidget *CustomWizardFieldPage::registerComboBox(const QString &fieldName, + const CustomWizardField &field) +{ + TextFieldComboBox *combo = new TextFieldComboBox; + do { // Set up items and current index + 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 + +QWidget *CustomWizardFieldPage::registerLineEdit(const QString &fieldName, + const CustomWizardField &field) +{ QLineEdit *lineEdit = new QLineEdit; + const QString validationRegExp = field.controlAttributes.value(QLatin1String("validator")); if (!validationRegExp.isEmpty()) { QRegExp re(validationRegExp); @@ -120,29 +146,40 @@ QWidget *CustomWizardFieldPage::registerControl(const CustomWizardField &field) } else { qWarning("Invalid custom wizard field validator regular expression %s.", qPrintable(validationRegExp)); } - m_validatorLineEdits.push_back(lineEdit); } - lineEdit->setText(field.controlAttributes.value(QLatin1String("defaulttext"))); registerField(fieldName, lineEdit, "text", SIGNAL(textEdited(QString))); + + const QString defaultText = field.controlAttributes.value(QLatin1String("defaulttext")); + m_lineEdits.push_back(LineEditData(lineEdit, defaultText)); return lineEdit; } -void CustomWizardFieldPage::addField(const CustomWizardField &field) +void CustomWizardFieldPage::initializePage() { - addRow(field.description, registerControl(field)); + QWizardPage::initializePage(); + // Note that the field mechanism will always restore the value + // set on it when entering the page, so, there is no point in + // trying to preserve user modifications of the text. + foreach(const LineEditData &led, m_lineEdits) { + if (!led.defaultText.isEmpty()) { + QString defaultText = led.defaultText; + CustomWizardContext::replaceFields(m_context->baseReplacements, &defaultText); + led.lineEdit->setText(defaultText); + } + } } bool CustomWizardFieldPage::validatePage() { // Check line edits with validators - foreach(QLineEdit *le, m_validatorLineEdits) { - int pos = 0; - const QValidator *val = le->validator(); - QTC_ASSERT(val, return false); - QString text = le->text(); - if (val->validate(text, pos) != QValidator::Acceptable) { - le->setFocus(); - return false; + foreach(const LineEditData &led, m_lineEdits) { + if (const QValidator *val = led.lineEdit->validator()) { + int pos = 0; + QString text = led.lineEdit->text(); + if (val->validate(text, pos) != QValidator::Acceptable) { + led.lineEdit->setFocus(); + return false; + } } } return QWizardPage::validatePage(); @@ -150,9 +187,10 @@ bool CustomWizardFieldPage::validatePage() // --------------- CustomWizardPage -CustomWizardPage::CustomWizardPage(const FieldList &f, +CustomWizardPage::CustomWizardPage(const QSharedPointer<CustomWizardContext> &ctx, + const FieldList &f, QWidget *parent) : - CustomWizardFieldPage(f, parent), + CustomWizardFieldPage(ctx, f, parent), m_pathChooser(new Utils::PathChooser) { addRow(tr("Path:"), m_pathChooser); diff --git a/src/plugins/projectexplorer/customwizard/customwizardpage.h b/src/plugins/projectexplorer/customwizard/customwizardpage.h index ce1484ab0c3..fe27a0873f3 100644 --- a/src/plugins/projectexplorer/customwizard/customwizardpage.h +++ b/src/plugins/projectexplorer/customwizard/customwizardpage.h @@ -48,6 +48,7 @@ namespace Internal { struct CustomWizardField; struct CustomWizardParameters; +struct CustomWizardContext; // A non-editable combo for text editing purposes that plays // with QWizard::registerField (providing a settable text property). @@ -74,19 +75,33 @@ class CustomWizardFieldPage : public QWizardPage { public: typedef QList<CustomWizardField> FieldList; - explicit CustomWizardFieldPage(const FieldList &f, + explicit CustomWizardFieldPage(const QSharedPointer<CustomWizardContext> &ctx, + const FieldList &f, QWidget *parent = 0); + virtual ~CustomWizardFieldPage(); + virtual bool validatePage(); + virtual void initializePage(); protected: inline void addRow(const QString &name, QWidget *w); private: - QWidget *registerControl(const CustomWizardField &f); + struct LineEditData { + explicit LineEditData(QLineEdit* le = 0, const QString &defText = QString()); + QLineEdit* lineEdit; + QString defaultText; + }; + typedef QList<LineEditData> LineEditDataList; + + QWidget *registerLineEdit(const QString &fieldName, const CustomWizardField &field); + QWidget *registerComboBox(const QString &fieldName, const CustomWizardField &field); void addField(const CustomWizardField &f); + + const QSharedPointer<CustomWizardContext> m_context; QFormLayout *m_formLayout; - QList<QLineEdit*> m_validatorLineEdits; + LineEditDataList m_lineEdits; }; // A custom wizard page presenting the fields to be used and a path chooser @@ -96,7 +111,8 @@ private: class CustomWizardPage : public CustomWizardFieldPage { Q_OBJECT public: - explicit CustomWizardPage(const FieldList &f, + explicit CustomWizardPage(const QSharedPointer<CustomWizardContext> &ctx, + const FieldList &f, QWidget *parent = 0); QString path() const; diff --git a/src/plugins/projectexplorer/customwizard/customwizardparameters.cpp b/src/plugins/projectexplorer/customwizard/customwizardparameters.cpp index bf89b9fe902..c64c62c8c15 100644 --- a/src/plugins/projectexplorer/customwizard/customwizardparameters.cpp +++ b/src/plugins/projectexplorer/customwizard/customwizardparameters.cpp @@ -29,6 +29,10 @@ #include "customwizardparameters.h" +#include <coreplugin/mimedatabase.h> +#include <coreplugin/icore.h> +#include <cpptools/cpptoolsconstants.h> + #include <QtCore/QDebug> #include <QtCore/QLocale> #include <QtCore/QFile> @@ -467,5 +471,75 @@ QString CustomWizardParameters::toString() const return rc; } +// ------------ CustomWizardContext + +void CustomWizardContext::replaceFields(const FieldReplacementMap &fm, QString *s) +{ + if (debug) { + qDebug().nospace() << "CustomWizardContext::replaceFields with " << + fm << *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 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; + case 'c': // Capitalize first letter + replacement = it.value(); + if (!replacement.isEmpty()) + replacement[0] = replacement.at(0).toUpper(); + break; + default: + break; + } + s->replace(pos, nextPos - pos, replacement); + pos += replacement.size(); + } +} + +void CustomWizardContext::reset() +{ + // Basic replacement fields: Suffixes. + baseReplacements.clear(); + const Core::MimeDatabase *mdb = Core::ICore::instance()->mimeDatabase(); + baseReplacements.insert(QLatin1String("CppSourceSuffix"), + mdb->preferredSuffixByType(QLatin1String(CppTools::Constants::CPP_SOURCE_MIMETYPE))); + baseReplacements.insert(QLatin1String("CppHeaderSuffix"), + mdb->preferredSuffixByType(QLatin1String(CppTools::Constants::CPP_HEADER_MIMETYPE))); +} + } // namespace Internal } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/customwizard/customwizardparameters.h b/src/plugins/projectexplorer/customwizard/customwizardparameters.h index 3ee6a19b23d..63c6c0adbc1 100644 --- a/src/plugins/projectexplorer/customwizard/customwizardparameters.h +++ b/src/plugins/projectexplorer/customwizard/customwizardparameters.h @@ -79,6 +79,28 @@ public: int firstPageId; }; +// Context used for one wizard run, shared between CustomWizard +// and the CustomWizardPage as it is used for the QLineEdit-type fields' +// default texts as well. Contains basic replacement fields +// like '%CppSourceSuffix%', '%CppHeaderSuffix%' (settings-dependent) +// reset() should be called before each wizard run to refresh them. +// CustomProjectWizard additionally inserts '%ProjectName%' from +// the intro page to have it available for default texts. + +struct CustomWizardContext { + typedef QMap<QString, QString> FieldReplacementMap; + + void reset(); + + // Replace field values delimited by '%' with special modifiers: + // %Field% -> simple replacement + // %Field:l% -> lower case replacement, 'u' upper case, + // 'c' capitalize first letter. + static void replaceFields(const FieldReplacementMap &fm, QString *s); + + FieldReplacementMap baseReplacements; +}; + } // namespace Internal } // namespace ProjectExplorer -- GitLab