Template-CustomWizard: Make QComboBox parameters more flexible.

Separate value and display text for the entries, use
in ListModel example.
Rubber-stamped-by: aportale <alessandro.portale@trolltech.com>
This commit is contained in:
Friedemann Kleint
2010-07-08 11:46:05 +02:00
parent cdf7dbeada
commit 280541aec9
5 changed files with 207 additions and 43 deletions

View File

@@ -51,7 +51,18 @@ Custom class wizard example configuration file. -->
<fielddescription xml:lang="de">Klassenname:</fielddescription> <fielddescription xml:lang="de">Klassenname:</fielddescription>
</field> </field>
<field name="Datatype"> <field name="Datatype">
<fieldcontrol class="QComboBox" combochoices="QString,int" defaultindex="0" /> <fieldcontrol class="QComboBox" defaultindex="0">
<comboentries>
<comboentry value="QString">
<comboentrytext>class QString</comboentrytext>
<comboentrytext xml:lang="de">Klasse QString</comboentrytext>
</comboentry>
<comboentry value="int">
<comboentrytext>Integer</comboentrytext>
<comboentrytext xml:lang="de">Ganzzahlwert</comboentrytext>
</comboentry>
</comboentries>
</fieldcontrol>
<fielddescription>Data type:</fielddescription> <fielddescription>Data type:</fielddescription>
<fielddescription xml:lang="de">Datentyp:</fielddescription> <fielddescription xml:lang="de">Datentyp:</fielddescription>
</field> </field>

View File

@@ -54,16 +54,43 @@ TextFieldComboBox::TextFieldComboBox(QWidget *parent) :
QComboBox(parent) QComboBox(parent)
{ {
setEditable(false); setEditable(false);
connect(this, SIGNAL(currentIndexChanged(QString)), this, SIGNAL(textChanged(QString))); connect(this, SIGNAL(currentIndexChanged(int)),
this, SLOT(slotCurrentIndexChanged(int)));
}
QString TextFieldComboBox::text() const
{
return valueAt(currentIndex());
} }
void TextFieldComboBox::setText(const QString &s) void TextFieldComboBox::setText(const QString &s)
{ {
const int index = findText(s); const int index = findData(QVariant(s), Qt::UserRole);
if (index != -1 && index != currentIndex()) if (index != -1 && index != currentIndex())
setCurrentIndex(index); setCurrentIndex(index);
} }
void TextFieldComboBox::slotCurrentIndexChanged(int i)
{
emit text4Changed(valueAt(i));
}
void TextFieldComboBox::setItems(const QStringList &displayTexts,
const QStringList &values)
{
QTC_ASSERT(displayTexts.size() == values.size(), return)
clear();
addItems(displayTexts);
const int count = values.count();
for (int i = 0; i < count; i++)
setItemData(i, QVariant(values.at(i)), Qt::UserRole);
}
QString TextFieldComboBox::valueAt(int i) const
{
return i >= 0 && i < count() ? itemData(i, Qt::UserRole).toString() : QString();
}
// -------------- TextCheckBox // -------------- TextCheckBox
TextFieldCheckBox::TextFieldCheckBox(const QString &text, QWidget *parent) : TextFieldCheckBox::TextFieldCheckBox(const QString &text, QWidget *parent) :
QCheckBox(text, parent), QCheckBox(text, parent),
@@ -154,15 +181,45 @@ void CustomWizardFieldPage::addField(const CustomWizardField &field)\
} }
} }
// Return the list of values and display texts for combo
static void comboChoices(const CustomWizardField::ControlAttributeMap &controlAttributes,
QStringList *values, QStringList *displayTexts)
{
typedef CustomWizardField::ControlAttributeMap::ConstIterator AttribMapConstIt;
values->clear();
displayTexts->clear();
// Pre 2.2 Legacy: "combochoices" attribute with a comma-separated list, for
// display == value.
const AttribMapConstIt attribConstEnd = controlAttributes.constEnd();
const AttribMapConstIt choicesIt = controlAttributes.constFind(QLatin1String("combochoices"));
if (choicesIt != attribConstEnd) {
const QString &choices = choicesIt.value();
if (!choices.isEmpty())
*values = *displayTexts = choices.split(QLatin1Char(','));
return;
}
// From 2.2 on: Separate lists of value and text. Add all values found.
for (int i = 0; ; i++) {
const QString valueKey = CustomWizardField::comboEntryValueKey(i);
const AttribMapConstIt valueIt = controlAttributes.constFind(valueKey);
if (valueIt == attribConstEnd)
break;
values->push_back(valueIt.value());
const QString textKey = CustomWizardField::comboEntryTextKey(i);
displayTexts->push_back(controlAttributes.value(textKey));
}
}
QWidget *CustomWizardFieldPage::registerComboBox(const QString &fieldName, QWidget *CustomWizardFieldPage::registerComboBox(const QString &fieldName,
const CustomWizardField &field) const CustomWizardField &field)
{ {
TextFieldComboBox *combo = new TextFieldComboBox; TextFieldComboBox *combo = new TextFieldComboBox;
do { // Set up items and current index do { // Set up items and current index
const QString choices = field.controlAttributes.value(QLatin1String("combochoices")); QStringList values;
if (choices.isEmpty()) QStringList displayTexts;
break; comboChoices(field.controlAttributes, &values, &displayTexts);
combo->addItems(choices.split(QLatin1Char(','))); combo->setItems(displayTexts, values);
bool ok; bool ok;
const QString currentIndexS = field.controlAttributes.value(QLatin1String("defaultindex")); const QString currentIndexS = field.controlAttributes.value(QLatin1String("defaultindex"));
if (currentIndexS.isEmpty()) if (currentIndexS.isEmpty())
@@ -201,6 +258,8 @@ QWidget *CustomWizardFieldPage::registerCheckBox(const QString &fieldName,
typedef CustomWizardField::ControlAttributeMap::const_iterator AttributeMapConstIt; typedef CustomWizardField::ControlAttributeMap::const_iterator AttributeMapConstIt;
TextFieldCheckBox *checkBox = new TextFieldCheckBox(fieldDescription); TextFieldCheckBox *checkBox = new TextFieldCheckBox(fieldDescription);
const bool defaultValue = field.controlAttributes.value(QLatin1String("defaultvalue")) == QLatin1String("true");
checkBox->setChecked(defaultValue);
const AttributeMapConstIt trueTextIt = field.controlAttributes.constFind(QLatin1String("truevalue")); const AttributeMapConstIt trueTextIt = field.controlAttributes.constFind(QLatin1String("truevalue"));
if (trueTextIt != field.controlAttributes.constEnd()) // Also set empty texts if (trueTextIt != field.controlAttributes.constEnd()) // Also set empty texts
checkBox->setTrueText(trueTextIt.value()); checkBox->setTrueText(trueTextIt.value());

View File

@@ -54,17 +54,27 @@ 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).
// Allows for a separation of values to be used for wizard fields replacement
// and display texts.
class TextFieldComboBox : public QComboBox { class TextFieldComboBox : public QComboBox {
Q_PROPERTY(QString text READ text WRITE setText) Q_PROPERTY(QString text READ text WRITE setText)
Q_OBJECT Q_OBJECT
public: public:
explicit TextFieldComboBox(QWidget *parent = 0); explicit TextFieldComboBox(QWidget *parent = 0);
QString text() const { return currentText(); } QString text() const;
void setText(const QString &s); void setText(const QString &s);
void setItems(const QStringList &displayTexts, const QStringList &values);
signals: signals:
void text4Changed(const QString &); // Do not conflict with Qt 3 compat signal. void text4Changed(const QString &); // Do not conflict with Qt 3 compat signal.
private slots:
void slotCurrentIndexChanged(int);
private:
inline QString valueAt(int) const;
}; };
// A Checkbox that plays with QWizard::registerField (providing a settable // A Checkbox that plays with QWizard::registerField (providing a settable

View File

@@ -58,6 +58,9 @@ static const char displayCategoryElementC[] = "displaycategory";
static const char fieldPageTitleElementC[] = "fieldpagetitle"; static const char fieldPageTitleElementC[] = "fieldpagetitle";
static const char fieldsElementC[] = "fields"; static const char fieldsElementC[] = "fields";
static const char fieldElementC[] = "field"; static const char fieldElementC[] = "field";
static const char comboEntriesElementC[] = "comboentries";
static const char comboEntryElementC[] = "comboentry";
static const char comboEntryTextElementC[] = "comboentrytext";
static const char fieldDescriptionElementC[] = "fielddescription"; static const char fieldDescriptionElementC[] = "fielddescription";
static const char fieldNameAttributeC[] = "name"; static const char fieldNameAttributeC[] = "name";
@@ -77,6 +80,11 @@ enum ParseState {
ParseWithinWizard, ParseWithinWizard,
ParseWithinFields, ParseWithinFields,
ParseWithinField, ParseWithinField,
ParseWithinFieldDescription,
ParseWithinFieldControl,
ParseWithinComboEntries,
ParseWithinComboEntry,
ParseWithinComboEntryText,
ParseWithinFiles, ParseWithinFiles,
ParseWithinFile, ParseWithinFile,
ParseError ParseError
@@ -98,6 +106,17 @@ void CustomWizardField::clear()
controlAttributes.clear(); controlAttributes.clear();
} }
// Attribute map keys for combo entries
QString CustomWizardField::comboEntryValueKey(int i)
{
return QLatin1String("comboValue") + QString::number(i);
}
QString CustomWizardField::comboEntryTextKey(int i)
{
return QLatin1String("comboText") + QString::number(i);
}
CustomWizardFile::CustomWizardFile() : CustomWizardFile::CustomWizardFile() :
openEditor(false), openProject(false) openEditor(false), openProject(false)
{ {
@@ -148,7 +167,7 @@ static inline bool skipOverElementText(QXmlStreamReader &reader)
// Assign the element text to the string passed on if the language matches, // 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. // that is, the element has no language attribute or there is an exact match.
// If there is no match, skip over the element text. // If there is no match, skip over the element text.
static inline void assignLanguageElementText(QXmlStreamReader &reader, static inline bool assignLanguageElementText(QXmlStreamReader &reader,
const QString &desiredLanguage, const QString &desiredLanguage,
QString *target) QString *target)
{ {
@@ -156,18 +175,21 @@ static inline void assignLanguageElementText(QXmlStreamReader &reader,
if (elementLanguage.isEmpty()) { if (elementLanguage.isEmpty()) {
// Try to find a translation for our built-in Wizards // Try to find a translation for our built-in Wizards
*target = QCoreApplication::translate("ProjectExplorer::CustomWizard", reader.readElementText().toLatin1().constData()); *target = QCoreApplication::translate("ProjectExplorer::CustomWizard", reader.readElementText().toLatin1().constData());
} else if (elementLanguage == desiredLanguage) { return true;
}
if (elementLanguage == desiredLanguage) {
*target = reader.readElementText(); *target = reader.readElementText();
} else { return true;
}
// Language mismatch: forward to end element. // Language mismatch: forward to end element.
skipOverElementText(reader); skipOverElementText(reader);
} return false;
} }
// Copy&paste from above to call a setter of BaseFileParameters. // Copy&paste from above to call a setter of BaseFileParameters.
// Implementation of a sophisticated mem_fun pattern is left // Implementation of a sophisticated mem_fun pattern is left
// as an exercise to the reader. // as an exercise to the reader.
static inline void assignLanguageElementText(QXmlStreamReader &reader, static inline bool assignLanguageElementText(QXmlStreamReader &reader,
const QString &desiredLanguage, const QString &desiredLanguage,
Core::BaseFileWizardParameters *bp, Core::BaseFileWizardParameters *bp,
void (Core::BaseFileWizardParameters::*setter)(const QString &)) void (Core::BaseFileWizardParameters::*setter)(const QString &))
@@ -177,12 +199,15 @@ static inline void assignLanguageElementText(QXmlStreamReader &reader,
// Try to find a translation for our built-in Wizards // Try to find a translation for our built-in Wizards
const QString translated = QCoreApplication::translate("ProjectExplorer::CustomWizard", reader.readElementText().toLatin1().constData()); const QString translated = QCoreApplication::translate("ProjectExplorer::CustomWizard", reader.readElementText().toLatin1().constData());
(bp->*setter)(translated); (bp->*setter)(translated);
} else if (elementLanguage == desiredLanguage) { return true;
}
if (elementLanguage == desiredLanguage) {
(bp->*setter)(reader.readElementText()); (bp->*setter)(reader.readElementText());
} else { return true;
}
// Language mismatch: forward to end element. // Language mismatch: forward to end element.
skipOverElementText(reader); skipOverElementText(reader);
} return false;
} }
// Read level sub-elements of "wizard" // Read level sub-elements of "wizard"
@@ -226,24 +251,12 @@ static bool parseCustomProjectElement(QXmlStreamReader &reader,
return false; return false;
} }
// Read sub-elements of "fields" static inline QMap<QString, QString> attributesToStringMap(const QXmlStreamAttributes &attributes)
static bool parseFieldElement(QXmlStreamReader &reader,
const QString &language,
CustomWizardField *m)
{ {
const QStringRef elementName = reader.name(); QMap<QString, QString> rc;
if (elementName == QLatin1String(fieldDescriptionElementC)) { foreach(const QXmlStreamAttribute &attribute, attributes)
assignLanguageElementText(reader, language, &m->description); rc.insert(attribute.name().toString(), attribute.value().toString());
return true; return rc;
}
// 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. // Switch parser state depending on opening element name.
@@ -264,11 +277,30 @@ static ParseState nextOpeningState(ParseState in, const QStringRef &name)
if (name == QLatin1String(fieldElementC)) if (name == QLatin1String(fieldElementC))
return ParseWithinField; return ParseWithinField;
break; break;
case ParseWithinField:
if (name == QLatin1String(fieldDescriptionElementC))
return ParseWithinFieldDescription;
if (name == QLatin1String(fieldControlElementC))
return ParseWithinFieldControl;
break;
case ParseWithinFieldControl:
if (name == QLatin1String(comboEntriesElementC))
return ParseWithinComboEntries;
break;
case ParseWithinComboEntries:
if (name == QLatin1String(comboEntryElementC))
return ParseWithinComboEntry;
break;
case ParseWithinComboEntry:
if (name == QLatin1String(comboEntryTextElementC))
return ParseWithinComboEntryText;
break;
case ParseWithinFiles: case ParseWithinFiles:
if (name == QLatin1String(fileElementC)) if (name == QLatin1String(fileElementC))
return ParseWithinFile; return ParseWithinFile;
break; break;
case ParseWithinField: case ParseWithinFieldDescription: // No subelements
case ParseWithinComboEntryText:
case ParseWithinFile: case ParseWithinFile:
case ParseError: case ParseError:
break; break;
@@ -302,6 +334,26 @@ static ParseState nextClosingState(ParseState in, const QStringRef &name)
if (name == QLatin1String(fileElementC)) if (name == QLatin1String(fileElementC))
return ParseWithinFiles; return ParseWithinFiles;
break; break;
case ParseWithinFieldDescription:
if (name == QLatin1String(fieldDescriptionElementC))
return ParseWithinField;
break;
case ParseWithinFieldControl:
if (name == QLatin1String(fieldControlElementC))
return ParseWithinField;
break;
case ParseWithinComboEntries:
if (name == QLatin1String(comboEntriesElementC))
return ParseWithinFieldControl;
break;
case ParseWithinComboEntry:
if (name == QLatin1String(comboEntryElementC))
return ParseWithinComboEntries;
break;
case ParseWithinComboEntryText:
if (name == QLatin1String(comboEntryTextElementC))
return ParseWithinComboEntry;
break;
case ParseError: case ParseError:
break; break;
} }
@@ -373,6 +425,7 @@ bool CustomWizardParameters::parse(QIODevice &device,
Core::BaseFileWizardParameters *bp, Core::BaseFileWizardParameters *bp,
QString *errorMessage) QString *errorMessage)
{ {
int comboEntryCount = 0;
QXmlStreamReader reader(&device); QXmlStreamReader reader(&device);
QXmlStreamReader::TokenType token = QXmlStreamReader::EndDocument; QXmlStreamReader::TokenType token = QXmlStreamReader::EndDocument;
ParseState state = ParseBeginning; ParseState state = ParseBeginning;
@@ -392,8 +445,6 @@ bool CustomWizardParameters::parse(QIODevice &device,
// Read out subelements applicable to current state // Read out subelements applicable to current state
if (state == ParseWithinWizard && parseCustomProjectElement(reader, configFileFullPath, language, this, bp)) if (state == ParseWithinWizard && parseCustomProjectElement(reader, configFileFullPath, language, this, bp))
break; break;
if (state == ParseWithinField && parseFieldElement(reader, language, &field))
break;
// switch to next state // switch to next state
state = nextOpeningState(state, reader.name()); state = nextOpeningState(state, reader.name());
// Read attributes post state-switching // Read attributes post state-switching
@@ -413,6 +464,29 @@ bool CustomWizardParameters::parse(QIODevice &device,
field.name = attributeValue(reader, fieldNameAttributeC); field.name = attributeValue(reader, fieldNameAttributeC);
field.mandatory = booleanAttributeValue(reader, fieldMandatoryAttributeC); field.mandatory = booleanAttributeValue(reader, fieldMandatoryAttributeC);
break; break;
case ParseWithinFieldDescription:
assignLanguageElementText(reader, language, &field.description);
state = ParseWithinField; // The above reads away the end tag, set state.
break;
case ParseWithinFieldControl: // Copy widget control attributes
field.controlAttributes = attributesToStringMap(reader.attributes());
break;
case ParseWithinComboEntries:
break;
case ParseWithinComboEntry: // Combo entry with 'value' attribute
field.controlAttributes.insert(CustomWizardField::comboEntryValueKey(comboEntryCount),
attributeValue(reader, "value"));
break;
case ParseWithinComboEntryText: {
// This reads away the end tag, set state here.
QString text;
if (assignLanguageElementText(reader, language, &text))
field.controlAttributes.insert(CustomWizardField::comboEntryTextKey(comboEntryCount),
text);
state = ParseWithinComboEntry;
}
break;
case ParseWithinFile: { // file attribute case ParseWithinFile: { // file attribute
CustomWizardFile file; CustomWizardFile file;
file.source = attributeValue(reader, fileNameSourceAttributeC); file.source = attributeValue(reader, fileNameSourceAttributeC);
@@ -435,14 +509,20 @@ bool CustomWizardParameters::parse(QIODevice &device,
break; break;
case QXmlStreamReader::EndElement: case QXmlStreamReader::EndElement:
state = nextClosingState(state, reader.name()); state = nextClosingState(state, reader.name());
if (state == ParseError) { switch (state) {
case ParseError:
*errorMessage = msgError(reader, configFileFullPath, *errorMessage = msgError(reader, configFileFullPath,
QString::fromLatin1("Unexpected end element %1").arg(reader.name().toString())); QString::fromLatin1("Unexpected end element %1").arg(reader.name().toString()));
return false; return false;
} case ParseWithinFields: // Leaving a field element
if (state == ParseWithinFields) { // Leaving a field element
fields.push_back(field); fields.push_back(field);
field.clear(); field.clear();
break;
case ParseWithinComboEntries:
comboEntryCount++;
break;
default:
break;
} }
break; break;
default: default:

View File

@@ -49,6 +49,10 @@ struct CustomWizardField {
CustomWizardField(); CustomWizardField();
void clear(); void clear();
// Attribute map keys for combo entries
static QString comboEntryValueKey(int i);
static QString comboEntryTextKey(int i);
QString description; QString description;
QString name; QString name;
ControlAttributeMap controlAttributes; ControlAttributeMap controlAttributes;