1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the Qt Designer of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
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 http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file. Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qdesigner_formbuilder_p.h"
43 #include "dynamicpropertysheet.h"
44 #include "qsimpleresource_p.h"
45 #include "widgetfactory_p.h"
46 #include "qdesigner_introspection_p.h"
47
48 #include <ui4_p.h>
49 #include <formbuilderextra_p.h>
50 // sdk
51 #include <QtDesigner/container.h>
52 #include <QtDesigner/customwidget.h>
53 #include <QtDesigner/propertysheet.h>
54 #include <QtDesigner/QExtensionManager>
55 #include <QtDesigner/QDesignerFormEditorInterface>
56 #include <QtDesigner/QDesignerFormWindowInterface>
57 #include <QtDesigner/QDesignerWidgetFactoryInterface>
58 #include <QtDesigner/QDesignerCustomWidgetInterface>
59 #include <abstractdialoggui_p.h>
60
61 // shared
62 #include <qdesigner_propertysheet_p.h>
63 #include <qdesigner_utils_p.h>
64 #include <formwindowbase_p.h>
65 #include <qtresourcemodel_p.h>
66 #include <scripterrordialog_p.h>
67
68 #include <QtGui/QWidget>
69 #include <QtGui/QMenu>
70 #include <QtGui/QToolBar>
71 #include <QtGui/QMenuBar>
72 #include <QtGui/QMainWindow>
73 #include <QtGui/QStyleFactory>
74 #include <QtGui/QStyle>
75 #include <QtGui/QApplication>
76 #include <QtGui/QAbstractScrollArea>
77 #include <QtGui/QMessageBox>
78 #include <QtGui/QPixmap>
79
80 #include <QtCore/QBuffer>
81 #include <QtCore/qdebug.h>
82 #include <QtCore/QCoreApplication>
83
84 QT_BEGIN_NAMESPACE
85
86 #ifndef QT_FORMBUILDER_NO_SCRIPT
summarizeScriptErrors(const QFormScriptRunner::Errors & errors)87 static QString summarizeScriptErrors(const QFormScriptRunner::Errors &errors)
88 {
89 QString rc = QCoreApplication::translate("QDesignerFormBuilder", "Script errors occurred:");
90 foreach (QFormScriptRunner::Error error, errors) {
91 rc += QLatin1Char('\n');
92 rc += error.errorMessage;
93 }
94 return rc;
95 }
96 #endif
97
98 namespace qdesigner_internal {
99
QDesignerFormBuilder(QDesignerFormEditorInterface * core,Mode mode,const DeviceProfile & deviceProfile)100 QDesignerFormBuilder::QDesignerFormBuilder(QDesignerFormEditorInterface *core,
101 Mode mode,
102 const DeviceProfile &deviceProfile) :
103 m_core(core),
104 m_mode(mode),
105 m_deviceProfile(deviceProfile),
106 m_pixmapCache(0),
107 m_iconCache(0),
108 m_ignoreCreateResources(false),
109 m_tempResourceSet(0),
110 m_mainWidget(true)
111 {
112 Q_ASSERT(m_core);
113 #ifndef QT_FORMBUILDER_NO_SCRIPT
114 // Disable scripting in the editors.
115 QFormScriptRunner::Options options = formScriptRunner()->options();
116 switch (m_mode) {
117 case DisableScripts:
118 options |= QFormScriptRunner::DisableScripts;
119 break;
120 case EnableScripts:
121 options |= QFormScriptRunner::DisableWarnings;
122 options &= ~QFormScriptRunner::DisableScripts;
123 break;
124 }
125 formScriptRunner()-> setOptions(options);
126 #endif
127 }
128
systemStyle() const129 QString QDesignerFormBuilder::systemStyle() const
130 {
131 return m_deviceProfile.isEmpty() ?
132 QString::fromUtf8(QApplication::style()->metaObject()->className()) :
133 m_deviceProfile.style();
134 }
135
createWidgetFromContents(const QString & contents,QWidget * parentWidget)136 QWidget *QDesignerFormBuilder::createWidgetFromContents(const QString &contents, QWidget *parentWidget)
137 {
138 QByteArray data = contents.toUtf8();
139 QBuffer buffer(&data);
140 buffer.open(QIODevice::ReadOnly);
141 return load(&buffer, parentWidget);
142 }
143
create(DomUI * ui,QWidget * parentWidget)144 QWidget *QDesignerFormBuilder::create(DomUI *ui, QWidget *parentWidget)
145 {
146 m_mainWidget = true;
147 QtResourceSet *resourceSet = core()->resourceModel()->currentResourceSet();
148
149 // reload resource properties;
150 createResources(ui->elementResources());
151 core()->resourceModel()->setCurrentResourceSet(m_tempResourceSet);
152
153 m_ignoreCreateResources = true;
154 DesignerPixmapCache pixmapCache;
155 DesignerIconCache iconCache(&pixmapCache);
156 m_pixmapCache = &pixmapCache;
157 m_iconCache = &iconCache;
158
159 QWidget *widget = QFormBuilder::create(ui, parentWidget);
160
161 core()->resourceModel()->setCurrentResourceSet(resourceSet);
162 core()->resourceModel()->removeResourceSet(m_tempResourceSet);
163 m_tempResourceSet = 0;
164 m_ignoreCreateResources = false;
165 m_pixmapCache = 0;
166 m_iconCache = 0;
167
168 m_customWidgetsWithScript.clear();
169 return widget;
170 }
171
createWidget(const QString & widgetName,QWidget * parentWidget,const QString & name)172 QWidget *QDesignerFormBuilder::createWidget(const QString &widgetName, QWidget *parentWidget, const QString &name)
173 {
174 QWidget *widget = 0;
175
176 if (widgetName == QLatin1String("QToolBar")) {
177 widget = new QToolBar(parentWidget);
178 } else if (widgetName == QLatin1String("QMenu")) {
179 widget = new QMenu(parentWidget);
180 } else if (widgetName == QLatin1String("QMenuBar")) {
181 widget = new QMenuBar(parentWidget);
182 } else {
183 widget = core()->widgetFactory()->createWidget(widgetName, parentWidget);
184 }
185
186 if (widget) {
187 widget->setObjectName(name);
188 if (QSimpleResource::hasCustomWidgetScript(m_core, widget))
189 m_customWidgetsWithScript.insert(widget);
190 }
191
192 if (m_mainWidget) { // We need to apply the DPI here to take effect on size hints, etc.
193 m_deviceProfile.apply(m_core, widget, DeviceProfile::ApplyPreview);
194 m_mainWidget = false;
195 }
196 return widget;
197 }
198
addItem(DomWidget * ui_widget,QWidget * widget,QWidget * parentWidget)199 bool QDesignerFormBuilder::addItem(DomWidget *ui_widget, QWidget *widget, QWidget *parentWidget)
200 {
201 // Use container extension or rely on scripts unless main window.
202 if (QFormBuilder::addItem(ui_widget, widget, parentWidget))
203 return true;
204
205 if (QDesignerContainerExtension *container = qt_extension<QDesignerContainerExtension*>(m_core->extensionManager(), parentWidget)) {
206 container->addWidget(widget);
207 return true;
208 }
209 return false;
210 }
211
addItem(DomLayoutItem * ui_item,QLayoutItem * item,QLayout * layout)212 bool QDesignerFormBuilder::addItem(DomLayoutItem *ui_item, QLayoutItem *item, QLayout *layout)
213 {
214 return QFormBuilder::addItem(ui_item, item, layout);
215 }
216
nameToIcon(const QString & filePath,const QString & qrcPath)217 QIcon QDesignerFormBuilder::nameToIcon(const QString &filePath, const QString &qrcPath)
218 {
219 Q_UNUSED(filePath)
220 Q_UNUSED(qrcPath)
221 qWarning() << "QDesignerFormBuilder::nameToIcon() is obsoleted";
222 return QIcon();
223 }
224
nameToPixmap(const QString & filePath,const QString & qrcPath)225 QPixmap QDesignerFormBuilder::nameToPixmap(const QString &filePath, const QString &qrcPath)
226 {
227 Q_UNUSED(filePath)
228 Q_UNUSED(qrcPath)
229 qWarning() << "QDesignerFormBuilder::nameToPixmap() is obsoleted";
230 return QPixmap();
231 }
232
233 /* If the property is a enum or flag value, retrieve
234 * the existing enum/flag type via property sheet and use it to convert */
235
readDomEnumerationValue(const DomProperty * p,const QDesignerPropertySheetExtension * sheet,QVariant & v)236 static bool readDomEnumerationValue(const DomProperty *p,
237 const QDesignerPropertySheetExtension* sheet,
238 QVariant &v)
239 {
240 switch (p->kind()) {
241 case DomProperty::Set: {
242 const int index = sheet->indexOf(p->attributeName());
243 if (index == -1)
244 return false;
245 const QVariant sheetValue = sheet->property(index);
246 if (sheetValue.canConvert<PropertySheetFlagValue>()) {
247 const PropertySheetFlagValue f = qvariant_cast<PropertySheetFlagValue>(sheetValue);
248 bool ok = false;
249 v = f.metaFlags.parseFlags(p->elementSet(), &ok);
250 if (!ok)
251 designerWarning(f.metaFlags.messageParseFailed(p->elementSet()));
252 return true;
253 }
254 }
255 break;
256 case DomProperty::Enum: {
257 const int index = sheet->indexOf(p->attributeName());
258 if (index == -1)
259 return false;
260 const QVariant sheetValue = sheet->property(index);
261 if (sheetValue.canConvert<PropertySheetEnumValue>()) {
262 const PropertySheetEnumValue e = qvariant_cast<PropertySheetEnumValue>(sheetValue);
263 bool ok = false;
264 v = e.metaEnum.parseEnum(p->elementEnum(), &ok);
265 if (!ok)
266 designerWarning(e.metaEnum.messageParseFailed(p->elementEnum()));
267 return true;
268 }
269 }
270 break;
271 default:
272 break;
273 }
274 return false;
275 }
276
applyProperties(QObject * o,const QList<DomProperty * > & properties)277 void QDesignerFormBuilder::applyProperties(QObject *o, const QList<DomProperty*> &properties)
278 {
279 typedef QList<DomProperty*> DomPropertyList;
280
281 if (properties.empty())
282 return;
283
284 QFormBuilderExtra *formBuilderExtra = QFormBuilderExtra::instance(this);
285 const QDesignerPropertySheetExtension *sheet = qt_extension<QDesignerPropertySheetExtension*>(core()->extensionManager(), o);
286 const QDesignerDynamicPropertySheetExtension *dynamicSheet = qt_extension<QDesignerDynamicPropertySheetExtension*>(core()->extensionManager(), o);
287 const bool changingMetaObject = WidgetFactory::classNameOf(core(), o) == QLatin1String("QAxWidget");
288 const QDesignerMetaObjectInterface *meta = core()->introspection()->metaObject(o);
289 const bool dynamicPropertiesAllowed = dynamicSheet && dynamicSheet->dynamicPropertiesAllowed();
290
291 QDesignerPropertySheet *designerPropertySheet = qobject_cast<QDesignerPropertySheet *>(
292 core()->extensionManager()->extension(o, Q_TYPEID(QDesignerPropertySheetExtension)));
293
294 if (designerPropertySheet) {
295 if (designerPropertySheet->pixmapCache())
296 designerPropertySheet->setPixmapCache(m_pixmapCache);
297 if (designerPropertySheet->iconCache())
298 designerPropertySheet->setIconCache(m_iconCache);
299 }
300
301 const DomPropertyList::const_iterator cend = properties.constEnd();
302 for (DomPropertyList::const_iterator it = properties.constBegin(); it != cend; ++it) {
303 DomProperty *p = *it;
304 QVariant v;
305 if (!readDomEnumerationValue(p, sheet, v))
306 v = toVariant(o->metaObject(), p);
307
308 if (v.isNull())
309 continue;
310
311 const QString attributeName = p->attributeName();
312 if (formBuilderExtra->applyPropertyInternally(o, attributeName, v))
313 continue;
314
315 // refuse fake properties like current tab name (weak test)
316 if (!dynamicPropertiesAllowed) {
317 if (changingMetaObject) // Changes after setting control of QAxWidget
318 meta = core()->introspection()->metaObject(o);
319 if (meta->indexOfProperty(attributeName) == -1)
320 continue;
321 }
322
323 QObject *obj = o;
324 QAbstractScrollArea *scroll = qobject_cast<QAbstractScrollArea *>(o);
325 if (scroll && attributeName == QLatin1String("cursor") && scroll->viewport())
326 obj = scroll->viewport();
327
328 // a real property
329 obj->setProperty(attributeName.toUtf8(), v);
330 }
331 }
332
createDom(QWidget * widget,DomWidget * ui_parentWidget,bool recursive)333 DomWidget *QDesignerFormBuilder::createDom(QWidget *widget, DomWidget *ui_parentWidget, bool recursive)
334 {
335 DomWidget *ui_widget = QFormBuilder::createDom(widget, ui_parentWidget, recursive);
336 QSimpleResource::addExtensionDataToDOM(this, m_core, ui_widget, widget);
337 return ui_widget;
338 }
339
create(DomWidget * ui_widget,QWidget * parentWidget)340 QWidget *QDesignerFormBuilder::create(DomWidget *ui_widget, QWidget *parentWidget)
341 {
342 QWidget *widget = QFormBuilder::create(ui_widget, parentWidget);
343 // Do not apply state if scripts are to be run in preview mode
344 QSimpleResource::applyExtensionDataFromDOM(this, m_core, ui_widget, widget, m_mode == DisableScripts);
345 return widget;
346 }
347
createResources(DomResources * resources)348 void QDesignerFormBuilder::createResources(DomResources *resources)
349 {
350 if (m_ignoreCreateResources)
351 return;
352 QStringList paths;
353 if (resources != 0) {
354 const QList<DomResource*> dom_include = resources->elementInclude();
355 foreach (DomResource *res, dom_include) {
356 QString path = QDir::cleanPath(workingDirectory().absoluteFilePath(res->attributeLocation()));
357 paths << path;
358 }
359 }
360
361 m_tempResourceSet = core()->resourceModel()->addResourceSet(paths);
362 }
363
create(DomLayout * ui_layout,QLayout * layout,QWidget * parentWidget)364 QLayout *QDesignerFormBuilder::create(DomLayout *ui_layout, QLayout *layout, QWidget *parentWidget)
365 {
366 return QFormBuilder::create(ui_layout, layout, parentWidget);
367 }
368
loadExtraInfo(DomWidget * ui_widget,QWidget * widget,QWidget * parentWidget)369 void QDesignerFormBuilder::loadExtraInfo(DomWidget *ui_widget, QWidget *widget, QWidget *parentWidget)
370 {
371 QFormBuilder::loadExtraInfo(ui_widget, widget, parentWidget);
372 }
373
createPreview(const QDesignerFormWindowInterface * fw,const QString & styleName,const QString & appStyleSheet,const DeviceProfile & deviceProfile,ScriptErrors * scriptErrors,QString * errorMessage)374 QWidget *QDesignerFormBuilder::createPreview(const QDesignerFormWindowInterface *fw,
375 const QString &styleName,
376 const QString &appStyleSheet,
377 const DeviceProfile &deviceProfile,
378 ScriptErrors *scriptErrors,
379 QString *errorMessage)
380 {
381 scriptErrors->clear();
382
383 // load
384 QDesignerFormBuilder builder(fw->core(), EnableScripts, deviceProfile);
385 builder.setWorkingDirectory(fw->absoluteDir());
386
387 const bool warningsEnabled = QSimpleResource::setWarningsEnabled(false);
388 QByteArray bytes = fw->contents().toUtf8();
389 QSimpleResource::setWarningsEnabled(warningsEnabled);
390
391 QBuffer buffer(&bytes);
392 buffer.open(QIODevice::ReadOnly);
393
394 QWidget *widget = builder.load(&buffer, 0);
395 if (!widget) { // Shouldn't happen
396 *errorMessage = QCoreApplication::translate("QDesignerFormBuilder", "The preview failed to build.");
397 return 0;
398 }
399 // Make sure palette is applied
400 const QString styleToUse = styleName.isEmpty() ? builder.deviceProfile().style() : styleName;
401 if (!styleToUse.isEmpty()) {
402 if (WidgetFactory *wf = qobject_cast<qdesigner_internal::WidgetFactory *>(fw->core()->widgetFactory())) {
403 if (styleToUse != wf->styleName())
404 WidgetFactory::applyStyleToTopLevel(wf->getStyle(styleToUse), widget);
405 }
406 }
407 #ifndef QT_FORMBUILDER_NO_SCRIPT
408 // Check for script errors
409 *scriptErrors = builder.formScriptRunner()->errors();
410 if (!scriptErrors->empty()) {
411 *errorMessage = summarizeScriptErrors(*scriptErrors);
412 delete widget;
413 return 0;
414 }
415 #endif
416 // Fake application style sheet by prepending. (If this doesn't work, fake by nesting
417 // into parent widget).
418 if (!appStyleSheet.isEmpty()) {
419 QString styleSheet = appStyleSheet;
420 styleSheet += QLatin1Char('\n');
421 styleSheet += widget->styleSheet();
422 widget->setStyleSheet(styleSheet);
423 }
424 return widget;
425 }
426
createPreview(const QDesignerFormWindowInterface * fw,const QString & styleName)427 QWidget *QDesignerFormBuilder::createPreview(const QDesignerFormWindowInterface *fw, const QString &styleName)
428 {
429 return createPreview(fw, styleName, QString());
430 }
431
createPreview(const QDesignerFormWindowInterface * fw,const QString & styleName,const QString & appStyleSheet,const DeviceProfile & deviceProfile,QString * errorMessage)432 QWidget *QDesignerFormBuilder::createPreview(const QDesignerFormWindowInterface *fw,
433 const QString &styleName,
434 const QString &appStyleSheet,
435 const DeviceProfile &deviceProfile,
436 QString *errorMessage)
437 {
438 ScriptErrors scriptErrors;
439 return createPreview(fw, styleName, appStyleSheet, deviceProfile, &scriptErrors, errorMessage);
440 }
441
createPreview(const QDesignerFormWindowInterface * fw,const QString & styleName,const QString & appStyleSheet,QString * errorMessage)442 QWidget *QDesignerFormBuilder::createPreview(const QDesignerFormWindowInterface *fw,
443 const QString &styleName,
444 const QString &appStyleSheet,
445 QString *errorMessage)
446 {
447 ScriptErrors scriptErrors;
448 return createPreview(fw, styleName, appStyleSheet, DeviceProfile(), &scriptErrors, errorMessage);
449 }
450
createPreview(const QDesignerFormWindowInterface * fw,const QString & styleName,const QString & appStyleSheet)451 QWidget *QDesignerFormBuilder::createPreview(const QDesignerFormWindowInterface *fw, const QString &styleName, const QString &appStyleSheet)
452 {
453 ScriptErrors scriptErrors;
454 QString errorMessage;
455 QWidget *widget = createPreview(fw, styleName, appStyleSheet, DeviceProfile(), &scriptErrors, &errorMessage);
456 if (!widget) {
457 // Display Script errors or message box
458 QWidget *dialogParent = fw->core()->topLevel();
459 if (scriptErrors.empty()) {
460 fw->core()->dialogGui()->message(dialogParent, QDesignerDialogGuiInterface::PreviewFailureMessage,
461 QMessageBox::Warning, QCoreApplication::translate("QDesignerFormBuilder", "Designer"), errorMessage, QMessageBox::Ok);
462 } else {
463 ScriptErrorDialog scriptErrorDialog(scriptErrors, dialogParent);
464 scriptErrorDialog.exec();
465 }
466 return 0;
467 }
468 return widget;
469 }
470
createPreviewPixmap(const QDesignerFormWindowInterface * fw,const QString & styleName,const QString & appStyleSheet)471 QPixmap QDesignerFormBuilder::createPreviewPixmap(const QDesignerFormWindowInterface *fw, const QString &styleName, const QString &appStyleSheet)
472 {
473 QWidget *widget = createPreview(fw, styleName, appStyleSheet);
474 if (!widget)
475 return QPixmap();
476
477 const QPixmap rc = QPixmap::grabWidget (widget);
478 widget->deleteLater();
479 return rc;
480 }
481
482 // ---------- NewFormWidgetFormBuilder
483
NewFormWidgetFormBuilder(QDesignerFormEditorInterface * core,Mode mode,const DeviceProfile & deviceProfile)484 NewFormWidgetFormBuilder::NewFormWidgetFormBuilder(QDesignerFormEditorInterface *core,
485 Mode mode,
486 const DeviceProfile &deviceProfile) :
487 QDesignerFormBuilder(core, mode, deviceProfile)
488 {
489 }
490
createCustomWidgets(DomCustomWidgets * dc)491 void NewFormWidgetFormBuilder::createCustomWidgets(DomCustomWidgets *dc)
492 {
493 QSimpleResource::handleDomCustomWidgets(core(), dc);
494 }
495
496 } // namespace qdesigner_internal
497
498 QT_END_NAMESPACE
499