customwizardpage.cpp 18.5 KB
Newer Older
1
2
3
4
/**************************************************************************
**
** This file is part of Qt Creator
**
hjk's avatar
hjk committed
5
** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
6
**
Eike Ziller's avatar
Eike Ziller committed
7
** Contact: http://www.qt-project.org/
8
9
10
11
**
**
** GNU Lesser General Public License Usage
**
hjk's avatar
hjk committed
12
13
14
15
16
17
** 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.
18
**
con's avatar
con committed
19
** In addition, as a special exception, Nokia gives you certain additional
hjk's avatar
hjk committed
20
** rights. These rights are described in the Nokia Qt LGPL Exception
con's avatar
con committed
21
22
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
23
24
25
26
27
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
28
29
30
31
32
33
34
**
**************************************************************************/

#include "customwizardpage.h"
#include "customwizardparameters.h"

#include <utils/pathchooser.h>
35
#include <utils/qtcassert.h>
36

37
38
39
#include <QRegExp>
#include <QDebug>
#include <QDir>
40
41
#include <QDate>
#include <QTime>
42

43
44
45
46
47
48
49
50
51
#include <QWizardPage>
#include <QFormLayout>
#include <QVBoxLayout>
#include <QLineEdit>
#include <QLabel>
#include <QRegExpValidator>
#include <QComboBox>
#include <QTextEdit>
#include <QSpacerItem>
52
53
54
55
56
57

enum { debug = 0 };

namespace ProjectExplorer {
namespace Internal {

58
// ----------- TextFieldComboBox
59
60
61
62
63
64
65
66
67
68
69
70

/*!
    \class ProjectExplorer::Internal::TextFieldComboBox
    \brief  A non-editable combo for text editing purposes that plays
    with QWizard::registerField (providing a settable 'text' property).

    Allows for a separation of values to be used for wizard fields replacement
    and display texts.

    \sa ProjectExplorer::Internal::CustomWizardFieldPage, ProjectExplorer::CustomWizard
*/

71
72
73
74
TextFieldComboBox::TextFieldComboBox(QWidget *parent) :
    QComboBox(parent)
{
    setEditable(false);
75
76
77
78
79
80
81
    connect(this, SIGNAL(currentIndexChanged(int)),
            this, SLOT(slotCurrentIndexChanged(int)));
}

QString TextFieldComboBox::text() const
{
    return valueAt(currentIndex());
82
83
84
85
}

void TextFieldComboBox::setText(const QString &s)
{
86
    const int index = findData(QVariant(s), Qt::UserRole);
87
88
89
90
    if (index != -1 && index != currentIndex())
        setCurrentIndex(index);
}

91
92
93
94
95
96
97
98
void TextFieldComboBox::slotCurrentIndexChanged(int i)
{
    emit text4Changed(valueAt(i));
}

void TextFieldComboBox::setItems(const QStringList &displayTexts,
                                 const QStringList &values)
{
99
    QTC_ASSERT(displayTexts.size() == values.size(), return);
100
101
102
103
104
105
106
107
108
109
110
111
    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();
}

112
113
114
115
116
117
118
119
120
/*!
    \class ProjectExplorer::Internal::TextFieldCheckBox
    \brief A Checkbox that plays with QWizard::registerField.

    Provides a settable 'text' property containing predefined strings for 'true'/'false').

    \sa ProjectExplorer::Internal::CustomWizardFieldPage, ProjectExplorer::CustomWizard
*/

121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
TextFieldCheckBox::TextFieldCheckBox(const QString &text, QWidget *parent) :
        QCheckBox(text, parent),
        m_trueText(QLatin1String("true")), m_falseText(QLatin1String("false"))
{
    connect(this, SIGNAL(stateChanged(int)), this, SLOT(slotStateChanged(int)));
}

QString TextFieldCheckBox::text() const
{
    return isChecked() ? m_trueText : m_falseText;
}

void TextFieldCheckBox::setText(const QString &s)
{
    setChecked(s == m_trueText);
}

void TextFieldCheckBox::slotStateChanged(int cs)
{
    emit textChanged(cs == Qt::Checked ? m_trueText : m_falseText);
}

143
144
145
146
147
148
149
150
151
152
153
154
155
/*!
    \class ProjectExplorer::Internal::CustomWizardFieldPage
    \brief 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.
    Implements validatePage() as the field logic cannot be tied up
    with additional validation. Performs checking of the Javascript-based
    validation rules of the parameters and displays error messages in a red
    warning label.

    \sa ProjectExplorer::CustomWizard
*/
156
157
158
159
160
161

CustomWizardFieldPage::LineEditData::LineEditData(QLineEdit* le, const QString &defText) :
    lineEdit(le), defaultText(defText)
{
}

162
163
164
165
166
CustomWizardFieldPage::TextEditData::TextEditData(QTextEdit* le, const QString &defText) :
    textEdit(le), defaultText(defText)
{
}

167
168
169
170
171
CustomWizardFieldPage::PathChooserData::PathChooserData(Utils::PathChooser* pe, const QString &defText) :
    pathChooser(pe), defaultText(defText)
{
}

172
CustomWizardFieldPage::CustomWizardFieldPage(const QSharedPointer<CustomWizardContext> &ctx,
173
                                             const QSharedPointer<CustomWizardParameters> &parameters,
174
175
                                             QWidget *parent) :
    QWizardPage(parent),
176
    m_parameters(parameters),
177
    m_context(ctx),
178
179
    m_formLayout(new QFormLayout),
    m_errorLabel(new QLabel)
180
{
181
    QVBoxLayout *vLayout = new QVBoxLayout;
182
    m_formLayout->setFieldGrowthPolicy(QFormLayout::ExpandingFieldsGrow);
183
    if (debug)
184
185
        qDebug() << Q_FUNC_INFO << parameters->fields.size();
    foreach(const CustomWizardField &f, parameters->fields)
186
        addField(f);
187
188
189
190
191
192
    vLayout->addLayout(m_formLayout);
    m_errorLabel->setVisible(false);
    m_errorLabel->setStyleSheet(QLatin1String("background: red"));
    vLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Ignored, QSizePolicy::MinimumExpanding));
    vLayout->addWidget(m_errorLabel);
    setLayout(vLayout);
193
194
}

195
196
197
198
CustomWizardFieldPage::~CustomWizardFieldPage()
{
}

199
200
201
202
203
void CustomWizardFieldPage::addRow(const QString &name, QWidget *w)
{
    m_formLayout->addRow(name, w);
}

204
205
206
207
208
209
210
211
212
213
214
215
void CustomWizardFieldPage::showError(const QString &m)
{
    m_errorLabel->setText(m);
    m_errorLabel->setVisible(true);
}

void CustomWizardFieldPage::clearError()
{
    m_errorLabel->setText(QString());
    m_errorLabel->setVisible(false);
}

216
217
218
219
220
/*!
    \brief Create widget a control based  on the control attributes map
    and register it with the QWizard.
*/

221
void CustomWizardFieldPage::addField(const CustomWizardField &field)\
222
{
223
    //  Register field, indicate mandatory by '*' (only when registering)
224
225
226
    QString fieldName = field.name;
    if (field.mandatory)
        fieldName += QLatin1Char('*');
227
    bool spansRow = false;
228
229
    // Check known classes: QComboBox
    const QString className = field.controlAttributes.value(QLatin1String("class"));
230
    QWidget *fieldWidget = 0;
231
    if (className == QLatin1String("QComboBox")) {
232
        fieldWidget = registerComboBox(fieldName, field);
233
234
235
236
    } else if (className == QLatin1String("QTextEdit")) {
        fieldWidget = registerTextEdit(fieldName, field);
    } else if (className == QLatin1String("Utils::PathChooser")) {
        fieldWidget = registerPathChooser(fieldName, field);
237
238
239
    } else if (className == QLatin1String("QCheckBox")) {
        fieldWidget = registerCheckBox(fieldName, field.description, field);
        spansRow = true; // Do not create a label for the checkbox.
240
241
242
    } else {
        fieldWidget = registerLineEdit(fieldName, field);
    }
243
244
245
246
247
    if (spansRow) {
        m_formLayout->addRow(fieldWidget);
    } else {
        addRow(field.description, fieldWidget);
    }
248
249
}

250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
// 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));
    }
}

280
281
282
283
284
QWidget *CustomWizardFieldPage::registerComboBox(const QString &fieldName,
                                                 const CustomWizardField &field)
{
    TextFieldComboBox *combo = new TextFieldComboBox;
    do { // Set up items and current index
285
286
287
288
        QStringList values;
        QStringList displayTexts;
        comboChoices(field.controlAttributes, &values, &displayTexts);
        combo->setItems(displayTexts, values);
289
290
291
292
293
294
295
296
297
298
299
300
301
        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

302
303
304
305
306
307
308
309
310
311
312
QWidget *CustomWizardFieldPage::registerTextEdit(const QString &fieldName,
                                                 const CustomWizardField &field)
{
    QTextEdit *textEdit = new QTextEdit;
    registerField(fieldName, textEdit, "plainText", SIGNAL(textChanged(QString)));
    const QString defaultText = field.controlAttributes.value(QLatin1String("defaulttext"));
    m_textEdits.push_back(TextEditData(textEdit, defaultText));
    return textEdit;
} // QTextEdit

QWidget *CustomWizardFieldPage::registerPathChooser(const QString &fieldName,
313
                                                 const CustomWizardField &field)
314
315
316
{
    Utils::PathChooser *pathChooser = new Utils::PathChooser;
    registerField(fieldName, pathChooser, "path", SIGNAL(changed(QString)));
317
318
    const QString defaultText = field.controlAttributes.value(QLatin1String("defaulttext"));
    m_pathChoosers.push_back(PathChooserData(pathChooser, defaultText));
319
320
321
    return pathChooser;
} // Utils::PathChooser

322
323
324
325
326
327
328
QWidget *CustomWizardFieldPage::registerCheckBox(const QString &fieldName,
                                                 const QString &fieldDescription,
                                                 const CustomWizardField &field)
{
    typedef CustomWizardField::ControlAttributeMap::const_iterator AttributeMapConstIt;

    TextFieldCheckBox *checkBox = new TextFieldCheckBox(fieldDescription);
329
330
    const bool defaultValue = field.controlAttributes.value(QLatin1String("defaultvalue")) == QLatin1String("true");
    checkBox->setChecked(defaultValue);
331
332
333
334
335
336
337
338
339
340
    const AttributeMapConstIt trueTextIt = field.controlAttributes.constFind(QLatin1String("truevalue"));
    if (trueTextIt != field.controlAttributes.constEnd()) // Also set empty texts
        checkBox->setTrueText(trueTextIt.value());
    const AttributeMapConstIt falseTextIt = field.controlAttributes.constFind(QLatin1String("falsevalue"));
    if (falseTextIt != field.controlAttributes.constEnd()) // Also set empty texts
        checkBox->setFalseText(falseTextIt.value());
    registerField(fieldName, checkBox, "text", SIGNAL(textChanged(QString)));
    return checkBox;
}

341
342
343
QWidget *CustomWizardFieldPage::registerLineEdit(const QString &fieldName,
                                                 const CustomWizardField &field)
{
344
    QLineEdit *lineEdit = new QLineEdit;
345

346
347
348
349
350
351
352
353
354
355
    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));
        }
    }
    registerField(fieldName, lineEdit, "text", SIGNAL(textEdited(QString)));
356
357
358

    const QString defaultText = field.controlAttributes.value(QLatin1String("defaulttext"));
    m_lineEdits.push_back(LineEditData(lineEdit, defaultText));
359
360
361
    return lineEdit;
}

362
void CustomWizardFieldPage::initializePage()
363
{
364
    QWizardPage::initializePage();
365
    clearError();
366
    foreach(const LineEditData &led, m_lineEdits) {
367
368
369
        if (!led.userChange.isNull()) {
            led.lineEdit->setText(led.userChange);
        } else if (!led.defaultText.isEmpty()) {
370
371
372
373
374
            QString defaultText = led.defaultText;
            CustomWizardContext::replaceFields(m_context->baseReplacements, &defaultText);
            led.lineEdit->setText(defaultText);
        }
    }
375
    foreach(const TextEditData &ted, m_textEdits) {
376
377
378
        if (!ted.userChange.isNull()) {
            ted.textEdit->setText(ted.userChange);
        } else if (!ted.defaultText.isEmpty()) {
379
380
381
382
383
            QString defaultText = ted.defaultText;
            CustomWizardContext::replaceFields(m_context->baseReplacements, &defaultText);
            ted.textEdit->setText(defaultText);
        }
    }
384
385
386
387
388
389
390
391
392
    foreach (const PathChooserData &ped, m_pathChoosers) {
        if (!ped.userChange.isNull()) {
            ped.pathChooser->setPath(ped.userChange);
        } else if (!ped.defaultText.isEmpty()) {
            QString defaultText = ped.defaultText;
            CustomWizardContext::replaceFields(m_context->baseReplacements, &defaultText);
            ped.pathChooser->setPath(defaultText);
        }
    }
393
394
}

395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
void CustomWizardFieldPage::cleanupPage()
{
    for (int i = 0; i < m_lineEdits.count(); ++i) {
        LineEditData &led = m_lineEdits[i];
        QString defaultText = led.defaultText;
        CustomWizardContext::replaceFields(m_context->baseReplacements, &defaultText);
        if (led.lineEdit->text() != defaultText)
            led.userChange = led.lineEdit->text();
        else
            led.userChange.clear();

    }
    for (int i= 0; i < m_textEdits.count(); ++i) {
        TextEditData &ted = m_textEdits[i];
        QString defaultText = ted.defaultText;
        CustomWizardContext::replaceFields(m_context->baseReplacements, &defaultText);
        if (ted.textEdit->toHtml() != ted.defaultText && ted.textEdit->toPlainText() != ted.defaultText)
            ted.userChange = ted.textEdit->toHtml();
        else
            ted.userChange.clear();
    }
416
417
418
419
420
421
422
423
424
    for (int i= 0; i < m_pathChoosers.count(); ++i) {
        PathChooserData &ped = m_pathChoosers[i];
        QString defaultText = ped.defaultText;
        CustomWizardContext::replaceFields(m_context->baseReplacements, &defaultText);
        if (ped.pathChooser->path() != ped.defaultText)
            ped.userChange = ped.pathChooser->path();
        else
            ped.userChange.clear();
    }
425
426
427
    QWizardPage::cleanupPage();
}

428
429
bool CustomWizardFieldPage::validatePage()
{
430
    clearError();
431
    // Check line edits with validators
432
433
434
435
436
437
438
439
    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;
            }
440
441
        }
    }
442
443
444
445
446
447
448
449
450
451
    // Any user validation rules -> Check all and display messages with
    // place holders applied.
    if (!m_parameters->rules.isEmpty()) {
        const QMap<QString, QString> values = replacementMap(wizard(), m_context, m_parameters->fields);
        QString message;
        if (!CustomWizardValidationRule::validateRules(m_parameters->rules, values, &message)) {
            showError(message);
            return false;
        }
    }
452
    return QWizardPage::validatePage();
453
454
}

455
456
457
458
459
460
461
462
463
464
465
466
QMap<QString, QString> CustomWizardFieldPage::replacementMap(const QWizard *w,
                                                             const QSharedPointer<CustomWizardContext> &ctx,
                                                             const FieldList &f)
{
    QMap<QString, QString> fieldReplacementMap = ctx->baseReplacements;
    foreach(const Internal::CustomWizardField &field, f) {
        const QString value = w->field(field.name).toString();
        fieldReplacementMap.insert(field.name, value);
    }
    // Insert paths for generator scripts.
    fieldReplacementMap.insert(QLatin1String("Path"), QDir::toNativeSeparators(ctx->path));
    fieldReplacementMap.insert(QLatin1String("TargetPath"), QDir::toNativeSeparators(ctx->targetPath));
467
468
469
470
471
472
473

    // Insert additional pre-defined variables
    fieldReplacementMap.insert(QLatin1String("CurrentDate"),
                               QDate::currentDate().toString(QLatin1String("yyyy-MM-dd")));
    fieldReplacementMap.insert(QLatin1String("CurrentTime"),
                               QTime::currentTime().toString(QLocale::system().
                                                             timeFormat(QLocale::ShortFormat)));
474
475
476
    return fieldReplacementMap;
}

477
478
479
480
481
482
483
484
485
/*!
    \class ProjectExplorer::Internal::CustomWizardPage
    \brief 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).

    \sa ProjectExplorer::CustomWizard
*/
486

487
CustomWizardPage::CustomWizardPage(const QSharedPointer<CustomWizardContext> &ctx,
488
                                   const QSharedPointer<CustomWizardParameters> &parameters,
489
                                   QWidget *parent) :
490
    CustomWizardFieldPage(ctx, parameters, parent),
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
    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