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 
52 QT_BEGIN_NAMESPACE
53 
54 namespace {
55     enum { debugWidgetDataBase = 0 };
56 }
57 
58 namespace qdesigner_internal {
59 
60 // ----------------------------------------------------------
WidgetDataBaseItem(const QString & name,const QString & group)61 WidgetDataBaseItem::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 
name() const71 QString WidgetDataBaseItem::name() const
72 {
73     return m_name;
74 }
75 
setName(const QString & name)76 void WidgetDataBaseItem::setName(const QString &name)
77 {
78     m_name = name;
79 }
80 
group() const81 QString WidgetDataBaseItem::group() const
82 {
83     return m_group;
84 }
85 
setGroup(const QString & group)86 void WidgetDataBaseItem::setGroup(const QString &group)
87 {
88     m_group = group;
89 }
90 
toolTip() const91 QString WidgetDataBaseItem::toolTip() const
92 {
93     return m_toolTip;
94 }
95 
setToolTip(const QString & toolTip)96 void WidgetDataBaseItem::setToolTip(const QString &toolTip)
97 {
98     m_toolTip = toolTip;
99 }
100 
whatsThis() const101 QString WidgetDataBaseItem::whatsThis() const
102 {
103     return m_whatsThis;
104 }
105 
setWhatsThis(const QString & whatsThis)106 void WidgetDataBaseItem::setWhatsThis(const QString &whatsThis)
107 {
108     m_whatsThis = whatsThis;
109 }
110 
includeFile() const111 QString WidgetDataBaseItem::includeFile() const
112 {
113     return m_includeFile;
114 }
115 
setIncludeFile(const QString & includeFile)116 void WidgetDataBaseItem::setIncludeFile(const QString &includeFile)
117 {
118     m_includeFile = includeFile;
119 }
120 
icon() const121 QIcon WidgetDataBaseItem::icon() const
122 {
123     return m_icon;
124 }
125 
setIcon(const QIcon & icon)126 void WidgetDataBaseItem::setIcon(const QIcon &icon)
127 {
128     m_icon = icon;
129 }
130 
isCompat() const131 bool WidgetDataBaseItem::isCompat() const
132 {
133     return m_compat;
134 }
135 
setCompat(bool b)136 void WidgetDataBaseItem::setCompat(bool b)
137 {
138     m_compat = b;
139 }
140 
isContainer() const141 bool WidgetDataBaseItem::isContainer() const
142 {
143     return m_container;
144 }
145 
setContainer(bool b)146 void WidgetDataBaseItem::setContainer(bool b)
147 {
148     m_container = b;
149 }
150 
isCustom() const151 bool WidgetDataBaseItem::isCustom() const
152 {
153     return m_custom;
154 }
155 
setCustom(bool b)156 void WidgetDataBaseItem::setCustom(bool b)
157 {
158     m_custom = b;
159 }
160 
pluginPath() const161 QString WidgetDataBaseItem::pluginPath() const
162 {
163     return m_pluginPath;
164 }
165 
setPluginPath(const QString & path)166 void WidgetDataBaseItem::setPluginPath(const QString &path)
167 {
168     m_pluginPath = path;
169 }
170 
isPromoted() const171 bool WidgetDataBaseItem::isPromoted() const
172 {
173     return m_promoted;
174 }
175 
setPromoted(bool b)176 void WidgetDataBaseItem::setPromoted(bool b)
177 {
178     m_promoted = b;
179 }
180 
extends() const181 QString WidgetDataBaseItem::extends() const
182 {
183     return m_extends;
184 }
185 
setExtends(const QString & s)186 void WidgetDataBaseItem::setExtends(const QString &s)
187 {
188     m_extends = s;
189 }
190 
setDefaultPropertyValues(const QList<QVariant> & list)191 void WidgetDataBaseItem::setDefaultPropertyValues(const QList<QVariant> &list)
192 {
193     m_defaultPropertyValues = list;
194 }
195 
defaultPropertyValues() const196 QList<QVariant> WidgetDataBaseItem::defaultPropertyValues() const
197 {
198     return m_defaultPropertyValues;
199 }
200 
fakeSlots() const201 QStringList WidgetDataBaseItem::fakeSlots() const
202 {
203     return m_fakeSlots;
204 }
205 
setFakeSlots(const QStringList & fs)206 void WidgetDataBaseItem::setFakeSlots(const QStringList &fs)
207 {
208     m_fakeSlots = fs;
209 }
210 
fakeSignals() const211 QStringList WidgetDataBaseItem::fakeSignals() const
212 {
213      return m_fakeSignals;
214 }
215 
setFakeSignals(const QStringList & fs)216 void WidgetDataBaseItem::setFakeSignals(const QStringList &fs)
217 {
218     m_fakeSignals = fs;
219 }
220 
addPageMethod() const221 QString WidgetDataBaseItem::addPageMethod() const
222 {
223     return m_addPageMethod;
224 }
225 
setAddPageMethod(const QString & m)226 void WidgetDataBaseItem::setAddPageMethod(const QString &m)
227 {
228     m_addPageMethod = m;
229 }
230 
clone(const QDesignerWidgetDataBaseItemInterface * item)231 WidgetDataBaseItem *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 // ----------------------------------------------------------
WidgetDataBase(QDesignerFormEditorInterface * core,QObject * parent)251 WidgetDataBase::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(new WidgetDataBaseItem(QString::fromUtf8("Line")));
267     append(new WidgetDataBaseItem(QString::fromUtf8("Spacer")));
268     append(new WidgetDataBaseItem(QString::fromUtf8("QSplitter")));
269     append(new WidgetDataBaseItem(QString::fromUtf8("QLayoutWidget")));
270     // QDesignerWidget is used as central widget and as container for tab widgets, etc.
271     WidgetDataBaseItem *designerWidgetItem = new WidgetDataBaseItem(QString::fromUtf8("QDesignerWidget"));
272     designerWidgetItem->setContainer(true);
273     append(designerWidgetItem);
274     append(new WidgetDataBaseItem(QString::fromUtf8("QDesignerDialog")));
275     append(new WidgetDataBaseItem(QString::fromUtf8("QDesignerMenu")));
276     append(new WidgetDataBaseItem(QString::fromUtf8("QDesignerMenuBar")));
277     append(new WidgetDataBaseItem(QString::fromUtf8("QDesignerDockWidget")));
278     append(new WidgetDataBaseItem(QString::fromUtf8("QAction")));
279     append(new WidgetDataBaseItem(QString::fromUtf8("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(indexOfClassName(QStringLiteral("QTabWidget")))->setContainer(true);
289     item(indexOfClassName(QStringLiteral("QGroupBox")))->setContainer(true);
290     item(indexOfClassName(QStringLiteral("QScrollArea")))->setContainer(true);
291     item(indexOfClassName(QStringLiteral("QStackedWidget")))->setContainer(true);
292     item(indexOfClassName(QStringLiteral("QToolBox")))->setContainer(true);
293     item(indexOfClassName(QStringLiteral("QFrame")))->setContainer(true);
294     item(indexOfClassName(QStringLiteral("QLayoutWidget")))->setContainer(true);
295     item(indexOfClassName(QStringLiteral("QDesignerWidget")))->setContainer(true);
296     item(indexOfClassName(QStringLiteral("QDesignerDialog")))->setContainer(true);
297     item(indexOfClassName(QStringLiteral("QSplitter")))->setContainer(true);
298     item(indexOfClassName(QStringLiteral("QMainWindow")))->setContainer(true);
299     item(indexOfClassName(QStringLiteral("QDockWidget")))->setContainer(true);
300     item(indexOfClassName(QStringLiteral("QDesignerDockWidget")))->setContainer(true);
301     item(indexOfClassName(QStringLiteral("QMdiArea")))->setContainer(true);
302     item(indexOfClassName(QStringLiteral("QWizard")))->setContainer(true);
303     item(indexOfClassName(QStringLiteral("QWizardPage")))->setContainer(true);
304 
305     item(indexOfClassName(QStringLiteral("QWidget")))->setContainer(true);
306     item(indexOfClassName(QStringLiteral("QDialog")))->setContainer(true);
307 }
308 
309 WidgetDataBase::~WidgetDataBase() = default;
310 
core() const311 QDesignerFormEditorInterface *WidgetDataBase::core() const
312 {
313     return m_core;
314 }
315 
indexOfObject(QObject * object,bool) const316 int WidgetDataBase::indexOfObject(QObject *object, bool /*resolveName*/) const
317 {
318     QExtensionManager *mgr = m_core->extensionManager();
319     QDesignerLanguageExtension *lang = qt_extension<QDesignerLanguageExtension*> (mgr, m_core);
320 
321     QString id;
322 
323     if (lang)
324         id = lang->classNameOf(object);
325 
326     if (id.isEmpty())
327         id = WidgetFactory::classNameOf(m_core,object);
328 
329     return QDesignerWidgetDataBaseInterface::indexOfClassName(id);
330 }
331 
createCustomWidgetItem(const QDesignerCustomWidgetInterface * c,const QDesignerCustomWidgetData & data)332 static 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 
loadPlugins()348 void 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(item->name(), i);
361         else
362             nonCustomClasses.insert(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, pm->customWidgetData(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(pluginList)) {
378             const QString pluginName = pluginItem->name();
379             NameIndexMap::iterator existingIt = existingCustomClasses.find(pluginName);
380             if (existingIt == existingCustomClasses.end()) {
381                 // Add new class.
382                 if (nonCustomClasses.contains(pluginName)) {
383                     designerWarning(tr("A custom widget plugin whose class name (%1) matches that of an existing class has been found.").arg(pluginName));
384                 } else {
385                     append(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(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(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 
remove(int index)414 void WidgetDataBase::remove(int index)
415 {
416     Q_ASSERT(index < m_items.size());
417     delete m_items.takeAt(index);
418 }
419 
defaultPropertyValues(const QString & name)420 QList<QVariant> WidgetDataBase::defaultPropertyValues(const QString &name)
421 {
422     WidgetFactory *factory = qobject_cast<WidgetFactory *>(m_core->widgetFactory());
423     Q_ASSERT(factory);
424     // Create non-widgets, widgets in order
425     QObject* object = factory->createObject(name, nullptr);
426     if (!object)
427         object = factory->createWidget(name, 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*>(m_core->extensionManager(), object)) {
435         const int propertyCount = sheet->count();
436         for (int i = 0; i < propertyCount; ++i) {
437             result.append(sheet->property(i));
438         }
439     }
440     delete object;
441     return result;
442 }
443 
grabDefaultPropertyValues()444 void WidgetDataBase::grabDefaultPropertyValues()
445 {
446     const int itemCount = count();
447     for (int i = 0; i < itemCount; ++i) {
448         QDesignerWidgetDataBaseItemInterface *dbItem = item(i);
449         const auto default_prop_values = defaultPropertyValues(dbItem->name());
450         dbItem->setDefaultPropertyValues(default_prop_values);
451     }
452 }
453 
grabStandardWidgetBoxIcons()454 void 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 *>(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(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(name, QStringLiteral("Containers")));
469                 } else {
470                     dbItem->setIcon(wb->iconForWidget(name));
471                 }
472             }
473         }
474     }
475 }
476 
477 // --------------------- Functions relevant generation of new forms based on widgets (apart from the standard templates)
478 
479 enum { NewFormWidth = 400, NewFormHeight = 300 };
480 
481 // Check if class is suitable to generate a form from
isExistingTemplate(const QString & className)482 static 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
suitableForNewForm(const QString & className)488 static 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.
formWidgetClasses(const QDesignerFormEditorInterface * core)501 QStringList 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(i);
509             if (item->isContainer() && !item->isCustom() && !item->isPromoted()) {
510                 const QString name = item->name(); // Standard Widgets: no existing templates
511                 if (!isExistingTemplate(name) && suitableForNewForm(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.
customFormWidgetClasses(const QDesignerFormEditorInterface * core)521 QStringList 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(i);
528         if (item->isContainer() && item->isCustom() && !item->isPromoted()) {
529             if (suitableForNewForm(item->name()) && suitableForNewForm(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
xmlFromWidgetBox(const QDesignerFormEditorInterface * core,const QString & className,const QString & objectName)538 static 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(core->widgetBox(), className, QString(), &widget);
544     if (!found)
545         return QString();
546     QScopedPointer<DomUI> domUI(QDesignerWidgetBox::xmlToUi(className, widget.domXml(), 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(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.
generateNewFormXML(const QString & className,const QString & similarClassName,const QString & name)600 static 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().
formTemplate(const QDesignerFormEditorInterface * core,const QString & className,const QString & objectName)622 QString 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, similarClass, objectName);
640     return rc;
641 }
642 
643 // Set a fixed size on a XML template
scaleFormTemplate(const QString & xml,const QSize & size,bool fixed)644 QString WidgetDataBase::scaleFormTemplate(const QString &xml, const QSize &size, bool fixed)
645 {
646     QScopedPointer<DomUI> domUI(QDesignerWidgetBox::xmlToUi(QStringLiteral("Form"), xml, 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(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(minimumSizeProperty);
686         }
687         if (!maximumSizeProperty) {
688             maximumSizeProperty = new DomProperty;
689             maximumSizeProperty->setAttributeName(maximumSizePropertyName);
690             maximumSizeProperty->setElementSize(new DomSize);
691             properties.push_back(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
includeSpecification(QString includeFile)728 QDESIGNER_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(includeFile.size() - 1, 1);
735         includeFile.remove(0, 1);
736     }
737     return IncludeSpecification(includeFile, global ? IncludeGlobal : IncludeLocal);
738 }
739 
buildIncludeFile(QString includeFile,IncludeType includeType)740 QDESIGNER_SHARED_EXPORT QString buildIncludeFile(QString includeFile, IncludeType includeType) {
741     if (includeType == IncludeGlobal && !includeFile.isEmpty()) {
742         includeFile.append(QLatin1Char('>'));
743         includeFile.insert(0, 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 
755 QDESIGNER_SHARED_EXPORT QDesignerWidgetDataBaseItemInterface *
appendDerived(QDesignerWidgetDataBaseInterface * db,const QString & className,const QString & group,const QString & baseClassName,const QString & includeFile,bool promoted,bool custom)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("** 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(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(QCoreApplication::translate("WidgetDataBase",
789           "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(className, baseClassName, existingBaseClass));
793         return derivedItem;
794     }
795     // Create this item, inheriting its base properties
796     const int baseIndex = db->indexOfClassName(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(baseIndex);
803     derivedItem = WidgetDataBaseItem::clone(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(derivedItem);
817     return derivedItem;
818 }
819 
820 /* Return a list of database items to which a class can be promoted to. */
821 
822 QDESIGNER_SHARED_EXPORT WidgetDataBaseItemList
promotionCandidates(const QDesignerWidgetDataBaseInterface * db,const QString & baseClassName)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(i);
831         if (item->isPromoted() && item->extends() == baseClassName) {
832             rc.push_back(item);
833         }
834     }
835     return rc;
836 }
837 } // namespace qdesigner_internal
838 
839 QT_END_NAMESPACE
840