tools/designer/src/lib/uilib/formbuilder.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the Qt Designer of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include "customwidget.h"
       
    43 #include "formbuilder.h"
       
    44 #include "formbuilderextra_p.h"
       
    45 #include "ui4_p.h"
       
    46 
       
    47 #include <QtGui/QtGui>
       
    48 #include <QtCore/QCoreApplication>
       
    49 
       
    50 QT_BEGIN_NAMESPACE
       
    51 
       
    52 #ifdef QFORMINTERNAL_NAMESPACE
       
    53 namespace QFormInternal {
       
    54 #endif
       
    55 
       
    56 /*!
       
    57     \class QFormBuilder
       
    58 
       
    59     \brief The QFormBuilder class is used to dynamically construct
       
    60     user interfaces from UI files at run-time.
       
    61 
       
    62     \inmodule QtDesigner
       
    63 
       
    64     The QFormBuilder class provides a mechanism for dynamically
       
    65     creating user interfaces at run-time, based on UI files
       
    66     created with \QD. For example:
       
    67 
       
    68     \snippet doc/src/snippets/code/tools_designer_src_lib_uilib_formbuilder.cpp 0
       
    69 
       
    70     By including the user interface in the example's resources (\c
       
    71     myForm.qrc), we ensure that it will be present when the example is
       
    72     run:
       
    73 
       
    74     \snippet doc/src/snippets/code/tools_designer_src_lib_uilib_formbuilder.cpp 1
       
    75 
       
    76     QFormBuilder extends the QAbstractFormBuilder base class with a
       
    77     number of functions that are used to support custom widget
       
    78     plugins:
       
    79 
       
    80     \list
       
    81     \o pluginPaths() returns the list of paths that the form builder
       
    82        searches when loading custom widget plugins.
       
    83     \o addPluginPath() allows additional paths to be registered with
       
    84        the form builder.
       
    85     \o setPluginPath() is used to replace the existing list of paths
       
    86        with a list obtained from some other source.
       
    87     \o clearPluginPaths() removes all paths registered with the form
       
    88        builder.
       
    89     \o customWidgets() returns a list of interfaces to plugins that
       
    90        can be used to create new instances of registered custom widgets.
       
    91     \endlist
       
    92 
       
    93     The QFormBuilder class is typically used by custom components and
       
    94     applications that embed \QD. Standalone applications that need to
       
    95     dynamically generate user interfaces at run-time use the
       
    96     QUiLoader class, found in the QtUiTools module.
       
    97 
       
    98     \sa QAbstractFormBuilder, {QtUiTools Module}
       
    99 */
       
   100 
       
   101 /*!
       
   102     \fn QFormBuilder::QFormBuilder()
       
   103 
       
   104     Constructs a new form builder.
       
   105 */
       
   106 
       
   107 QFormBuilder::QFormBuilder() : QAbstractFormBuilder()
       
   108 {
       
   109 }
       
   110 
       
   111 /*!
       
   112     Destroys the form builder.
       
   113 */
       
   114 QFormBuilder::~QFormBuilder()
       
   115 {
       
   116 }
       
   117 
       
   118 /*!
       
   119     \internal
       
   120 */
       
   121 QWidget *QFormBuilder::create(DomWidget *ui_widget, QWidget *parentWidget)
       
   122 {
       
   123     QFormBuilderExtra *fb = QFormBuilderExtra::instance(this);
       
   124     if (!fb->parentWidgetIsSet())
       
   125         fb->setParentWidget(parentWidget);
       
   126     fb->setProcessingLayoutWidget(false);
       
   127     if (ui_widget->attributeClass() == QFormBuilderStrings::instance().qWidgetClass && !ui_widget->hasAttributeNative()
       
   128             && parentWidget
       
   129 #ifndef QT_NO_MAINWINDOW
       
   130             && !qobject_cast<QMainWindow *>(parentWidget)
       
   131 #endif
       
   132 #ifndef QT_NO_TOOLBOX
       
   133             && !qobject_cast<QToolBox *>(parentWidget)
       
   134 #endif
       
   135 #ifndef QT_NO_STACKEDWIDGET
       
   136             && !qobject_cast<QStackedWidget *>(parentWidget)
       
   137 #endif
       
   138 #ifndef QT_NO_STACKEDWIDGET
       
   139             && !qobject_cast<QTabWidget *>(parentWidget)
       
   140 #endif
       
   141 #ifndef QT_NO_SCROLLAREA
       
   142             && !qobject_cast<QScrollArea *>(parentWidget)
       
   143 #endif
       
   144 #ifndef QT_NO_MDIAREA
       
   145             && !qobject_cast<QMdiArea *>(parentWidget)
       
   146 #endif
       
   147 #ifndef QT_NO_DOCKWIDGET
       
   148             && !qobject_cast<QDockWidget *>(parentWidget)
       
   149 #endif
       
   150             )
       
   151         fb->setProcessingLayoutWidget(true);
       
   152     return QAbstractFormBuilder::create(ui_widget, parentWidget);
       
   153 }
       
   154 
       
   155 
       
   156 /*!
       
   157     \internal
       
   158 */
       
   159 QWidget *QFormBuilder::createWidget(const QString &widgetName, QWidget *parentWidget, const QString &name)
       
   160 {
       
   161     if (widgetName.isEmpty()) {
       
   162         //: Empty class name passed to widget factory method
       
   163         qWarning() << QCoreApplication::translate("QFormBuilder", "An empty class name was passed on to %1 (object name: '%2').").arg(QString::fromUtf8(Q_FUNC_INFO), name);
       
   164         return 0;
       
   165     }
       
   166 
       
   167     QWidget *w = 0;
       
   168 
       
   169 #ifndef QT_NO_TABWIDGET
       
   170     if (qobject_cast<QTabWidget*>(parentWidget))
       
   171         parentWidget = 0;
       
   172 #endif
       
   173 #ifndef QT_NO_STACKEDWIDGET
       
   174     if (qobject_cast<QStackedWidget*>(parentWidget))
       
   175         parentWidget = 0;
       
   176 #endif
       
   177 #ifndef QT_NO_TOOLBOX
       
   178     if (qobject_cast<QToolBox*>(parentWidget))
       
   179         parentWidget = 0;
       
   180 #endif
       
   181 
       
   182     // ### special-casing for Line (QFrame) -- fix for 4.2
       
   183     do {
       
   184         if (widgetName == QFormBuilderStrings::instance().lineClass) {
       
   185             w = new QFrame(parentWidget);
       
   186             static_cast<QFrame*>(w)->setFrameStyle(QFrame::HLine | QFrame::Sunken);
       
   187             break;
       
   188         }
       
   189         const QByteArray widgetNameBA = widgetName.toUtf8();
       
   190         const char *widgetNameC = widgetNameBA.constData();
       
   191         if (w) { // symmetry for macro
       
   192         }
       
   193 
       
   194 #define DECLARE_LAYOUT(L, C)
       
   195 #define DECLARE_COMPAT_WIDGET(W, C)
       
   196 #define DECLARE_WIDGET(W, C) else if (!qstrcmp(widgetNameC, #W)) { Q_ASSERT(w == 0); w = new W(parentWidget); }
       
   197 #define DECLARE_WIDGET_1(W, C) else if (!qstrcmp(widgetNameC, #W)) { Q_ASSERT(w == 0); w = new W(0, parentWidget); }
       
   198 
       
   199 #include "widgets.table"
       
   200 
       
   201 #undef DECLARE_COMPAT_WIDGET
       
   202 #undef DECLARE_LAYOUT
       
   203 #undef DECLARE_WIDGET
       
   204 #undef DECLARE_WIDGET_1
       
   205 
       
   206         if (w)
       
   207             break;
       
   208 
       
   209         // try with a registered custom widget
       
   210         QDesignerCustomWidgetInterface *factory = m_customWidgets.value(widgetName);
       
   211         if (factory != 0)
       
   212             w = factory->createWidget(parentWidget);
       
   213     } while(false);
       
   214 
       
   215     QFormBuilderExtra *fb = QFormBuilderExtra::instance(this);
       
   216     if (w == 0) { // Attempt to instantiate base class of promoted/custom widgets
       
   217         const QString baseClassName = fb->customWidgetBaseClass(widgetName);
       
   218         if (!baseClassName.isEmpty()) {
       
   219             qWarning() << QCoreApplication::translate("QFormBuilder", "QFormBuilder was unable to create a custom widget of the class '%1'; defaulting to base class '%2'.").arg(widgetName, baseClassName);
       
   220             return createWidget(baseClassName, parentWidget, name);
       
   221         }
       
   222     }
       
   223 
       
   224     if (w == 0) { // nothing to do
       
   225         qWarning() << QCoreApplication::translate("QFormBuilder", "QFormBuilder was unable to create a widget of the class '%1'.").arg(widgetName);
       
   226         return 0;
       
   227     }
       
   228 
       
   229     w->setObjectName(name);
       
   230 
       
   231     if (qobject_cast<QDialog *>(w))
       
   232         w->setParent(parentWidget);
       
   233 
       
   234     return w;
       
   235 }
       
   236 
       
   237 /*!
       
   238     \internal
       
   239 */
       
   240 QLayout *QFormBuilder::createLayout(const QString &layoutName, QObject *parent, const QString &name)
       
   241 {
       
   242     QLayout *l = 0;
       
   243 
       
   244     QWidget *parentWidget = qobject_cast<QWidget*>(parent);
       
   245     QLayout *parentLayout = qobject_cast<QLayout*>(parent);
       
   246 
       
   247     Q_ASSERT(parentWidget || parentLayout);
       
   248 
       
   249 #define DECLARE_WIDGET(W, C)
       
   250 #define DECLARE_COMPAT_WIDGET(W, C)
       
   251 
       
   252 #define DECLARE_LAYOUT(L, C) \
       
   253     if (layoutName == QLatin1String(#L)) { \
       
   254         Q_ASSERT(l == 0); \
       
   255         l = parentLayout \
       
   256             ? new L() \
       
   257             : new L(parentWidget); \
       
   258     }
       
   259 
       
   260 #include "widgets.table"
       
   261 
       
   262 #undef DECLARE_LAYOUT
       
   263 #undef DECLARE_COMPAT_WIDGET
       
   264 #undef DECLARE_WIDGET
       
   265 
       
   266     if (l) {
       
   267         l->setObjectName(name);
       
   268         if (parentLayout) {
       
   269             QWidget *w = qobject_cast<QWidget *>(parentLayout->parent());
       
   270             if (w && w->inherits("Q3GroupBox")) {
       
   271                 l->setContentsMargins(w->style()->pixelMetric(QStyle::PM_LayoutLeftMargin),
       
   272                                     w->style()->pixelMetric(QStyle::PM_LayoutTopMargin),
       
   273                                     w->style()->pixelMetric(QStyle::PM_LayoutRightMargin),
       
   274                                     w->style()->pixelMetric(QStyle::PM_LayoutBottomMargin));
       
   275                 QGridLayout *grid = qobject_cast<QGridLayout *>(l);
       
   276                 if (grid) {
       
   277                     grid->setHorizontalSpacing(-1);
       
   278                     grid->setVerticalSpacing(-1);
       
   279                 } else {
       
   280                     l->setSpacing(-1);
       
   281                 }
       
   282                 l->setAlignment(Qt::AlignTop);
       
   283             }
       
   284         }
       
   285     } else {
       
   286         qWarning() << QCoreApplication::translate("QFormBuilder", "The layout type `%1' is not supported.").arg(layoutName);
       
   287     }
       
   288 
       
   289     return l;
       
   290 }
       
   291 
       
   292 /*!
       
   293     \internal
       
   294 */
       
   295 bool QFormBuilder::addItem(DomLayoutItem *ui_item, QLayoutItem *item, QLayout *layout)
       
   296 {
       
   297     return QAbstractFormBuilder::addItem(ui_item, item, layout);
       
   298 }
       
   299 
       
   300 /*!
       
   301     \internal
       
   302 */
       
   303 bool QFormBuilder::addItem(DomWidget *ui_widget, QWidget *widget, QWidget *parentWidget)
       
   304 {
       
   305     return QAbstractFormBuilder::addItem(ui_widget, widget, parentWidget);
       
   306 }
       
   307 
       
   308 /*!
       
   309     \internal
       
   310 */
       
   311 QWidget *QFormBuilder::widgetByName(QWidget *topLevel, const QString &name)
       
   312 {
       
   313     Q_ASSERT(topLevel);
       
   314     if (topLevel->objectName() == name)
       
   315         return topLevel;
       
   316 
       
   317     return qFindChild<QWidget*>(topLevel, name);
       
   318 }
       
   319 
       
   320 static QObject *objectByName(QWidget *topLevel, const QString &name)
       
   321 {
       
   322     Q_ASSERT(topLevel);
       
   323     if (topLevel->objectName() == name)
       
   324         return topLevel;
       
   325 
       
   326     return qFindChild<QObject*>(topLevel, name);
       
   327 }
       
   328 
       
   329 /*!
       
   330     \internal
       
   331 */
       
   332 void QFormBuilder::createConnections(DomConnections *ui_connections, QWidget *widget)
       
   333 {
       
   334     typedef QList<DomConnection*> DomConnectionList;
       
   335     Q_ASSERT(widget != 0);
       
   336 
       
   337     if (ui_connections == 0)
       
   338         return;
       
   339 
       
   340     const DomConnectionList connections = ui_connections->elementConnection();
       
   341     if (!connections.empty()) {
       
   342         const DomConnectionList::const_iterator cend = connections.constEnd();
       
   343         for (DomConnectionList::const_iterator it = connections.constBegin(); it != cend; ++it) {
       
   344 
       
   345             QObject *sender = objectByName(widget, (*it)->elementSender());
       
   346             QObject *receiver = objectByName(widget, (*it)->elementReceiver());
       
   347             if (!sender || !receiver)
       
   348                 continue;
       
   349 
       
   350             QByteArray sig = (*it)->elementSignal().toUtf8();
       
   351             sig.prepend("2");
       
   352             QByteArray sl = (*it)->elementSlot().toUtf8();
       
   353             sl.prepend("1");
       
   354             QObject::connect(sender, sig, receiver, sl);
       
   355         }
       
   356     }
       
   357 }
       
   358 
       
   359 /*!
       
   360     \internal
       
   361 */
       
   362 QWidget *QFormBuilder::create(DomUI *ui, QWidget *parentWidget)
       
   363 {
       
   364     return QAbstractFormBuilder::create(ui, parentWidget);
       
   365 }
       
   366 
       
   367 /*!
       
   368     \internal
       
   369 */
       
   370 QLayout *QFormBuilder::create(DomLayout *ui_layout, QLayout *layout, QWidget *parentWidget)
       
   371 {
       
   372     QFormBuilderExtra *fb = QFormBuilderExtra::instance(this);
       
   373     // Is this a temporary layout widget used to represent QLayout hierarchies in Designer?
       
   374     // Set its margins to 0.
       
   375     bool layoutWidget = fb->processingLayoutWidget();
       
   376     QLayout *l = QAbstractFormBuilder::create(ui_layout, layout, parentWidget);
       
   377     if (layoutWidget) {
       
   378         const QFormBuilderStrings &strings = QFormBuilderStrings::instance();
       
   379         int left, top, right, bottom;
       
   380         left = top = right = bottom = 0;
       
   381         const DomPropertyHash properties = propertyMap(ui_layout->elementProperty());
       
   382 
       
   383         if (DomProperty *prop = properties.value(strings.leftMarginProperty))
       
   384             left = prop->elementNumber();
       
   385 
       
   386         if (DomProperty *prop = properties.value(strings.topMarginProperty))
       
   387             top = prop->elementNumber();
       
   388 
       
   389         if (DomProperty *prop = properties.value(strings.rightMarginProperty))
       
   390             right = prop->elementNumber();
       
   391 
       
   392         if (DomProperty *prop = properties.value(strings.bottomMarginProperty))
       
   393             bottom = prop->elementNumber();
       
   394 
       
   395         l->setContentsMargins(left, top, right, bottom);
       
   396         fb->setProcessingLayoutWidget(false);
       
   397     }
       
   398     return l;
       
   399 }
       
   400 
       
   401 /*!
       
   402     \internal
       
   403 */
       
   404 QLayoutItem *QFormBuilder::create(DomLayoutItem *ui_layoutItem, QLayout *layout, QWidget *parentWidget)
       
   405 {
       
   406     return QAbstractFormBuilder::create(ui_layoutItem, layout, parentWidget);
       
   407 }
       
   408 
       
   409 /*!
       
   410     \internal
       
   411 */
       
   412 QAction *QFormBuilder::create(DomAction *ui_action, QObject *parent)
       
   413 {
       
   414     return QAbstractFormBuilder::create(ui_action, parent);
       
   415 }
       
   416 
       
   417 /*!
       
   418     \internal
       
   419 */
       
   420 QActionGroup *QFormBuilder::create(DomActionGroup *ui_action_group, QObject *parent)
       
   421 {
       
   422     return QAbstractFormBuilder::create(ui_action_group, parent);
       
   423 }
       
   424 
       
   425 /*!
       
   426     Returns the list of paths the form builder searches for plugins.
       
   427 
       
   428     \sa addPluginPath()
       
   429 */
       
   430 QStringList QFormBuilder::pluginPaths() const
       
   431 {
       
   432     return m_pluginPaths;
       
   433 }
       
   434 
       
   435 /*!
       
   436     Clears the list of paths that the form builder uses to search for
       
   437     custom widget plugins.
       
   438 
       
   439     \sa pluginPaths()
       
   440 */
       
   441 void QFormBuilder::clearPluginPaths()
       
   442 {
       
   443     m_pluginPaths.clear();
       
   444     updateCustomWidgets();
       
   445 }
       
   446 
       
   447 /*!
       
   448     Adds a new plugin path specified by \a pluginPath to the list of
       
   449     paths that will be searched by the form builder when loading a
       
   450     custom widget plugin.
       
   451 
       
   452     \sa setPluginPath(), clearPluginPaths()
       
   453 */
       
   454 void QFormBuilder::addPluginPath(const QString &pluginPath)
       
   455 {
       
   456     m_pluginPaths.append(pluginPath);
       
   457     updateCustomWidgets();
       
   458 }
       
   459 
       
   460 /*!
       
   461     Sets the list of plugin paths to the list specified by \a pluginPaths.
       
   462 
       
   463     \sa addPluginPath()
       
   464 */
       
   465 void QFormBuilder::setPluginPath(const QStringList &pluginPaths)
       
   466 {
       
   467     m_pluginPaths = pluginPaths;
       
   468     updateCustomWidgets();
       
   469 }
       
   470 
       
   471 static void insertPlugins(QObject *o, QMap<QString, QDesignerCustomWidgetInterface*> *customWidgets)
       
   472 {
       
   473     // step 1) try with a normal plugin
       
   474     if (QDesignerCustomWidgetInterface *iface = qobject_cast<QDesignerCustomWidgetInterface *>(o)) {
       
   475         customWidgets->insert(iface->name(), iface);
       
   476         return;
       
   477     }
       
   478     // step 2) try with a collection of plugins
       
   479     if (QDesignerCustomWidgetCollectionInterface *c = qobject_cast<QDesignerCustomWidgetCollectionInterface *>(o)) {
       
   480         foreach (QDesignerCustomWidgetInterface *iface, c->customWidgets())
       
   481             customWidgets->insert(iface->name(), iface);
       
   482     }
       
   483 }
       
   484 
       
   485 /*!
       
   486     \internal
       
   487 */
       
   488 void QFormBuilder::updateCustomWidgets()
       
   489 {
       
   490     m_customWidgets.clear();
       
   491 
       
   492     foreach (QString path, m_pluginPaths) {
       
   493         const QDir dir(path);
       
   494         const QStringList candidates = dir.entryList(QDir::Files);
       
   495 
       
   496         foreach (const QString &plugin, candidates) {
       
   497             if (!QLibrary::isLibrary(plugin))
       
   498                 continue;
       
   499 
       
   500             QString loaderPath = path;
       
   501             loaderPath += QLatin1Char('/');
       
   502             loaderPath += plugin;
       
   503 
       
   504             QPluginLoader loader(loaderPath);
       
   505             if (loader.load())
       
   506                 insertPlugins(loader.instance(), &m_customWidgets);
       
   507         }
       
   508     }
       
   509     // Check statically linked plugins
       
   510     const QObjectList staticPlugins = QPluginLoader::staticInstances();
       
   511     if (!staticPlugins.empty())
       
   512         foreach (QObject *o, staticPlugins)
       
   513             insertPlugins(o, &m_customWidgets);
       
   514 }
       
   515 
       
   516 /*!
       
   517     \fn QList<QDesignerCustomWidgetInterface*> QFormBuilder::customWidgets() const
       
   518 
       
   519     Returns a list of the available plugins.
       
   520 */
       
   521 QList<QDesignerCustomWidgetInterface*> QFormBuilder::customWidgets() const
       
   522 {
       
   523     return m_customWidgets.values();
       
   524 }
       
   525 
       
   526 /*!
       
   527     \internal
       
   528 */
       
   529 
       
   530 void QFormBuilder::applyProperties(QObject *o, const QList<DomProperty*> &properties)
       
   531 {
       
   532     typedef QList<DomProperty*> DomPropertyList;
       
   533 
       
   534     if (properties.empty())
       
   535         return;
       
   536 
       
   537     QFormBuilderExtra *fb = QFormBuilderExtra::instance(this);
       
   538     const QFormBuilderStrings &strings = QFormBuilderStrings::instance();
       
   539 
       
   540     const DomPropertyList::const_iterator cend = properties.constEnd();
       
   541     for (DomPropertyList::const_iterator it = properties.constBegin(); it != cend; ++it) {
       
   542         const QVariant v = toVariant(o->metaObject(), *it);
       
   543         if (v.isNull())
       
   544             continue;
       
   545 
       
   546         const QString attributeName = (*it)->attributeName();
       
   547         const bool isWidget = o->isWidgetType();
       
   548         if (isWidget && o->parent() == fb->parentWidget() && attributeName == strings.geometryProperty) {
       
   549             // apply only the size part of a geometry for the root widget
       
   550             static_cast<QWidget*>(o)->resize(qvariant_cast<QRect>(v).size());
       
   551         } else if (fb->applyPropertyInternally(o, attributeName, v)) {
       
   552         } else if (isWidget && !qstrcmp("QFrame", o->metaObject()->className ()) && attributeName == strings.orientationProperty) {
       
   553             // ### special-casing for Line (QFrame) -- try to fix me
       
   554             o->setProperty("frameShape", v); // v is of QFrame::Shape enum
       
   555         } else {
       
   556             o->setProperty(attributeName.toUtf8(), v);
       
   557         }
       
   558     }
       
   559 }
       
   560 
       
   561 #ifdef QFORMINTERNAL_NAMESPACE
       
   562 } // namespace QFormInternal
       
   563 #endif
       
   564 
       
   565 QT_END_NAMESPACE