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
247static void addWidgetItem(WidgetDataBase *wdb, const char *name, const QMetaObject &mo,
248 const char *comment)
249{
251 if (auto *base = mo.superClass())
253 if (comment[0])
255 wdb->append(item);
256}
257
258// ----------------------------------------------------------
261 m_core(core)
262{
263#define DECLARE_LAYOUT(L, C)
264#define DECLARE_COMPAT_WIDGET(W, C) DECLARE_WIDGET(W, C)
265#define DECLARE_WIDGET(W, C) addWidgetItem(this, #W, W::staticMetaObject, C);
266
267// AXIVION DISABLE Style Qt-Generic-NoIrregularInclude Required functionality
268#include <widgets.table>
269// AXIVION ENABLE Style Qt-Generic-NoIrregularInclude
270
271#undef DECLARE_COMPAT_WIDGET
272#undef DECLARE_LAYOUT
273#undef DECLARE_WIDGET
274#undef DECLARE_WIDGET_1
275
277 QCoreApplication::translate("WidgetDataBase",
278 "Abstract base class that cannot be instantiated. For promotion/custom widget usage only.");
279
280#if QT_CONFIG(abstractbutton)
281 auto *abItem = new WidgetDataBaseItem(u"QAbstractButton"_s);
283 abItem->setBaseClassName(u"QWidget"_s);
284 append(abItem);
285#endif // QT_CONFIG(abstractbutton)
286
287#if QT_CONFIG(itemviews)
288 auto *aivItem = new WidgetDataBaseItem(u"QAbstractItemView"_s);
289 aivItem->setBaseClassName(u"QAbstractScrollArea"_s);
292#endif // QT_CONFIG(itemviews)
293
294 append(new WidgetDataBaseItem(u"Line"_s));
295 append(new WidgetDataBaseItem(u"Spacer"_s));
296 append(new WidgetDataBaseItem(u"QSplitter"_s));
297 append(new WidgetDataBaseItem(u"QLayoutWidget"_s));
298 // QDesignerWidget is used as central widget and as container for tab widgets, etc.
299 WidgetDataBaseItem *designerWidgetItem = new WidgetDataBaseItem(u"QDesignerWidget"_s);
302 append(new WidgetDataBaseItem(u"QDesignerDialog"_s));
303 append(new WidgetDataBaseItem(u"QDesignerMenu"_s));
304 append(new WidgetDataBaseItem(u"QDesignerMenuBar"_s));
305 append(new WidgetDataBaseItem(u"QDesignerDockWidget"_s));
306 append(new WidgetDataBaseItem(u"QAction"_s));
307 append(new WidgetDataBaseItem(u"QButtonGroup"_s));
308
309 // ### remove me
310 // ### check the casts
311
312#if 0 // ### enable me after 4.1
313 item(indexOfClassName(u"QToolBar"_s))->setContainer(true);
314#endif
315
316 item(indexOfClassName(u"QTabWidget"_s))->setContainer(true);
317 item(indexOfClassName(u"QGroupBox"_s))->setContainer(true);
318 item(indexOfClassName(u"QScrollArea"_s))->setContainer(true);
319 item(indexOfClassName(u"QStackedWidget"_s))->setContainer(true);
320 item(indexOfClassName(u"QToolBox"_s))->setContainer(true);
321 item(indexOfClassName(u"QFrame"_s))->setContainer(true);
322 item(indexOfClassName(u"QLayoutWidget"_s))->setContainer(true);
323 item(indexOfClassName(u"QDesignerWidget"_s))->setContainer(true);
324 item(indexOfClassName(u"QDesignerDialog"_s))->setContainer(true);
325 item(indexOfClassName(u"QSplitter"_s))->setContainer(true);
326 item(indexOfClassName(u"QMainWindow"_s))->setContainer(true);
327 item(indexOfClassName(u"QDockWidget"_s))->setContainer(true);
328 item(indexOfClassName(u"QDesignerDockWidget"_s))->setContainer(true);
329 item(indexOfClassName(u"QMdiArea"_s))->setContainer(true);
330 item(indexOfClassName(u"QWizard"_s))->setContainer(true);
331 item(indexOfClassName(u"QWizardPage"_s))->setContainer(true);
332
333 item(indexOfClassName(u"QWidget"_s))->setContainer(true);
334 item(indexOfClassName(u"QDialog"_s))->setContainer(true);
335}
336
337WidgetDataBase::~WidgetDataBase() = default;
338
343
359
361 const QDesignerCustomWidgetData &data)
362{
363 WidgetDataBaseItem *item = new WidgetDataBaseItem(c->name(), c->group());
364 item->setContainer(c->isContainer());
365 item->setCustom(true);
366 item->setIcon(c->icon());
367 item->setIncludeFile(c->includeFile());
368 item->setToolTip(c->toolTip());
369 item->setWhatsThis(c->whatsThis());
370 item->setPluginPath(data.pluginPath());
371 item->setAddPageMethod(data.xmlAddPageMethod());
372 item->setExtends(data.xmlExtends());
373 return item;
374}
375
377{
378 // 1) create a map of existing custom classes
381 for (qsizetype i = 0, count = m_items.size(); i < count; ++i) {
383 if (item->isCustom() && !item->isPromoted())
385 else
387 }
388 // 2) create a list plugins
394
395 // 3) replace custom classes or add new ones, remove them from existingCustomClasses,
396 // leaving behind deleted items
397 unsigned replacedPlugins = 0;
398 unsigned addedPlugins = 0;
399 unsigned removedPlugins = 0;
400 if (!pluginList.isEmpty()) {
405 // Add new class.
407 designerWarning(tr("A custom widget plugin whose class name (%1) matches that of an existing class has been found.").arg(pluginName));
408 } else {
410 addedPlugins++;
411 }
412 } else {
413 // replace existing info
414 const auto existingIndex = existingIt.value();
415 delete m_items[existingIndex];
419
420 }
421 }
422 }
423 // 4) remove classes that have not been matched. The stored indexes become invalid while deleting.
425 const int index = indexOfClassName(it.key());
426 if (index != -1) {
427 remove(index);
429 }
430 }
431
433 qDebug() << "WidgetDataBase::loadPlugins(): " << addedPlugins << " added, " << replacedPlugins << " replaced, " << removedPlugins << "deleted.";
434}
435
437{
439 delete m_items.takeAt(index);
440}
441
443{
446 // Create non-widgets, widgets in order
447 QObject* object = factory->createObject(name, nullptr);
448 if (!object)
449 object = factory->createWidget(name, nullptr);
450 if (!object) {
451 qDebug() << "** WARNING Factory failed to create " << name;
452 return {};
453 }
454 // Get properties from sheet.
457 const int propertyCount = sheet->count();
458 for (int i = 0; i < propertyCount; ++i) {
460 }
461 }
462 delete object;
463 return result;
464}
465
475
477{
478 // At this point, grab the default icons for the non-custom widgets from
479 // the widget box. They will show up in the object inspector.
481 const int itemCount = count();
482 for (int i = 0; i < itemCount; ++i) {
484 if (!dbItem->isCustom() && dbItem->icon().isNull()) {
485 // Careful not to catch the layout icons when looking for
486 // QWidget
487 const QString name = dbItem->name();
488 if (name == "QWidget"_L1) {
489 dbItem->setIcon(wb->iconForWidget(name, u"Containers"_s));
490 } else {
492 }
493 }
494 }
495 }
496}
497
498// --------------------- Functions relevant generation of new forms based on widgets (apart from the standard templates)
499
500enum { NewFormWidth = 400, NewFormHeight = 300 };
501
502// Check if class is suitable to generate a form from
503static inline bool isExistingTemplate(const QString &className)
504{
505 return className == "QWidget"_L1 || className == "QDialog"_L1 || className == "QMainWindow"_L1;
506}
507
508// Check if class is suitable to generate a form from
509static inline bool suitableForNewForm(const QString &className)
510{
511 if (className.isEmpty()) // Missing custom widget information
512 return false;
513 if (className == "QSplitter"_L1)
514 return false;
515 if (className.startsWith("QDesigner"_L1) || className.startsWith("QLayout"_L1))
516 return false;
517 return true;
518}
519
520// Return a list of widget classes from which new forms can be generated.
521// Suitable for 'New form' wizards in integrations.
523{
524 static QStringList rc;
525 if (rc.isEmpty()) {
527 const int widgetCount = wdb->count();
528 for (int i = 0; i < widgetCount; i++) {
530 if (item->isContainer() && !item->isCustom() && !item->isPromoted()) {
531 const QString name = item->name(); // Standard Widgets: no existing templates
533 rc += name;
534 }
535 }
536 }
537 return rc;
538}
539
540// Return a list of custom widget classes from which new forms can be generated.
541// Suitable for 'New form' wizards in integrations.
543{
546 const int widgetCount = wdb->count();
547 for (int i = 0; i < widgetCount; i++) { // Custom widgets: check name and base class.
549 if (item->isContainer() && item->isCustom() && !item->isPromoted()) {
551 rc += item->name();
552 }
553 }
554 return rc;
555}
556
557// Get XML for a new form from the widget box. Change objectName/geometry
558// properties to be suitable for new forms
559static QString xmlFromWidgetBox(const QDesignerFormEditorInterface *core, const QString &className, const QString &objectName)
560{
561 QDesignerWidgetBoxInterface::Widget widget;
562 const bool found = QDesignerWidgetBox::findWidget(core->widgetBox(), className, QString(), &widget);
563 if (!found)
564 return QString();
565 std::unique_ptr<DomUI> domUI(QDesignerWidgetBox::xmlToUi(className, widget.domXml(), false));
566 if (!domUI)
567 return QString();
568 domUI->setAttributeVersion(u"4.0"_s);
569 DomWidget *domWidget = domUI->elementWidget();
570 if (!domWidget)
571 return QString();
572 // Properties: Remove the "objectName" property in favour of the name attribute and check geometry.
573 domWidget->setAttributeName(objectName);
574 QList<DomProperty *> properties = domWidget->elementProperty();
575 for (auto it = properties.begin(); it != properties.end(); ) {
576 DomProperty *property = *it;
577 if (property->attributeName() == "objectName"_L1) { // remove "objectName"
578 it = properties.erase(it);
579 delete property;
580 } else {
581 if (property->attributeName() == "geometry"_L1) { // Make sure form is at least 400, 300
582 if (DomRect *geom = property->elementRect()) {
583 if (geom->elementWidth() < NewFormWidth)
584 geom->setElementWidth(NewFormWidth);
585 if (geom->elementHeight() < NewFormHeight)
586 geom->setElementHeight(NewFormHeight);
587 }
588 }
589 ++it;
590 }
591 }
592 // Add a window title property
593 DomString *windowTitleString = new DomString;
594 windowTitleString->setText(objectName);
595 DomProperty *windowTitleProperty = new DomProperty;
596 windowTitleProperty->setAttributeName(u"windowTitle"_s);
597 windowTitleProperty->setElementString(windowTitleString);
598 properties.push_back(windowTitleProperty);
599 // ------
600 domWidget->setElementProperty(properties);
601 // Embed in in DomUI and get string. Omit the version number.
602 domUI->setElementClass(objectName);
603
604 QString rc;
605 { // Serialize domUI
606 QXmlStreamWriter writer(&rc);
607 writer.setAutoFormatting(true);
608 writer.setAutoFormattingIndent(1);
609 writer.writeStartDocument();
610 domUI->write(writer);
611 writer.writeEndDocument();
612 }
613 return rc;
614}
615
616// Generate default standard ui new form xml based on the class passed on as similarClassName.
617static QString generateNewFormXML(const QString &className, const QString &similarClassName, const QString &name)
618{
619 QString rc;
620 QTextStream str(&rc);
621 str << R"(<ui version="4.0"><class>)" << name << "</class>"
622 << R"(<widget class=")" << className << R"(" name=")" << name << R"(">)"
623 << R"(<property name="geometry" ><rect><x>0</x><y>0</y><width>)"
624 << NewFormWidth << "</width><height>" << NewFormHeight << "</height></rect></property>"
625 << R"(<property name="windowTitle"><string>)" << name << "</string></property>\n";
626
627 if (similarClassName == "QMainWindow"_L1) {
628 str << R"(<widget class="QWidget" name="centralwidget"/>)";
629 } else if (similarClassName == "QWizard"_L1) {
630 str << R"(<widget class="QWizardPage" name="wizardPage1"/><widget class="QWizardPage" name="wizardPage2"/>)";
631 } else if (similarClassName == "QDockWidget"_L1) {
632 str << R"(<widget class="QWidget" name="dockWidgetContents"/>)";
633 }
634 str << "</widget></ui>\n";
635 return rc;
636}
637
638// Generate a form template using a class name obtained from formWidgetClasses(), customFormWidgetClasses().
640{
641 // How to find suitable XML for a class:
642 // 1) Look in widget box (as all the required centralwidgets, tab widget pages, etc. should be there).
644 if (!widgetBoxXml.isEmpty())
645 return widgetBoxXml;
646 // 2) If that fails, only custom main windows, custom dialogs and unsupported Qt Widgets should
647 // be left over. Generate something that is similar to the default templates. Find a similar class.
649 QString similarClass = u"QWidget"_s;
650 const int index = wdb->indexOfClassName(className);
651 if (index != -1) {
654 }
655 // Generate standard ui based on the class passed on as baseClassName.
657 return rc;
658}
659
660// Set a fixed size on a XML template
662{
663 std::unique_ptr<DomUI> domUI(QDesignerWidgetBox::xmlToUi(u"Form"_s, xml, false));
664 if (!domUI)
665 return QString();
667 if (!domWidget)
668 return QString();
669 // Properties: Find/Ensure the geometry, minimum and maximum sizes properties
670 const QString geometryPropertyName = u"geometry"_s;
671 const QString minimumSizePropertyName = u"minimumSize"_s;
672 const QString maximumSizePropertyName = u"maximumSize"_s;
673 DomProperty *geomProperty = nullptr;
676
678 for (DomProperty *p : std::as_const(properties)) {
679 const QString name = p->attributeName();
680 if (name == geometryPropertyName) {
681 geomProperty = p;
682 } else if (name == minimumSizePropertyName) {
684 } else if (name == maximumSizePropertyName) {
686 }
687 }
688 if (!geomProperty) {
693 }
694 if (fixed) {
695 if (!minimumSizeProperty) {
700 }
701 if (!maximumSizeProperty) {
706 }
707 }
708 // Set values of geometry, minimum and maximum sizes properties
709 const int width = size.width();
710 const int height = size.height();
714 }
715 if (fixed) {
719 }
723 }
724 }
725 // write back
727
728 QString rc;
729 { // serialize domUI
736 }
737
738 return rc;
739}
740
741// ---- free functions
751
760
761
762/* Appends a derived class to the database inheriting the data of the base class. Used
763 for custom and promoted widgets.
764
765 Depending on whether an entry exists, the existing or a newly created entry is
766 returned. A return value of 0 indicates that the base class could not be found. */
767
768QDESIGNER_SHARED_EXPORT QDesignerWidgetDataBaseItemInterface *
770 const QString &className, const QString &group,
771 const QString &baseClassName,
772 const QString &includeFile,
773 bool promoted, bool custom)
774{
776 qDebug() << "appendDerived " << className << " derived from " << baseClassName;
777 // Check.
779 qWarning("** WARNING %s called with an empty class names: '%s' extends '%s'.",
781 return nullptr;
782 }
783 // Check whether item already exists.
786 if ( existingIndex != -1)
788 if (derivedItem) {
789 // Check the existing item for base class mismatch. This will likely
790 // happen when loading a file written by an instance with missing plugins.
791 // In that case, just warn and ignore the file properties.
792 //
793 // An empty base class indicates that it is not known (for example, for custom plugins).
794 // In this case, the widget DB is later updated once the widget is created
795 // by DOM (by querying the metaobject). Suppress the warning.
798 return derivedItem;
799
800 // Warn about mismatches
802 "The file contains a custom widget '%1' whose base class (%2)"
803 " differs from the current entry in the widget database (%3)."
804 " The widget database is left unchanged.").
806 return derivedItem;
807 }
808 // Create this item, inheriting its base properties
810 if (baseIndex == -1) {
812 qDebug() << "appendDerived failed due to missing base class";
813 return nullptr;
814 }
817 // Sort of hack: If base class is QWidget, we most likely
818 // do not want to inherit the container attribute.
819 if (baseItem->name() == "QWidget"_L1)
821 // set new props
829 return derivedItem;
830}
831
832/* Return a list of database items to which a class can be promoted to. */
833
834QDESIGNER_SHARED_EXPORT WidgetDataBaseItemList
836 const QString &baseClassName)
837{
839 // find existing promoted widgets deriving from base.
840 const int count = db->count();
841 for (int i = 0; i < count; ++i) {
843 if (item->isPromoted() && item->extends() == baseClassName) {
845 }
846 }
847 return rc;
848}
849} // namespace qdesigner_internal
850
851QT_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 void addWidgetItem(WidgetDataBase *wdb, const char *name, const QMetaObject &mo, const char *comment)
static bool suitableForNewForm(const QString &className)
#define QDESIGNER_SHARED_EXPORT