OSDN Git Service

It's 2011 now.
[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 (qt-info@nokia.com)
8 **
9 ** No Commercial Usage
10 **
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
14 ** this package.
15 **
16 ** GNU Lesser General Public License Usage
17 **
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.
24 **
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.
28 **
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
31 **
32 **************************************************************************/
33
34 #include "newclasswidget.h"
35 #include "ui_newclasswidget.h"
36
37 #include <utils/filewizardpage.h>
38
39 #include <QtGui/QFileDialog>
40 #include <QtCore/QFileInfo>
41 #include <QtCore/QStringList>
42 #include <QtCore/QDir>
43 #include <QtCore/QDebug>
44 #include <QtCore/QRegExp>
45
46 enum { debugNewClassWidget = 0 };
47
48 namespace Utils {
49
50 struct NewClassWidgetPrivate {
51     NewClassWidgetPrivate();
52
53     Ui::NewClassWidget m_ui;
54     QString m_headerExtension;
55     QString m_sourceExtension;
56     QString m_formExtension;
57     bool m_valid;
58     bool m_classEdited;
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;
66 };
67
68 NewClassWidgetPrivate:: NewClassWidgetPrivate() :
69     m_headerExtension(QLatin1String("h")),
70     m_sourceExtension(QLatin1String("cpp")),
71     m_formExtension(QLatin1String("ui")),
72     m_valid(false),
73     m_classEdited(false),
74     m_baseClassInputVisible(true),
75     m_formInputVisible(true),
76     m_pathInputVisible(true),
77     m_qobjectCheckBoxVisible(false),
78     m_formInputCheckable(false)
79
80 {
81 }
82
83 // --------------------- NewClassWidget
84 NewClassWidget::NewClassWidget(QWidget *parent) :
85     QWidget(parent),
86     m_d(new NewClassWidgetPrivate)
87 {
88     m_d->m_ui.setupUi(this);
89
90     m_d->m_ui.baseClassComboBox->setEditable(false);
91
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()));
112
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()));
125
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);
133 }
134
135 NewClassWidget::~NewClassWidget()
136 {
137     delete m_d;
138 }
139
140 void NewClassWidget::classNameEdited()
141 {
142     if (debugNewClassWidget)
143         qDebug() << Q_FUNC_INFO << m_d->m_headerExtension << m_d->m_sourceExtension;
144     m_d->m_classEdited = true;
145 }
146
147 void NewClassWidget::suggestClassNameFromBase()
148 {
149     if (debugNewClassWidget)
150         qDebug() << Q_FUNC_INFO << m_d->m_headerExtension << m_d->m_sourceExtension;
151     if (m_d->m_classEdited)
152         return;
153     // Suggest a class unless edited ("QMainWindow"->"MainWindow")
154     QString base = baseClassName();
155     if (base.startsWith(QLatin1Char('Q'))) {
156         base.remove(0, 1);
157         setClassName(base);
158     }
159 }
160
161 QStringList NewClassWidget::baseClassChoices() const
162 {
163     QStringList rc;
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));
167     return rc;
168 }
169
170 void NewClassWidget::setBaseClassChoices(const QStringList &choices)
171 {
172     m_d->m_ui.baseClassComboBox->clear();
173     m_d->m_ui.baseClassComboBox->addItems(choices);
174 }
175
176 void NewClassWidget::setBaseClassInputVisible(bool visible)
177 {
178     m_d->m_baseClassInputVisible = visible;
179     m_d->m_ui.baseClassLabel->setVisible(visible);
180     m_d->m_ui.baseClassComboBox->setVisible(visible);
181 }
182
183 void NewClassWidget::setBaseClassEditable(bool editable)
184 {
185     m_d->m_ui.baseClassComboBox->setEditable(editable);
186 }
187
188 bool NewClassWidget::isBaseClassInputVisible() const
189 {
190     return  m_d->m_baseClassInputVisible;
191 }
192
193 bool NewClassWidget::isBaseClassEditable() const
194 {
195     return  m_d->m_ui.baseClassComboBox->isEditable();
196 }
197
198 void NewClassWidget::setFormInputVisible(bool visible)
199 {
200     m_d->m_formInputVisible = visible;
201     m_d->m_ui.formLabel->setVisible(visible);
202     m_d->m_ui.formFileLineEdit->setVisible(visible);
203 }
204
205 bool NewClassWidget::isFormInputVisible() const
206 {
207     return m_d->m_formInputVisible;
208 }
209
210 void NewClassWidget::setFormInputCheckable(bool checkable)
211 {
212     setFormInputCheckable(checkable, false);
213 }
214
215 void NewClassWidget::setFormInputCheckable(bool checkable, bool force)
216 {
217     if (!force && checkable == m_d->m_formInputCheckable)
218         return;
219     m_d->m_formInputCheckable = checkable;
220     m_d->m_ui.generateFormLabel->setVisible(checkable);
221     m_d->m_ui.generateFormCheckBox->setVisible(checkable);
222 }
223
224 void NewClassWidget::setFormInputChecked(bool v)
225 {
226     m_d->m_ui.generateFormCheckBox->setChecked(v);
227 }
228
229 bool NewClassWidget::formInputCheckable() const
230 {
231     return m_d->m_formInputCheckable;
232 }
233
234 bool NewClassWidget::formInputChecked() const
235 {
236     return m_d->m_ui.generateFormCheckBox->isChecked();
237 }
238
239 void NewClassWidget::slotFormInputChecked()
240 {
241     const bool checked = formInputChecked();
242     m_d->m_ui.formLabel->setEnabled(checked);
243     m_d->m_ui.formFileLineEdit->setEnabled(checked);
244 }
245
246 void NewClassWidget::setPathInputVisible(bool visible)
247 {
248     m_d->m_pathInputVisible = visible;
249     m_d->m_ui.pathLabel->setVisible(visible);
250     m_d->m_ui.pathChooser->setVisible(visible);
251 }
252
253 bool NewClassWidget::isPathInputVisible() const
254 {
255     return m_d->m_pathInputVisible;
256 }
257
258 void NewClassWidget::setClassName(const QString &suggestedName)
259 {
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));
263 }
264
265 QString NewClassWidget::className() const
266 {
267     return m_d->m_ui.classLineEdit->text();
268 }
269
270 QString NewClassWidget::baseClassName() const
271 {
272     return m_d->m_ui.baseClassComboBox->currentText();
273 }
274
275 void NewClassWidget::setBaseClassName(const QString &c)
276 {
277     const int index = m_d->m_ui.baseClassComboBox->findText(c);
278     if (index != -1) {
279         m_d->m_ui.baseClassComboBox->setCurrentIndex(index);
280         suggestClassNameFromBase();
281     }
282 }
283
284 QString NewClassWidget::sourceFileName() const
285 {
286     return m_d->m_ui.sourceFileLineEdit->text();
287 }
288
289 QString NewClassWidget::headerFileName() const
290 {
291     return m_d->m_ui.headerFileLineEdit->text();
292 }
293
294 QString NewClassWidget::formFileName() const
295 {
296     return m_d->m_ui.formFileLineEdit->text();
297 }
298
299 QString NewClassWidget::path() const
300 {
301     return m_d->m_ui.pathChooser->path();
302 }
303
304 void NewClassWidget::setPath(const QString &path)
305 {
306      m_d->m_ui.pathChooser->setPath(path);
307 }
308
309 bool NewClassWidget::namespacesEnabled() const
310 {
311     return  m_d->m_ui.classLineEdit->namespacesEnabled();
312 }
313
314 void NewClassWidget::setNamespacesEnabled(bool b)
315 {
316     m_d->m_ui.classLineEdit->setNamespacesEnabled(b);
317 }
318
319 QString NewClassWidget::sourceExtension() const
320 {
321     return m_d->m_sourceExtension;
322 }
323
324 void NewClassWidget::setSourceExtension(const QString &e)
325 {
326     if (debugNewClassWidget)
327         qDebug() << Q_FUNC_INFO << e;
328     m_d->m_sourceExtension = fixSuffix(e);
329 }
330
331 QString NewClassWidget::headerExtension() const
332 {
333     return m_d->m_headerExtension;
334 }
335
336 void NewClassWidget::setHeaderExtension(const QString &e)
337 {
338     if (debugNewClassWidget)
339         qDebug() << Q_FUNC_INFO << e;
340     m_d->m_headerExtension = fixSuffix(e);
341 }
342
343 QString NewClassWidget::formExtension() const
344 {
345     return m_d->m_formExtension;
346 }
347
348 void NewClassWidget::setFormExtension(const QString &e)
349 {
350     if (debugNewClassWidget)
351         qDebug() << Q_FUNC_INFO << e;
352     m_d->m_formExtension = fixSuffix(e);
353 }
354
355 bool NewClassWidget::allowDirectories() const
356 {
357     return m_d->m_ui.headerFileLineEdit->allowDirectories();
358 }
359
360 void NewClassWidget::setAllowDirectories(bool v)
361 {
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);
367     }
368 }
369
370 bool NewClassWidget::lowerCaseFiles() const
371 {
372     return m_d->m_ui.classLineEdit->lowerCaseFileName();
373 }
374
375 void NewClassWidget::setLowerCaseFiles(bool v)
376 {
377     m_d->m_ui.classLineEdit->setLowerCaseFileName(v);
378 }
379
380 NewClassWidget::ClassType NewClassWidget::classType() const
381 {
382     return static_cast<ClassType>(m_d->m_ui.classTypeComboBox->currentIndex());
383 }
384
385 void NewClassWidget::setClassType(ClassType ct)
386 {
387     m_d->m_ui.classTypeComboBox->setCurrentIndex(ct);
388 }
389
390 bool NewClassWidget::isClassTypeComboVisible() const
391 {
392     return m_d->m_ui.classTypeLabel->isVisible();
393 }
394
395 void NewClassWidget::setClassTypeComboVisible(bool v)
396 {
397     m_d->m_ui.classTypeLabel->setVisible(v);
398     m_d->m_ui.classTypeComboBox->setVisible(v);
399 }
400
401 // Guess suitable type information with some smartness
402 static inline NewClassWidget::ClassType classTypeForBaseClass(const QString &baseClass)
403 {
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;
410     // Widgets.
411     if (baseClass == QLatin1String("QWidget") || baseClass == QLatin1String("QMainWindow")
412         || baseClass == QLatin1String("QDialog"))
413         return NewClassWidget::ClassInheritsQWidget;
414     // Declarative Items
415     if (baseClass == QLatin1String("QDeclarativeItem"))
416         return NewClassWidget::ClassInheritsQDeclarativeItem;
417     return NewClassWidget::NoClassType;
418 }
419
420 void NewClassWidget::slotBaseClassEdited(const QString &baseClass)
421 {
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);
427 }
428
429 void NewClassWidget::slotValidChanged()
430 {
431     const bool newValid = isValid();
432     if (newValid != m_d->m_valid) {
433         m_d->m_valid = newValid;
434         emit validChanged();
435     }
436 }
437
438 bool NewClassWidget::isValid(QString *error) const
439 {
440     if (!m_d->m_ui.classLineEdit->isValid()) {
441         if (error)
442             *error = m_d->m_ui.classLineEdit->errorMessage();
443         return false;
444     }
445
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)) {
451             if (error)
452                 *error = tr("Invalid base class name");
453             return false;
454         }
455     }
456
457     if (!m_d->m_ui.headerFileLineEdit->isValid()) {
458         if (error)
459             *error = tr("Invalid header file name: '%1'").arg(m_d->m_ui.headerFileLineEdit->errorMessage());
460         return false;
461     }
462
463     if (!m_d->m_ui.sourceFileLineEdit->isValid()) {
464         if (error)
465             *error = tr("Invalid source file name: '%1'").arg(m_d->m_ui.sourceFileLineEdit->errorMessage());
466         return false;
467     }
468
469     if (isFormInputVisible() &&
470         (!m_d->m_formInputCheckable || m_d->m_ui.generateFormCheckBox->isChecked())) {
471         if (!m_d->m_ui.formFileLineEdit->isValid()) {
472             if (error)
473                 *error = tr("Invalid form file name: '%1'").arg(m_d->m_ui.formFileLineEdit->errorMessage());
474             return false;
475         }
476     }
477
478     if (isPathInputVisible()) {
479         if (!m_d->m_ui.pathChooser->isValid()) {
480             if (error)
481                 *error =  m_d->m_ui.pathChooser->errorMessage();
482             return false;
483         }
484     }
485     return true;
486 }
487
488 void NewClassWidget::triggerUpdateFileNames()
489 {
490     m_d->m_ui.classLineEdit->triggerChanged();
491 }
492
493 void NewClassWidget::slotUpdateFileNames(const QString &baseName)
494 {
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);
501 }
502
503 void NewClassWidget::slotActivated()
504 {
505     if (m_d->m_valid)
506         emit activated();
507 }
508
509 QString NewClassWidget::fixSuffix(const QString &suffix)
510 {
511     QString s = suffix;
512     if (s.startsWith(QLatin1Char('.')))
513         s.remove(0, 1);
514     return s;
515 }
516
517 // Utility to add a suffix to a file unless the user specified one
518 static QString ensureSuffix(QString f, const QString &extension)
519 {
520     const QChar dot = QLatin1Char('.');
521     if (f.contains(dot))
522         return f;
523     f += dot;
524     f += extension;
525     return f;
526 }
527
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)
530 {
531     if (name.isEmpty())
532         return QString();
533     return dir.absoluteFilePath(ensureSuffix(name, extension));
534 }
535
536 QStringList NewClassWidget::files() const
537 {
538     QStringList rc;
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()));
544     return rc;
545 }
546
547 } // namespace Utils