1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the Qt Designer of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file. Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "widgetdatabase_p.h"
43 #include "widgetfactory_p.h"
44 #include "spacer_widget_p.h"
45 #include "abstractlanguage.h"
46 #include "pluginmanager_p.h"
47 #include "qdesigner_widgetbox_p.h"
48 #include "qdesigner_utils_p.h"
49 #include <ui4_p.h>
50
51 #include <QtDesigner/customwidget.h>
52 #include <QtDesigner/propertysheet.h>
53 #include <QtDesigner/QExtensionManager>
54 #include <QtDesigner/QDesignerFormEditorInterface>
55
56 #include <QtXml/QXmlStreamWriter>
57
58 #include <QtCore/QScopedPointer>
59 #include <QtCore/qdebug.h>
60 #include <QtCore/QMetaProperty>
61 #include <QtCore/QTextStream>
62 #include <QtCore/QRegExp>
63 #include <QtCore/QCoreApplication>
64
65 QT_BEGIN_NAMESPACE
66
67 namespace {
68 enum { debugWidgetDataBase = 0 };
69 }
70
71 namespace qdesigner_internal {
72
73 // ----------------------------------------------------------
WidgetDataBaseItem(const QString & name,const QString & group)74 WidgetDataBaseItem::WidgetDataBaseItem(const QString &name, const QString &group)
75 : m_name(name),
76 m_group(group),
77 m_compat(0),
78 m_container(0),
79 m_form(0),
80 m_custom(0),
81 m_promoted(0)
82 {
83 }
84
name() const85 QString WidgetDataBaseItem::name() const
86 {
87 return m_name;
88 }
89
setName(const QString & name)90 void WidgetDataBaseItem::setName(const QString &name)
91 {
92 m_name = name;
93 }
94
group() const95 QString WidgetDataBaseItem::group() const
96 {
97 return m_group;
98 }
99
setGroup(const QString & group)100 void WidgetDataBaseItem::setGroup(const QString &group)
101 {
102 m_group = group;
103 }
104
toolTip() const105 QString WidgetDataBaseItem::toolTip() const
106 {
107 return m_toolTip;
108 }
109
setToolTip(const QString & toolTip)110 void WidgetDataBaseItem::setToolTip(const QString &toolTip)
111 {
112 m_toolTip = toolTip;
113 }
114
whatsThis() const115 QString WidgetDataBaseItem::whatsThis() const
116 {
117 return m_whatsThis;
118 }
119
setWhatsThis(const QString & whatsThis)120 void WidgetDataBaseItem::setWhatsThis(const QString &whatsThis)
121 {
122 m_whatsThis = whatsThis;
123 }
124
includeFile() const125 QString WidgetDataBaseItem::includeFile() const
126 {
127 return m_includeFile;
128 }
129
setIncludeFile(const QString & includeFile)130 void WidgetDataBaseItem::setIncludeFile(const QString &includeFile)
131 {
132 m_includeFile = includeFile;
133 }
134
icon() const135 QIcon WidgetDataBaseItem::icon() const
136 {
137 return m_icon;
138 }
139
setIcon(const QIcon & icon)140 void WidgetDataBaseItem::setIcon(const QIcon &icon)
141 {
142 m_icon = icon;
143 }
144
isCompat() const145 bool WidgetDataBaseItem::isCompat() const
146 {
147 return m_compat;
148 }
149
setCompat(bool b)150 void WidgetDataBaseItem::setCompat(bool b)
151 {
152 m_compat = b;
153 }
154
isContainer() const155 bool WidgetDataBaseItem::isContainer() const
156 {
157 return m_container;
158 }
159
setContainer(bool b)160 void WidgetDataBaseItem::setContainer(bool b)
161 {
162 m_container = b;
163 }
164
isCustom() const165 bool WidgetDataBaseItem::isCustom() const
166 {
167 return m_custom;
168 }
169
setCustom(bool b)170 void WidgetDataBaseItem::setCustom(bool b)
171 {
172 m_custom = b;
173 }
174
pluginPath() const175 QString WidgetDataBaseItem::pluginPath() const
176 {
177 return m_pluginPath;
178 }
179
setPluginPath(const QString & path)180 void WidgetDataBaseItem::setPluginPath(const QString &path)
181 {
182 m_pluginPath = path;
183 }
184
isPromoted() const185 bool WidgetDataBaseItem::isPromoted() const
186 {
187 return m_promoted;
188 }
189
setPromoted(bool b)190 void WidgetDataBaseItem::setPromoted(bool b)
191 {
192 m_promoted = b;
193 }
194
extends() const195 QString WidgetDataBaseItem::extends() const
196 {
197 return m_extends;
198 }
199
setExtends(const QString & s)200 void WidgetDataBaseItem::setExtends(const QString &s)
201 {
202 m_extends = s;
203 }
204
setDefaultPropertyValues(const QList<QVariant> & list)205 void WidgetDataBaseItem::setDefaultPropertyValues(const QList<QVariant> &list)
206 {
207 m_defaultPropertyValues = list;
208 }
209
defaultPropertyValues() const210 QList<QVariant> WidgetDataBaseItem::defaultPropertyValues() const
211 {
212 return m_defaultPropertyValues;
213 }
214
fakeSlots() const215 QStringList WidgetDataBaseItem::fakeSlots() const
216 {
217 return m_fakeSlots;
218 }
219
setFakeSlots(const QStringList & fs)220 void WidgetDataBaseItem::setFakeSlots(const QStringList &fs)
221 {
222 m_fakeSlots = fs;
223 }
224
fakeSignals() const225 QStringList WidgetDataBaseItem::fakeSignals() const
226 {
227 return m_fakeSignals;
228 }
229
setFakeSignals(const QStringList & fs)230 void WidgetDataBaseItem::setFakeSignals(const QStringList &fs)
231 {
232 m_fakeSignals = fs;
233 }
234
addPageMethod() const235 QString WidgetDataBaseItem::addPageMethod() const
236 {
237 return m_addPageMethod;
238 }
239
setAddPageMethod(const QString & m)240 void WidgetDataBaseItem::setAddPageMethod(const QString &m)
241 {
242 m_addPageMethod = m;
243 }
244
clone(const QDesignerWidgetDataBaseItemInterface * item)245 WidgetDataBaseItem *WidgetDataBaseItem::clone(const QDesignerWidgetDataBaseItemInterface *item)
246 {
247 WidgetDataBaseItem *rc = new WidgetDataBaseItem(item->name(), item->group());
248
249 rc->setToolTip(item->toolTip());
250 rc->setWhatsThis(item->whatsThis());
251 rc->setIncludeFile(item->includeFile());
252 rc->setIcon(item->icon());
253 rc->setCompat(item->isCompat());
254 rc->setContainer(item->isContainer());
255 rc->setCustom(item->isCustom() );
256 rc->setPluginPath(item->pluginPath());
257 rc->setPromoted(item->isPromoted());
258 rc->setExtends(item->extends());
259 rc->setDefaultPropertyValues(item->defaultPropertyValues());
260 // container page method, fake slots and signals ignored here.y
261 return rc;
262 }
263
264 // ----------------------------------------------------------
WidgetDataBase(QDesignerFormEditorInterface * core,QObject * parent)265 WidgetDataBase::WidgetDataBase(QDesignerFormEditorInterface *core, QObject *parent)
266 : QDesignerWidgetDataBaseInterface(parent),
267 m_core(core)
268 {
269 #define DECLARE_LAYOUT(L, C)
270 #define DECLARE_COMPAT_WIDGET(W, C) DECLARE_WIDGET(W, C)
271 #define DECLARE_WIDGET(W, C) append(new WidgetDataBaseItem(QString::fromUtf8(#W)));
272
273 #include "widgets.table"
274
275 #undef DECLARE_COMPAT_WIDGET
276 #undef DECLARE_LAYOUT
277 #undef DECLARE_WIDGET
278 #undef DECLARE_WIDGET_1
279
280 append(new WidgetDataBaseItem(QString::fromUtf8("Line")));
281 append(new WidgetDataBaseItem(QString::fromUtf8("Spacer")));
282 append(new WidgetDataBaseItem(QString::fromUtf8("QSplitter")));
283 append(new WidgetDataBaseItem(QString::fromUtf8("QLayoutWidget")));
284 // QDesignerWidget is used as central widget and as container for tab widgets, etc.
285 WidgetDataBaseItem *designerWidgetItem = new WidgetDataBaseItem(QString::fromUtf8("QDesignerWidget"));
286 designerWidgetItem->setContainer(true);
287 append(designerWidgetItem);
288 append(new WidgetDataBaseItem(QString::fromUtf8("QDesignerDialog")));
289 append(new WidgetDataBaseItem(QString::fromUtf8("QDesignerMenu")));
290 append(new WidgetDataBaseItem(QString::fromUtf8("QDesignerMenuBar")));
291 append(new WidgetDataBaseItem(QString::fromUtf8("QDesignerDockWidget")));
292 append(new WidgetDataBaseItem(QString::fromUtf8("QDesignerQ3WidgetStack")));
293 append(new WidgetDataBaseItem(QString::fromUtf8("QAction")));
294 append(new WidgetDataBaseItem(QString::fromUtf8("QButtonGroup")));
295
296 // ### remove me
297 // ### check the casts
298
299 #if 0 // ### enable me after 4.1
300 item(indexOfClassName(QLatin1String("QToolBar")))->setContainer(true);
301 #endif
302
303 item(indexOfClassName(QLatin1String("QTabWidget")))->setContainer(true);
304 item(indexOfClassName(QLatin1String("QGroupBox")))->setContainer(true);
305 item(indexOfClassName(QLatin1String("QScrollArea")))->setContainer(true);
306 item(indexOfClassName(QLatin1String("QStackedWidget")))->setContainer(true);
307 item(indexOfClassName(QLatin1String("QToolBox")))->setContainer(true);
308 item(indexOfClassName(QLatin1String("QFrame")))->setContainer(true);
309 item(indexOfClassName(QLatin1String("QLayoutWidget")))->setContainer(true);
310 item(indexOfClassName(QLatin1String("QDesignerWidget")))->setContainer(true);
311 item(indexOfClassName(QLatin1String("QDesignerDialog")))->setContainer(true);
312 item(indexOfClassName(QLatin1String("QSplitter")))->setContainer(true);
313 item(indexOfClassName(QLatin1String("QMainWindow")))->setContainer(true);
314 item(indexOfClassName(QLatin1String("QDockWidget")))->setContainer(true);
315 item(indexOfClassName(QLatin1String("QDesignerDockWidget")))->setContainer(true);
316 item(indexOfClassName(QLatin1String("QDesignerQ3WidgetStack")))->setContainer(true);
317 item(indexOfClassName(QLatin1String("QMdiArea")))->setContainer(true);
318 item(indexOfClassName(QLatin1String("QWorkspace")))->setContainer(true);
319 item(indexOfClassName(QLatin1String("QWizard")))->setContainer(true);
320 item(indexOfClassName(QLatin1String("QWizardPage")))->setContainer(true);
321
322 item(indexOfClassName(QLatin1String("QWidget")))->setContainer(true);
323 item(indexOfClassName(QLatin1String("QDialog")))->setContainer(true);
324 }
325
~WidgetDataBase()326 WidgetDataBase::~WidgetDataBase()
327 {
328 }
329
core() const330 QDesignerFormEditorInterface *WidgetDataBase::core() const
331 {
332 return m_core;
333 }
334
indexOfObject(QObject * object,bool) const335 int WidgetDataBase::indexOfObject(QObject *object, bool /*resolveName*/) const
336 {
337 QExtensionManager *mgr = m_core->extensionManager();
338 QDesignerLanguageExtension *lang = qt_extension<QDesignerLanguageExtension*> (mgr, m_core);
339
340 QString id;
341
342 if (lang)
343 id = lang->classNameOf(object);
344
345 if (id.isEmpty())
346 id = WidgetFactory::classNameOf(m_core,object);
347
348 return QDesignerWidgetDataBaseInterface::indexOfClassName(id);
349 }
350
createCustomWidgetItem(const QDesignerCustomWidgetInterface * c,const QDesignerCustomWidgetData & data)351 static WidgetDataBaseItem *createCustomWidgetItem(const QDesignerCustomWidgetInterface *c,
352 const QDesignerCustomWidgetData &data)
353 {
354 WidgetDataBaseItem *item = new WidgetDataBaseItem(c->name(), c->group());
355 item->setContainer(c->isContainer());
356 item->setCustom(true);
357 item->setIcon(c->icon());
358 item->setIncludeFile(c->includeFile());
359 item->setToolTip(c->toolTip());
360 item->setWhatsThis(c->whatsThis());
361 item->setPluginPath(data.pluginPath());
362 item->setAddPageMethod(data.xmlAddPageMethod());
363 item->setExtends(data.xmlExtends());
364 return item;
365 }
366
loadPlugins()367 void WidgetDataBase::loadPlugins()
368 {
369 typedef QMap<QString, int> NameIndexMap;
370 typedef QList<QDesignerWidgetDataBaseItemInterface*> ItemList;
371 typedef QSet<QString> NameSet;
372 // 1) create a map of existing custom classes
373 NameIndexMap existingCustomClasses;
374 NameSet nonCustomClasses;
375 const int count = m_items.size();
376 for (int i = 0; i < count; i++) {
377 const QDesignerWidgetDataBaseItemInterface* item = m_items[i];
378 if (item->isCustom() && !item->isPromoted())
379 existingCustomClasses.insert(item->name(), i);
380 else
381 nonCustomClasses.insert(item->name());
382 }
383 // 2) create a list plugins
384 ItemList pluginList;
385 const QDesignerPluginManager *pm = m_core->pluginManager();
386 foreach(QDesignerCustomWidgetInterface* c, pm->registeredCustomWidgets())
387 pluginList += createCustomWidgetItem(c, pm->customWidgetData(c));
388
389 // 3) replace custom classes or add new ones, remove them from existingCustomClasses,
390 // leaving behind deleted items
391 unsigned replacedPlugins = 0;
392 unsigned addedPlugins = 0;
393 unsigned removedPlugins = 0;
394 if (!pluginList.empty()) {
395 ItemList::const_iterator cend = pluginList.constEnd();
396 for (ItemList::const_iterator it = pluginList.constBegin();it != cend; ++it ) {
397 QDesignerWidgetDataBaseItemInterface* pluginItem = *it;
398 const QString pluginName = pluginItem->name();
399 NameIndexMap::iterator existingIt = existingCustomClasses.find(pluginName);
400 if (existingIt == existingCustomClasses.end()) {
401 // Add new class.
402 if (nonCustomClasses.contains(pluginName)) {
403 designerWarning(tr("A custom widget plugin whose class name (%1) matches that of an existing class has been found.").arg(pluginName));
404 } else {
405 append(pluginItem);
406 addedPlugins++;
407 }
408 } else {
409 // replace existing info
410 const int existingIndex = existingIt.value();
411 delete m_items[existingIndex];
412 m_items[existingIndex] = pluginItem;
413 existingCustomClasses.erase(existingIt);
414 replacedPlugins++;
415
416 }
417 }
418 }
419 // 4) remove classes that have not been matched. The stored indexes become invalid while deleting.
420 if (!existingCustomClasses.empty()) {
421 NameIndexMap::const_iterator cend = existingCustomClasses.constEnd();
422 for (NameIndexMap::const_iterator it = existingCustomClasses.constBegin();it != cend; ++it ) {
423 const int index = indexOfClassName(it.key());
424 if (index != -1) {
425 remove(index);
426 removedPlugins++;
427 }
428 }
429 }
430 if (debugWidgetDataBase)
431 qDebug() << "WidgetDataBase::loadPlugins(): " << addedPlugins << " added, " << replacedPlugins << " replaced, " << removedPlugins << "deleted.";
432 }
433
remove(int index)434 void WidgetDataBase::remove(int index)
435 {
436 Q_ASSERT(index < m_items.size());
437 delete m_items.takeAt(index);
438 }
439
defaultPropertyValues(const QString & name)440 QList<QVariant> WidgetDataBase::defaultPropertyValues(const QString &name)
441 {
442 WidgetFactory *factory = qobject_cast<WidgetFactory *>(m_core->widgetFactory());
443 Q_ASSERT(factory);
444 // Create non-widgets, widgets in order
445 QObject* object = factory->createObject(name, 0);
446 if (!object)
447 object = factory->createWidget(name, 0);
448 if (!object) {
449 qDebug() << "** WARNING Factory failed to create " << name;
450 return QList<QVariant>();
451 }
452 // Get properties from sheet.
453 QList<QVariant> result;
454 if (const QDesignerPropertySheetExtension *sheet = qt_extension<QDesignerPropertySheetExtension*>(m_core->extensionManager(), object)) {
455 const int propertyCount = sheet->count();
456 for (int i = 0; i < propertyCount; ++i) {
457 result.append(sheet->property(i));
458 }
459 }
460 delete object;
461 return result;
462 }
463
grabDefaultPropertyValues()464 void WidgetDataBase::grabDefaultPropertyValues()
465 {
466 const int itemCount = count();
467 for (int i = 0; i < itemCount; ++i) {
468 QDesignerWidgetDataBaseItemInterface *dbItem = item(i);
469 const QList<QVariant> default_prop_values = defaultPropertyValues(dbItem->name());
470 dbItem->setDefaultPropertyValues(default_prop_values);
471 }
472 }
473
grabStandardWidgetBoxIcons()474 void WidgetDataBase::grabStandardWidgetBoxIcons()
475 {
476 // At this point, grab the default icons for the non-custom widgets from
477 // the widget box. They will show up in the object inspector.
478 if (const QDesignerWidgetBox *wb = qobject_cast<const QDesignerWidgetBox *>(m_core->widgetBox())) {
479 const QString qWidgetClass = QLatin1String("QWidget");
480 const int itemCount = count();
481 for (int i = 0; i < itemCount; ++i) {
482 QDesignerWidgetDataBaseItemInterface *dbItem = item(i);
483 if (!dbItem->isCustom() && dbItem->icon().isNull()) {
484 // Careful not to catch the layout icons when looking for
485 // QWidget
486 const QString name = dbItem->name();
487 if (name == qWidgetClass) {
488 dbItem->setIcon(wb->iconForWidget(name, QLatin1String("Containers")));
489 } else {
490 dbItem->setIcon(wb->iconForWidget(name));
491 }
492 }
493 }
494 }
495 }
496
497 // --------------------- Functions relevant generation of new forms based on widgets (apart from the standard templates)
498
499 enum { NewFormWidth = 400, NewFormHeight = 300 };
500
501 // Check if class is suitable to generate a form from
isExistingTemplate(const QString & className)502 static inline bool isExistingTemplate(const QString &className)
503 {
504 return className == QLatin1String("QWidget") || className == QLatin1String("QDialog") || className == QLatin1String("QMainWindow");
505 }
506
507 // Check if class is suitable to generate a form from
suitableForNewForm(const QString & className)508 static inline bool suitableForNewForm(const QString &className)
509 {
510 if (className.isEmpty()) // Missing custom widget information
511 return false;
512 if (className == QLatin1String("QWorkspace"))
513 return false;
514 if (className == QLatin1String("QSplitter"))
515 return false;
516 if (className.startsWith(QLatin1String("QDesigner")) || className.startsWith(QLatin1String("Q3")) || className.startsWith(QLatin1String("QLayout")))
517 return false;
518 return true;
519 }
520
521 // Return a list of widget classes from which new forms can be generated.
522 // Suitable for 'New form' wizards in integrations.
formWidgetClasses(const QDesignerFormEditorInterface * core)523 QStringList WidgetDataBase::formWidgetClasses(const QDesignerFormEditorInterface *core)
524 {
525 static QStringList rc;
526 if (rc.empty()) {
527 const QDesignerWidgetDataBaseInterface *wdb = core->widgetDataBase();
528 const int widgetCount = wdb->count();
529 for (int i = 0; i < widgetCount; i++) {
530 const QDesignerWidgetDataBaseItemInterface *item = wdb->item(i);
531 if (item->isContainer() && !item->isCustom() && !item->isPromoted()) {
532 const QString name = item->name(); // Standard Widgets: no existing templates
533 if (!isExistingTemplate(name) && suitableForNewForm(name))
534 rc += name;
535 }
536 }
537 }
538 return rc;
539 }
540
541 // Return a list of custom widget classes from which new forms can be generated.
542 // Suitable for 'New form' wizards in integrations.
customFormWidgetClasses(const QDesignerFormEditorInterface * core)543 QStringList WidgetDataBase::customFormWidgetClasses(const QDesignerFormEditorInterface *core)
544 {
545 QStringList rc;
546 const QDesignerWidgetDataBaseInterface *wdb = core->widgetDataBase();
547 const int widgetCount = wdb->count();
548 for (int i = 0; i < widgetCount; i++) { // Custom widgets: check name and base class.
549 const QDesignerWidgetDataBaseItemInterface *item = wdb->item(i);
550 if (item->isContainer() && item->isCustom() && !item->isPromoted()) {
551 if (suitableForNewForm(item->name()) && suitableForNewForm(item->extends()))
552 rc += item->name();
553 }
554 }
555 return rc;
556 }
557
558 // Get XML for a new form from the widget box. Change objectName/geometry
559 // properties to be suitable for new forms
xmlFromWidgetBox(const QDesignerFormEditorInterface * core,const QString & className,const QString & objectName)560 static QString xmlFromWidgetBox(const QDesignerFormEditorInterface *core, const QString &className, const QString &objectName)
561 {
562 typedef QList<DomProperty*> PropertyList;
563
564 QDesignerWidgetBoxInterface::Widget widget;
565 const bool found = QDesignerWidgetBox::findWidget(core->widgetBox(), className, QString(), &widget);
566 if (!found)
567 return QString();
568 QScopedPointer<DomUI> domUI(QDesignerWidgetBox::xmlToUi(className, widget.domXml(), false));
569 if (domUI.isNull())
570 return QString();
571 domUI->setAttributeVersion(QLatin1String("4.0"));
572 DomWidget *domWidget = domUI->elementWidget();
573 if (!domWidget)
574 return QString();
575 // Properties: Remove the "objectName" property in favour of the name attribute and check geometry.
576 domWidget->setAttributeName(objectName);
577 const QString geometryProperty = QLatin1String("geometry");
578 const QString objectNameProperty = QLatin1String("objectName");
579 PropertyList properties = domWidget->elementProperty();
580 for (PropertyList::iterator it = properties.begin(); it != properties.end(); ) {
581 DomProperty *property = *it;
582 if (property->attributeName() == objectNameProperty) { // remove "objectName"
583 it = properties.erase(it);
584 delete property;
585 } else {
586 if (property->attributeName() == geometryProperty) { // Make sure form is at least 400, 300
587 if (DomRect *geom = property->elementRect()) {
588 if (geom->elementWidth() < NewFormWidth)
589 geom->setElementWidth(NewFormWidth);
590 if (geom->elementHeight() < NewFormHeight)
591 geom->setElementHeight(NewFormHeight);
592 }
593 }
594 ++it;
595 }
596 }
597 // Add a window title property
598 DomString *windowTitleString = new DomString;
599 windowTitleString->setText(objectName);
600 DomProperty *windowTitleProperty = new DomProperty;
601 windowTitleProperty->setAttributeName(QLatin1String("windowTitle"));
602 windowTitleProperty->setElementString(windowTitleString);
603 properties.push_back(windowTitleProperty);
604 // ------
605 domWidget->setElementProperty(properties);
606 // Embed in in DomUI and get string. Omit the version number.
607 domUI->setElementClass(objectName);
608
609 QString rc;
610 { // Serialize domUI
611 QXmlStreamWriter writer(&rc);
612 writer.setAutoFormatting(true);
613 writer.setAutoFormattingIndent(1);
614 writer.writeStartDocument();
615 domUI->write(writer);
616 writer.writeEndDocument();
617 }
618 return rc;
619 }
620
621 // Generate default standard ui new form xml based on the class passed on as similarClassName.
generateNewFormXML(const QString & className,const QString & similarClassName,const QString & name)622 static QString generateNewFormXML(const QString &className, const QString &similarClassName, const QString &name)
623 {
624 QString rc; {
625 QTextStream str(&rc);
626 str << QLatin1String("<ui version=\"4.0\" >\n<class>") << name << QLatin1String("</class>\n")
627 << QLatin1String("<widget class=\"") << className << QLatin1String("\" name=\"") << name << QLatin1String("\" >\n")
628 << QLatin1String("<property name=\"geometry\" >\n<rect><x>0</x><y>0</y><width>")
629 << NewFormWidth << QLatin1String("</width><height>") << NewFormHeight << QLatin1String("</height></rect>\n</property>\n");
630 str << QLatin1String("<property name=\"windowTitle\" >\n<string>") << name << QLatin1String("</string>\n</property>\n");
631
632 if (similarClassName == QLatin1String("QMainWindow")) {
633 str << QLatin1String("<widget class=\"QWidget\" name=\"centralwidget\" />\n");
634 } else {
635 if (similarClassName == QLatin1String("QWizard"))
636 str << QLatin1String("<widget class=\"QWizardPage\" name=\"wizardPage1\" /><widget class=\"QWizardPage\" name=\"wizardPage2\" />\n");
637 }
638 str << QLatin1String("</widget>\n</ui>\n");
639 }
640 return rc;
641 }
642
643 // Generate a form template using a class name obtained from formWidgetClasses(), customFormWidgetClasses().
formTemplate(const QDesignerFormEditorInterface * core,const QString & className,const QString & objectName)644 QString WidgetDataBase::formTemplate(const QDesignerFormEditorInterface *core, const QString &className, const QString &objectName)
645 {
646 // How to find suitable XML for a class:
647 // 1) Look in widget box (as all the required centralwidgets, tab widget pages, etc. should be there).
648 const QString widgetBoxXml = xmlFromWidgetBox(core, className, objectName);
649 if (!widgetBoxXml.isEmpty())
650 return widgetBoxXml;
651 // 2) If that fails, only custom main windows, custom dialogs and unsupported Qt Widgets should
652 // be left over. Generate something that is similar to the default templates. Find a similar class.
653 const QDesignerWidgetDataBaseInterface *wdb = core->widgetDataBase();
654 QString similarClass = QLatin1String("QWidget");
655 const int index = wdb->indexOfClassName(className);
656 if (index != -1) {
657 const QDesignerWidgetDataBaseItemInterface *item = wdb->item(index);
658 similarClass = item->isCustom() ? item->extends() : item->name();
659 }
660 // Generate standard ui based on the class passed on as baseClassName.
661 const QString rc = generateNewFormXML(className, similarClass, objectName);
662 return rc;
663 }
664
665 // Set a fixed size on a XML template
scaleFormTemplate(const QString & xml,const QSize & size,bool fixed)666 QString WidgetDataBase::scaleFormTemplate(const QString &xml, const QSize &size, bool fixed)
667 {
668 typedef QList<DomProperty*> PropertyList;
669 DomUI *domUI = QDesignerWidgetBox::xmlToUi(QLatin1String("Form"), xml, false);
670 if (!domUI)
671 return QString();
672 DomWidget *domWidget = domUI->elementWidget();
673 if (!domWidget)
674 return QString();
675 // Properties: Find/Ensure the geometry, minimum and maximum sizes properties
676 const QString geometryPropertyName = QLatin1String("geometry");
677 const QString minimumSizePropertyName = QLatin1String("minimumSize");
678 const QString maximumSizePropertyName = QLatin1String("maximumSize");
679 DomProperty *geomProperty = 0;
680 DomProperty *minimumSizeProperty = 0;
681 DomProperty *maximumSizeProperty = 0;
682
683 PropertyList properties = domWidget->elementProperty();
684 const PropertyList::const_iterator cend = properties.constEnd();
685 for (PropertyList::const_iterator it = properties.constBegin(); it != cend; ++it) {
686 const QString name = (*it)->attributeName();
687 if (name == geometryPropertyName) {
688 geomProperty = *it;
689 } else {
690 if (name == minimumSizePropertyName) {
691 minimumSizeProperty = *it;
692 } else {
693 if (name == maximumSizePropertyName)
694 maximumSizeProperty = *it;
695 }
696 }
697 }
698 if (!geomProperty) {
699 geomProperty = new DomProperty;
700 geomProperty->setAttributeName(geometryPropertyName);
701 geomProperty->setElementRect(new DomRect);
702 properties.push_front(geomProperty);
703 }
704 if (fixed) {
705 if (!minimumSizeProperty) {
706 minimumSizeProperty = new DomProperty;
707 minimumSizeProperty->setAttributeName(minimumSizePropertyName);
708 minimumSizeProperty->setElementSize(new DomSize);
709 properties.push_back(minimumSizeProperty);
710 }
711 if (!maximumSizeProperty) {
712 maximumSizeProperty = new DomProperty;
713 maximumSizeProperty->setAttributeName(maximumSizePropertyName);
714 maximumSizeProperty->setElementSize(new DomSize);
715 properties.push_back(maximumSizeProperty);
716 }
717 }
718 // Set values of geometry, minimum and maximum sizes properties
719 const int width = size.width();
720 const int height = size.height();
721 if (DomRect *geom = geomProperty->elementRect()) {
722 geom->setElementWidth(width);
723 geom->setElementHeight(height);
724 }
725 if (fixed) {
726 if (DomSize *s = minimumSizeProperty->elementSize()) {
727 s->setElementWidth(width);
728 s->setElementHeight(height);
729 }
730 if (DomSize *s = maximumSizeProperty->elementSize()) {
731 s->setElementWidth(width);
732 s->setElementHeight(height);
733 }
734 }
735 // write back
736 domWidget->setElementProperty(properties);
737
738 QString rc;
739 { // serialize domUI
740 QXmlStreamWriter writer(&rc);
741 writer.setAutoFormatting(true);
742 writer.setAutoFormattingIndent(1);
743 writer.writeStartDocument();
744 domUI->write(writer);
745 writer.writeEndDocument();
746 }
747
748 delete domUI;
749 return rc;
750 }
751
752 // ---- free functions
includeSpecification(QString includeFile)753 QDESIGNER_SHARED_EXPORT IncludeSpecification includeSpecification(QString includeFile)
754 {
755 const bool global = !includeFile.isEmpty() &&
756 includeFile[0] == QLatin1Char('<') &&
757 includeFile[includeFile.size() - 1] == QLatin1Char('>');
758 if (global) {
759 includeFile.remove(includeFile.size() - 1, 1);
760 includeFile.remove(0, 1);
761 }
762 return IncludeSpecification(includeFile, global ? IncludeGlobal : IncludeLocal);
763 }
764
buildIncludeFile(QString includeFile,IncludeType includeType)765 QDESIGNER_SHARED_EXPORT QString buildIncludeFile(QString includeFile, IncludeType includeType) {
766 if (includeType == IncludeGlobal && !includeFile.isEmpty()) {
767 includeFile.append(QLatin1Char('>'));
768 includeFile.insert(0, QLatin1Char('<'));
769 }
770 return includeFile;
771 }
772
773
774 /* Appends a derived class to the database inheriting the data of the base class. Used
775 for custom and promoted widgets.
776
777 Depending on whether an entry exists, the existing or a newly created entry is
778 returned. A return value of 0 indicates that the base class could not be found. */
779
780 QDESIGNER_SHARED_EXPORT QDesignerWidgetDataBaseItemInterface *
appendDerived(QDesignerWidgetDataBaseInterface * db,const QString & className,const QString & group,const QString & baseClassName,const QString & includeFile,bool promoted,bool custom)781 appendDerived(QDesignerWidgetDataBaseInterface *db,
782 const QString &className, const QString &group,
783 const QString &baseClassName,
784 const QString &includeFile,
785 bool promoted, bool custom)
786 {
787 if (debugWidgetDataBase)
788 qDebug() << "appendDerived " << className << " derived from " << baseClassName;
789 // Check.
790 if (className.isEmpty() || baseClassName.isEmpty()) {
791 qWarning("** WARNING %s called with an empty class names: '%s' extends '%s'.",
792 Q_FUNC_INFO, className.toUtf8().constData(), baseClassName.toUtf8().constData());
793 return 0;
794 }
795 // Check whether item already exists.
796 QDesignerWidgetDataBaseItemInterface *derivedItem = 0;
797 const int existingIndex = db->indexOfClassName(className);
798 if ( existingIndex != -1)
799 derivedItem = db->item(existingIndex);
800 if (derivedItem) {
801 // Check the existing item for base class mismatch. This will likely
802 // happen when loading a file written by an instance with missing plugins.
803 // In that case, just warn and ignore the file properties.
804 //
805 // An empty base class indicates that it is not known (for example, for custom plugins).
806 // In this case, the widget DB is later updated once the widget is created
807 // by DOM (by querying the metaobject). Suppress the warning.
808 const QString existingBaseClass = derivedItem->extends();
809 if (existingBaseClass.isEmpty() || baseClassName == existingBaseClass)
810 return derivedItem;
811
812 // Warn about mismatches
813 designerWarning(QCoreApplication::translate("WidgetDataBase",
814 "The file contains a custom widget '%1' whose base class (%2)"
815 " differs from the current entry in the widget database (%3)."
816 " The widget database is left unchanged.").
817 arg(className, baseClassName, existingBaseClass));
818 return derivedItem;
819 }
820 // Create this item, inheriting its base properties
821 const int baseIndex = db->indexOfClassName(baseClassName);
822 if (baseIndex == -1) {
823 if (debugWidgetDataBase)
824 qDebug() << "appendDerived failed due to missing base class";
825 return 0;
826 }
827 const QDesignerWidgetDataBaseItemInterface *baseItem = db->item(baseIndex);
828 derivedItem = WidgetDataBaseItem::clone(baseItem);
829 // Sort of hack: If base class is QWidget, we most likely
830 // do not want to inherit the container attribute.
831 static const QString qWidgetName = QLatin1String("QWidget");
832 if (baseItem->name() == qWidgetName)
833 derivedItem->setContainer(false);
834 // set new props
835 derivedItem->setName(className);
836 derivedItem->setGroup(group);
837 derivedItem->setCustom(custom);
838 derivedItem->setPromoted(promoted);
839 derivedItem->setExtends(baseClassName);
840 derivedItem->setIncludeFile(includeFile);
841 db->append(derivedItem);
842 return derivedItem;
843 }
844
845 /* Return a list of database items to which a class can be promoted to. */
846
847 QDESIGNER_SHARED_EXPORT WidgetDataBaseItemList
promotionCandidates(const QDesignerWidgetDataBaseInterface * db,const QString & baseClassName)848 promotionCandidates(const QDesignerWidgetDataBaseInterface *db,
849 const QString &baseClassName)
850 {
851 WidgetDataBaseItemList rc;
852 // find existing promoted widgets deriving from base.
853 const int count = db->count();
854 for (int i = 0; i < count; ++i) {
855 QDesignerWidgetDataBaseItemInterface *item = db->item(i);
856 if (item->isPromoted() && item->extends() == baseClassName) {
857 rc.push_back(item);
858 }
859 }
860 return rc;
861 }
862 } // namespace qdesigner_internal
863
864 QT_END_NAMESPACE
865