OSDN Git Service

Update license.
[qt-creator-jp/qt-creator-jp.git] / src / libs / utils / newclasswidget.cpp
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (info@qt.nokia.com)
8 **
9 **
10 ** GNU Lesser General Public License Usage
11 **
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.
18 **
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.
22 **
23 ** Other Usage
24 **
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.
27 **
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
30 **
31 **************************************************************************/
32
33 #include "newclasswidget.h"
34 #include "ui_newclasswidget.h"
35
36 #include <utils/filewizardpage.h>
37
38 #include <QtGui/QFileDialog>
39 #include <QtCore/QFileInfo>
40 #include <QtCore/QStringList>
41 #include <QtCore/QDir>
42 #include <QtCore/QDebug>
43 #include <QtCore/QRegExp>
44
45 enum { debugNewClassWidget = 0 };
46
47 /*! \class Utils::NewClassWidget
48
49     \brief Utility widget for 'New Class' wizards
50
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. */
55
56 namespace Utils {
57
58 struct NewClassWidgetPrivate {
59     NewClassWidgetPrivate();
60
61     Ui::NewClassWidget m_ui;
62     QString m_headerExtension;
63     QString m_sourceExtension;
64     QString m_formExtension;
65     bool m_valid;
66     bool m_classEdited;
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;
74 };
75
76 NewClassWidgetPrivate:: NewClassWidgetPrivate() :
77     m_headerExtension(QLatin1String("h")),
78     m_sourceExtension(QLatin1String("cpp")),
79     m_formExtension(QLatin1String("ui")),
80     m_valid(false),
81     m_classEdited(false),
82     m_baseClassInputVisible(true),
83     m_formInputVisible(true),
84     m_pathInputVisible(true),
85     m_qobjectCheckBoxVisible(false),
86     m_formInputCheckable(false)
87
88 {
89 }
90
91 // --------------------- NewClassWidget
92 NewClassWidget::NewClassWidget(QWidget *parent) :
93     QWidget(parent),
94     m_d(new NewClassWidgetPrivate)
95 {
96     m_d->m_ui.setupUi(this);
97
98     m_d->m_ui.baseClassComboBox->setEditable(false);
99
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()));
120
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()));
133
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);
141 }
142
143 NewClassWidget::~NewClassWidget()
144 {
145     delete m_d;
146 }
147
148 void NewClassWidget::classNameEdited()
149 {
150     if (debugNewClassWidget)
151         qDebug() << Q_FUNC_INFO << m_d->m_headerExtension << m_d->m_sourceExtension;
152     m_d->m_classEdited = true;
153 }
154
155 void NewClassWidget::suggestClassNameFromBase()
156 {
157     if (debugNewClassWidget)
158         qDebug() << Q_FUNC_INFO << m_d->m_headerExtension << m_d->m_sourceExtension;
159     if (m_d->m_classEdited)
160         return;
161     // Suggest a class unless edited ("QMainWindow"->"MainWindow")
162     QString base = baseClassName();
163     if (base.startsWith(QLatin1Char('Q'))) {
164         base.remove(0, 1);
165         setClassName(base);
166     }
167 }
168
169 QStringList NewClassWidget::baseClassChoices() const
170 {
171     QStringList rc;
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));
175     return rc;
176 }
177
178 void NewClassWidget::setBaseClassChoices(const QStringList &choices)
179 {
180     m_d->m_ui.baseClassComboBox->clear();
181     m_d->m_ui.baseClassComboBox->addItems(choices);
182 }
183
184 void NewClassWidget::setBaseClassInputVisible(bool visible)
185 {
186     m_d->m_baseClassInputVisible = visible;
187     m_d->m_ui.baseClassLabel->setVisible(visible);
188     m_d->m_ui.baseClassComboBox->setVisible(visible);
189 }
190
191 void NewClassWidget::setBaseClassEditable(bool editable)
192 {
193     m_d->m_ui.baseClassComboBox->setEditable(editable);
194 }
195
196 bool NewClassWidget::isBaseClassInputVisible() const
197 {
198     return  m_d->m_baseClassInputVisible;
199 }
200
201 bool NewClassWidget::isBaseClassEditable() const
202 {
203     return  m_d->m_ui.baseClassComboBox->isEditable();
204 }
205
206 void NewClassWidget::setFormInputVisible(bool visible)
207 {
208     m_d->m_formInputVisible = visible;
209     m_d->m_ui.formLabel->setVisible(visible);
210     m_d->m_ui.formFileLineEdit->setVisible(visible);
211 }
212
213 bool NewClassWidget::isFormInputVisible() const
214 {
215     return m_d->m_formInputVisible;
216 }
217
218 void NewClassWidget::setFormInputCheckable(bool checkable)
219 {
220     setFormInputCheckable(checkable, false);
221 }
222
223 void NewClassWidget::setFormInputCheckable(bool checkable, bool force)
224 {
225     if (!force && checkable == m_d->m_formInputCheckable)
226         return;
227     m_d->m_formInputCheckable = checkable;
228     m_d->m_ui.generateFormLabel->setVisible(checkable);
229     m_d->m_ui.generateFormCheckBox->setVisible(checkable);
230 }
231
232 void NewClassWidget::setFormInputChecked(bool v)
233 {
234     m_d->m_ui.generateFormCheckBox->setChecked(v);
235 }
236
237 bool NewClassWidget::formInputCheckable() const
238 {
239     return m_d->m_formInputCheckable;
240 }
241
242 bool NewClassWidget::formInputChecked() const
243 {
244     return m_d->m_ui.generateFormCheckBox->isChecked();
245 }
246
247 void NewClassWidget::slotFormInputChecked()
248 {
249     const bool checked = formInputChecked();
250     m_d->m_ui.formLabel->setEnabled(checked);
251     m_d->m_ui.formFileLineEdit->setEnabled(checked);
252 }
253
254 void NewClassWidget::setPathInputVisible(bool visible)
255 {
256     m_d->m_pathInputVisible = visible;
257     m_d->m_ui.pathLabel->setVisible(visible);
258     m_d->m_ui.pathChooser->setVisible(visible);
259 }
260
261 bool NewClassWidget::isPathInputVisible() const
262 {
263     return m_d->m_pathInputVisible;
264 }
265
266 void NewClassWidget::setClassName(const QString &suggestedName)
267 {
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));
271 }
272
273 QString NewClassWidget::className() const
274 {
275     return m_d->m_ui.classLineEdit->text();
276 }
277
278 QString NewClassWidget::baseClassName() const
279 {
280     return m_d->m_ui.baseClassComboBox->currentText();
281 }
282
283 void NewClassWidget::setBaseClassName(const QString &c)
284 {
285     const int index = m_d->m_ui.baseClassComboBox->findText(c);
286     if (index != -1) {
287         m_d->m_ui.baseClassComboBox->setCurrentIndex(index);
288         suggestClassNameFromBase();
289     }
290 }
291
292 QString NewClassWidget::sourceFileName() const
293 {
294     return m_d->m_ui.sourceFileLineEdit->text();
295 }
296
297 QString NewClassWidget::headerFileName() const
298 {
299     return m_d->m_ui.headerFileLineEdit->text();
300 }
301
302 QString NewClassWidget::formFileName() const
303 {
304     return m_d->m_ui.formFileLineEdit->text();
305 }
306
307 QString NewClassWidget::path() const
308 {
309     return m_d->m_ui.pathChooser->path();
310 }
311
312 void NewClassWidget::setPath(const QString &path)
313 {
314      m_d->m_ui.pathChooser->setPath(path);
315 }
316
317 bool NewClassWidget::namespacesEnabled() const
318 {
319     return  m_d->m_ui.classLineEdit->namespacesEnabled();
320 }
321
322 void NewClassWidget::setNamespacesEnabled(bool b)
323 {
324     m_d->m_ui.classLineEdit->setNamespacesEnabled(b);
325 }
326
327 QString NewClassWidget::sourceExtension() const
328 {
329     return m_d->m_sourceExtension;
330 }
331
332 void NewClassWidget::setSourceExtension(const QString &e)
333 {
334     if (debugNewClassWidget)
335         qDebug() << Q_FUNC_INFO << e;
336     m_d->m_sourceExtension = fixSuffix(e);
337 }
338
339 QString NewClassWidget::headerExtension() const
340 {
341     return m_d->m_headerExtension;
342 }
343
344 void NewClassWidget::setHeaderExtension(const QString &e)
345 {
346     if (debugNewClassWidget)
347         qDebug() << Q_FUNC_INFO << e;
348     m_d->m_headerExtension = fixSuffix(e);
349 }
350
351 QString NewClassWidget::formExtension() const
352 {
353     return m_d->m_formExtension;
354 }
355
356 void NewClassWidget::setFormExtension(const QString &e)
357 {
358     if (debugNewClassWidget)
359         qDebug() << Q_FUNC_INFO << e;
360     m_d->m_formExtension = fixSuffix(e);
361 }
362
363 bool NewClassWidget::allowDirectories() const
364 {
365     return m_d->m_ui.headerFileLineEdit->allowDirectories();
366 }
367
368 void NewClassWidget::setAllowDirectories(bool v)
369 {
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);
375     }
376 }
377
378 bool NewClassWidget::lowerCaseFiles() const
379 {
380     return m_d->m_ui.classLineEdit->lowerCaseFileName();
381 }
382
383 void NewClassWidget::setLowerCaseFiles(bool v)
384 {
385     m_d->m_ui.classLineEdit->setLowerCaseFileName(v);
386 }
387
388 NewClassWidget::ClassType NewClassWidget::classType() const
389 {
390     return static_cast<ClassType>(m_d->m_ui.classTypeComboBox->currentIndex());
391 }
392
393 void NewClassWidget::setClassType(ClassType ct)
394 {
395     m_d->m_ui.classTypeComboBox->setCurrentIndex(ct);
396 }
397
398 bool NewClassWidget::isClassTypeComboVisible() const
399 {
400     return m_d->m_ui.classTypeLabel->isVisible();
401 }
402
403 void NewClassWidget::setClassTypeComboVisible(bool v)
404 {
405     m_d->m_ui.classTypeLabel->setVisible(v);
406     m_d->m_ui.classTypeComboBox->setVisible(v);
407 }
408
409 // Guess suitable type information with some smartness
410 static inline NewClassWidget::ClassType classTypeForBaseClass(const QString &baseClass)
411 {
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;
418     // Widgets.
419     if (baseClass == QLatin1String("QWidget") || baseClass == QLatin1String("QMainWindow")
420         || baseClass == QLatin1String("QDialog"))
421         return NewClassWidget::ClassInheritsQWidget;
422     // Declarative Items
423     if (baseClass == QLatin1String("QDeclarativeItem"))
424         return NewClassWidget::ClassInheritsQDeclarativeItem;
425     return NewClassWidget::NoClassType;
426 }
427
428 void NewClassWidget::slotBaseClassEdited(const QString &baseClass)
429 {
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);
435 }
436
437 void NewClassWidget::slotValidChanged()
438 {
439     const bool newValid = isValid();
440     if (newValid != m_d->m_valid) {
441         m_d->m_valid = newValid;
442         emit validChanged();
443     }
444 }
445
446 bool NewClassWidget::isValid(QString *error) const
447 {
448     if (!m_d->m_ui.classLineEdit->isValid()) {
449         if (error)
450             *error = m_d->m_ui.classLineEdit->errorMessage();
451         return false;
452     }
453
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)) {
459             if (error)
460                 *error = tr("Invalid base class name");
461             return false;
462         }
463     }
464
465     if (!m_d->m_ui.headerFileLineEdit->isValid()) {
466         if (error)
467             *error = tr("Invalid header file name: '%1'").arg(m_d->m_ui.headerFileLineEdit->errorMessage());
468         return false;
469     }
470
471     if (!m_d->m_ui.sourceFileLineEdit->isValid()) {
472         if (error)
473             *error = tr("Invalid source file name: '%1'").arg(m_d->m_ui.sourceFileLineEdit->errorMessage());
474         return false;
475     }
476
477     if (isFormInputVisible() &&
478         (!m_d->m_formInputCheckable || m_d->m_ui.generateFormCheckBox->isChecked())) {
479         if (!m_d->m_ui.formFileLineEdit->isValid()) {
480             if (error)
481                 *error = tr("Invalid form file name: '%1'").arg(m_d->m_ui.formFileLineEdit->errorMessage());
482             return false;
483         }
484     }
485
486     if (isPathInputVisible()) {
487         if (!m_d->m_ui.pathChooser->isValid()) {
488             if (error)
489                 *error =  m_d->m_ui.pathChooser->errorMessage();
490             return false;
491         }
492     }
493     return true;
494 }
495
496 void NewClassWidget::triggerUpdateFileNames()
497 {
498     m_d->m_ui.classLineEdit->triggerChanged();
499 }
500
501 void NewClassWidget::slotUpdateFileNames(const QString &baseName)
502 {
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);
509 }
510
511 void NewClassWidget::slotActivated()
512 {
513     if (m_d->m_valid)
514         emit activated();
515 }
516
517 QString NewClassWidget::fixSuffix(const QString &suffix)
518 {
519     QString s = suffix;
520     if (s.startsWith(QLatin1Char('.')))
521         s.remove(0, 1);
522     return s;
523 }
524
525 // Utility to add a suffix to a file unless the user specified one
526 static QString ensureSuffix(QString f, const QString &extension)
527 {
528     const QChar dot = QLatin1Char('.');
529     if (f.contains(dot))
530         return f;
531     f += dot;
532     f += extension;
533     return f;
534 }
535
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)
538 {
539     if (name.isEmpty())
540         return QString();
541     return dir.absoluteFilePath(ensureSuffix(name, extension));
542 }
543
544 QStringList NewClassWidget::files() const
545 {
546     QStringList rc;
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()));
552     return rc;
553 }
554
555 } // namespace Utils