Revision control

++ // Copyright (C) 2020 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
++ // Qt-Security score:significant reason:default
++
++ #include "quiloader.h"
++ #include "quiloader_p.h"
++
++ #include <QtUiPlugin/customwidget.h>
++
++ #include <formbuilder.h>
++ #include <formbuilderextra_p.h>
++ #include <objectutils_p.h>
++ #include <textbuilder_p.h>
++ #include <ui4_p.h>
++
++ #include <QtWidgets/qapplication.h>
++ #include <QtWidgets/qlayout.h>
++ #include <QtWidgets/qwidget.h>
++ #if QT_CONFIG(tabwidget)
++ # include <QtWidgets/qtabwidget.h>
++ #endif
++ #if QT_CONFIG(treewidget)
++ # include <QtWidgets/qtreewidget.h>
++ #endif
++ #if QT_CONFIG(listwidget)
++ # include <QtWidgets/qlistwidget.h>
++ #endif
++ #if QT_CONFIG(tablewidget)
++ # include <QtWidgets/qtablewidget.h>
++ #endif
++ #if QT_CONFIG(toolbox)
++ # include <QtWidgets/qtoolbox.h>
++ #endif
++ #if QT_CONFIG(combobox)
++ # include <QtWidgets/qcombobox.h>
++ #endif
++
++ #include <QtGui/qaction.h>
++ #include <QtGui/qactiongroup.h>
++
++ #include <QtCore/qdebug.h>
++ #include <QtCore/qdatastream.h>
++ #include <QtCore/qmap.h>
++ #include <QtCore/qdir.h>
++ #include <QtCore/qlibraryinfo.h>
++
++ #include <algorithm>
++
++ QT_BEGIN_NAMESPACE
++
++ using widget_map = QMap<QString, bool>;
++ Q_GLOBAL_STATIC(widget_map, g_widgets)
++
++ class QUiLoader;
++ class QUiLoaderPrivate;
++
++ #ifndef QT_NO_DATASTREAM
++ // QUiTranslatableStringValue must be streamable since they become part of the QVariant-based
++ // mime data when dragging items in views with QAbstractItemView::InternalMove.
++ QDataStream &operator<<(QDataStream &out, const QUiTranslatableStringValue &s)
++ {
++ out << s.qualifier() << s.value();
++ return out;
++ }
++
++ QDataStream &operator>>(QDataStream &in, QUiTranslatableStringValue &s)
++ {
++ QByteArray qualifier, value;
++ in >> qualifier >> value;
++ s.setQualifier(qualifier);
++ s.setValue(value);
++ return in;
++ }
++ #endif // QT_NO_DATASTREAM
++
++ QString QUiTranslatableStringValue::translate(const QByteArray &className, bool idBased) const
++ {
++ return idBased
++ ? qtTrId(m_qualifier.constData())
++ : QCoreApplication::translate(className.constData(), m_value.constData(), m_qualifier.constData());
++ }
++
++ #ifdef QFORMINTERNAL_NAMESPACE
++ namespace QFormInternal
++ {
++ #endif
++
++ class TranslatingTextBuilder : public QTextBuilder
++ {
++ public:
++ explicit TranslatingTextBuilder(bool idBased, bool trEnabled, const QByteArray &className) :
++ m_idBased(idBased), m_trEnabled(trEnabled), m_className(className) {}
++
++ QVariant loadText(const DomProperty *icon) const override;
++
++ QVariant toNativeValue(const QVariant &value) const override;
++
++ bool idBased() const { return m_idBased; }
++
++ private:
++ bool m_idBased;
++ bool m_trEnabled;
++ QByteArray m_className;
++ };
++
++ QVariant TranslatingTextBuilder::loadText(const DomProperty *text) const
++ {
++ const DomString *str = text->elementString();
++ if (!str)
++ return {};
++ if (str->hasAttributeNotr()) {
++ const QString notr = str->attributeNotr();
++ if (notr == QStringLiteral("true") || notr == QStringLiteral("yes"))
++ return QVariant::fromValue(str->text());
++ }
++ QUiTranslatableStringValue strVal;
++ strVal.setValue(str->text().toUtf8());
++ if (m_idBased)
++ strVal.setQualifier(str->attributeId().toUtf8());
++ else if (str->hasAttributeComment())
++ strVal.setQualifier(str->attributeComment().toUtf8());
++ return QVariant::fromValue(strVal);
++ }
++
++ QVariant TranslatingTextBuilder::toNativeValue(const QVariant &value) const
++ {
++ if (value.canConvert<QUiTranslatableStringValue>()) {
++ auto tsv = qvariant_cast<QUiTranslatableStringValue>(value);
++ if (!m_trEnabled)
++ return QString::fromUtf8(tsv.value().constData());
++ return QVariant::fromValue(tsv.translate(m_className, m_idBased));
++ }
++ if (value.canConvert<QString>())
++ return QVariant::fromValue(qvariant_cast<QString>(value));
++ return value;
++ }
++
++ // This is "exported" to linguist
++ const QUiItemRolePair qUiItemRoles[] = {
++ { Qt::DisplayRole, Qt::DisplayPropertyRole },
++ #if QT_CONFIG(tooltip)
++ { Qt::ToolTipRole, Qt::ToolTipPropertyRole },
++ #endif
++ #if QT_CONFIG(statustip)
++ { Qt::StatusTipRole, Qt::StatusTipPropertyRole },
++ #endif
++ #if QT_CONFIG(whatsthis)
++ { Qt::WhatsThisRole, Qt::WhatsThisPropertyRole },
++ #endif
++ { -1 , -1 }
++ };
++
++ #if QT_CONFIG(treewidget)
++ static void recursiveReTranslate(QTreeWidgetItem *item, const QByteArray &class_name, bool idBased)
++ {
++ const QUiItemRolePair *irs = qUiItemRoles;
++
++ int cnt = item->columnCount();
++ for (int i = 0; i < cnt; ++i) {
++ for (unsigned j = 0; irs[j].shadowRole >= 0; j++) {
++ QVariant v = item->data(i, irs[j].shadowRole);
++ if (v.isValid()) {
++ auto tsv = qvariant_cast<QUiTranslatableStringValue>(v);
++ item->setData(i, irs[j].realRole, tsv.translate(class_name, idBased));
++ }
++ }
++ }
++
++ cnt = item->childCount();
++ for (int i = 0; i < cnt; ++i)
++ recursiveReTranslate(item->child(i), class_name, idBased);
++ }
++ #endif
++
++ template<typename T>
++ static void reTranslateWidgetItem(T *item, const QByteArray &class_name, bool idBased)
++ {
++ const QUiItemRolePair *irs = qUiItemRoles;
++
++ for (unsigned j = 0; irs[j].shadowRole >= 0; j++) {
++ QVariant v = item->data(irs[j].shadowRole);
++ if (v.isValid()) {
++ auto tsv = qvariant_cast<QUiTranslatableStringValue>(v);
++ item->setData(irs[j].realRole, tsv.translate(class_name, idBased));
++ }
++ }
++ }
++
++ #if QT_CONFIG(tablewidget)
++ static void reTranslateTableItem(QTableWidgetItem *item, const QByteArray &class_name, bool idBased)
++ {
++ if (item)
++ reTranslateWidgetItem(item, class_name, idBased);
++ }
++ #endif
++
++ class TranslationWatcher: public QObject
++ {
++ Q_OBJECT
++
++ public:
++ explicit TranslationWatcher(QObject *parent, const QByteArray &className, bool idBased):
++ QObject(parent),
++ m_className(className),
++ m_idBased(idBased)
++ {
++ }
++
++ bool eventFilter(QObject *o, QEvent *event) override
++ {
++ if (event->type() == QEvent::LanguageChange) {
++ const auto &dynamicPropertyNames = o->dynamicPropertyNames();
++ for (const QByteArray &prop : dynamicPropertyNames) {
++ if (prop.startsWith(PROP_GENERIC_PREFIX)) {
++ const QByteArray propName = prop.mid(sizeof(PROP_GENERIC_PREFIX) - 1);
++ const auto tsv =
++ qvariant_cast<QUiTranslatableStringValue>(o->property(prop.constData()));
++ o->setProperty(propName.constData(), tsv.translate(m_className, m_idBased));
++ }
++ }
++ if (0) {
++ #if QT_CONFIG(tabwidget)
++ } else if (auto *tabw = qobject_cast<QTabWidget*>(o)) {
++ const int cnt = tabw->count();
++ for (int i = 0; i < cnt; ++i) {
++ retranslateSubwidgetProperty(tabw, &QTabWidget::setTabText, i, PROP_TABPAGETEXT);
++ #if QT_CONFIG(tooltip)
++ retranslateSubwidgetProperty(tabw, &QTabWidget::setTabToolTip, i, PROP_TABPAGETOOLTIP);
++ # endif
++ #if QT_CONFIG(whatsthis)
++ retranslateSubwidgetProperty(tabw, &QTabWidget::setTabWhatsThis, i, PROP_TABPAGEWHATSTHIS);
++ # endif
++ }
++ #endif
++ #if QT_CONFIG(listwidget)
++ } else if (auto *listw = qobject_cast<QListWidget*>(o)) {
++ const int cnt = listw->count();
++ for (int i = 0; i < cnt; ++i)
++ reTranslateWidgetItem(listw->item(i), m_className, m_idBased);
++ #endif
++ #if QT_CONFIG(treewidget)
++ } else if (auto *treew = qobject_cast<QTreeWidget*>(o)) {
++ if (QTreeWidgetItem *item = treew->headerItem())
++ recursiveReTranslate(item, m_className, m_idBased);
++ const int cnt = treew->topLevelItemCount();
++ for (int i = 0; i < cnt; ++i) {
++ QTreeWidgetItem *item = treew->topLevelItem(i);
++ recursiveReTranslate(item, m_className, m_idBased);
++ }
++ #endif
++ #if QT_CONFIG(tablewidget)
++ } else if (auto *tablew = qobject_cast<QTableWidget*>(o)) {
++ const int row_cnt = tablew->rowCount();
++ const int col_cnt = tablew->columnCount();
++ for (int j = 0; j < col_cnt; ++j)
++ reTranslateTableItem(tablew->horizontalHeaderItem(j), m_className, m_idBased);
++ for (int i = 0; i < row_cnt; ++i) {
++ reTranslateTableItem(tablew->verticalHeaderItem(i), m_className, m_idBased);
++ for (int j = 0; j < col_cnt; ++j)
++ reTranslateTableItem(tablew->item(i, j), m_className, m_idBased);
++ }
++ #endif
++ #if QT_CONFIG(combobox)
++ } else if (auto *combow = qobject_cast<QComboBox*>(o)) {
++ if (!QFormBuilderExtra::isQFontComboBox(combow)) {
++ const int cnt = combow->count();
++ for (int i = 0; i < cnt; ++i) {
++ const QVariant v = combow->itemData(i, Qt::DisplayPropertyRole);
++ if (v.isValid()) {
++ auto tsv = qvariant_cast<QUiTranslatableStringValue>(v);
++ combow->setItemText(i, tsv.translate(m_className, m_idBased));
++ }
++ }
++ }
++ #endif
++ #if QT_CONFIG(toolbox)
++ } else if (auto *toolw = qobject_cast<QToolBox*>(o)) {
++ const int cnt = toolw->count();
++ for (int i = 0; i < cnt; ++i) {
++ retranslateSubwidgetProperty(toolw, &QToolBox::setItemText, i, PROP_TOOLITEMTEXT);
++ #if QT_CONFIG(tooltip)
++ retranslateSubwidgetProperty(toolw, &QToolBox::setItemToolTip, i, PROP_TOOLITEMTOOLTIP);
++ # endif
++ }
++ #endif
++ }
++ }
++ return false;
++ }
++
++ template <class Widget>
++ void retranslateSubwidgetProperty(Widget *mainWidget,
++ void (Widget::*setter)(int, const QString &),
++ int i,
++ const char *propName) const
++ {
++ QVariant v = mainWidget->widget(i)->property(propName);
++ if (v.isValid()) {
++ const auto tsv = qvariant_cast<QUiTranslatableStringValue>(v);
++ (mainWidget->*setter)(i, tsv.translate(m_className, m_idBased));
++ }
++ }
++
++ private:
++ QByteArray m_className;
++ bool m_idBased;
++ };
++
++ class FormBuilderPrivate: public QFormBuilder
++ {
++ friend class QT_PREPEND_NAMESPACE(QUiLoader);
++ friend class QT_PREPEND_NAMESPACE(QUiLoaderPrivate);
++ using ParentClass = QFormBuilder;
++
++ public:
++ QUiLoader *loader = nullptr;
++
++ bool dynamicTr = false;
++ bool trEnabled = true;
++
++ FormBuilderPrivate() = default;
++
++ QWidget *defaultCreateWidget(const QString &className, QWidget *parent, const QString &name)
++ {
++ return ParentClass::createWidget(className, parent, name);
++ }
++
++ QLayout *defaultCreateLayout(const QString &className, QObject *parent, const QString &name)
++ {
++ return ParentClass::createLayout(className, parent, name);
++ }
++
++ QAction *defaultCreateAction(QObject *parent, const QString &name)
++ {
++ return ParentClass::createAction(parent, name);
++ }
++
++ QActionGroup *defaultCreateActionGroup(QObject *parent, const QString &name)
++ {
++ return ParentClass::createActionGroup(parent, name);
++ }
++
++ QWidget *createWidget(const QString &className, QWidget *parent, const QString &name) override
++ {
++ if (QWidget *widget = loader->createWidget(className, parent, name)) {
++ widget->setObjectName(name);
++ return widget;
++ }
++
++ return nullptr;
++ }
++
++ QLayout *createLayout(const QString &className, QObject *parent, const QString &name) override
++ {
++ if (QLayout *layout = loader->createLayout(className, parent, name)) {
++ layout->setObjectName(name);
++ return layout;
++ }
++
++ return nullptr;
++ }
++
++ QActionGroup *createActionGroup(QObject *parent, const QString &name) override
++ {
++ if (QActionGroup *actionGroup = loader->createActionGroup(parent, name)) {
++ actionGroup->setObjectName(name);
++ return actionGroup;
++ }
++
++ return nullptr;
++ }
++
++ QAction *createAction(QObject *parent, const QString &name) override
++ {
++ if (QAction *action = loader->createAction(parent, name)) {
++ action->setObjectName(name);
++ return action;
++ }
++
++ return nullptr;
++ }
++
++ void applyProperties(QObject *o, const QList<DomProperty*> &properties) override;
++ QWidget *create(DomUI *ui, QWidget *parentWidget) override;
++ QWidget *create(DomWidget *ui_widget, QWidget *parentWidget) override;
++ bool addItem(DomWidget *ui_widget, QWidget *widget, QWidget *parentWidget) override;
++
++ template <class Widget>
++ void translateSubwidgetProperty(const QList<DomProperty*> &attributes,
++ Widget *mainWidget,
++ const QString &attribute,
++ int i,
++ void (Widget::*setter)(int, const QString &),
++ const char *propName) const;
++
++ private:
++ QByteArray m_class;
++ TranslationWatcher *m_trwatch = nullptr;
++ bool m_idBased = false;
++ };
++
++ static QString convertTranslatable(const DomProperty *p, const QByteArray &className,
++ bool idBased, QUiTranslatableStringValue *strVal)
++ {
++ if (p->kind() != DomProperty::String)
++ return {};
++ const DomString *dom_str = p->elementString();
++ if (!dom_str)
++ return {};
++ if (dom_str->hasAttributeNotr()) {
++ const QString notr = dom_str->attributeNotr();
++ if (notr == QStringLiteral("yes") || notr == QStringLiteral("true"))
++ return {};
++ }
++ strVal->setValue(dom_str->text().toUtf8());
++ strVal->setQualifier(idBased ? dom_str->attributeId().toUtf8() : dom_str->attributeComment().toUtf8());
++ if (strVal->value().isEmpty() && strVal->qualifier().isEmpty())
++ return {};
++ return strVal->translate(className, idBased);
++ }
++
++ static DomProperty *attributeByName(const QList<DomProperty*> &attributes,
++ const QString &attribute)
++ {
++ auto pred = [&attribute](const DomProperty *prop) {
++ return prop->attributeName() == attribute;
++ };
++ auto it = std::find_if(attributes.cbegin(), attributes.cend(), pred);
++ return it != attributes.cend() ? *it : nullptr;
++ }
++
++ template <class Widget>
++ void FormBuilderPrivate::translateSubwidgetProperty(const QList<DomProperty*> &attributes,
++ Widget *mainWidget,
++ const QString &attribute,
++ int i,
++ void (Widget::*setter)(int, const QString &),
++ const char *propName) const
++ {
++ if (const auto *p = attributeByName(attributes, attribute)) {
++ QUiTranslatableStringValue strVal;
++ const QString text = convertTranslatable(p, m_class, m_idBased, &strVal);
++ if (!text.isEmpty()) {
++ if (dynamicTr)
++ mainWidget->widget(i)->setProperty(propName, QVariant::fromValue(strVal));
++ (mainWidget->*setter)(i, text);
++ }
++ }
++ }
++
++ void FormBuilderPrivate::applyProperties(QObject *o, const QList<DomProperty*> &properties)
++ {
++ QFormBuilder::applyProperties(o, properties);
++
++ if (!m_trwatch)
++ m_trwatch = new TranslationWatcher(o, m_class, m_idBased);
++
++ if (properties.isEmpty())
++ return;
++
++ // Unlike string item roles, string properties are not loaded via the textBuilder
++ // (as they are "shadowed" by the property sheets in designer). So do the initial
++ // translation here.
++ bool anyTrs = false;
++ for (const DomProperty *p : properties) {
++ QUiTranslatableStringValue strVal;
++ const QString text = convertTranslatable(p, m_class, m_idBased, &strVal);
++ if (text.isEmpty())
++ continue;
++ const QByteArray name = p->attributeName().toUtf8();
++ if (dynamicTr) {
++ const QByteArray dynname = QByteArray(PROP_GENERIC_PREFIX + name);
++ o->setProperty(dynname.constData(), QVariant::fromValue(strVal));
++ anyTrs = trEnabled;
++ }
++ if (p->elementString()->text() != text)
++ o->setProperty(name.constData(), text);
++ }
++ if (anyTrs)
++ o->installEventFilter(m_trwatch);
++ }
++
++ QWidget *FormBuilderPrivate::create(DomUI *ui, QWidget *parentWidget)
++ {
++ m_class = ui->elementClass().toUtf8();
++ m_trwatch = nullptr;
++ m_idBased = ui->attributeIdbasedtr();
++ setTextBuilder(new TranslatingTextBuilder(m_idBased, trEnabled, m_class));
++ return QFormBuilder::create(ui, parentWidget);
++ }
++
++ QWidget *FormBuilderPrivate::create(DomWidget *ui_widget, QWidget *parentWidget)
++ {
++ QWidget *w = QFormBuilder::create(ui_widget, parentWidget);
++ if (w == nullptr)
++ return nullptr;
++
++ if (0) {
++ #if QT_CONFIG(tabwidget)
++ } else if (qobject_cast<QTabWidget*>(w)) {
++ #endif
++ #if QT_CONFIG(listwidget)
++ } else if (qobject_cast<QListWidget*>(w)) {
++ #endif
++ #if QT_CONFIG(treewidget)
++ } else if (qobject_cast<QTreeWidget*>(w)) {
++ #endif
++ #if QT_CONFIG(tablewidget)
++ } else if (qobject_cast<QTableWidget*>(w)) {
++ #endif
++ #if QT_CONFIG(combobox)
++ } else if (qobject_cast<QComboBox*>(w)) {
++ if (QFormBuilderExtra::isQFontComboBox(w))
++ return w;
++ #endif
++ #if QT_CONFIG(toolbox)
++ } else if (qobject_cast<QToolBox*>(w)) {
++ #endif
++ } else {
++ return w;
++ }
++ if (dynamicTr && trEnabled)
++ w->installEventFilter(m_trwatch);
++ return w;
++ }
++
++ bool FormBuilderPrivate::addItem(DomWidget *ui_widget, QWidget *widget, QWidget *parentWidget)
++ {
++ if (parentWidget == nullptr)
++ return true;
++
++ if (!ParentClass::addItem(ui_widget, widget, parentWidget))
++ return false;
++
++ // Check special cases. First: Custom container
++ const QString className = QLatin1String(parentWidget->metaObject()->className());
++ if (!d->customWidgetAddPageMethod(className).isEmpty())
++ return true;
++
++ if (0) {
++ #if QT_CONFIG(tabwidget)
++ } else if (auto *tabWidget = qobject_cast<QTabWidget*>(parentWidget)) {
++ const int i = tabWidget->count() - 1;
++ translateSubwidgetProperty(ui_widget->elementAttribute(), tabWidget,
++ QFormBuilderStrings::titleAttribute,
++ i, &QTabWidget::setTabText, PROP_TABPAGETEXT);
++ #if QT_CONFIG(tooltip)
++ translateSubwidgetProperty(ui_widget->elementAttribute(), tabWidget,
++ QFormBuilderStrings::toolTipAttribute,
++ i, &QTabWidget::setTabToolTip, PROP_TABPAGETOOLTIP);
++ # endif
++ #if QT_CONFIG(whatsthis)
++ translateSubwidgetProperty(ui_widget->elementAttribute(), tabWidget,
++ QFormBuilderStrings::whatsThisAttribute,
++ i, &QTabWidget::setTabWhatsThis, PROP_TABPAGEWHATSTHIS);
++ # endif
++ #endif
++ #if QT_CONFIG(toolbox)
++ } else if (auto *toolBox = qobject_cast<QToolBox*>(parentWidget)) {
++ const int i = toolBox->count() - 1;
++ translateSubwidgetProperty(ui_widget->elementAttribute(), toolBox,
++ QFormBuilderStrings::labelAttribute,
++ i, &QToolBox::setItemText, PROP_TOOLITEMTEXT);
++ #if QT_CONFIG(tooltip)
++ translateSubwidgetProperty(ui_widget->elementAttribute(), toolBox,
++ QFormBuilderStrings::toolTipAttribute,
++ i, &QToolBox::setItemToolTip, PROP_TOOLITEMTOOLTIP);
++ # endif
++ #endif
++ }
++
++ return true;
++ }
++
++ #ifdef QFORMINTERNAL_NAMESPACE
++ }
++ #endif
++
++ class QUiLoaderPrivate
++ {
++ public:
++ #ifdef QFORMINTERNAL_NAMESPACE
++ QFormInternal::FormBuilderPrivate builder;
++ #else
++ FormBuilderPrivate builder;
++ #endif
++
++ void setupWidgetMap() const;
++ };
++
++ void QUiLoaderPrivate::setupWidgetMap() const
++ {
++ if (!g_widgets()->isEmpty())
++ return;
++
++ #ifdef QFORMINTERNAL_NAMESPACE
++ const QStringList names = QFormInternal::widgetNames();
++ #else
++ const QStringList names = widgetNames();
++ #endif
++ for (const QString &name : names)
++ g_widgets()->insert(name, true);
++ }
++
++ /*!
++ \class QUiLoader
++ \inmodule QtUiTools
++
++ \brief Loads and instantiates \QD forms at runtime.
++
++ Use QUiLoader to dynamically create QWidget-based user interfaces
++ based on the information stored in UI files (created with \QD).
++
++ The \l load() function reads the content of a UI file, instantiates
++ the widgets described in the file, and returns a pointer to the
++ top-level \l QWidget. This widget can then be shown:
++
++ \snippet quiloader/mywidget.cpp 0
++
++ If the instantiation fails, the function returns
++ a \c nullptr; use the \l errorString() function to retrieve a human-readable
++ description of the error that occurred.
++
++ \section1 Loading forms with custom widgets
++
++ If the UI file contains custom widgets implemented in a \QD plugin, loading
++ will fail by default. To work around this, you can subclass \l QUiLoader and
++ override the \l createWidget() function. If this is not possible, you can also
++ let the module load \QD plugins by adding their locations via
++ \l addPluginPath() or the \c QT_PLUGIN_PATH environment variable.
++ See the \l{Creating Custom Widgets for Qt Widgets Designer}
++ page for more details.
++
++ \section1 Loading a particular widget from a UI file
++
++ You can load a specific widget from a UI file, instead of a
++ complete UI file. Use the \l availableWidgets() function to
++ retrieve the names of the available widgets, and the
++ \l createWidget() function to instantiate a specific one. For example:
++
++ \snippet quiloader/main.cpp 0
++
++ \section1 Customizing the widget creation
++
++ The \l createAction(), \l createActionGroup(), \l createLayout(), and
++ \l createWidget() functions are used internally by the QUiLoader class whenever
++ it has to create an action, action group, layout, or widget respectively.
++ You can subclass QUiLoader and reimplement these functions to
++ customize the UI creation workflow. For example, you
++ might want to have a list of the actions created when loading a form or
++ creating a custom widget.
++
++ \section1 Example
++
++ For a complete example using the QUiLoader class, see the
++ \l{Calculator Builder}.
++
++ \sa {Qt UI Tools}, QFormBuilder
++ */
++
++ /*!
++ Creates a form loader with the given \a parent.
++ */
++ QUiLoader::QUiLoader(QObject *parent)
++ : QObject(parent), d_ptr(new QUiLoaderPrivate)
++ {
++ Q_D(QUiLoader);
++
++ #ifndef QT_NO_DATASTREAM
++ static int metaTypeId = 0;
++ if (!metaTypeId) {
++ metaTypeId = qRegisterMetaType<QUiTranslatableStringValue>("QUiTranslatableStringValue");
++ }
++ #endif // QT_NO_DATASTREAM
++ d->builder.loader = this;
++
++ #if QT_CONFIG(library)
++ QStringList paths;
++ const QStringList &libraryPaths = QApplication::libraryPaths();
++ for (const QString &path : libraryPaths) {
++ QString libPath = path;
++ libPath += QDir::separator();
++ libPath += QStringLiteral("designer");
++ paths.append(libPath);
++ }
++
++ d->builder.setPluginPath(paths);
++ #endif // QT_CONFIG(library)
++ }
++
++ /*!
++ Destroys the loader.
++ */
++ QUiLoader::~QUiLoader() = default;
++
++ /*!
++ Instantiates a form from the given \a device. Returns a new QWidget with the
++ given \a parentWidget if successful. Returns \c nullptr otherwise.
++
++ \warning
++ \include security_considerations.qdocinc
++
++ \sa createWidget(), errorString()
++ */
++ QWidget *QUiLoader::load(QIODevice *device, QWidget *parentWidget)
++ {
++ Q_D(QUiLoader);
++ // QXmlStreamReader will report errors on open failure.
++ if (!device->isOpen())
++ device->open(QIODevice::ReadOnly|QIODevice::Text);
++ return d->builder.load(device, parentWidget);
++ }
++
++ /*!
++ Returns a list naming the paths in which the loader will search when
++ locating custom widget plugins.
++
++ \sa addPluginPath(), clearPluginPaths()
++ */
++ QStringList QUiLoader::pluginPaths() const
++ {
++ Q_D(const QUiLoader);
++ return d->builder.pluginPaths();
++ }
++
++ /*!
++ Clears the list of paths in which the loader will search when locating
++ plugins.
++
++ \sa addPluginPath(), pluginPaths()
++ */
++ void QUiLoader::clearPluginPaths()
++ {
++ Q_D(QUiLoader);
++ d->builder.clearPluginPaths();
++ }
++
++ /*!
++ Adds the given \a path to the list of paths in which the loader will search
++ when locating plugins.
++
++ \warning Only set paths that you trust. Allowing untrusted users to create
++ or add content in a specified path may lead to security vulnerabilities.
++
++ \sa pluginPaths(), clearPluginPaths()
++ */
++ void QUiLoader::addPluginPath(const QString &path)
++ {
++ Q_D(QUiLoader);
++ d->builder.addPluginPath(path);
++ }
++
++ /*!
++ Creates a new widget with the given \a parent and \a name using the class
++ specified by \a className. You can use this function to create any of the
++ widgets returned by the availableWidgets() function.
++
++ The function is also used internally by the QUiLoader class whenever it
++ creates a widget. Therefore, you can subclass QUiLoader and reimplement this
++ function to intervene in the process of constructing a user interface or widget.
++ However, in your implementation, ensure that you call QUiLoader's version
++ first.
++
++ \sa availableWidgets(), load()
++ */
++ QWidget *QUiLoader::createWidget(const QString &className, QWidget *parent, const QString &name)
++ {
++ Q_D(QUiLoader);
++ return d->builder.defaultCreateWidget(className, parent, name);
++ }
++
++ /*!
++ Creates a new layout with the given \a parent and \a name using the class
++ specified by \a className.
++
++ The function is also used internally by the QUiLoader class whenever it
++ creates a layout. Therefore, you can subclass QUiLoader and reimplement this
++ function to intervene in the process of constructing a user interface or widget.
++ However, in your implementation, ensure that you call QUiLoader's version
++ first.
++
++ \sa createWidget(), load()
++ */
++ QLayout *QUiLoader::createLayout(const QString &className, QObject *parent, const QString &name)
++ {
++ Q_D(QUiLoader);
++ return d->builder.defaultCreateLayout(className, parent, name);
++ }
++
++ /*!
++ Creates a new action group with the given \a parent and \a name.
++
++ The function is also used internally by the QUiLoader class whenever it
++ creates an action group. Therefore, you can subclass QUiLoader and reimplement
++ this function to intervene in the process of constructing a user interface or
++ widget. However, in your implementation, ensure that you call QUiLoader's
++ version first.
++
++ \sa createAction(), createWidget(), load()
++ */
++ QActionGroup *QUiLoader::createActionGroup(QObject *parent, const QString &name)
++ {
++ Q_D(QUiLoader);
++ return d->builder.defaultCreateActionGroup(parent, name);
++ }
++
++ /*!
++ Creates a new action with the given \a parent and \a name.
++
++ The function is also used internally by the QUiLoader class whenever it
++ creates an action. Therefore, you can subclass QUiLoader and reimplement this
++ function to intervene in the process of constructing a user interface or widget.
++ However, in your implementation, ensure that you call QUiLoader's version
++ first.
++
++ \sa createActionGroup(), createWidget(), load()
++ */
++ QAction *QUiLoader::createAction(QObject *parent, const QString &name)
++ {
++ Q_D(QUiLoader);
++ return d->builder.defaultCreateAction(parent, name);
++ }
++
++ /*!
++ Returns a list of all available widgets that can be built using the
++ createWidget() function, that is, all the widgets specified within the given
++ plugin paths.
++
++ \sa pluginPaths(), createWidget()
++
++ */
++ QStringList QUiLoader::availableWidgets() const
++ {
++ Q_D(const QUiLoader);
++
++ d->setupWidgetMap();
++ widget_map available = *g_widgets();
++
++ const auto &customWidgets = d->builder.customWidgets();
++ for (QDesignerCustomWidgetInterface *plugin : customWidgets)
++ available.insert(plugin->name(), true);
++
++ return available.keys();
++ }
++
++
++ /*!
++ \since 4.5
++ Returns a list of all available layouts that can be built using the
++ createLayout() function.
++
++ \sa createLayout()
++ */
++
++ QStringList QUiLoader::availableLayouts() const
++ {
++ #ifdef QFORMINTERNAL_NAMESPACE
++ return QFormInternal::layoutNames();
++ #else
++ return layoutNames();
++ #endif
++ }
++
++ /*!
++ Sets the working directory of the loader to \a dir. The loader will look
++ for other resources, such as icons and resource files, in paths relative to
++ this directory.
++
++ \warning Only set a directory that you trust. Allowing untrusted users to
++ create or add content in the working directory may lead to security
++ vulnerabilities.
++
++ \sa workingDirectory()
++ */
++
++ void QUiLoader::setWorkingDirectory(const QDir &dir)
++ {
++ Q_D(QUiLoader);
++ d->builder.setWorkingDirectory(dir);
++ }
++
++ /*!
++ Returns the working directory of the loader.
++
++ \sa setWorkingDirectory()
++ */
++
++ QDir QUiLoader::workingDirectory() const
++ {
++ Q_D(const QUiLoader);
++ return d->builder.workingDirectory();
++ }
++ /*!
++ \since 4.5
++
++ If \a enabled is true, user interfaces loaded by this loader will
++ automatically retranslate themselves upon receiving a language change
++ event. Otherwise, the user interfaces will not be retranslated.
++
++ \sa isLanguageChangeEnabled()
++ */
++
++ void QUiLoader::setLanguageChangeEnabled(bool enabled)
++ {
++ Q_D(QUiLoader);
++ d->builder.dynamicTr = enabled;
++ }
++
++ /*!
++ \since 4.5
++
++ Returns \c true if dynamic retranslation on language change is enabled;
++ returns \c false otherwise.
++
++ The default is \c false.
++
++ \sa setLanguageChangeEnabled()
++ */
++
++ bool QUiLoader::isLanguageChangeEnabled() const
++ {
++ Q_D(const QUiLoader);
++ return d->builder.dynamicTr;
++ }
++
++ /*!
++ \internal
++ \since 4.5
++
++ If \a enabled is true, user interfaces loaded by this loader will be
++ translated. Otherwise, the user interfaces will not be translated.
++
++ \note This is orthogonal to languageChangeEnabled.
++
++ \sa isLanguageChangeEnabled(), setLanguageChangeEnabled()
++ */
++
++ void QUiLoader::setTranslationEnabled(bool enabled)
++ {
++ Q_D(QUiLoader);
++ d->builder.trEnabled = enabled;
++ }
++
++ /*!
++ \internal
++ \since 4.5
++
++ Returns \c true if translation is enabled; returns \c false otherwise.
++
++ The default is \c true.
++
++ \sa setTranslationEnabled()
++ */
++
++ bool QUiLoader::isTranslationEnabled() const
++ {
++ Q_D(const QUiLoader);
++ return d->builder.trEnabled;
++ }
++
++ /*!
++ Returns a human-readable description of the last error that occurred in load().
++
++ \since 5.0
++ \sa load()
++ */
++
++ QString QUiLoader::errorString() const
++ {
++ Q_D(const QUiLoader);
++ return d->builder.errorString();
++ }
++
++ QT_END_NAMESPACE
++
++ #include "quiloader.moc"