/**************************************************************************** | |
** | |
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). | |
** All rights reserved. | |
** Contact: Nokia Corporation (qt-info@nokia.com) | |
** | |
** This file is part of the Qt Designer of the Qt Toolkit. | |
** | |
** $QT_BEGIN_LICENSE:LGPL$ | |
** GNU Lesser General Public License Usage | |
** This file may be used under the terms of the GNU Lesser General Public | |
** License version 2.1 as published by the Free Software Foundation and | |
** appearing in the file LICENSE.LGPL included in the packaging of this | |
** file. Please review the following information to ensure the GNU Lesser | |
** General Public License version 2.1 requirements will be met: | |
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. | |
** | |
** In addition, as a special exception, Nokia gives you certain additional | |
** rights. These rights are described in the Nokia Qt LGPL Exception | |
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. | |
** | |
** GNU General Public License Usage | |
** Alternatively, this file may be used under the terms of the GNU General | |
** Public License version 3.0 as published by the Free Software Foundation | |
** and appearing in the file LICENSE.GPL included in the packaging of this | |
** file. Please review the following information to ensure the GNU General | |
** Public License version 3.0 requirements will be met: | |
** http://www.gnu.org/copyleft/gpl.html. | |
** | |
** Other Usage | |
** Alternatively, this file may be used in accordance with the terms and | |
** conditions contained in a signed written agreement between you and Nokia. | |
** | |
** | |
** | |
** | |
** | |
** $QT_END_LICENSE$ | |
** | |
****************************************************************************/ | |
#include "widgetdatabase_p.h" | |
#include "widgetfactory_p.h" | |
#include "spacer_widget_p.h" | |
#include "abstractlanguage.h" | |
#include "pluginmanager_p.h" | |
#include "qdesigner_widgetbox_p.h" | |
#include "qdesigner_utils_p.h" | |
#include <ui4_p.h> | |
#include <QtDesigner/customwidget.h> | |
#include <QtDesigner/propertysheet.h> | |
#include <QtDesigner/QExtensionManager> | |
#include <QtDesigner/QDesignerFormEditorInterface> | |
#include <QtXml/QXmlStreamWriter> | |
#include <QtCore/QtAlgorithms> | |
#include <QtCore/qdebug.h> | |
#include <QtCore/QMetaProperty> | |
#include <QtCore/QTextStream> | |
#include <QtCore/QRegExp> | |
#include <QtCore/QCoreApplication> | |
QT_BEGIN_NAMESPACE | |
namespace { | |
enum { debugWidgetDataBase = 0 }; | |
} | |
namespace qdesigner_internal { | |
// ---------------------------------------------------------- | |
WidgetDataBaseItem::WidgetDataBaseItem(const QString &name, const QString &group) | |
: m_name(name), | |
m_group(group), | |
m_compat(0), | |
m_container(0), | |
m_form(0), | |
m_custom(0), | |
m_promoted(0) | |
{ | |
} | |
QString WidgetDataBaseItem::name() const | |
{ | |
return m_name; | |
} | |
void WidgetDataBaseItem::setName(const QString &name) | |
{ | |
m_name = name; | |
} | |
QString WidgetDataBaseItem::group() const | |
{ | |
return m_group; | |
} | |
void WidgetDataBaseItem::setGroup(const QString &group) | |
{ | |
m_group = group; | |
} | |
QString WidgetDataBaseItem::toolTip() const | |
{ | |
return m_toolTip; | |
} | |
void WidgetDataBaseItem::setToolTip(const QString &toolTip) | |
{ | |
m_toolTip = toolTip; | |
} | |
QString WidgetDataBaseItem::whatsThis() const | |
{ | |
return m_whatsThis; | |
} | |
void WidgetDataBaseItem::setWhatsThis(const QString &whatsThis) | |
{ | |
m_whatsThis = whatsThis; | |
} | |
QString WidgetDataBaseItem::includeFile() const | |
{ | |
return m_includeFile; | |
} | |
void WidgetDataBaseItem::setIncludeFile(const QString &includeFile) | |
{ | |
m_includeFile = includeFile; | |
} | |
QIcon WidgetDataBaseItem::icon() const | |
{ | |
return m_icon; | |
} | |
void WidgetDataBaseItem::setIcon(const QIcon &icon) | |
{ | |
m_icon = icon; | |
} | |
bool WidgetDataBaseItem::isCompat() const | |
{ | |
return m_compat; | |
} | |
void WidgetDataBaseItem::setCompat(bool b) | |
{ | |
m_compat = b; | |
} | |
bool WidgetDataBaseItem::isContainer() const | |
{ | |
return m_container; | |
} | |
void WidgetDataBaseItem::setContainer(bool b) | |
{ | |
m_container = b; | |
} | |
bool WidgetDataBaseItem::isCustom() const | |
{ | |
return m_custom; | |
} | |
void WidgetDataBaseItem::setCustom(bool b) | |
{ | |
m_custom = b; | |
} | |
QString WidgetDataBaseItem::pluginPath() const | |
{ | |
return m_pluginPath; | |
} | |
void WidgetDataBaseItem::setPluginPath(const QString &path) | |
{ | |
m_pluginPath = path; | |
} | |
bool WidgetDataBaseItem::isPromoted() const | |
{ | |
return m_promoted; | |
} | |
void WidgetDataBaseItem::setPromoted(bool b) | |
{ | |
m_promoted = b; | |
} | |
QString WidgetDataBaseItem::extends() const | |
{ | |
return m_extends; | |
} | |
void WidgetDataBaseItem::setExtends(const QString &s) | |
{ | |
m_extends = s; | |
} | |
void WidgetDataBaseItem::setDefaultPropertyValues(const QList<QVariant> &list) | |
{ | |
m_defaultPropertyValues = list; | |
} | |
QList<QVariant> WidgetDataBaseItem::defaultPropertyValues() const | |
{ | |
return m_defaultPropertyValues; | |
} | |
QStringList WidgetDataBaseItem::fakeSlots() const | |
{ | |
return m_fakeSlots; | |
} | |
void WidgetDataBaseItem::setFakeSlots(const QStringList &fs) | |
{ | |
m_fakeSlots = fs; | |
} | |
QStringList WidgetDataBaseItem::fakeSignals() const | |
{ | |
return m_fakeSignals; | |
} | |
void WidgetDataBaseItem::setFakeSignals(const QStringList &fs) | |
{ | |
m_fakeSignals = fs; | |
} | |
QString WidgetDataBaseItem::addPageMethod() const | |
{ | |
return m_addPageMethod; | |
} | |
void WidgetDataBaseItem::setAddPageMethod(const QString &m) | |
{ | |
m_addPageMethod = m; | |
} | |
WidgetDataBaseItem *WidgetDataBaseItem::clone(const QDesignerWidgetDataBaseItemInterface *item) | |
{ | |
WidgetDataBaseItem *rc = new WidgetDataBaseItem(item->name(), item->group()); | |
rc->setToolTip(item->toolTip()); | |
rc->setWhatsThis(item->whatsThis()); | |
rc->setIncludeFile(item->includeFile()); | |
rc->setIcon(item->icon()); | |
rc->setCompat(item->isCompat()); | |
rc->setContainer(item->isContainer()); | |
rc->setCustom(item->isCustom() ); | |
rc->setPluginPath(item->pluginPath()); | |
rc->setPromoted(item->isPromoted()); | |
rc->setExtends(item->extends()); | |
rc->setDefaultPropertyValues(item->defaultPropertyValues()); | |
// container page method, fake slots and signals ignored here.y | |
return rc; | |
} | |
// ---------------------------------------------------------- | |
WidgetDataBase::WidgetDataBase(QDesignerFormEditorInterface *core, QObject *parent) | |
: QDesignerWidgetDataBaseInterface(parent), | |
m_core(core) | |
{ | |
#define DECLARE_LAYOUT(L, C) | |
#define DECLARE_COMPAT_WIDGET(W, C) DECLARE_WIDGET(W, C) | |
#define DECLARE_WIDGET(W, C) append(new WidgetDataBaseItem(QString::fromUtf8(#W))); | |
#include "widgets.table" | |
#undef DECLARE_COMPAT_WIDGET | |
#undef DECLARE_LAYOUT | |
#undef DECLARE_WIDGET | |
#undef DECLARE_WIDGET_1 | |
append(new WidgetDataBaseItem(QString::fromUtf8("Line"))); | |
append(new WidgetDataBaseItem(QString::fromUtf8("Spacer"))); | |
append(new WidgetDataBaseItem(QString::fromUtf8("QSplitter"))); | |
append(new WidgetDataBaseItem(QString::fromUtf8("QLayoutWidget"))); | |
// QDesignerWidget is used as central widget and as container for tab widgets, etc. | |
WidgetDataBaseItem *designerWidgetItem = new WidgetDataBaseItem(QString::fromUtf8("QDesignerWidget")); | |
designerWidgetItem->setContainer(true); | |
append(designerWidgetItem); | |
append(new WidgetDataBaseItem(QString::fromUtf8("QDesignerDialog"))); | |
append(new WidgetDataBaseItem(QString::fromUtf8("QDesignerMenu"))); | |
append(new WidgetDataBaseItem(QString::fromUtf8("QDesignerMenuBar"))); | |
append(new WidgetDataBaseItem(QString::fromUtf8("QDesignerDockWidget"))); | |
append(new WidgetDataBaseItem(QString::fromUtf8("QDesignerQ3WidgetStack"))); | |
append(new WidgetDataBaseItem(QString::fromUtf8("QAction"))); | |
append(new WidgetDataBaseItem(QString::fromUtf8("QButtonGroup"))); | |
// ### remove me | |
// ### check the casts | |
#if 0 // ### enable me after 4.1 | |
item(indexOfClassName(QLatin1String("QToolBar")))->setContainer(true); | |
#endif | |
item(indexOfClassName(QLatin1String("QTabWidget")))->setContainer(true); | |
item(indexOfClassName(QLatin1String("QGroupBox")))->setContainer(true); | |
item(indexOfClassName(QLatin1String("QScrollArea")))->setContainer(true); | |
item(indexOfClassName(QLatin1String("QStackedWidget")))->setContainer(true); | |
item(indexOfClassName(QLatin1String("QToolBox")))->setContainer(true); | |
item(indexOfClassName(QLatin1String("QFrame")))->setContainer(true); | |
item(indexOfClassName(QLatin1String("QLayoutWidget")))->setContainer(true); | |
item(indexOfClassName(QLatin1String("QDesignerWidget")))->setContainer(true); | |
item(indexOfClassName(QLatin1String("QDesignerDialog")))->setContainer(true); | |
item(indexOfClassName(QLatin1String("QSplitter")))->setContainer(true); | |
item(indexOfClassName(QLatin1String("QMainWindow")))->setContainer(true); | |
item(indexOfClassName(QLatin1String("QDockWidget")))->setContainer(true); | |
item(indexOfClassName(QLatin1String("QDesignerDockWidget")))->setContainer(true); | |
item(indexOfClassName(QLatin1String("QDesignerQ3WidgetStack")))->setContainer(true); | |
item(indexOfClassName(QLatin1String("QMdiArea")))->setContainer(true); | |
item(indexOfClassName(QLatin1String("QWorkspace")))->setContainer(true); | |
item(indexOfClassName(QLatin1String("QWizard")))->setContainer(true); | |
item(indexOfClassName(QLatin1String("QWizardPage")))->setContainer(true); | |
item(indexOfClassName(QLatin1String("QWidget")))->setContainer(true); | |
item(indexOfClassName(QLatin1String("QDialog")))->setContainer(true); | |
} | |
WidgetDataBase::~WidgetDataBase() | |
{ | |
} | |
QDesignerFormEditorInterface *WidgetDataBase::core() const | |
{ | |
return m_core; | |
} | |
int WidgetDataBase::indexOfObject(QObject *object, bool /*resolveName*/) const | |
{ | |
QExtensionManager *mgr = m_core->extensionManager(); | |
QDesignerLanguageExtension *lang = qt_extension<QDesignerLanguageExtension*> (mgr, m_core); | |
QString id; | |
if (lang) | |
id = lang->classNameOf(object); | |
if (id.isEmpty()) | |
id = WidgetFactory::classNameOf(m_core,object); | |
return QDesignerWidgetDataBaseInterface::indexOfClassName(id); | |
} | |
static WidgetDataBaseItem *createCustomWidgetItem(const QDesignerCustomWidgetInterface *c, | |
const QDesignerCustomWidgetData &data) | |
{ | |
WidgetDataBaseItem *item = new WidgetDataBaseItem(c->name(), c->group()); | |
item->setContainer(c->isContainer()); | |
item->setCustom(true); | |
item->setIcon(c->icon()); | |
item->setIncludeFile(c->includeFile()); | |
item->setToolTip(c->toolTip()); | |
item->setWhatsThis(c->whatsThis()); | |
item->setPluginPath(data.pluginPath()); | |
item->setAddPageMethod(data.xmlAddPageMethod()); | |
item->setExtends(data.xmlExtends()); | |
return item; | |
} | |
void WidgetDataBase::loadPlugins() | |
{ | |
typedef QMap<QString, int> NameIndexMap; | |
typedef QList<QDesignerWidgetDataBaseItemInterface*> ItemList; | |
typedef QMap<QString, QDesignerWidgetDataBaseItemInterface*> NameItemMap; | |
typedef QSet<QString> NameSet; | |
// 1) create a map of existing custom classes | |
NameIndexMap existingCustomClasses; | |
NameSet nonCustomClasses; | |
const int count = m_items.size(); | |
for (int i = 0; i < count; i++) { | |
const QDesignerWidgetDataBaseItemInterface* item = m_items[i]; | |
if (item->isCustom() && !item->isPromoted()) | |
existingCustomClasses.insert(item->name(), i); | |
else | |
nonCustomClasses.insert(item->name()); | |
} | |
// 2) create a list plugins | |
ItemList pluginList; | |
const QDesignerPluginManager *pm = m_core->pluginManager(); | |
foreach(QDesignerCustomWidgetInterface* c, pm->registeredCustomWidgets()) | |
pluginList += createCustomWidgetItem(c, pm->customWidgetData(c)); | |
// 3) replace custom classes or add new ones, remove them from existingCustomClasses, | |
// leaving behind deleted items | |
unsigned replacedPlugins = 0; | |
unsigned addedPlugins = 0; | |
unsigned removedPlugins = 0; | |
if (!pluginList.empty()) { | |
ItemList::const_iterator cend = pluginList.constEnd(); | |
for (ItemList::const_iterator it = pluginList.constBegin();it != cend; ++it ) { | |
QDesignerWidgetDataBaseItemInterface* pluginItem = *it; | |
const QString pluginName = pluginItem->name(); | |
NameIndexMap::iterator existingIt = existingCustomClasses.find(pluginName); | |
if (existingIt == existingCustomClasses.end()) { | |
// Add new class. | |
if (nonCustomClasses.contains(pluginName)) { | |
designerWarning(tr("A custom widget plugin whose class name (%1) matches that of an existing class has been found.").arg(pluginName)); | |
} else { | |
append(pluginItem); | |
addedPlugins++; | |
} | |
} else { | |
// replace existing info | |
const int existingIndex = existingIt.value(); | |
delete m_items[existingIndex]; | |
m_items[existingIndex] = pluginItem; | |
existingCustomClasses.erase(existingIt); | |
replacedPlugins++; | |
} | |
} | |
} | |
// 4) remove classes that have not been matched. The stored indexes become invalid while deleting. | |
if (!existingCustomClasses.empty()) { | |
NameIndexMap::const_iterator cend = existingCustomClasses.constEnd(); | |
for (NameIndexMap::const_iterator it = existingCustomClasses.constBegin();it != cend; ++it ) { | |
const int index = indexOfClassName(it.key()); | |
if (index != -1) { | |
remove(index); | |
removedPlugins++; | |
} | |
} | |
} | |
if (debugWidgetDataBase) | |
qDebug() << "WidgetDataBase::loadPlugins(): " << addedPlugins << " added, " << replacedPlugins << " replaced, " << removedPlugins << "deleted."; | |
} | |
void WidgetDataBase::remove(int index) | |
{ | |
Q_ASSERT(index < m_items.size()); | |
delete m_items.takeAt(index); | |
} | |
QList<QVariant> WidgetDataBase::defaultPropertyValues(const QString &name) | |
{ | |
WidgetFactory *factory = qobject_cast<WidgetFactory *>(m_core->widgetFactory()); | |
Q_ASSERT(factory); | |
// Create non-widgets, widgets in order | |
QObject* object = factory->createObject(name, 0); | |
if (!object) | |
object = factory->createWidget(name, 0); | |
if (!object) { | |
qDebug() << "** WARNING Factory failed to create " << name; | |
return QList<QVariant>(); | |
} | |
// Get properties from sheet. | |
QList<QVariant> result; | |
if (const QDesignerPropertySheetExtension *sheet = qt_extension<QDesignerPropertySheetExtension*>(m_core->extensionManager(), object)) { | |
const int propertyCount = sheet->count(); | |
for (int i = 0; i < propertyCount; ++i) { | |
result.append(sheet->property(i)); | |
} | |
} | |
delete object; | |
return result; | |
} | |
void WidgetDataBase::grabDefaultPropertyValues() | |
{ | |
const int itemCount = count(); | |
for (int i = 0; i < itemCount; ++i) { | |
QDesignerWidgetDataBaseItemInterface *dbItem = item(i); | |
const QList<QVariant> default_prop_values = defaultPropertyValues(dbItem->name()); | |
dbItem->setDefaultPropertyValues(default_prop_values); | |
} | |
} | |
void WidgetDataBase::grabStandardWidgetBoxIcons() | |
{ | |
// At this point, grab the default icons for the non-custom widgets from | |
// the widget box. They will show up in the object inspector. | |
if (const QDesignerWidgetBox *wb = qobject_cast<const QDesignerWidgetBox *>(m_core->widgetBox())) { | |
const QString qWidgetClass = QLatin1String("QWidget"); | |
const int itemCount = count(); | |
for (int i = 0; i < itemCount; ++i) { | |
QDesignerWidgetDataBaseItemInterface *dbItem = item(i); | |
if (!dbItem->isCustom() && dbItem->icon().isNull()) { | |
// Careful not to catch the layout icons when looking for | |
// QWidget | |
const QString name = dbItem->name(); | |
if (name == qWidgetClass) { | |
dbItem->setIcon(wb->iconForWidget(name, QLatin1String("Containers"))); | |
} else { | |
dbItem->setIcon(wb->iconForWidget(name)); | |
} | |
} | |
} | |
} | |
} | |
// --------------------- Functions relevant generation of new forms based on widgets (apart from the standard templates) | |
enum { NewFormWidth = 400, NewFormHeight = 300 }; | |
// Check if class is suitable to generate a form from | |
static inline bool isExistingTemplate(const QString &className) | |
{ | |
return className == QLatin1String("QWidget") || className == QLatin1String("QDialog") || className == QLatin1String("QMainWindow"); | |
} | |
// Check if class is suitable to generate a form from | |
static inline bool suitableForNewForm(const QString &className) | |
{ | |
if (className.isEmpty()) // Missing custom widget information | |
return false; | |
if (className == QLatin1String("QWorkspace")) | |
return false; | |
if (className == QLatin1String("QSplitter")) | |
return false; | |
if (className.startsWith(QLatin1String("QDesigner")) || className.startsWith(QLatin1String("Q3")) || className.startsWith(QLatin1String("QLayout"))) | |
return false; | |
return true; | |
} | |
// Return a list of widget classes from which new forms can be generated. | |
// Suitable for 'New form' wizards in integrations. | |
QStringList WidgetDataBase::formWidgetClasses(const QDesignerFormEditorInterface *core) | |
{ | |
static QStringList rc; | |
if (rc.empty()) { | |
const QDesignerWidgetDataBaseInterface *wdb = core->widgetDataBase(); | |
const int widgetCount = wdb->count(); | |
for (int i = 0; i < widgetCount; i++) { | |
const QDesignerWidgetDataBaseItemInterface *item = wdb->item(i); | |
if (item->isContainer() && !item->isCustom() && !item->isPromoted()) { | |
const QString name = item->name(); // Standard Widgets: no existing templates | |
if (!isExistingTemplate(name) && suitableForNewForm(name)) | |
rc += name; | |
} | |
} | |
} | |
return rc; | |
} | |
// Return a list of custom widget classes from which new forms can be generated. | |
// Suitable for 'New form' wizards in integrations. | |
QStringList WidgetDataBase::customFormWidgetClasses(const QDesignerFormEditorInterface *core) | |
{ | |
QStringList rc; | |
const QDesignerWidgetDataBaseInterface *wdb = core->widgetDataBase(); | |
const int widgetCount = wdb->count(); | |
for (int i = 0; i < widgetCount; i++) { // Custom widgets: check name and base class. | |
const QDesignerWidgetDataBaseItemInterface *item = wdb->item(i); | |
if (item->isContainer() && item->isCustom() && !item->isPromoted()) { | |
if (suitableForNewForm(item->name()) && suitableForNewForm(item->extends())) | |
rc += item->name(); | |
} | |
} | |
return rc; | |
} | |
// Get XML for a new form from the widget box. Change objectName/geometry | |
// properties to be suitable for new forms | |
static QString xmlFromWidgetBox(const QDesignerFormEditorInterface *core, const QString &className, const QString &objectName) | |
{ | |
typedef QList<DomProperty*> PropertyList; | |
QDesignerWidgetBoxInterface::Widget widget; | |
const bool found = QDesignerWidgetBox::findWidget(core->widgetBox(), className, QString(), &widget); | |
if (!found) | |
return QString(); | |
DomUI *domUI = QDesignerWidgetBox::xmlToUi(className, widget.domXml(), false); | |
domUI->setAttributeVersion(QLatin1String("4.0")); | |
if (!domUI) | |
return QString(); | |
DomWidget *domWidget = domUI->elementWidget(); | |
if (!domWidget) | |
return QString(); | |
// Properties: Remove the "objectName" property in favour of the name attribute and check geometry. | |
domWidget->setAttributeName(objectName); | |
const QString geometryProperty = QLatin1String("geometry"); | |
const QString objectNameProperty = QLatin1String("objectName"); | |
PropertyList properties = domWidget->elementProperty(); | |
for (PropertyList::iterator it = properties.begin(); it != properties.end(); ) { | |
DomProperty *property = *it; | |
if (property->attributeName() == objectNameProperty) { // remove "objectName" | |
it = properties.erase(it); | |
delete property; | |
} else { | |
if (property->attributeName() == geometryProperty) { // Make sure form is at least 400, 300 | |
if (DomRect *geom = property->elementRect()) { | |
if (geom->elementWidth() < NewFormWidth) | |
geom->setElementWidth(NewFormWidth); | |
if (geom->elementHeight() < NewFormHeight) | |
geom->setElementHeight(NewFormHeight); | |
} | |
} | |
++it; | |
} | |
} | |
// Add a window title property | |
DomString *windowTitleString = new DomString; | |
windowTitleString->setText(objectName); | |
DomProperty *windowTitleProperty = new DomProperty; | |
windowTitleProperty->setAttributeName(QLatin1String("windowTitle")); | |
windowTitleProperty->setElementString(windowTitleString); | |
properties.push_back(windowTitleProperty); | |
// ------ | |
domWidget->setElementProperty(properties); | |
// Embed in in DomUI and get string. Omit the version number. | |
domUI->setElementClass(objectName); | |
QString rc; | |
{ // Serialize domUI | |
QXmlStreamWriter writer(&rc); | |
writer.setAutoFormatting(true); | |
writer.setAutoFormattingIndent(1); | |
writer.writeStartDocument(); | |
domUI->write(writer); | |
writer.writeEndDocument(); | |
} | |
delete domUI; | |
return rc; | |
} | |
// Generate default standard ui new form xml based on the class passed on as similarClassName. | |
static QString generateNewFormXML(const QString &className, const QString &similarClassName, const QString &name) | |
{ | |
QString rc; { | |
QTextStream str(&rc); | |
str << QLatin1String("<ui version=\"4.0\" >\n<class>") << name << QLatin1String("</class>\n") | |
<< QLatin1String("<widget class=\"") << className << QLatin1String("\" name=\"") << name << QLatin1String("\" >\n") | |
<< QLatin1String("<property name=\"geometry\" >\n<rect><x>0</x><y>0</y><width>") | |
<< NewFormWidth << QLatin1String("</width><height>") << NewFormHeight << QLatin1String("</height></rect>\n</property>\n"); | |
str << QLatin1String("<property name=\"windowTitle\" >\n<string>") << name << QLatin1String("</string>\n</property>\n"); | |
if (similarClassName == QLatin1String("QMainWindow")) { | |
str << QLatin1String("<widget class=\"QWidget\" name=\"centralwidget\" />\n"); | |
} else { | |
if (similarClassName == QLatin1String("QWizard")) | |
str << QLatin1String("<widget class=\"QWizardPage\" name=\"wizardPage1\" /><widget class=\"QWizardPage\" name=\"wizardPage2\" />\n"); | |
} | |
str << QLatin1String("</widget>\n</ui>\n"); | |
} | |
return rc; | |
} | |
// Generate a form template using a class name obtained from formWidgetClasses(), customFormWidgetClasses(). | |
QString WidgetDataBase::formTemplate(const QDesignerFormEditorInterface *core, const QString &className, const QString &objectName) | |
{ | |
// How to find suitable XML for a class: | |
// 1) Look in widget box (as all the required centralwidgets, tab widget pages, etc. should be there). | |
const QString widgetBoxXml = xmlFromWidgetBox(core, className, objectName); | |
if (!widgetBoxXml.isEmpty()) | |
return widgetBoxXml; | |
// 2) If that fails, only custom main windows, custom dialogs and unsupported Qt Widgets should | |
// be left over. Generate something that is similar to the default templates. Find a similar class. | |
const QDesignerWidgetDataBaseInterface *wdb = core->widgetDataBase(); | |
QString similarClass = QLatin1String("QWidget"); | |
const int index = wdb->indexOfClassName(className); | |
if (index != -1) { | |
const QDesignerWidgetDataBaseItemInterface *item = wdb->item(index); | |
similarClass = item->isCustom() ? item->extends() : item->name(); | |
} | |
// Generate standard ui based on the class passed on as baseClassName. | |
const QString rc = generateNewFormXML(className, similarClass, objectName); | |
return rc; | |
} | |
// Set a fixed size on a XML template | |
QString WidgetDataBase::scaleFormTemplate(const QString &xml, const QSize &size, bool fixed) | |
{ | |
typedef QList<DomProperty*> PropertyList; | |
DomUI *domUI = QDesignerWidgetBox::xmlToUi(QLatin1String("Form"), xml, false); | |
if (!domUI) | |
return QString(); | |
DomWidget *domWidget = domUI->elementWidget(); | |
if (!domWidget) | |
return QString(); | |
// Properties: Find/Ensure the geometry, minimum and maximum sizes properties | |
const QString geometryPropertyName = QLatin1String("geometry"); | |
const QString minimumSizePropertyName = QLatin1String("minimumSize"); | |
const QString maximumSizePropertyName = QLatin1String("maximumSize"); | |
DomProperty *geomProperty = 0; | |
DomProperty *minimumSizeProperty = 0; | |
DomProperty *maximumSizeProperty = 0; | |
PropertyList properties = domWidget->elementProperty(); | |
const PropertyList::const_iterator cend = properties.constEnd(); | |
for (PropertyList::const_iterator it = properties.constBegin(); it != cend; ++it) { | |
const QString name = (*it)->attributeName(); | |
if (name == geometryPropertyName) { | |
geomProperty = *it; | |
} else { | |
if (name == minimumSizePropertyName) { | |
minimumSizeProperty = *it; | |
} else { | |
if (name == maximumSizePropertyName) | |
maximumSizeProperty = *it; | |
} | |
} | |
} | |
if (!geomProperty) { | |
geomProperty = new DomProperty; | |
geomProperty->setAttributeName(geometryPropertyName); | |
geomProperty->setElementRect(new DomRect); | |
properties.push_front(geomProperty); | |
} | |
if (fixed) { | |
if (!minimumSizeProperty) { | |
minimumSizeProperty = new DomProperty; | |
minimumSizeProperty->setAttributeName(minimumSizePropertyName); | |
minimumSizeProperty->setElementSize(new DomSize); | |
properties.push_back(minimumSizeProperty); | |
} | |
if (!maximumSizeProperty) { | |
maximumSizeProperty = new DomProperty; | |
maximumSizeProperty->setAttributeName(maximumSizePropertyName); | |
maximumSizeProperty->setElementSize(new DomSize); | |
properties.push_back(maximumSizeProperty); | |
} | |
} | |
// Set values of geometry, minimum and maximum sizes properties | |
const int width = size.width(); | |
const int height = size.height(); | |
if (DomRect *geom = geomProperty->elementRect()) { | |
geom->setElementWidth(width); | |
geom->setElementHeight(height); | |
} | |
if (fixed) { | |
if (DomSize *s = minimumSizeProperty->elementSize()) { | |
s->setElementWidth(width); | |
s->setElementHeight(height); | |
} | |
if (DomSize *s = maximumSizeProperty->elementSize()) { | |
s->setElementWidth(width); | |
s->setElementHeight(height); | |
} | |
} | |
// write back | |
domWidget->setElementProperty(properties); | |
QString rc; | |
{ // serialize domUI | |
QXmlStreamWriter writer(&rc); | |
writer.setAutoFormatting(true); | |
writer.setAutoFormattingIndent(1); | |
writer.writeStartDocument(); | |
domUI->write(writer); | |
writer.writeEndDocument(); | |
} | |
delete domUI; | |
return rc; | |
} | |
// ---- free functions | |
QDESIGNER_SHARED_EXPORT IncludeSpecification includeSpecification(QString includeFile) | |
{ | |
const bool global = !includeFile.isEmpty() && | |
includeFile[0] == QLatin1Char('<') && | |
includeFile[includeFile.size() - 1] == QLatin1Char('>'); | |
if (global) { | |
includeFile.remove(includeFile.size() - 1, 1); | |
includeFile.remove(0, 1); | |
} | |
return IncludeSpecification(includeFile, global ? IncludeGlobal : IncludeLocal); | |
} | |
QDESIGNER_SHARED_EXPORT QString buildIncludeFile(QString includeFile, IncludeType includeType) { | |
if (includeType == IncludeGlobal && !includeFile.isEmpty()) { | |
includeFile.append(QLatin1Char('>')); | |
includeFile.insert(0, QLatin1Char('<')); | |
} | |
return includeFile; | |
} | |
/* Appends a derived class to the database inheriting the data of the base class. Used | |
for custom and promoted widgets. | |
Depending on whether an entry exists, the existing or a newly created entry is | |
returned. A return value of 0 indicates that the base class could not be found. */ | |
QDESIGNER_SHARED_EXPORT QDesignerWidgetDataBaseItemInterface * | |
appendDerived(QDesignerWidgetDataBaseInterface *db, | |
const QString &className, const QString &group, | |
const QString &baseClassName, | |
const QString &includeFile, | |
bool promoted, bool custom) | |
{ | |
if (debugWidgetDataBase) | |
qDebug() << "appendDerived " << className << " derived from " << baseClassName; | |
// Check. | |
if (className.isEmpty() || baseClassName.isEmpty()) { | |
qWarning("** WARNING %s called with an empty class names: '%s' extends '%s'.", | |
Q_FUNC_INFO, className.toUtf8().constData(), baseClassName.toUtf8().constData()); | |
return 0; | |
} | |
// Check whether item already exists. | |
QDesignerWidgetDataBaseItemInterface *derivedItem = 0; | |
const int existingIndex = db->indexOfClassName(className); | |
if ( existingIndex != -1) | |
derivedItem = db->item(existingIndex); | |
if (derivedItem) { | |
// Check the existing item for base class mismatch. This will likely | |
// happen when loading a file written by an instance with missing plugins. | |
// In that case, just warn and ignore the file properties. | |
// | |
// An empty base class indicates that it is not known (for example, for custom plugins). | |
// In this case, the widget DB is later updated once the widget is created | |
// by DOM (by querying the metaobject). Suppress the warning. | |
const QString existingBaseClass = derivedItem->extends(); | |
if (existingBaseClass.isEmpty() || baseClassName == existingBaseClass) | |
return derivedItem; | |
// Warn about mismatches | |
designerWarning(QCoreApplication::translate("WidgetDataBase", | |
"The file contains a custom widget '%1' whose base class (%2)" | |
" differs from the current entry in the widget database (%3)." | |
" The widget database is left unchanged."). | |
arg(className, baseClassName, existingBaseClass)); | |
return derivedItem; | |
} | |
// Create this item, inheriting its base properties | |
const int baseIndex = db->indexOfClassName(baseClassName); | |
if (baseIndex == -1) { | |
if (debugWidgetDataBase) | |
qDebug() << "appendDerived failed due to missing base class"; | |
return 0; | |
} | |
const QDesignerWidgetDataBaseItemInterface *baseItem = db->item(baseIndex); | |
derivedItem = WidgetDataBaseItem::clone(baseItem); | |
// Sort of hack: If base class is QWidget, we most likely | |
// do not want to inherit the container attribute. | |
static const QString qWidgetName = QLatin1String("QWidget"); | |
if (baseItem->name() == qWidgetName) | |
derivedItem->setContainer(false); | |
// set new props | |
derivedItem->setName(className); | |
derivedItem->setGroup(group); | |
derivedItem->setCustom(custom); | |
derivedItem->setPromoted(promoted); | |
derivedItem->setExtends(baseClassName); | |
derivedItem->setIncludeFile(includeFile); | |
db->append(derivedItem); | |
return derivedItem; | |
} | |
/* Return a list of database items to which a class can be promoted to. */ | |
QDESIGNER_SHARED_EXPORT WidgetDataBaseItemList | |
promotionCandidates(const QDesignerWidgetDataBaseInterface *db, | |
const QString &baseClassName) | |
{ | |
WidgetDataBaseItemList rc; | |
// find existing promoted widgets deriving from base. | |
const int count = db->count(); | |
for (int i = 0; i < count; ++i) { | |
QDesignerWidgetDataBaseItemInterface *item = db->item(i); | |
if (item->isPromoted() && item->extends() == baseClassName) { | |
rc.push_back(item); | |
} | |
} | |
return rc; | |
} | |
} // namespace qdesigner_internal | |
QT_END_NAMESPACE |