1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (info@qt.nokia.com)
10 ** GNU Lesser General Public License Usage
12 ** This file may be used under the terms of the GNU Lesser General Public
13 ** License version 2.1 as published by the Free Software Foundation and
14 ** appearing in the file LICENSE.LGPL included in the packaging of this file.
15 ** Please review the following information to ensure the GNU Lesser General
16 ** Public License version 2.1 requirements will be met:
17 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
19 ** In addition, as a special exception, Nokia gives you certain additional
20 ** rights. These rights are described in the Nokia Qt LGPL Exception
21 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
25 ** Alternatively, this file may be used in accordance with the terms and
26 ** conditions contained in a signed written agreement between you and Nokia.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
31 **************************************************************************/
33 #include "newclasswidget.h"
34 #include "ui_newclasswidget.h"
36 #include <utils/filewizardpage.h>
38 #include <QtGui/QFileDialog>
39 #include <QtCore/QFileInfo>
40 #include <QtCore/QStringList>
41 #include <QtCore/QDir>
42 #include <QtCore/QDebug>
43 #include <QtCore/QRegExp>
45 enum { debugNewClassWidget = 0 };
47 /*! \class Utils::NewClassWidget
49 \brief Utility widget for 'New Class' wizards
51 Utility widget for 'New Class' wizards. Prompts the user
52 to enter a class name (optionally derived from some base class) and file
53 names for header, source and form files. Has some smart logic to derive
54 the file names from the class name. */
58 struct NewClassWidgetPrivate {
59 NewClassWidgetPrivate();
61 Ui::NewClassWidget m_ui;
62 QString m_headerExtension;
63 QString m_sourceExtension;
64 QString m_formExtension;
67 // Store the "visible" values to prevent the READ accessors from being
68 // fooled by a temporarily hidden widget
69 bool m_baseClassInputVisible;
70 bool m_formInputVisible;
71 bool m_pathInputVisible;
72 bool m_qobjectCheckBoxVisible;
73 bool m_formInputCheckable;
76 NewClassWidgetPrivate:: NewClassWidgetPrivate() :
77 m_headerExtension(QLatin1String("h")),
78 m_sourceExtension(QLatin1String("cpp")),
79 m_formExtension(QLatin1String("ui")),
82 m_baseClassInputVisible(true),
83 m_formInputVisible(true),
84 m_pathInputVisible(true),
85 m_qobjectCheckBoxVisible(false),
86 m_formInputCheckable(false)
91 // --------------------- NewClassWidget
92 NewClassWidget::NewClassWidget(QWidget *parent) :
94 m_d(new NewClassWidgetPrivate)
96 m_d->m_ui.setupUi(this);
98 m_d->m_ui.baseClassComboBox->setEditable(false);
100 connect(m_d->m_ui.classLineEdit, SIGNAL(updateFileName(QString)),
101 this, SLOT(slotUpdateFileNames(QString)));
102 connect(m_d->m_ui.classLineEdit, SIGNAL(textEdited(QString)),
103 this, SLOT(classNameEdited()));
104 connect(m_d->m_ui.baseClassComboBox, SIGNAL(currentIndexChanged(int)),
105 this, SLOT(suggestClassNameFromBase()));
106 connect(m_d->m_ui.baseClassComboBox, SIGNAL(editTextChanged(QString)),
107 this, SLOT(slotValidChanged()));
108 connect(m_d->m_ui.classLineEdit, SIGNAL(validChanged()),
109 this, SLOT(slotValidChanged()));
110 connect(m_d->m_ui.headerFileLineEdit, SIGNAL(validChanged()),
111 this, SLOT(slotValidChanged()));
112 connect(m_d->m_ui.sourceFileLineEdit, SIGNAL(validChanged()),
113 this, SLOT(slotValidChanged()));
114 connect(m_d->m_ui.formFileLineEdit, SIGNAL(validChanged()),
115 this, SLOT(slotValidChanged()));
116 connect(m_d->m_ui.pathChooser, SIGNAL(validChanged()),
117 this, SLOT(slotValidChanged()));
118 connect(m_d->m_ui.generateFormCheckBox, SIGNAL(toggled(bool)),
119 this, SLOT(slotValidChanged()));
121 connect(m_d->m_ui.classLineEdit, SIGNAL(validReturnPressed()),
122 this, SLOT(slotActivated()));
123 connect(m_d->m_ui.headerFileLineEdit, SIGNAL(validReturnPressed()),
124 this, SLOT(slotActivated()));
125 connect(m_d->m_ui.sourceFileLineEdit, SIGNAL(validReturnPressed()),
126 this, SLOT(slotActivated()));
127 connect(m_d->m_ui.formFileLineEdit, SIGNAL(validReturnPressed()),
128 this, SLOT(slotActivated()));
129 connect(m_d->m_ui.formFileLineEdit, SIGNAL(validReturnPressed()),
130 this, SLOT(slotActivated()));
131 connect(m_d->m_ui.pathChooser, SIGNAL(returnPressed()),
132 this, SLOT(slotActivated()));
134 connect(m_d->m_ui.generateFormCheckBox, SIGNAL(stateChanged(int)),
135 this, SLOT(slotFormInputChecked()));
136 connect(m_d->m_ui.baseClassComboBox, SIGNAL(editTextChanged(QString)),
137 this, SLOT(slotBaseClassEdited(QString)));
138 m_d->m_ui.generateFormCheckBox->setChecked(true);
139 setFormInputCheckable(false, true);
140 setClassType(NoClassType);
143 NewClassWidget::~NewClassWidget()
148 void NewClassWidget::classNameEdited()
150 if (debugNewClassWidget)
151 qDebug() << Q_FUNC_INFO << m_d->m_headerExtension << m_d->m_sourceExtension;
152 m_d->m_classEdited = true;
155 void NewClassWidget::suggestClassNameFromBase()
157 if (debugNewClassWidget)
158 qDebug() << Q_FUNC_INFO << m_d->m_headerExtension << m_d->m_sourceExtension;
159 if (m_d->m_classEdited)
161 // Suggest a class unless edited ("QMainWindow"->"MainWindow")
162 QString base = baseClassName();
163 if (base.startsWith(QLatin1Char('Q'))) {
169 QStringList NewClassWidget::baseClassChoices() const
172 const int count = m_d->m_ui.baseClassComboBox->count();
173 for (int i = 0; i < count; i++)
174 rc.push_back(m_d->m_ui.baseClassComboBox->itemText(i));
178 void NewClassWidget::setBaseClassChoices(const QStringList &choices)
180 m_d->m_ui.baseClassComboBox->clear();
181 m_d->m_ui.baseClassComboBox->addItems(choices);
184 void NewClassWidget::setBaseClassInputVisible(bool visible)
186 m_d->m_baseClassInputVisible = visible;
187 m_d->m_ui.baseClassLabel->setVisible(visible);
188 m_d->m_ui.baseClassComboBox->setVisible(visible);
191 void NewClassWidget::setBaseClassEditable(bool editable)
193 m_d->m_ui.baseClassComboBox->setEditable(editable);
196 bool NewClassWidget::isBaseClassInputVisible() const
198 return m_d->m_baseClassInputVisible;
201 bool NewClassWidget::isBaseClassEditable() const
203 return m_d->m_ui.baseClassComboBox->isEditable();
206 void NewClassWidget::setFormInputVisible(bool visible)
208 m_d->m_formInputVisible = visible;
209 m_d->m_ui.formLabel->setVisible(visible);
210 m_d->m_ui.formFileLineEdit->setVisible(visible);
213 bool NewClassWidget::isFormInputVisible() const
215 return m_d->m_formInputVisible;
218 void NewClassWidget::setFormInputCheckable(bool checkable)
220 setFormInputCheckable(checkable, false);
223 void NewClassWidget::setFormInputCheckable(bool checkable, bool force)
225 if (!force && checkable == m_d->m_formInputCheckable)
227 m_d->m_formInputCheckable = checkable;
228 m_d->m_ui.generateFormLabel->setVisible(checkable);
229 m_d->m_ui.generateFormCheckBox->setVisible(checkable);
232 void NewClassWidget::setFormInputChecked(bool v)
234 m_d->m_ui.generateFormCheckBox->setChecked(v);
237 bool NewClassWidget::formInputCheckable() const
239 return m_d->m_formInputCheckable;
242 bool NewClassWidget::formInputChecked() const
244 return m_d->m_ui.generateFormCheckBox->isChecked();
247 void NewClassWidget::slotFormInputChecked()
249 const bool checked = formInputChecked();
250 m_d->m_ui.formLabel->setEnabled(checked);
251 m_d->m_ui.formFileLineEdit->setEnabled(checked);
254 void NewClassWidget::setPathInputVisible(bool visible)
256 m_d->m_pathInputVisible = visible;
257 m_d->m_ui.pathLabel->setVisible(visible);
258 m_d->m_ui.pathChooser->setVisible(visible);
261 bool NewClassWidget::isPathInputVisible() const
263 return m_d->m_pathInputVisible;
266 void NewClassWidget::setClassName(const QString &suggestedName)
268 if (debugNewClassWidget)
269 qDebug() << Q_FUNC_INFO << suggestedName << m_d->m_headerExtension << m_d->m_sourceExtension;
270 m_d->m_ui.classLineEdit->setText(ClassNameValidatingLineEdit::createClassName(suggestedName));
273 QString NewClassWidget::className() const
275 return m_d->m_ui.classLineEdit->text();
278 QString NewClassWidget::baseClassName() const
280 return m_d->m_ui.baseClassComboBox->currentText();
283 void NewClassWidget::setBaseClassName(const QString &c)
285 const int index = m_d->m_ui.baseClassComboBox->findText(c);
287 m_d->m_ui.baseClassComboBox->setCurrentIndex(index);
288 suggestClassNameFromBase();
292 QString NewClassWidget::sourceFileName() const
294 return m_d->m_ui.sourceFileLineEdit->text();
297 QString NewClassWidget::headerFileName() const
299 return m_d->m_ui.headerFileLineEdit->text();
302 QString NewClassWidget::formFileName() const
304 return m_d->m_ui.formFileLineEdit->text();
307 QString NewClassWidget::path() const
309 return m_d->m_ui.pathChooser->path();
312 void NewClassWidget::setPath(const QString &path)
314 m_d->m_ui.pathChooser->setPath(path);
317 bool NewClassWidget::namespacesEnabled() const
319 return m_d->m_ui.classLineEdit->namespacesEnabled();
322 void NewClassWidget::setNamespacesEnabled(bool b)
324 m_d->m_ui.classLineEdit->setNamespacesEnabled(b);
327 QString NewClassWidget::sourceExtension() const
329 return m_d->m_sourceExtension;
332 void NewClassWidget::setSourceExtension(const QString &e)
334 if (debugNewClassWidget)
335 qDebug() << Q_FUNC_INFO << e;
336 m_d->m_sourceExtension = fixSuffix(e);
339 QString NewClassWidget::headerExtension() const
341 return m_d->m_headerExtension;
344 void NewClassWidget::setHeaderExtension(const QString &e)
346 if (debugNewClassWidget)
347 qDebug() << Q_FUNC_INFO << e;
348 m_d->m_headerExtension = fixSuffix(e);
351 QString NewClassWidget::formExtension() const
353 return m_d->m_formExtension;
356 void NewClassWidget::setFormExtension(const QString &e)
358 if (debugNewClassWidget)
359 qDebug() << Q_FUNC_INFO << e;
360 m_d->m_formExtension = fixSuffix(e);
363 bool NewClassWidget::allowDirectories() const
365 return m_d->m_ui.headerFileLineEdit->allowDirectories();
368 void NewClassWidget::setAllowDirectories(bool v)
370 // We keep all in sync
371 if (allowDirectories() != v) {
372 m_d->m_ui.sourceFileLineEdit->setAllowDirectories(v);
373 m_d->m_ui.headerFileLineEdit->setAllowDirectories(v);
374 m_d->m_ui.formFileLineEdit->setAllowDirectories(v);
378 bool NewClassWidget::lowerCaseFiles() const
380 return m_d->m_ui.classLineEdit->lowerCaseFileName();
383 void NewClassWidget::setLowerCaseFiles(bool v)
385 m_d->m_ui.classLineEdit->setLowerCaseFileName(v);
388 NewClassWidget::ClassType NewClassWidget::classType() const
390 return static_cast<ClassType>(m_d->m_ui.classTypeComboBox->currentIndex());
393 void NewClassWidget::setClassType(ClassType ct)
395 m_d->m_ui.classTypeComboBox->setCurrentIndex(ct);
398 bool NewClassWidget::isClassTypeComboVisible() const
400 return m_d->m_ui.classTypeLabel->isVisible();
403 void NewClassWidget::setClassTypeComboVisible(bool v)
405 m_d->m_ui.classTypeLabel->setVisible(v);
406 m_d->m_ui.classTypeComboBox->setVisible(v);
409 // Guess suitable type information with some smartness
410 static inline NewClassWidget::ClassType classTypeForBaseClass(const QString &baseClass)
412 if (!baseClass.startsWith(QLatin1Char('Q')))
413 return NewClassWidget::NoClassType;
414 // QObject types: QObject as such and models.
415 if (baseClass == QLatin1String("QObject") ||
416 (baseClass.startsWith(QLatin1String("QAbstract")) && baseClass.endsWith(QLatin1String("Model"))))
417 return NewClassWidget::ClassInheritsQObject;
419 if (baseClass == QLatin1String("QWidget") || baseClass == QLatin1String("QMainWindow")
420 || baseClass == QLatin1String("QDialog"))
421 return NewClassWidget::ClassInheritsQWidget;
423 if (baseClass == QLatin1String("QDeclarativeItem"))
424 return NewClassWidget::ClassInheritsQDeclarativeItem;
425 return NewClassWidget::NoClassType;
428 void NewClassWidget::slotBaseClassEdited(const QString &baseClass)
430 // Set type information with some smartness.
431 const ClassType currentClassType = classType();
432 const ClassType recommendedClassType = classTypeForBaseClass(baseClass);
433 if (recommendedClassType != NoClassType && currentClassType != recommendedClassType)
434 setClassType(recommendedClassType);
437 void NewClassWidget::slotValidChanged()
439 const bool newValid = isValid();
440 if (newValid != m_d->m_valid) {
441 m_d->m_valid = newValid;
446 bool NewClassWidget::isValid(QString *error) const
448 if (!m_d->m_ui.classLineEdit->isValid()) {
450 *error = m_d->m_ui.classLineEdit->errorMessage();
454 if (isBaseClassInputVisible() && isBaseClassEditable()) {
455 // TODO: Should this be a ClassNameValidatingComboBox?
456 QRegExp classNameValidator(QLatin1String("[a-zA-Z_][a-zA-Z0-9_]*(::[a-zA-Z_][a-zA-Z0-9_]*)*"));
457 const QString baseClass = m_d->m_ui.baseClassComboBox->currentText().trimmed();
458 if (!baseClass.isEmpty() && !classNameValidator.exactMatch(baseClass)) {
460 *error = tr("Invalid base class name");
465 if (!m_d->m_ui.headerFileLineEdit->isValid()) {
467 *error = tr("Invalid header file name: '%1'").arg(m_d->m_ui.headerFileLineEdit->errorMessage());
471 if (!m_d->m_ui.sourceFileLineEdit->isValid()) {
473 *error = tr("Invalid source file name: '%1'").arg(m_d->m_ui.sourceFileLineEdit->errorMessage());
477 if (isFormInputVisible() &&
478 (!m_d->m_formInputCheckable || m_d->m_ui.generateFormCheckBox->isChecked())) {
479 if (!m_d->m_ui.formFileLineEdit->isValid()) {
481 *error = tr("Invalid form file name: '%1'").arg(m_d->m_ui.formFileLineEdit->errorMessage());
486 if (isPathInputVisible()) {
487 if (!m_d->m_ui.pathChooser->isValid()) {
489 *error = m_d->m_ui.pathChooser->errorMessage();
496 void NewClassWidget::triggerUpdateFileNames()
498 m_d->m_ui.classLineEdit->triggerChanged();
501 void NewClassWidget::slotUpdateFileNames(const QString &baseName)
503 if (debugNewClassWidget)
504 qDebug() << Q_FUNC_INFO << baseName << m_d->m_headerExtension << m_d->m_sourceExtension;
505 const QChar dot = QLatin1Char('.');
506 m_d->m_ui.sourceFileLineEdit->setText(baseName + dot + m_d->m_sourceExtension);
507 m_d->m_ui.headerFileLineEdit->setText(baseName + dot + m_d->m_headerExtension);
508 m_d->m_ui.formFileLineEdit->setText(baseName + dot + m_d->m_formExtension);
511 void NewClassWidget::slotActivated()
517 QString NewClassWidget::fixSuffix(const QString &suffix)
520 if (s.startsWith(QLatin1Char('.')))
525 // Utility to add a suffix to a file unless the user specified one
526 static QString ensureSuffix(QString f, const QString &extension)
528 const QChar dot = QLatin1Char('.');
536 // If a non-empty name was passed, expand to directory and suffix
537 static QString expandFileName(const QDir &dir, const QString name, const QString &extension)
541 return dir.absoluteFilePath(ensureSuffix(name, extension));
544 QStringList NewClassWidget::files() const
547 const QDir dir = QDir(path());
548 rc.push_back(expandFileName(dir, headerFileName(), headerExtension()));
549 rc.push_back(expandFileName(dir, sourceFileName(), sourceExtension()));
550 if (isFormInputVisible())
551 rc.push_back(expandFileName(dir, formFileName(), formExtension()));