Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Toggle main menu visibility
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
5
#
include
"qdesigner_promotion_p.h"
6
#
include
"widgetdatabase_p.h"
7
#
include
"metadatabase_p.h"
8
#
include
"widgetdatabase_p.h"
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
21
QT_BEGIN_NAMESPACE
22
23
using
namespace
Qt::StringLiterals;
24
25
namespace
{
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
97
static
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
104
namespace
qdesigner_internal
{
105
106
QDesignerPromotion
::
QDesignerPromotion
(
QDesignerFormEditorInterface
*
core
) :
107
m_core
(
core
) {
108
}
109
110
bool
QDesignerPromotion
::
addPromotedClass
(
const
QString
&
baseClass
,
111
const
QString
&
className
,
112
const
QString
&
includeFile
,
113
QString
*
errorMessage
)
114
{
115
QDesignerWidgetDataBaseInterface
*
widgetDataBase
=
m_core
->
widgetDataBase
();
116
const
int
baseClassIndex
=
widgetDataBase
->
indexOfClassName
(
baseClass
);
117
118
if
(
baseClassIndex
== -1) {
119
*
errorMessage
=
QCoreApplication
::
tr
(
"The base class %1 is invalid."
).
arg
(
baseClass
);
120
return
false
;
121
}
122
123
const
int
existingClassIndex
=
widgetDataBase
->
indexOfClassName
(
className
);
124
125
if
(
existingClassIndex
!= -1) {
126
*
errorMessage
=
QCoreApplication
::
tr
(
"The class %1 already exists."
).
arg
(
className
);
127
return
false
;
128
}
129
// Clone derived item.
130
QDesignerWidgetDataBaseItemInterface
*
promotedItem
=
WidgetDataBaseItem
::
clone
(
widgetDataBase
->
item
(
baseClassIndex
));
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
134
promotedItem
->
setName
(
className
);
135
promotedItem
->
setGroup
(
QCoreApplication
::
tr
(
"Promoted Widgets"
));
136
promotedItem
->
setCustom
(
true
);
137
promotedItem
->
setPromoted
(
true
);
138
promotedItem
->
setExtends
(
baseClass
);
139
promotedItem
->
setIncludeFile
(
includeFile
);
140
widgetDataBase
->
append
(
promotedItem
);
141
markFormsDirty
(
m_core
);
142
return
true
;
143
}
144
145
QList
<
QDesignerWidgetDataBaseItemInterface
*>
QDesignerPromotion
::
promotionBaseClasses
()
const
146
{
147
using
SortedDatabaseItemMap
=
QMap
<
QString
,
QDesignerWidgetDataBaseItemInterface
*>;
148
SortedDatabaseItemMap
sortedDatabaseItemMap
;
149
150
QDesignerWidgetDataBaseInterface
*
widgetDataBase
=
m_core
->
widgetDataBase
();
151
152
const
int
cnt
=
widgetDataBase
->
count
();
153
for
(
int
i
= 0;
i
<
cnt
;
i
++) {
154
QDesignerWidgetDataBaseItemInterface
*
dbItem
=
widgetDataBase
->
item
(
i
);
155
if
(
canBePromoted
(
dbItem
)) {
156
sortedDatabaseItemMap
.
insert
(
dbItem
->
name
(),
dbItem
);
157
}
158
}
159
160
return
sortedDatabaseItemMap
.
values
();
161
}
162
163
164
bool
QDesignerPromotion
::
canBePromoted
(
const
QDesignerWidgetDataBaseItemInterface
*
dbItem
)
const
165
{
166
if
(
dbItem
->
isPromoted
() || !
dbItem
->
extends
().
isEmpty
())
167
return
false
;
168
169
const
QString
name
=
dbItem
->
name
();
170
171
if
(
nonPromotableClasses
().
contains
(
name
))
172
return
false
;
173
174
if
(
name
.
startsWith
(
"QDesigner"_L1
) ||
name
.
startsWith
(
"QLayout"_L1
))
175
return
false
;
176
177
return
true
;
178
}
179
180
QDesignerPromotion
::
PromotedClasses
QDesignerPromotion
::
promotedClasses
()
const
181
{
182
using
ClassNameItemMap
=
QMap
<
QString
,
QDesignerWidgetDataBaseItemInterface
*>;
183
// A map containing base classes and their promoted classes.
184
QMap
<
QString
,
ClassNameItemMap
>
baseClassPromotedMap
;
185
186
QDesignerWidgetDataBaseInterface
*
widgetDataBase
=
m_core
->
widgetDataBase
();
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
++) {
190
QDesignerWidgetDataBaseItemInterface
*
dbItem
=
widgetDataBase
->
item
(
i
);
191
if
(
dbItem
->
isPromoted
()) {
192
const
QString
baseClassName
=
dbItem
->
extends
();
193
auto
it
=
baseClassPromotedMap
.
find
(
baseClassName
);
194
if
(
it
==
baseClassPromotedMap
.
end
()) {
195
it
=
baseClassPromotedMap
.
insert
(
baseClassName
,
ClassNameItemMap
());
196
}
197
it
.
value
().
insert
(
dbItem
->
name
(),
dbItem
);
198
}
199
}
200
// convert map into list.
201
PromotedClasses
rc
;
202
203
if
(
baseClassPromotedMap
.
isEmpty
())
204
return
rc
;
205
206
for
(
auto
bit
=
baseClassPromotedMap
.
cbegin
(),
bcend
=
baseClassPromotedMap
.
cend
();
bit
!=
bcend
; ++
bit
) {
207
const
int
baseIndex
=
widgetDataBase
->
indexOfClassName
(
bit
.
key
());
208
Q_ASSERT
(
baseIndex
>= 0);
209
QDesignerWidgetDataBaseItemInterface
*
baseItem
=
widgetDataBase
->
item
(
baseIndex
);
210
// promoted
211
for
(
auto
pit
=
bit
.
value
().
cbegin
(),
pcend
=
bit
.
value
().
cend
();
pit
!=
pcend
; ++
pit
) {
212
PromotedClass
item
;
213
item
.
baseItem
=
baseItem
;
214
item
.
promotedItem
=
pit
.
value
();
215
rc
.
push_back
(
item
);
216
}
217
}
218
219
return
rc
;
220
}
221
222
QSet
<
QString
>
QDesignerPromotion
::
referencedPromotedClassNames
()
const
{
223
QSet
<
QString
>
rc
;
224
const
MetaDataBase
*
metaDataBase
=
qobject_cast
<
const
MetaDataBase
*>(
m_core
->
metaDataBase
());
225
if
(!
metaDataBase
)
226
return
rc
;
227
228
const
QObjectList
&
objects
=
metaDataBase
->
objects
();
229
for
(
QObject
*
object
:
objects
) {
230
const
QString
customClass
=
metaDataBase
->
metaDataBaseItem
(
object
)->
customClassName
();
231
if
(!
customClass
.
isEmpty
())
232
rc
.
insert
(
customClass
);
233
234
}
235
// check the scratchpad of the widget box
236
if
(
QDesignerWidgetBoxInterface
*
widgetBox
=
m_core
->
widgetBox
()) {
237
const
QStringList
scratchPadClasses
=
getScratchPadClasses
(
widgetBox
);
238
if
(!
scratchPadClasses
.
isEmpty
()) {
239
// Check whether these are actually promoted
240
QDesignerWidgetDataBaseInterface
*
widgetDataBase
=
m_core
->
widgetDataBase
();
241
for
(
const
auto
&
scItem
:
scratchPadClasses
) {
242
const
int
index
=
widgetDataBase
->
indexOfClassName
(
scItem
);
243
if
(
index
!= -1 &&
widgetDataBase
->
item
(
index
)->
isPromoted
())
244
rc
.
insert
(
scItem
);
245
}
246
}
247
}
248
return
rc
;
249
}
250
251
bool
QDesignerPromotion
::
removePromotedClass
(
const
QString
&
className
,
QString
*
errorMessage
) {
252
// check if it exists and is promoted
253
WidgetDataBase
*
widgetDataBase
=
qobject_cast
<
WidgetDataBase
*>(
m_core
->
widgetDataBase
());
254
if
(!
widgetDataBase
) {
255
*
errorMessage
=
QCoreApplication
::
tr
(
"The class %1 cannot be removed"
).
arg
(
className
);
256
return
false
;
257
}
258
259
const
int
index
=
promotedWidgetDataBaseIndex
(
widgetDataBase
,
className
,
errorMessage
);
260
if
(
index
== -1)
261
return
false
;
262
263
if
(
referencedPromotedClassNames
().
contains
(
className
)) {
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.
275
const
PromotedClasses
promotedList
=
promotedClasses
();
276
for
(
const
auto
&
pc
:
promotedList
) {
277
if
(
pc
.
baseItem
->
name
() ==
className
) {
278
const
QString
extends
=
widgetDataBase
->
item
(
index
)->
extends
();
279
qWarning
().
nospace
() <<
"Warning: Promoted class "
<<
pc
.
promotedItem
->
name
()
280
<<
" extends "
<<
className
<<
", changing its base class to "
<<
extends
<<
'.'
;
281
pc
.
promotedItem
->
setExtends
(
extends
);
282
}
283
}
284
widgetDataBase
->
remove
(
index
);
285
markFormsDirty
(
m_core
);
286
return
true
;
287
}
288
289
bool
QDesignerPromotion
::
changePromotedClassName
(
const
QString
&
oldclassName
,
const
QString
&
newClassName
,
QString
*
errorMessage
) {
290
const
MetaDataBase
*
metaDataBase
=
qobject_cast
<
const
MetaDataBase
*>(
m_core
->
metaDataBase
());
291
if
(!
metaDataBase
) {
292
*
errorMessage
=
QCoreApplication
::
tr
(
"The class %1 cannot be renamed"
).
arg
(
oldclassName
);
293
return
false
;
294
}
295
QDesignerWidgetDataBaseInterface
*
widgetDataBase
=
m_core
->
widgetDataBase
();
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
}
302
const
int
existingIndex
=
widgetDataBase
->
indexOfClassName
(
newClassName
);
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
308
QDesignerWidgetDataBaseItemInterface
*
dbItem
=
promotedWidgetDataBaseItem
(
widgetDataBase
,
oldclassName
,
errorMessage
);
309
if
(!
dbItem
)
310
return
false
;
311
312
// Change the name in the data base and change all referencing objects in the meta database
313
dbItem
->
setName
(
newClassName
);
314
bool
foundReferences
=
false
;
315
const
QObjectList
&
dbObjects
=
metaDataBase
->
objects
();
316
for
(
QObject
*
object
:
dbObjects
) {
317
MetaDataBaseItem
*
item
=
metaDataBase
->
metaDataBaseItem
(
object
);
318
Q_ASSERT
(
item
);
319
if
(
item
->
customClassName
() ==
oldclassName
) {
320
item
->
setCustomClassName
(
newClassName
);
321
foundReferences
=
true
;
322
}
323
}
324
// set state
325
if
(
foundReferences
)
326
refreshObjectInspector
();
327
328
markFormsDirty
(
m_core
);
329
return
true
;
330
}
331
332
bool
QDesignerPromotion
::
setPromotedClassIncludeFile
(
const
QString
&
className
,
const
QString
&
includeFile
,
QString
*
errorMessage
) {
333
// check file
334
if
(
includeFile
.
isEmpty
()) {
335
*
errorMessage
=
QCoreApplication
::
tr
(
"Cannot set an empty include file."
);
336
return
false
;
337
}
338
// check item
339
QDesignerWidgetDataBaseInterface
*
widgetDataBase
=
m_core
->
widgetDataBase
();
340
QDesignerWidgetDataBaseItemInterface
*
dbItem
=
promotedWidgetDataBaseItem
(
widgetDataBase
,
className
,
errorMessage
);
341
if
(!
dbItem
)
342
return
false
;
343
if
(
dbItem
->
includeFile
() !=
includeFile
) {
344
dbItem
->
setIncludeFile
(
includeFile
);
345
markFormsDirty
(
m_core
);
346
}
347
return
true
;
348
}
349
350
void
QDesignerPromotion
::
refreshObjectInspector
() {
351
if
(
QDesignerFormWindowManagerInterface
*
fwm
=
m_core
->
formWindowManager
()) {
352
if
(
QDesignerFormWindowInterface
*
fw
=
fwm
->
activeFormWindow
())
353
if
(
QDesignerObjectInspectorInterface
*
oi
=
m_core
->
objectInspector
()) {
354
oi
->
setFormWindow
(
fw
);
355
}
356
}
357
}
358
}
359
360
QT_END_NAMESPACE
QT_BEGIN_NAMESPACE
Combined button and popup list for selecting options.
Definition
qsequentialanimationgroup.cpp:47
qdesigner_internal
Auxiliary methods to store/retrieve settings.
Definition
buddyeditor.cpp:67
markFormsDirty
static void markFormsDirty(const QDesignerFormEditorInterface *core)
Definition
qdesigner_promotion.cpp:97
qttools
src
designer
src
lib
shared
qdesigner_promotion.cpp
Generated on
for Qt by
1.18.0