1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the tools applications of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21 ** included in the packaging of this file. Please review the following
22 ** information to ensure the GNU General Public License requirements will
23 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24 **
25 ** $QT_END_LICENSE$
26 **
27 ****************************************************************************/
28
29 #include "driver.h"
30 #include "uic.h"
31 #include "ui4.h"
32
33 #include <language.h>
34
35 #include <qfileinfo.h>
36 #include <qdebug.h>
37
38 #include <algorithm>
39
40 QT_BEGIN_NAMESPACE
41
Driver()42 Driver::Driver()
43 : m_stdout(stdout, QFile::WriteOnly | QFile::Text)
44 {
45 m_output = &m_stdout;
46 }
47
48 Driver::~Driver() = default;
49
spacerItemClass()50 static inline QString spacerItemClass() { return QStringLiteral("QSpacerItem"); }
actionGroupClass()51 static inline QString actionGroupClass() { return QStringLiteral("QActionGroup"); }
actionClass()52 static inline QString actionClass() { return QStringLiteral("QAction"); }
buttonGroupClass()53 static inline QString buttonGroupClass() { return QStringLiteral("QButtonGroup"); }
54
55 template <class DomClass>
56 Driver::DomObjectHashConstIt<DomClass>
findByAttributeNameIt(const DomObjectHash<DomClass> & domHash,const QString & name) const57 Driver::findByAttributeNameIt(const DomObjectHash<DomClass> &domHash,
58 const QString &name) const
59 {
60 const auto end = domHash.cend();
61 for (auto it = domHash.cbegin(); it != end; ++it) {
62 if (it.key()->attributeName() == name)
63 return it;
64 }
65 return end;
66 }
67
68 template <class DomClass>
findByAttributeName(const DomObjectHash<DomClass> & domHash,const QString & name) const69 const DomClass *Driver::findByAttributeName(const DomObjectHash<DomClass> &domHash,
70 const QString &name) const
71 {
72 auto it = findByAttributeNameIt(domHash, name);
73 return it != domHash.cend() ? it.key() : nullptr;
74 }
75
76 template <class DomClass>
findOrInsert(DomObjectHash<DomClass> * domHash,const DomClass * dom,const QString & className,bool isMember)77 QString Driver::findOrInsert(DomObjectHash<DomClass> *domHash, const DomClass *dom,
78 const QString &className, bool isMember)
79 {
80 auto it = domHash->find(dom);
81 if (it == domHash->end()) {
82 const QString name = this->unique(dom->attributeName(), className);
83 it = domHash->insert(dom, isMember ? language::self + name : name);
84 }
85 return it.value();
86 }
87
findOrInsertWidget(const DomWidget * ui_widget)88 QString Driver::findOrInsertWidget(const DomWidget *ui_widget)
89 {
90 // Top level is passed into setupUI(), everything else is a member variable
91 const bool isMember = !m_widgets.isEmpty();
92 return findOrInsert(&m_widgets, ui_widget, ui_widget->attributeClass(), isMember);
93 }
94
findOrInsertSpacer(const DomSpacer * ui_spacer)95 QString Driver::findOrInsertSpacer(const DomSpacer *ui_spacer)
96 {
97 return findOrInsert(&m_spacers, ui_spacer, spacerItemClass());
98 }
99
findOrInsertLayout(const DomLayout * ui_layout)100 QString Driver::findOrInsertLayout(const DomLayout *ui_layout)
101 {
102 return findOrInsert(&m_layouts, ui_layout, ui_layout->attributeClass());
103 }
104
findOrInsertLayoutItem(const DomLayoutItem * ui_layoutItem)105 QString Driver::findOrInsertLayoutItem(const DomLayoutItem *ui_layoutItem)
106 {
107 switch (ui_layoutItem->kind()) {
108 case DomLayoutItem::Widget:
109 return findOrInsertWidget(ui_layoutItem->elementWidget());
110 case DomLayoutItem::Spacer:
111 return findOrInsertSpacer(ui_layoutItem->elementSpacer());
112 case DomLayoutItem::Layout:
113 return findOrInsertLayout(ui_layoutItem->elementLayout());
114 case DomLayoutItem::Unknown:
115 break;
116 }
117
118 Q_ASSERT( 0 );
119
120 return QString();
121 }
122
findOrInsertActionGroup(const DomActionGroup * ui_group)123 QString Driver::findOrInsertActionGroup(const DomActionGroup *ui_group)
124 {
125 return findOrInsert(&m_actionGroups, ui_group, actionGroupClass());
126 }
127
findOrInsertAction(const DomAction * ui_action)128 QString Driver::findOrInsertAction(const DomAction *ui_action)
129 {
130 return findOrInsert(&m_actions, ui_action, actionClass());
131 }
132
findOrInsertButtonGroup(const DomButtonGroup * ui_group)133 QString Driver::findOrInsertButtonGroup(const DomButtonGroup *ui_group)
134 {
135 return findOrInsert(&m_buttonGroups, ui_group, buttonGroupClass());
136 }
137
138 // Find a group by its non-uniqified name
findButtonGroup(const QString & attributeName) const139 const DomButtonGroup *Driver::findButtonGroup(const QString &attributeName) const
140 {
141 return findByAttributeName(m_buttonGroups, attributeName);
142 }
143
144
findOrInsertName(const QString & name)145 QString Driver::findOrInsertName(const QString &name)
146 {
147 return unique(name);
148 }
149
normalizedName(const QString & name)150 QString Driver::normalizedName(const QString &name)
151 {
152 QString result = name;
153 std::replace_if(result.begin(), result.end(),
154 [] (QChar c) { return !c.isLetterOrNumber(); },
155 QLatin1Char('_'));
156 return result;
157 }
158
unique(const QString & instanceName,const QString & className)159 QString Driver::unique(const QString &instanceName, const QString &className)
160 {
161 QString name;
162 bool alreadyUsed = false;
163
164 if (!instanceName.isEmpty()) {
165 name = normalizedName(instanceName);
166 QString base = name;
167
168 for (int id = 1; m_nameRepository.contains(name); ++id) {
169 alreadyUsed = true;
170 name = base + QString::number(id);
171 }
172 } else if (!className.isEmpty()) {
173 name = unique(qtify(className));
174 } else {
175 name = unique(QLatin1String("var"));
176 }
177
178 if (alreadyUsed && !className.isEmpty()) {
179 fprintf(stderr, "%s: Warning: The name '%s' (%s) is already in use, defaulting to '%s'.\n",
180 qPrintable(m_option.messagePrefix()),
181 qPrintable(instanceName), qPrintable(className),
182 qPrintable(name));
183 }
184
185 m_nameRepository.insert(name, true);
186 return name;
187 }
188
qtify(const QString & name)189 QString Driver::qtify(const QString &name)
190 {
191 QString qname = name;
192
193 if (qname.at(0) == QLatin1Char('Q') || qname.at(0) == QLatin1Char('K'))
194 qname.remove(0, 1);
195
196 for (int i = 0, size = qname.size(); i < size && qname.at(i).isUpper(); ++i)
197 qname[i] = qname.at(i).toLower();
198
199 return qname;
200 }
201
isAnsiCCharacter(QChar c)202 static bool isAnsiCCharacter(QChar c)
203 {
204 return (c.toUpper() >= QLatin1Char('A') && c.toUpper() <= QLatin1Char('Z'))
205 || c.isDigit() || c == QLatin1Char('_');
206 }
207
headerFileName() const208 QString Driver::headerFileName() const
209 {
210 QString name = m_option.outputFile;
211
212 if (name.isEmpty()) {
213 name = QLatin1String("ui_"); // ### use ui_ as prefix.
214 name.append(m_option.inputFile);
215 }
216
217 return headerFileName(name);
218 }
219
headerFileName(const QString & fileName)220 QString Driver::headerFileName(const QString &fileName)
221 {
222 if (fileName.isEmpty())
223 return headerFileName(QLatin1String("noname"));
224
225 QFileInfo info(fileName);
226 QString baseName = info.baseName();
227 // Transform into a valid C++ identifier
228 if (!baseName.isEmpty() && baseName.at(0).isDigit())
229 baseName.prepend(QLatin1Char('_'));
230 for (int i = 0; i < baseName.size(); ++i) {
231 QChar c = baseName.at(i);
232 if (!isAnsiCCharacter(c)) {
233 // Replace character by its unicode value
234 QString hex = QString::number(c.unicode(), 16);
235 baseName.replace(i, 1, QLatin1Char('_') + hex + QLatin1Char('_'));
236 i += hex.size() + 1;
237 }
238 }
239 return baseName.toUpper() + QLatin1String("_H");
240 }
241
printDependencies(const QString & fileName)242 bool Driver::printDependencies(const QString &fileName)
243 {
244 Q_ASSERT(m_option.dependencies == true);
245
246 m_option.inputFile = fileName;
247
248 Uic tool(this);
249 return tool.printDependencies();
250 }
251
uic(const QString & fileName,DomUI * ui,QTextStream * out)252 bool Driver::uic(const QString &fileName, DomUI *ui, QTextStream *out)
253 {
254 m_option.inputFile = fileName;
255 setUseIdBasedTranslations(ui->attributeIdbasedtr());
256
257 QTextStream *oldOutput = m_output;
258
259 m_output = out != nullptr ? out : &m_stdout;
260
261 Uic tool(this);
262 const bool result = tool.write(ui);
263
264 m_output = oldOutput;
265
266 return result;
267 }
268
uic(const QString & fileName,QTextStream * out)269 bool Driver::uic(const QString &fileName, QTextStream *out)
270 {
271 QFile f;
272 if (fileName.isEmpty())
273 f.open(stdin, QIODevice::ReadOnly);
274 else {
275 f.setFileName(fileName);
276 if (!f.open(QIODevice::ReadOnly))
277 return false;
278 }
279
280 m_option.inputFile = fileName;
281
282 QTextStream *oldOutput = m_output;
283 bool deleteOutput = false;
284
285 if (out) {
286 m_output = out;
287 } else {
288 #ifdef Q_OS_WIN
289 // As one might also redirect the output to a file on win,
290 // we should not create the textstream with QFile::Text flag.
291 // The redirected file is opened in TextMode and this will
292 // result in broken line endings as writing will replace \n again.
293 m_output = new QTextStream(stdout, QIODevice::WriteOnly);
294 #else
295 m_output = new QTextStream(stdout, QIODevice::WriteOnly | QFile::Text);
296 #endif
297 deleteOutput = true;
298 }
299
300 Uic tool(this);
301 bool rtn = tool.write(&f);
302 f.close();
303
304 if (deleteOutput)
305 delete m_output;
306
307 m_output = oldOutput;
308
309 return rtn;
310 }
311
widgetByName(const QString & attributeName) const312 const DomWidget *Driver::widgetByName(const QString &attributeName) const
313 {
314 return findByAttributeName(m_widgets, attributeName);
315 }
316
widgetVariableName(const QString & attributeName) const317 QString Driver::widgetVariableName(const QString &attributeName) const
318 {
319 auto it = findByAttributeNameIt(m_widgets, attributeName);
320 return it != m_widgets.cend() ? it.value() : QString();
321 }
322
actionGroupByName(const QString & attributeName) const323 const DomActionGroup *Driver::actionGroupByName(const QString &attributeName) const
324 {
325 return findByAttributeName(m_actionGroups, attributeName);
326 }
327
actionByName(const QString & attributeName) const328 const DomAction *Driver::actionByName(const QString &attributeName) const
329 {
330 return findByAttributeName(m_actions, attributeName);
331 }
332
333 QT_END_NAMESPACE
334