diff -r 000000000000 -r 1918ee327afb tools/designer/src/lib/uilib/formbuilder.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/designer/src/lib/uilib/formbuilder.cpp Mon Jan 11 14:00:40 2010 +0000 @@ -0,0 +1,565 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "customwidget.h" +#include "formbuilder.h" +#include "formbuilderextra_p.h" +#include "ui4_p.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +#ifdef QFORMINTERNAL_NAMESPACE +namespace QFormInternal { +#endif + +/*! + \class QFormBuilder + + \brief The QFormBuilder class is used to dynamically construct + user interfaces from UI files at run-time. + + \inmodule QtDesigner + + The QFormBuilder class provides a mechanism for dynamically + creating user interfaces at run-time, based on UI files + created with \QD. For example: + + \snippet doc/src/snippets/code/tools_designer_src_lib_uilib_formbuilder.cpp 0 + + By including the user interface in the example's resources (\c + myForm.qrc), we ensure that it will be present when the example is + run: + + \snippet doc/src/snippets/code/tools_designer_src_lib_uilib_formbuilder.cpp 1 + + QFormBuilder extends the QAbstractFormBuilder base class with a + number of functions that are used to support custom widget + plugins: + + \list + \o pluginPaths() returns the list of paths that the form builder + searches when loading custom widget plugins. + \o addPluginPath() allows additional paths to be registered with + the form builder. + \o setPluginPath() is used to replace the existing list of paths + with a list obtained from some other source. + \o clearPluginPaths() removes all paths registered with the form + builder. + \o customWidgets() returns a list of interfaces to plugins that + can be used to create new instances of registered custom widgets. + \endlist + + The QFormBuilder class is typically used by custom components and + applications that embed \QD. Standalone applications that need to + dynamically generate user interfaces at run-time use the + QUiLoader class, found in the QtUiTools module. + + \sa QAbstractFormBuilder, {QtUiTools Module} +*/ + +/*! + \fn QFormBuilder::QFormBuilder() + + Constructs a new form builder. +*/ + +QFormBuilder::QFormBuilder() : QAbstractFormBuilder() +{ +} + +/*! + Destroys the form builder. +*/ +QFormBuilder::~QFormBuilder() +{ +} + +/*! + \internal +*/ +QWidget *QFormBuilder::create(DomWidget *ui_widget, QWidget *parentWidget) +{ + QFormBuilderExtra *fb = QFormBuilderExtra::instance(this); + if (!fb->parentWidgetIsSet()) + fb->setParentWidget(parentWidget); + fb->setProcessingLayoutWidget(false); + if (ui_widget->attributeClass() == QFormBuilderStrings::instance().qWidgetClass && !ui_widget->hasAttributeNative() + && parentWidget +#ifndef QT_NO_MAINWINDOW + && !qobject_cast(parentWidget) +#endif +#ifndef QT_NO_TOOLBOX + && !qobject_cast(parentWidget) +#endif +#ifndef QT_NO_STACKEDWIDGET + && !qobject_cast(parentWidget) +#endif +#ifndef QT_NO_STACKEDWIDGET + && !qobject_cast(parentWidget) +#endif +#ifndef QT_NO_SCROLLAREA + && !qobject_cast(parentWidget) +#endif +#ifndef QT_NO_MDIAREA + && !qobject_cast(parentWidget) +#endif +#ifndef QT_NO_DOCKWIDGET + && !qobject_cast(parentWidget) +#endif + ) + fb->setProcessingLayoutWidget(true); + return QAbstractFormBuilder::create(ui_widget, parentWidget); +} + + +/*! + \internal +*/ +QWidget *QFormBuilder::createWidget(const QString &widgetName, QWidget *parentWidget, const QString &name) +{ + if (widgetName.isEmpty()) { + //: Empty class name passed to widget factory method + qWarning() << QCoreApplication::translate("QFormBuilder", "An empty class name was passed on to %1 (object name: '%2').").arg(QString::fromUtf8(Q_FUNC_INFO), name); + return 0; + } + + QWidget *w = 0; + +#ifndef QT_NO_TABWIDGET + if (qobject_cast(parentWidget)) + parentWidget = 0; +#endif +#ifndef QT_NO_STACKEDWIDGET + if (qobject_cast(parentWidget)) + parentWidget = 0; +#endif +#ifndef QT_NO_TOOLBOX + if (qobject_cast(parentWidget)) + parentWidget = 0; +#endif + + // ### special-casing for Line (QFrame) -- fix for 4.2 + do { + if (widgetName == QFormBuilderStrings::instance().lineClass) { + w = new QFrame(parentWidget); + static_cast(w)->setFrameStyle(QFrame::HLine | QFrame::Sunken); + break; + } + const QByteArray widgetNameBA = widgetName.toUtf8(); + const char *widgetNameC = widgetNameBA.constData(); + if (w) { // symmetry for macro + } + +#define DECLARE_LAYOUT(L, C) +#define DECLARE_COMPAT_WIDGET(W, C) +#define DECLARE_WIDGET(W, C) else if (!qstrcmp(widgetNameC, #W)) { Q_ASSERT(w == 0); w = new W(parentWidget); } +#define DECLARE_WIDGET_1(W, C) else if (!qstrcmp(widgetNameC, #W)) { Q_ASSERT(w == 0); w = new W(0, parentWidget); } + +#include "widgets.table" + +#undef DECLARE_COMPAT_WIDGET +#undef DECLARE_LAYOUT +#undef DECLARE_WIDGET +#undef DECLARE_WIDGET_1 + + if (w) + break; + + // try with a registered custom widget + QDesignerCustomWidgetInterface *factory = m_customWidgets.value(widgetName); + if (factory != 0) + w = factory->createWidget(parentWidget); + } while(false); + + QFormBuilderExtra *fb = QFormBuilderExtra::instance(this); + if (w == 0) { // Attempt to instantiate base class of promoted/custom widgets + const QString baseClassName = fb->customWidgetBaseClass(widgetName); + if (!baseClassName.isEmpty()) { + qWarning() << QCoreApplication::translate("QFormBuilder", "QFormBuilder was unable to create a custom widget of the class '%1'; defaulting to base class '%2'.").arg(widgetName, baseClassName); + return createWidget(baseClassName, parentWidget, name); + } + } + + if (w == 0) { // nothing to do + qWarning() << QCoreApplication::translate("QFormBuilder", "QFormBuilder was unable to create a widget of the class '%1'.").arg(widgetName); + return 0; + } + + w->setObjectName(name); + + if (qobject_cast(w)) + w->setParent(parentWidget); + + return w; +} + +/*! + \internal +*/ +QLayout *QFormBuilder::createLayout(const QString &layoutName, QObject *parent, const QString &name) +{ + QLayout *l = 0; + + QWidget *parentWidget = qobject_cast(parent); + QLayout *parentLayout = qobject_cast(parent); + + Q_ASSERT(parentWidget || parentLayout); + +#define DECLARE_WIDGET(W, C) +#define DECLARE_COMPAT_WIDGET(W, C) + +#define DECLARE_LAYOUT(L, C) \ + if (layoutName == QLatin1String(#L)) { \ + Q_ASSERT(l == 0); \ + l = parentLayout \ + ? new L() \ + : new L(parentWidget); \ + } + +#include "widgets.table" + +#undef DECLARE_LAYOUT +#undef DECLARE_COMPAT_WIDGET +#undef DECLARE_WIDGET + + if (l) { + l->setObjectName(name); + if (parentLayout) { + QWidget *w = qobject_cast(parentLayout->parent()); + if (w && w->inherits("Q3GroupBox")) { + l->setContentsMargins(w->style()->pixelMetric(QStyle::PM_LayoutLeftMargin), + w->style()->pixelMetric(QStyle::PM_LayoutTopMargin), + w->style()->pixelMetric(QStyle::PM_LayoutRightMargin), + w->style()->pixelMetric(QStyle::PM_LayoutBottomMargin)); + QGridLayout *grid = qobject_cast(l); + if (grid) { + grid->setHorizontalSpacing(-1); + grid->setVerticalSpacing(-1); + } else { + l->setSpacing(-1); + } + l->setAlignment(Qt::AlignTop); + } + } + } else { + qWarning() << QCoreApplication::translate("QFormBuilder", "The layout type `%1' is not supported.").arg(layoutName); + } + + return l; +} + +/*! + \internal +*/ +bool QFormBuilder::addItem(DomLayoutItem *ui_item, QLayoutItem *item, QLayout *layout) +{ + return QAbstractFormBuilder::addItem(ui_item, item, layout); +} + +/*! + \internal +*/ +bool QFormBuilder::addItem(DomWidget *ui_widget, QWidget *widget, QWidget *parentWidget) +{ + return QAbstractFormBuilder::addItem(ui_widget, widget, parentWidget); +} + +/*! + \internal +*/ +QWidget *QFormBuilder::widgetByName(QWidget *topLevel, const QString &name) +{ + Q_ASSERT(topLevel); + if (topLevel->objectName() == name) + return topLevel; + + return qFindChild(topLevel, name); +} + +static QObject *objectByName(QWidget *topLevel, const QString &name) +{ + Q_ASSERT(topLevel); + if (topLevel->objectName() == name) + return topLevel; + + return qFindChild(topLevel, name); +} + +/*! + \internal +*/ +void QFormBuilder::createConnections(DomConnections *ui_connections, QWidget *widget) +{ + typedef QList DomConnectionList; + Q_ASSERT(widget != 0); + + if (ui_connections == 0) + return; + + const DomConnectionList connections = ui_connections->elementConnection(); + if (!connections.empty()) { + const DomConnectionList::const_iterator cend = connections.constEnd(); + for (DomConnectionList::const_iterator it = connections.constBegin(); it != cend; ++it) { + + QObject *sender = objectByName(widget, (*it)->elementSender()); + QObject *receiver = objectByName(widget, (*it)->elementReceiver()); + if (!sender || !receiver) + continue; + + QByteArray sig = (*it)->elementSignal().toUtf8(); + sig.prepend("2"); + QByteArray sl = (*it)->elementSlot().toUtf8(); + sl.prepend("1"); + QObject::connect(sender, sig, receiver, sl); + } + } +} + +/*! + \internal +*/ +QWidget *QFormBuilder::create(DomUI *ui, QWidget *parentWidget) +{ + return QAbstractFormBuilder::create(ui, parentWidget); +} + +/*! + \internal +*/ +QLayout *QFormBuilder::create(DomLayout *ui_layout, QLayout *layout, QWidget *parentWidget) +{ + QFormBuilderExtra *fb = QFormBuilderExtra::instance(this); + // Is this a temporary layout widget used to represent QLayout hierarchies in Designer? + // Set its margins to 0. + bool layoutWidget = fb->processingLayoutWidget(); + QLayout *l = QAbstractFormBuilder::create(ui_layout, layout, parentWidget); + if (layoutWidget) { + const QFormBuilderStrings &strings = QFormBuilderStrings::instance(); + int left, top, right, bottom; + left = top = right = bottom = 0; + const DomPropertyHash properties = propertyMap(ui_layout->elementProperty()); + + if (DomProperty *prop = properties.value(strings.leftMarginProperty)) + left = prop->elementNumber(); + + if (DomProperty *prop = properties.value(strings.topMarginProperty)) + top = prop->elementNumber(); + + if (DomProperty *prop = properties.value(strings.rightMarginProperty)) + right = prop->elementNumber(); + + if (DomProperty *prop = properties.value(strings.bottomMarginProperty)) + bottom = prop->elementNumber(); + + l->setContentsMargins(left, top, right, bottom); + fb->setProcessingLayoutWidget(false); + } + return l; +} + +/*! + \internal +*/ +QLayoutItem *QFormBuilder::create(DomLayoutItem *ui_layoutItem, QLayout *layout, QWidget *parentWidget) +{ + return QAbstractFormBuilder::create(ui_layoutItem, layout, parentWidget); +} + +/*! + \internal +*/ +QAction *QFormBuilder::create(DomAction *ui_action, QObject *parent) +{ + return QAbstractFormBuilder::create(ui_action, parent); +} + +/*! + \internal +*/ +QActionGroup *QFormBuilder::create(DomActionGroup *ui_action_group, QObject *parent) +{ + return QAbstractFormBuilder::create(ui_action_group, parent); +} + +/*! + Returns the list of paths the form builder searches for plugins. + + \sa addPluginPath() +*/ +QStringList QFormBuilder::pluginPaths() const +{ + return m_pluginPaths; +} + +/*! + Clears the list of paths that the form builder uses to search for + custom widget plugins. + + \sa pluginPaths() +*/ +void QFormBuilder::clearPluginPaths() +{ + m_pluginPaths.clear(); + updateCustomWidgets(); +} + +/*! + Adds a new plugin path specified by \a pluginPath to the list of + paths that will be searched by the form builder when loading a + custom widget plugin. + + \sa setPluginPath(), clearPluginPaths() +*/ +void QFormBuilder::addPluginPath(const QString &pluginPath) +{ + m_pluginPaths.append(pluginPath); + updateCustomWidgets(); +} + +/*! + Sets the list of plugin paths to the list specified by \a pluginPaths. + + \sa addPluginPath() +*/ +void QFormBuilder::setPluginPath(const QStringList &pluginPaths) +{ + m_pluginPaths = pluginPaths; + updateCustomWidgets(); +} + +static void insertPlugins(QObject *o, QMap *customWidgets) +{ + // step 1) try with a normal plugin + if (QDesignerCustomWidgetInterface *iface = qobject_cast(o)) { + customWidgets->insert(iface->name(), iface); + return; + } + // step 2) try with a collection of plugins + if (QDesignerCustomWidgetCollectionInterface *c = qobject_cast(o)) { + foreach (QDesignerCustomWidgetInterface *iface, c->customWidgets()) + customWidgets->insert(iface->name(), iface); + } +} + +/*! + \internal +*/ +void QFormBuilder::updateCustomWidgets() +{ + m_customWidgets.clear(); + + foreach (QString path, m_pluginPaths) { + const QDir dir(path); + const QStringList candidates = dir.entryList(QDir::Files); + + foreach (const QString &plugin, candidates) { + if (!QLibrary::isLibrary(plugin)) + continue; + + QString loaderPath = path; + loaderPath += QLatin1Char('/'); + loaderPath += plugin; + + QPluginLoader loader(loaderPath); + if (loader.load()) + insertPlugins(loader.instance(), &m_customWidgets); + } + } + // Check statically linked plugins + const QObjectList staticPlugins = QPluginLoader::staticInstances(); + if (!staticPlugins.empty()) + foreach (QObject *o, staticPlugins) + insertPlugins(o, &m_customWidgets); +} + +/*! + \fn QList QFormBuilder::customWidgets() const + + Returns a list of the available plugins. +*/ +QList QFormBuilder::customWidgets() const +{ + return m_customWidgets.values(); +} + +/*! + \internal +*/ + +void QFormBuilder::applyProperties(QObject *o, const QList &properties) +{ + typedef QList DomPropertyList; + + if (properties.empty()) + return; + + QFormBuilderExtra *fb = QFormBuilderExtra::instance(this); + const QFormBuilderStrings &strings = QFormBuilderStrings::instance(); + + const DomPropertyList::const_iterator cend = properties.constEnd(); + for (DomPropertyList::const_iterator it = properties.constBegin(); it != cend; ++it) { + const QVariant v = toVariant(o->metaObject(), *it); + if (v.isNull()) + continue; + + const QString attributeName = (*it)->attributeName(); + const bool isWidget = o->isWidgetType(); + if (isWidget && o->parent() == fb->parentWidget() && attributeName == strings.geometryProperty) { + // apply only the size part of a geometry for the root widget + static_cast(o)->resize(qvariant_cast(v).size()); + } else if (fb->applyPropertyInternally(o, attributeName, v)) { + } else if (isWidget && !qstrcmp("QFrame", o->metaObject()->className ()) && attributeName == strings.orientationProperty) { + // ### special-casing for Line (QFrame) -- try to fix me + o->setProperty("frameShape", v); // v is of QFrame::Shape enum + } else { + o->setProperty(attributeName.toUtf8(), v); + } + } +} + +#ifdef QFORMINTERNAL_NAMESPACE +} // namespace QFormInternal +#endif + +QT_END_NAMESPACE