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
80QT_BEGIN_NAMESPACE
81
82typedef QMap<QString, bool> widget_map;
83Q_GLOBAL_STATIC(widget_map, g_widgets)
84
85class QUiLoader;
86class 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.
91QDataStream &operator<<(QDataStream &out, const QUiTranslatableStringValue &s)
92{
93 out << s.qualifier() << s.value();
94 return out;
95}
96
97QDataStream &operator>>(QDataStream &in, QUiTranslatableStringValue &s)
98{
99 in >> s.m_qualifier >> s.m_value;
100 return in;
101}
102#endif // QT_NO_DATASTREAM
103
104QString QUiTranslatableStringValue::translate(const QByteArray &className, bool idBased) const
105{
106 return idBased
107 ? qtTrId(id: m_qualifier.constData())
108 : QCoreApplication::translate(context: className.constData(), key: m_value.constData(), disambiguation: m_qualifier.constData());
109}
110
111#ifdef QFORMINTERNAL_NAMESPACE
112namespace QFormInternal
113{
114#endif
115
116class TranslatingTextBuilder : public QTextBuilder
117{
118public:
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
126 bool idBased() const { return m_idBased; }
127
128private:
129 bool m_idBased;
130 bool m_trEnabled;
131 QByteArray m_className;
132};
133
134QVariant 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(value: 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(value: strVal);
151}
152
153QVariant TranslatingTextBuilder::toNativeValue(const QVariant &value) const
154{
155 if (value.canConvert<QUiTranslatableStringValue>()) {
156 QUiTranslatableStringValue tsv = qvariant_cast<QUiTranslatableStringValue>(v: value);
157 if (!m_trEnabled)
158 return QString::fromUtf8(str: tsv.value().constData());
159 return QVariant::fromValue(value: tsv.translate(className: m_className, idBased: m_idBased));
160 }
161 if (value.canConvert<QString>())
162 return QVariant::fromValue(value: qvariant_cast<QString>(v: value));
163 return value;
164}
165
166// This is "exported" to linguist
167const QUiItemRolePair qUiItemRoles[] = {
168 { .realRole: Qt::DisplayRole, .shadowRole: Qt::DisplayPropertyRole },
169#if QT_CONFIG(tooltip)
170 { .realRole: Qt::ToolTipRole, .shadowRole: Qt::ToolTipPropertyRole },
171#endif
172#if QT_CONFIG(statustip)
173 { .realRole: Qt::StatusTipRole, .shadowRole: Qt::StatusTipPropertyRole },
174#endif
175#if QT_CONFIG(whatsthis)
176 { .realRole: Qt::WhatsThisRole, .shadowRole: Qt::WhatsThisPropertyRole },
177#endif
178 { .realRole: -1 , .shadowRole: -1 }
179};
180
181static 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(column: i, role: irs[j].shadowRole);
189 if (v.isValid()) {
190 QUiTranslatableStringValue tsv = qvariant_cast<QUiTranslatableStringValue>(v);
191 item->setData(column: i, role: irs[j].realRole, value: tsv.translate(className: class_name, idBased));
192 }
193 }
194 }
195
196 cnt = item->childCount();
197 for (int i = 0; i < cnt; ++i)
198 recursiveReTranslate(item: item->child(index: i), class_name, idBased);
199}
200
201template<typename T>
202static 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(className: class_name, idBased));
211 }
212 }
213}
214
215static 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
230class TranslationWatcher: public QObject
231{
232 Q_OBJECT
233
234public:
235 explicit TranslationWatcher(QObject *parent, const QByteArray &className, bool idBased):
236 QObject(parent),
237 m_className(className),
238 m_idBased(idBased)
239 {
240 }
241
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(index: sizeof(PROP_GENERIC_PREFIX) - 1);
249 const QUiTranslatableStringValue tsv =
250 qvariant_cast<QUiTranslatableStringValue>(v: o->property(name: prop));
251 o->setProperty(name: propName, value: tsv.translate(className: m_className, idBased: m_idBased));
252 }
253 }
254 if (0) {
255#if QT_CONFIG(tabwidget)
256 } else if (QTabWidget *tabw = qobject_cast<QTabWidget*>(object: 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*>(object: o)) {
270 const int cnt = listw->count();
271 for (int i = 0; i < cnt; ++i)
272 reTranslateWidgetItem(item: listw->item(row: i), class_name: m_className, idBased: m_idBased);
273#endif
274#if QT_CONFIG(treewidget)
275 } else if (QTreeWidget *treew = qobject_cast<QTreeWidget*>(object: o)) {
276 if (QTreeWidgetItem *item = treew->headerItem())
277 recursiveReTranslate(item, class_name: m_className, idBased: m_idBased);
278 const int cnt = treew->topLevelItemCount();
279 for (int i = 0; i < cnt; ++i) {
280 QTreeWidgetItem *item = treew->topLevelItem(index: i);
281 recursiveReTranslate(item, class_name: m_className, idBased: m_idBased);
282 }
283#endif
284#if QT_CONFIG(tablewidget)
285 } else if (QTableWidget *tablew = qobject_cast<QTableWidget*>(object: 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(item: tablew->horizontalHeaderItem(column: j), class_name: m_className, idBased: m_idBased);
290 for (int i = 0; i < row_cnt; ++i) {
291 reTranslateTableItem(item: tablew->verticalHeaderItem(row: i), class_name: m_className, idBased: m_idBased);
292 for (int j = 0; j < col_cnt; ++j)
293 reTranslateTableItem(item: tablew->item(row: i, column: j), class_name: m_className, idBased: m_idBased);
294 }
295#endif
296#if QT_CONFIG(combobox)
297 } else if (QComboBox *combow = qobject_cast<QComboBox*>(object: o)) {
298 if (!qobject_cast<QFontComboBox*>(object: o)) {
299 const int cnt = combow->count();
300 for (int i = 0; i < cnt; ++i) {
301 const QVariant v = combow->itemData(index: i, role: Qt::DisplayPropertyRole);
302 if (v.isValid()) {
303 QUiTranslatableStringValue tsv = qvariant_cast<QUiTranslatableStringValue>(v);
304 combow->setItemText(index: i, text: tsv.translate(className: m_className, idBased: m_idBased));
305 }
306 }
307 }
308#endif
309#if QT_CONFIG(toolbox)
310 } else if (QToolBox *toolw = qobject_cast<QToolBox*>(object: 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
324private:
325 QByteArray m_className;
326 bool m_idBased;
327};
328
329class FormBuilderPrivate: public QFormBuilder
330{
331 friend class QT_PREPEND_NAMESPACE(QUiLoader);
332 friend class QT_PREPEND_NAMESPACE(QUiLoaderPrivate);
333 using ParentClass = QFormBuilder;
334
335public:
336 QUiLoader *loader = nullptr;
337
338 bool dynamicTr = false;
339 bool trEnabled = true;
340
341 FormBuilderPrivate() = default;
342
343 QWidget *defaultCreateWidget(const QString &className, QWidget *parent, const QString &name)
344 {
345 return ParentClass::createWidget(widgetName: className, parentWidget: parent, name);
346 }
347
348 QLayout *defaultCreateLayout(const QString &className, QObject *parent, const QString &name)
349 {
350 return ParentClass::createLayout(layoutName: className, parent, name);
351 }
352
353 QAction *defaultCreateAction(QObject *parent, const QString &name)
354 {
355 return ParentClass::createAction(parent, name);
356 }
357
358 QActionGroup *defaultCreateActionGroup(QObject *parent, const QString &name)
359 {
360 return ParentClass::createActionGroup(parent, name);
361 }
362
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
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
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
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
408private:
409 QByteArray m_class;
410 TranslationWatcher *m_trwatch = nullptr;
411 bool m_idBased = false;
412};
413
414static 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
434void 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, className: m_class, idBased: m_idBased, strVal: &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(name: dynname, value: QVariant::fromValue(value: strVal));
457 anyTrs = trEnabled;
458 }
459 o->setProperty(name, value: text);
460 }
461 if (anyTrs)
462 o->installEventFilter(filterObj: m_trwatch);
463}
464
465QWidget *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
474QWidget *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*>(object: w)) {
483#endif
484#if QT_CONFIG(listwidget)
485 } else if (qobject_cast<QListWidget*>(object: w)) {
486#endif
487#if QT_CONFIG(treewidget)
488 } else if (qobject_cast<QTreeWidget*>(object: w)) {
489#endif
490#if QT_CONFIG(tablewidget)
491 } else if (qobject_cast<QTableWidget*>(object: w)) {
492#endif
493#if QT_CONFIG(combobox)
494 } else if (qobject_cast<QComboBox*>(object: w)) {
495 if (qobject_cast<QFontComboBox*>(object: w))
496 return w;
497#endif
498#if QT_CONFIG(toolbox)
499 } else if (qobject_cast<QToolBox*>(object: w)) {
500#endif
501 } else {
502 return w;
503 }
504 if (dynamicTr && trEnabled)
505 w->installEventFilter(filterObj: 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
522bool 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*>(object: parentWidget)) {
540 const DomPropertyHash attributes = propertyMap(properties: 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*>(object: parentWidget)) {
552 const DomPropertyHash attributes = propertyMap(properties: 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
568class QUiLoaderPrivate
569{
570public:
571#ifdef QFORMINTERNAL_NAMESPACE
572 QFormInternal::FormBuilderPrivate builder;
573#else
574 FormBuilderPrivate builder;
575#endif
576
577 void setupWidgetMap() const;
578};
579
580void 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*/
653QUiLoader::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>(typeName: "QUiTranslatableStringValue");
662 qRegisterMetaTypeStreamOperators<QUiTranslatableStringValue>(typeName: "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(t: libPath);
675 }
676
677 d->builder.setPluginPath(paths);
678#endif // QT_CONFIG(library)
679}
680
681/*!
682 Destroys the loader.
683*/
684QUiLoader::~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*/
692QWidget *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(mode: QIODevice::ReadOnly|QIODevice::Text);
698 return d->builder.load(dev: 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*/
707QStringList 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*/
719void 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*/
731void QUiLoader::addPluginPath(const QString &path)
732{
733 Q_D(QUiLoader);
734 d->builder.addPluginPath(pluginPath: 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*/
750QWidget *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*/
768QLayout *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 */
785QActionGroup *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*/
802QAction *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*/
816QStringList 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(akey: plugin->name(), avalue: 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
839QStringList 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
860void 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
872QDir 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
887void 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
902bool 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
920void 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
935bool 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
948QString QUiLoader::errorString() const
949{
950 Q_D(const QUiLoader);
951 return d->builder.errorString();
952}
953
954QT_END_NAMESPACE
955
956#include "quiloader.moc"
957

source code of qttools/src/designer/src/uitools/quiloader.cpp