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

source code of qttools/src/designer/src/lib/shared/widgetdatabase.cpp