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.
This commit is contained in:
Friedemann Kleint
2010-03-22 15:33:33 +01:00
parent af14d9fc48
commit 5bc886c5b6
9 changed files with 279 additions and 123 deletions

View File

@@ -53,7 +53,8 @@ leave room for the Qt 4 target page.
<fieldpagetitle xml:lang="de">Hallo Welt Parameter</fieldpagetitle> <fieldpagetitle xml:lang="de">Hallo Welt Parameter</fieldpagetitle>
<fields> <fields>
<field mandatory="true" name="MESSAGE"> <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>Hello world message:</fielddescription>
<fielddescription xml:lang="de">Hallo-Welt-Nachricht:</fielddescription> <fielddescription xml:lang="de">Hallo-Welt-Nachricht:</fielddescription>
</field> </field>

View File

@@ -41,13 +41,17 @@ namespace ProjectExplorer {
struct BaseProjectWizardDialogPrivate { struct BaseProjectWizardDialogPrivate {
explicit BaseProjectWizardDialogPrivate(Utils::ProjectIntroPage *page, int id = -1); explicit BaseProjectWizardDialogPrivate(Utils::ProjectIntroPage *page, int id = -1);
const int introId; const int desiredIntroPageId;
Utils::ProjectIntroPage *introPage; Utils::ProjectIntroPage *introPage;
int introPageId;
int lastId;
}; };
BaseProjectWizardDialogPrivate::BaseProjectWizardDialogPrivate(Utils::ProjectIntroPage *page, int id) : BaseProjectWizardDialogPrivate::BaseProjectWizardDialogPrivate(Utils::ProjectIntroPage *page, int id) :
introId(id), desiredIntroPageId(id),
introPage(page) introPage(page),
introPageId(-1),
lastId(-1)
{ {
} }
@@ -70,8 +74,14 @@ BaseProjectWizardDialog::BaseProjectWizardDialog(Utils::ProjectIntroPage *introP
void BaseProjectWizardDialog::init() void BaseProjectWizardDialog::init()
{ {
Core::BaseFileWizard::setupWizard(this); 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(accepted()), this, SLOT(slotAccepted()));
connect(this, SIGNAL(currentIdChanged(int)), this, SLOT(slotBaseCurrentIdChanged(int)));
} }
BaseProjectWizardDialog::~BaseProjectWizardDialog() BaseProjectWizardDialog::~BaseProjectWizardDialog()
@@ -107,12 +117,21 @@ void BaseProjectWizardDialog::setProjectName(const QString &name)
void BaseProjectWizardDialog::slotAccepted() void BaseProjectWizardDialog::slotAccepted()
{ {
if (d->introPage->useAsDefaultPath()) { if (d->introPage->useAsDefaultPath()) {
// Store the path as default path for new projects if desired.
Core::FileManager *fm = Core::ICore::instance()->fileManager(); Core::FileManager *fm = Core::ICore::instance()->fileManager();
fm->setProjectsDirectory(path()); fm->setProjectsDirectory(path());
fm->setUseProjectsDirectory(true); 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 Utils::ProjectIntroPage *BaseProjectWizardDialog::introPage() const
{ {
return d->introPage; return d->introPage;

View File

@@ -71,11 +71,15 @@ public slots:
void setPath(const QString &path); void setPath(const QString &path);
void setProjectName(const QString &name); void setProjectName(const QString &name);
signals:
void introPageLeft(const QString &projectName, const QString &path);
protected: protected:
Utils::ProjectIntroPage *introPage() const; Utils::ProjectIntroPage *introPage() const;
private slots: private slots:
void slotAccepted(); void slotAccepted();
void slotBaseCurrentIdChanged(int);
private: private:
void init(); void init();

View File

@@ -35,8 +35,6 @@
#include <coreplugin/icore.h> #include <coreplugin/icore.h>
#include <coreplugin/messagemanager.h> #include <coreplugin/messagemanager.h>
#include <coreplugin/mimedatabase.h>
#include <cpptools/cpptoolsconstants.h>
#include <extensionsystem/pluginmanager.h> #include <extensionsystem/pluginmanager.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
@@ -52,7 +50,10 @@ static const char configFileC[] = "wizard.xml";
namespace ProjectExplorer { namespace ProjectExplorer {
struct CustomWizardPrivate { struct CustomWizardPrivate {
CustomWizardPrivate() : m_context(new Internal::CustomWizardContext) {}
QSharedPointer<Internal::CustomWizardParameters> m_parameters; QSharedPointer<Internal::CustomWizardParameters> m_parameters;
QSharedPointer<Internal::CustomWizardContext> m_context;
static int verbose; static int verbose;
}; };
@@ -105,7 +106,9 @@ void CustomWizard::initWizardDialog(QWizard *wizard, const QString &defaultPath,
const WizardPageList &extensionPages) const const WizardPageList &extensionPages) const
{ {
QTC_ASSERT(!parameters().isNull(), return); 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); customPage->setPath(defaultPath);
addWizardPage(wizard, customPage, parameters()->firstPageId); addWizardPage(wizard, customPage, parameters()->firstPageId);
if (!parameters()->fieldPageTitle.isEmpty()) if (!parameters()->fieldPageTitle.isEmpty())
@@ -127,57 +130,6 @@ QWizard *CustomWizard::createWizardDialog(QWidget *parent,
return wizard; 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. // Read out files and store contents with field contents replaced.
static inline bool createFile(Internal::CustomWizardFile cwFile, static inline bool createFile(Internal::CustomWizardFile cwFile,
const QString &sourceDirectory, const QString &sourceDirectory,
@@ -188,7 +140,8 @@ static inline bool createFile(Internal::CustomWizardFile cwFile,
{ {
const QChar slash = QLatin1Char('/'); const QChar slash = QLatin1Char('/');
const QString sourcePath = sourceDirectory + slash + cwFile.source; 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); const QString targetPath = QDir::toNativeSeparators(targetDirectory + slash + cwFile.target);
if (CustomWizardPrivate::verbose) if (CustomWizardPrivate::verbose)
qDebug() << "generating " << targetPath << sourcePath << fm; 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()); *errorMessage = QString::fromLatin1("Cannot open %1: %2").arg(sourcePath, file.errorString());
return false; return false;
} }
// Field replacement on contents
QString contents = QString::fromLocal8Bit(file.readAll()); QString contents = QString::fromLocal8Bit(file.readAll());
if (!contents.isEmpty() && !fm.isEmpty()) if (!contents.isEmpty() && !fm.isEmpty())
replaceFields(fm, &contents); Internal::CustomWizardContext::replaceFields(fm, &contents);
Core::GeneratedFile generatedFile; Core::GeneratedFile generatedFile;
generatedFile.setContents(contents); generatedFile.setContents(contents);
generatedFile.setPath(targetPath); generatedFile.setPath(targetPath);
@@ -223,9 +177,16 @@ Core::GeneratedFiles CustomWizard::generateFiles(const QWizard *dialog, QString
const Internal::CustomWizardPage *cwp = findWizardPage<Internal::CustomWizardPage>(dialog); const Internal::CustomWizardPage *cwp = findWizardPage<Internal::CustomWizardPage>(dialog);
QTC_ASSERT(cwp, return Core::GeneratedFiles()) QTC_ASSERT(cwp, return Core::GeneratedFiles())
QString path = cwp->path(); QString path = cwp->path();
const FieldReplacementMap fieldMap = defaultReplacementMap(dialog); const FieldReplacementMap fieldMap = replacementMap(dialog);
if (CustomWizardPrivate::verbose) if (CustomWizardPrivate::verbose) {
qDebug() << "CustomWizard::generateFiles" << dialog << path << fieldMap; 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); return generateWizardFiles(path, fieldMap, errorMessage);
} }
@@ -243,20 +204,14 @@ Core::GeneratedFiles CustomWizard::generateWizardFiles(const QString &targetPath
return rc; return rc;
} }
// Create a default replacement map from the wizard dialog via fields // Create a replacement map of static base fields + wizard dialog fields
// and add some useful fields. CustomWizard::FieldReplacementMap CustomWizard::replacementMap(const QWizard *w) const
CustomWizard::FieldReplacementMap CustomWizard::defaultReplacementMap(const QWizard *w) const
{ {
FieldReplacementMap fieldReplacementMap; FieldReplacementMap fieldReplacementMap = d->m_context->baseReplacements;
foreach(const Internal::CustomWizardField &field, d->m_parameters->fields) { foreach(const Internal::CustomWizardField &field, d->m_parameters->fields) {
const QString value = w->field(field.name).toString(); const QString value = w->field(field.name).toString();
fieldReplacementMap.insert(field.name, value); 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; return fieldReplacementMap;
} }
@@ -265,6 +220,11 @@ CustomWizard::CustomWizardParametersPtr CustomWizard::parameters() const
return d->m_parameters; return d->m_parameters;
} }
CustomWizard::CustomWizardContextPtr CustomWizard::context() const
{
return d->m_context;
}
// Static factory map // Static factory map
typedef QMap<QString, QSharedPointer<ICustomWizardFactory> > CustomWizardFactoryMap; typedef QMap<QString, QSharedPointer<ICustomWizardFactory> > CustomWizardFactoryMap;
Q_GLOBAL_STATIC(CustomWizardFactoryMap, customWizardFactoryMap) Q_GLOBAL_STATIC(CustomWizardFactoryMap, customWizardFactoryMap)
@@ -407,17 +367,25 @@ void CustomProjectWizard::initProjectWizardDialog(BaseProjectWizardDialog *w,
const QString &defaultPath, const QString &defaultPath,
const WizardPageList &extensionPages) const const WizardPageList &extensionPages) const
{ {
QTC_ASSERT(!parameters().isNull(), return); const CustomWizardParametersPtr pa = parameters();
if (!parameters()->fields.isEmpty()) { QTC_ASSERT(!pa.isNull(), return);
Internal::CustomWizardFieldPage *cp = new Internal::CustomWizardFieldPage(parameters()->fields);
const CustomWizardContextPtr ctx = context();
ctx->reset();
if (!pa->fields.isEmpty()) {
Internal::CustomWizardFieldPage *cp = new Internal::CustomWizardFieldPage(ctx, pa->fields);
addWizardPage(w, cp, parameters()->firstPageId); addWizardPage(w, cp, parameters()->firstPageId);
if (!parameters()->fieldPageTitle.isEmpty()) if (!pa->fieldPageTitle.isEmpty())
cp->setTitle(parameters()->fieldPageTitle); cp->setTitle(pa->fieldPageTitle);
} }
foreach(QWizardPage *ep, extensionPages) foreach(QWizardPage *ep, extensionPages)
w->addPage(ep); w->addPage(ep);
w->setPath(defaultPath); w->setPath(defaultPath);
w->setProjectName(BaseProjectWizardDialog::uniqueProjectName(defaultPath)); w->setProjectName(BaseProjectWizardDialog::uniqueProjectName(defaultPath));
connect(w, SIGNAL(introPageLeft(QString,QString)), this, SLOT(introPageLeft(QString,QString)));
if (CustomWizardPrivate::verbose) if (CustomWizardPrivate::verbose)
qDebug() << "initProjectWizardDialog" << w << w->pageIds(); qDebug() << "initProjectWizardDialog" << w << w->pageIds();
} }
@@ -428,7 +396,7 @@ Core::GeneratedFiles CustomProjectWizard::generateFiles(const QWizard *w, QStrin
QTC_ASSERT(dialog, return Core::GeneratedFiles()) QTC_ASSERT(dialog, return Core::GeneratedFiles())
const QString targetPath = dialog->path() + QLatin1Char('/') + dialog->projectName(); const QString targetPath = dialog->path() + QLatin1Char('/') + dialog->projectName();
// Add project name as macro. // Add project name as macro.
FieldReplacementMap fieldReplacementMap = defaultReplacementMap(dialog); FieldReplacementMap fieldReplacementMap = replacementMap(dialog);
fieldReplacementMap.insert(QLatin1String("ProjectName"), dialog->projectName()); fieldReplacementMap.insert(QLatin1String("ProjectName"), dialog->projectName());
if (CustomWizardPrivate::verbose) if (CustomWizardPrivate::verbose)
qDebug() << "CustomProjectWizard::generateFiles" << dialog << targetPath << fieldReplacementMap; qDebug() << "CustomProjectWizard::generateFiles" << dialog << targetPath << fieldReplacementMap;
@@ -449,4 +417,10 @@ bool CustomProjectWizard::postGenerateFiles(const QWizard *, const Core::Generat
return true; 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 } // namespace ProjectExplorer

View File

@@ -49,6 +49,7 @@ class BaseProjectWizardDialog;
namespace Internal { namespace Internal {
struct CustomWizardParameters; struct CustomWizardParameters;
struct CustomWizardContext;
} }
// Factory for creating wizard. Can be registered under a name // Factory for creating wizard. Can be registered under a name
@@ -105,6 +106,7 @@ public:
protected: protected:
typedef QSharedPointer<Internal::CustomWizardParameters> CustomWizardParametersPtr; typedef QSharedPointer<Internal::CustomWizardParameters> CustomWizardParametersPtr;
typedef QSharedPointer<Internal::CustomWizardContext> CustomWizardContextPtr;
void initWizardDialog(QWizard *w, const QString &defaultPath, void initWizardDialog(QWizard *w, const QString &defaultPath,
const WizardPageList &extensionPages) const; const WizardPageList &extensionPages) const;
@@ -113,10 +115,11 @@ protected:
Core::GeneratedFiles generateWizardFiles(const QString &path, Core::GeneratedFiles generateWizardFiles(const QString &path,
const FieldReplacementMap &defaultFields, const FieldReplacementMap &defaultFields,
QString *errorMessage) const; QString *errorMessage) const;
// Create replacement map from QWizard fields with additional useful fields. // Create replacement map as static base fields + QWizard fields
FieldReplacementMap defaultReplacementMap(const QWizard *w) const; FieldReplacementMap replacementMap(const QWizard *w) const;
CustomWizardParametersPtr parameters() const; CustomWizardParametersPtr parameters() const;
CustomWizardContextPtr context() const;
private: private:
void setParameters(const CustomWizardParametersPtr &p); void setParameters(const CustomWizardParametersPtr &p);
@@ -128,7 +131,9 @@ private:
// A custom project wizard presenting CustomProjectWizardDialog // A custom project wizard presenting CustomProjectWizardDialog
// (Project intro page and fields page) for wizards of type "project". // (Project intro page and fields page) for wizards of type "project".
// Overwrites postGenerateFiles() to open the project file which is the // 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 class PROJECTEXPLORER_EXPORT CustomProjectWizard : public CustomWizard
{ {
@@ -150,6 +155,9 @@ protected:
void initProjectWizardDialog(BaseProjectWizardDialog *w, const QString &defaultPath, void initProjectWizardDialog(BaseProjectWizardDialog *w, const QString &defaultPath,
const WizardPageList &extensionPages) const; const WizardPageList &extensionPages) const;
private slots:
void introPageLeft(const QString &project, const QString &path);
}; };
} // namespace ProjectExplorer } // namespace ProjectExplorer

View File

@@ -63,9 +63,17 @@ void TextFieldComboBox::setText(const QString &s)
} }
// --------------- CustomWizardFieldPage // --------------- 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) : QWidget *parent) :
QWizardPage(parent), QWizardPage(parent),
m_context(ctx),
m_formLayout(new QFormLayout) m_formLayout(new QFormLayout)
{ {
if (debug) if (debug)
@@ -75,6 +83,10 @@ CustomWizardFieldPage::CustomWizardFieldPage(const FieldList &fields,
setLayout(m_formLayout); setLayout(m_formLayout);
} }
CustomWizardFieldPage::~CustomWizardFieldPage()
{
}
void CustomWizardFieldPage::addRow(const QString &name, QWidget *w) void CustomWizardFieldPage::addRow(const QString &name, QWidget *w)
{ {
m_formLayout->addRow(name, w); m_formLayout->addRow(name, w);
@@ -82,18 +94,28 @@ void CustomWizardFieldPage::addRow(const QString &name, QWidget *w)
// Create widget a control based on the control attributes map // Create widget a control based on the control attributes map
// and register it with the QWizard. // 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; QString fieldName = field.name;
if (field.mandatory) if (field.mandatory)
fieldName += QLatin1Char('*'); fieldName += QLatin1Char('*');
// Check known classes: QComboBox // Check known classes: QComboBox
const QString className = field.controlAttributes.value(QLatin1String("class")); const QString className = field.controlAttributes.value(QLatin1String("class"));
QWidget *fieldWidget = 0;
if (className == QLatin1String("QComboBox")) { if (className == QLatin1String("QComboBox")) {
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; TextFieldComboBox *combo = new TextFieldComboBox;
// Set up items do { // Set up items and current index
do {
const QString choices = field.controlAttributes.value(QLatin1String("combochoices")); const QString choices = field.controlAttributes.value(QLatin1String("combochoices"));
if (choices.isEmpty()) if (choices.isEmpty())
break; break;
@@ -109,9 +131,13 @@ QWidget *CustomWizardFieldPage::registerControl(const CustomWizardField &field)
} while (false); } while (false);
registerField(fieldName, combo, "text", SIGNAL(text4Changed(QString))); registerField(fieldName, combo, "text", SIGNAL(text4Changed(QString)));
return combo; return combo;
} // QComboBox } // QComboBox
// Default to QLineEdit
QWidget *CustomWizardFieldPage::registerLineEdit(const QString &fieldName,
const CustomWizardField &field)
{
QLineEdit *lineEdit = new QLineEdit; QLineEdit *lineEdit = new QLineEdit;
const QString validationRegExp = field.controlAttributes.value(QLatin1String("validator")); const QString validationRegExp = field.controlAttributes.value(QLatin1String("validator"));
if (!validationRegExp.isEmpty()) { if (!validationRegExp.isEmpty()) {
QRegExp re(validationRegExp); QRegExp re(validationRegExp);
@@ -120,39 +146,51 @@ QWidget *CustomWizardFieldPage::registerControl(const CustomWizardField &field)
} else { } else {
qWarning("Invalid custom wizard field validator regular expression %s.", qPrintable(validationRegExp)); 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))); registerField(fieldName, lineEdit, "text", SIGNAL(textEdited(QString)));
const QString defaultText = field.controlAttributes.value(QLatin1String("defaulttext"));
m_lineEdits.push_back(LineEditData(lineEdit, defaultText));
return lineEdit; 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() bool CustomWizardFieldPage::validatePage()
{ {
// Check line edits with validators // Check line edits with validators
foreach(QLineEdit *le, m_validatorLineEdits) { foreach(const LineEditData &led, m_lineEdits) {
if (const QValidator *val = led.lineEdit->validator()) {
int pos = 0; int pos = 0;
const QValidator *val = le->validator(); QString text = led.lineEdit->text();
QTC_ASSERT(val, return false);
QString text = le->text();
if (val->validate(text, pos) != QValidator::Acceptable) { if (val->validate(text, pos) != QValidator::Acceptable) {
le->setFocus(); led.lineEdit->setFocus();
return false; return false;
} }
} }
}
return QWizardPage::validatePage(); return QWizardPage::validatePage();
} }
// --------------- CustomWizardPage // --------------- CustomWizardPage
CustomWizardPage::CustomWizardPage(const FieldList &f, CustomWizardPage::CustomWizardPage(const QSharedPointer<CustomWizardContext> &ctx,
const FieldList &f,
QWidget *parent) : QWidget *parent) :
CustomWizardFieldPage(f, parent), CustomWizardFieldPage(ctx, f, parent),
m_pathChooser(new Utils::PathChooser) m_pathChooser(new Utils::PathChooser)
{ {
addRow(tr("Path:"), m_pathChooser); addRow(tr("Path:"), m_pathChooser);

View File

@@ -48,6 +48,7 @@ namespace Internal {
struct CustomWizardField; struct CustomWizardField;
struct CustomWizardParameters; struct CustomWizardParameters;
struct CustomWizardContext;
// A non-editable combo for text editing purposes that plays // A non-editable combo for text editing purposes that plays
// with QWizard::registerField (providing a settable text property). // with QWizard::registerField (providing a settable text property).
@@ -74,19 +75,33 @@ class CustomWizardFieldPage : public QWizardPage {
public: public:
typedef QList<CustomWizardField> FieldList; typedef QList<CustomWizardField> FieldList;
explicit CustomWizardFieldPage(const FieldList &f, explicit CustomWizardFieldPage(const QSharedPointer<CustomWizardContext> &ctx,
const FieldList &f,
QWidget *parent = 0); QWidget *parent = 0);
virtual ~CustomWizardFieldPage();
virtual bool validatePage(); virtual bool validatePage();
virtual void initializePage();
protected: protected:
inline void addRow(const QString &name, QWidget *w); inline void addRow(const QString &name, QWidget *w);
private: 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); void addField(const CustomWizardField &f);
const QSharedPointer<CustomWizardContext> m_context;
QFormLayout *m_formLayout; QFormLayout *m_formLayout;
QList<QLineEdit*> m_validatorLineEdits; LineEditDataList m_lineEdits;
}; };
// A custom wizard page presenting the fields to be used and a path chooser // A custom wizard page presenting the fields to be used and a path chooser
@@ -96,7 +111,8 @@ private:
class CustomWizardPage : public CustomWizardFieldPage { class CustomWizardPage : public CustomWizardFieldPage {
Q_OBJECT Q_OBJECT
public: public:
explicit CustomWizardPage(const FieldList &f, explicit CustomWizardPage(const QSharedPointer<CustomWizardContext> &ctx,
const FieldList &f,
QWidget *parent = 0); QWidget *parent = 0);
QString path() const; QString path() const;

View File

@@ -29,6 +29,10 @@
#include "customwizardparameters.h" #include "customwizardparameters.h"
#include <coreplugin/mimedatabase.h>
#include <coreplugin/icore.h>
#include <cpptools/cpptoolsconstants.h>
#include <QtCore/QDebug> #include <QtCore/QDebug>
#include <QtCore/QLocale> #include <QtCore/QLocale>
#include <QtCore/QFile> #include <QtCore/QFile>
@@ -467,5 +471,75 @@ QString CustomWizardParameters::toString() const
return rc; 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 Internal
} // namespace ProjectExplorer } // namespace ProjectExplorer

View File

@@ -79,6 +79,28 @@ public:
int firstPageId; 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 Internal
} // namespace ProjectExplorer } // namespace ProjectExplorer