1 /****************************************************************************
2 **
3 ** Copyright (C) 2018 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:BSD$
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 ** BSD License Usage
18 ** Alternatively, you may use this file under the terms of the BSD license
19 ** as follows:
20 **
21 ** "Redistribution and use in source and binary forms, with or without
22 ** modification, are permitted provided that the following conditions are
23 ** met:
24 **   * Redistributions of source code must retain the above copyright
25 **     notice, this list of conditions and the following disclaimer.
26 **   * Redistributions in binary form must reproduce the above copyright
27 **     notice, this list of conditions and the following disclaimer in
28 **     the documentation and/or other materials provided with the
29 **     distribution.
30 **   * Neither the name of The Qt Company Ltd nor the names of its
31 **     contributors may be used to endorse or promote products derived
32 **     from this software without specific prior written permission.
33 **
34 **
35 ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
36 ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
37 ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
38 ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
39 ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40 ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41 ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
42 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
43 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
44 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
45 ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
46 **
47 ** $QT_END_LICENSE$
48 **
49 ****************************************************************************/
50 
51 #include "abstractformbuilder.h"
52 #include "formbuilderextra_p.h"
53 #include "resourcebuilder_p.h"
54 #include "textbuilder_p.h"
55 #include "ui4_p.h"
56 #include "properties_p.h"
57 
58 #include <QtCore/qvariant.h>
59 #include <QtCore/qmetaobject.h>
60 #include <QtCore/qfileinfo.h>
61 #include <QtCore/qdir.h>
62 #include <QtCore/qqueue.h>
63 #include <QtCore/qhash.h>
64 #include <QtCore/qpair.h>
65 #include <QtCore/qdebug.h>
66 #include <QtCore/qcoreapplication.h>
67 
68 #include <QtWidgets/qaction.h>
69 #include <QtWidgets/qmainwindow.h>
70 #include <QtWidgets/qmenu.h>
71 #include <QtWidgets/qmenubar.h>
72 #include <QtWidgets/qstatusbar.h>
73 #include <QtWidgets/qtoolbar.h>
74 #include <QtWidgets/qmdiarea.h>
75 #include <QtWidgets/qdockwidget.h>
76 #include <QtWidgets/qwizard.h>
77 #include <QtWidgets/qstackedwidget.h>
78 #include <QtWidgets/qtoolbox.h>
79 #include <QtWidgets/qtabwidget.h>
80 #include <QtWidgets/qsplitter.h>
81 #include <QtWidgets/qbuttongroup.h>
82 #include <QtWidgets/qboxlayout.h>
83 #include <QtWidgets/qformlayout.h>
84 #include <QtWidgets/qgridlayout.h>
85 #include <QtWidgets/qscrollarea.h>
86 #include <QtWidgets/qtreewidget.h>
87 #include <QtWidgets/qlistwidget.h>
88 #include <QtWidgets/qheaderview.h>
89 #include <QtWidgets/qtablewidget.h>
90 #include <QtWidgets/qfontcombobox.h>
91 #include <QtWidgets/qpushbutton.h>
92 #ifndef QFORMINTERNAL_NAMESPACE
93 #  include <private/qlayout_p.h> // Compiling within Designer
94 #endif
95 
96 #include <QtCore/qdebug.h>
97 #include <QtCore/qxmlstream.h>
98 
99 #include <limits.h>
100 
101 #include <algorithm>
102 #include <iterator>
103 
104 Q_DECLARE_METATYPE(QWidgetList)
105 
106 static const char *buttonGroupPropertyC = "buttonGroup";
107 
108 QT_BEGIN_NAMESPACE
109 
110 #ifdef QFORMINTERNAL_NAMESPACE
111 using namespace QFormInternal;
112 #endif
113 
114 class QFriendlyLayout: public QLayout
115 {
116 public:
QFriendlyLayout()117     inline QFriendlyLayout() { Q_ASSERT(0); }
118 
119 #ifdef QFORMINTERNAL_NAMESPACE
120     friend class QFormInternal::QAbstractFormBuilder;
121 #else
122     friend class QAbstractFormBuilder;
123 #endif
124 };
125 
126 /*!
127     \class QAbstractFormBuilder
128 
129     \brief The QAbstractFormBuilder class provides a default
130     implementation for classes that create user interfaces at
131     run-time.
132 
133     \inmodule QtDesigner
134 
135     QAbstractFormBuilder provides a standard interface and a default
136     implementation for constructing forms from user interface
137     files. It is not intended to be instantiated directly. Use the
138     QFormBuilder class to create user interfaces from UI files at
139     run-time. For example:
140 
141     \snippet lib/tools_designer_src_lib_uilib_abstractformbuilder.cpp 0
142 
143     To override certain aspects of the form builder's behavior,
144     subclass QAbstractFormBuilder and reimplement the relevant virtual
145     functions:
146 
147     \list
148     \li load() handles reading of UI format files from arbitrary
149        QIODevices, and construction of widgets from the XML data
150        that they contain.
151     \li save() handles saving of widget details in UI format to
152        arbitrary QIODevices.
153     \li workingDirectory() and setWorkingDirectory() control the
154        directory in which forms are held. The form builder looks for
155        other resources on paths relative to this directory.
156     \endlist
157 
158     The QFormBuilder class is typically used by custom components and
159     applications that embed \QD. Standalone applications that need to
160     dynamically generate user interfaces at run-time use the
161     QUiLoader, found in the \l{Qt UI Tools} module.
162 
163     \sa {Qt UI Tools}
164 */
165 
166 /*!
167     Constructs a new form builder.*/
QAbstractFormBuilder()168 QAbstractFormBuilder::QAbstractFormBuilder() : d(new QFormBuilderExtra)
169 {
170     setResourceBuilder(new QResourceBuilder());
171     setTextBuilder(new QTextBuilder());
172 }
173 
174 /*!
175     Destroys the form builder.*/
176 QAbstractFormBuilder::~QAbstractFormBuilder() = default;
177 
178 /*!
179     \fn QWidget *QAbstractFormBuilder::load(QIODevice *device, QWidget *parent)
180 
181     Loads an XML representation of a widget from the given \a device,
182     and constructs a new widget with the specified \a parent.
183 
184     \sa save(), errorString()
185 */
load(QIODevice * dev,QWidget * parentWidget)186 QWidget *QAbstractFormBuilder::load(QIODevice *dev, QWidget *parentWidget)
187 {
188     QScopedPointer<DomUI> ui(d->readUi(dev));
189     if (ui.isNull())
190         return nullptr;
191     QWidget *widget = create(ui.data(), parentWidget);
192     if (!widget && d->m_errorString.isEmpty())
193         d->m_errorString = QFormBuilderExtra::msgInvalidUiFile();
194     return widget;
195 }
196 
197 /*!
198     \internal
199 */
create(DomUI * ui,QWidget * parentWidget)200 QWidget *QAbstractFormBuilder::create(DomUI *ui, QWidget *parentWidget)
201 {
202     using ButtonGroupHash = QFormBuilderExtra::ButtonGroupHash;
203 
204     d->clear();
205     if (const DomLayoutDefault *def = ui->elementLayoutDefault()) {
206        d->m_defaultMargin = def->hasAttributeMargin() ? def->attributeMargin() : INT_MIN;
207        d->m_defaultSpacing = def->hasAttributeSpacing() ? def->attributeSpacing() : INT_MIN;
208     }
209 
210     DomWidget *ui_widget = ui->elementWidget();
211     if (!ui_widget)
212         return nullptr;
213 
214     initialize(ui);
215 
216     if (const DomButtonGroups *domButtonGroups = ui->elementButtonGroups())
217         d->registerButtonGroups(domButtonGroups);
218 
219     if (QWidget *widget = create(ui_widget, parentWidget)) {
220         // Reparent button groups that were actually created to main container for them to be found in the signal/slot part
221         const ButtonGroupHash &buttonGroups = d->buttonGroups();
222         if (!buttonGroups.isEmpty()) {
223             const ButtonGroupHash::const_iterator cend = buttonGroups.constEnd();
224             for (ButtonGroupHash::const_iterator it = buttonGroups.constBegin(); it != cend; ++it)
225                 if (it.value().second)
226                     it.value().second->setParent(widget);
227         }
228         createConnections(ui->elementConnections(), widget);
229         createResources(ui->elementResources()); // maybe this should go first, before create()...
230         applyTabStops(widget, ui->elementTabStops());
231         d->applyInternalProperties();
232         reset();
233         d->clear();
234         return widget;
235     }
236     d->clear();
237     return nullptr;
238 }
239 
240 /*!
241     \internal
242     Retrieve relevant information from the custom widgets section.
243     Called by create(DomUI *, QWidget *); call manually if you
244     just use create(DomWidget *, QWidget *) on some child widget of DomUI.
245  */
246 
initialize(const DomUI * ui)247 void QAbstractFormBuilder::initialize(const DomUI *ui)
248 {
249     DomCustomWidgets *domCustomWidgets  = ui->elementCustomWidgets();
250     createCustomWidgets(domCustomWidgets);
251 
252     if (domCustomWidgets) {
253         const auto &customWidgets = domCustomWidgets->elementCustomWidget();
254         for (const DomCustomWidget *w : customWidgets)
255             d->storeCustomWidgetData(w->elementClass(), w);
256     }
257 }
258 
259 /*!
260     \internal
261 */
create(DomWidget * ui_widget,QWidget * parentWidget)262 QWidget *QAbstractFormBuilder::create(DomWidget *ui_widget, QWidget *parentWidget)
263 {
264     QWidget *w = createWidget(ui_widget->attributeClass(), parentWidget, ui_widget->attributeName());
265     if (!w)
266         return nullptr;
267 
268     applyProperties(w, ui_widget->elementProperty());
269 
270     const auto &elementAction = ui_widget->elementAction();
271     for (DomAction *ui_action : elementAction) {
272         QAction *child_action = create(ui_action, w);
273         Q_UNUSED( child_action );
274     }
275 
276     const auto &elementActionGroup = ui_widget->elementActionGroup();
277     for (DomActionGroup *ui_action_group : elementActionGroup) {
278         QActionGroup *child_action_group = create(ui_action_group, w);
279         Q_UNUSED( child_action_group );
280     }
281 
282     QWidgetList children;
283     const auto &elementWidget = ui_widget->elementWidget();
284     for (DomWidget *ui_child : elementWidget) {
285         if (QWidget *child  = create(ui_child, w)) {
286             children += child;
287         } else {
288             const QString className = ui_child->elementClass().value(0);
289             uiLibWarning(QCoreApplication::translate("QAbstractFormBuilder", "The creation of a widget of the class '%1' failed.").arg(className));
290         }
291     }
292 
293     const auto &elementLayout = ui_widget->elementLayout();
294     for (DomLayout *ui_lay : elementLayout) {
295         QLayout *child_lay = create(ui_lay, nullptr, w);
296         Q_UNUSED( child_lay );
297     }
298 
299     const auto &addActions = ui_widget->elementAddAction();
300     if (!addActions.isEmpty()) {
301         const QFormBuilderStrings &strings = QFormBuilderStrings::instance();
302         for (DomActionRef *ui_action_ref : addActions) {
303             const QString name = ui_action_ref->attributeName();
304             if (name == strings.separator) {
305                 QAction *sep = new QAction(w);
306                 sep->setSeparator(true);
307                 w->addAction(sep);
308                 addMenuAction(sep);
309             } else if (QAction *a = d->m_actions.value(name)) {
310                 w->addAction(a);
311             } else if (QActionGroup *g = d->m_actionGroups.value(name)) {
312                 w->addActions(g->actions());
313             } else if (QMenu *menu = w->findChild<QMenu*>(name)) {
314                 w->addAction(menu->menuAction());
315                 addMenuAction(menu->menuAction());
316             }
317         }
318     }
319 
320     loadExtraInfo(ui_widget, w, parentWidget);
321     addItem(ui_widget, w, parentWidget);
322 
323     if (qobject_cast<QDialog *>(w) && parentWidget)
324         w->setAttribute(Qt::WA_Moved, false); // So that QDialog::setVisible(true) will center it
325 
326     const QStringList zOrderNames = ui_widget->elementZOrder();
327     if (!zOrderNames.isEmpty()) {
328         QWidgetList zOrder = qvariant_cast<QWidgetList>(w->property("_q_zOrder"));
329         for (const QString &widgetName : zOrderNames) {
330             if (QWidget *child = w->findChild<QWidget*>(widgetName)) {
331                 if (child->parentWidget() == w) {
332                     zOrder.removeAll(child);
333                     zOrder.append(child);
334                     child->raise();
335                 }
336             }
337         }
338         w->setProperty("_q_zOrder", QVariant::fromValue(zOrder));
339     }
340 
341     return w;
342 }
343 
344 /*!
345     \internal
346 */
create(DomAction * ui_action,QObject * parent)347 QAction *QAbstractFormBuilder::create(DomAction *ui_action, QObject *parent)
348 {
349     QAction *a = createAction(parent, ui_action->attributeName());
350     if (!a)
351         return nullptr;
352 
353     d->m_actions.insert(ui_action->attributeName(), a);
354     applyProperties(a, ui_action->elementProperty());
355     return a;
356 }
357 
358 /*!
359     \internal
360 */
create(DomActionGroup * ui_action_group,QObject * parent)361 QActionGroup *QAbstractFormBuilder::create(DomActionGroup *ui_action_group, QObject *parent)
362 {
363     QActionGroup *a = createActionGroup(parent, ui_action_group->attributeName());
364     if (!a)
365         return nullptr;
366     d->m_actionGroups.insert(ui_action_group->attributeName(), a);
367     applyProperties(a, ui_action_group->elementProperty());
368 
369     const auto &elementAction = ui_action_group->elementAction();
370     for (DomAction *ui_action : elementAction) {
371         QAction *child_action = create(ui_action, a);
372         Q_UNUSED( child_action );
373     }
374 
375     const auto &elementActionGroup = ui_action_group->elementActionGroup();
376     for (DomActionGroup *g : elementActionGroup) {
377         QActionGroup *child_action_group = create(g, parent);
378         Q_UNUSED( child_action_group );
379     }
380 
381     return a;
382 }
383 
384 // figure out the toolbar area of a DOM attrib list.
385 // By legacy, it is stored as an integer. As of 4.3.0, it is the enumeration value.
toolbarAreaFromDOMAttributes(const DomPropertyHash & attributes)386 Qt::ToolBarArea QAbstractFormBuilder::toolbarAreaFromDOMAttributes(const DomPropertyHash &attributes) {
387     const DomProperty *attr = attributes.value(QFormBuilderStrings::instance().toolBarAreaAttribute);
388     if (!attr)
389         return Qt::TopToolBarArea;
390     switch(attr->kind()) {
391     case DomProperty::Number:
392         return static_cast<Qt::ToolBarArea>(attr->elementNumber());
393     case DomProperty::Enum:
394         return enumKeyOfObjectToValue<QAbstractFormBuilderGadget, Qt::ToolBarArea>("toolBarArea",
395                                                                                    attr->elementEnum().toLatin1().constData());
396     default:
397         break;
398     }
399     return Qt::TopToolBarArea;
400 }
401 
402 /*!
403     \internal
404 */
addItem(DomWidget * ui_widget,QWidget * widget,QWidget * parentWidget)405 bool QAbstractFormBuilder::addItem(DomWidget *ui_widget, QWidget *widget, QWidget *parentWidget)
406 {
407     const QFormBuilderStrings &strings = QFormBuilderStrings::instance();
408     const DomPropertyHash attributes = propertyMap(ui_widget->elementAttribute());
409 
410     if (parentWidget == nullptr)
411         return true;
412     // Check special cases. First: Custom container
413     const QString className = QLatin1String(parentWidget->metaObject()->className());
414     const QString addPageMethod = d->customWidgetAddPageMethod(className);
415     if (!addPageMethod.isEmpty()) {
416         // If this fails ( non-existent or non-slot), use ContainerExtension in Designer, else it can't be helped
417         return QMetaObject::invokeMethod(parentWidget, addPageMethod.toUtf8().constData(), Qt::DirectConnection, Q_ARG(QWidget*, widget));
418     }
419 
420     if (QMainWindow *mw = qobject_cast<QMainWindow*>(parentWidget)) {
421 
422 #if QT_CONFIG(menubar)
423         // the menubar
424         if (QMenuBar *menuBar = qobject_cast<QMenuBar*>(widget)) {
425             mw->setMenuBar(menuBar);
426             return true;
427         }
428 #endif
429 
430 #if QT_CONFIG(toolbar)
431         // apply the toolbar's attributes
432         if (QToolBar *toolBar = qobject_cast<QToolBar*>(widget)) {
433             mw->addToolBar(toolbarAreaFromDOMAttributes(attributes), toolBar);
434             // check break
435             if (const DomProperty *attr = attributes.value(strings.toolBarBreakAttribute))
436                 if (attr->elementBool() == strings.trueValue)
437                     mw->insertToolBarBreak (toolBar);
438 
439             return true;
440         }
441 #endif
442 
443 #if QT_CONFIG(statusbar)
444         // statusBar
445         if (QStatusBar *statusBar = qobject_cast<QStatusBar*>(widget)) {
446             mw->setStatusBar(statusBar);
447             return true;
448         }
449 #endif
450 
451 #if QT_CONFIG(dockwidget)
452         // apply the dockwidget's attributes
453         if (QDockWidget *dockWidget = qobject_cast<QDockWidget*>(widget)) {
454             if (const DomProperty *attr = attributes.value(strings.dockWidgetAreaAttribute)) {
455                 Qt::DockWidgetArea area = static_cast<Qt::DockWidgetArea>(attr->elementNumber());
456                 if (!dockWidget->isAreaAllowed(area)) {
457                     if (dockWidget->isAreaAllowed(Qt::LeftDockWidgetArea))
458                         area = Qt::LeftDockWidgetArea;
459                     else if (dockWidget->isAreaAllowed(Qt::RightDockWidgetArea))
460                         area = Qt::RightDockWidgetArea;
461                     else if (dockWidget->isAreaAllowed(Qt::TopDockWidgetArea))
462                         area = Qt::TopDockWidgetArea;
463                     else if (dockWidget->isAreaAllowed(Qt::BottomDockWidgetArea))
464                         area = Qt::BottomDockWidgetArea;
465                 }
466                 mw->addDockWidget(area, dockWidget);
467             } else {
468                 mw->addDockWidget(Qt::LeftDockWidgetArea, dockWidget);
469             }
470             return true;
471         }
472 #endif
473 
474         if (!mw->centralWidget()) {
475             mw->setCentralWidget(widget);
476             return true;
477         }
478     }
479 
480 #if QT_CONFIG(tabwidget)
481     else if (QTabWidget *tabWidget = qobject_cast<QTabWidget*>(parentWidget)) {
482         widget->setParent(nullptr);
483 
484         const int tabIndex = tabWidget->count();
485         if (const DomProperty *titleP = attributes.value(strings.titleAttribute, 0))
486             tabWidget->addTab(widget, toString(titleP->elementString()));
487         else
488             tabWidget->addTab(widget, strings.defaultTitle);
489 
490         if (DomProperty *picon = attributes.value(strings.iconAttribute)) {
491             QVariant v = resourceBuilder()->loadResource(workingDirectory(), picon);
492             QVariant nativeValue = resourceBuilder()->toNativeValue(v);
493             tabWidget->setTabIcon(tabIndex, qvariant_cast<QIcon>(nativeValue));
494         }
495 
496 #if QT_CONFIG(tooltip)
497         if (const DomProperty *ptoolTip = attributes.value(strings.toolTipAttribute)) {
498             tabWidget->setTabToolTip(tabIndex, toString(ptoolTip->elementString()));
499         }
500 #endif
501 
502 #if QT_CONFIG(whatsthis)
503         if (const DomProperty *pwhatsThis = attributes.value(strings.whatsThisAttribute)) {
504             tabWidget->setTabWhatsThis(tabIndex, toString(pwhatsThis->elementString()));
505         }
506 #endif
507 
508         return true;
509     }
510 #endif
511 
512 #if QT_CONFIG(toolbox)
513     else if (QToolBox *toolBox = qobject_cast<QToolBox*>(parentWidget)) {
514         const int tabIndex = toolBox->count();
515         if (const DomProperty *labelP =  attributes.value(strings.labelAttribute, 0))
516             toolBox->addItem(widget, toString(labelP->elementString()));
517         else
518             toolBox->addItem(widget, strings.defaultTitle);
519 
520         if (DomProperty *picon = attributes.value(strings.iconAttribute)) {
521             QVariant v = resourceBuilder()->loadResource(workingDirectory(), picon);
522             QVariant nativeValue = resourceBuilder()->toNativeValue(v);
523             toolBox->setItemIcon(tabIndex, qvariant_cast<QIcon>(nativeValue));
524         }
525 
526 #if QT_CONFIG(tooltip)
527         if (const DomProperty *ptoolTip = attributes.value(strings.toolTipAttribute)) {
528             toolBox->setItemToolTip(tabIndex, toString(ptoolTip->elementString()));
529         }
530 #endif
531 
532         return true;
533     }
534 #endif
535 
536 #if QT_CONFIG(stackedwidget)
537     else if (QStackedWidget *stackedWidget = qobject_cast<QStackedWidget*>(parentWidget)) {
538         stackedWidget->addWidget(widget);
539         return true;
540     }
541 #endif
542 
543 #if QT_CONFIG(splitter)
544     else if (QSplitter *splitter = qobject_cast<QSplitter*>(parentWidget)) {
545         splitter->addWidget(widget);
546         return true;
547     }
548 #endif
549 
550 #if QT_CONFIG(mdiarea)
551     else if (QMdiArea *mdiArea = qobject_cast<QMdiArea*>(parentWidget)) {
552         mdiArea->addSubWindow(widget);
553         return true;
554     }
555 #endif
556 
557 #if QT_CONFIG(dockwidget)
558     else if (QDockWidget *dockWidget = qobject_cast<QDockWidget*>(parentWidget)) {
559         dockWidget->setWidget(widget);
560         return true;
561     }
562 #endif
563 
564 #if QT_CONFIG(scrollarea)
565     else if (QScrollArea *scrollArea = qobject_cast<QScrollArea*>(parentWidget)) {
566         scrollArea->setWidget(widget);
567         return true;
568     }
569 #endif
570 
571 #if QT_CONFIG(wizard)
572     else if (QWizard *wizard = qobject_cast<QWizard *>(parentWidget)) {
573         QWizardPage *page = qobject_cast<QWizardPage*>(widget);
574         if (!page) {
575             uiLibWarning(QCoreApplication::translate("QAbstractFormBuilder", "Attempt to add child that is not of class QWizardPage to QWizard."));
576             return false;
577         }
578         wizard->addPage(page);
579         return true;
580     }
581 #endif
582     return false;
583 }
584 
585 /*!
586     \internal
587 */
layoutInfo(DomLayout * ui_layout,QObject * parent,int * margin,int * spacing)588 void QAbstractFormBuilder::layoutInfo(DomLayout *ui_layout, QObject *parent, int *margin, int *spacing)
589 {
590     Q_UNUSED(parent);
591     const QFormBuilderStrings &strings = QFormBuilderStrings::instance();
592     const DomPropertyHash properties = propertyMap(ui_layout->elementProperty());
593 
594     int mar = INT_MIN;
595     int spac = INT_MIN;
596     if (const DomProperty *p = properties.value(strings.marginProperty, 0))
597         mar = p->elementNumber();
598 
599     if (const DomProperty *p = properties.value(strings.spacingProperty, 0))
600         spac = p->elementNumber();
601 
602 #ifdef Q_OS_MACOS
603     // here we recognize UI file < 4.3 (no we don't store margin property)
604     if (mar != INT_MIN) {
605         const int defaultMargin = parent->inherits("QLayoutWidget") ? 0 : 9;
606         if (mar == defaultMargin)
607             mar = INT_MIN;
608         if (spac == 6)
609             spac = INT_MIN;
610 
611         if (mar == INT_MIN || spac == INT_MIN) {
612             auto properties = ui_layout->elementProperty();
613             for (auto it = properties.begin(); it != properties.end(); ) {
614                 DomProperty *prop = *it;
615                 if ((mar == INT_MIN && prop->attributeName() == strings.marginProperty)
616                     || (spac == INT_MIN && prop->attributeName() == strings.spacingProperty)) {
617                     delete prop;
618                     it = properties.erase(it);
619                 } else {
620                     ++it;
621                 }
622             }
623             ui_layout->setElementProperty(properties);
624         }
625     }
626 #endif
627     if (margin)
628         *margin = mar;
629     if (spacing)
630         *spacing = spac;
631 }
632 
633 /*!
634     \internal
635 */
create(DomLayout * ui_layout,QLayout * parentLayout,QWidget * parentWidget)636 QLayout *QAbstractFormBuilder::create(DomLayout *ui_layout, QLayout *parentLayout, QWidget *parentWidget)
637 {
638     QObject *p = parentLayout;
639 
640     if (p == nullptr)
641         p = parentWidget;
642 
643     Q_ASSERT(p != nullptr);
644 
645     bool tracking = false;
646 
647     if (p == parentWidget && parentWidget->layout()) {
648         tracking = true;
649         p = parentWidget->layout();
650     }
651 
652     QLayout *layout = createLayout(ui_layout->attributeClass(), p, ui_layout->hasAttributeName() ? ui_layout->attributeName() : QString());
653 
654     if (layout == nullptr)
655         return 0;
656 
657     if (tracking && layout->parent() == nullptr) {
658         QBoxLayout *box = qobject_cast<QBoxLayout*>(parentWidget->layout());
659         if (!box) {  // only QBoxLayout is supported
660             const QString widgetClass = QString::fromUtf8(parentWidget->metaObject()->className());
661             const QString layoutClass = QString::fromUtf8(parentWidget->layout()->metaObject()->className());
662             const QString msg = QCoreApplication::translate("QAbstractFormBuilder", "Attempt to add a layout to a widget '%1' (%2) which already has a layout of non-box type %3.\n"
663                                             "This indicates an inconsistency in the ui-file.").
664                                             arg(parentWidget->objectName(), widgetClass, layoutClass);
665             uiLibWarning(msg);
666             return nullptr;
667         }
668         box->addLayout(layout);
669     }
670 
671     int margin = INT_MIN, spacing = INT_MIN;
672     layoutInfo(ui_layout, p, &margin, &spacing);
673 
674     if (margin != INT_MIN) {
675         layout->setContentsMargins(margin, margin, margin, margin);
676     } else {
677         const QFormBuilderStrings &strings = QFormBuilderStrings::instance();
678         int left, top, right, bottom;
679         left = top = right = bottom = -1;
680         layout->getContentsMargins(&left, &top, &right, &bottom);
681 
682         const DomPropertyHash properties = propertyMap(ui_layout->elementProperty());
683 
684         if (const DomProperty *p = properties.value(strings.leftMarginProperty, 0))
685             left = p->elementNumber();
686 
687         if (const DomProperty *p = properties.value(strings.topMarginProperty, 0))
688             top = p->elementNumber();
689 
690         if (const DomProperty *p = properties.value(strings.rightMarginProperty, 0))
691             right = p->elementNumber();
692 
693         if (const DomProperty *p = properties.value(strings.bottomMarginProperty, 0))
694             bottom = p->elementNumber();
695 
696         layout->setContentsMargins(left, top, right, bottom);
697     }
698 
699     if (spacing != INT_MIN) {
700         layout->setSpacing(spacing);
701     } else {
702         QGridLayout *grid = qobject_cast<QGridLayout *>(layout);
703         if (grid) {
704             const QFormBuilderStrings &strings = QFormBuilderStrings::instance();
705             const DomPropertyHash properties = propertyMap(ui_layout->elementProperty());
706 
707             if (const DomProperty *p = properties.value(strings.horizontalSpacingProperty, 0))
708                 grid->setHorizontalSpacing(p->elementNumber());
709             if (const DomProperty *p = properties.value(strings.verticalSpacingProperty, 0))
710                 grid->setVerticalSpacing(p->elementNumber());
711         }
712     }
713 
714     applyProperties(layout, ui_layout->elementProperty());
715 
716     const auto &elementItem = ui_layout->elementItem();
717     for (DomLayoutItem *ui_item : elementItem) {
718         if (QLayoutItem *item = create(ui_item, layout, parentWidget)) {
719             addItem(ui_item, item, layout);
720         }
721     }
722     // Check the box stretch attributes
723     if (QBoxLayout *box = qobject_cast<QBoxLayout*>(layout)) {
724         const QString boxStretch = ui_layout->attributeStretch();
725         if (!boxStretch.isEmpty())
726             QFormBuilderExtra::setBoxLayoutStretch(boxStretch, box);
727     }
728     // Check the grid stretch/minimum size attributes
729     if (QGridLayout *grid = qobject_cast<QGridLayout*>(layout)) {
730         // Stretch
731         const QString gridRowStretch = ui_layout->attributeRowStretch();
732         if (!gridRowStretch.isEmpty())
733             QFormBuilderExtra::setGridLayoutRowStretch(gridRowStretch, grid);
734         const QString gridColumnStretch = ui_layout->attributeColumnStretch();
735         if (!gridColumnStretch.isEmpty())
736             QFormBuilderExtra::setGridLayoutColumnStretch(gridColumnStretch, grid);
737         // Minimum size
738         const QString gridColumnMinimumWidth = ui_layout->attributeColumnMinimumWidth();
739         if (!gridColumnMinimumWidth.isEmpty())
740             QFormBuilderExtra::setGridLayoutColumnMinimumWidth(gridColumnMinimumWidth, grid);
741         const QString gridRowMinimumHeight = ui_layout->attributeRowMinimumHeight();
742         if (!gridRowMinimumHeight.isEmpty())
743             QFormBuilderExtra::setGridLayoutRowMinimumHeight(gridRowMinimumHeight, grid);
744     }
745     return layout;
746 }
747 
748 #if QT_CONFIG(formlayout)
formLayoutRole(int column,int colspan)749 static inline QFormLayout::ItemRole formLayoutRole(int column, int colspan)
750 {
751     if (colspan > 1)
752         return QFormLayout::SpanningRole;
753     return column == 0 ? QFormLayout::LabelRole : QFormLayout::FieldRole;
754 }
755 #endif
756 
alignmentValue(Qt::Alignment a)757 static inline QString alignmentValue(Qt::Alignment a)
758 {
759     QString h,v;
760     switch (a & Qt::AlignHorizontal_Mask) {
761     case Qt::AlignLeft:
762         h = QStringLiteral("Qt::AlignLeft");
763         break;
764     case Qt::AlignRight:
765         h = QStringLiteral("Qt::AlignRight");
766         break;
767     case Qt::AlignHCenter:
768         h = QStringLiteral("Qt::AlignHCenter");
769         break;
770     case Qt::AlignJustify:
771         h = QStringLiteral("Qt::AlignJustify");
772         break;
773     }
774     switch (a & Qt::AlignVertical_Mask) {
775     case Qt::AlignTop:
776         v = QStringLiteral("Qt::AlignTop");
777         break;
778     case Qt::AlignBottom:
779         v = QStringLiteral("Qt::AlignBottom");
780         break;
781     case Qt::AlignVCenter:
782         v = QStringLiteral("Qt::AlignVCenter");
783         break;
784     }
785     if (h.isEmpty() && v.isEmpty())
786         return QString();
787     if (!v.isEmpty()) {
788         if (!h.isEmpty())
789             h += QLatin1Char('|');
790         h += v;
791     }
792     return h;
793 }
794 
alignmentFromDom(const QString & in)795 static inline Qt::Alignment alignmentFromDom(const QString &in)
796 {
797     Qt::Alignment rc;
798     if (!in.isEmpty()) {
799         const auto flags = in.splitRef(QLatin1Char('|'));
800         for (const auto &f : flags) {
801             if (f == QStringLiteral("Qt::AlignLeft")) {
802                 rc |= Qt::AlignLeft;
803             } else if (f == QStringLiteral("Qt::AlignRight")) {
804                 rc |= Qt::AlignRight;
805             } else if (f == QStringLiteral("Qt::AlignHCenter")) {
806                 rc |= Qt::AlignHCenter;
807             } else if (f == QStringLiteral("Qt::AlignJustify")) {
808                 rc |= Qt::AlignJustify;
809             } else if (f == QStringLiteral("Qt::AlignTop")) {
810                 rc |= Qt::AlignTop;
811             } else if (f == QStringLiteral("Qt::AlignBottom")) {
812                 rc |= Qt::AlignBottom;
813             } else if (f == QStringLiteral("Qt::AlignVCenter")) {
814                 rc |= Qt::AlignVCenter;
815             }
816         }
817     }
818     return rc;
819 }
820 
821 /*!
822     \internal
823 */
addItem(DomLayoutItem * ui_item,QLayoutItem * item,QLayout * layout)824 bool QAbstractFormBuilder::addItem(DomLayoutItem *ui_item, QLayoutItem *item, QLayout *layout)
825 {
826     // Calling addChildWidget(), etc.  is required to maintain consistency of the layouts,
827     // see documentation of addItem(), which should ideally not be used.
828     if (item->widget()) {
829         static_cast<QFriendlyLayout*>(layout)->addChildWidget(item->widget());
830     } else if (item->layout()) {
831         static_cast<QFriendlyLayout*>(layout)->addChildLayout(item->layout());
832     } else if (item->spacerItem()) {
833         // nothing to do
834     } else {
835         return false;
836     }
837 
838     if (QGridLayout *grid = qobject_cast<QGridLayout*>(layout)) {
839         const int rowSpan = ui_item->hasAttributeRowSpan() ? ui_item->attributeRowSpan() : 1;
840         const int colSpan = ui_item->hasAttributeColSpan() ? ui_item->attributeColSpan() : 1;
841         grid->addItem(item, ui_item->attributeRow(), ui_item->attributeColumn(),
842                         rowSpan, colSpan, item->alignment());
843         return true;
844     }
845 #if QT_CONFIG(formlayout)
846     if (QFormLayout *form = qobject_cast<QFormLayout *>(layout)) {
847         const int row =  ui_item->attributeRow();
848         const int colSpan = ui_item->hasAttributeColSpan() ? ui_item->attributeColSpan() : 1;
849         form->setItem(row, formLayoutRole(ui_item->attributeColumn(), colSpan), item);
850         return true;
851     }
852 
853 #endif
854     layout->addItem(item);
855     return true;
856 }
857 
858 /*!
859     \internal
860 */
create(DomLayoutItem * ui_layoutItem,QLayout * layout,QWidget * parentWidget)861 QLayoutItem *QAbstractFormBuilder::create(DomLayoutItem *ui_layoutItem, QLayout *layout, QWidget *parentWidget)
862 {
863     switch (ui_layoutItem->kind()) {
864     case DomLayoutItem::Widget: {
865         if (QWidget *w = create(ui_layoutItem->elementWidget(), parentWidget)) {
866 #ifdef QFORMINTERNAL_NAMESPACE // uilib
867             QWidgetItem *item = new QWidgetItemV2(w);
868 #else                         // Within Designer: Use factory method that returns special items that refuse to shrink to 0,0
869             QWidgetItem *item = QLayoutPrivate::createWidgetItem(layout, w);
870 #endif
871             item->setAlignment(alignmentFromDom(ui_layoutItem->attributeAlignment()));
872             return item;
873         }
874         qWarning() << QCoreApplication::translate("QAbstractFormBuilder", "Empty widget item in %1 '%2'.").arg(QString::fromUtf8(layout->metaObject()->className()), layout->objectName());
875         return nullptr;
876     }
877     case DomLayoutItem::Spacer: {
878         QSize size(0, 0);
879         QSizePolicy::Policy sizeType = QSizePolicy::Expanding;
880         bool isVspacer = false;
881 
882         const DomSpacer *ui_spacer = ui_layoutItem->elementSpacer();
883         const auto &spacerProperties =  ui_spacer->elementProperty();
884         if (!spacerProperties.isEmpty()) {
885             const QFormBuilderStrings &strings = QFormBuilderStrings::instance();
886             for (DomProperty *p : spacerProperties) {
887                 const QVariant v = toVariant(&QAbstractFormBuilderGadget::staticMetaObject, p); // ### remove me
888                 if (v.isNull())
889                     continue;
890                 if (p->attributeName() == strings.sizeHintProperty && p->kind() == DomProperty::Size) {
891                     size = v.toSize();  // ###  remove me
892                 } else if (p->attributeName() == strings.sizeTypeProperty && p->kind() == DomProperty::Enum) {
893                     sizeType = static_cast<QSizePolicy::Policy>(v.toInt());
894                 } else if (p->attributeName() == strings.orientationProperty && p->kind() == DomProperty::Enum) {
895                     const Qt::Orientation o = static_cast<Qt::Orientation>(v.toInt());
896                     isVspacer = (o == Qt::Vertical);
897                 }
898             }
899         }
900 
901         QSpacerItem *spacer = nullptr;
902         if (isVspacer)
903             spacer = new QSpacerItem(size.width(), size.height(), QSizePolicy::Minimum, sizeType);
904         else
905             spacer = new QSpacerItem(size.width(), size.height(), sizeType, QSizePolicy::Minimum);
906         return spacer; }
907 
908     case DomLayoutItem::Layout:
909         return create(ui_layoutItem->elementLayout(), layout, parentWidget);
910 
911     default:
912         break;
913     }
914 
915     return nullptr;
916 }
917 
918 /*!
919     \internal
920 */
applyProperties(QObject * o,const QList<DomProperty * > & properties)921 void QAbstractFormBuilder::applyProperties(QObject *o, const QList<DomProperty*> &properties)
922 {
923     for (DomProperty *p : properties) {
924         const QVariant v = toVariant(o->metaObject(), p);
925         if (!v.isNull()) {
926             QString attributeName = p->attributeName();
927             if (attributeName == QLatin1String("numDigits") && o->inherits("QLCDNumber")) // Deprecated in Qt 4, removed in Qt 5.
928                 attributeName = QLatin1String("digitCount");
929             if (!d->applyPropertyInternally(o, attributeName, v))
930                 o->setProperty(attributeName.toUtf8(), v);
931         }
932     }
933 }
934 
935 
936 /*!
937     \internal
938     Check whether a property is applied internally by QAbstractFormBuilder. Call this
939    from overwritten applyProperties().
940 */
941 
applyPropertyInternally(QObject * o,const QString & propertyName,const QVariant & value)942 bool QAbstractFormBuilder::applyPropertyInternally(QObject *o, const QString &propertyName, const QVariant &value)
943 {
944     return d->applyPropertyInternally(o,propertyName, value);
945 }
946 
947 /*!
948     \internal
949 */
950 
toVariant(const QMetaObject * meta,DomProperty * p)951 QVariant QAbstractFormBuilder::toVariant(const QMetaObject *meta, DomProperty *p)
952 {
953     return domPropertyToVariant(this, meta, p);
954 }
955 
956 /*!
957     \internal
958 */
setupColorGroup(QPalette & palette,QPalette::ColorGroup colorGroup,DomColorGroup * group)959 void QAbstractFormBuilder::setupColorGroup(QPalette &palette, QPalette::ColorGroup colorGroup,
960             DomColorGroup *group)
961 {
962     QFormBuilderExtra::setupColorGroup(&palette, colorGroup, group);
963 }
964 
965 /*!
966     \internal
967 */
saveColorGroup(const QPalette & palette)968 DomColorGroup *QAbstractFormBuilder::saveColorGroup(const QPalette &palette)
969 {
970     return QFormBuilderExtra::saveColorGroup(palette,
971                                              palette.currentColorGroup());
972 }
973 
974 /*!
975     \internal
976 */
setupBrush(DomBrush * brush)977 QBrush QAbstractFormBuilder::setupBrush(DomBrush *brush)
978 {
979     return QFormBuilderExtra::setupBrush(brush);
980 }
981 
saveBrush(const QBrush & br)982 DomBrush *QAbstractFormBuilder::saveBrush(const QBrush &br)
983 {
984     return QFormBuilderExtra::saveBrush(br);
985 }
986 
987 /*!
988     \internal
989 */
createWidget(const QString & widgetName,QWidget * parentWidget,const QString & name)990 QWidget *QAbstractFormBuilder::createWidget(const QString &widgetName, QWidget *parentWidget, const QString &name)
991 {
992     Q_UNUSED(widgetName);
993     Q_UNUSED(parentWidget);
994     Q_UNUSED(name);
995     return nullptr;
996 }
997 
998 /*!
999     \internal
1000 */
createLayout(const QString & layoutName,QObject * parent,const QString & name)1001 QLayout *QAbstractFormBuilder::createLayout(const QString &layoutName, QObject *parent, const QString &name)
1002 {
1003     Q_UNUSED(layoutName);
1004     Q_UNUSED(parent);
1005     Q_UNUSED(name);
1006     return nullptr;
1007 }
1008 
1009 /*!
1010     \internal
1011 */
createAction(QObject * parent,const QString & name)1012 QAction *QAbstractFormBuilder::createAction(QObject *parent, const QString &name)
1013 {
1014     QAction *action = new QAction(parent);
1015     action->setObjectName(name);
1016     return action;
1017 }
1018 
1019 /*!
1020     \internal
1021 */
createActionGroup(QObject * parent,const QString & name)1022 QActionGroup *QAbstractFormBuilder::createActionGroup(QObject *parent, const QString &name)
1023 {
1024     QActionGroup *g = new QActionGroup(parent);
1025     g->setObjectName(name);
1026     return g;
1027 }
1028 
1029 /*!
1030     \fn void QAbstractFormBuilder::save(QIODevice *device, QWidget *widget)
1031 
1032     Saves an XML representation of the given \a widget to the
1033     specified \a device in the standard UI file format.
1034 
1035     \note Unlike when saving a form in Qt Designer, all property values are
1036     written. This is because, the state of whether a property value was
1037     modified or not isn't stored in the Qt property system. The widget that
1038     is being saved, could have been created dynamically, not loaded via
1039     \l load(), so in this case the form builder isn't aware of the list of
1040     changed properties. Also, there's no generic way to do this for widgets
1041     that were created dynamically.
1042 
1043     Therefore, you should remove properties that are not required from your
1044     resulting XML files, before loading them. Alternatively, if you already
1045     know which properties you want to save when you call this method,
1046     you can overload \c computeProperties() and return a filtered list of
1047     required properties. Otherwise, unexpected behavior may occur as some
1048     of these properties may depend on each other.
1049 
1050     \sa load()
1051 */
save(QIODevice * dev,QWidget * widget)1052 void QAbstractFormBuilder::save(QIODevice *dev, QWidget *widget)
1053 {
1054     DomWidget *ui_widget = createDom(widget, nullptr);
1055     Q_ASSERT( ui_widget != nullptr );
1056 
1057     DomUI *ui = new DomUI();
1058     ui->setAttributeVersion(QStringLiteral("4.0"));
1059     ui->setElementWidget(ui_widget);
1060 
1061     saveDom(ui, widget);
1062 
1063     QXmlStreamWriter writer(dev);
1064     writer.setAutoFormatting(true);
1065     writer.setAutoFormattingIndent(1);
1066     writer.writeStartDocument();
1067     ui->write(writer);
1068     writer.writeEndDocument();
1069 
1070     d->m_laidout.clear();
1071 
1072     delete ui;
1073 }
1074 
1075 /*!
1076     \internal
1077 */
saveDom(DomUI * ui,QWidget * widget)1078 void QAbstractFormBuilder::saveDom(DomUI *ui, QWidget *widget)
1079 {
1080     ui->setElementClass(widget->objectName());
1081 
1082     if (DomConnections *ui_connections = saveConnections()) {
1083         ui->setElementConnections(ui_connections);
1084     }
1085 
1086     if (DomCustomWidgets *ui_customWidgets = saveCustomWidgets()) {
1087         ui->setElementCustomWidgets(ui_customWidgets);
1088     }
1089 
1090     if (DomTabStops *ui_tabStops = saveTabStops()) {
1091         ui->setElementTabStops(ui_tabStops);
1092     }
1093 
1094     if (DomResources *ui_resources = saveResources()) {
1095         ui->setElementResources(ui_resources);
1096     }
1097     if (DomButtonGroups *ui_buttonGroups = saveButtonGroups(widget))
1098         ui->setElementButtonGroups(ui_buttonGroups);
1099 }
1100 
1101 /*!
1102     \internal
1103 */
saveConnections()1104 DomConnections *QAbstractFormBuilder::saveConnections()
1105 {
1106     return new DomConnections;
1107 }
1108 
1109 /*!
1110     \internal
1111 */
1112 
createDom(QWidget * widget,DomWidget * ui_parentWidget,bool recursive)1113 DomWidget *QAbstractFormBuilder::createDom(QWidget *widget, DomWidget *ui_parentWidget, bool recursive)
1114 {
1115     DomWidget *ui_widget = new DomWidget();
1116     ui_widget->setAttributeClass(QLatin1String(widget->metaObject()->className()));
1117     ui_widget->setElementProperty(computeProperties(widget));
1118 
1119     if (recursive) {
1120         if (QLayout *layout = widget->layout()) {
1121             if (DomLayout *ui_layout = createDom(layout, nullptr, ui_parentWidget)) {
1122                 QVector<DomLayout *> ui_layouts;
1123                 ui_layouts.append(ui_layout);
1124 
1125                 ui_widget->setElementLayout(ui_layouts);
1126             }
1127         }
1128     }
1129 
1130     // widgets, actions and action groups
1131     QVector<DomWidget *> ui_widgets;
1132     QVector<DomAction *> ui_actions;
1133     QVector<DomActionGroup *> ui_action_groups;
1134 
1135     QObjectList children;
1136 
1137     // splitters need to store their children in the order specified by child indexes,
1138     // not the order of the child list.
1139 #if QT_CONFIG(splitter)
1140     if (const QSplitter *splitter = qobject_cast<const QSplitter*>(widget)) {
1141         const int count = splitter->count();
1142         for (int i = 0; i < count; ++i)
1143             children.append(splitter->widget(i));
1144     } else
1145 #endif
1146     {
1147         QObjectList childObjects = widget->children();
1148 
1149         const QWidgetList list = qvariant_cast<QWidgetList>(widget->property("_q_widgetOrder"));
1150         for (QWidget *w : list) {
1151             if (childObjects.contains(w)) {
1152                 children.append(w);
1153                 childObjects.removeAll(w);
1154             }
1155         }
1156         children += childObjects;
1157 
1158         const QWidgetList zOrder = qvariant_cast<QWidgetList>(widget->property("_q_zOrder"));
1159         if (list != zOrder) {
1160             QStringList zOrderList;
1161             zOrderList.reserve(zOrder.size());
1162             std::transform(zOrder.cbegin(), zOrder.cend(),
1163                            std::back_inserter(zOrderList),
1164                            [] (const QWidget *w) { return w->objectName(); });
1165             ui_widget->setElementZOrder(zOrderList);
1166         }
1167     }
1168 
1169     for (QObject *obj : qAsConst(children)) {
1170         if (QWidget *childWidget = qobject_cast<QWidget*>(obj)) {
1171             if (d->m_laidout.contains(childWidget) || !recursive)
1172                 continue;
1173 
1174             if (QMenu *menu = qobject_cast<QMenu *>(childWidget)) {
1175                 const auto actions = menu->parentWidget()->actions();
1176                 const bool found =
1177                     std::any_of(actions.cbegin(), actions.cend(),
1178                                 [menu] (const QAction *a) { return a->menu() == menu; });
1179                 if (!found)
1180                     continue;
1181             }
1182 
1183             if (DomWidget *ui_child = createDom(childWidget, ui_widget)) {
1184                 ui_widgets.append(ui_child);
1185             }
1186         } else if (QAction *childAction = qobject_cast<QAction*>(obj)) {
1187             if (childAction->actionGroup() != nullptr) {
1188                 // it will be added later.
1189                 continue;
1190             }
1191 
1192             if (DomAction *ui_action = createDom(childAction)) {
1193                 ui_actions.append(ui_action);
1194             }
1195         } else if (QActionGroup *childActionGroup = qobject_cast<QActionGroup*>(obj)) {
1196             if (DomActionGroup *ui_action_group = createDom(childActionGroup)) {
1197                 ui_action_groups.append(ui_action_group);
1198             }
1199         }
1200     }
1201 
1202     // add-action
1203     QVector<DomActionRef *> ui_action_refs;
1204     const auto &actions = widget->actions();
1205     ui_action_refs.reserve(actions.size());
1206     for (QAction *action : actions) {
1207         if (DomActionRef *ui_action_ref = createActionRefDom(action)) {
1208             ui_action_refs.append(ui_action_ref);
1209         }
1210     }
1211 
1212     if (recursive)
1213         ui_widget->setElementWidget(ui_widgets);
1214 
1215     ui_widget->setElementAction(ui_actions);
1216     ui_widget->setElementActionGroup(ui_action_groups);
1217     ui_widget->setElementAddAction(ui_action_refs);
1218 
1219     saveExtraInfo(widget, ui_widget, ui_parentWidget);
1220 
1221     return ui_widget;
1222 }
1223 
1224 /*!
1225     \internal
1226 */
createActionRefDom(QAction * action)1227 DomActionRef *QAbstractFormBuilder::createActionRefDom(QAction *action)
1228 {
1229     QString name = action->objectName();
1230 
1231     if (action->menu() != nullptr)
1232         name = action->menu()->objectName();
1233 
1234     DomActionRef *ui_action_ref = new DomActionRef();
1235     if (action->isSeparator())
1236         ui_action_ref->setAttributeName(QFormBuilderStrings::instance().separator);
1237     else
1238         ui_action_ref->setAttributeName(name);
1239 
1240     return ui_action_ref;
1241 }
1242 
1243 // Struct to store layout item parameters for saving layout items
1244 struct FormBuilderSaveLayoutEntry {
FormBuilderSaveLayoutEntryFormBuilderSaveLayoutEntry1245     explicit FormBuilderSaveLayoutEntry(QLayoutItem *li = nullptr) :
1246         item(li) {}
1247 
1248     void setAlignment(Qt::Alignment al);
1249 
1250     QLayoutItem *item;
1251     int row = -1;
1252     int column = -1;
1253     int rowSpan = 0;
1254     int columnSpan = 0;
1255     Qt::Alignment alignment;
1256 };
1257 
1258 // filter out the case of "Spacer" and "QLayoutWidget" widgets
setAlignment(Qt::Alignment al)1259 void FormBuilderSaveLayoutEntry::setAlignment(Qt::Alignment al)
1260 {
1261     if (const QWidget *widget = item->widget()) {
1262         const char *className = widget->metaObject()->className();
1263         if (qstrcmp(className, "Spacer") && qstrcmp(className, "QLayoutWidget"))
1264             alignment = al;
1265     }
1266 }
1267 
1268 // Create list from standard box layout
saveLayoutEntries(const QLayout * layout)1269 static QList<FormBuilderSaveLayoutEntry> saveLayoutEntries(const QLayout *layout)
1270 {
1271     QList<FormBuilderSaveLayoutEntry> rc;
1272     if (const int count = layout->count()) {
1273         rc.reserve(count);
1274         for (int idx = 0; idx < count; ++idx) {
1275             QLayoutItem *item = layout->itemAt(idx);
1276             FormBuilderSaveLayoutEntry entry(item);
1277             entry.setAlignment(item->alignment());
1278             rc.append(entry);
1279         }
1280     }
1281     return rc;
1282 }
1283 
1284 // Create list from grid layout
saveGridLayoutEntries(QGridLayout * gridLayout)1285 static QList<FormBuilderSaveLayoutEntry> saveGridLayoutEntries(QGridLayout *gridLayout)
1286 {
1287     QList<FormBuilderSaveLayoutEntry> rc;
1288     if (const int count = gridLayout->count()) {
1289         rc.reserve(count);
1290         for (int idx = 0; idx < count; ++idx) {
1291             QLayoutItem *item = gridLayout->itemAt(idx);
1292             FormBuilderSaveLayoutEntry entry(item);
1293             gridLayout->getItemPosition(idx, &entry.row, &entry.column, &entry.rowSpan,&entry.columnSpan);
1294             entry.setAlignment(item->alignment());
1295             rc.append(entry);
1296         }
1297     }
1298     return rc;
1299 }
1300 
1301 #if QT_CONFIG(formlayout)
1302 // Create list from form layout
saveFormLayoutEntries(const QFormLayout * formLayout)1303 static QList<FormBuilderSaveLayoutEntry> saveFormLayoutEntries(const QFormLayout *formLayout)
1304 {
1305     QList<FormBuilderSaveLayoutEntry> rc;
1306     if (const int count = formLayout->count()) {
1307         rc.reserve(count);
1308         for (int idx = 0; idx < count; ++idx) {
1309             QLayoutItem *item = formLayout->itemAt(idx);
1310             QFormLayout::ItemRole role = QFormLayout::LabelRole;
1311             FormBuilderSaveLayoutEntry entry(item);
1312             formLayout->getItemPosition(idx, &entry.row, &role);
1313             switch (role ) {
1314             case QFormLayout::LabelRole:
1315                 entry.column = 0;
1316                 break;
1317             case QFormLayout::FieldRole:
1318                 entry.column = 1;
1319                 break;
1320             case QFormLayout::SpanningRole:
1321                 entry.column = 0;
1322                 entry.columnSpan = 2;
1323                 break;
1324             }
1325             rc.push_back(entry);
1326         }
1327     }
1328     return rc;
1329 }
1330 #endif
1331 
1332 /*!
1333     \internal
1334 */
1335 
createDom(QLayout * layout,DomLayout * ui_layout,DomWidget * ui_parentWidget)1336 DomLayout *QAbstractFormBuilder::createDom(QLayout *layout, DomLayout *ui_layout, DomWidget *ui_parentWidget)
1337 {
1338     Q_UNUSED(ui_layout);
1339     DomLayout *lay = new DomLayout();
1340     lay->setAttributeClass(QLatin1String(layout->metaObject()->className()));
1341     const QString objectName = layout->objectName();
1342     if (!objectName.isEmpty())
1343         lay->setAttributeName(objectName);
1344     lay->setElementProperty(computeProperties(layout));
1345 
1346     QList<FormBuilderSaveLayoutEntry> newList;
1347     if (QGridLayout *gridLayout = qobject_cast<QGridLayout *>(layout)) {
1348         newList = saveGridLayoutEntries(gridLayout);
1349 #if QT_CONFIG(formlayout)
1350     } else if (const QFormLayout *formLayout = qobject_cast<const QFormLayout *>(layout)) {
1351         newList = saveFormLayoutEntries(formLayout);
1352 #endif
1353     } else {
1354         newList = saveLayoutEntries(layout);
1355     }
1356 
1357     QVector<DomLayoutItem *> ui_items;
1358     ui_items.reserve(newList.size());
1359     for (const FormBuilderSaveLayoutEntry &item : qAsConst(newList)) {
1360         if (DomLayoutItem *ui_item = createDom(item.item, lay, ui_parentWidget)) {
1361             if (item.row >= 0)
1362                 ui_item->setAttributeRow(item.row);
1363             if (item.column >= 0)
1364                 ui_item->setAttributeColumn(item.column);
1365             if (item.rowSpan > 1)
1366                 ui_item->setAttributeRowSpan(item.rowSpan);
1367             if (item.columnSpan > 1)
1368                 ui_item->setAttributeColSpan(item.columnSpan);
1369             if (item.alignment)
1370                 ui_item->setAttributeAlignment(alignmentValue(item.alignment));
1371             ui_items.append(ui_item);
1372         }
1373     }
1374 
1375     lay->setElementItem(ui_items);
1376 
1377     return lay;
1378 }
1379 
1380 /*!
1381     \internal
1382 */
createDom(QLayoutItem * item,DomLayout * ui_layout,DomWidget * ui_parentWidget)1383 DomLayoutItem *QAbstractFormBuilder::createDom(QLayoutItem *item, DomLayout *ui_layout, DomWidget *ui_parentWidget)
1384 {
1385     DomLayoutItem *ui_item = new DomLayoutItem();
1386 
1387     if (item->widget())  {
1388         ui_item->setElementWidget(createDom(item->widget(), ui_parentWidget));
1389         d->m_laidout.insert(item->widget(), true);
1390     } else if (item->layout()) {
1391         ui_item->setElementLayout(createDom(item->layout(), ui_layout, ui_parentWidget));
1392     } else if (item->spacerItem()) {
1393         ui_item->setElementSpacer(createDom(item->spacerItem(), ui_layout, ui_parentWidget));
1394     }
1395 
1396     return ui_item;
1397 }
1398 
1399 /*!
1400     \internal
1401 */
createDom(QSpacerItem * spacer,DomLayout * ui_layout,DomWidget * ui_parentWidget)1402 DomSpacer *QAbstractFormBuilder::createDom(QSpacerItem *spacer, DomLayout *ui_layout, DomWidget *ui_parentWidget)
1403 {
1404     Q_UNUSED(ui_layout);
1405     Q_UNUSED(ui_parentWidget);
1406 
1407     DomSpacer *ui_spacer = new DomSpacer();
1408     QList<DomProperty*> properties;
1409 
1410     DomProperty *prop = nullptr;
1411     const QFormBuilderStrings &strings = QFormBuilderStrings::instance();
1412     // sizeHint property
1413     prop = new DomProperty();
1414     prop->setAttributeName(strings.sizeHintProperty);
1415     prop->setElementSize(new DomSize());
1416     prop->elementSize()->setElementWidth(spacer->sizeHint().width());
1417     prop->elementSize()->setElementHeight(spacer->sizeHint().height());
1418     properties.append(prop);
1419 
1420     // orientation property
1421     prop = new DomProperty(); // ### we don't implemented the case where expandingDirections() is both Vertical and Horizontal
1422     prop->setAttributeName(strings.orientationProperty);
1423     prop->setElementEnum((spacer->expandingDirections() & Qt::Horizontal) ? strings.qtHorizontal : strings.qtVertical);
1424     properties.append(prop);
1425 
1426     ui_spacer->setElementProperty(properties);
1427     return ui_spacer;
1428 }
1429 
1430 /*!
1431     \internal
1432 */
createProperty(QObject * obj,const QString & pname,const QVariant & v)1433 DomProperty *QAbstractFormBuilder::createProperty(QObject *obj, const QString &pname, const QVariant &v)
1434 {
1435     if (!checkProperty(obj, pname)) {
1436         return nullptr;
1437     }
1438     return variantToDomProperty(this, obj->metaObject(), pname, v);
1439 }
1440 
1441 /*!
1442     \internal
1443 */
computeProperties(QObject * obj)1444 QList<DomProperty*> QAbstractFormBuilder::computeProperties(QObject *obj)
1445 {
1446     QList<DomProperty*> lst;
1447 
1448     const QMetaObject *meta = obj->metaObject();
1449 
1450     QHash<QByteArray, bool> properties;
1451     const int propertyCount = meta->propertyCount();
1452     for(int i=0; i < propertyCount; ++i)
1453         properties.insert(meta->property(i).name(), true);
1454 
1455     const auto propertyNames = properties.keys();
1456 
1457     const int propertyNamesCount = propertyNames.size();
1458     for(int i=0; i<propertyNamesCount ; ++i) {
1459         const QString pname = QString::fromUtf8(propertyNames.at(i));
1460         const QMetaProperty prop = meta->property(meta->indexOfProperty(pname.toUtf8()));
1461 
1462         if (!prop.isWritable() || !checkProperty(obj, QLatin1String(prop.name())))
1463             continue;
1464 
1465         const QVariant v = prop.read(obj);
1466 
1467         DomProperty *dom_prop = nullptr;
1468         if (v.type() == QVariant::Int) {
1469             dom_prop = new DomProperty();
1470 
1471             if (prop.isFlagType())
1472                 uiLibWarning(QCoreApplication::translate("QAbstractFormBuilder", "Flags property are not supported yet."));
1473 
1474             if (prop.isEnumType()) {
1475                 QString scope = QString::fromUtf8(prop.enumerator().scope());
1476                 if (scope.size())
1477                     scope += QString::fromUtf8("::");
1478                 const QString e = QString::fromUtf8(prop.enumerator().valueToKey(v.toInt()));
1479                 if (e.size())
1480                     dom_prop->setElementEnum(scope + e);
1481             } else
1482                 dom_prop->setElementNumber(v.toInt());
1483             dom_prop->setAttributeName(pname);
1484         } else {
1485             dom_prop = createProperty(obj, pname, v);
1486         }
1487 
1488         if (!dom_prop || dom_prop->kind() == DomProperty::Unknown)
1489             delete dom_prop;
1490         else
1491             lst.append(dom_prop);
1492     }
1493 
1494     return lst;
1495 }
1496 
1497 
1498 /*!
1499    \internal
1500    \typedef QAbstractFormBuilder::DomPropertyHash
1501    \typedef QAbstractFormBuilder::IconPaths
1502 */
1503 
1504 
1505 /*!
1506     \internal
1507 */
propertyMap(const QList<DomProperty * > & properties)1508 QAbstractFormBuilder::DomPropertyHash QAbstractFormBuilder::propertyMap(const QList<DomProperty*> &properties)
1509 {
1510     DomPropertyHash map;
1511 
1512     for (DomProperty *p : properties)
1513         map.insert(p->attributeName(), p);
1514 
1515     return map;
1516 }
1517 
1518 /*!
1519     \internal
1520 */
checkProperty(QObject * obj,const QString & prop) const1521 bool QAbstractFormBuilder::checkProperty(QObject *obj, const QString &prop) const
1522 {
1523     Q_UNUSED(obj);
1524     Q_UNUSED(prop);
1525 
1526     return true;
1527 }
1528 
1529 /*!
1530     \internal
1531 */
toString(const DomString * str)1532 QString QAbstractFormBuilder::toString(const DomString *str)
1533 {
1534     return str ? str->text() : QString();
1535 }
1536 
1537 /*!
1538     \internal
1539 */
applyTabStops(QWidget * widget,DomTabStops * tabStops)1540 void QAbstractFormBuilder::applyTabStops(QWidget *widget, DomTabStops *tabStops)
1541 {
1542     if (!tabStops)
1543         return;
1544 
1545     const QStringList &names = tabStops->elementTabStop();
1546     QWidgetList widgets;
1547     widgets.reserve(names.size());
1548     for (const QString &name : names) {
1549         if (QWidget *child = widget->findChild<QWidget*>(name)) {
1550             widgets.append(child);
1551         } else {
1552             uiLibWarning(QCoreApplication::translate("QAbstractFormBuilder",
1553                                                      "While applying tab stops: The widget '%1' could not be found.")
1554                                                      .arg(name));
1555         }
1556     }
1557 
1558     for (int i = 1, count = widgets.size(); i < count; ++i)
1559         QWidget::setTabOrder(widgets.at(i - 1), widgets.at(i));
1560 }
1561 
1562 /*!
1563     \internal
1564 */
saveCustomWidgets()1565 DomCustomWidgets *QAbstractFormBuilder::saveCustomWidgets()
1566 {
1567     return nullptr;
1568 }
1569 
1570 /*!
1571     \internal
1572 */
saveTabStops()1573 DomTabStops *QAbstractFormBuilder::saveTabStops()
1574 {
1575     return nullptr;
1576 }
1577 
1578 /*!
1579     \internal
1580 */
saveResources()1581 DomResources *QAbstractFormBuilder::saveResources()
1582 {
1583     return nullptr;
1584 }
1585 
1586 /*!
1587     \internal
1588     \since 4.5
1589 */
1590 
saveButtonGroups(const QWidget * mainContainer)1591 DomButtonGroups *QAbstractFormBuilder::saveButtonGroups(const QWidget *mainContainer)
1592 {
1593     // Save fst order buttongroup children of maincontainer
1594     const QObjectList &mchildren = mainContainer->children();
1595     if (mchildren.isEmpty())
1596         return nullptr;
1597     QVector<DomButtonGroup *> domGroups;
1598     for (QObject *o : mchildren) {
1599         if (auto bg = qobject_cast<QButtonGroup *>(o))
1600             if (DomButtonGroup* dg = createDom(bg))
1601                 domGroups.push_back(dg);
1602     }
1603     if (domGroups.isEmpty())
1604         return nullptr;
1605     DomButtonGroups *rc = new DomButtonGroups;
1606     rc->setElementButtonGroup(domGroups);
1607     return rc;
1608 }
1609 
1610 // VC6 would not find templated members, so we use statics and this utter hack.
1611 class FriendlyFB : public QAbstractFormBuilder {
1612 public:
1613     using QAbstractFormBuilder::saveResource;
1614     using QAbstractFormBuilder::saveText;
1615     using QAbstractFormBuilder::resourceBuilder;
1616     using QAbstractFormBuilder::textBuilder;
1617     using QAbstractFormBuilder::toVariant;
1618 };
1619 
1620 template<class T>
storeItemFlags(const T * item,QList<DomProperty * > * properties)1621 static void storeItemFlags(const T *item, QList<DomProperty*> *properties)
1622 {
1623     static const QFormBuilderStrings &strings = QFormBuilderStrings::instance();
1624     static const Qt::ItemFlags defaultFlags = T().flags();
1625     static const QMetaEnum itemFlags_enum = metaEnum<QAbstractFormBuilderGadget>("itemFlags");
1626 
1627     if (item->flags() != defaultFlags) {
1628         DomProperty *p = new DomProperty;
1629         p->setAttributeName(strings.flagsAttribute);
1630         p->setElementSet(QString::fromLatin1(itemFlags_enum.valueToKeys(item->flags())));
1631         properties->append(p);
1632     }
1633 }
1634 
1635 template<class T>
storeItemProps(QAbstractFormBuilder * abstractFormBuilder,const T * item,QList<DomProperty * > * properties)1636 static void storeItemProps(QAbstractFormBuilder *abstractFormBuilder, const T *item,
1637         QList<DomProperty*> *properties)
1638 {
1639     static const QFormBuilderStrings &strings = QFormBuilderStrings::instance();
1640     FriendlyFB * const formBuilder = static_cast<FriendlyFB *>(abstractFormBuilder);
1641 
1642     DomProperty *p;
1643     QVariant v;
1644 
1645     for (const QFormBuilderStrings::TextRoleNName &it : strings.itemTextRoles)
1646         if ((p = formBuilder->saveText(it.second, item->data(it.first.second))))
1647             properties->append(p);
1648 
1649     for (const QFormBuilderStrings::RoleNName &it : strings.itemRoles)
1650         if ((v = item->data(it.first)).isValid() &&
1651             (p = variantToDomProperty(abstractFormBuilder,
1652                 static_cast<const QMetaObject *>(&QAbstractFormBuilderGadget::staticMetaObject),
1653                 it.second, v)))
1654             properties->append(p);
1655 
1656     if ((p = formBuilder->saveResource(item->data(Qt::DecorationPropertyRole))))
1657         properties->append(p);
1658 }
1659 
1660 template<class T>
storeItemPropsNFlags(QAbstractFormBuilder * abstractFormBuilder,const T * item,QList<DomProperty * > * properties)1661 static void storeItemPropsNFlags(QAbstractFormBuilder *abstractFormBuilder, const T *item,
1662         QList<DomProperty*> *properties)
1663 {
1664     storeItemProps<T>(abstractFormBuilder, item, properties);
1665     storeItemFlags<T>(item, properties);
1666 }
1667 
1668 template<class T>
loadItemProps(QAbstractFormBuilder * abstractFormBuilder,T * item,const QHash<QString,DomProperty * > & properties)1669 static void loadItemProps(QAbstractFormBuilder *abstractFormBuilder, T *item,
1670         const QHash<QString, DomProperty*> &properties)
1671 {
1672     static const QFormBuilderStrings &strings = QFormBuilderStrings::instance();
1673     FriendlyFB * const formBuilder = static_cast<FriendlyFB *>(abstractFormBuilder);
1674 
1675     DomProperty *p;
1676     QVariant v;
1677 
1678     for (const QFormBuilderStrings::TextRoleNName &it : strings.itemTextRoles)
1679         if ((p = properties.value(it.second))) {
1680             v = formBuilder->textBuilder()->loadText(p);
1681             QVariant nativeValue = formBuilder->textBuilder()->toNativeValue(v);
1682             item->setData(it.first.first, qvariant_cast<QString>(nativeValue));
1683             item->setData(it.first.second, v);
1684         }
1685 
1686     for (const QFormBuilderStrings::RoleNName &it : strings.itemRoles)
1687         if ((p = properties.value(it.second)) &&
1688             (v = formBuilder->toVariant(&QAbstractFormBuilderGadget::staticMetaObject, p)).isValid())
1689             item->setData(it.first, v);
1690 
1691     if ((p = properties.value(strings.iconAttribute))) {
1692         v = formBuilder->resourceBuilder()->loadResource(formBuilder->workingDirectory(), p);
1693         QVariant nativeValue = formBuilder->resourceBuilder()->toNativeValue(v);
1694         item->setIcon(qvariant_cast<QIcon>(nativeValue));
1695         item->setData(Qt::DecorationPropertyRole, v);
1696     }
1697 }
1698 
1699 template<class T>
loadItemPropsNFlags(QAbstractFormBuilder * abstractFormBuilder,T * item,const QHash<QString,DomProperty * > & properties)1700 static void loadItemPropsNFlags(QAbstractFormBuilder *abstractFormBuilder, T *item,
1701         const QHash<QString, DomProperty*> &properties)
1702 {
1703     static const QFormBuilderStrings &strings = QFormBuilderStrings::instance();
1704     static const QMetaEnum itemFlags_enum = metaEnum<QAbstractFormBuilderGadget>("itemFlags");
1705 
1706     loadItemProps<T>(abstractFormBuilder, item, properties);
1707 
1708     DomProperty *p;
1709     if ((p = properties.value(strings.flagsAttribute)) && p->kind() == DomProperty::Set)
1710         item->setFlags(enumKeysToValue<Qt::ItemFlags>(itemFlags_enum, p->elementSet().toLatin1()));
1711 }
1712 
1713 /*!
1714     \internal
1715 */
saveTreeWidgetExtraInfo(QTreeWidget * treeWidget,DomWidget * ui_widget,DomWidget * ui_parentWidget)1716 void QAbstractFormBuilder::saveTreeWidgetExtraInfo(QTreeWidget *treeWidget, DomWidget *ui_widget, DomWidget *ui_parentWidget)
1717 {
1718     Q_UNUSED(ui_parentWidget);
1719 
1720     QVector<DomColumn *> columns;
1721     DomProperty *p;
1722     QVariant v;
1723     const QFormBuilderStrings &strings = QFormBuilderStrings::instance();
1724     // save the header
1725     for (int c = 0; c<treeWidget->columnCount(); ++c) {
1726         DomColumn *column = new DomColumn;
1727 
1728         QList<DomProperty*> properties;
1729 
1730         for (const QFormBuilderStrings::TextRoleNName &it : strings.itemTextRoles) {
1731             p = saveText(it.second, treeWidget->headerItem()->data(c, it.first.second));
1732             // Prevent uic 4.4.X from crashing if it cannot find a column text
1733             if (!p && it.first.first == Qt::EditRole && it.second == QStringLiteral("text")) {
1734                 DomString *defaultHeader = new DomString;
1735                 defaultHeader->setText(QString::number(c + 1));
1736                 defaultHeader->setAttributeNotr(QStringLiteral("true"));
1737                 p = new DomProperty;
1738                 p->setAttributeName(it.second);
1739                 p->setElementString(defaultHeader);
1740             }
1741             if (p)
1742                 properties.append(p);
1743         }
1744 
1745         for (const QFormBuilderStrings::RoleNName &it : strings.itemRoles)
1746             if ((v = treeWidget->headerItem()->data(c, it.first)).isValid() &&
1747                 (p = variantToDomProperty(this, &QAbstractFormBuilderGadget::staticMetaObject, it.second, v)))
1748                 properties.append(p);
1749 
1750         if ((p = saveResource(treeWidget->headerItem()->data(c, Qt::DecorationPropertyRole))))
1751             properties.append(p);
1752 
1753         column->setElementProperty(properties);
1754         columns.append(column);
1755     }
1756 
1757     ui_widget->setElementColumn(columns);
1758 
1759     auto items = ui_widget->elementItem();
1760 
1761     QQueue<QPair<QTreeWidgetItem *, DomItem *> > pendingQueue;
1762     for (int i = 0; i < treeWidget->topLevelItemCount(); i++)
1763         pendingQueue.enqueue(qMakePair(treeWidget->topLevelItem(i), nullptr));
1764 
1765     while (!pendingQueue.isEmpty()) {
1766         const QPair<QTreeWidgetItem *, DomItem *> pair = pendingQueue.dequeue();
1767         QTreeWidgetItem *item = pair.first;
1768         DomItem *parentDomItem = pair.second;
1769 
1770         DomItem *currentDomItem = new DomItem;
1771 
1772         QList<DomProperty*> properties;
1773         for (int c = 0; c < treeWidget->columnCount(); c++) {
1774             for (const QFormBuilderStrings::TextRoleNName &it : strings.itemTextRoles)
1775                 if ((p = saveText(it.second, item->data(c, it.first.second))))
1776                     properties.append(p);
1777 
1778             for (const QFormBuilderStrings::RoleNName &it : strings.itemRoles)
1779                 if ((v = item->data(c, it.first)).isValid() &&
1780                     (p = variantToDomProperty(this, &QAbstractFormBuilderGadget::staticMetaObject, it.second, v)))
1781                     properties.append(p);
1782 
1783             if ((p = saveResource(item->data(c, Qt::DecorationPropertyRole))))
1784                 properties.append(p);
1785         }
1786         storeItemFlags(item, &properties);
1787         currentDomItem->setElementProperty(properties);
1788 
1789         if (parentDomItem) {
1790             auto childrenItems = parentDomItem->elementItem();
1791             childrenItems.append(currentDomItem);
1792             parentDomItem->setElementItem(childrenItems);
1793         } else
1794             items.append(currentDomItem);
1795 
1796         for (int i = 0; i < item->childCount(); i++)
1797             pendingQueue.enqueue(qMakePair(item->child(i), currentDomItem));
1798     }
1799 
1800     ui_widget->setElementItem(items);
1801 }
1802 
1803 /*!
1804     \internal
1805 */
saveTableWidgetExtraInfo(QTableWidget * tableWidget,DomWidget * ui_widget,DomWidget * ui_parentWidget)1806 void QAbstractFormBuilder::saveTableWidgetExtraInfo(QTableWidget *tableWidget, DomWidget *ui_widget, DomWidget *ui_parentWidget)
1807 {
1808     Q_UNUSED(ui_parentWidget);
1809 
1810     // save the horizontal header
1811     QVector<DomColumn *> columns;
1812     for (int c = 0; c < tableWidget->columnCount(); c++) {
1813         QList<DomProperty*> properties;
1814         QTableWidgetItem *item = tableWidget->horizontalHeaderItem(c);
1815         if (item)
1816             storeItemProps(this, item, &properties);
1817 
1818         DomColumn *column = new DomColumn;
1819         column->setElementProperty(properties);
1820         columns.append(column);
1821     }
1822     ui_widget->setElementColumn(columns);
1823 
1824     // save the vertical header
1825     QVector<DomRow *> rows;
1826     for (int r = 0; r < tableWidget->rowCount(); r++) {
1827         QList<DomProperty*> properties;
1828         QTableWidgetItem *item = tableWidget->verticalHeaderItem(r);
1829         if (item)
1830             storeItemProps(this, item, &properties);
1831 
1832         DomRow *row = new DomRow;
1833         row->setElementProperty(properties);
1834         rows.append(row);
1835     }
1836     ui_widget->setElementRow(rows);
1837 
1838     auto items = ui_widget->elementItem();
1839     for (int r = 0; r < tableWidget->rowCount(); r++)
1840         for (int c = 0; c < tableWidget->columnCount(); c++) {
1841             QTableWidgetItem *item = tableWidget->item(r, c);
1842             if (item) {
1843                 QList<DomProperty*> properties;
1844                 storeItemPropsNFlags(this, item, &properties);
1845 
1846                 DomItem *domItem = new DomItem;
1847                 domItem->setAttributeRow(r);
1848                 domItem->setAttributeColumn(c);
1849                 domItem->setElementProperty(properties);
1850                 items.append(domItem);
1851             }
1852         }
1853 
1854     ui_widget->setElementItem(items);
1855 }
1856 
1857 /*!
1858     \internal
1859 */
saveListWidgetExtraInfo(QListWidget * listWidget,DomWidget * ui_widget,DomWidget * ui_parentWidget)1860 void QAbstractFormBuilder::saveListWidgetExtraInfo(QListWidget *listWidget, DomWidget *ui_widget, DomWidget *ui_parentWidget)
1861 {
1862     Q_UNUSED(ui_parentWidget);
1863 
1864     auto ui_items = ui_widget->elementItem();
1865     for (int i=0; i<listWidget->count(); ++i) {
1866         QList<DomProperty*> properties;
1867         storeItemPropsNFlags(this, listWidget->item(i), &properties);
1868 
1869         DomItem *ui_item = new DomItem();
1870         ui_item->setElementProperty(properties);
1871         ui_items.append(ui_item);
1872     }
1873 
1874     ui_widget->setElementItem(ui_items);
1875 }
1876 
1877 /*!
1878     \internal
1879 */
saveComboBoxExtraInfo(QComboBox * comboBox,DomWidget * ui_widget,DomWidget * ui_parentWidget)1880 void QAbstractFormBuilder::saveComboBoxExtraInfo(QComboBox *comboBox, DomWidget *ui_widget, DomWidget *ui_parentWidget)
1881 {
1882     Q_UNUSED(ui_parentWidget);
1883     auto ui_items = ui_widget->elementItem();
1884 
1885     const int count = comboBox->count();
1886     for (int i=0; i < count; ++i) {
1887         // We might encounter items for which both builders return 0 in Designer
1888         // (indicating a custom combo adding items in the constructor). Ignore those.
1889         DomProperty *textProperty = saveText(QFormBuilderStrings::instance().textAttribute,
1890                                              comboBox->itemData(i, Qt::DisplayPropertyRole));
1891         DomProperty *iconProperty = saveResource(comboBox->itemData(i, Qt::DecorationPropertyRole));
1892         if (textProperty || iconProperty) {
1893             QList<DomProperty*> properties;
1894             if (textProperty)
1895                 properties.push_back(textProperty);
1896             if (iconProperty)
1897                 properties.push_back(iconProperty);
1898 
1899             DomItem *ui_item = new DomItem();
1900             ui_item->setElementProperty(properties);
1901             ui_items.push_back(ui_item);
1902         }
1903     }
1904 
1905     ui_widget->setElementItem(ui_items);
1906 }
1907 
1908 /*!
1909     \internal
1910     \since 4.5
1911 */
1912 
saveButtonExtraInfo(const QAbstractButton * widget,DomWidget * ui_widget,DomWidget *)1913 void QAbstractFormBuilder::saveButtonExtraInfo(const QAbstractButton *widget, DomWidget *ui_widget, DomWidget *)
1914 {
1915     using DomPropertyList = QList<DomProperty *>;
1916     if (const QButtonGroup *buttonGroup = widget->group()) {
1917         DomPropertyList attributes = ui_widget->elementAttribute();
1918         DomString *domString = new DomString();
1919         domString->setText(buttonGroup->objectName());
1920         domString->setAttributeNotr(QStringLiteral("true"));
1921         DomProperty *domProperty = new DomProperty();
1922         domProperty->setAttributeName(QLatin1String(buttonGroupPropertyC));
1923         domProperty->setElementString(domString);
1924         attributes += domProperty;
1925         ui_widget->setElementAttribute(attributes);
1926     }
1927 }
1928 
1929 /*!
1930     \internal
1931     \since 4.5
1932 */
saveItemViewExtraInfo(const QAbstractItemView * itemView,DomWidget * ui_widget,DomWidget *)1933 void QAbstractFormBuilder::saveItemViewExtraInfo(const QAbstractItemView *itemView,
1934                                                  DomWidget *ui_widget, DomWidget *)
1935 {
1936     //
1937     // Special handling for qtableview/qtreeview fake header attributes
1938     //
1939     static const QLatin1String realPropertyNames[] = {
1940         QLatin1String("visible"),
1941         QLatin1String("cascadingSectionResizes"),
1942         QLatin1String("minimumSectionSize"),    // before defaultSectionSize
1943         QLatin1String("defaultSectionSize"),
1944         QLatin1String("highlightSections"),
1945         QLatin1String("showSortIndicator"),
1946         QLatin1String("stretchLastSection"),
1947     };
1948 
1949     if (const QTreeView *treeView = qobject_cast<const QTreeView*>(itemView)) {
1950         auto viewProperties = ui_widget->elementAttribute();
1951         const auto &headerProperties = computeProperties(treeView->header());
1952         for (const QString &realPropertyName : realPropertyNames) {
1953             const QString upperPropertyName = realPropertyName.at(0).toUpper()
1954                                               + realPropertyName.mid(1);
1955             const QString fakePropertyName = QStringLiteral("header") + upperPropertyName;
1956             for (DomProperty *property : headerProperties) {
1957                 if (property->attributeName() == realPropertyName) {
1958                     property->setAttributeName(fakePropertyName);
1959                     viewProperties << property;
1960                 }
1961             }
1962         }
1963         ui_widget->setElementAttribute(viewProperties);
1964     } else if (const QTableView *tableView = qobject_cast<const QTableView*>(itemView)) {
1965         static const QStringList headerPrefixes =
1966                 (QStringList() << QStringLiteral("horizontalHeader")
1967                                << QStringLiteral("verticalHeader"));
1968 
1969         auto viewProperties = ui_widget->elementAttribute();
1970         for (const QString &headerPrefix : headerPrefixes) {
1971             const auto &headerProperties = headerPrefix == QStringLiteral("horizontalHeader")
1972                 ? computeProperties(tableView->horizontalHeader())
1973                 : computeProperties(tableView->verticalHeader());
1974             for (const QString &realPropertyName : realPropertyNames) {
1975                 const QString upperPropertyName = realPropertyName.at(0).toUpper()
1976                                                   + realPropertyName.mid(1);
1977                 const QString fakePropertyName = headerPrefix + upperPropertyName;
1978                 for (DomProperty *property : qAsConst(headerProperties)) {
1979                     if (property->attributeName() == realPropertyName) {
1980                         property->setAttributeName(fakePropertyName);
1981                         viewProperties << property;
1982                     }
1983                 }
1984             }
1985         }
1986         ui_widget->setElementAttribute(viewProperties);
1987     }
1988 }
1989 
1990 /*!
1991     \internal
1992     \since 4.4
1993 */
1994 
setResourceBuilder(QResourceBuilder * builder)1995 void QAbstractFormBuilder::setResourceBuilder(QResourceBuilder *builder)
1996 {
1997     d->setResourceBuilder(builder);
1998 }
1999 
2000 /*!
2001     \internal
2002     \since 4.4
2003 */
2004 
resourceBuilder() const2005 QResourceBuilder *QAbstractFormBuilder::resourceBuilder() const
2006 {
2007     return d->resourceBuilder();
2008 }
2009 
2010 /*!
2011     \internal
2012     \since 4.5
2013 */
2014 
setTextBuilder(QTextBuilder * builder)2015 void QAbstractFormBuilder::setTextBuilder(QTextBuilder *builder)
2016 {
2017     d->setTextBuilder(builder);
2018 }
2019 
2020 /*!
2021     \internal
2022     \since 4.5
2023 */
2024 
textBuilder() const2025 QTextBuilder *QAbstractFormBuilder::textBuilder() const
2026 {
2027     return d->textBuilder();
2028 }
2029 
2030 /*!
2031     \internal
2032 */
saveExtraInfo(QWidget * widget,DomWidget * ui_widget,DomWidget * ui_parentWidget)2033 void QAbstractFormBuilder::saveExtraInfo(QWidget *widget, DomWidget *ui_widget,
2034                                          DomWidget *ui_parentWidget)
2035 {
2036     if (QListWidget *listWidget = qobject_cast<QListWidget*>(widget)) {
2037         saveListWidgetExtraInfo(listWidget, ui_widget, ui_parentWidget);
2038     } else if (QTreeWidget *treeWidget = qobject_cast<QTreeWidget*>(widget)) {
2039         saveTreeWidgetExtraInfo(treeWidget, ui_widget, ui_parentWidget);
2040     } else if (QTableWidget *tableWidget = qobject_cast<QTableWidget*>(widget)) {
2041         saveTableWidgetExtraInfo(tableWidget, ui_widget, ui_parentWidget);
2042     } else if (QComboBox *comboBox = qobject_cast<QComboBox*>(widget)) {
2043         if (!qobject_cast<QFontComboBox*>(widget))
2044             saveComboBoxExtraInfo(comboBox, ui_widget, ui_parentWidget);
2045     } else if(QAbstractButton *ab = qobject_cast<QAbstractButton *>(widget)) {
2046         saveButtonExtraInfo(ab, ui_widget, ui_parentWidget);
2047     }
2048     if (QAbstractItemView *itemView = qobject_cast<QAbstractItemView *>(widget)) {
2049         saveItemViewExtraInfo(itemView, ui_widget, ui_parentWidget);
2050     }
2051 }
2052 
2053 /*!
2054     \internal
2055 */
loadListWidgetExtraInfo(DomWidget * ui_widget,QListWidget * listWidget,QWidget * parentWidget)2056 void QAbstractFormBuilder::loadListWidgetExtraInfo(DomWidget *ui_widget, QListWidget *listWidget, QWidget *parentWidget)
2057 {
2058     Q_UNUSED(parentWidget);
2059     const QFormBuilderStrings &strings = QFormBuilderStrings::instance();
2060 
2061     const auto &elementItem = ui_widget->elementItem();
2062     for (DomItem *ui_item : elementItem) {
2063         const DomPropertyHash properties = propertyMap(ui_item->elementProperty());
2064         QListWidgetItem *item = new QListWidgetItem(listWidget);
2065         loadItemPropsNFlags<QListWidgetItem>(this, item, properties);
2066     }
2067 
2068     DomProperty *currentRow = propertyMap(ui_widget->elementProperty()).value(strings.currentRowProperty);
2069     if (currentRow)
2070         listWidget->setCurrentRow(currentRow->elementNumber());
2071 }
2072 
2073 /*!
2074     \internal
2075 */
loadTreeWidgetExtraInfo(DomWidget * ui_widget,QTreeWidget * treeWidget,QWidget * parentWidget)2076 void QAbstractFormBuilder::loadTreeWidgetExtraInfo(DomWidget *ui_widget, QTreeWidget *treeWidget, QWidget *parentWidget)
2077 {
2078     Q_UNUSED(parentWidget);
2079     const QFormBuilderStrings &strings = QFormBuilderStrings::instance();
2080     const QMetaEnum itemFlags_enum = metaEnum<QAbstractFormBuilderGadget>("itemFlags");
2081     const auto &columns = ui_widget->elementColumn();
2082     if (columns.count() > 0)
2083         treeWidget->setColumnCount(columns.count());
2084 
2085     for (int i = 0; i<columns.count(); ++i) {
2086         const DomColumn *c = columns.at(i);
2087         const DomPropertyHash properties = propertyMap(c->elementProperty());
2088 
2089         DomProperty *p;
2090         QVariant v;
2091 
2092         for (const QFormBuilderStrings::RoleNName &it : strings.itemRoles)
2093             if ((p = properties.value(it.second)) &&
2094                 (v = toVariant(&QAbstractFormBuilderGadget::staticMetaObject, p)).isValid())
2095                 treeWidget->headerItem()->setData(i, it.first, v);
2096 
2097         for (const QFormBuilderStrings::TextRoleNName &it : strings.itemTextRoles)
2098             if ((p = properties.value(it.second))) {
2099                 v = textBuilder()->loadText(p);
2100                 QVariant nativeValue = textBuilder()->toNativeValue(v);
2101                 treeWidget->headerItem()->setData(i, it.first.first, qvariant_cast<QString>(nativeValue));
2102                 treeWidget->headerItem()->setData(i, it.first.second, v);
2103             }
2104 
2105         if ((p = properties.value(strings.iconAttribute))) {
2106             v = resourceBuilder()->loadResource(workingDirectory(), p);
2107             QVariant nativeValue = resourceBuilder()->toNativeValue(v);
2108             treeWidget->headerItem()->setIcon(i, qvariant_cast<QIcon>(nativeValue));
2109             treeWidget->headerItem()->setData(i, Qt::DecorationPropertyRole, v);
2110         }
2111     }
2112 
2113     QQueue<QPair<DomItem *, QTreeWidgetItem *> > pendingQueue;
2114     const auto &widgetElementItem = ui_widget->elementItem();
2115     for (DomItem *ui_item : widgetElementItem)
2116         pendingQueue.enqueue(qMakePair(ui_item, nullptr));
2117 
2118     while (!pendingQueue.isEmpty()) {
2119         const QPair<DomItem *, QTreeWidgetItem *> pair = pendingQueue.dequeue();
2120         const DomItem *domItem = pair.first;
2121         QTreeWidgetItem *parentItem = pair.second;
2122 
2123         QTreeWidgetItem *currentItem = nullptr;
2124 
2125         if (parentItem)
2126             currentItem = new QTreeWidgetItem(parentItem);
2127         else
2128             currentItem = new QTreeWidgetItem(treeWidget);
2129 
2130         const auto &properties = domItem->elementProperty();
2131         int col = -1;
2132         for (DomProperty *property : properties) {
2133             if (property->attributeName() == strings.flagsAttribute && !property->elementSet().isEmpty()) {
2134                 currentItem->setFlags(enumKeysToValue<Qt::ItemFlags>(itemFlags_enum, property->elementSet().toLatin1()));
2135             } else if (property->attributeName() == strings.textAttribute && property->elementString()) {
2136                 col++;
2137                 QVariant textV = textBuilder()->loadText(property);
2138                 QVariant nativeValue = textBuilder()->toNativeValue(textV);
2139                 currentItem->setText(col, qvariant_cast<QString>(nativeValue));
2140                 currentItem->setData(col, Qt::DisplayPropertyRole, textV);
2141             } else if (col >= 0) {
2142                 if (property->attributeName() == strings.iconAttribute) {
2143                     QVariant v = resourceBuilder()->loadResource(workingDirectory(), property);
2144                     if (v.isValid()) {
2145                         QVariant nativeValue = resourceBuilder()->toNativeValue(v);
2146                         currentItem->setIcon(col, qvariant_cast<QIcon>(nativeValue));
2147                         currentItem->setData(col, Qt::DecorationPropertyRole, v);
2148                     }
2149                 } else {
2150                     QVariant v;
2151                     int role = strings.treeItemRoleHash.value(property->attributeName(), (Qt::ItemDataRole)-1);
2152                     if (role >= 0) {
2153                         if ((v = toVariant(&QAbstractFormBuilderGadget::staticMetaObject, property)).isValid())
2154                             currentItem->setData(col, role, v);
2155                     } else {
2156                         QPair<Qt::ItemDataRole, Qt::ItemDataRole> rolePair =
2157                             strings.treeItemTextRoleHash.value(property->attributeName(),
2158                                          qMakePair((Qt::ItemDataRole)-1, (Qt::ItemDataRole)-1));
2159                         if (rolePair.first >= 0) {
2160                             QVariant textV = textBuilder()->loadText(property);
2161                             QVariant nativeValue = textBuilder()->toNativeValue(textV);
2162                             currentItem->setData(col, rolePair.first, qvariant_cast<QString>(nativeValue));
2163                             currentItem->setData(col, rolePair.second, textV);
2164                         }
2165                     }
2166                 }
2167             }
2168         }
2169 
2170         const auto &elementItem = domItem->elementItem();
2171         for (DomItem *childItem : elementItem)
2172             pendingQueue.enqueue(qMakePair(childItem, currentItem));
2173 
2174     }
2175 }
2176 
2177 /*!
2178     \internal
2179 */
loadTableWidgetExtraInfo(DomWidget * ui_widget,QTableWidget * tableWidget,QWidget * parentWidget)2180 void QAbstractFormBuilder::loadTableWidgetExtraInfo(DomWidget *ui_widget, QTableWidget *tableWidget, QWidget *parentWidget)
2181 {
2182     Q_UNUSED(parentWidget);
2183 
2184     const auto &columns = ui_widget->elementColumn();
2185     if (columns.count() > 0)
2186         tableWidget->setColumnCount(columns.count());
2187     for (int i = 0; i< columns.count(); i++) {
2188         DomColumn *c = columns.at(i);
2189         const DomPropertyHash properties = propertyMap(c->elementProperty());
2190 
2191         if (!properties.isEmpty()) {
2192             QTableWidgetItem *item = new QTableWidgetItem;
2193             loadItemProps(this, item, properties);
2194             tableWidget->setHorizontalHeaderItem(i, item);
2195         }
2196     }
2197 
2198     const auto &rows = ui_widget->elementRow();
2199     if (rows.count() > 0)
2200         tableWidget->setRowCount(rows.count());
2201     for (int i = 0; i< rows.count(); i++) {
2202         const DomRow *r = rows.at(i);
2203         const DomPropertyHash properties = propertyMap(r->elementProperty());
2204 
2205         if (!properties.isEmpty()) {
2206             QTableWidgetItem *item = new QTableWidgetItem;
2207             loadItemProps(this, item, properties);
2208             tableWidget->setVerticalHeaderItem(i, item);
2209         }
2210     }
2211 
2212     const auto &elementItem = ui_widget->elementItem();
2213     for (DomItem *ui_item : elementItem) {
2214         if (ui_item->hasAttributeRow() && ui_item->hasAttributeColumn()) {
2215             const DomPropertyHash properties = propertyMap(ui_item->elementProperty());
2216             QTableWidgetItem *item = new QTableWidgetItem;
2217             loadItemPropsNFlags(this, item, properties);
2218             tableWidget->setItem(ui_item->attributeRow(), ui_item->attributeColumn(), item);
2219         }
2220     }
2221 }
2222 
2223 /*!
2224     \internal
2225 */
loadComboBoxExtraInfo(DomWidget * ui_widget,QComboBox * comboBox,QWidget * parentWidget)2226 void QAbstractFormBuilder::loadComboBoxExtraInfo(DomWidget *ui_widget, QComboBox *comboBox, QWidget *parentWidget)
2227 {
2228     Q_UNUSED(parentWidget);
2229     const QFormBuilderStrings &strings = QFormBuilderStrings::instance();
2230     const auto &elementItem = ui_widget->elementItem();
2231     for (DomItem *ui_item : elementItem) {
2232         const DomPropertyHash properties = propertyMap(ui_item->elementProperty());
2233         QString text;
2234         QIcon icon;
2235         QVariant textData;
2236         QVariant iconData;
2237 
2238         DomProperty *p = nullptr;
2239 
2240         p = properties.value(strings.textAttribute);
2241         if (p && p->elementString()) {
2242              textData = textBuilder()->loadText(p);
2243              text = qvariant_cast<QString>(textBuilder()->toNativeValue(textData));
2244         }
2245 
2246         p = properties.value(strings.iconAttribute);
2247         if (p) {
2248              iconData = resourceBuilder()->loadResource(workingDirectory(), p);
2249              icon = qvariant_cast<QIcon>(resourceBuilder()->toNativeValue(iconData));
2250         }
2251 
2252         comboBox->addItem(icon, text);
2253         comboBox->setItemData((comboBox->count()-1), iconData, Qt::DecorationPropertyRole);
2254         comboBox->setItemData((comboBox->count()-1), textData, Qt::DisplayPropertyRole);
2255     }
2256 
2257     DomProperty *currentIndex = propertyMap(ui_widget->elementProperty()).value(strings.currentIndexProperty);
2258     if (currentIndex)
2259         comboBox->setCurrentIndex(currentIndex->elementNumber());
2260 }
2261 
2262 // Get the button group name out of a widget's attribute list
buttonGroupName(const DomWidget * ui_widget)2263 static QString buttonGroupName(const DomWidget *ui_widget)
2264 {
2265     const auto &attributes = ui_widget->elementAttribute();
2266     if (attributes.isEmpty())
2267         return QString();
2268     const QString buttonGroupProperty = QLatin1String(buttonGroupPropertyC);
2269     for (const DomProperty *p : attributes) {
2270         if (p->attributeName() == buttonGroupProperty)
2271             return p->elementString()->text();
2272     }
2273     return QString();
2274 }
2275 
2276 /*!
2277     \internal
2278     \since 4.5
2279 */
2280 
loadButtonExtraInfo(const DomWidget * ui_widget,QAbstractButton * button,QWidget *)2281 void QAbstractFormBuilder::loadButtonExtraInfo(const DomWidget *ui_widget, QAbstractButton *button, QWidget *)
2282 {
2283     using ButtonGroupHash = QFormBuilderExtra::ButtonGroupHash;
2284 
2285     const QString groupName = buttonGroupName(ui_widget);
2286     if (groupName.isEmpty())
2287         return;
2288     // Find entry
2289     ButtonGroupHash &buttonGroups = d->buttonGroups();
2290     ButtonGroupHash::iterator it = buttonGroups.find(groupName);
2291     if (it == buttonGroups.end()) {
2292 #ifdef QFORMINTERNAL_NAMESPACE // Suppress the warning when copying in Designer
2293         uiLibWarning(QCoreApplication::translate("QAbstractFormBuilder", "Invalid QButtonGroup reference '%1' referenced by '%2'.").arg(groupName, button->objectName()));
2294 #endif
2295         return;
2296     }
2297     // Create button group on demand?
2298     QButtonGroup *&group = it.value().second;
2299     if (group == nullptr) {
2300         group = new QButtonGroup;
2301         group->setObjectName(groupName);
2302         applyProperties(group,  it.value().first->elementProperty());
2303     }
2304     group->addButton(button);
2305 }
2306 
2307 /*!
2308     \internal
2309     \since 4.5
2310 */
loadItemViewExtraInfo(DomWidget * ui_widget,QAbstractItemView * itemView,QWidget *)2311 void QAbstractFormBuilder::loadItemViewExtraInfo(DomWidget *ui_widget, QAbstractItemView *itemView,
2312                                                  QWidget *)
2313 {
2314     //
2315     // Special handling for qtableview/qtreeview fake header attributes
2316     //
2317     static const QLatin1String realPropertyNames[] = {
2318         QLatin1String("visible"),
2319         QLatin1String("cascadingSectionResizes"),
2320         QLatin1String("minimumSectionSize"),    // before defaultSectionSize
2321         QLatin1String("defaultSectionSize"),
2322         QLatin1String("highlightSections"),
2323         QLatin1String("showSortIndicator"),
2324         QLatin1String("stretchLastSection"),
2325     };
2326 
2327     if (QTreeView *treeView = qobject_cast<QTreeView*>(itemView)) {
2328         const auto &allAttributes = ui_widget->elementAttribute();
2329         QList<DomProperty *> headerProperties;
2330         for (const QString &realPropertyName : realPropertyNames) {
2331             const QString upperPropertyName = realPropertyName.at(0).toUpper()
2332                                               + realPropertyName.mid(1);
2333             const QString fakePropertyName = QStringLiteral("header") + upperPropertyName;
2334             for (DomProperty *attr : allAttributes) {
2335                 if (attr->attributeName() == fakePropertyName) {
2336                     attr->setAttributeName(realPropertyName);
2337                     headerProperties << attr;
2338                 }
2339             }
2340         }
2341         applyProperties(treeView->header(), headerProperties);
2342     } else if (QTableView *tableView = qobject_cast<QTableView*>(itemView)) {
2343         static const QStringList headerPrefixes =
2344                 (QStringList() << QStringLiteral("horizontalHeader")
2345                                << QStringLiteral("verticalHeader"));
2346 
2347         const auto &allAttributes = ui_widget->elementAttribute();
2348         for (const QString &headerPrefix : headerPrefixes) {
2349             QList<DomProperty*> headerProperties;
2350             for (const QString &realPropertyName : realPropertyNames) {
2351                 const QString upperPropertyName = realPropertyName.at(0).toUpper()
2352                                                   + realPropertyName.mid(1);
2353                 const QString fakePropertyName = headerPrefix + upperPropertyName;
2354                 for (DomProperty *attr : allAttributes) {
2355                     if (attr->attributeName() == fakePropertyName) {
2356                         attr->setAttributeName(realPropertyName);
2357                         headerProperties << attr;
2358                     }
2359                 }
2360             }
2361             if (headerPrefix == QStringLiteral("horizontalHeader"))
2362                 applyProperties(tableView->horizontalHeader(), headerProperties);
2363             else
2364                 applyProperties(tableView->verticalHeader(), headerProperties);
2365         }
2366     }
2367 }
2368 
2369 /*!
2370     \internal
2371 */
loadExtraInfo(DomWidget * ui_widget,QWidget * widget,QWidget * parentWidget)2372 void QAbstractFormBuilder::loadExtraInfo(DomWidget *ui_widget, QWidget *widget, QWidget *parentWidget)
2373 {
2374     const QFormBuilderStrings &strings = QFormBuilderStrings::instance();
2375     if (false) {
2376 #if QT_CONFIG(listwidget)
2377     } else if (QListWidget *listWidget = qobject_cast<QListWidget*>(widget)) {
2378         loadListWidgetExtraInfo(ui_widget, listWidget, parentWidget);
2379 #endif
2380 #if QT_CONFIG(treewidget)
2381     } else if (QTreeWidget *treeWidget = qobject_cast<QTreeWidget*>(widget)) {
2382         loadTreeWidgetExtraInfo(ui_widget, treeWidget, parentWidget);
2383 #endif
2384 #if QT_CONFIG(tablewidget)
2385     } else if (QTableWidget *tableWidget = qobject_cast<QTableWidget*>(widget)) {
2386         loadTableWidgetExtraInfo(ui_widget, tableWidget, parentWidget);
2387 #endif
2388 #if QT_CONFIG(combobox)
2389     } else if (QComboBox *comboBox = qobject_cast<QComboBox*>(widget)) {
2390         if (!qobject_cast<QFontComboBox *>(widget))
2391             loadComboBoxExtraInfo(ui_widget, comboBox, parentWidget);
2392 #endif
2393 #if QT_CONFIG(tabwidget)
2394     } else if (QTabWidget *tabWidget = qobject_cast<QTabWidget*>(widget)) {
2395         const DomProperty *currentIndex = propertyMap(ui_widget->elementProperty()).value(strings.currentIndexProperty);
2396         if (currentIndex)
2397             tabWidget->setCurrentIndex(currentIndex->elementNumber());
2398 #endif
2399 #if QT_CONFIG(stackedwidget)
2400     } else if (QStackedWidget *stackedWidget = qobject_cast<QStackedWidget*>(widget)) {
2401         const DomProperty *currentIndex = propertyMap(ui_widget->elementProperty()).value(strings.currentIndexProperty);
2402         if (currentIndex)
2403             stackedWidget->setCurrentIndex(currentIndex->elementNumber());
2404 #endif
2405 #if QT_CONFIG(toolbox)
2406     } else if (QToolBox *toolBox = qobject_cast<QToolBox*>(widget)) {
2407         const DomProperty *currentIndex = propertyMap(ui_widget->elementProperty()).value(strings.currentIndexProperty);
2408         if (currentIndex)
2409             toolBox->setCurrentIndex(currentIndex->elementNumber());
2410         const DomProperty *tabSpacing = propertyMap(ui_widget->elementProperty()).value(strings.tabSpacingProperty);
2411         if (tabSpacing)
2412             toolBox->layout()->setSpacing(tabSpacing->elementNumber());
2413 #endif
2414     } else if (QAbstractButton *ab = qobject_cast<QAbstractButton *>(widget)) {
2415         loadButtonExtraInfo(ui_widget, ab, parentWidget);
2416     }
2417     if (QAbstractItemView *itemView = qobject_cast<QAbstractItemView *>(widget)) {
2418         loadItemViewExtraInfo(ui_widget, itemView, parentWidget);
2419     }
2420 }
2421 
2422 /*!
2423     Returns the current working directory of the form builder.
2424 
2425     \sa setWorkingDirectory()
2426 */
workingDirectory() const2427 QDir QAbstractFormBuilder::workingDirectory() const
2428 {
2429     return d->m_workingDirectory;
2430 }
2431 
2432 /*!
2433     Sets the current working directory of the form builder to the
2434     specified \a directory.
2435 
2436     \sa workingDirectory()
2437 */
setWorkingDirectory(const QDir & directory)2438 void QAbstractFormBuilder::setWorkingDirectory(const QDir &directory)
2439 {
2440     d->m_workingDirectory = directory;
2441 }
2442 
2443 /*!
2444     \internal
2445 */
createDom(QAction * action)2446 DomAction *QAbstractFormBuilder::createDom(QAction *action)
2447 {
2448     if (action->parentWidget() == action->menu() || action->isSeparator())
2449         return nullptr;
2450 
2451     DomAction *ui_action = new DomAction;
2452     ui_action->setAttributeName(action->objectName());
2453 
2454     ui_action->setElementProperty(computeProperties(action));
2455 
2456     return ui_action;
2457 }
2458 
2459 /*!
2460     \internal
2461     \since 4.5
2462 */
2463 
createDom(QButtonGroup * buttonGroup)2464 DomButtonGroup *QAbstractFormBuilder::createDom(QButtonGroup *buttonGroup)
2465 {
2466     if (buttonGroup->buttons().count() == 0) // Empty group left over on form?
2467         return nullptr;
2468     DomButtonGroup *domButtonGroup = new DomButtonGroup;
2469     domButtonGroup->setAttributeName(buttonGroup->objectName());
2470 
2471     domButtonGroup->setElementProperty(computeProperties(buttonGroup));
2472     return domButtonGroup;
2473 }
2474 
2475 /*!
2476     \internal
2477 */
createDom(QActionGroup * actionGroup)2478 DomActionGroup *QAbstractFormBuilder::createDom(QActionGroup *actionGroup)
2479 {
2480     DomActionGroup *ui_action_group = new DomActionGroup;
2481     ui_action_group->setAttributeName(actionGroup->objectName());
2482 
2483     ui_action_group->setElementProperty(computeProperties(actionGroup));
2484 
2485     QVector<DomAction *> ui_actions;
2486 
2487     const auto &actions = actionGroup->actions();
2488     ui_actions.reserve(actions.size());
2489     for (QAction *action : actions) {
2490         if (DomAction *ui_action = createDom(action)) {
2491             ui_actions.append(ui_action);
2492         }
2493     }
2494 
2495     ui_action_group->setElementAction(ui_actions);
2496 
2497     return ui_action_group;
2498 }
2499 
2500 /*!
2501     \internal
2502 */
addMenuAction(QAction * action)2503 void QAbstractFormBuilder::addMenuAction(QAction *action)
2504 {
2505     Q_UNUSED(action);
2506 }
2507 
2508 /*!
2509     \internal
2510 */
reset()2511 void QAbstractFormBuilder::reset()
2512 {
2513     d->m_laidout.clear();
2514     d->m_actions.clear();
2515     d->m_actionGroups.clear();
2516     d->m_defaultMargin = INT_MIN;
2517     d->m_defaultSpacing = INT_MIN;
2518 }
2519 
2520 /*!
2521     \internal
2522     Access meta enumeration for Qt::ToolBarArea
2523 */
2524 
toolBarAreaMetaEnum()2525 QMetaEnum QAbstractFormBuilder::toolBarAreaMetaEnum()
2526 {
2527     return metaEnum<QAbstractFormBuilderGadget>("toolBarArea");
2528 }
2529 
2530 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
2531 
2532 /*!
2533     \internal
2534     Return paths of an icon.
2535 */
2536 
iconPaths(const QIcon & icon) const2537 QAbstractFormBuilder::IconPaths QAbstractFormBuilder::iconPaths(const QIcon &icon) const
2538 {
2539     Q_UNUSED(icon);
2540     qWarning() << "QAbstractFormBuilder::iconPaths() is obsoleted";
2541     return IconPaths();
2542 }
2543 
2544 /*!
2545     \internal
2546     Return paths of a pixmap.
2547 */
2548 
pixmapPaths(const QPixmap & pixmap) const2549 QAbstractFormBuilder::IconPaths QAbstractFormBuilder::pixmapPaths(const QPixmap &pixmap) const
2550 {
2551     Q_UNUSED(pixmap);
2552     qWarning() << "QAbstractFormBuilder::pixmapPaths() is obsoleted";
2553     return IconPaths();
2554 }
2555 
2556 #endif // < Qt 6
2557 
2558 /*!
2559     \internal
2560     Set up a DOM property with icon.
2561 */
2562 
setIconProperty(DomProperty & p,const IconPaths & ip) const2563 void QAbstractFormBuilder::setIconProperty(DomProperty &p, const IconPaths &ip) const
2564 {
2565     DomResourceIcon *dpi = new DomResourceIcon;
2566 
2567  /* TODO
2568     if (!ip.second.isEmpty())
2569         pix->setAttributeResource(ip.second);
2570 */
2571     dpi->setText(ip.first);
2572 
2573     p.setAttributeName(QFormBuilderStrings::instance().iconAttribute);
2574     p.setElementIconSet(dpi);
2575 }
2576 
2577 /*!
2578     \internal
2579     Set up a DOM property with pixmap.
2580 */
2581 
setPixmapProperty(DomProperty & p,const IconPaths & ip) const2582 void QAbstractFormBuilder::setPixmapProperty(DomProperty &p, const IconPaths &ip) const
2583 {
2584     QFormBuilderExtra::setPixmapProperty(&p, ip);
2585 }
2586 
2587 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
2588 
2589 /*!
2590     \internal
2591     Convenience. Return DOM property for icon; 0 if icon.isNull().
2592 */
2593 
iconToDomProperty(const QIcon & icon) const2594 DomProperty* QAbstractFormBuilder::iconToDomProperty(const QIcon &icon) const
2595 {
2596     Q_UNUSED(icon);
2597     qWarning() << "QAbstractFormBuilder::iconToDomProperty() is obsoleted";
2598     return nullptr;
2599 }
2600 
2601 #endif // < Qt 6
2602 
2603 /*!
2604     \internal
2605     \since 4.4
2606 */
2607 
saveResource(const QVariant & v) const2608 DomProperty *QAbstractFormBuilder::saveResource(const QVariant &v) const
2609 {
2610     if (v.isNull())
2611         return nullptr;
2612 
2613     DomProperty *p = resourceBuilder()->saveResource(workingDirectory(), v);
2614     if (p)
2615         p->setAttributeName(QFormBuilderStrings::instance().iconAttribute);
2616     return p;
2617 }
2618 
2619 /*!
2620     \internal
2621     \since 4.5
2622 */
2623 
saveText(const QString & attributeName,const QVariant & v) const2624 DomProperty *QAbstractFormBuilder::saveText(const QString &attributeName, const QVariant &v) const
2625 {
2626     if (v.isNull())
2627         return nullptr;
2628 
2629     DomProperty *p = textBuilder()->saveText(v);
2630     if (p)
2631         p->setAttributeName(attributeName);
2632     return p;
2633 }
2634 
2635 /*!
2636     \internal
2637     Return the appropriate DOM pixmap for an image dom property.
2638     From 4.4 - unused
2639 */
2640 
domPixmap(const DomProperty * p)2641 const DomResourcePixmap *QAbstractFormBuilder::domPixmap(const DomProperty* p) {
2642     switch (p->kind()) {
2643     case DomProperty::IconSet:
2644         qDebug() << "** WARNING QAbstractFormBuilder::domPixmap() called for icon set!";
2645         break;
2646     case DomProperty::Pixmap:
2647         return p->elementPixmap();
2648     default:
2649         break;
2650     }
2651     return nullptr;
2652 }
2653 
2654 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
2655 
2656 /*!
2657     \internal
2658     Create icon from DOM.
2659     From 4.4 - unused
2660 */
2661 
domPropertyToIcon(const DomResourcePixmap * icon)2662 QIcon QAbstractFormBuilder::domPropertyToIcon(const DomResourcePixmap *icon)
2663 {
2664     Q_UNUSED(icon);
2665     qWarning() << "QAbstractFormBuilder::domPropertyToIcon() is obsoleted";
2666     return QIcon();
2667 }
2668 
2669 /*!
2670     \internal
2671     Create icon from DOM. Assert if !domPixmap
2672     From 4.4 - unused
2673 */
2674 
domPropertyToIcon(const DomProperty * p)2675 QIcon QAbstractFormBuilder::domPropertyToIcon(const DomProperty* p)
2676 {
2677     Q_UNUSED(p);
2678     qWarning() << "QAbstractFormBuilder::domPropertyToIcon() is obsoleted";
2679     return QIcon();
2680 }
2681 
2682 
2683 /*!
2684     \internal
2685     Create pixmap from DOM.
2686     From 4.4 - unused
2687 */
2688 
domPropertyToPixmap(const DomResourcePixmap * pixmap)2689 QPixmap QAbstractFormBuilder::domPropertyToPixmap(const DomResourcePixmap* pixmap)
2690 {
2691     Q_UNUSED(pixmap);
2692     qWarning() << "QAbstractFormBuilder::domPropertyToPixmap() is obsoleted";
2693     return QPixmap();
2694 }
2695 
2696 
2697 /*!
2698     \internal
2699     Create pixmap from DOM. Assert if !domPixmap
2700     From 4.4 - unused
2701 */
2702 
domPropertyToPixmap(const DomProperty * p)2703 QPixmap QAbstractFormBuilder::domPropertyToPixmap(const DomProperty* p)
2704 {
2705     Q_UNUSED(p);
2706     qWarning() << "QAbstractFormBuilder::domPropertyToPixmap() is obsoleted";
2707     return QPixmap();
2708 }
2709 
2710 #endif // < Qt 6
2711 
2712 /*!
2713     \fn void QAbstractFormBuilder::createConnections ( DomConnections *, QWidget * )
2714     \internal
2715 */
2716 
2717 /*!
2718     \fn void QAbstractFormBuilder::createCustomWidgets ( DomCustomWidgets * )
2719     \internal
2720 */
2721 
2722 /*!
2723     \fn void QAbstractFormBuilder::createResources ( DomResources * )
2724     \internal
2725 */
2726 
2727 /*!
2728     Returns a human-readable description of the last error occurred in load().
2729 
2730     \since 5.0
2731     \sa load()
2732 */
2733 
errorString() const2734 QString QAbstractFormBuilder::errorString() const
2735 {
2736     return d->m_errorString;
2737 }
2738 
2739 QT_END_NAMESPACE
2740