Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
widgetdatabase.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
11#include <QtDesigner/private/ui4_p.h>
12
13#include <QtDesigner/propertysheet.h>
14#include <QtDesigner/qextensionmanager.h>
15#include <QtDesigner/abstractformeditor.h>
16
17#include <QtUiPlugin/customwidget.h>
18#include <QtWidgets/QtWidgets>
19#ifdef QT_OPENGLWIDGETS_LIB
20#include <QtOpenGLWidgets/qopenglwidget.h>
21#endif
22
23#include <QtCore/qxmlstream.h>
24
25#include <QtCore/qcoreapplication.h>
26#include <QtCore/qdebug.h>
27#include <QtCore/qmetaobject.h>
28#include <QtCore/qset.h>
29#include <QtCore/qstring.h>
30#include <QtCore/qtextstream.h>
31#include <QtCore/qcoreapplication.h>
32
33#include <memory>
34
35QT_BEGIN_NAMESPACE
36
37using namespace Qt::StringLiterals;
38
39namespace {
40 enum { debugWidgetDataBase = 0 };
41}
42
43namespace qdesigner_internal {
44
45using namespace Qt::StringLiterals;
46
47// ----------------------------------------------------------
57
59{
60 return m_name;
61}
62
64{
65 m_name = name;
66}
67
69{
70 return m_group;
71}
72
77
79{
80 return m_toolTip;
81}
82
87
89{
90 return m_whatsThis;
91}
92
97
102
107
109{
110 return m_icon;
111}
112
114{
115 m_icon = icon;
116}
117
119{
120 return m_compat;
121}
122
124{
125 m_compat = b;
126}
127
129{
130 return m_container;
131}
132
134{
135 m_container = b;
136}
137
139{
140 return m_custom;
141}
142
144{
145 m_custom = b;
146}
147
149{
150 return m_pluginPath;
151}
152
157
159{
160 return m_promoted;
161}
162
164{
165 m_promoted = b;
166}
167
169{
170 return m_extends;
171}
172
174{
175 m_extends = s;
176}
177
182
187
192
197
202
207
212
217
236
241
246
248{
250 if (auto *base = mo->superClass())
252#if QT_CONFIG(abstractbutton)
254 item->setToolTip(WidgetDataBase::tr("text"));
255#endif
256 wdb->append(item);
257}
258
259// ----------------------------------------------------------
262 m_core(core)
263{
265 for (const QMetaObject *metaObject : metaObjects)
267
269 QCoreApplication::translate("WidgetDataBase",
270 "Abstract base class that cannot be instantiated. For promotion/custom widget usage only.");
271
272#if QT_CONFIG(abstractbutton)
273 auto *abItem = new WidgetDataBaseItem(u"QAbstractButton"_s);
275 abItem->setBaseClassName(u"QWidget"_s);
276 append(abItem);
277#endif // QT_CONFIG(abstractbutton)
278
279#if QT_CONFIG(itemviews)
280 auto *aivItem = new WidgetDataBaseItem(u"QAbstractItemView"_s);
281 aivItem->setBaseClassName(u"QAbstractScrollArea"_s);
284#endif // QT_CONFIG(itemviews)
285
286 append(new WidgetDataBaseItem(u"Line"_s));
287 append(new WidgetDataBaseItem(u"Spacer"_s));
288 append(new WidgetDataBaseItem(u"QSplitter"_s));
289 append(new WidgetDataBaseItem(u"QLayoutWidget"_s));
290 // QDesignerWidget is used as central widget and as container for tab widgets, etc.
291 WidgetDataBaseItem *designerWidgetItem = new WidgetDataBaseItem(u"QDesignerWidget"_s);
294 append(new WidgetDataBaseItem(u"QDesignerDialog"_s));
295 append(new WidgetDataBaseItem(u"QDesignerMenu"_s));
296 append(new WidgetDataBaseItem(u"QDesignerMenuBar"_s));
297 append(new WidgetDataBaseItem(u"QDesignerDockWidget"_s));
298 append(new WidgetDataBaseItem(u"QAction"_s));
299 append(new WidgetDataBaseItem(u"QButtonGroup"_s));
300
301 // ### remove me
302 // ### check the casts
303
304#if 0 // ### enable me after 4.1
305 item(indexOfClassName(u"QToolBar"_s))->setContainer(true);
306#endif
307
308 item(indexOfClassName(u"QTabWidget"_s))->setContainer(true);
309 item(indexOfClassName(u"QGroupBox"_s))->setContainer(true);
310 item(indexOfClassName(u"QScrollArea"_s))->setContainer(true);
311 item(indexOfClassName(u"QStackedWidget"_s))->setContainer(true);
312 item(indexOfClassName(u"QToolBox"_s))->setContainer(true);
313 item(indexOfClassName(u"QFrame"_s))->setContainer(true);
314 item(indexOfClassName(u"QLayoutWidget"_s))->setContainer(true);
315 item(indexOfClassName(u"QDesignerWidget"_s))->setContainer(true);
316 item(indexOfClassName(u"QDesignerDialog"_s))->setContainer(true);
317 item(indexOfClassName(u"QSplitter"_s))->setContainer(true);
318 item(indexOfClassName(u"QMainWindow"_s))->setContainer(true);
319 item(indexOfClassName(u"QDockWidget"_s))->setContainer(true);
320 item(indexOfClassName(u"QDesignerDockWidget"_s))->setContainer(true);
321 item(indexOfClassName(u"QMdiArea"_s))->setContainer(true);
322 item(indexOfClassName(u"QWizard"_s))->setContainer(true);
323 item(indexOfClassName(u"QWizardPage"_s))->setContainer(true);
324
325 item(indexOfClassName(u"QWidget"_s))->setContainer(true);
326 item(indexOfClassName(u"QDialog"_s))->setContainer(true);
327}
328
329WidgetDataBase::~WidgetDataBase() = default;
330
335
351
353 const QDesignerCustomWidgetData &data)
354{
355 WidgetDataBaseItem *item = new WidgetDataBaseItem(c->name(), c->group());
356 item->setContainer(c->isContainer());
357 item->setCustom(true);
358 item->setIcon(c->icon());
359 item->setIncludeFile(c->includeFile());
360 item->setToolTip(c->toolTip());
361 item->setWhatsThis(c->whatsThis());
362 item->setPluginPath(data.pluginPath());
363 item->setAddPageMethod(data.xmlAddPageMethod());
364 item->setExtends(data.xmlExtends());
365 return item;
366}
367
369{
370 // 1) create a map of existing custom classes
373 for (qsizetype i = 0, count = m_items.size(); i < count; ++i) {
375 if (item->isCustom() && !item->isPromoted())
377 else
379 }
380 // 2) create a list plugins
386
387 // 3) replace custom classes or add new ones, remove them from existingCustomClasses,
388 // leaving behind deleted items
389 unsigned replacedPlugins = 0;
390 unsigned addedPlugins = 0;
391 unsigned removedPlugins = 0;
392 if (!pluginList.isEmpty()) {
397 // Add new class.
399 designerWarning(tr("A custom widget plugin whose class name (%1) matches that of an existing class has been found.").arg(pluginName));
400 } else {
402 addedPlugins++;
403 }
404 } else {
405 // replace existing info
406 const auto existingIndex = existingIt.value();
407 delete m_items[existingIndex];
411
412 }
413 }
414 }
415 // 4) remove classes that have not been matched. The stored indexes become invalid while deleting.
417 const int index = indexOfClassName(it.key());
418 if (index != -1) {
419 remove(index);
421 }
422 }
423
425 qDebug() << "WidgetDataBase::loadPlugins(): " << addedPlugins << " added, " << replacedPlugins << " replaced, " << removedPlugins << "deleted.";
426}
427
429{
431 delete m_items.takeAt(index);
432}
433
435{
438 // Create non-widgets, widgets in order
439 QObject* object = factory->createObject(name, nullptr);
440 if (!object)
441 object = factory->createWidget(name, nullptr);
442 if (!object) {
443 qDebug() << "** WARNING Factory failed to create " << name;
444 return {};
445 }
446 // Get properties from sheet.
449 const int propertyCount = sheet->count();
450 for (int i = 0; i < propertyCount; ++i) {
452 }
453 }
454 delete object;
455 return result;
456}
457
467
469{
470 // At this point, grab the default icons for the non-custom widgets from
471 // the widget box. They will show up in the object inspector.
473 const int itemCount = count();
474 for (int i = 0; i < itemCount; ++i) {
476 if (!dbItem->isCustom() && dbItem->icon().isNull()) {
477 // Careful not to catch the layout icons when looking for
478 // QWidget
479 const QString name = dbItem->name();
480 if (name == "QWidget"_L1) {
481 dbItem->setIcon(wb->iconForWidget(name, u"Containers"_s));
482 } else {
484 }
485 }
486 }
487 }
488}
489
490// --------------------- Functions relevant generation of new forms based on widgets (apart from the standard templates)
491
492enum { NewFormWidth = 400, NewFormHeight = 300 };
493
494// Check if class is suitable to generate a form from
495static inline bool isExistingTemplate(const QString &className)
496{
497 return className == "QWidget"_L1 || className == "QDialog"_L1 || className == "QMainWindow"_L1;
498}
499
500// Check if class is suitable to generate a form from
501static inline bool suitableForNewForm(const QString &className)
502{
503 if (className.isEmpty()) // Missing custom widget information
504 return false;
505 if (className == "QSplitter"_L1)
506 return false;
507 if (className.startsWith("QDesigner"_L1) || className.startsWith("QLayout"_L1))
508 return false;
509 return true;
510}
511
512// Return a list of widget classes from which new forms can be generated.
513// Suitable for 'New form' wizards in integrations.
515{
516 static QStringList rc;
517 if (rc.isEmpty()) {
519 const int widgetCount = wdb->count();
520 for (int i = 0; i < widgetCount; i++) {
522 if (item->isContainer() && !item->isCustom() && !item->isPromoted()) {
523 const QString name = item->name(); // Standard Widgets: no existing templates
525 rc += name;
526 }
527 }
528 }
529 return rc;
530}
531
532// Return a list of custom widget classes from which new forms can be generated.
533// Suitable for 'New form' wizards in integrations.
535{
538 const int widgetCount = wdb->count();
539 for (int i = 0; i < widgetCount; i++) { // Custom widgets: check name and base class.
541 if (item->isContainer() && item->isCustom() && !item->isPromoted()) {
543 rc += item->name();
544 }
545 }
546 return rc;
547}
548
549// Get XML for a new form from the widget box. Change objectName/geometry
550// properties to be suitable for new forms
551static QString xmlFromWidgetBox(const QDesignerFormEditorInterface *core, const QString &className, const QString &objectName)
552{
553 QDesignerWidgetBoxInterface::Widget widget;
554 const bool found = QDesignerWidgetBox::findWidget(core->widgetBox(), className, QString(), &widget);
555 if (!found)
556 return QString();
557 std::unique_ptr<DomUI> domUI(QDesignerWidgetBox::xmlToUi(className, widget.domXml(), false));
558 if (!domUI)
559 return QString();
560 domUI->setAttributeVersion(u"4.0"_s);
561 DomWidget *domWidget = domUI->elementWidget();
562 if (!domWidget)
563 return QString();
564 // Properties: Remove the "objectName" property in favour of the name attribute and check geometry.
565 domWidget->setAttributeName(objectName);
566 QList<DomProperty *> properties = domWidget->elementProperty();
567 for (auto it = properties.begin(); it != properties.end(); ) {
568 DomProperty *property = *it;
569 if (property->attributeName() == "objectName"_L1) { // remove "objectName"
570 it = properties.erase(it);
571 delete property;
572 } else {
573 if (property->attributeName() == "geometry"_L1) { // Make sure form is at least 400, 300
574 if (DomRect *geom = property->elementRect()) {
575 if (geom->elementWidth() < NewFormWidth)
576 geom->setElementWidth(NewFormWidth);
577 if (geom->elementHeight() < NewFormHeight)
578 geom->setElementHeight(NewFormHeight);
579 }
580 }
581 ++it;
582 }
583 }
584 // Add a window title property
585 DomString *windowTitleString = new DomString;
586 windowTitleString->setText(objectName);
587 DomProperty *windowTitleProperty = new DomProperty;
588 windowTitleProperty->setAttributeName(u"windowTitle"_s);
589 windowTitleProperty->setElementString(windowTitleString);
590 properties.push_back(windowTitleProperty);
591 // ------
592 domWidget->setElementProperty(properties);
593 // Embed in in DomUI and get string. Omit the version number.
594 domUI->setElementClass(objectName);
595
596 QString rc;
597 { // Serialize domUI
598 QXmlStreamWriter writer(&rc);
599 writer.setAutoFormatting(true);
600 writer.setAutoFormattingIndent(1);
601 writer.writeStartDocument();
602 domUI->write(writer);
603 writer.writeEndDocument();
604 }
605 return rc;
606}
607
608// Generate default standard ui new form xml based on the class passed on as similarClassName.
609static QString generateNewFormXML(const QString &className, const QString &similarClassName, const QString &name)
610{
611 QString rc;
612 QTextStream str(&rc);
613 str << R"(<ui version="4.0"><class>)" << name << "</class>"
614 << R"(<widget class=")" << className << R"(" name=")" << name << R"(">)"
615 << R"(<property name="geometry" ><rect><x>0</x><y>0</y><width>)"
616 << NewFormWidth << "</width><height>" << NewFormHeight << "</height></rect></property>"
617 << R"(<property name="windowTitle"><string>)" << name << "</string></property>\n";
618
619 if (similarClassName == "QMainWindow"_L1) {
620 str << R"(<widget class="QWidget" name="centralwidget"/>)";
621 } else if (similarClassName == "QWizard"_L1) {
622 str << R"(<widget class="QWizardPage" name="wizardPage1"/><widget class="QWizardPage" name="wizardPage2"/>)";
623 } else if (similarClassName == "QDockWidget"_L1) {
624 str << R"(<widget class="QWidget" name="dockWidgetContents"/>)";
625 }
626 str << "</widget></ui>\n";
627 return rc;
628}
629
630// Generate a form template using a class name obtained from formWidgetClasses(), customFormWidgetClasses().
632{
633 // How to find suitable XML for a class:
634 // 1) Look in widget box (as all the required centralwidgets, tab widget pages, etc. should be there).
636 if (!widgetBoxXml.isEmpty())
637 return widgetBoxXml;
638 // 2) If that fails, only custom main windows, custom dialogs and unsupported Qt Widgets should
639 // be left over. Generate something that is similar to the default templates. Find a similar class.
641 QString similarClass = u"QWidget"_s;
642 const int index = wdb->indexOfClassName(className);
643 if (index != -1) {
646 }
647 // Generate standard ui based on the class passed on as baseClassName.
649 return rc;
650}
651
652// Set a fixed size on a XML template
654{
655 std::unique_ptr<DomUI> domUI(QDesignerWidgetBox::xmlToUi(u"Form"_s, xml, false));
656 if (!domUI)
657 return QString();
659 if (!domWidget)
660 return QString();
661 // Properties: Find/Ensure the geometry, minimum and maximum sizes properties
662 const QString geometryPropertyName = u"geometry"_s;
663 const QString minimumSizePropertyName = u"minimumSize"_s;
664 const QString maximumSizePropertyName = u"maximumSize"_s;
665 DomProperty *geomProperty = nullptr;
668
670 for (DomProperty *p : std::as_const(properties)) {
671 const QString name = p->attributeName();
672 if (name == geometryPropertyName) {
673 geomProperty = p;
674 } else if (name == minimumSizePropertyName) {
676 } else if (name == maximumSizePropertyName) {
678 }
679 }
680 if (!geomProperty) {
685 }
686 if (fixed) {
687 if (!minimumSizeProperty) {
692 }
693 if (!maximumSizeProperty) {
698 }
699 }
700 // Set values of geometry, minimum and maximum sizes properties
701 const int width = size.width();
702 const int height = size.height();
706 }
707 if (fixed) {
711 }
715 }
716 }
717 // write back
719
720 QString rc;
721 { // serialize domUI
728 }
729
730 return rc;
731}
732
733// ---- free functions
743
752
753
754/* Appends a derived class to the database inheriting the data of the base class. Used
755 for custom and promoted widgets.
756
757 Depending on whether an entry exists, the existing or a newly created entry is
758 returned. A return value of 0 indicates that the base class could not be found. */
759
760QDESIGNER_SHARED_EXPORT QDesignerWidgetDataBaseItemInterface *
762 const QString &className, const QString &group,
763 const QString &baseClassName,
764 const QString &includeFile,
765 bool promoted, bool custom)
766{
768 qDebug() << "appendDerived " << className << " derived from " << baseClassName;
769 // Check.
771 qWarning("** WARNING %s called with an empty class names: '%s' extends '%s'.",
773 return nullptr;
774 }
775 // Check whether item already exists.
778 if ( existingIndex != -1)
780 if (derivedItem) {
781 // Check the existing item for base class mismatch. This will likely
782 // happen when loading a file written by an instance with missing plugins.
783 // In that case, just warn and ignore the file properties.
784 //
785 // An empty base class indicates that it is not known (for example, for custom plugins).
786 // In this case, the widget DB is later updated once the widget is created
787 // by DOM (by querying the metaobject). Suppress the warning.
790 return derivedItem;
791
792 // Warn about mismatches
794 "The file contains a custom widget '%1' whose base class (%2)"
795 " differs from the current entry in the widget database (%3)."
796 " The widget database is left unchanged.").
798 return derivedItem;
799 }
800 // Create this item, inheriting its base properties
802 if (baseIndex == -1) {
804 qDebug() << "appendDerived failed due to missing base class";
805 return nullptr;
806 }
809 // Sort of hack: If base class is QWidget, we most likely
810 // do not want to inherit the container attribute.
811 if (baseItem->name() == "QWidget"_L1)
813 // set new props
821 return derivedItem;
822}
823
824/* Return a list of database items to which a class can be promoted to. */
825
826QDESIGNER_SHARED_EXPORT WidgetDataBaseItemList
828 const QString &baseClassName)
829{
831 // find existing promoted widgets deriving from base.
832 const int count = db->count();
833 for (int i = 0; i < count; ++i) {
835 if (item->isPromoted() && item->extends() == baseClassName) {
837 }
838 }
839 return rc;
840}
841} // namespace qdesigner_internal
842
843QT_END_NAMESPACE
Auxiliary methods to store/retrieve settings.
static QString xmlFromWidgetBox(const QDesignerFormEditorInterface *core, const QString &className, const QString &objectName)
static QString generateNewFormXML(const QString &className, const QString &similarClassName, const QString &name)
static bool isExistingTemplate(const QString &className)
static WidgetDataBaseItem * createCustomWidgetItem(const QDesignerCustomWidgetInterface *c, const QDesignerCustomWidgetData &data)
static bool suitableForNewForm(const QString &className)
static void addWidgetItem(WidgetDataBase *wdb, const QMetaObject *mo)
#define QDESIGNER_SHARED_EXPORT