1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). |
4 | ** Contact: http://www.qt-project.org/legal |
5 | ** |
6 | ** This file is part of the tools applications of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
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 Digia. For licensing terms and |
14 | ** conditions see http://qt.digia.com/licensing. For further information |
15 | ** use the contact form at http://qt.digia.com/contact-us. |
16 | ** |
17 | ** 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. |
24 | ** |
25 | ** In addition, as a special exception, Digia gives you certain additional |
26 | ** rights. These rights are described in the Digia Qt LGPL Exception |
27 | ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
28 | ** |
29 | ** GNU General Public License Usage |
30 | ** Alternatively, this file may be used under the terms of the GNU |
31 | ** General Public License version 3.0 as published by the Free Software |
32 | ** Foundation and appearing in the file LICENSE.GPL included in the |
33 | ** packaging of this file. Please review the following information to |
34 | ** ensure the GNU General Public License version 3.0 requirements will be |
35 | ** met: http://www.gnu.org/copyleft/gpl.html. |
36 | ** |
37 | ** |
38 | ** $QT_END_LICENSE$ |
39 | ** |
40 | ****************************************************************************/ |
41 | |
42 | #include "driver.h" |
43 | #include "uic.h" |
44 | #include "ui4.h" |
45 | |
46 | #include <QtCore/QRegExp> |
47 | #include <QtCore/QFileInfo> |
48 | #include <QtCore/QDebug> |
49 | |
50 | QT_BEGIN_NAMESPACE |
51 | |
52 | Driver::Driver() |
53 | : m_stdout(stdout, QFile::WriteOnly | QFile::Text) |
54 | { |
55 | m_output = &m_stdout; |
56 | } |
57 | |
58 | Driver::~Driver() |
59 | { |
60 | } |
61 | |
62 | QString Driver::findOrInsertWidget(DomWidget *ui_widget) |
63 | { |
64 | if (!m_widgets.contains(ui_widget)) |
65 | m_widgets.insert(ui_widget, unique(ui_widget->attributeName(), ui_widget->attributeClass())); |
66 | |
67 | return m_widgets.value(ui_widget); |
68 | } |
69 | |
70 | QString Driver::findOrInsertSpacer(DomSpacer *ui_spacer) |
71 | { |
72 | if (!m_spacers.contains(ui_spacer)) { |
73 | const QString name = ui_spacer->hasAttributeName() ? ui_spacer->attributeName() : QString(); |
74 | m_spacers.insert(ui_spacer, unique(name, QLatin1String("QSpacerItem" ))); |
75 | } |
76 | |
77 | return m_spacers.value(ui_spacer); |
78 | } |
79 | |
80 | QString Driver::findOrInsertLayout(DomLayout *ui_layout) |
81 | { |
82 | if (!m_layouts.contains(ui_layout)) { |
83 | const QString name = ui_layout->hasAttributeName() ? ui_layout->attributeName() : QString(); |
84 | m_layouts.insert(ui_layout, unique(name, ui_layout->attributeClass())); |
85 | } |
86 | |
87 | return m_layouts.value(ui_layout); |
88 | } |
89 | |
90 | QString Driver::findOrInsertLayoutItem(DomLayoutItem *ui_layoutItem) |
91 | { |
92 | switch (ui_layoutItem->kind()) { |
93 | case DomLayoutItem::Widget: |
94 | return findOrInsertWidget(ui_layoutItem->elementWidget()); |
95 | case DomLayoutItem::Spacer: |
96 | return findOrInsertSpacer(ui_layoutItem->elementSpacer()); |
97 | case DomLayoutItem::Layout: |
98 | return findOrInsertLayout(ui_layoutItem->elementLayout()); |
99 | case DomLayoutItem::Unknown: |
100 | break; |
101 | } |
102 | |
103 | Q_ASSERT( 0 ); |
104 | |
105 | return QString(); |
106 | } |
107 | |
108 | QString Driver::findOrInsertActionGroup(DomActionGroup *ui_group) |
109 | { |
110 | if (!m_actionGroups.contains(ui_group)) |
111 | m_actionGroups.insert(ui_group, unique(ui_group->attributeName(), QLatin1String("QActionGroup" ))); |
112 | |
113 | return m_actionGroups.value(ui_group); |
114 | } |
115 | |
116 | QString Driver::findOrInsertAction(DomAction *ui_action) |
117 | { |
118 | if (!m_actions.contains(ui_action)) |
119 | m_actions.insert(ui_action, unique(ui_action->attributeName(), QLatin1String("QAction" ))); |
120 | |
121 | return m_actions.value(ui_action); |
122 | } |
123 | |
124 | QString Driver::findOrInsertButtonGroup(const DomButtonGroup *ui_group) |
125 | { |
126 | ButtonGroupNameHash::iterator it = m_buttonGroups.find(ui_group); |
127 | if (it == m_buttonGroups.end()) |
128 | it = m_buttonGroups.insert(ui_group, unique(ui_group->attributeName(), QLatin1String("QButtonGroup" ))); |
129 | return it.value(); |
130 | } |
131 | |
132 | // Find a group by its non-uniqified name |
133 | const DomButtonGroup *Driver::findButtonGroup(const QString &attributeName) const |
134 | { |
135 | const ButtonGroupNameHash::const_iterator cend = m_buttonGroups.constEnd(); |
136 | for (ButtonGroupNameHash::const_iterator it = m_buttonGroups.constBegin(); it != cend; ++it) |
137 | if (it.key()->attributeName() == attributeName) |
138 | return it.key(); |
139 | return 0; |
140 | } |
141 | |
142 | |
143 | QString Driver::findOrInsertName(const QString &name) |
144 | { |
145 | return unique(name); |
146 | } |
147 | |
148 | QString Driver::normalizedName(const QString &name) |
149 | { |
150 | QString result = name; |
151 | QChar *data = result.data(); |
152 | for (int i = name.size(); --i >= 0; ++data) { |
153 | if (!data->isLetterOrNumber()) |
154 | *data = QLatin1Char('_'); |
155 | } |
156 | return result; |
157 | } |
158 | |
159 | QString Driver::unique(const QString &instanceName, const QString &className) |
160 | { |
161 | QString name; |
162 | bool alreadyUsed = false; |
163 | |
164 | if (instanceName.size()) { |
165 | int id = 1; |
166 | name = instanceName; |
167 | name = normalizedName(name); |
168 | QString base = name; |
169 | |
170 | while (m_nameRepository.contains(name)) { |
171 | alreadyUsed = true; |
172 | name = base + QString::number(id++); |
173 | } |
174 | } else if (className.size()) { |
175 | name = unique(qtify(className)); |
176 | } else { |
177 | name = unique(QLatin1String("var" )); |
178 | } |
179 | |
180 | if (alreadyUsed && className.size()) { |
181 | fprintf(stderr, "%s: Warning: The name '%s' (%s) is already in use, defaulting to '%s'.\n" , |
182 | qPrintable(m_option.messagePrefix()), |
183 | qPrintable(instanceName), qPrintable(className), |
184 | qPrintable(name)); |
185 | } |
186 | |
187 | m_nameRepository.insert(name, true); |
188 | return name; |
189 | } |
190 | |
191 | QString Driver::qtify(const QString &name) |
192 | { |
193 | QString qname = name; |
194 | |
195 | if (qname.at(0) == QLatin1Char('Q') || qname.at(0) == QLatin1Char('K')) |
196 | qname = qname.mid(1); |
197 | |
198 | int i=0; |
199 | while (i < qname.length()) { |
200 | if (qname.at(i).toLower() != qname.at(i)) |
201 | qname[i] = qname.at(i).toLower(); |
202 | else |
203 | break; |
204 | |
205 | ++i; |
206 | } |
207 | |
208 | return qname; |
209 | } |
210 | |
211 | static bool isAnsiCCharacter(const QChar& c) |
212 | { |
213 | return (c.toUpper() >= QLatin1Char('A') && c.toUpper() <= QLatin1Char('Z')) |
214 | || c.isDigit() || c == QLatin1Char('_'); |
215 | } |
216 | |
217 | QString Driver::() const |
218 | { |
219 | QString name = m_option.outputFile; |
220 | |
221 | if (name.isEmpty()) { |
222 | name = QLatin1String("ui_" ); // ### use ui_ as prefix. |
223 | name.append(m_option.inputFile); |
224 | } |
225 | |
226 | return headerFileName(name); |
227 | } |
228 | |
229 | QString Driver::(const QString &fileName) |
230 | { |
231 | if (fileName.isEmpty()) |
232 | return headerFileName(QLatin1String("noname" )); |
233 | |
234 | QFileInfo info(fileName); |
235 | QString baseName = info.baseName(); |
236 | // Transform into a valid C++ identifier |
237 | if (!baseName.isEmpty() && baseName.at(0).isDigit()) |
238 | baseName.prepend(QLatin1Char('_')); |
239 | for (int i = 0; i < baseName.size(); ++i) { |
240 | QChar c = baseName.at(i); |
241 | if (!isAnsiCCharacter(c)) { |
242 | // Replace character by its unicode value |
243 | QString hex = QString::number(c.unicode(), 16); |
244 | baseName.replace(i, 1, QLatin1Char('_') + hex + QLatin1Char('_')); |
245 | i += hex.size() + 1; |
246 | } |
247 | } |
248 | return baseName.toUpper() + QLatin1String("_H" ); |
249 | } |
250 | |
251 | bool Driver::printDependencies(const QString &fileName) |
252 | { |
253 | Q_ASSERT(m_option.dependencies == true); |
254 | |
255 | m_option.inputFile = fileName; |
256 | |
257 | Uic tool(this); |
258 | return tool.printDependencies(); |
259 | } |
260 | |
261 | bool Driver::uic(const QString &fileName, DomUI *ui, QTextStream *out) |
262 | { |
263 | m_option.inputFile = fileName; |
264 | |
265 | QTextStream *oldOutput = m_output; |
266 | |
267 | m_output = out != 0 ? out : &m_stdout; |
268 | |
269 | Uic tool(this); |
270 | bool rtn = false; |
271 | #ifdef QT_UIC_CPP_GENERATOR |
272 | rtn = tool.write(ui); |
273 | #else |
274 | Q_UNUSED(ui); |
275 | fprintf(stderr, "uic: option to generate cpp code not compiled in [%s:%d]\n" , |
276 | __FILE__, __LINE__); |
277 | #endif |
278 | |
279 | m_output = oldOutput; |
280 | |
281 | return rtn; |
282 | } |
283 | |
284 | bool Driver::uic(const QString &fileName, QTextStream *out) |
285 | { |
286 | QFile f; |
287 | if (fileName.isEmpty()) |
288 | f.open(stdin, QIODevice::ReadOnly); |
289 | else { |
290 | f.setFileName(fileName); |
291 | if (!f.open(QIODevice::ReadOnly)) |
292 | return false; |
293 | } |
294 | |
295 | m_option.inputFile = fileName; |
296 | |
297 | QTextStream *oldOutput = m_output; |
298 | bool deleteOutput = false; |
299 | |
300 | if (out) { |
301 | m_output = out; |
302 | } else { |
303 | #ifdef Q_WS_WIN |
304 | // As one might also redirect the output to a file on win, |
305 | // we should not create the textstream with QFile::Text flag. |
306 | // The redirected file is opened in TextMode and this will |
307 | // result in broken line endings as writing will replace \n again. |
308 | m_output = new QTextStream(stdout, QIODevice::WriteOnly); |
309 | #else |
310 | m_output = new QTextStream(stdout, QIODevice::WriteOnly | QFile::Text); |
311 | #endif |
312 | deleteOutput = true; |
313 | } |
314 | |
315 | Uic tool(this); |
316 | bool rtn = tool.write(&f); |
317 | f.close(); |
318 | |
319 | if (deleteOutput) |
320 | delete m_output; |
321 | |
322 | m_output = oldOutput; |
323 | |
324 | return rtn; |
325 | } |
326 | |
327 | void Driver::reset() |
328 | { |
329 | Q_ASSERT( m_output == 0 ); |
330 | |
331 | m_option = Option(); |
332 | m_output = 0; |
333 | m_problems.clear(); |
334 | |
335 | QStringList m_problems; |
336 | |
337 | m_widgets.clear(); |
338 | m_spacers.clear(); |
339 | m_layouts.clear(); |
340 | m_actionGroups.clear(); |
341 | m_actions.clear(); |
342 | m_nameRepository.clear(); |
343 | m_pixmaps.clear(); |
344 | } |
345 | |
346 | void Driver::insertPixmap(const QString &pixmap) |
347 | { |
348 | m_pixmaps.insert(pixmap, true); |
349 | } |
350 | |
351 | bool Driver::containsPixmap(const QString &pixmap) const |
352 | { |
353 | return m_pixmaps.contains(pixmap); |
354 | } |
355 | |
356 | DomWidget *Driver::widgetByName(const QString &name) const |
357 | { |
358 | return m_widgets.key(name); |
359 | } |
360 | |
361 | DomSpacer *Driver::spacerByName(const QString &name) const |
362 | { |
363 | return m_spacers.key(name); |
364 | } |
365 | |
366 | DomLayout *Driver::layoutByName(const QString &name) const |
367 | { |
368 | return m_layouts.key(name); |
369 | } |
370 | |
371 | DomActionGroup *Driver::actionGroupByName(const QString &name) const |
372 | { |
373 | return m_actionGroups.key(name); |
374 | } |
375 | |
376 | DomAction *Driver::actionByName(const QString &name) const |
377 | { |
378 | return m_actions.key(name); |
379 | } |
380 | |
381 | QT_END_NAMESPACE |
382 | |