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 "cppwriteincludes.h" |
30 | #include "driver.h" |
31 | #include "ui4.h" |
32 | #include "uic.h" |
33 | #include "databaseinfo.h" |
34 | |
35 | #include <qdebug.h> |
36 | #include <qfileinfo.h> |
37 | #include <qtextstream.h> |
38 | |
39 | #include <stdio.h> |
40 | |
41 | QT_BEGIN_NAMESPACE |
42 | |
43 | enum { debugWriteIncludes = 0 }; |
44 | enum { = 0 }; |
45 | |
46 | struct ClassInfoEntry |
47 | { |
48 | const char *klass; |
49 | const char *module; |
50 | const char *; |
51 | }; |
52 | |
53 | static const ClassInfoEntry qclass_lib_map[] = { |
54 | #define QT_CLASS_LIB(klass, module, header) { #klass, #module, #header }, |
55 | #include "qclass_lib_map.h" |
56 | |
57 | #undef QT_CLASS_LIB |
58 | }; |
59 | |
60 | // Format a module header as 'QtCore/QObject' |
61 | static inline QString (const QString &module, const QString &) |
62 | { |
63 | QString rc = module; |
64 | rc += QLatin1Char('/'); |
65 | rc += header; |
66 | return rc; |
67 | } |
68 | |
69 | namespace CPP { |
70 | |
71 | WriteIncludes::WriteIncludes(Uic *uic) |
72 | : m_uic(uic), m_output(uic->output()) |
73 | { |
74 | // When possible (no namespace) use the "QtModule/QClass" convention |
75 | // and create a re-mapping of the old header "qclass.h" to it. Do not do this |
76 | // for the "Phonon::Someclass" classes, however. |
77 | const QString namespaceDelimiter = QLatin1String("::" ); |
78 | const ClassInfoEntry *classLibEnd = qclass_lib_map + sizeof(qclass_lib_map)/sizeof(ClassInfoEntry); |
79 | for (const ClassInfoEntry *it = qclass_lib_map; it < classLibEnd; ++it) { |
80 | const QString klass = QLatin1String(it->klass); |
81 | const QString module = QLatin1String(it->module); |
82 | QLatin1String = QLatin1String(it->header); |
83 | if (klass.contains(namespaceDelimiter)) { |
84 | m_classToHeader.insert(klass, moduleHeader(module, header)); |
85 | } else { |
86 | const QString = moduleHeader(module, klass); |
87 | m_classToHeader.insert(klass, newHeader); |
88 | m_oldHeaderToNewHeader.insert(header, newHeader); |
89 | } |
90 | } |
91 | } |
92 | |
93 | void WriteIncludes::acceptUI(DomUI *node) |
94 | { |
95 | m_laidOut = false; |
96 | m_localIncludes.clear(); |
97 | m_globalIncludes.clear(); |
98 | m_knownClasses.clear(); |
99 | m_includeBaseNames.clear(); |
100 | |
101 | if (node->elementIncludes()) |
102 | acceptIncludes(node->elementIncludes()); |
103 | |
104 | if (node->elementCustomWidgets()) |
105 | TreeWalker::acceptCustomWidgets(node->elementCustomWidgets()); |
106 | |
107 | add(QLatin1String("QApplication" )); |
108 | add(QLatin1String("QVariant" )); |
109 | |
110 | if (node->elementButtonGroups()) |
111 | add(QLatin1String("QButtonGroup" )); |
112 | |
113 | TreeWalker::acceptUI(node); |
114 | |
115 | const auto includeFile = m_uic->option().includeFile; |
116 | if (!includeFile.isEmpty()) |
117 | m_globalIncludes.insert(includeFile); |
118 | |
119 | writeHeaders(m_globalIncludes, true); |
120 | writeHeaders(m_localIncludes, false); |
121 | |
122 | m_output << '\n'; |
123 | } |
124 | |
125 | void WriteIncludes::acceptWidget(DomWidget *node) |
126 | { |
127 | if (debugWriteIncludes) |
128 | fprintf(stderr, "%s '%s'\n" , Q_FUNC_INFO, qPrintable(node->attributeClass())); |
129 | |
130 | add(node->attributeClass()); |
131 | TreeWalker::acceptWidget(node); |
132 | } |
133 | |
134 | void WriteIncludes::acceptLayout(DomLayout *node) |
135 | { |
136 | add(node->attributeClass()); |
137 | m_laidOut = true; |
138 | TreeWalker::acceptLayout(node); |
139 | } |
140 | |
141 | void WriteIncludes::acceptSpacer(DomSpacer *node) |
142 | { |
143 | add(QLatin1String("QSpacerItem" )); |
144 | TreeWalker::acceptSpacer(node); |
145 | } |
146 | |
147 | void WriteIncludes::acceptProperty(DomProperty *node) |
148 | { |
149 | if (node->kind() == DomProperty::Date) |
150 | add(QLatin1String("QDate" )); |
151 | if (node->kind() == DomProperty::Locale) |
152 | add(QLatin1String("QLocale" )); |
153 | if (node->kind() == DomProperty::IconSet) |
154 | add(QLatin1String("QIcon" )); |
155 | TreeWalker::acceptProperty(node); |
156 | } |
157 | |
158 | void WriteIncludes::insertIncludeForClass(const QString &className, QString , bool global) |
159 | { |
160 | if (debugWriteIncludes) |
161 | fprintf(stderr, "%s %s '%s' %d\n" , Q_FUNC_INFO, qPrintable(className), qPrintable(header), global); |
162 | |
163 | do { |
164 | if (!header.isEmpty()) |
165 | break; |
166 | |
167 | // Known class |
168 | const StringMap::const_iterator it = m_classToHeader.constFind(className); |
169 | if (it != m_classToHeader.constEnd()) { |
170 | header = it.value(); |
171 | global = true; |
172 | break; |
173 | } |
174 | |
175 | // Quick check by class name to detect includehints provided for custom widgets. |
176 | // Remove namespaces |
177 | QString lowerClassName = className.toLower(); |
178 | static const QString namespaceSeparator = QLatin1String("::" ); |
179 | const int namespaceIndex = lowerClassName.lastIndexOf(namespaceSeparator); |
180 | if (namespaceIndex != -1) |
181 | lowerClassName.remove(0, namespaceIndex + namespaceSeparator.size()); |
182 | if (m_includeBaseNames.contains(lowerClassName)) { |
183 | header.clear(); |
184 | break; |
185 | } |
186 | |
187 | // Last resort: Create default header |
188 | if (!m_uic->option().implicitIncludes) |
189 | break; |
190 | header = lowerClassName; |
191 | header += QLatin1String(".h" ); |
192 | if (warnHeaderGeneration) { |
193 | qWarning("%s: Warning: generated header '%s' for class '%s'." , |
194 | qPrintable(m_uic->option().messagePrefix()), |
195 | qPrintable(header), qPrintable(className)); |
196 | |
197 | } |
198 | |
199 | global = true; |
200 | } while (false); |
201 | |
202 | if (!header.isEmpty()) |
203 | insertInclude(header, global); |
204 | } |
205 | |
206 | void WriteIncludes::add(const QString &className, bool , const QString &, bool global) |
207 | { |
208 | if (debugWriteIncludes) |
209 | fprintf(stderr, "%s %s '%s' %d\n" , Q_FUNC_INFO, qPrintable(className), qPrintable(header), global); |
210 | |
211 | if (className.isEmpty() || m_knownClasses.contains(className)) |
212 | return; |
213 | |
214 | m_knownClasses.insert(className); |
215 | |
216 | const CustomWidgetsInfo *cwi = m_uic->customWidgetsInfo(); |
217 | static const QStringList = { |
218 | QLatin1String("QTreeView" ), QLatin1String("QTreeWidget" ), |
219 | QLatin1String("QTableView" ), QLatin1String("QTableWidget" ) |
220 | }; |
221 | if (cwi->extendsOneOf(className, treeViewsWithHeaders)) |
222 | add(QLatin1String("QHeaderView" )); |
223 | |
224 | if (!m_laidOut && cwi->extends(className, QLatin1String("QToolBox" ))) |
225 | add(QLatin1String("QLayout" )); // spacing property of QToolBox) |
226 | |
227 | if (className == QLatin1String("Line" )) { // ### hmm, deprecate me! |
228 | add(QLatin1String("QFrame" )); |
229 | return; |
230 | } |
231 | |
232 | if (cwi->extends(className, QLatin1String("QDialogButtonBox" ))) |
233 | add(QLatin1String("QAbstractButton" )); // for signal "clicked(QAbstractButton*)" |
234 | |
235 | if (determineHeader) |
236 | insertIncludeForClass(className, header, global); |
237 | } |
238 | |
239 | void WriteIncludes::acceptCustomWidget(DomCustomWidget *node) |
240 | { |
241 | const QString className = node->elementClass(); |
242 | if (className.isEmpty()) |
243 | return; |
244 | |
245 | if (!node->elementHeader() || node->elementHeader()->text().isEmpty()) { |
246 | add(className, false); // no header specified |
247 | } else { |
248 | // custom header unless it is a built-in qt class |
249 | QString ; |
250 | bool global = false; |
251 | if (!m_classToHeader.contains(className)) { |
252 | global = node->elementHeader()->attributeLocation().toLower() == QLatin1String("global" ); |
253 | header = node->elementHeader()->text(); |
254 | } |
255 | add(className, true, header, global); |
256 | } |
257 | } |
258 | |
259 | void WriteIncludes::acceptActionGroup(DomActionGroup *node) |
260 | { |
261 | add(QLatin1String("QActionGroup" )); |
262 | TreeWalker::acceptActionGroup(node); |
263 | } |
264 | |
265 | void WriteIncludes::acceptAction(DomAction *node) |
266 | { |
267 | add(QLatin1String("QAction" )); |
268 | TreeWalker::acceptAction(node); |
269 | } |
270 | |
271 | void WriteIncludes::acceptActionRef(DomActionRef *node) |
272 | { |
273 | add(QLatin1String("QAction" )); |
274 | TreeWalker::acceptActionRef(node); |
275 | } |
276 | |
277 | void WriteIncludes::acceptCustomWidgets(DomCustomWidgets *node) |
278 | { |
279 | Q_UNUSED(node); |
280 | } |
281 | |
282 | void WriteIncludes::acceptIncludes(DomIncludes *node) |
283 | { |
284 | TreeWalker::acceptIncludes(node); |
285 | } |
286 | |
287 | void WriteIncludes::acceptInclude(DomInclude *node) |
288 | { |
289 | bool global = true; |
290 | if (node->hasAttributeLocation()) |
291 | global = node->attributeLocation() == QLatin1String("global" ); |
292 | insertInclude(node->text(), global); |
293 | } |
294 | |
295 | void WriteIncludes::insertInclude(const QString &, bool global) |
296 | { |
297 | if (debugWriteIncludes) |
298 | fprintf(stderr, "%s %s %d\n" , Q_FUNC_INFO, qPrintable(header), global); |
299 | |
300 | OrderedSet &includes = global ? m_globalIncludes : m_localIncludes; |
301 | // Insert (if not already done). |
302 | const bool = includes.insert(header).second; |
303 | if (!isNewHeader) |
304 | return; |
305 | // Also remember base name for quick check of suspicious custom plugins |
306 | const QString lowerBaseName = QFileInfo(header).completeBaseName ().toLower(); |
307 | m_includeBaseNames.insert(lowerBaseName); |
308 | } |
309 | |
310 | void WriteIncludes::(const OrderedSet &, bool global) |
311 | { |
312 | const QChar openingQuote = global ? QLatin1Char('<') : QLatin1Char('"'); |
313 | const QChar closingQuote = global ? QLatin1Char('>') : QLatin1Char('"'); |
314 | |
315 | // Check for the old headers 'qslider.h' and replace by 'QtGui/QSlider' |
316 | for (const QString & : headers) { |
317 | const QString value = m_oldHeaderToNewHeader.value(header, header); |
318 | const auto trimmed = QStringView(value).trimmed(); |
319 | if (!trimmed.isEmpty()) |
320 | m_output << "#include " << openingQuote << trimmed << closingQuote << '\n'; |
321 | } |
322 | } |
323 | |
324 | } // namespace CPP |
325 | |
326 | QT_END_NAMESPACE |
327 | |