|
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 |