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

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