Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qdesigner_promotion.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3// Qt-Security score:significant reason:default
4
9
10#include <QtDesigner/abstractformeditor.h>
11#include <QtDesigner/abstractformwindow.h>
12#include <QtDesigner/abstractformwindowmanager.h>
13#include <QtDesigner/abstractobjectinspector.h>
14#include <QtDesigner/abstractwidgetbox.h>
15#include <QtDesigner/abstractwidgetdatabase.h>
16
17#include <QtCore/qmap.h>
18#include <QtCore/qcoreapplication.h>
19#include <qdebug.h>
20
22
23using namespace Qt::StringLiterals;
24
25namespace {
26 // Return a set of on-promotable classes
27 const QSet<QString> &nonPromotableClasses() {
28 static const QSet<QString> rc = {
29 u"Line"_s,
30 u"QAction"_s,
31 u"Spacer"_s,
32 u"QMainWindow"_s,
33 u"QDialog"_s,
34 u"QMdiArea"_s,
35 u"QMdiSubWindow"_s
36 };
37 return rc;
38 }
39
40 // Return widget database index of a promoted class or -1 with error message
41 int promotedWidgetDataBaseIndex(const QDesignerWidgetDataBaseInterface *widgetDataBase,
42 const QString &className,
43 QString *errorMessage) {
44 const int index = widgetDataBase->indexOfClassName(className);
45 if (index == -1 || !widgetDataBase->item(index)->isPromoted()) {
46 *errorMessage = QCoreApplication::tr("%1 is not a promoted class.").arg(className);
47 return -1;
48 }
49 return index;
50 }
51
52 // Return widget database item of a promoted class or 0 with error message
53 QDesignerWidgetDataBaseItemInterface *promotedWidgetDataBaseItem(const QDesignerWidgetDataBaseInterface *widgetDataBase,
54 const QString &className,
55 QString *errorMessage) {
56
57 const int index = promotedWidgetDataBaseIndex(widgetDataBase, className, errorMessage);
58 if (index == -1)
59 return nullptr;
60 return widgetDataBase->item(index);
61 }
62
63 // extract class name from xml "<widget class="QWidget" ...>". Quite a hack.
64 QString classNameFromXml(QString xml)
65 {
66 constexpr auto tag = "class=\""_L1;
67 const int pos = xml.indexOf(tag);
68 if (pos == -1)
69 return QString();
70 xml.remove(0, pos + tag.size());
71 const auto closingPos = xml.indexOf(u'"');
72 if (closingPos == -1)
73 return QString();
74 xml.remove(closingPos, xml.size() - closingPos);
75 return xml;
76 }
77
78 // return a list of class names in the scratch pad
79 QStringList getScratchPadClasses(const QDesignerWidgetBoxInterface *wb) {
80 QStringList rc;
81 const int catCount = wb->categoryCount();
82 for (int c = 0; c < catCount; c++) {
83 const QDesignerWidgetBoxInterface::Category category = wb->category(c);
84 if (category.type() == QDesignerWidgetBoxInterface::Category::Scratchpad) {
85 const int widgetCount = category.widgetCount();
86 for (int w = 0; w < widgetCount; w++) {
87 const QString className = classNameFromXml( category.widget(w).domXml());
88 if (!className.isEmpty())
89 rc += className;
90 }
91 }
92 }
93 return rc;
94 }
95}
96
97static void markFormsDirty(const QDesignerFormEditorInterface *core)
98{
99 const QDesignerFormWindowManagerInterface *fwm = core->formWindowManager();
100 for (int f = 0, count = fwm->formWindowCount(); f < count; ++f)
101 fwm->formWindow(f)->setDirty(true);
102}
103
104namespace qdesigner_internal {
105
109
111 const QString &className,
112 const QString &includeFile,
114 {
117
118 if (baseClassIndex == -1) {
119 *errorMessage = QCoreApplication::tr("The base class %1 is invalid.").arg(baseClass);
120 return false;
121 }
122
124
125 if (existingClassIndex != -1) {
126 *errorMessage = QCoreApplication::tr("The class %1 already exists.").arg(className);
127 return false;
128 }
129 // Clone derived item.
131 // Also inherit the container flag in case of QWidget-derived classes
132 // as it is most likely intended for stacked pages.
133 // set new props
135 promotedItem->setGroup(QCoreApplication::tr("Promoted Widgets"));
136 promotedItem->setCustom(true);
142 return true;
143 }
144
162
163
165 {
166 if (dbItem->isPromoted() || !dbItem->extends().isEmpty())
167 return false;
168
169 const QString name = dbItem->name();
170
172 return false;
173
174 if (name.startsWith("QDesigner"_L1) || name.startsWith("QLayout"_L1))
175 return false;
176
177 return true;
178 }
179
181 {
183 // A map containing base classes and their promoted classes.
185
187 // Look for promoted classes and insert into map according to base class.
188 const int cnt = widgetDataBase->count();
189 for (int i = 0; i < cnt; i++) {
191 if (dbItem->isPromoted()) {
194 if (it == baseClassPromotedMap.end()) {
196 }
198 }
199 }
200 // convert map into list.
202
204 return rc;
205
208 Q_ASSERT(baseIndex >= 0);
210 // promoted
211 for (auto pit = bit.value().cbegin(), pcend = bit.value().cend(); pit != pcend; ++pit) {
216 }
217 }
218
219 return rc;
220 }
221
223 QSet<QString> rc;
225 if (!metaDataBase)
226 return rc;
227
229 for (QObject *object : objects) {
231 if (!customClass.isEmpty())
233
234 }
235 // check the scratchpad of the widget box
238 if (!scratchPadClasses.isEmpty()) {
239 // Check whether these are actually promoted
241 for (const auto &scItem : scratchPadClasses) {
243 if (index != -1 && widgetDataBase->item(index)->isPromoted())
245 }
246 }
247 }
248 return rc;
249 }
250
252 // check if it exists and is promoted
254 if (!widgetDataBase) {
255 *errorMessage = QCoreApplication::tr("The class %1 cannot be removed").arg(className);
256 return false;
257 }
258
260 if (index == -1)
261 return false;
262
264 *errorMessage = QCoreApplication::tr("The class %1 cannot be removed because it is still referenced.").arg(className);
265 return false;
266 }
267 // QTBUG-52963: Check for classes that specify the to-be-removed class as
268 // base class of a promoted class. This should not happen in the normal case
269 // as promoted classes cannot serve as base for further promotion. It is possible
270 // though if a class provided by a plugin (say Qt WebKit's QWebView) is used as
271 // a base class for a promoted widget B and the plugin is removed in the next
272 // launch. QWebView will then appear as promoted class itself and the promoted
273 // class B will depend on it. When removing QWebView, the base class of B will
274 // be changed to that of QWebView by the below code.
276 for (const auto &pc : promotedList) {
277 if (pc.baseItem->name() == className) {
279 qWarning().nospace() << "Warning: Promoted class " << pc.promotedItem->name()
280 << " extends " << className << ", changing its base class to " << extends << '.';
282 }
283 }
286 return true;
287 }
288
291 if (!metaDataBase) {
292 *errorMessage = QCoreApplication::tr("The class %1 cannot be renamed").arg(oldclassName);
293 return false;
294 }
296
297 // check the new name
298 if (newClassName.isEmpty()) {
299 *errorMessage = QCoreApplication::tr("The class %1 cannot be renamed to an empty name.").arg(oldclassName);
300 return false;
301 }
303 if (existingIndex != -1) {
304 *errorMessage = QCoreApplication::tr("There is already a class named %1.").arg(newClassName);
305 return false;
306 }
307 // Check old class
309 if (!dbItem)
310 return false;
311
312 // Change the name in the data base and change all referencing objects in the meta database
314 bool foundReferences = false;
316 for (QObject* object : dbObjects) {
318 Q_ASSERT(item);
321 foundReferences = true;
322 }
323 }
324 // set state
325 if (foundReferences)
327
329 return true;
330 }
331
333 // check file
334 if (includeFile.isEmpty()) {
335 *errorMessage = QCoreApplication::tr("Cannot set an empty include file.");
336 return false;
337 }
338 // check item
341 if (!dbItem)
342 return false;
343 if (dbItem->includeFile() != includeFile) {
346 }
347 return true;
348 }
349
355 }
356 }
357 }
358}
359
360QT_END_NAMESPACE
Combined button and popup list for selecting options.
Auxiliary methods to store/retrieve settings.
static void markFormsDirty(const QDesignerFormEditorInterface *core)