866 lines
31 KiB
C++
866 lines
31 KiB
C++
|
/****************************************************************************
|
||
|
**
|
||
|
** Copyright (C) 2010 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$
|
||
|
** Commercial Usage
|
||
|
** Licensees holding valid Qt Commercial licenses may use this file in
|
||
|
** accordance with the Qt Commercial License Agreement provided with the
|
||
|
** Software or, alternatively, in accordance with the terms contained in
|
||
|
** a written agreement between you and Nokia.
|
||
|
**
|
||
|
** GNU Lesser General Public License Usage
|
||
|
** Alternatively, 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.
|
||
|
**
|
||
|
** If you have questions regarding the use of this file, please contact
|
||
|
** Nokia at qt-info@nokia.com.
|
||
|
** $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
|