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