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