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 Qt Designer 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 "qdesigner_formbuilder_p.h" |
30 | #include "dynamicpropertysheet.h" |
31 | #include "qsimpleresource_p.h" |
32 | #include "widgetfactory_p.h" |
33 | #include "qdesigner_introspection_p.h" |
34 | |
35 | #include <QtDesigner/private/ui4_p.h> |
36 | #include <QtDesigner/private/formbuilderextra_p.h> |
37 | // sdk |
38 | #include <QtDesigner/container.h> |
39 | #include <QtDesigner/propertysheet.h> |
40 | #include <QtDesigner/qextensionmanager.h> |
41 | #include <QtDesigner/abstractformeditor.h> |
42 | #include <QtDesigner/abstractformwindow.h> |
43 | #include <QtDesigner/abstractwidgetfactory.h> |
44 | #include <abstractdialoggui_p.h> |
45 | |
46 | #include <QtUiPlugin/customwidget.h> |
47 | |
48 | // shared |
49 | #include <qdesigner_propertysheet_p.h> |
50 | #include <qdesigner_utils_p.h> |
51 | #include <formwindowbase_p.h> |
52 | #include <qtresourcemodel_p.h> |
53 | |
54 | #include <QtWidgets/qwidget.h> |
55 | #include <QtWidgets/qmenu.h> |
56 | #include <QtWidgets/qtoolbar.h> |
57 | #include <QtWidgets/qmenubar.h> |
58 | #include <QtWidgets/qmainwindow.h> |
59 | #include <QtWidgets/qstylefactory.h> |
60 | #include <QtWidgets/qstyle.h> |
61 | #include <QtWidgets/qapplication.h> |
62 | #include <QtWidgets/qabstractscrollarea.h> |
63 | #include <QtWidgets/qmessagebox.h> |
64 | #include <QtGui/qpixmap.h> |
65 | |
66 | #include <QtCore/qbuffer.h> |
67 | #include <QtCore/qdebug.h> |
68 | #include <QtCore/qcoreapplication.h> |
69 | |
70 | QT_BEGIN_NAMESPACE |
71 | |
72 | namespace qdesigner_internal { |
73 | |
74 | QDesignerFormBuilder::QDesignerFormBuilder(QDesignerFormEditorInterface *core, |
75 | const DeviceProfile &deviceProfile) : |
76 | m_core(core), |
77 | m_deviceProfile(deviceProfile), |
78 | m_pixmapCache(nullptr), |
79 | m_iconCache(nullptr), |
80 | m_ignoreCreateResources(false), |
81 | m_tempResourceSet(nullptr), |
82 | m_mainWidget(true) |
83 | { |
84 | Q_ASSERT(m_core); |
85 | } |
86 | |
87 | QString QDesignerFormBuilder::systemStyle() const |
88 | { |
89 | return m_deviceProfile.isEmpty() ? |
90 | QString::fromUtf8(str: QApplication::style()->metaObject()->className()) : |
91 | m_deviceProfile.style(); |
92 | } |
93 | |
94 | QWidget *QDesignerFormBuilder::create(DomUI *ui, QWidget *parentWidget) |
95 | { |
96 | m_mainWidget = true; |
97 | QtResourceSet *resourceSet = core()->resourceModel()->currentResourceSet(); |
98 | |
99 | // reload resource properties; |
100 | createResources(resources: ui->elementResources()); |
101 | core()->resourceModel()->setCurrentResourceSet(resourceSet: m_tempResourceSet); |
102 | |
103 | m_ignoreCreateResources = true; |
104 | DesignerPixmapCache pixmapCache; |
105 | DesignerIconCache iconCache(&pixmapCache); |
106 | m_pixmapCache = &pixmapCache; |
107 | m_iconCache = &iconCache; |
108 | |
109 | QWidget *widget = QFormBuilder::create(ui, parentWidget); |
110 | |
111 | core()->resourceModel()->setCurrentResourceSet(resourceSet); |
112 | core()->resourceModel()->removeResourceSet(resourceSet: m_tempResourceSet); |
113 | m_tempResourceSet = nullptr; |
114 | m_ignoreCreateResources = false; |
115 | m_pixmapCache = nullptr; |
116 | m_iconCache = nullptr; |
117 | |
118 | m_customWidgetsWithScript.clear(); |
119 | return widget; |
120 | } |
121 | |
122 | QWidget *QDesignerFormBuilder::createWidget(const QString &widgetName, QWidget *parentWidget, const QString &name) |
123 | { |
124 | QWidget *widget = nullptr; |
125 | |
126 | if (widgetName == QStringLiteral("QToolBar" )) { |
127 | widget = new QToolBar(parentWidget); |
128 | } else if (widgetName == QStringLiteral("QMenu" )) { |
129 | widget = new QMenu(parentWidget); |
130 | } else if (widgetName == QStringLiteral("QMenuBar" )) { |
131 | widget = new QMenuBar(parentWidget); |
132 | } else { |
133 | widget = core()->widgetFactory()->createWidget(name: widgetName, parentWidget); |
134 | } |
135 | |
136 | if (widget) { |
137 | widget->setObjectName(name); |
138 | if (QSimpleResource::hasCustomWidgetScript(core: m_core, object: widget)) |
139 | m_customWidgetsWithScript.insert(value: widget); |
140 | } |
141 | |
142 | if (m_mainWidget) { // We need to apply the DPI here to take effect on size hints, etc. |
143 | m_deviceProfile.apply(core: m_core, widget, am: DeviceProfile::ApplyPreview); |
144 | m_mainWidget = false; |
145 | } |
146 | return widget; |
147 | } |
148 | |
149 | bool QDesignerFormBuilder::addItem(DomWidget *ui_widget, QWidget *widget, QWidget *parentWidget) |
150 | { |
151 | // Use container extension or rely on scripts unless main window. |
152 | if (QFormBuilder::addItem(ui_widget, widget, parentWidget)) |
153 | return true; |
154 | |
155 | if (QDesignerContainerExtension *container = qt_extension<QDesignerContainerExtension*>(manager: m_core->extensionManager(), object: parentWidget)) { |
156 | container->addWidget(widget); |
157 | return true; |
158 | } |
159 | return false; |
160 | } |
161 | |
162 | bool QDesignerFormBuilder::addItem(DomLayoutItem *ui_item, QLayoutItem *item, QLayout *layout) |
163 | { |
164 | return QFormBuilder::addItem(ui_item, item, layout); |
165 | } |
166 | |
167 | QIcon QDesignerFormBuilder::nameToIcon(const QString &filePath, const QString &qrcPath) |
168 | { |
169 | Q_UNUSED(filePath); |
170 | Q_UNUSED(qrcPath); |
171 | qWarning() << "QDesignerFormBuilder::nameToIcon() is obsoleted" ; |
172 | return QIcon(); |
173 | } |
174 | |
175 | QPixmap QDesignerFormBuilder::nameToPixmap(const QString &filePath, const QString &qrcPath) |
176 | { |
177 | Q_UNUSED(filePath); |
178 | Q_UNUSED(qrcPath); |
179 | qWarning() << "QDesignerFormBuilder::nameToPixmap() is obsoleted" ; |
180 | return QPixmap(); |
181 | } |
182 | |
183 | /* If the property is a enum or flag value, retrieve |
184 | * the existing enum/flag type via property sheet and use it to convert */ |
185 | |
186 | static bool (const DomProperty *p, |
187 | const QDesignerPropertySheetExtension* sheet, |
188 | QVariant &v) |
189 | { |
190 | switch (p->kind()) { |
191 | case DomProperty::Set: { |
192 | const int index = sheet->indexOf(name: p->attributeName()); |
193 | if (index == -1) |
194 | return false; |
195 | const QVariant sheetValue = sheet->property(index); |
196 | if (sheetValue.canConvert<PropertySheetFlagValue>()) { |
197 | const PropertySheetFlagValue f = qvariant_cast<PropertySheetFlagValue>(v: sheetValue); |
198 | bool ok = false; |
199 | v = f.metaFlags.parseFlags(s: p->elementSet(), ok: &ok); |
200 | if (!ok) |
201 | designerWarning(message: f.metaFlags.messageParseFailed(s: p->elementSet())); |
202 | return true; |
203 | } |
204 | } |
205 | break; |
206 | case DomProperty::Enum: { |
207 | const int index = sheet->indexOf(name: p->attributeName()); |
208 | if (index == -1) |
209 | return false; |
210 | const QVariant sheetValue = sheet->property(index); |
211 | if (sheetValue.canConvert<PropertySheetEnumValue>()) { |
212 | const PropertySheetEnumValue e = qvariant_cast<PropertySheetEnumValue>(v: sheetValue); |
213 | bool ok = false; |
214 | v = e.metaEnum.parseEnum(s: p->elementEnum(), ok: &ok); |
215 | if (!ok) |
216 | designerWarning(message: e.metaEnum.messageParseFailed(s: p->elementEnum())); |
217 | return true; |
218 | } |
219 | } |
220 | break; |
221 | default: |
222 | break; |
223 | } |
224 | return false; |
225 | } |
226 | |
227 | void QDesignerFormBuilder::applyProperties(QObject *o, const QList<DomProperty*> &properties) |
228 | { |
229 | if (properties.isEmpty()) |
230 | return; |
231 | |
232 | const QDesignerPropertySheetExtension *sheet = qt_extension<QDesignerPropertySheetExtension*>(manager: core()->extensionManager(), object: o); |
233 | const QDesignerDynamicPropertySheetExtension *dynamicSheet = qt_extension<QDesignerDynamicPropertySheetExtension*>(manager: core()->extensionManager(), object: o); |
234 | const bool changingMetaObject = WidgetFactory::classNameOf(core: core(), o) == QStringLiteral("QAxWidget" ); |
235 | const QDesignerMetaObjectInterface *meta = core()->introspection()->metaObject(object: o); |
236 | const bool dynamicPropertiesAllowed = dynamicSheet && dynamicSheet->dynamicPropertiesAllowed(); |
237 | |
238 | QDesignerPropertySheet *designerPropertySheet = qobject_cast<QDesignerPropertySheet *>( |
239 | object: core()->extensionManager()->extension(object: o, Q_TYPEID(QDesignerPropertySheetExtension))); |
240 | |
241 | if (designerPropertySheet) { |
242 | if (designerPropertySheet->pixmapCache()) |
243 | designerPropertySheet->setPixmapCache(m_pixmapCache); |
244 | if (designerPropertySheet->iconCache()) |
245 | designerPropertySheet->setIconCache(m_iconCache); |
246 | } |
247 | |
248 | for (DomProperty *p : properties) { |
249 | QVariant v; |
250 | if (!readDomEnumerationValue(p, sheet, v)) |
251 | v = toVariant(meta: o->metaObject(), property: p); |
252 | |
253 | if (v.isNull()) |
254 | continue; |
255 | |
256 | const QString attributeName = p->attributeName(); |
257 | if (d->applyPropertyInternally(o, propertyName: attributeName, value: v)) |
258 | continue; |
259 | |
260 | // refuse fake properties like current tab name (weak test) |
261 | if (!dynamicPropertiesAllowed) { |
262 | if (changingMetaObject) // Changes after setting control of QAxWidget |
263 | meta = core()->introspection()->metaObject(object: o); |
264 | if (meta->indexOfProperty(name: attributeName) == -1) |
265 | continue; |
266 | } |
267 | |
268 | QObject *obj = o; |
269 | QAbstractScrollArea *scroll = qobject_cast<QAbstractScrollArea *>(object: o); |
270 | if (scroll && attributeName == QStringLiteral("cursor" ) && scroll->viewport()) |
271 | obj = scroll->viewport(); |
272 | |
273 | // a real property |
274 | obj->setProperty(name: attributeName.toUtf8(), value: v); |
275 | } |
276 | } |
277 | |
278 | DomWidget *QDesignerFormBuilder::createDom(QWidget *widget, DomWidget *ui_parentWidget, bool recursive) |
279 | { |
280 | DomWidget *ui_widget = QFormBuilder::createDom(widget, ui_parentWidget, recursive); |
281 | QSimpleResource::addExtensionDataToDOM(afb: this, core: m_core, ui_widget, widget); |
282 | return ui_widget; |
283 | } |
284 | |
285 | QWidget *QDesignerFormBuilder::create(DomWidget *ui_widget, QWidget *parentWidget) |
286 | { |
287 | QWidget *widget = QFormBuilder::create(ui_widget, parentWidget); |
288 | // Do not apply state if scripts are to be run in preview mode |
289 | QSimpleResource::applyExtensionDataFromDOM(afb: this, core: m_core, ui_widget, widget); |
290 | return widget; |
291 | } |
292 | |
293 | void QDesignerFormBuilder::createResources(DomResources *resources) |
294 | { |
295 | if (m_ignoreCreateResources) |
296 | return; |
297 | QStringList paths; |
298 | if (resources != nullptr) { |
299 | const auto &dom_include = resources->elementInclude(); |
300 | for (DomResource *res : dom_include) { |
301 | QString path = QDir::cleanPath(path: workingDirectory().absoluteFilePath(fileName: res->attributeLocation())); |
302 | paths << path; |
303 | } |
304 | } |
305 | |
306 | m_tempResourceSet = core()->resourceModel()->addResourceSet(paths); |
307 | } |
308 | |
309 | QLayout *QDesignerFormBuilder::create(DomLayout *ui_layout, QLayout *layout, QWidget *parentWidget) |
310 | { |
311 | return QFormBuilder::create(ui_layout, layout, parentWidget); |
312 | } |
313 | |
314 | void QDesignerFormBuilder::(DomWidget *ui_widget, QWidget *widget, QWidget *parentWidget) |
315 | { |
316 | QFormBuilder::loadExtraInfo(ui_widget, widget, parentWidget); |
317 | } |
318 | |
319 | QWidget *QDesignerFormBuilder::createPreview(const QDesignerFormWindowInterface *fw, |
320 | const QString &styleName, |
321 | const QString &appStyleSheet, |
322 | const DeviceProfile &deviceProfile, |
323 | QString *errorMessage) |
324 | { |
325 | // load |
326 | QDesignerFormBuilder builder(fw->core(), deviceProfile); |
327 | builder.setWorkingDirectory(fw->absoluteDir()); |
328 | |
329 | QByteArray bytes = fw->contents().toUtf8(); |
330 | |
331 | QBuffer buffer(&bytes); |
332 | buffer.open(openMode: QIODevice::ReadOnly); |
333 | |
334 | QWidget *widget = builder.load(dev: &buffer, parentWidget: nullptr); |
335 | if (!widget) { // Shouldn't happen |
336 | *errorMessage = QCoreApplication::translate(context: "QDesignerFormBuilder" , key: "The preview failed to build." ); |
337 | return nullptr; |
338 | } |
339 | // Make sure palette is applied |
340 | const QString styleToUse = styleName.isEmpty() ? builder.deviceProfile().style() : styleName; |
341 | if (!styleToUse.isEmpty()) { |
342 | if (WidgetFactory *wf = qobject_cast<qdesigner_internal::WidgetFactory *>(object: fw->core()->widgetFactory())) { |
343 | if (styleToUse != wf->styleName()) |
344 | WidgetFactory::applyStyleToTopLevel(style: wf->getStyle(styleName: styleToUse), widget); |
345 | } |
346 | } |
347 | // Fake application style sheet by prepending. (If this doesn't work, fake by nesting |
348 | // into parent widget). |
349 | if (!appStyleSheet.isEmpty()) { |
350 | QString styleSheet = appStyleSheet; |
351 | styleSheet += QLatin1Char('\n'); |
352 | styleSheet += widget->styleSheet(); |
353 | widget->setStyleSheet(styleSheet); |
354 | } |
355 | return widget; |
356 | } |
357 | |
358 | QWidget *QDesignerFormBuilder::createPreview(const QDesignerFormWindowInterface *fw, const QString &styleName) |
359 | { |
360 | return createPreview(fw, styleName, appStyleSheet: QString()); |
361 | } |
362 | |
363 | QWidget *QDesignerFormBuilder::createPreview(const QDesignerFormWindowInterface *fw, |
364 | const QString &styleName, |
365 | const QString &appStyleSheet, |
366 | QString *errorMessage) |
367 | { |
368 | return createPreview(fw, styleName, appStyleSheet, deviceProfile: DeviceProfile(), errorMessage); |
369 | } |
370 | |
371 | QWidget *QDesignerFormBuilder::createPreview(const QDesignerFormWindowInterface *fw, const QString &styleName, const QString &appStyleSheet) |
372 | { |
373 | QString errorMessage; |
374 | QWidget *widget = createPreview(fw, styleName, appStyleSheet, deviceProfile: DeviceProfile(), errorMessage: &errorMessage); |
375 | if (!widget && !errorMessage.isEmpty()) { |
376 | // Display Script errors or message box |
377 | QWidget *dialogParent = fw->core()->topLevel(); |
378 | fw->core()->dialogGui()->message(parent: dialogParent, context: QDesignerDialogGuiInterface::PreviewFailureMessage, |
379 | icon: QMessageBox::Warning, title: QCoreApplication::translate(context: "QDesignerFormBuilder" , key: "Designer" ), |
380 | text: errorMessage, buttons: QMessageBox::Ok); |
381 | return nullptr; |
382 | } |
383 | return widget; |
384 | } |
385 | |
386 | QPixmap QDesignerFormBuilder::createPreviewPixmap(const QDesignerFormWindowInterface *fw, const QString &styleName, const QString &appStyleSheet) |
387 | { |
388 | QWidget *widget = createPreview(fw, styleName, appStyleSheet); |
389 | if (!widget) |
390 | return QPixmap(); |
391 | |
392 | const QPixmap rc = widget->grab(rectangle: QRect(0, 0, -1, -1)); |
393 | widget->deleteLater(); |
394 | return rc; |
395 | } |
396 | |
397 | // ---------- NewFormWidgetFormBuilder |
398 | |
399 | NewFormWidgetFormBuilder::NewFormWidgetFormBuilder(QDesignerFormEditorInterface *core, |
400 | const DeviceProfile &deviceProfile) : |
401 | QDesignerFormBuilder(core, deviceProfile) |
402 | { |
403 | } |
404 | |
405 | void NewFormWidgetFormBuilder::createCustomWidgets(DomCustomWidgets *dc) |
406 | { |
407 | QSimpleResource::handleDomCustomWidgets(core: core(), dom_custom_widgets: dc); |
408 | } |
409 | |
410 | } // namespace qdesigner_internal |
411 | |
412 | QT_END_NAMESPACE |
413 | |