1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qt Creator.
7 **
8 ** Commercial License Usage
9 ** Licensees holding valid commercial Qt licenses may use this file in
10 ** accordance with the commercial license agreement provided with the
11 ** Software or, alternatively, in accordance with the terms contained in
12 ** a written agreement between you and The Qt Company. For licensing terms
13 ** and conditions see https://www.qt.io/terms-conditions. For further
14 ** information use the contact form at https://www.qt.io/contact-us.
15 **
16 ** GNU General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU
18 ** General Public License version 3 as published by the Free Software
19 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
20 ** included in the packaging of this file. Please review the following
21 ** information to ensure the GNU General Public License requirements will
22 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23 **
24 ****************************************************************************/
25 
26 #include "newclasswidget.h"
27 #include "ui_newclasswidget.h"
28 
29 #include <QFileDialog>
30 #include <QDebug>
31 
32 enum { debugNewClassWidget = 0 };
33 
34 /*! \class Utils::NewClassWidget
35 
36     \brief The NewClassWidget class is a utility widget for 'New Class' wizards.
37 
38     This widget prompts the user
39     to enter a class name (optionally derived from some base class) and file
40     names for header, source and form files. Has some smart logic to derive
41     the file names from the class name. */
42 
43 namespace Designer {
44 namespace Internal {
45 
46 struct NewClassWidgetPrivate {
47     NewClassWidgetPrivate();
48 
49     Ui::NewClassWidget m_ui;
50     QString m_headerExtension;
51     QString m_sourceExtension;
52     QString m_formExtension;
53     bool m_valid = false;
54     bool m_classEdited = false;
55 };
56 
NewClassWidgetPrivate()57 NewClassWidgetPrivate:: NewClassWidgetPrivate() :
58     m_headerExtension(QLatin1Char('h')),
59     m_sourceExtension(QLatin1String("cpp")),
60     m_formExtension(QLatin1String("ui"))
61 {
62 }
63 
64 // --------------------- NewClassWidget
NewClassWidget(QWidget * parent)65 NewClassWidget::NewClassWidget(QWidget *parent) :
66     QWidget(parent),
67     d(new NewClassWidgetPrivate)
68 {
69     d->m_ui.setupUi(this);
70 
71     d->m_ui.baseClassLabel->setVisible(false);
72     d->m_ui.baseClassComboBox->setVisible(false);
73     d->m_ui.classTypeLabel->setVisible(false);
74     d->m_ui.classTypeComboBox->setVisible(false);
75 
76     d->m_ui.classLineEdit->setNamespacesEnabled(true);
77     setNamesDelimiter(QLatin1String("::"));
78 
79     connect(d->m_ui.classLineEdit, &Utils::ClassNameValidatingLineEdit::updateFileName,
80             this, &NewClassWidget::slotUpdateFileNames);
81     connect(d->m_ui.classLineEdit, &QLineEdit::textEdited,
82             this, &NewClassWidget::classNameEdited);
83     connect(d->m_ui.baseClassComboBox,
84             QOverload<int>::of(&QComboBox::currentIndexChanged),
85             this, &NewClassWidget::suggestClassNameFromBase);
86     connect(d->m_ui.baseClassComboBox, &QComboBox::editTextChanged,
87             this, &NewClassWidget::slotValidChanged);
88     connect(d->m_ui.classLineEdit, &Utils::FancyLineEdit::validChanged,
89             this, &NewClassWidget::slotValidChanged);
90     connect(d->m_ui.headerFileLineEdit, &Utils::FancyLineEdit::validChanged,
91             this, &NewClassWidget::slotValidChanged);
92     connect(d->m_ui.sourceFileLineEdit, &Utils::FancyLineEdit::validChanged,
93             this, &NewClassWidget::slotValidChanged);
94     connect(d->m_ui.formFileLineEdit, &Utils::FancyLineEdit::validChanged,
95             this, &NewClassWidget::slotValidChanged);
96     connect(d->m_ui.pathChooser, &Utils::PathChooser::validChanged,
97             this, &NewClassWidget::slotValidChanged);
98 
99     connect(d->m_ui.classLineEdit, &Utils::FancyLineEdit::validReturnPressed,
100             this, &NewClassWidget::slotActivated);
101     connect(d->m_ui.headerFileLineEdit, &Utils::FancyLineEdit::validReturnPressed,
102             this, &NewClassWidget::slotActivated);
103     connect(d->m_ui.sourceFileLineEdit, &Utils::FancyLineEdit::validReturnPressed,
104             this, &NewClassWidget::slotActivated);
105     connect(d->m_ui.formFileLineEdit, &Utils::FancyLineEdit::validReturnPressed,
106             this, &NewClassWidget::slotActivated);
107     connect(d->m_ui.formFileLineEdit, &Utils::FancyLineEdit::validReturnPressed,
108             this, &NewClassWidget::slotActivated);
109     connect(d->m_ui.pathChooser, &Utils::PathChooser::returnPressed,
110              this, &NewClassWidget::slotActivated);
111 
112     setClassType(NoClassType);
113 }
114 
~NewClassWidget()115 NewClassWidget::~NewClassWidget()
116 {
117     delete d;
118 }
119 
classNameEdited()120 void NewClassWidget::classNameEdited()
121 {
122     if (debugNewClassWidget)
123         qDebug() << Q_FUNC_INFO << d->m_headerExtension << d->m_sourceExtension;
124     d->m_classEdited = true;
125 }
126 
suggestClassNameFromBase()127 void NewClassWidget::suggestClassNameFromBase()
128 {
129     if (debugNewClassWidget)
130         qDebug() << Q_FUNC_INFO << d->m_headerExtension << d->m_sourceExtension;
131     if (d->m_classEdited)
132         return;
133     // Suggest a class unless edited ("QMainWindow"->"MainWindow")
134     QString base = baseClassName();
135     if (base.startsWith(QLatin1Char('Q'))) {
136         base.remove(0, 1);
137         setClassName(base);
138     }
139 }
140 
setClassName(const QString & suggestedName)141 void NewClassWidget::setClassName(const QString &suggestedName)
142 {
143     if (debugNewClassWidget)
144         qDebug() << Q_FUNC_INFO << suggestedName << d->m_headerExtension << d->m_sourceExtension;
145     d->m_ui.classLineEdit->setText(
146                 Utils::ClassNameValidatingLineEdit::createClassName(suggestedName));
147 }
148 
className() const149 QString NewClassWidget::className() const
150 {
151     return d->m_ui.classLineEdit->text();
152 }
153 
baseClassName() const154 QString NewClassWidget::baseClassName() const
155 {
156     return d->m_ui.baseClassComboBox->currentText();
157 }
158 
sourceFileName() const159 QString NewClassWidget::sourceFileName() const
160 {
161     return d->m_ui.sourceFileLineEdit->text();
162 }
163 
headerFileName() const164 QString NewClassWidget::headerFileName() const
165 {
166     return d->m_ui.headerFileLineEdit->text();
167 }
168 
formFileName() const169 QString NewClassWidget::formFileName() const
170 {
171     return d->m_ui.formFileLineEdit->text();
172 }
173 
path() const174 QString NewClassWidget::path() const
175 {
176     return d->m_ui.pathChooser->filePath().toString();
177 }
178 
setPath(const QString & path)179 void NewClassWidget::setPath(const QString &path)
180 {
181      d->m_ui.pathChooser->setPath(path);
182 }
183 
sourceExtension() const184 QString NewClassWidget::sourceExtension() const
185 {
186     return d->m_sourceExtension;
187 }
188 
setSourceExtension(const QString & e)189 void NewClassWidget::setSourceExtension(const QString &e)
190 {
191     if (debugNewClassWidget)
192         qDebug() << Q_FUNC_INFO << e;
193     d->m_sourceExtension = fixSuffix(e);
194 }
195 
headerExtension() const196 QString NewClassWidget::headerExtension() const
197 {
198     return d->m_headerExtension;
199 }
200 
setHeaderExtension(const QString & e)201 void NewClassWidget::setHeaderExtension(const QString &e)
202 {
203     if (debugNewClassWidget)
204         qDebug() << Q_FUNC_INFO << e;
205     d->m_headerExtension = fixSuffix(e);
206 }
207 
formExtension() const208 QString NewClassWidget::formExtension() const
209 {
210     return d->m_formExtension;
211 }
212 
213 
setLowerCaseFiles(bool v)214 void NewClassWidget::setLowerCaseFiles(bool v)
215 {
216     d->m_ui.classLineEdit->setLowerCaseFileName(v);
217 }
218 
setClassType(ClassType ct)219 void NewClassWidget::setClassType(ClassType ct)
220 {
221     d->m_ui.classTypeComboBox->setCurrentIndex(ct);
222 }
223 
setNamesDelimiter(const QString & delimiter)224 void NewClassWidget::setNamesDelimiter(const QString &delimiter)
225 {
226     d->m_ui.classLineEdit->setNamespaceDelimiter(delimiter);
227 }
228 
slotValidChanged()229 void NewClassWidget::slotValidChanged()
230 {
231     const bool newValid = isValid();
232     if (newValid != d->m_valid) {
233         d->m_valid = newValid;
234         emit validChanged();
235     }
236 }
237 
isValid(QString * error) const238 bool NewClassWidget::isValid(QString *error) const
239 {
240     if (!d->m_ui.classLineEdit->isValid()) {
241         if (error)
242             *error = d->m_ui.classLineEdit->errorMessage();
243         return false;
244     }
245 
246     if (!d->m_ui.headerFileLineEdit->isValid()) {
247         if (error)
248             *error = tr("Invalid header file name: \"%1\"").arg(d->m_ui.headerFileLineEdit->errorMessage());
249         return false;
250     }
251 
252     if (!d->m_ui.sourceFileLineEdit->isValid()) {
253         if (error)
254             *error = tr("Invalid source file name: \"%1\"").arg(d->m_ui.sourceFileLineEdit->errorMessage());
255         return false;
256     }
257 
258     if (!d->m_ui.formFileLineEdit->isValid()) {
259         if (error)
260             *error = tr("Invalid form file name: \"%1\"").arg(d->m_ui.formFileLineEdit->errorMessage());
261         return false;
262     }
263 
264     if (!d->m_ui.pathChooser->isValid()) {
265         if (error)
266             *error =  d->m_ui.pathChooser->errorMessage();
267         return false;
268     }
269     return true;
270 }
271 
slotUpdateFileNames(const QString & baseName)272 void NewClassWidget::slotUpdateFileNames(const QString &baseName)
273 {
274     if (debugNewClassWidget)
275         qDebug() << Q_FUNC_INFO << baseName << d->m_headerExtension << d->m_sourceExtension;
276     const QChar dot = QLatin1Char('.');
277     d->m_ui.sourceFileLineEdit->setText(baseName + dot + d->m_sourceExtension);
278     d->m_ui.headerFileLineEdit->setText(baseName + dot + d->m_headerExtension);
279     d->m_ui.formFileLineEdit->setText(baseName + dot + d->m_formExtension);
280 }
281 
slotActivated()282 void NewClassWidget::slotActivated()
283 {
284     if (d->m_valid)
285         emit activated();
286 }
287 
fixSuffix(const QString & suffix)288 QString NewClassWidget::fixSuffix(const QString &suffix)
289 {
290     QString s = suffix;
291     if (s.startsWith(QLatin1Char('.')))
292         s.remove(0, 1);
293     return s;
294 }
295 
296 // Utility to add a suffix to a file unless the user specified one
ensureSuffix(QString f,const QString & extension)297 static QString ensureSuffix(QString f, const QString &extension)
298 {
299     const QChar dot = QLatin1Char('.');
300     if (f.contains(dot))
301         return f;
302     f += dot;
303     f += extension;
304     return f;
305 }
306 
307 // If a non-empty name was passed, expand to directory and suffix
expandFileName(const QDir & dir,const QString & name,const QString & extension)308 static QString expandFileName(const QDir &dir, const QString &name, const QString &extension)
309 {
310     if (name.isEmpty())
311         return QString();
312     return dir.absoluteFilePath(ensureSuffix(name, extension));
313 }
314 
files() const315 QStringList NewClassWidget::files() const
316 {
317     QStringList rc;
318     const QDir dir = QDir(path());
319     rc.push_back(expandFileName(dir, headerFileName(), headerExtension()));
320     rc.push_back(expandFileName(dir, sourceFileName(), sourceExtension()));
321     rc.push_back(expandFileName(dir, formFileName(), formExtension()));
322     return rc;
323 }
324 
325 } // namespace Internal
326 } // namespace Designer
327