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

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