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
8
9#include <QtDesigner/abstractformeditor.h>
10#include <QtDesigner/abstractformwindow.h>
11#include <QtDesigner/abstractformwindowmanager.h>
12#include <QtDesigner/abstractobjectinspector.h>
13#include <QtDesigner/abstractwidgetbox.h>
14#include <QtDesigner/abstractwidgetdatabase.h>
15
16#include <QtCore/qmap.h>
17#include <QtCore/qcoreapplication.h>
18#include <qdebug.h>
19
21
22using namespace Qt::StringLiterals;
23
24namespace {
25 // Return a set of on-promotable classes
26 const QSet<QString> &nonPromotableClasses() {
27 static const QSet<QString> rc = {
28 u"Line"_s,
29 u"QAction"_s,
30 u"Spacer"_s,
31 u"QMainWindow"_s,
32 u"QDialog"_s,
33 u"QMdiArea"_s,
34 u"QMdiSubWindow"_s
35 };
36 return rc;
37 }
38
39 // Return widget database index of a promoted class or -1 with error message
40 int promotedWidgetDataBaseIndex(const QDesignerWidgetDataBaseInterface *widgetDataBase,
41 const QString &className,
42 QString *errorMessage) {
43 const int index = widgetDataBase->indexOfClassName(className);
44 if (index == -1 || !widgetDataBase->item(index)->isPromoted()) {
45 *errorMessage = QCoreApplication::tr("%1 is not a promoted class.").arg(className);
46 return -1;
47 }
48 return index;
49 }
50
51 // Return widget database item of a promoted class or 0 with error message
52 QDesignerWidgetDataBaseItemInterface *promotedWidgetDataBaseItem(const QDesignerWidgetDataBaseInterface *widgetDataBase,
53 const QString &className,
54 QString *errorMessage) {
55
56 const int index = promotedWidgetDataBaseIndex(widgetDataBase, className, errorMessage);
57 if (index == -1)
58 return nullptr;
59 return widgetDataBase->item(index);
60 }
61
62 // extract class name from xml "<widget class="QWidget" ...>". Quite a hack.
63 QString classNameFromXml(QString xml)
64 {
65 constexpr auto tag = "class=\""_L1;
66 const int pos = xml.indexOf(tag);
67 if (pos == -1)
68 return QString();
69 xml.remove(0, pos + tag.size());
70 const auto closingPos = xml.indexOf(u'"');
71 if (closingPos == -1)
72 return QString();
73 xml.remove(closingPos, xml.size() - closingPos);
74 return xml;
75 }
76
77 // return a list of class names in the scratch pad
78 QStringList getScratchPadClasses(const QDesignerWidgetBoxInterface *wb) {
79 QStringList rc;
80 const int catCount = wb->categoryCount();
81 for (int c = 0; c < catCount; c++) {
82 const QDesignerWidgetBoxInterface::Category category = wb->category(c);
83 if (category.type() == QDesignerWidgetBoxInterface::Category::Scratchpad) {
84 const int widgetCount = category.widgetCount();
85 for (int w = 0; w < widgetCount; w++) {
86 const QString className = classNameFromXml( category.widget(w).domXml());
87 if (!className.isEmpty())
88 rc += className;
89 }
90 }
91 }
92 return rc;
93 }
94}
95
96static void markFormsDirty(const QDesignerFormEditorInterface *core)
97{
98 const QDesignerFormWindowManagerInterface *fwm = core->formWindowManager();
99 for (int f = 0, count = fwm->formWindowCount(); f < count; ++f)
100 fwm->formWindow(f)->setDirty(true);
101}
102
103namespace qdesigner_internal {
104
108
110 const QString &className,
111 const QString &includeFile,
113 {
116
117 if (baseClassIndex == -1) {
118 *errorMessage = QCoreApplication::tr("The base class %1 is invalid.").arg(baseClass);
119 return false;
120 }
121
123
124 if (existingClassIndex != -1) {
125 *errorMessage = QCoreApplication::tr("The class %1 already exists.").arg(className);
126 return false;
127 }
128 // Clone derived item.
130 // Also inherit the container flag in case of QWidget-derived classes
131 // as it is most likely intended for stacked pages.
132 // set new props
134 promotedItem->setGroup(QCoreApplication::tr("Promoted Widgets"));
135 promotedItem->setCustom(true);
141 return true;
142 }
143
161
162
164 {
165 if (dbItem->isPromoted() || !dbItem->extends().isEmpty())
166 return false;
167
168 const QString name = dbItem->name();
169
171 return false;
172
173 if (name.startsWith("QDesigner"_L1) || name.startsWith("QLayout"_L1))
174 return false;
175
176 return true;
177 }
178
180 {
182 // A map containing base classes and their promoted classes.
184
186 // Look for promoted classes and insert into map according to base class.
187 const int cnt = widgetDataBase->count();
188 for (int i = 0; i < cnt; i++) {
190 if (dbItem->isPromoted()) {
193 if (it == baseClassPromotedMap.end()) {
195 }
197 }
198 }
199 // convert map into list.
201
203 return rc;
204
207 Q_ASSERT(baseIndex >= 0);
209 // promoted
210 for (auto pit = bit.value().cbegin(), pcend = bit.value().cend(); pit != pcend; ++pit) {
215 }
216 }
217
218 return rc;
219 }
220
222 QSet<QString> rc;
224 if (!metaDataBase)
225 return rc;
226
228 for (QObject *object : objects) {
230 if (!customClass.isEmpty())
232
233 }
234 // check the scratchpad of the widget box
237 if (!scratchPadClasses.isEmpty()) {
238 // Check whether these are actually promoted
240 for (const auto &scItem : scratchPadClasses) {
242 if (index != -1 && widgetDataBase->item(index)->isPromoted())
244 }
245 }
246 }
247 return rc;
248 }
249
251 // check if it exists and is promoted
253 if (!widgetDataBase) {
254 *errorMessage = QCoreApplication::tr("The class %1 cannot be removed").arg(className);
255 return false;
256 }
257
259 if (index == -1)
260 return false;
261
263 *errorMessage = QCoreApplication::tr("The class %1 cannot be removed because it is still referenced.").arg(className);
264 return false;
265 }
266 // QTBUG-52963: Check for classes that specify the to-be-removed class as
267 // base class of a promoted class. This should not happen in the normal case
268 // as promoted classes cannot serve as base for further promotion. It is possible
269 // though if a class provided by a plugin (say Qt WebKit's QWebView) is used as
270 // a base class for a promoted widget B and the plugin is removed in the next
271 // launch. QWebView will then appear as promoted class itself and the promoted
272 // class B will depend on it. When removing QWebView, the base class of B will
273 // be changed to that of QWebView by the below code.
275 for (const auto &pc : promotedList) {
276 if (pc.baseItem->name() == className) {
278 qWarning().nospace() << "Warning: Promoted class " << pc.promotedItem->name()
279 << " extends " << className << ", changing its base class to " << extends << '.';
281 }
282 }
285 return true;
286 }
287
290 if (!metaDataBase) {
291 *errorMessage = QCoreApplication::tr("The class %1 cannot be renamed").arg(oldclassName);
292 return false;
293 }
295
296 // check the new name
297 if (newClassName.isEmpty()) {
298 *errorMessage = QCoreApplication::tr("The class %1 cannot be renamed to an empty name.").arg(oldclassName);
299 return false;
300 }
302 if (existingIndex != -1) {
303 *errorMessage = QCoreApplication::tr("There is already a class named %1.").arg(newClassName);
304 return false;
305 }
306 // Check old class
308 if (!dbItem)
309 return false;
310
311 // Change the name in the data base and change all referencing objects in the meta database
313 bool foundReferences = false;
315 for (QObject* object : dbObjects) {
317 Q_ASSERT(item);
320 foundReferences = true;
321 }
322 }
323 // set state
324 if (foundReferences)
326
328 return true;
329 }
330
332 // check file
333 if (includeFile.isEmpty()) {
334 *errorMessage = QCoreApplication::tr("Cannot set an empty include file.");
335 return false;
336 }
337 // check item
340 if (!dbItem)
341 return false;
342 if (dbItem->includeFile() != includeFile) {
345 }
346 return true;
347 }
348
354 }
355 }
356 }
357}
358
359QT_END_NAMESPACE
Combined button and popup list for selecting options.
Auxiliary methods to store/retrieve settings.
static void markFormsDirty(const QDesignerFormEditorInterface *core)