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 
52 #include "quiloader.h"
53 #include "quiloader_p.h"
54 
55 #include <QtUiPlugin/customwidget.h>
56 
57 #include <formbuilder.h>
58 #include <formbuilderextra_p.h>
59 #include <textbuilder_p.h>
60 #include <ui4_p.h>
61 
62 #include <QtCore/qdebug.h>
63 #include <QtCore/qdatastream.h>
64 #include <QtWidgets/qaction.h>
65 #include <QtWidgets/qactiongroup.h>
66 #include <QtWidgets/qapplication.h>
67 #include <QtCore/qdir.h>
68 #include <QtCore/qlibraryinfo.h>
69 #include <QtWidgets/qlayout.h>
70 #include <QtWidgets/qwidget.h>
71 #include <QtCore/qmap.h>
72 #include <QtWidgets/qtabwidget.h>
73 #include <QtWidgets/qtreewidget.h>
74 #include <QtWidgets/qlistwidget.h>
75 #include <QtWidgets/qtablewidget.h>
76 #include <QtWidgets/qtoolbox.h>
77 #include <QtWidgets/qcombobox.h>
78 #include <QtWidgets/qfontcombobox.h>
79 
80 QT_BEGIN_NAMESPACE
81 
82 typedef QMap<QString, bool> widget_map;
83 Q_GLOBAL_STATIC(widget_map, g_widgets)
84 
85 class QUiLoader;
86 class QUiLoaderPrivate;
87 
88 #ifndef QT_NO_DATASTREAM
89 // QUiTranslatableStringValue must be streamable since they become part of the QVariant-based
90 // mime data when dragging items in views with QAbstractItemView::InternalMove.
operator <<(QDataStream & out,const QUiTranslatableStringValue & s)91 QDataStream &operator<<(QDataStream &out, const QUiTranslatableStringValue &s)
92 {
93     out << s.qualifier() << s.value();
94     return out;
95 }
96 
operator >>(QDataStream & in,QUiTranslatableStringValue & s)97 QDataStream &operator>>(QDataStream &in, QUiTranslatableStringValue &s)
98 {
99     in >> s.m_qualifier >> s.m_value;
100     return in;
101 }
102 #endif // QT_NO_DATASTREAM
103 
translate(const QByteArray & className,bool idBased) const104 QString QUiTranslatableStringValue::translate(const QByteArray &className, bool idBased) const
105 {
106     return idBased
107         ? qtTrId(m_qualifier.constData())
108         : QCoreApplication::translate(className.constData(), m_value.constData(), m_qualifier.constData());
109 }
110 
111 #ifdef QFORMINTERNAL_NAMESPACE
112 namespace QFormInternal
113 {
114 #endif
115 
116 class TranslatingTextBuilder : public QTextBuilder
117 {
118 public:
TranslatingTextBuilder(bool idBased,bool trEnabled,const QByteArray & className)119     explicit TranslatingTextBuilder(bool idBased, bool trEnabled, const QByteArray &className) :
120         m_idBased(idBased), m_trEnabled(trEnabled), m_className(className) {}
121 
122     QVariant loadText(const DomProperty *icon) const override;
123 
124     QVariant toNativeValue(const QVariant &value) const override;
125 
idBased() const126     bool idBased() const { return m_idBased; }
127 
128 private:
129     bool m_idBased;
130     bool m_trEnabled;
131     QByteArray m_className;
132 };
133 
loadText(const DomProperty * text) const134 QVariant TranslatingTextBuilder::loadText(const DomProperty *text) const
135 {
136     const DomString *str = text->elementString();
137     if (!str)
138         return QVariant();
139     if (str->hasAttributeNotr()) {
140         const QString notr = str->attributeNotr();
141         if (notr == QStringLiteral("true") || notr == QStringLiteral("yes"))
142             return QVariant::fromValue(str->text());
143     }
144     QUiTranslatableStringValue strVal;
145     strVal.setValue(str->text().toUtf8());
146     if (m_idBased)
147         strVal.setQualifier(str->attributeId().toUtf8());
148     else if (str->hasAttributeComment())
149         strVal.setQualifier(str->attributeComment().toUtf8());
150     return QVariant::fromValue(strVal);
151 }
152 
toNativeValue(const QVariant & value) const153 QVariant TranslatingTextBuilder::toNativeValue(const QVariant &value) const
154 {
155     if (value.canConvert<QUiTranslatableStringValue>()) {
156         QUiTranslatableStringValue tsv = qvariant_cast<QUiTranslatableStringValue>(value);
157         if (!m_trEnabled)
158             return QString::fromUtf8(tsv.value().constData());
159         return QVariant::fromValue(tsv.translate(m_className, m_idBased));
160     }
161     if (value.canConvert<QString>())
162         return QVariant::fromValue(qvariant_cast<QString>(value));
163     return value;
164 }
165 
166 // This is "exported" to linguist
167 const QUiItemRolePair qUiItemRoles[] = {
168     { Qt::DisplayRole, Qt::DisplayPropertyRole },
169 #if QT_CONFIG(tooltip)
170     { Qt::ToolTipRole, Qt::ToolTipPropertyRole },
171 #endif
172 #if QT_CONFIG(statustip)
173     { Qt::StatusTipRole, Qt::StatusTipPropertyRole },
174 #endif
175 #if QT_CONFIG(whatsthis)
176     { Qt::WhatsThisRole, Qt::WhatsThisPropertyRole },
177 #endif
178     { -1 , -1 }
179 };
180 
recursiveReTranslate(QTreeWidgetItem * item,const QByteArray & class_name,bool idBased)181 static void recursiveReTranslate(QTreeWidgetItem *item, const QByteArray &class_name, bool idBased)
182 {
183     const QUiItemRolePair *irs = qUiItemRoles;
184 
185     int cnt = item->columnCount();
186     for (int i = 0; i < cnt; ++i) {
187         for (unsigned j = 0; irs[j].shadowRole >= 0; j++) {
188             QVariant v = item->data(i, irs[j].shadowRole);
189             if (v.isValid()) {
190                 QUiTranslatableStringValue tsv = qvariant_cast<QUiTranslatableStringValue>(v);
191                 item->setData(i, irs[j].realRole, tsv.translate(class_name, idBased));
192             }
193         }
194     }
195 
196     cnt = item->childCount();
197     for (int i = 0; i < cnt; ++i)
198         recursiveReTranslate(item->child(i), class_name, idBased);
199 }
200 
201 template<typename T>
reTranslateWidgetItem(T * item,const QByteArray & class_name,bool idBased)202 static void reTranslateWidgetItem(T *item, const QByteArray &class_name, bool idBased)
203 {
204     const QUiItemRolePair *irs = qUiItemRoles;
205 
206     for (unsigned j = 0; irs[j].shadowRole >= 0; j++) {
207         QVariant v = item->data(irs[j].shadowRole);
208         if (v.isValid()) {
209             QUiTranslatableStringValue tsv = qvariant_cast<QUiTranslatableStringValue>(v);
210             item->setData(irs[j].realRole, tsv.translate(class_name, idBased));
211         }
212     }
213 }
214 
reTranslateTableItem(QTableWidgetItem * item,const QByteArray & class_name,bool idBased)215 static void reTranslateTableItem(QTableWidgetItem *item, const QByteArray &class_name, bool idBased)
216 {
217     if (item)
218         reTranslateWidgetItem(item, class_name, idBased);
219 }
220 
221 #define RETRANSLATE_SUBWIDGET_PROP(mainWidget, setter, propName) \
222     do { \
223         QVariant v = mainWidget->widget(i)->property(propName); \
224         if (v.isValid()) { \
225             QUiTranslatableStringValue tsv = qvariant_cast<QUiTranslatableStringValue>(v); \
226             mainWidget->setter(i, tsv.translate(m_className, m_idBased)); \
227         } \
228     } while (0)
229 
230 class TranslationWatcher: public QObject
231 {
232     Q_OBJECT
233 
234 public:
TranslationWatcher(QObject * parent,const QByteArray & className,bool idBased)235     explicit TranslationWatcher(QObject *parent, const QByteArray &className, bool idBased):
236         QObject(parent),
237         m_className(className),
238         m_idBased(idBased)
239     {
240     }
241 
eventFilter(QObject * o,QEvent * event)242     bool eventFilter(QObject *o, QEvent *event) override
243     {
244         if (event->type() == QEvent::LanguageChange) {
245             const auto &dynamicPropertyNames = o->dynamicPropertyNames();
246             for (const QByteArray &prop : dynamicPropertyNames) {
247                 if (prop.startsWith(PROP_GENERIC_PREFIX)) {
248                     const QByteArray propName = prop.mid(sizeof(PROP_GENERIC_PREFIX) - 1);
249                     const QUiTranslatableStringValue tsv =
250                                 qvariant_cast<QUiTranslatableStringValue>(o->property(prop));
251                     o->setProperty(propName, tsv.translate(m_className, m_idBased));
252                 }
253             }
254             if (0) {
255 #if QT_CONFIG(tabwidget)
256             } else if (QTabWidget *tabw = qobject_cast<QTabWidget*>(o)) {
257                 const int cnt = tabw->count();
258                 for (int i = 0; i < cnt; ++i) {
259                     RETRANSLATE_SUBWIDGET_PROP(tabw, setTabText, PROP_TABPAGETEXT);
260 #if QT_CONFIG(tooltip)
261                     RETRANSLATE_SUBWIDGET_PROP(tabw, setTabToolTip, PROP_TABPAGETOOLTIP);
262 # endif
263 #if QT_CONFIG(whatsthis)
264                     RETRANSLATE_SUBWIDGET_PROP(tabw, setTabWhatsThis, PROP_TABPAGEWHATSTHIS);
265 # endif
266                 }
267 #endif
268 #if QT_CONFIG(listwidget)
269             } else if (QListWidget *listw = qobject_cast<QListWidget*>(o)) {
270                 const int cnt = listw->count();
271                 for (int i = 0; i < cnt; ++i)
272                     reTranslateWidgetItem(listw->item(i), m_className, m_idBased);
273 #endif
274 #if QT_CONFIG(treewidget)
275             } else if (QTreeWidget *treew = qobject_cast<QTreeWidget*>(o)) {
276                 if (QTreeWidgetItem *item = treew->headerItem())
277                     recursiveReTranslate(item, m_className, m_idBased);
278                 const int cnt = treew->topLevelItemCount();
279                 for (int i = 0; i < cnt; ++i) {
280                     QTreeWidgetItem *item = treew->topLevelItem(i);
281                     recursiveReTranslate(item, m_className, m_idBased);
282                 }
283 #endif
284 #if QT_CONFIG(tablewidget)
285             } else if (QTableWidget *tablew = qobject_cast<QTableWidget*>(o)) {
286                 const int row_cnt = tablew->rowCount();
287                 const int col_cnt = tablew->columnCount();
288                 for (int j = 0; j < col_cnt; ++j)
289                     reTranslateTableItem(tablew->horizontalHeaderItem(j), m_className, m_idBased);
290                 for (int i = 0; i < row_cnt; ++i) {
291                     reTranslateTableItem(tablew->verticalHeaderItem(i), m_className, m_idBased);
292                     for (int j = 0; j < col_cnt; ++j)
293                         reTranslateTableItem(tablew->item(i, j), m_className, m_idBased);
294                 }
295 #endif
296 #if QT_CONFIG(combobox)
297             } else if (QComboBox *combow = qobject_cast<QComboBox*>(o)) {
298                 if (!qobject_cast<QFontComboBox*>(o)) {
299                     const int cnt = combow->count();
300                     for (int i = 0; i < cnt; ++i) {
301                         const QVariant v = combow->itemData(i, Qt::DisplayPropertyRole);
302                         if (v.isValid()) {
303                             QUiTranslatableStringValue tsv = qvariant_cast<QUiTranslatableStringValue>(v);
304                             combow->setItemText(i, tsv.translate(m_className, m_idBased));
305                         }
306                     }
307                 }
308 #endif
309 #if QT_CONFIG(toolbox)
310             } else if (QToolBox *toolw = qobject_cast<QToolBox*>(o)) {
311                 const int cnt = toolw->count();
312                 for (int i = 0; i < cnt; ++i) {
313                     RETRANSLATE_SUBWIDGET_PROP(toolw, setItemText, PROP_TOOLITEMTEXT);
314 #if QT_CONFIG(tooltip)
315                     RETRANSLATE_SUBWIDGET_PROP(toolw, setItemToolTip, PROP_TOOLITEMTOOLTIP);
316 # endif
317                 }
318 #endif
319             }
320         }
321         return false;
322     }
323 
324 private:
325     QByteArray m_className;
326     bool m_idBased;
327 };
328 
329 class FormBuilderPrivate: public QFormBuilder
330 {
331     friend class QT_PREPEND_NAMESPACE(QUiLoader);
332     friend class QT_PREPEND_NAMESPACE(QUiLoaderPrivate);
333     using ParentClass = QFormBuilder;
334 
335 public:
336     QUiLoader *loader = nullptr;
337 
338     bool dynamicTr = false;
339     bool trEnabled = true;
340 
341     FormBuilderPrivate() = default;
342 
defaultCreateWidget(const QString & className,QWidget * parent,const QString & name)343     QWidget *defaultCreateWidget(const QString &className, QWidget *parent, const QString &name)
344     {
345         return ParentClass::createWidget(className, parent, name);
346     }
347 
defaultCreateLayout(const QString & className,QObject * parent,const QString & name)348     QLayout *defaultCreateLayout(const QString &className, QObject *parent, const QString &name)
349     {
350         return ParentClass::createLayout(className, parent, name);
351     }
352 
defaultCreateAction(QObject * parent,const QString & name)353     QAction *defaultCreateAction(QObject *parent, const QString &name)
354     {
355         return ParentClass::createAction(parent, name);
356     }
357 
defaultCreateActionGroup(QObject * parent,const QString & name)358     QActionGroup *defaultCreateActionGroup(QObject *parent, const QString &name)
359     {
360         return ParentClass::createActionGroup(parent, name);
361     }
362 
createWidget(const QString & className,QWidget * parent,const QString & name)363     QWidget *createWidget(const QString &className, QWidget *parent, const QString &name) override
364     {
365         if (QWidget *widget = loader->createWidget(className, parent, name)) {
366             widget->setObjectName(name);
367             return widget;
368         }
369 
370         return nullptr;
371     }
372 
createLayout(const QString & className,QObject * parent,const QString & name)373     QLayout *createLayout(const QString &className, QObject *parent, const QString &name) override
374     {
375         if (QLayout *layout = loader->createLayout(className, parent, name)) {
376             layout->setObjectName(name);
377             return layout;
378         }
379 
380         return nullptr;
381     }
382 
createActionGroup(QObject * parent,const QString & name)383     QActionGroup *createActionGroup(QObject *parent, const QString &name) override
384     {
385         if (QActionGroup *actionGroup = loader->createActionGroup(parent, name)) {
386             actionGroup->setObjectName(name);
387             return actionGroup;
388         }
389 
390         return nullptr;
391     }
392 
createAction(QObject * parent,const QString & name)393     QAction *createAction(QObject *parent, const QString &name)  override
394     {
395         if (QAction *action = loader->createAction(parent, name)) {
396             action->setObjectName(name);
397             return action;
398         }
399 
400         return nullptr;
401     }
402 
403     void applyProperties(QObject *o, const QList<DomProperty*> &properties) override;
404     QWidget *create(DomUI *ui, QWidget *parentWidget) override;
405     QWidget *create(DomWidget *ui_widget, QWidget *parentWidget) override;
406     bool addItem(DomWidget *ui_widget, QWidget *widget, QWidget *parentWidget) override;
407 
408 private:
409     QByteArray m_class;
410     TranslationWatcher *m_trwatch = nullptr;
411     bool m_idBased = false;
412 };
413 
convertTranslatable(const DomProperty * p,const QByteArray & className,bool idBased,QUiTranslatableStringValue * strVal)414 static QString convertTranslatable(const DomProperty *p, const QByteArray &className,
415                                    bool idBased, QUiTranslatableStringValue *strVal)
416 {
417     if (p->kind() != DomProperty::String)
418         return QString();
419     const DomString *dom_str = p->elementString();
420     if (!dom_str)
421         return QString();
422     if (dom_str->hasAttributeNotr()) {
423         const QString notr = dom_str->attributeNotr();
424         if (notr == QStringLiteral("yes") || notr == QStringLiteral("true"))
425             return QString();
426     }
427     strVal->setValue(dom_str->text().toUtf8());
428     strVal->setQualifier(idBased ? dom_str->attributeId().toUtf8() : dom_str->attributeComment().toUtf8());
429     if (strVal->value().isEmpty() && strVal->qualifier().isEmpty())
430         return QString();
431     return strVal->translate(className, idBased);
432 }
433 
applyProperties(QObject * o,const QList<DomProperty * > & properties)434 void FormBuilderPrivate::applyProperties(QObject *o, const QList<DomProperty*> &properties)
435 {
436     QFormBuilder::applyProperties(o, properties);
437 
438     if (!m_trwatch)
439         m_trwatch = new TranslationWatcher(o, m_class, m_idBased);
440 
441     if (properties.isEmpty())
442         return;
443 
444     // Unlike string item roles, string properties are not loaded via the textBuilder
445     // (as they are "shadowed" by the property sheets in designer). So do the initial
446     // translation here.
447     bool anyTrs = false;
448     for (const DomProperty *p : properties) {
449         QUiTranslatableStringValue strVal;
450         const QString text = convertTranslatable(p, m_class, m_idBased, &strVal);
451         if (text.isEmpty())
452             continue;
453         const QByteArray name = p->attributeName().toUtf8();
454         if (dynamicTr) {
455             const QByteArray dynname = QByteArray(PROP_GENERIC_PREFIX + name);
456             o->setProperty(dynname, QVariant::fromValue(strVal));
457             anyTrs = trEnabled;
458         }
459         o->setProperty(name, text);
460     }
461     if (anyTrs)
462         o->installEventFilter(m_trwatch);
463 }
464 
create(DomUI * ui,QWidget * parentWidget)465 QWidget *FormBuilderPrivate::create(DomUI *ui, QWidget *parentWidget)
466 {
467     m_class = ui->elementClass().toUtf8();
468     m_trwatch = nullptr;
469     m_idBased = ui->attributeIdbasedtr();
470     setTextBuilder(new TranslatingTextBuilder(m_idBased, trEnabled, m_class));
471     return QFormBuilder::create(ui, parentWidget);
472 }
473 
create(DomWidget * ui_widget,QWidget * parentWidget)474 QWidget *FormBuilderPrivate::create(DomWidget *ui_widget, QWidget *parentWidget)
475 {
476     QWidget *w = QFormBuilder::create(ui_widget, parentWidget);
477     if (w == nullptr)
478         return nullptr;
479 
480     if (0) {
481 #if QT_CONFIG(tabwidget)
482     } else if (qobject_cast<QTabWidget*>(w)) {
483 #endif
484 #if QT_CONFIG(listwidget)
485     } else if (qobject_cast<QListWidget*>(w)) {
486 #endif
487 #if QT_CONFIG(treewidget)
488     } else if (qobject_cast<QTreeWidget*>(w)) {
489 #endif
490 #if QT_CONFIG(tablewidget)
491     } else if (qobject_cast<QTableWidget*>(w)) {
492 #endif
493 #if QT_CONFIG(combobox)
494     } else if (qobject_cast<QComboBox*>(w)) {
495         if (qobject_cast<QFontComboBox*>(w))
496             return w;
497 #endif
498 #if QT_CONFIG(toolbox)
499     } else if (qobject_cast<QToolBox*>(w)) {
500 #endif
501     } else {
502         return w;
503     }
504     if (dynamicTr && trEnabled)
505         w->installEventFilter(m_trwatch);
506     return w;
507 }
508 
509 #define TRANSLATE_SUBWIDGET_PROP(mainWidget, attribute, setter, propName) \
510     do { \
511         if (const DomProperty *p##attribute = attributes.value(strings.attribute)) { \
512             QUiTranslatableStringValue strVal; \
513             const QString text = convertTranslatable(p##attribute, m_class, m_idBased, &strVal); \
514             if (!text.isEmpty()) { \
515                 if (dynamicTr) \
516                     mainWidget->widget(i)->setProperty(propName, QVariant::fromValue(strVal)); \
517                 mainWidget->setter(i, text); \
518             } \
519         } \
520     } while (0)
521 
addItem(DomWidget * ui_widget,QWidget * widget,QWidget * parentWidget)522 bool FormBuilderPrivate::addItem(DomWidget *ui_widget, QWidget *widget, QWidget *parentWidget)
523 {
524     if (parentWidget == nullptr)
525         return true;
526 
527     if (!ParentClass::addItem(ui_widget, widget, parentWidget))
528         return false;
529 
530     // Check special cases. First: Custom container
531     const QString className = QLatin1String(parentWidget->metaObject()->className());
532     if (!d->customWidgetAddPageMethod(className).isEmpty())
533         return true;
534 
535     const QFormBuilderStrings &strings = QFormBuilderStrings::instance();
536 
537     if (0) {
538 #if QT_CONFIG(tabwidget)
539     } else if (QTabWidget *tabWidget = qobject_cast<QTabWidget*>(parentWidget)) {
540         const DomPropertyHash attributes = propertyMap(ui_widget->elementAttribute());
541         const int i = tabWidget->count() - 1;
542         TRANSLATE_SUBWIDGET_PROP(tabWidget, titleAttribute, setTabText, PROP_TABPAGETEXT);
543 #if QT_CONFIG(tooltip)
544         TRANSLATE_SUBWIDGET_PROP(tabWidget, toolTipAttribute, setTabToolTip, PROP_TABPAGETOOLTIP);
545 # endif
546 #if QT_CONFIG(whatsthis)
547         TRANSLATE_SUBWIDGET_PROP(tabWidget, whatsThisAttribute, setTabWhatsThis, PROP_TABPAGEWHATSTHIS);
548 # endif
549 #endif
550 #if QT_CONFIG(toolbox)
551     } else if (QToolBox *toolBox = qobject_cast<QToolBox*>(parentWidget)) {
552         const DomPropertyHash attributes = propertyMap(ui_widget->elementAttribute());
553         const int i = toolBox->count() - 1;
554         TRANSLATE_SUBWIDGET_PROP(toolBox, labelAttribute, setItemText, PROP_TOOLITEMTEXT);
555 #if QT_CONFIG(tooltip)
556         TRANSLATE_SUBWIDGET_PROP(toolBox, toolTipAttribute, setItemToolTip, PROP_TOOLITEMTOOLTIP);
557 # endif
558 #endif
559     }
560 
561     return true;
562 }
563 
564 #ifdef QFORMINTERNAL_NAMESPACE
565 }
566 #endif
567 
568 class QUiLoaderPrivate
569 {
570 public:
571 #ifdef QFORMINTERNAL_NAMESPACE
572     QFormInternal::FormBuilderPrivate builder;
573 #else
574     FormBuilderPrivate builder;
575 #endif
576 
577     void setupWidgetMap() const;
578 };
579 
setupWidgetMap() const580 void QUiLoaderPrivate::setupWidgetMap() const
581 {
582     if (!g_widgets()->isEmpty())
583         return;
584 
585 #define DECLARE_WIDGET(a, b) g_widgets()->insert(QLatin1String(#a), true);
586 #define DECLARE_LAYOUT(a, b)
587 
588 #include "widgets.table"
589 
590 #undef DECLARE_WIDGET
591 #undef DECLARE_WIDGET_1
592 #undef DECLARE_LAYOUT
593 }
594 
595 /*!
596     \class QUiLoader
597     \inmodule QtUiTools
598 
599     \brief The QUiLoader class enables standalone applications to
600     dynamically create user interfaces at run-time using the
601     information stored in UI files or specified in plugin paths.
602 
603     In addition, you can customize or create your own user interface by
604     deriving your own loader class.
605 
606     If you have a custom component or an application that embeds \QD, you can
607     also use the QFormBuilder class provided by the QtDesigner module to create
608     user interfaces from UI files.
609 
610     The QUiLoader class provides a collection of functions allowing you to
611     create widgets based on the information stored in UI files (created
612     with \QD) or available in the specified plugin paths. The specified plugin
613     paths can be retrieved using the pluginPaths() function. Similarly, the
614     contents of a UI file can be retrieved using the load() function. For
615     example:
616 
617     \snippet quiloader/mywidget.cpp 0
618 
619     \if !defined(qtforpython)
620     By including the user interface in the form's resources (\c myform.qrc), we
621     ensure that it will be present at run-time:
622 
623     \quotefile quiloader/mywidget.qrc
624     \endif
625 
626     The availableWidgets() function returns a QStringList with the class names
627     of the widgets available in the specified plugin paths. To create these
628     widgets, simply use the createWidget() function. For example:
629 
630     \snippet quiloader/main.cpp 0
631 
632     To make a custom widget available to the loader, you can use the
633     addPluginPath() function; to remove all available widgets, you can call
634     the clearPluginPaths() function.
635 
636     The createAction(), createActionGroup(), createLayout(), and createWidget()
637     functions are used internally by the QUiLoader class whenever it has to
638     create an action, action group, layout, or widget respectively. For that
639     reason, you can subclass the QUiLoader class and reimplement these
640     functions to intervene the process of constructing a user interface. For
641     example, you might want to have a list of the actions created when loading
642     a form or creating a custom widget.
643 
644     For a complete example using the QUiLoader class, see the
645     \l{Calculator Builder Example}.
646 
647     \sa {Qt UI Tools}, QFormBuilder
648 */
649 
650 /*!
651     Creates a form loader with the given \a parent.
652 */
QUiLoader(QObject * parent)653 QUiLoader::QUiLoader(QObject *parent)
654     : QObject(parent), d_ptr(new QUiLoaderPrivate)
655 {
656     Q_D(QUiLoader);
657 
658 #ifndef QT_NO_DATASTREAM
659     static int metaTypeId = 0;
660     if (!metaTypeId) {
661         metaTypeId = qRegisterMetaType<QUiTranslatableStringValue>("QUiTranslatableStringValue");
662         qRegisterMetaTypeStreamOperators<QUiTranslatableStringValue>("QUiTranslatableStringValue");
663     }
664 #endif // QT_NO_DATASTREAM
665     d->builder.loader = this;
666 
667 #if QT_CONFIG(library)
668     QStringList paths;
669     const QStringList &libraryPaths = QApplication::libraryPaths();
670     for (const QString &path : libraryPaths) {
671         QString libPath = path;
672         libPath  += QDir::separator();
673         libPath  += QStringLiteral("designer");
674         paths.append(libPath);
675     }
676 
677     d->builder.setPluginPath(paths);
678 #endif // QT_CONFIG(library)
679 }
680 
681 /*!
682     Destroys the loader.
683 */
684 QUiLoader::~QUiLoader() = default;
685 
686 /*!
687     Loads a form from the given \a device and creates a new widget with the
688     given \a parentWidget to hold its contents.
689 
690     \sa createWidget(), errorString()
691 */
load(QIODevice * device,QWidget * parentWidget)692 QWidget *QUiLoader::load(QIODevice *device, QWidget *parentWidget)
693 {
694     Q_D(QUiLoader);
695     // QXmlStreamReader will report errors on open failure.
696     if (!device->isOpen())
697         device->open(QIODevice::ReadOnly|QIODevice::Text);
698     return d->builder.load(device, parentWidget);
699 }
700 
701 /*!
702     Returns a list naming the paths in which the loader will search when
703     locating custom widget plugins.
704 
705     \sa addPluginPath(), clearPluginPaths()
706 */
pluginPaths() const707 QStringList QUiLoader::pluginPaths() const
708 {
709     Q_D(const QUiLoader);
710     return d->builder.pluginPaths();
711 }
712 
713 /*!
714     Clears the list of paths in which the loader will search when locating
715     plugins.
716 
717     \sa addPluginPath(), pluginPaths()
718 */
clearPluginPaths()719 void QUiLoader::clearPluginPaths()
720 {
721     Q_D(QUiLoader);
722     d->builder.clearPluginPaths();
723 }
724 
725 /*!
726     Adds the given \a path to the list of paths in which the loader will search
727     when locating plugins.
728 
729     \sa pluginPaths(), clearPluginPaths()
730 */
addPluginPath(const QString & path)731 void QUiLoader::addPluginPath(const QString &path)
732 {
733     Q_D(QUiLoader);
734     d->builder.addPluginPath(path);
735 }
736 
737 /*!
738     Creates a new widget with the given \a parent and \a name using the class
739     specified by \a className. You can use this function to create any of the
740     widgets returned by the availableWidgets() function.
741 
742     The function is also used internally by the QUiLoader class whenever it
743     creates a widget. Hence, you can subclass QUiLoader and reimplement this
744     function to intervene process of constructing a user interface or widget.
745     However, in your implementation, ensure that you call QUiLoader's version
746     first.
747 
748     \sa availableWidgets(), load()
749 */
createWidget(const QString & className,QWidget * parent,const QString & name)750 QWidget *QUiLoader::createWidget(const QString &className, QWidget *parent, const QString &name)
751 {
752     Q_D(QUiLoader);
753     return d->builder.defaultCreateWidget(className, parent, name);
754 }
755 
756 /*!
757     Creates a new layout with the given \a parent and \a name using the class
758     specified by \a className.
759 
760     The function is also used internally by the QUiLoader class whenever it
761     creates a widget. Hence, you can subclass QUiLoader and reimplement this
762     function to intervene process of constructing a user interface or widget.
763     However, in your implementation, ensure that you call QUiLoader's version
764     first.
765 
766     \sa createWidget(), load()
767 */
createLayout(const QString & className,QObject * parent,const QString & name)768 QLayout *QUiLoader::createLayout(const QString &className, QObject *parent, const QString &name)
769 {
770     Q_D(QUiLoader);
771     return d->builder.defaultCreateLayout(className, parent, name);
772 }
773 
774 /*!
775     Creates a new action group with the given \a parent and \a name.
776 
777     The function is also used internally by the QUiLoader class whenever it
778     creates a widget. Hence, you can subclass QUiLoader and reimplement this
779     function to intervene process of constructing a user interface or widget.
780     However, in your implementation, ensure that you call QUiLoader's version
781     first.
782 
783     \sa createAction(), createWidget(), load()
784  */
createActionGroup(QObject * parent,const QString & name)785 QActionGroup *QUiLoader::createActionGroup(QObject *parent, const QString &name)
786 {
787     Q_D(QUiLoader);
788     return d->builder.defaultCreateActionGroup(parent, name);
789 }
790 
791 /*!
792     Creates a new action with the given \a parent and \a name.
793 
794     The function is also used internally by the QUiLoader class whenever it
795     creates a widget. Hence, you can subclass QUiLoader and reimplement this
796     function to intervene process of constructing a user interface or widget.
797     However, in your implementation, ensure that you call QUiLoader's version
798     first.
799 
800     \sa createActionGroup(), createWidget(), load()
801 */
createAction(QObject * parent,const QString & name)802 QAction *QUiLoader::createAction(QObject *parent, const QString &name)
803 {
804     Q_D(QUiLoader);
805     return d->builder.defaultCreateAction(parent, name);
806 }
807 
808 /*!
809     Returns a list naming all available widgets that can be built using the
810     createWidget() function, i.e all the widgets specified within the given
811     plugin paths.
812 
813     \sa pluginPaths(), createWidget()
814 
815 */
availableWidgets() const816 QStringList QUiLoader::availableWidgets() const
817 {
818     Q_D(const QUiLoader);
819 
820     d->setupWidgetMap();
821     widget_map available = *g_widgets();
822 
823     const auto &customWidgets = d->builder.customWidgets();
824     for (QDesignerCustomWidgetInterface *plugin : customWidgets)
825         available.insert(plugin->name(), true);
826 
827     return available.keys();
828 }
829 
830 
831 /*!
832     \since 4.5
833     Returns a list naming all available layouts that can be built using the
834     createLayout() function
835 
836     \sa createLayout()
837 */
838 
availableLayouts() const839 QStringList QUiLoader::availableLayouts() const
840 {
841     QStringList rc;
842 #define DECLARE_WIDGET(a, b)
843 #define DECLARE_LAYOUT(a, b) rc.push_back(QLatin1String(#a));
844 
845 #include "widgets.table"
846 
847 #undef DECLARE_WIDGET
848 #undef DECLARE_LAYOUT
849     return rc;
850 }
851 
852 /*!
853     Sets the working directory of the loader to \a dir. The loader will look
854     for other resources, such as icons and resource files, in paths relative to
855     this directory.
856 
857     \sa workingDirectory()
858 */
859 
setWorkingDirectory(const QDir & dir)860 void QUiLoader::setWorkingDirectory(const QDir &dir)
861 {
862     Q_D(QUiLoader);
863     d->builder.setWorkingDirectory(dir);
864 }
865 
866 /*!
867     Returns the working directory of the loader.
868 
869     \sa setWorkingDirectory()
870 */
871 
workingDirectory() const872 QDir QUiLoader::workingDirectory() const
873 {
874     Q_D(const QUiLoader);
875     return d->builder.workingDirectory();
876 }
877 /*!
878     \since 4.5
879 
880     If \a enabled is true, user interfaces loaded by this loader will
881     automatically retranslate themselves upon receiving a language change
882     event. Otherwise, the user interfaces will not be retranslated.
883 
884     \sa isLanguageChangeEnabled()
885 */
886 
setLanguageChangeEnabled(bool enabled)887 void QUiLoader::setLanguageChangeEnabled(bool enabled)
888 {
889     Q_D(QUiLoader);
890     d->builder.dynamicTr = enabled;
891 }
892 
893 /*!
894     \since 4.5
895 
896     Returns true if dynamic retranslation on language change is enabled;
897     returns false otherwise.
898 
899     \sa setLanguageChangeEnabled()
900 */
901 
isLanguageChangeEnabled() const902 bool QUiLoader::isLanguageChangeEnabled() const
903 {
904     Q_D(const QUiLoader);
905     return d->builder.dynamicTr;
906 }
907 
908 /*!
909     \internal
910     \since 4.5
911 
912     If \a enabled is true, user interfaces loaded by this loader will be
913     translated. Otherwise, the user interfaces will not be translated.
914 
915     \note This is orthogonal to languageChangeEnabled.
916 
917     \sa isLanguageChangeEnabled(), setLanguageChangeEnabled()
918 */
919 
setTranslationEnabled(bool enabled)920 void QUiLoader::setTranslationEnabled(bool enabled)
921 {
922     Q_D(QUiLoader);
923     d->builder.trEnabled = enabled;
924 }
925 
926 /*!
927     \internal
928     \since 4.5
929 
930     Returns true if translation is enabled; returns false otherwise.
931 
932     \sa setTranslationEnabled()
933 */
934 
isTranslationEnabled() const935 bool QUiLoader::isTranslationEnabled() const
936 {
937     Q_D(const QUiLoader);
938     return d->builder.trEnabled;
939 }
940 
941 /*!
942     Returns a human-readable description of the last error occurred in load().
943 
944     \since 5.0
945     \sa load()
946 */
947 
errorString() const948 QString QUiLoader::errorString() const
949 {
950     Q_D(const QUiLoader);
951     return d->builder.errorString();
952 }
953 
954 QT_END_NAMESPACE
955 
956 #include "quiloader.moc"
957