1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
16 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Nokia gives you certain additional
26 ** rights. These rights are described in the Nokia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
32 **************************************************************************/
34 #include "newclasswidget.h"
35 #include "ui_newclasswidget.h"
37 #include <utils/filewizardpage.h>
39 #include <QtGui/QFileDialog>
40 #include <QtCore/QFileInfo>
41 #include <QtCore/QStringList>
42 #include <QtCore/QDir>
43 #include <QtCore/QDebug>
44 #include <QtCore/QRegExp>
46 enum { debugNewClassWidget = 0 };
50 struct NewClassWidgetPrivate {
51 NewClassWidgetPrivate();
53 Ui::NewClassWidget m_ui;
54 QString m_headerExtension;
55 QString m_sourceExtension;
56 QString m_formExtension;
59 // Store the "visible" values to prevent the READ accessors from being
60 // fooled by a temporarily hidden widget
61 bool m_baseClassInputVisible;
62 bool m_formInputVisible;
63 bool m_pathInputVisible;
64 bool m_qobjectCheckBoxVisible;
65 bool m_formInputCheckable;
68 NewClassWidgetPrivate:: NewClassWidgetPrivate() :
69 m_headerExtension(QLatin1String("h")),
70 m_sourceExtension(QLatin1String("cpp")),
71 m_formExtension(QLatin1String("ui")),
74 m_baseClassInputVisible(true),
75 m_formInputVisible(true),
76 m_pathInputVisible(true),
77 m_qobjectCheckBoxVisible(false),
78 m_formInputCheckable(false)
83 // --------------------- NewClassWidget
84 NewClassWidget::NewClassWidget(QWidget *parent) :
86 m_d(new NewClassWidgetPrivate)
88 m_d->m_ui.setupUi(this);
90 m_d->m_ui.baseClassComboBox->setEditable(false);
92 connect(m_d->m_ui.classLineEdit, SIGNAL(updateFileName(QString)),
93 this, SLOT(slotUpdateFileNames(QString)));
94 connect(m_d->m_ui.classLineEdit, SIGNAL(textEdited(QString)),
95 this, SLOT(classNameEdited()));
96 connect(m_d->m_ui.baseClassComboBox, SIGNAL(currentIndexChanged(int)),
97 this, SLOT(suggestClassNameFromBase()));
98 connect(m_d->m_ui.baseClassComboBox, SIGNAL(editTextChanged(QString)),
99 this, SLOT(slotValidChanged()));
100 connect(m_d->m_ui.classLineEdit, SIGNAL(validChanged()),
101 this, SLOT(slotValidChanged()));
102 connect(m_d->m_ui.headerFileLineEdit, SIGNAL(validChanged()),
103 this, SLOT(slotValidChanged()));
104 connect(m_d->m_ui.sourceFileLineEdit, SIGNAL(validChanged()),
105 this, SLOT(slotValidChanged()));
106 connect(m_d->m_ui.formFileLineEdit, SIGNAL(validChanged()),
107 this, SLOT(slotValidChanged()));
108 connect(m_d->m_ui.pathChooser, SIGNAL(validChanged()),
109 this, SLOT(slotValidChanged()));
110 connect(m_d->m_ui.generateFormCheckBox, SIGNAL(toggled(bool)),
111 this, SLOT(slotValidChanged()));
113 connect(m_d->m_ui.classLineEdit, SIGNAL(validReturnPressed()),
114 this, SLOT(slotActivated()));
115 connect(m_d->m_ui.headerFileLineEdit, SIGNAL(validReturnPressed()),
116 this, SLOT(slotActivated()));
117 connect(m_d->m_ui.sourceFileLineEdit, SIGNAL(validReturnPressed()),
118 this, SLOT(slotActivated()));
119 connect(m_d->m_ui.formFileLineEdit, SIGNAL(validReturnPressed()),
120 this, SLOT(slotActivated()));
121 connect(m_d->m_ui.formFileLineEdit, SIGNAL(validReturnPressed()),
122 this, SLOT(slotActivated()));
123 connect(m_d->m_ui.pathChooser, SIGNAL(returnPressed()),
124 this, SLOT(slotActivated()));
126 connect(m_d->m_ui.generateFormCheckBox, SIGNAL(stateChanged(int)),
127 this, SLOT(slotFormInputChecked()));
128 connect(m_d->m_ui.baseClassComboBox, SIGNAL(editTextChanged(QString)),
129 this, SLOT(slotBaseClassEdited(QString)));
130 m_d->m_ui.generateFormCheckBox->setChecked(true);
131 setFormInputCheckable(false, true);
132 setClassType(NoClassType);
135 NewClassWidget::~NewClassWidget()
140 void NewClassWidget::classNameEdited()
142 if (debugNewClassWidget)
143 qDebug() << Q_FUNC_INFO << m_d->m_headerExtension << m_d->m_sourceExtension;
144 m_d->m_classEdited = true;
147 void NewClassWidget::suggestClassNameFromBase()
149 if (debugNewClassWidget)
150 qDebug() << Q_FUNC_INFO << m_d->m_headerExtension << m_d->m_sourceExtension;
151 if (m_d->m_classEdited)
153 // Suggest a class unless edited ("QMainWindow"->"MainWindow")
154 QString base = baseClassName();
155 if (base.startsWith(QLatin1Char('Q'))) {
161 QStringList NewClassWidget::baseClassChoices() const
164 const int count = m_d->m_ui.baseClassComboBox->count();
165 for (int i = 0; i < count; i++)
166 rc.push_back(m_d->m_ui.baseClassComboBox->itemText(i));
170 void NewClassWidget::setBaseClassChoices(const QStringList &choices)
172 m_d->m_ui.baseClassComboBox->clear();
173 m_d->m_ui.baseClassComboBox->addItems(choices);
176 void NewClassWidget::setBaseClassInputVisible(bool visible)
178 m_d->m_baseClassInputVisible = visible;
179 m_d->m_ui.baseClassLabel->setVisible(visible);
180 m_d->m_ui.baseClassComboBox->setVisible(visible);
183 void NewClassWidget::setBaseClassEditable(bool editable)
185 m_d->m_ui.baseClassComboBox->setEditable(editable);
188 bool NewClassWidget::isBaseClassInputVisible() const
190 return m_d->m_baseClassInputVisible;
193 bool NewClassWidget::isBaseClassEditable() const
195 return m_d->m_ui.baseClassComboBox->isEditable();
198 void NewClassWidget::setFormInputVisible(bool visible)
200 m_d->m_formInputVisible = visible;
201 m_d->m_ui.formLabel->setVisible(visible);
202 m_d->m_ui.formFileLineEdit->setVisible(visible);
205 bool NewClassWidget::isFormInputVisible() const
207 return m_d->m_formInputVisible;
210 void NewClassWidget::setFormInputCheckable(bool checkable)
212 setFormInputCheckable(checkable, false);
215 void NewClassWidget::setFormInputCheckable(bool checkable, bool force)
217 if (!force && checkable == m_d->m_formInputCheckable)
219 m_d->m_formInputCheckable = checkable;
220 m_d->m_ui.generateFormLabel->setVisible(checkable);
221 m_d->m_ui.generateFormCheckBox->setVisible(checkable);
224 void NewClassWidget::setFormInputChecked(bool v)
226 m_d->m_ui.generateFormCheckBox->setChecked(v);
229 bool NewClassWidget::formInputCheckable() const
231 return m_d->m_formInputCheckable;
234 bool NewClassWidget::formInputChecked() const
236 return m_d->m_ui.generateFormCheckBox->isChecked();
239 void NewClassWidget::slotFormInputChecked()
241 const bool checked = formInputChecked();
242 m_d->m_ui.formLabel->setEnabled(checked);
243 m_d->m_ui.formFileLineEdit->setEnabled(checked);
246 void NewClassWidget::setPathInputVisible(bool visible)
248 m_d->m_pathInputVisible = visible;
249 m_d->m_ui.pathLabel->setVisible(visible);
250 m_d->m_ui.pathChooser->setVisible(visible);
253 bool NewClassWidget::isPathInputVisible() const
255 return m_d->m_pathInputVisible;
258 void NewClassWidget::setClassName(const QString &suggestedName)
260 if (debugNewClassWidget)
261 qDebug() << Q_FUNC_INFO << suggestedName << m_d->m_headerExtension << m_d->m_sourceExtension;
262 m_d->m_ui.classLineEdit->setText(ClassNameValidatingLineEdit::createClassName(suggestedName));
265 QString NewClassWidget::className() const
267 return m_d->m_ui.classLineEdit->text();
270 QString NewClassWidget::baseClassName() const
272 return m_d->m_ui.baseClassComboBox->currentText();
275 void NewClassWidget::setBaseClassName(const QString &c)
277 const int index = m_d->m_ui.baseClassComboBox->findText(c);
279 m_d->m_ui.baseClassComboBox->setCurrentIndex(index);
280 suggestClassNameFromBase();
284 QString NewClassWidget::sourceFileName() const
286 return m_d->m_ui.sourceFileLineEdit->text();
289 QString NewClassWidget::headerFileName() const
291 return m_d->m_ui.headerFileLineEdit->text();
294 QString NewClassWidget::formFileName() const
296 return m_d->m_ui.formFileLineEdit->text();
299 QString NewClassWidget::path() const
301 return m_d->m_ui.pathChooser->path();
304 void NewClassWidget::setPath(const QString &path)
306 m_d->m_ui.pathChooser->setPath(path);
309 bool NewClassWidget::namespacesEnabled() const
311 return m_d->m_ui.classLineEdit->namespacesEnabled();
314 void NewClassWidget::setNamespacesEnabled(bool b)
316 m_d->m_ui.classLineEdit->setNamespacesEnabled(b);
319 QString NewClassWidget::sourceExtension() const
321 return m_d->m_sourceExtension;
324 void NewClassWidget::setSourceExtension(const QString &e)
326 if (debugNewClassWidget)
327 qDebug() << Q_FUNC_INFO << e;
328 m_d->m_sourceExtension = fixSuffix(e);
331 QString NewClassWidget::headerExtension() const
333 return m_d->m_headerExtension;
336 void NewClassWidget::setHeaderExtension(const QString &e)
338 if (debugNewClassWidget)
339 qDebug() << Q_FUNC_INFO << e;
340 m_d->m_headerExtension = fixSuffix(e);
343 QString NewClassWidget::formExtension() const
345 return m_d->m_formExtension;
348 void NewClassWidget::setFormExtension(const QString &e)
350 if (debugNewClassWidget)
351 qDebug() << Q_FUNC_INFO << e;
352 m_d->m_formExtension = fixSuffix(e);
355 bool NewClassWidget::allowDirectories() const
357 return m_d->m_ui.headerFileLineEdit->allowDirectories();
360 void NewClassWidget::setAllowDirectories(bool v)
362 // We keep all in sync
363 if (allowDirectories() != v) {
364 m_d->m_ui.sourceFileLineEdit->setAllowDirectories(v);
365 m_d->m_ui.headerFileLineEdit->setAllowDirectories(v);
366 m_d->m_ui.formFileLineEdit->setAllowDirectories(v);
370 bool NewClassWidget::lowerCaseFiles() const
372 return m_d->m_ui.classLineEdit->lowerCaseFileName();
375 void NewClassWidget::setLowerCaseFiles(bool v)
377 m_d->m_ui.classLineEdit->setLowerCaseFileName(v);
380 NewClassWidget::ClassType NewClassWidget::classType() const
382 return static_cast<ClassType>(m_d->m_ui.classTypeComboBox->currentIndex());
385 void NewClassWidget::setClassType(ClassType ct)
387 m_d->m_ui.classTypeComboBox->setCurrentIndex(ct);
390 bool NewClassWidget::isClassTypeComboVisible() const
392 return m_d->m_ui.classTypeLabel->isVisible();
395 void NewClassWidget::setClassTypeComboVisible(bool v)
397 m_d->m_ui.classTypeLabel->setVisible(v);
398 m_d->m_ui.classTypeComboBox->setVisible(v);
401 // Guess suitable type information with some smartness
402 static inline NewClassWidget::ClassType classTypeForBaseClass(const QString &baseClass)
404 if (!baseClass.startsWith(QLatin1Char('Q')))
405 return NewClassWidget::NoClassType;
406 // QObject types: QObject as such and models.
407 if (baseClass == QLatin1String("QObject") ||
408 (baseClass.startsWith(QLatin1String("QAbstract")) && baseClass.endsWith(QLatin1String("Model"))))
409 return NewClassWidget::ClassInheritsQObject;
411 if (baseClass == QLatin1String("QWidget") || baseClass == QLatin1String("QMainWindow")
412 || baseClass == QLatin1String("QDialog"))
413 return NewClassWidget::ClassInheritsQWidget;
415 if (baseClass == QLatin1String("QDeclarativeItem"))
416 return NewClassWidget::ClassInheritsQDeclarativeItem;
417 return NewClassWidget::NoClassType;
420 void NewClassWidget::slotBaseClassEdited(const QString &baseClass)
422 // Set type information with some smartness.
423 const ClassType currentClassType = classType();
424 const ClassType recommendedClassType = classTypeForBaseClass(baseClass);
425 if (recommendedClassType != NoClassType && currentClassType != recommendedClassType)
426 setClassType(recommendedClassType);
429 void NewClassWidget::slotValidChanged()
431 const bool newValid = isValid();
432 if (newValid != m_d->m_valid) {
433 m_d->m_valid = newValid;
438 bool NewClassWidget::isValid(QString *error) const
440 if (!m_d->m_ui.classLineEdit->isValid()) {
442 *error = m_d->m_ui.classLineEdit->errorMessage();
446 if (isBaseClassInputVisible() && isBaseClassEditable()) {
447 // TODO: Should this be a ClassNameValidatingComboBox?
448 QRegExp classNameValidator(QLatin1String("[a-zA-Z_][a-zA-Z0-9_]*(::[a-zA-Z_][a-zA-Z0-9_]*)*"));
449 const QString baseClass = m_d->m_ui.baseClassComboBox->currentText().trimmed();
450 if (!baseClass.isEmpty() && !classNameValidator.exactMatch(baseClass)) {
452 *error = tr("Invalid base class name");
457 if (!m_d->m_ui.headerFileLineEdit->isValid()) {
459 *error = tr("Invalid header file name: '%1'").arg(m_d->m_ui.headerFileLineEdit->errorMessage());
463 if (!m_d->m_ui.sourceFileLineEdit->isValid()) {
465 *error = tr("Invalid source file name: '%1'").arg(m_d->m_ui.sourceFileLineEdit->errorMessage());
469 if (isFormInputVisible() &&
470 (!m_d->m_formInputCheckable || m_d->m_ui.generateFormCheckBox->isChecked())) {
471 if (!m_d->m_ui.formFileLineEdit->isValid()) {
473 *error = tr("Invalid form file name: '%1'").arg(m_d->m_ui.formFileLineEdit->errorMessage());
478 if (isPathInputVisible()) {
479 if (!m_d->m_ui.pathChooser->isValid()) {
481 *error = m_d->m_ui.pathChooser->errorMessage();
488 void NewClassWidget::triggerUpdateFileNames()
490 m_d->m_ui.classLineEdit->triggerChanged();
493 void NewClassWidget::slotUpdateFileNames(const QString &baseName)
495 if (debugNewClassWidget)
496 qDebug() << Q_FUNC_INFO << baseName << m_d->m_headerExtension << m_d->m_sourceExtension;
497 const QChar dot = QLatin1Char('.');
498 m_d->m_ui.sourceFileLineEdit->setText(baseName + dot + m_d->m_sourceExtension);
499 m_d->m_ui.headerFileLineEdit->setText(baseName + dot + m_d->m_headerExtension);
500 m_d->m_ui.formFileLineEdit->setText(baseName + dot + m_d->m_formExtension);
503 void NewClassWidget::slotActivated()
509 QString NewClassWidget::fixSuffix(const QString &suffix)
512 if (s.startsWith(QLatin1Char('.')))
517 // Utility to add a suffix to a file unless the user specified one
518 static QString ensureSuffix(QString f, const QString &extension)
520 const QChar dot = QLatin1Char('.');
528 // If a non-empty name was passed, expand to directory and suffix
529 static QString expandFileName(const QDir &dir, const QString name, const QString &extension)
533 return dir.absoluteFilePath(ensureSuffix(name, extension));
536 QStringList NewClassWidget::files() const
539 const QDir dir = QDir(path());
540 rc.push_back(expandFileName(dir, headerFileName(), headerExtension()));
541 rc.push_back(expandFileName(dir, sourceFileName(), sourceExtension()));
542 if (isFormInputVisible())
543 rc.push_back(expandFileName(dir, formFileName(), formExtension()));