diff options
Diffstat (limited to 'src/designer/src/components/formeditor/qdesigner_resource.cpp')
-rw-r--r-- | src/designer/src/components/formeditor/qdesigner_resource.cpp | 2475 |
1 files changed, 2475 insertions, 0 deletions
diff --git a/src/designer/src/components/formeditor/qdesigner_resource.cpp b/src/designer/src/components/formeditor/qdesigner_resource.cpp new file mode 100644 index 000000000..409a20e8d --- /dev/null +++ b/src/designer/src/components/formeditor/qdesigner_resource.cpp @@ -0,0 +1,2475 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdesigner_resource.h" +#include "formwindow.h" +#include "dynamicpropertysheet.h" +#include "qdesigner_tabwidget_p.h" +#include "qdesigner_toolbox_p.h" +#include "qdesigner_stackedbox_p.h" +#include "qdesigner_toolbar_p.h" +#include "qdesigner_dockwidget_p.h" +#include "qdesigner_menu_p.h" +#include "qdesigner_menubar_p.h" +#include "qdesigner_membersheet_p.h" +#include "qtresourcemodel_p.h" +#include "qmdiarea_container.h" +#include "qwizard_container.h" +#include "layout_propertysheet.h" + +#include <ui4_p.h> +#include <formbuilderextra_p.h> +#include <resourcebuilder_p.h> +#include <textbuilder_p.h> +#include <qdesigner_widgetitem_p.h> + +// shared +#include <widgetdatabase_p.h> +#include <metadatabase_p.h> +#include <layout_p.h> +#include <layoutinfo_p.h> +#include <spacer_widget_p.h> +#include <pluginmanager_p.h> +#include <widgetfactory_p.h> +#include <abstractlanguage.h> +#include <abstractintrospection_p.h> + +#include <qlayout_widget_p.h> +#include <qdesigner_utils_p.h> +#include <ui4_p.h> + +// sdk +#include <QtDesigner/QDesignerPropertySheetExtension> +#include <QtDesigner/QDesignerFormEditorInterface> +#include <QtDesigner/QDesignerExtraInfoExtension> +#include <QtDesigner/QDesignerFormWindowToolInterface> +#include <QtDesigner/QExtensionManager> +#include <QtDesigner/QDesignerContainerExtension> +#include <abstractdialoggui_p.h> + +#include <QtGui/QMenu> +#include <QtGui/QMessageBox> +#include <QtGui/QLayout> +#include <QtGui/QFormLayout> +#include <QtGui/QTabWidget> +#include <QtGui/QToolBox> +#include <QtGui/QStackedWidget> +#include <QtGui/QToolBar> +#include <QtGui/QTabBar> +#include <QtGui/QAction> +#include <QtGui/QActionGroup> +#include <QtGui/QButtonGroup> +#include <QtGui/QApplication> +#include <QtGui/QMainWindow> +#include <QtGui/QSplitter> +#include <QtGui/QMdiArea> +#include <QtGui/QWorkspace> +#include <QtGui/QMenuBar> +#include <QtGui/QFileDialog> +#include <QtGui/QHeaderView> +#include <QtGui/QWizardPage> +#include <private/qlayoutengine_p.h> + +#include <QtCore/QBuffer> +#include <QtCore/QDir> +#include <QtCore/QMetaProperty> +#include <QtCore/qdebug.h> +#include <QtCore/QXmlStreamWriter> + +Q_DECLARE_METATYPE(QWidgetList) + +QT_BEGIN_NAMESPACE + +namespace { + typedef QList<DomProperty*> DomPropertyList; +} + +static const char *currentUiVersion = "4.0"; +static const char *clipboardObjectName = "__qt_fake_top_level"; + +#define OLD_RESOURCE_FORMAT // Support pre 4.4 format. + +namespace qdesigner_internal { + +// -------------------- QDesignerResourceBuilder: A resource builder that works on the property sheet icon types. +class QDesignerResourceBuilder : public QResourceBuilder +{ +public: + QDesignerResourceBuilder(QDesignerFormEditorInterface *core, DesignerPixmapCache *pixmapCache, DesignerIconCache *iconCache); + + void setPixmapCache(DesignerPixmapCache *pixmapCache) { m_pixmapCache = pixmapCache; } + void setIconCache(DesignerIconCache *iconCache) { m_iconCache = iconCache; } + bool isSaveRelative() const { return m_saveRelative; } + void setSaveRelative(bool relative) { m_saveRelative = relative; } + QStringList usedQrcFiles() const { return m_usedQrcFiles.keys(); } +#ifdef OLD_RESOURCE_FORMAT + QStringList loadedQrcFiles() const { return m_loadedQrcFiles.keys(); } // needed only for loading old resource attribute of <iconset> tag. +#endif + + virtual QVariant loadResource(const QDir &workingDirectory, const DomProperty *icon) const; + + virtual QVariant toNativeValue(const QVariant &value) const; + + virtual DomProperty *saveResource(const QDir &workingDirectory, const QVariant &value) const; + + virtual bool isResourceType(const QVariant &value) const; +private: + + QDesignerFormEditorInterface *m_core; + DesignerPixmapCache *m_pixmapCache; + DesignerIconCache *m_iconCache; + const QDesignerLanguageExtension *m_lang; + bool m_saveRelative; + mutable QMap<QString, bool> m_usedQrcFiles; + mutable QMap<QString, bool> m_loadedQrcFiles; +}; + +QDesignerResourceBuilder::QDesignerResourceBuilder(QDesignerFormEditorInterface *core, DesignerPixmapCache *pixmapCache, DesignerIconCache *iconCache) : + m_core(core), + m_pixmapCache(pixmapCache), + m_iconCache(iconCache), + m_lang(qt_extension<QDesignerLanguageExtension *>(core->extensionManager(), core)), + m_saveRelative(true) +{ +} + +static inline void setIconPixmap(QIcon::Mode m, QIcon::State s, const QDir &workingDirectory, + QString path, PropertySheetIconValue &icon, + const QDesignerLanguageExtension *lang = 0) +{ + if (lang == 0 || !lang->isLanguageResource(path)) + path = QFileInfo(workingDirectory, path).absoluteFilePath(); + icon.setPixmap(m, s, PropertySheetPixmapValue(path)); +} + +QVariant QDesignerResourceBuilder::loadResource(const QDir &workingDirectory, const DomProperty *property) const +{ + switch (property->kind()) { + case DomProperty::Pixmap: { + PropertySheetPixmapValue pixmap; + DomResourcePixmap *dp = property->elementPixmap(); + if (!dp->text().isEmpty()) { + if (m_lang != 0 && m_lang->isLanguageResource(dp->text())) { + pixmap.setPath(dp->text()); + } else { + pixmap.setPath(QFileInfo(workingDirectory, dp->text()).absoluteFilePath()); + } +#ifdef OLD_RESOURCE_FORMAT + if (dp->hasAttributeResource()) + m_loadedQrcFiles.insert(QFileInfo(workingDirectory, dp->attributeResource()).absoluteFilePath(), false); +#endif + } + return QVariant::fromValue(pixmap); + } + + case DomProperty::IconSet: { + PropertySheetIconValue icon; + DomResourceIcon *di = property->elementIconSet(); + icon.setTheme(di->attributeTheme()); + if (const int flags = iconStateFlags(di)) { // new, post 4.4 format + if (flags & NormalOff) + setIconPixmap(QIcon::Normal, QIcon::Off, workingDirectory, di->elementNormalOff()->text(), icon, m_lang); + if (flags & NormalOn) + setIconPixmap(QIcon::Normal, QIcon::On, workingDirectory, di->elementNormalOn()->text(), icon, m_lang); + if (flags & DisabledOff) + setIconPixmap(QIcon::Disabled, QIcon::Off, workingDirectory, di->elementDisabledOff()->text(), icon, m_lang); + if (flags & DisabledOn) + setIconPixmap(QIcon::Disabled, QIcon::On, workingDirectory, di->elementDisabledOn()->text(), icon, m_lang); + if (flags & ActiveOff) + setIconPixmap(QIcon::Active, QIcon::Off, workingDirectory, di->elementActiveOff()->text(), icon, m_lang); + if (flags & ActiveOn) + setIconPixmap(QIcon::Active, QIcon::On, workingDirectory, di->elementActiveOn()->text(), icon, m_lang); + if (flags & SelectedOff) + setIconPixmap(QIcon::Selected, QIcon::Off, workingDirectory, di->elementSelectedOff()->text(), icon, m_lang); + if (flags & SelectedOn) + setIconPixmap(QIcon::Selected, QIcon::On, workingDirectory, di->elementSelectedOn()->text(), icon, m_lang); + } else { +#ifdef OLD_RESOURCE_FORMAT + setIconPixmap(QIcon::Normal, QIcon::Off, workingDirectory, di->text(), icon, m_lang); + if (di->hasAttributeResource()) + m_loadedQrcFiles.insert(QFileInfo(workingDirectory, di->attributeResource()).absoluteFilePath(), false); +#endif + } + return QVariant::fromValue(icon); + } + default: + break; + } + return QVariant(); +} + +QVariant QDesignerResourceBuilder::toNativeValue(const QVariant &value) const +{ + if (value.canConvert<PropertySheetPixmapValue>()) { + if (m_pixmapCache) + return m_pixmapCache->pixmap(qvariant_cast<PropertySheetPixmapValue>(value)); + } else if (value.canConvert<PropertySheetIconValue>()) { + if (m_iconCache) + return m_iconCache->icon(qvariant_cast<PropertySheetIconValue>(value)); + } + return value; +} + +DomProperty *QDesignerResourceBuilder::saveResource(const QDir &workingDirectory, const QVariant &value) const +{ + DomProperty *p = new DomProperty; + if (value.canConvert<PropertySheetPixmapValue>()) { + const PropertySheetPixmapValue pix = qvariant_cast<PropertySheetPixmapValue>(value); + DomResourcePixmap *rp = new DomResourcePixmap; + const QString pixPath = pix.path(); + switch (pix.pixmapSource(m_core)) { + case PropertySheetPixmapValue::LanguageResourcePixmap: + rp->setText(pixPath); + break; + case PropertySheetPixmapValue::ResourcePixmap: { + rp->setText(pixPath); + const QString qrcFile = m_core->resourceModel()->qrcPath(pixPath); + if (!qrcFile.isEmpty()) { + m_usedQrcFiles.insert(qrcFile, false); +#ifdef OLD_RESOURCE_FORMAT // Legacy: Add qrc path + rp->setAttributeResource(workingDirectory.relativeFilePath(qrcFile)); +#endif + } + } + break; + case PropertySheetPixmapValue::FilePixmap: + rp->setText(m_saveRelative ? workingDirectory.relativeFilePath(pixPath) : pixPath); + break; + } + p->setElementPixmap(rp); + return p; + } else if (value.canConvert<PropertySheetIconValue>()) { + const PropertySheetIconValue icon = qvariant_cast<PropertySheetIconValue>(value); + const QMap<QPair<QIcon::Mode, QIcon::State>, PropertySheetPixmapValue> pixmaps = icon.paths(); + const QString theme = icon.theme(); + if (!pixmaps.isEmpty() || !theme.isEmpty()) { + DomResourceIcon *ri = new DomResourceIcon; + if (!theme.isEmpty()) + ri->setAttributeTheme(theme); + QMapIterator<QPair<QIcon::Mode, QIcon::State>, PropertySheetPixmapValue> itPix(pixmaps); + while (itPix.hasNext()) { + const QIcon::Mode mode = itPix.next().key().first; + const QIcon::State state = itPix.key().second; + DomResourcePixmap *rp = new DomResourcePixmap; + const PropertySheetPixmapValue pix = itPix.value(); + const PropertySheetPixmapValue::PixmapSource ps = pix.pixmapSource(m_core); + const QString pixPath = pix.path(); + rp->setText(ps == PropertySheetPixmapValue::FilePixmap && m_saveRelative ? workingDirectory.relativeFilePath(pixPath) : pixPath); + if (state == QIcon::Off) { + switch (mode) { + case QIcon::Normal: + ri->setElementNormalOff(rp); +#ifdef OLD_RESOURCE_FORMAT // Legacy: Set Normal off as text/path in old format. + ri->setText(rp->text()); +#endif + if (ps == PropertySheetPixmapValue::ResourcePixmap) { + // Be sure that ri->text() file comes from active resourceSet (i.e. make appropriate + // resourceSet active before calling this method). + const QString qrcFile = m_core->resourceModel()->qrcPath(ri->text()); + if (!qrcFile.isEmpty()) { + m_usedQrcFiles.insert(qrcFile, false); +#ifdef OLD_RESOURCE_FORMAT // Legacy: Set Normal off as text/path in old format. + ri->setAttributeResource(workingDirectory.relativeFilePath(qrcFile)); +#endif + } + } + break; + case QIcon::Disabled: ri->setElementDisabledOff(rp); break; + case QIcon::Active: ri->setElementActiveOff(rp); break; + case QIcon::Selected: ri->setElementSelectedOff(rp); break; + } + } else { + switch (mode) { + case QIcon::Normal: ri->setElementNormalOn(rp); break; + case QIcon::Disabled: ri->setElementDisabledOn(rp); break; + case QIcon::Active: ri->setElementActiveOn(rp); break; + case QIcon::Selected: ri->setElementSelectedOn(rp); break; + } + } + } + p->setElementIconSet(ri); + return p; + } + } + delete p; + return 0; +} + +bool QDesignerResourceBuilder::isResourceType(const QVariant &value) const +{ + if (value.canConvert<PropertySheetPixmapValue>() || value.canConvert<PropertySheetIconValue>()) + return true; + return false; +} +// ------------------------- QDesignerTextBuilder +class QDesignerTextBuilder : public QTextBuilder +{ +public: + QDesignerTextBuilder() {} + + virtual QVariant loadText(const DomProperty *icon) const; + + virtual QVariant toNativeValue(const QVariant &value) const; + + virtual DomProperty *saveText(const QVariant &value) const; +}; + +QVariant QDesignerTextBuilder::loadText(const DomProperty *text) const +{ + const DomString *str = text->elementString(); + PropertySheetStringValue strVal(str->text()); + if (str->hasAttributeComment()) { + strVal.setDisambiguation(str->attributeComment()); + } + if (str->hasAttributeExtraComment()) { + strVal.setComment(str->attributeExtraComment()); + } + if (str->hasAttributeNotr()) { + const QString notr = str->attributeNotr(); + const bool translatable = !(notr == QLatin1String("true") || notr == QLatin1String("yes")); + if (!translatable) + strVal.setTranslatable(translatable); + } + return QVariant::fromValue(strVal); +} + +QVariant QDesignerTextBuilder::toNativeValue(const QVariant &value) const +{ + if (value.canConvert<PropertySheetStringValue>()) + return QVariant::fromValue(qvariant_cast<PropertySheetStringValue>(value).value()); + return value; +} + +DomProperty *QDesignerTextBuilder::saveText(const QVariant &value) const +{ + if (!value.canConvert<PropertySheetStringValue>() && !value.canConvert<QString>()) + return 0; + + DomProperty *property = new DomProperty(); + DomString *domStr = new DomString(); + + if (value.canConvert<PropertySheetStringValue>()) { + PropertySheetStringValue str = qvariant_cast<PropertySheetStringValue>(value); + + domStr->setText(str.value()); + + const QString property_comment = str.disambiguation(); + if (!property_comment.isEmpty()) + domStr->setAttributeComment(property_comment); + const QString property_extraComment = str.comment(); + if (!property_extraComment.isEmpty()) + domStr->setAttributeExtraComment(property_extraComment); + const bool property_translatable = str.translatable(); + if (!property_translatable) + domStr->setAttributeNotr(QLatin1String("true")); + } else { + domStr->setText(value.toString()); + } + + property->setElementString(domStr); + return property; +} + +QDesignerResource::QDesignerResource(FormWindow *formWindow) : + QEditorFormBuilder(formWindow->core()), + m_formWindow(formWindow), + m_topLevelSpacerCount(0), + m_copyWidget(false), + m_selected(0), + m_resourceBuilder(new QDesignerResourceBuilder(m_formWindow->core(), m_formWindow->pixmapCache(), m_formWindow->iconCache())) +{ + setWorkingDirectory(formWindow->absoluteDir()); + setResourceBuilder(m_resourceBuilder); + setTextBuilder(new QDesignerTextBuilder()); + + // ### generalise + const QString designerWidget = QLatin1String("QDesignerWidget"); + const QString layoutWidget = QLatin1String("QLayoutWidget"); + const QString widget = QLatin1String("QWidget"); + m_internal_to_qt.insert(layoutWidget, widget); + m_internal_to_qt.insert(designerWidget, widget); + m_internal_to_qt.insert(QLatin1String("QDesignerDialog"), QLatin1String("QDialog")); + m_internal_to_qt.insert(QLatin1String("QDesignerMenuBar"), QLatin1String("QMenuBar")); + m_internal_to_qt.insert(QLatin1String("QDesignerMenu"), QLatin1String("QMenu")); + m_internal_to_qt.insert(QLatin1String("QDesignerDockWidget"), QLatin1String("QDockWidget")); + m_internal_to_qt.insert(QLatin1String("QDesignerQ3WidgetStack"), QLatin1String("Q3WidgetStack")); + + // invert + QHash<QString, QString>::const_iterator cend = m_internal_to_qt.constEnd(); + for (QHash<QString, QString>::const_iterator it = m_internal_to_qt.constBegin();it != cend; ++it ) { + if (it.value() != designerWidget && it.value() != layoutWidget) + m_qt_to_internal.insert(it.value(), it.key()); + + } +} + +QDesignerResource::~QDesignerResource() +{ +} + +static inline QString messageBoxTitle() +{ + return QApplication::translate("Designer", "Qt Designer"); +} + +void QDesignerResource::save(QIODevice *dev, QWidget *widget) +{ + m_topLevelSpacerCount = 0; + + QAbstractFormBuilder::save(dev, widget); + + if (QSimpleResource::warningsEnabled() && m_topLevelSpacerCount != 0) { + const QString message = QApplication::translate("Designer", "This file contains top level spacers.<br>" + "They have <b>NOT</b> been saved into the form."); + const QString infoMessage = QApplication::translate("Designer", "Perhaps you forgot to create a layout?"); + + core()->dialogGui()->message(widget->window(), QDesignerDialogGuiInterface::TopLevelSpacerMessage, + QMessageBox::Warning, messageBoxTitle(), message, infoMessage, + QMessageBox::Ok); + } +} + +void QDesignerResource::saveDom(DomUI *ui, QWidget *widget) +{ + QAbstractFormBuilder::saveDom(ui, widget); + + QDesignerPropertySheetExtension *sheet = qt_extension<QDesignerPropertySheetExtension*>(core()->extensionManager(), widget); + Q_ASSERT(sheet != 0); + + const QVariant classVar = sheet->property(sheet->indexOf(QLatin1String("objectName"))); + QString classStr; + if (classVar.canConvert(QVariant::String)) + classStr = classVar.toString(); + else + classStr = qvariant_cast<PropertySheetStringValue>(classVar).value(); + ui->setElementClass(classStr); + + for (int index = 0; index < m_formWindow->toolCount(); ++index) { + QDesignerFormWindowToolInterface *tool = m_formWindow->tool(index); + Q_ASSERT(tool != 0); + tool->saveToDom(ui, widget); + } + + const QString author = m_formWindow->author(); + if (!author.isEmpty()) { + ui->setElementAuthor(author); + } + + const QString comment = m_formWindow->comment(); + if (!comment.isEmpty()) { + ui->setElementComment(comment); + } + + const QString exportMacro = m_formWindow->exportMacro(); + if (!exportMacro.isEmpty()) { + ui->setElementExportMacro(exportMacro); + } + + const QVariantMap designerFormData = m_formWindow->formData(); + if (!designerFormData.empty()) { + DomPropertyList domPropertyList; + const QVariantMap::const_iterator cend = designerFormData.constEnd(); + for (QVariantMap::const_iterator it = designerFormData.constBegin(); it != cend; ++it) { + if (DomProperty *prop = variantToDomProperty(this, widget->metaObject(), it.key(), it.value())) + domPropertyList += prop; + } + if (!domPropertyList.empty()) { + DomDesignerData* domDesignerFormData = new DomDesignerData; + domDesignerFormData->setElementProperty(domPropertyList); + ui->setElementDesignerdata(domDesignerFormData); + } + } + + if (!m_formWindow->includeHints().isEmpty()) { + const QString local = QLatin1String("local"); + const QString global = QLatin1String("global"); + QList<DomInclude*> ui_includes; + foreach (QString includeHint, m_formWindow->includeHints()) { + if (includeHint.isEmpty()) + continue; + DomInclude *incl = new DomInclude; + const QString location = includeHint.at(0) == QLatin1Char('<') ? global : local; + includeHint.remove(QLatin1Char('"')); + includeHint.remove(QLatin1Char('<')); + includeHint.remove(QLatin1Char('>')); + incl->setAttributeLocation(location); + incl->setText(includeHint); + ui_includes.append(incl); + } + + DomIncludes *includes = new DomIncludes; + includes->setElementInclude(ui_includes); + ui->setElementIncludes(includes); + } + + int defaultMargin = INT_MIN, defaultSpacing = INT_MIN; + m_formWindow->layoutDefault(&defaultMargin, &defaultSpacing); + + if (defaultMargin != INT_MIN || defaultSpacing != INT_MIN) { + DomLayoutDefault *def = new DomLayoutDefault; + if (defaultMargin != INT_MIN) + def->setAttributeMargin(defaultMargin); + if (defaultSpacing != INT_MIN) + def->setAttributeSpacing(defaultSpacing); + ui->setElementLayoutDefault(def); + } + + QString marginFunction, spacingFunction; + m_formWindow->layoutFunction(&marginFunction, &spacingFunction); + if (!marginFunction.isEmpty() || !spacingFunction.isEmpty()) { + DomLayoutFunction *def = new DomLayoutFunction; + + if (!marginFunction.isEmpty()) + def->setAttributeMargin(marginFunction); + if (!spacingFunction.isEmpty()) + def->setAttributeSpacing(spacingFunction); + ui->setElementLayoutFunction(def); + } + + QString pixFunction = m_formWindow->pixmapFunction(); + if (!pixFunction.isEmpty()) { + ui->setElementPixmapFunction(pixFunction); + } + + if (QDesignerExtraInfoExtension *extra = qt_extension<QDesignerExtraInfoExtension*>(core()->extensionManager(), core())) + extra->saveUiExtraInfo(ui); + + if (MetaDataBase *metaDataBase = qobject_cast<MetaDataBase *>(core()->metaDataBase())) { + const MetaDataBaseItem *item = metaDataBase->metaDataBaseItem(m_formWindow->mainContainer()); + const QStringList fakeSlots = item->fakeSlots(); + const QStringList fakeSignals =item->fakeSignals(); + if (!fakeSlots.empty() || !fakeSignals.empty()) { + DomSlots *domSlots = new DomSlots(); + domSlots->setElementSlot(fakeSlots); + domSlots->setElementSignal(fakeSignals); + ui->setElementSlots(domSlots); + } + } +} + +namespace { + enum LoadPreCheck { LoadPreCheckFailed, LoadPreCheckVersion3, LoadPreCheckVersionMismatch, LoadPreCheckOk }; + // Pair of major, minor + typedef QPair<int, int> UiVersion; +} + +static UiVersion uiVersion(const QString &attr) +{ + const QStringList versions = attr.split(QLatin1Char('.')); + if (versions.empty()) + return UiVersion(-1, -1); + + bool ok = false; + UiVersion rc(versions.at(0).toInt(&ok), 0); + + if (!ok) + return UiVersion(-1, -1); + + if (versions.size() > 1) { + const int minorVersion = versions.at(1).toInt(&ok); + if (ok) + rc.second = minorVersion; + } + return rc; +} + +// Read version and language attributes of an <UI> element. +static bool readUiAttributes(QIODevice *dev, QString *errorMessage, + QString *version, + QString *language) +{ + const QString uiElement = QLatin1String("ui"); + const QString versionAttribute = QLatin1String("version"); + const QString languageAttribute = QLatin1String("language"); + QXmlStreamReader reader(dev); + // Read up to first element + while (!reader.atEnd()) { + if (reader.readNext() == QXmlStreamReader::StartElement) { + const QStringRef tag = reader.name(); + if (reader.name().compare(uiElement, Qt::CaseInsensitive) == 0) { + const QXmlStreamAttributes attributes = reader.attributes(); + if (attributes.hasAttribute(versionAttribute)) + *version = attributes.value(versionAttribute).toString(); + if (attributes.hasAttribute(languageAttribute)) + *language = attributes.value(languageAttribute).toString(); + return true; + } else { + *errorMessage = QCoreApplication::translate("Designer", "Invalid UI file: The root element <ui> is missing."); + return false; + + } + } + } + *errorMessage = QCoreApplication::translate("Designer", "An error has occurred while reading the UI file at line %1, column %2: %3") + .arg(reader.lineNumber()).arg(reader.columnNumber()).arg(reader.errorString()); + return false; +} + +// While loading a file, check language, version and extra extension +static LoadPreCheck loadPrecheck(QDesignerFormEditorInterface *core, + QIODevice *dev, + QString *errorMessage, QString *versionString) +{ + QString language; + // Read attributes of <ui> and rewind + if (!readUiAttributes(dev, errorMessage, versionString, &language)) { + // XML error: Mimick the behaviour occurring if an XML error is + // detected later on, report to warning log and have embedding + // application display a dialog. + designerWarning(*errorMessage); + errorMessage->clear(); + return LoadPreCheckFailed; + } + dev->seek(0); + + // Check language unless extension present (Jambi) + if (!language.isEmpty() && !qt_extension<QDesignerLanguageExtension*>(core->extensionManager(), core)) { + if (language.toLower() != QLatin1String("c++")) { + // Jambi?! + *errorMessage = QApplication::translate("Designer", "This file cannot be read because it was created using %1.").arg(language); + return LoadPreCheckFailed; + } + } + + // Version + if (!versionString->isEmpty()) { + const UiVersion version = uiVersion(*versionString); + switch (version.first) { + case 3: + return LoadPreCheckVersion3; + case 4: + break; + default: + *errorMessage = QApplication::translate("Designer", "This file was created using Designer from Qt-%1 and cannot be read.").arg(*versionString); + return LoadPreCheckVersionMismatch; + } + } + return LoadPreCheckOk; +} + +QWidget *QDesignerResource::load(QIODevice *dev, QWidget *parentWidget) +{ + // Run loadPreCheck for version and language + QString errorMessage; + QString version; + switch (loadPrecheck(core(), dev, &errorMessage, &version)) { + case LoadPreCheckFailed: + case LoadPreCheckVersionMismatch: + if (!errorMessage.isEmpty()) + core()->dialogGui()->message(parentWidget->window(), QDesignerDialogGuiInterface::FormLoadFailureMessage, + QMessageBox::Warning, messageBoxTitle(), errorMessage, QMessageBox::Ok); + return 0; + case LoadPreCheckVersion3: { + QWidget *w = 0; + QByteArray ba; + if (runUIC( m_formWindow->fileName(), UIC_ConvertV3, ba, errorMessage)) { + QBuffer buffer(&ba); + buffer.open(QIODevice::ReadOnly); + w = load(&buffer, parentWidget); + if (w) { + // Force the form to pop up a save file dialog + m_formWindow->setFileName(QString()); + } else { + errorMessage = QApplication::translate("Designer", "The converted file could not be read."); + } + } + if (w) { + const QString message = QApplication::translate("Designer", + "This file was created using Designer from Qt-%1 and" + " will be converted to a new form by Qt Designer.").arg(version); + const QString infoMessage = QApplication::translate("Designer", + "The old form has not been touched, but you will have to save the form" + " under a new name."); + + core()->dialogGui()->message(parentWidget->window(), + QDesignerDialogGuiInterface::UiVersionMismatchMessage, + QMessageBox::Information, messageBoxTitle(), message, infoMessage, + QMessageBox::Ok); + return w; + } + + const QString message = QApplication::translate("Designer", + "This file was created using Designer from Qt-%1 and " + "could not be read:\n%2").arg(version).arg(errorMessage); + const QString infoMessage = QApplication::translate("Designer", + "Please run it through <b>uic3 -convert</b> to convert " + "it to Qt-4's ui format."); + core()->dialogGui()->message(parentWidget->window(), QDesignerDialogGuiInterface::FormLoadFailureMessage, + QMessageBox::Warning, messageBoxTitle(), message, infoMessage, + QMessageBox::Ok); + return 0; + } + + case LoadPreCheckOk: + break; + } + QWidget *w = QEditorFormBuilder::load(dev, parentWidget); + if (w) // Store the class name as 'reset' value for the main container's object name. + w->setProperty("_q_classname", w->objectName()); + return w; +} + +bool QDesignerResource::saveRelative() const +{ + return m_resourceBuilder->isSaveRelative(); +} + +void QDesignerResource::setSaveRelative(bool relative) +{ + m_resourceBuilder->setSaveRelative(relative); +} + +QWidget *QDesignerResource::create(DomUI *ui, QWidget *parentWidget) +{ + // Load extra info extension. This is used by Jambi for preventing + // C++ UI files from being loaded + if (QDesignerExtraInfoExtension *extra = qt_extension<QDesignerExtraInfoExtension*>(core()->extensionManager(), core())) { + if (!extra->loadUiExtraInfo(ui)) { + const QString errorMessage = QApplication::translate("Designer", "This file cannot be read because the extra info extension failed to load."); + core()->dialogGui()->message(parentWidget->window(), QDesignerDialogGuiInterface::FormLoadFailureMessage, + QMessageBox::Warning, messageBoxTitle(), errorMessage, QMessageBox::Ok); + return 0; + } + } + + qdesigner_internal::WidgetFactory *factory = qobject_cast<qdesigner_internal::WidgetFactory*>(core()->widgetFactory()); + Q_ASSERT(factory != 0); + + QDesignerFormWindowInterface *previousFormWindow = factory->currentFormWindow(m_formWindow); + + m_isMainWidget = true; + QDesignerWidgetItemInstaller wii; // Make sure we use QDesignerWidgetItem. + QWidget *mainWidget = QAbstractFormBuilder::create(ui, parentWidget); + + if (mainWidget && m_formWindow) { + m_formWindow->setAuthor(ui->elementAuthor()); + m_formWindow->setComment(ui->elementComment()); + m_formWindow->setExportMacro(ui->elementExportMacro()); + + // Designer data + QVariantMap designerFormData; + if (ui->hasElementDesignerdata()) { + const DomPropertyList domPropertyList = ui->elementDesignerdata()->elementProperty(); + const DomPropertyList::const_iterator cend = domPropertyList.constEnd(); + for (DomPropertyList::const_iterator it = domPropertyList.constBegin(); it != cend; ++it) { + const QVariant vprop = domPropertyToVariant(this, mainWidget->metaObject(), *it); + if (vprop.type() != QVariant::Invalid) + designerFormData.insert((*it)->attributeName(), vprop); + } + } + m_formWindow->setFormData(designerFormData); + + m_formWindow->setPixmapFunction(ui->elementPixmapFunction()); + + if (DomLayoutDefault *def = ui->elementLayoutDefault()) { + m_formWindow->setLayoutDefault(def->attributeMargin(), def->attributeSpacing()); + } + + if (DomLayoutFunction *fun = ui->elementLayoutFunction()) { + m_formWindow->setLayoutFunction(fun->attributeMargin(), fun->attributeSpacing()); + } + + if (DomIncludes *includes = ui->elementIncludes()) { + const QString global = QLatin1String("global"); + QStringList includeHints; + foreach (DomInclude *incl, includes->elementInclude()) { + QString text = incl->text(); + + if (text.isEmpty()) + continue; + + if (incl->hasAttributeLocation() && incl->attributeLocation() == global ) { + text = text.prepend(QLatin1Char('<')).append(QLatin1Char('>')); + } else { + text = text.prepend(QLatin1Char('"')).append(QLatin1Char('"')); + } + + includeHints.append(text); + } + + m_formWindow->setIncludeHints(includeHints); + } + + // Register all button groups the form builder adds as children of the main container for them to be found + // in the signal slot editor + const QObjectList mchildren = mainWidget->children(); + if (!mchildren.empty()) { + QDesignerMetaDataBaseInterface *mdb = core()->metaDataBase(); + const QObjectList::const_iterator cend = mchildren.constEnd(); + for (QObjectList::const_iterator it = mchildren.constBegin(); it != cend; ++it) + if (QButtonGroup *bg = qobject_cast<QButtonGroup*>(*it)) + mdb->add(bg); + } + // Load tools + for (int index = 0; index < m_formWindow->toolCount(); ++index) { + QDesignerFormWindowToolInterface *tool = m_formWindow->tool(index); + Q_ASSERT(tool != 0); + tool->loadFromDom(ui, mainWidget); + } + } + + factory->currentFormWindow(previousFormWindow); + + if (const DomSlots *domSlots = ui->elementSlots()) { + if (MetaDataBase *metaDataBase = qobject_cast<MetaDataBase *>(core()->metaDataBase())) { + QStringList fakeSlots; + QStringList fakeSignals; + if (addFakeMethods(domSlots, fakeSlots, fakeSignals)) { + MetaDataBaseItem *item = metaDataBase->metaDataBaseItem(mainWidget); + item->setFakeSlots(fakeSlots); + item->setFakeSignals(fakeSignals); + } + } + } + if (mainWidget) { + // Initialize the mainwindow geometry. Has it been explicitly specified? + bool hasExplicitGeometry = false; + const QList<DomProperty *> properties = ui->elementWidget()->elementProperty(); + if (!properties.empty()) { + const QString geometry = QLatin1String("geometry"); + foreach (const DomProperty *p, properties) + if (p->attributeName() == geometry) { + hasExplicitGeometry = true; + break; + } + } + if (hasExplicitGeometry) { + // Geometry was specified explicitly: Verify that smartMinSize is respected + // (changed fonts, label wrapping policies, etc). This does not happen automatically in docked mode. + const QSize size = mainWidget->size(); + const QSize minSize = size.expandedTo(qSmartMinSize(mainWidget)); + if (minSize != size) + mainWidget->resize(minSize); + } else { + // No explicit Geometry: perform an adjustSize() to resize the form correctly before embedding it into a container + // (which might otherwise squeeze the form) + mainWidget->adjustSize(); + } + // Some integration wizards create forms with main containers + // based on derived classes of QWidget and load them into Designer + // without the plugin existing. This will trigger the auto-promotion + // mechanism of Designer, which will set container=false for + // QWidgets. For the main container, force container=true and warn. + const QDesignerWidgetDataBaseInterface *wdb = core()->widgetDataBase(); + const int wdbIndex = wdb->indexOfObject(mainWidget); + if (wdbIndex != -1) { + QDesignerWidgetDataBaseItemInterface *item = wdb->item(wdbIndex); + // Promoted main container that is not of container type + if (item->isPromoted() && !item->isContainer()) { + item->setContainer(true); + qWarning("** WARNING The form's main container is an unknown custom widget '%s'." + " Defaulting to a promoted instance of '%s', assuming container.", + item->name().toUtf8().constData(), item->extends().toUtf8().constData()); + } + } + } + return mainWidget; +} + +QWidget *QDesignerResource::create(DomWidget *ui_widget, QWidget *parentWidget) +{ + const QString className = ui_widget->attributeClass(); + if (!m_isMainWidget && className == QLatin1String("QWidget") && ui_widget->elementLayout().size() && + !ui_widget->hasAttributeNative()) { + // ### check if elementLayout.size() == 1 + + QDesignerContainerExtension *container = qt_extension<QDesignerContainerExtension*>(core()->extensionManager(), parentWidget); + + if (container == 0) { + // generate a QLayoutWidget iff the parent is not an QDesignerContainerExtension. + ui_widget->setAttributeClass(QLatin1String("QLayoutWidget")); + } + } + + // save the actions + const QList<DomActionRef*> actionRefs = ui_widget->elementAddAction(); + ui_widget->setElementAddAction(QList<DomActionRef*>()); + + QWidget *w = QAbstractFormBuilder::create(ui_widget, parentWidget); + + // restore the actions + ui_widget->setElementAddAction(actionRefs); + + if (w == 0) + return 0; + + // ### generalize using the extension manager + QDesignerMenu *menu = qobject_cast<QDesignerMenu*>(w); + QDesignerMenuBar *menuBar = qobject_cast<QDesignerMenuBar*>(w); + + if (menu) { + menu->interactive(false); + menu->hide(); + } else if (menuBar) { + menuBar->interactive(false); + } + + foreach (DomActionRef *ui_action_ref, actionRefs) { + const QString name = ui_action_ref->attributeName(); + if (name == QLatin1String("separator")) { + QAction *sep = new QAction(w); + sep->setSeparator(true); + w->addAction(sep); + addMenuAction(sep); + } else if (QAction *a = m_actions.value(name)) { + w->addAction(a); + } else if (QActionGroup *g = m_actionGroups.value(name)) { + w->addActions(g->actions()); + } else if (QMenu *menu = w->findChild<QMenu*>(name)) { + w->addAction(menu->menuAction()); + addMenuAction(menu->menuAction()); + } + } + + if (menu) { + menu->interactive(true); + menu->adjustSpecialActions(); + } else if (menuBar) { + menuBar->interactive(true); + menuBar->adjustSpecialActions(); + } + + ui_widget->setAttributeClass(className); // fix the class name + applyExtensionDataFromDOM(this, core(), ui_widget, w, true); + + // store user-defined scripts + if (MetaDataBase *metaDataBase = qobject_cast<MetaDataBase *>(core()->metaDataBase())) { + const QString designerSource = QLatin1String("designer"); + const DomScripts domScripts = ui_widget->elementScript(); + if (!domScripts.empty()) { + foreach (const DomScript *script, domScripts) { + if (script->hasAttributeSource() && script->attributeSource() == designerSource) { + metaDataBase->metaDataBaseItem(w)->setScript(script->text()); + } + } + } + } + + return w; +} + +QLayout *QDesignerResource::create(DomLayout *ui_layout, QLayout *layout, QWidget *parentWidget) +{ + QLayout *l = QAbstractFormBuilder::create(ui_layout, layout, parentWidget); + + if (QGridLayout *gridLayout = qobject_cast<QGridLayout*>(l)) { + QLayoutSupport::createEmptyCells(gridLayout); + } else { + if (QFormLayout *formLayout = qobject_cast<QFormLayout*>(l)) + QLayoutSupport::createEmptyCells(formLayout); + } + // While the actual values are applied by the form builder, we still need + // to mark them as 'changed'. + LayoutPropertySheet::markChangedStretchProperties(core(), l, ui_layout); + return l; +} + +QLayoutItem *QDesignerResource::create(DomLayoutItem *ui_layoutItem, QLayout *layout, QWidget *parentWidget) +{ + if (ui_layoutItem->kind() == DomLayoutItem::Spacer) { + const DomSpacer *domSpacer = ui_layoutItem->elementSpacer(); + const QHash<QString, DomProperty*> properties = propertyMap(domSpacer->elementProperty()); + Spacer *spacer = static_cast<Spacer*>(core()->widgetFactory()->createWidget(QLatin1String("Spacer"), parentWidget)); + if (domSpacer->hasAttributeName()) + changeObjectName(spacer, domSpacer->attributeName()); + core()->metaDataBase()->add(spacer); + + spacer->setInteractiveMode(false); + applyProperties(spacer, ui_layoutItem->elementSpacer()->elementProperty()); + spacer->setInteractiveMode(true); + + if (m_formWindow) { + m_formWindow->manageWidget(spacer); + if (QDesignerPropertySheetExtension *sheet = qt_extension<QDesignerPropertySheetExtension*>(core()->extensionManager(), spacer)) + sheet->setChanged(sheet->indexOf(QLatin1String("orientation")), true); + } + + return new QWidgetItem(spacer); + } else if (ui_layoutItem->kind() == DomLayoutItem::Layout && parentWidget) { + DomLayout *ui_layout = ui_layoutItem->elementLayout(); + QLayoutWidget *layoutWidget = new QLayoutWidget(m_formWindow, parentWidget); + core()->metaDataBase()->add(layoutWidget); + if (m_formWindow) + m_formWindow->manageWidget(layoutWidget); + (void) create(ui_layout, 0, layoutWidget); + return new QWidgetItem(layoutWidget); + } + return QAbstractFormBuilder::create(ui_layoutItem, layout, parentWidget); +} + +void QDesignerResource::changeObjectName(QObject *o, QString objName) +{ + m_formWindow->unify(o, objName, true); + o->setObjectName(objName); + +} + +/* If the property is a enum or flag value, retrieve + * the existing enum/flag via property sheet and use it to convert */ + +static bool readDomEnumerationValue(const DomProperty *p, + const QDesignerPropertySheetExtension* sheet, int index, + QVariant &v) +{ + switch (p->kind()) { + case DomProperty::Set: { + const QVariant sheetValue = sheet->property(index); + if (sheetValue.canConvert<PropertySheetFlagValue>()) { + const PropertySheetFlagValue f = qvariant_cast<PropertySheetFlagValue>(sheetValue); + bool ok = false; + v = f.metaFlags.parseFlags(p->elementSet(), &ok); + if (!ok) + designerWarning(f.metaFlags.messageParseFailed(p->elementSet())); + return true; + } + } + break; + case DomProperty::Enum: { + const QVariant sheetValue = sheet->property(index); + if (sheetValue.canConvert<PropertySheetEnumValue>()) { + const PropertySheetEnumValue e = qvariant_cast<PropertySheetEnumValue>(sheetValue); + bool ok = false; + v = e.metaEnum.parseEnum(p->elementEnum(), &ok); + if (!ok) + designerWarning(e.metaEnum.messageParseFailed(p->elementEnum())); + return true; + } + } + break; + default: + break; + } + return false; +} + +void QDesignerResource::applyProperties(QObject *o, const QList<DomProperty*> &properties) +{ + if (properties.empty()) + return; + + QDesignerPropertySheetExtension *sheet = qt_extension<QDesignerPropertySheetExtension*>(core()->extensionManager(), o); + if (!sheet) + return; + + QFormBuilderExtra *formBuilderExtra = QFormBuilderExtra::instance(this); + QDesignerDynamicPropertySheetExtension *dynamicSheet = qt_extension<QDesignerDynamicPropertySheetExtension*>(core()->extensionManager(), o); + const bool dynamicPropertiesAllowed = dynamicSheet && dynamicSheet->dynamicPropertiesAllowed(); + + const QString objectNameProperty = QLatin1String("objectName"); + const DomPropertyList::const_iterator cend = properties.constEnd(); + for (DomPropertyList::const_iterator it = properties.constBegin(); it != cend; ++it) { + const DomProperty *p = *it; + const QString propertyName = p->attributeName(); + const int index = sheet->indexOf(propertyName); + QVariant v; + if (!readDomEnumerationValue(p, sheet, index, v)) + v = toVariant(o->metaObject(), *it); + + if (p->kind() == DomProperty::String) { + if (index != -1 && sheet->property(index).userType() == qMetaTypeId<PropertySheetKeySequenceValue>()) { + const DomString *key = p->elementString(); + PropertySheetKeySequenceValue keyVal(QKeySequence(key->text())); + if (key->hasAttributeComment()) + keyVal.setDisambiguation(key->attributeComment()); + if (key->hasAttributeExtraComment()) + keyVal.setComment(key->attributeExtraComment()); + if (key->hasAttributeNotr()) { + const QString notr = key->attributeNotr(); + const bool translatable = !(notr == QLatin1String("true") || notr == QLatin1String("yes")); + if (!translatable) + keyVal.setTranslatable(translatable); + } + v = QVariant::fromValue(keyVal); + } else { + const DomString *str = p->elementString(); + PropertySheetStringValue strVal(v.toString()); + if (str->hasAttributeComment()) + strVal.setDisambiguation(str->attributeComment()); + if (str->hasAttributeExtraComment()) + strVal.setComment(str->attributeExtraComment()); + if (str->hasAttributeNotr()) { + const QString notr = str->attributeNotr(); + const bool translatable = !(notr == QLatin1String("true") || notr == QLatin1String("yes")); + if (!translatable) + strVal.setTranslatable(translatable); + } + v = QVariant::fromValue(strVal); + } + } + + formBuilderExtra->applyPropertyInternally(o, propertyName, v); + if (index != -1) { + sheet->setProperty(index, v); + sheet->setChanged(index, true); + } else if (dynamicPropertiesAllowed) { + QVariant defaultValue = QVariant(v.type()); + bool isDefault = (v == defaultValue); + if (v.canConvert<PropertySheetIconValue>()) { + defaultValue = QVariant(QVariant::Icon); + isDefault = (qvariant_cast<PropertySheetIconValue>(v) == PropertySheetIconValue()); + } else if (v.canConvert<PropertySheetPixmapValue>()) { + defaultValue = QVariant(QVariant::Pixmap); + isDefault = (qvariant_cast<PropertySheetPixmapValue>(v) == PropertySheetPixmapValue()); + } else if (v.canConvert<PropertySheetStringValue>()) { + defaultValue = QVariant(QVariant::String); + isDefault = (qvariant_cast<PropertySheetStringValue>(v) == PropertySheetStringValue()); + } else if (v.canConvert<PropertySheetKeySequenceValue>()) { + defaultValue = QVariant(QVariant::KeySequence); + isDefault = (qvariant_cast<PropertySheetKeySequenceValue>(v) == PropertySheetKeySequenceValue()); + } + if (defaultValue.type() != QVariant::UserType) { + const int idx = dynamicSheet->addDynamicProperty(p->attributeName(), defaultValue); + if (idx != -1) { + sheet->setProperty(idx, v); + sheet->setChanged(idx, !isDefault); + } + } + } + + if (propertyName == objectNameProperty) + changeObjectName(o, o->objectName()); + } +} + +QWidget *QDesignerResource::createWidget(const QString &widgetName, QWidget *parentWidget, const QString &_name) +{ + QString name = _name; + QString className = widgetName; + if (m_isMainWidget) + m_isMainWidget = false; + + QWidget *w = core()->widgetFactory()->createWidget(className, parentWidget); + if (!w) + return 0; + + if (name.isEmpty()) { + QDesignerWidgetDataBaseInterface *db = core()->widgetDataBase(); + if (QDesignerWidgetDataBaseItemInterface *item = db->item(db->indexOfObject(w))) + name = qtify(item->name()); + } + + changeObjectName(w, name); + + QDesignerContainerExtension *container = qt_extension<QDesignerContainerExtension*>(core()->extensionManager(), parentWidget); + if (!qobject_cast<QMenu*>(w) && (!parentWidget || !container)) { + m_formWindow->manageWidget(w); + if (parentWidget) { + QList<QWidget *> list = qvariant_cast<QWidgetList>(parentWidget->property("_q_widgetOrder")); + list.append(w); + parentWidget->setProperty("_q_widgetOrder", QVariant::fromValue(list)); + QList<QWidget *> zOrder = qvariant_cast<QWidgetList>(parentWidget->property("_q_zOrder")); + zOrder.append(w); + parentWidget->setProperty("_q_zOrder", QVariant::fromValue(zOrder)); + } + } else { + core()->metaDataBase()->add(w); + } + + w->setWindowFlags(w->windowFlags() & ~Qt::Window); + // Make sure it is non-modal (for example, KDialog calls setModal(true) in the constructor). + w->setWindowModality(Qt::NonModal); + + return w; +} + +QLayout *QDesignerResource::createLayout(const QString &layoutName, QObject *parent, const QString &name) +{ + QWidget *layoutBase = 0; + QLayout *layout = qobject_cast<QLayout*>(parent); + + if (parent->isWidgetType()) + layoutBase = static_cast<QWidget*>(parent); + else { + Q_ASSERT( layout != 0 ); + layoutBase = layout->parentWidget(); + } + + LayoutInfo::Type layoutType = LayoutInfo::layoutType(layoutName); + if (layoutType == LayoutInfo::NoLayout) { + designerWarning(QCoreApplication::translate("QDesignerResource", "The layout type '%1' is not supported, defaulting to grid.").arg(layoutName)); + layoutType = LayoutInfo::Grid; + } + QLayout *lay = core()->widgetFactory()->createLayout(layoutBase, layout, layoutType); + if (lay != 0) + changeObjectName(lay, name); + + return lay; +} + +// save +DomWidget *QDesignerResource::createDom(QWidget *widget, DomWidget *ui_parentWidget, bool recursive) +{ + QDesignerMetaDataBaseItemInterface *item = core()->metaDataBase()->item(widget); + if (!item) + return 0; + + if (qobject_cast<Spacer*>(widget) && m_copyWidget == false) { + ++m_topLevelSpacerCount; + return 0; + } + + const QDesignerWidgetDataBaseInterface *wdb = core()->widgetDataBase(); + QDesignerWidgetDataBaseItemInterface *widgetInfo = 0; + const int widgetInfoIndex = wdb->indexOfObject(widget, false); + if (widgetInfoIndex != -1) { + widgetInfo = wdb->item(widgetInfoIndex); + // Recursively add all dependent custom widgets + QDesignerWidgetDataBaseItemInterface *customInfo = widgetInfo; + while (customInfo && customInfo->isCustom()) { + m_usedCustomWidgets.insert(customInfo, true); + const QString extends = customInfo->extends(); + if (extends == customInfo->name()) { + break; // There are faulty files around that have name==extends + } else { + const int extendsIndex = wdb->indexOfClassName(customInfo->extends()); + customInfo = extendsIndex != -1 ? wdb->item(extendsIndex) : static_cast<QDesignerWidgetDataBaseItemInterface *>(0); + } + } + } + + DomWidget *w = 0; + + if (QTabWidget *tabWidget = qobject_cast<QTabWidget*>(widget)) + w = saveWidget(tabWidget, ui_parentWidget); + else if (QStackedWidget *stackedWidget = qobject_cast<QStackedWidget*>(widget)) + w = saveWidget(stackedWidget, ui_parentWidget); + else if (QToolBox *toolBox = qobject_cast<QToolBox*>(widget)) + w = saveWidget(toolBox, ui_parentWidget); + else if (QToolBar *toolBar = qobject_cast<QToolBar*>(widget)) + w = saveWidget(toolBar, ui_parentWidget); + else if (QDesignerDockWidget *dockWidget = qobject_cast<QDesignerDockWidget*>(widget)) + w = saveWidget(dockWidget, ui_parentWidget); + else if (QDesignerContainerExtension *container = qt_extension<QDesignerContainerExtension*>(core()->extensionManager(), widget)) + w = saveWidget(widget, container, ui_parentWidget); + else if (QWizardPage *wizardPage = qobject_cast<QWizardPage*>(widget)) + w = saveWidget(wizardPage, ui_parentWidget); + else + w = QAbstractFormBuilder::createDom(widget, ui_parentWidget, recursive); + + Q_ASSERT( w != 0 ); + + if (!qobject_cast<QLayoutWidget*>(widget) && w->attributeClass() == QLatin1String("QWidget")) { + w->setAttributeNative(true); + } + + const QString className = w->attributeClass(); + if (m_internal_to_qt.contains(className)) + w->setAttributeClass(m_internal_to_qt.value(className)); + + w->setAttributeName(widget->objectName()); + + if (isPromoted( core(), widget)) { // is promoted? + Q_ASSERT(widgetInfo != 0); + + w->setAttributeName(widget->objectName()); + w->setAttributeClass(widgetInfo->name()); + + QList<DomProperty*> prop_list = w->elementProperty(); + foreach (DomProperty *prop, prop_list) { + if (prop->attributeName() == QLatin1String("geometry")) { + if (DomRect *rect = prop->elementRect()) { + rect->setElementX(widget->x()); + rect->setElementY(widget->y()); + } + break; + } + } + } else if (widgetInfo != 0 && m_usedCustomWidgets.contains(widgetInfo)) { + if (widgetInfo->name() != w->attributeClass()) + w->setAttributeClass(widgetInfo->name()); + } + addExtensionDataToDOM(this, core(), w, widget); + + addUserDefinedScripts(widget, w); + return w; +} + +DomLayout *QDesignerResource::createDom(QLayout *layout, DomLayout *ui_parentLayout, DomWidget *ui_parentWidget) +{ + QDesignerMetaDataBaseItemInterface *item = core()->metaDataBase()->item(layout); + + if (item == 0) { + layout = layout->findChild<QLayout*>(); + // refresh the meta database item + item = core()->metaDataBase()->item(layout); + } + + if (item == 0) { + // nothing to do. + return 0; + } + + if (qobject_cast<QSplitter*>(layout->parentWidget()) != 0) { + // nothing to do. + return 0; + } + + m_chain.push(layout); + + DomLayout *l = QAbstractFormBuilder::createDom(layout, ui_parentLayout, ui_parentWidget); + Q_ASSERT(l != 0); + LayoutPropertySheet::stretchAttributesToDom(core(), layout, l); + + m_chain.pop(); + + return l; +} + +DomLayoutItem *QDesignerResource::createDom(QLayoutItem *item, DomLayout *ui_layout, DomWidget *ui_parentWidget) +{ + DomLayoutItem *ui_item = 0; + + if (Spacer *s = qobject_cast<Spacer*>(item->widget())) { + if (!core()->metaDataBase()->item(s)) + return 0; + + DomSpacer *spacer = new DomSpacer(); + const QString objectName = s->objectName(); + if (!objectName.isEmpty()) + spacer->setAttributeName(objectName); + const QList<DomProperty*> properties = computeProperties(item->widget()); + // ### filter the properties + spacer->setElementProperty(properties); + + ui_item = new DomLayoutItem(); + ui_item->setElementSpacer(spacer); + m_laidout.insert(item->widget(), true); + } else if (QLayoutWidget *layoutWidget = qobject_cast<QLayoutWidget*>(item->widget())) { + // Do not save a QLayoutWidget if it is within a layout (else it is saved as "QWidget" + Q_ASSERT(layoutWidget->layout()); + DomLayout *l = createDom(layoutWidget->layout(), ui_layout, ui_parentWidget); + ui_item = new DomLayoutItem(); + ui_item->setElementLayout(l); + m_laidout.insert(item->widget(), true); + } else if (!item->spacerItem()) { // we use spacer as fake item in the Designer + ui_item = QAbstractFormBuilder::createDom(item, ui_layout, ui_parentWidget); + } else { + return 0; + } + return ui_item; +} + +void QDesignerResource::createCustomWidgets(DomCustomWidgets *dom_custom_widgets) +{ + QSimpleResource::handleDomCustomWidgets(core(), dom_custom_widgets); +} + +DomTabStops *QDesignerResource::saveTabStops() +{ + QDesignerMetaDataBaseItemInterface *item = core()->metaDataBase()->item(m_formWindow); + Q_ASSERT(item); + + QStringList tabStops; + foreach (QWidget *widget, item->tabOrder()) { + if (m_formWindow->mainContainer()->isAncestorOf(widget)) + tabStops.append(widget->objectName()); + } + + if (tabStops.count()) { + DomTabStops *dom = new DomTabStops; + dom->setElementTabStop(tabStops); + return dom; + } + + return 0; +} + +void QDesignerResource::applyTabStops(QWidget *widget, DomTabStops *tabStops) +{ + if (!tabStops) + return; + + QList<QWidget*> tabOrder; + foreach (const QString &widgetName, tabStops->elementTabStop()) { + if (QWidget *w = widget->findChild<QWidget*>(widgetName)) { + tabOrder.append(w); + } + } + + QDesignerMetaDataBaseItemInterface *item = core()->metaDataBase()->item(m_formWindow); + Q_ASSERT(item); + item->setTabOrder(tabOrder); +} + +/* Unmanaged container pages occur when someone adds a page in a custom widget + * constructor. They don't have a meta DB entry which causes createDom + * to return 0. */ +inline QString msgUnmanagedPage(QDesignerFormEditorInterface *core, + QWidget *container, int index, QWidget *page) +{ + return QCoreApplication::translate("QDesignerResource", +"The container extension of the widget '%1' (%2) returned a widget not managed by Designer '%3' (%4) when queried for page #%5.\n" +"Container pages should only be added by specifying them in XML returned by the domXml() method of the custom widget."). + arg(container->objectName(), WidgetFactory::classNameOf(core, container), + page->objectName(), WidgetFactory::classNameOf(core, page)). + arg(index); +} + +DomWidget *QDesignerResource::saveWidget(QWidget *widget, QDesignerContainerExtension *container, DomWidget *ui_parentWidget) +{ + DomWidget *ui_widget = QAbstractFormBuilder::createDom(widget, ui_parentWidget, false); + QList<DomWidget*> ui_widget_list; + + for (int i=0; i<container->count(); ++i) { + QWidget *page = container->widget(i); + Q_ASSERT(page); + + if (DomWidget *ui_page = createDom(page, ui_widget)) { + ui_widget_list.append(ui_page); + } else { + if (QSimpleResource::warningsEnabled()) + designerWarning(msgUnmanagedPage(core(), widget, i, page)); + } + } + + ui_widget->setElementWidget(ui_widget_list); + + return ui_widget; +} + +DomWidget *QDesignerResource::saveWidget(QStackedWidget *widget, DomWidget *ui_parentWidget) +{ + DomWidget *ui_widget = QAbstractFormBuilder::createDom(widget, ui_parentWidget, false); + QList<DomWidget*> ui_widget_list; + if (QDesignerContainerExtension *container = qt_extension<QDesignerContainerExtension*>(core()->extensionManager(), widget)) { + for (int i=0; i<container->count(); ++i) { + QWidget *page = container->widget(i); + Q_ASSERT(page); + if (DomWidget *ui_page = createDom(page, ui_widget)) { + ui_widget_list.append(ui_page); + } else { + if (QSimpleResource::warningsEnabled()) + designerWarning(msgUnmanagedPage(core(), widget, i, page)); + } + } + } + + ui_widget->setElementWidget(ui_widget_list); + + return ui_widget; +} + +DomWidget *QDesignerResource::saveWidget(QToolBar *toolBar, DomWidget *ui_parentWidget) +{ + DomWidget *ui_widget = QAbstractFormBuilder::createDom(toolBar, ui_parentWidget, false); + if (const QMainWindow *mainWindow = qobject_cast<QMainWindow*>(toolBar->parentWidget())) { + const bool toolBarBreak = mainWindow->toolBarBreak(toolBar); + const Qt::ToolBarArea area = mainWindow->toolBarArea(toolBar); + + QList<DomProperty*> attributes = ui_widget->elementAttribute(); + + DomProperty *attr = new DomProperty(); + attr->setAttributeName(QLatin1String("toolBarArea")); + attr->setElementEnum(QLatin1String(toolBarAreaMetaEnum().valueToKey(area))); + attributes << attr; + + attr = new DomProperty(); + attr->setAttributeName(QLatin1String("toolBarBreak")); + attr->setElementBool(toolBarBreak ? QLatin1String("true") : QLatin1String("false")); + attributes << attr; + ui_widget->setElementAttribute(attributes); + } + + return ui_widget; +} + +DomWidget *QDesignerResource::saveWidget(QDesignerDockWidget *dockWidget, DomWidget *ui_parentWidget) +{ + DomWidget *ui_widget = QAbstractFormBuilder::createDom(dockWidget, ui_parentWidget, true); + if (QMainWindow *mainWindow = qobject_cast<QMainWindow*>(dockWidget->parentWidget())) { + const Qt::DockWidgetArea area = mainWindow->dockWidgetArea(dockWidget); + DomProperty *attr = new DomProperty(); + attr->setAttributeName(QLatin1String("dockWidgetArea")); + attr->setElementNumber(int(area)); + ui_widget->setElementAttribute(ui_widget->elementAttribute() << attr); + } + + return ui_widget; +} + +static void saveStringProperty(DomProperty *property, const PropertySheetStringValue &value) +{ + DomString *str = new DomString(); + str->setText(value.value()); + + const QString property_comment = value.disambiguation(); + if (!property_comment.isEmpty()) + str->setAttributeComment(property_comment); + const QString property_extraComment = value.comment(); + if (!property_extraComment.isEmpty()) + str->setAttributeExtraComment(property_extraComment); + const bool property_translatable = value.translatable(); + if (!property_translatable) + str->setAttributeNotr(QLatin1String("true")); + + property->setElementString(str); +} + +static void saveKeySequenceProperty(DomProperty *property, const PropertySheetKeySequenceValue &value) +{ + DomString *str = new DomString(); + str->setText(value.value().toString()); + + const QString property_comment = value.disambiguation(); + if (!property_comment.isEmpty()) + str->setAttributeComment(property_comment); + const QString property_extraComment = value.comment(); + if (!property_extraComment.isEmpty()) + str->setAttributeExtraComment(property_extraComment); + const bool property_translatable = value.translatable(); + if (!property_translatable) + str->setAttributeNotr(QLatin1String("true")); + + property->setElementString(str); +} + +DomWidget *QDesignerResource::saveWidget(QTabWidget *widget, DomWidget *ui_parentWidget) +{ + DomWidget *ui_widget = QAbstractFormBuilder::createDom(widget, ui_parentWidget, false); + QList<DomWidget*> ui_widget_list; + + if (QDesignerContainerExtension *container = qt_extension<QDesignerContainerExtension*>(core()->extensionManager(), widget)) { + const int current = widget->currentIndex(); + for (int i=0; i<container->count(); ++i) { + QWidget *page = container->widget(i); + Q_ASSERT(page); + + DomWidget *ui_page = createDom(page, ui_widget); + if (!ui_page) { + if (QSimpleResource::warningsEnabled()) + designerWarning(msgUnmanagedPage(core(), widget, i, page)); + continue; + } + QList<DomProperty*> ui_attribute_list; + + const QFormBuilderStrings &strings = QFormBuilderStrings::instance(); + // attribute `icon' + widget->setCurrentIndex(i); + QDesignerPropertySheetExtension *sheet = qt_extension<QDesignerPropertySheetExtension*>(core()->extensionManager(), widget); + PropertySheetIconValue icon = qvariant_cast<PropertySheetIconValue>(sheet->property(sheet->indexOf(QLatin1String("currentTabIcon")))); + DomProperty *p = resourceBuilder()->saveResource(workingDirectory(), QVariant::fromValue(icon)); + if (p) { + p->setAttributeName(strings.iconAttribute); + ui_attribute_list.append(p); + } + // attribute `title' + p = textBuilder()->saveText(sheet->property(sheet->indexOf(QLatin1String("currentTabText")))); + if (p) { + p->setAttributeName(strings.titleAttribute); + ui_attribute_list.append(p); + } + + // attribute `toolTip' + QVariant v = sheet->property(sheet->indexOf(QLatin1String("currentTabToolTip"))); + if (!qvariant_cast<PropertySheetStringValue>(v).value().isEmpty()) { + p = textBuilder()->saveText(v); + if (p) { + p->setAttributeName(strings.toolTipAttribute); + ui_attribute_list.append(p); + } + } + + // attribute `whatsThis' + v = sheet->property(sheet->indexOf(QLatin1String("currentTabWhatsThis"))); + if (!qvariant_cast<PropertySheetStringValue>(v).value().isEmpty()) { + p = textBuilder()->saveText(v); + if (p) { + p->setAttributeName(strings.whatsThisAttribute); + ui_attribute_list.append(p); + } + } + + ui_page->setElementAttribute(ui_attribute_list); + + ui_widget_list.append(ui_page); + } + widget->setCurrentIndex(current); + } + + ui_widget->setElementWidget(ui_widget_list); + + return ui_widget; +} + +DomWidget *QDesignerResource::saveWidget(QToolBox *widget, DomWidget *ui_parentWidget) +{ + DomWidget *ui_widget = QAbstractFormBuilder::createDom(widget, ui_parentWidget, false); + QList<DomWidget*> ui_widget_list; + + if (QDesignerContainerExtension *container = qt_extension<QDesignerContainerExtension*>(core()->extensionManager(), widget)) { + const int current = widget->currentIndex(); + for (int i=0; i<container->count(); ++i) { + QWidget *page = container->widget(i); + Q_ASSERT(page); + + DomWidget *ui_page = createDom(page, ui_widget); + if (!ui_page) { + if (QSimpleResource::warningsEnabled()) + designerWarning(msgUnmanagedPage(core(), widget, i, page)); + continue; + } + + // attribute `label' + QList<DomProperty*> ui_attribute_list; + + const QFormBuilderStrings &strings = QFormBuilderStrings::instance(); + + // attribute `icon' + widget->setCurrentIndex(i); + QDesignerPropertySheetExtension *sheet = qt_extension<QDesignerPropertySheetExtension*>(core()->extensionManager(), widget); + PropertySheetIconValue icon = qvariant_cast<PropertySheetIconValue>(sheet->property(sheet->indexOf(QLatin1String("currentItemIcon")))); + DomProperty *p = resourceBuilder()->saveResource(workingDirectory(), QVariant::fromValue(icon)); + if (p) { + p->setAttributeName(strings.iconAttribute); + ui_attribute_list.append(p); + } + p = textBuilder()->saveText(sheet->property(sheet->indexOf(QLatin1String("currentItemText")))); + if (p) { + p->setAttributeName(strings.labelAttribute); + ui_attribute_list.append(p); + } + + // attribute `toolTip' + QVariant v = sheet->property(sheet->indexOf(QLatin1String("currentItemToolTip"))); + if (!qvariant_cast<PropertySheetStringValue>(v).value().isEmpty()) { + p = textBuilder()->saveText(v); + if (p) { + p->setAttributeName(strings.toolTipAttribute); + ui_attribute_list.append(p); + } + } + + ui_page->setElementAttribute(ui_attribute_list); + + ui_widget_list.append(ui_page); + } + widget->setCurrentIndex(current); + } + + ui_widget->setElementWidget(ui_widget_list); + + return ui_widget; +} + +DomWidget *QDesignerResource::saveWidget(QWizardPage *wizardPage, DomWidget *ui_parentWidget) +{ + DomWidget *ui_widget = QAbstractFormBuilder::createDom(wizardPage, ui_parentWidget, true); + QDesignerPropertySheetExtension *sheet = qt_extension<QDesignerPropertySheetExtension*>(core()->extensionManager(), wizardPage); + // Save the page id (string) attribute, append to existing attributes + const QString pageIdPropertyName = QLatin1String(QWizardPagePropertySheet::pageIdProperty); + const int pageIdIndex = sheet->indexOf(pageIdPropertyName); + if (pageIdIndex != -1 && sheet->isChanged(pageIdIndex)) { + DomProperty *property = variantToDomProperty(this, wizardPage->metaObject(), pageIdPropertyName, sheet->property(pageIdIndex)); + Q_ASSERT(property); + property->elementString()->setAttributeNotr(QLatin1String("true")); + DomPropertyList attributes = ui_widget->elementAttribute(); + attributes.push_back(property); + ui_widget->setElementAttribute(attributes); + } + return ui_widget; +} + +// Do not save the 'currentTabName' properties of containers +static inline bool checkContainerProperty(const QWidget *w, const QString &propertyName) +{ + if (qobject_cast<const QToolBox *>(w)) + return QToolBoxWidgetPropertySheet::checkProperty(propertyName); + if (qobject_cast<const QTabWidget *>(w)) + return QTabWidgetPropertySheet::checkProperty(propertyName); + if (qobject_cast<const QStackedWidget *>(w)) + return QStackedWidgetPropertySheet::checkProperty(propertyName); + if (qobject_cast<const QMdiArea *>(w) || qobject_cast<const QWorkspace *>(w)) + return QMdiAreaPropertySheet::checkProperty(propertyName); + return true; +} + +bool QDesignerResource::checkProperty(QObject *obj, const QString &prop) const +{ + const QDesignerMetaObjectInterface *meta = core()->introspection()->metaObject(obj); + + const int pindex = meta->indexOfProperty(prop); + if (pindex != -1 && !(meta->property(pindex)->attributes(obj) & QDesignerMetaPropertyInterface::StoredAttribute)) + return false; + + if (prop == QLatin1String("objectName") || prop == QLatin1String("spacerName")) // ### don't store the property objectName + return false; + + QWidget *check_widget = 0; + if (obj->isWidgetType()) + check_widget = static_cast<QWidget*>(obj); + + if (check_widget && prop == QLatin1String("geometry")) { + if (check_widget == m_formWindow->mainContainer()) + return true; // Save although maincontainer is technically laid-out by embedding container + if (m_selected && m_selected == check_widget) + return true; + + return !LayoutInfo::isWidgetLaidout(core(), check_widget); + } + + if (check_widget && !checkContainerProperty(check_widget, prop)) + return false; + + if (QDesignerPropertySheetExtension *sheet = qt_extension<QDesignerPropertySheetExtension*>(core()->extensionManager(), obj)) { + QDesignerDynamicPropertySheetExtension *dynamicSheet = qt_extension<QDesignerDynamicPropertySheetExtension*>(core()->extensionManager(), obj); + const int pindex = sheet->indexOf(prop); + if (sheet->isAttribute(pindex)) + return false; + + if (!dynamicSheet || !dynamicSheet->isDynamicProperty(pindex)) + return sheet->isChanged(pindex); + if (!sheet->isVisible(pindex)) + return false; + return true; + } + + return false; +} + +bool QDesignerResource::addItem(DomLayoutItem *ui_item, QLayoutItem *item, QLayout *layout) +{ + if (item->widget() == 0) { + return false; + } + + QGridLayout *grid = qobject_cast<QGridLayout*>(layout); + QBoxLayout *box = qobject_cast<QBoxLayout*>(layout); + + if (grid != 0) { + const int rowSpan = ui_item->hasAttributeRowSpan() ? ui_item->attributeRowSpan() : 1; + const int colSpan = ui_item->hasAttributeColSpan() ? ui_item->attributeColSpan() : 1; + grid->addWidget(item->widget(), ui_item->attributeRow(), ui_item->attributeColumn(), rowSpan, colSpan, item->alignment()); + return true; + } else if (box != 0) { + box->addItem(item); + return true; + } + + return QAbstractFormBuilder::addItem(ui_item, item, layout); +} + +bool QDesignerResource::addItem(DomWidget *ui_widget, QWidget *widget, QWidget *parentWidget) +{ + core()->metaDataBase()->add(widget); // ensure the widget is in the meta database + + if (! QAbstractFormBuilder::addItem(ui_widget, widget, parentWidget) || qobject_cast<QMainWindow*> (parentWidget)) { + if (QDesignerContainerExtension *container = qt_extension<QDesignerContainerExtension*>(core()->extensionManager(), parentWidget)) + container->addWidget(widget); + } + + if (QTabWidget *tabWidget = qobject_cast<QTabWidget*>(parentWidget)) { + const int tabIndex = tabWidget->count() - 1; + const int current = tabWidget->currentIndex(); + + tabWidget->setCurrentIndex(tabIndex); + + const QFormBuilderStrings &strings = QFormBuilderStrings::instance(); + + const DomPropertyHash attributes = propertyMap(ui_widget->elementAttribute()); + QDesignerPropertySheetExtension *sheet = qt_extension<QDesignerPropertySheetExtension*>(core()->extensionManager(), parentWidget); + if (DomProperty *picon = attributes.value(strings.iconAttribute)) { + QVariant v = resourceBuilder()->loadResource(workingDirectory(), picon); + sheet->setProperty(sheet->indexOf(QLatin1String("currentTabIcon")), v); + } + if (DomProperty *ptext = attributes.value(strings.titleAttribute)) { + QVariant v = textBuilder()->loadText(ptext); + sheet->setProperty(sheet->indexOf(QLatin1String("currentTabText")), v); + } + if (DomProperty *ptext = attributes.value(strings.toolTipAttribute)) { + QVariant v = textBuilder()->loadText(ptext); + sheet->setProperty(sheet->indexOf(QLatin1String("currentTabToolTip")), v); + } + if (DomProperty *ptext = attributes.value(strings.whatsThisAttribute)) { + QVariant v = textBuilder()->loadText(ptext); + sheet->setProperty(sheet->indexOf(QLatin1String("currentTabWhatsThis")), v); + } + tabWidget->setCurrentIndex(current); + } else if (QToolBox *toolBox = qobject_cast<QToolBox*>(parentWidget)) { + const int itemIndex = toolBox->count() - 1; + const int current = toolBox->currentIndex(); + + toolBox->setCurrentIndex(itemIndex); + + const QFormBuilderStrings &strings = QFormBuilderStrings::instance(); + + const DomPropertyHash attributes = propertyMap(ui_widget->elementAttribute()); + QDesignerPropertySheetExtension *sheet = qt_extension<QDesignerPropertySheetExtension*>(core()->extensionManager(), parentWidget); + if (DomProperty *picon = attributes.value(strings.iconAttribute)) { + QVariant v = resourceBuilder()->loadResource(workingDirectory(), picon); + sheet->setProperty(sheet->indexOf(QLatin1String("currentItemIcon")), v); + } + if (DomProperty *ptext = attributes.value(strings.labelAttribute)) { + QVariant v = textBuilder()->loadText(ptext); + sheet->setProperty(sheet->indexOf(QLatin1String("currentItemText")), v); + } + if (DomProperty *ptext = attributes.value(strings.toolTipAttribute)) { + QVariant v = textBuilder()->loadText(ptext); + sheet->setProperty(sheet->indexOf(QLatin1String("currentItemToolTip")), v); + } + toolBox->setCurrentIndex(current); + } + + return true; +} + +bool QDesignerResource::copy(QIODevice *dev, const FormBuilderClipboard &selection) +{ + m_copyWidget = true; + + DomUI *ui = copy(selection); + + m_laidout.clear(); + m_copyWidget = false; + + if (!ui) + return false; + + QXmlStreamWriter writer(dev); + writer.setAutoFormatting(true); + writer.setAutoFormattingIndent(1); + writer.writeStartDocument(); + ui->write(writer); + writer.writeEndDocument(); + delete ui; + return true; +} + +DomUI *QDesignerResource::copy(const FormBuilderClipboard &selection) +{ + if (selection.empty()) + return 0; + + m_copyWidget = true; + + DomWidget *ui_widget = new DomWidget(); + ui_widget->setAttributeName(QLatin1String(clipboardObjectName)); + bool hasItems = false; + // Widgets + if (!selection.m_widgets.empty()) { + QList<DomWidget*> ui_widget_list; + const int size = selection.m_widgets.size(); + for (int i=0; i< size; ++i) { + QWidget *w = selection.m_widgets.at(i); + m_selected = w; + DomWidget *ui_child = createDom(w, ui_widget); + m_selected = 0; + if (ui_child) + ui_widget_list.append(ui_child); + } + if (!ui_widget_list.empty()) { + ui_widget->setElementWidget(ui_widget_list); + hasItems = true; + } + } + // actions + if (!selection.m_actions.empty()) { + QList<DomAction*> domActions; + foreach(QAction* action, selection.m_actions) + if (DomAction *domAction = createDom(action)) + domActions += domAction; + if (!domActions.empty()) { + ui_widget-> setElementAction(domActions); + hasItems = true; + } + } + + m_laidout.clear(); + m_copyWidget = false; + + if (!hasItems) { + delete ui_widget; + return 0; + } + // UI + DomUI *ui = new DomUI(); + ui->setAttributeVersion(QLatin1String(currentUiVersion)); + ui->setElementWidget(ui_widget); + ui->setElementResources(saveResources(m_resourceBuilder->usedQrcFiles())); + if (DomCustomWidgets *cws = saveCustomWidgets()) + ui->setElementCustomWidgets(cws); + return ui; +} + +FormBuilderClipboard QDesignerResource::paste(DomUI *ui, QWidget *widgetParent, QObject *actionParent) +{ + QDesignerWidgetItemInstaller wii; // Make sure we use QDesignerWidgetItem. + const int saved = m_isMainWidget; + m_isMainWidget = false; + + FormBuilderClipboard rc; + + // Widgets + const DomWidget *topLevel = ui->elementWidget(); + initialize(ui); + const QList<DomWidget*> domWidgets = topLevel->elementWidget(); + if (!domWidgets.empty()) { + const QPoint offset = m_formWindow->grid(); + foreach (DomWidget* domWidget, domWidgets) { + if (QWidget *w = create(domWidget, widgetParent)) { + w->move(w->pos() + offset); + // ### change the init properties of w + rc.m_widgets.append(w); + } + } + } + const QList<DomAction*> domActions = topLevel->elementAction(); + if (!domActions.empty()) + foreach (DomAction *domAction, domActions) + if (QAction *a = create(domAction, actionParent)) + rc.m_actions .append(a); + + m_isMainWidget = saved; + + if (QDesignerExtraInfoExtension *extra = qt_extension<QDesignerExtraInfoExtension*>(core()->extensionManager(), core())) + extra->loadUiExtraInfo(ui); + + createResources(ui->elementResources()); + + return rc; +} + +FormBuilderClipboard QDesignerResource::paste(QIODevice *dev, QWidget *widgetParent, QObject *actionParent) +{ + DomUI ui; + QXmlStreamReader reader(dev); + bool uiInitialized = false; + + const QString uiElement = QLatin1String("ui"); + while (!reader.atEnd()) { + if (reader.readNext() == QXmlStreamReader::StartElement) { + if (reader.name().compare(uiElement, Qt::CaseInsensitive)) { + ui.read(reader); + uiInitialized = true; + } else { + //: Parsing clipboard contents + reader.raiseError(QCoreApplication::translate("QDesignerResource", "Unexpected element <%1>").arg(reader.name().toString())); + } + } + } + if (reader.hasError()) { + //: Parsing clipboard contents + designerWarning(QCoreApplication::translate("QDesignerResource", "Error while pasting clipboard contents at line %1, column %2: %3") + .arg(reader.lineNumber()).arg(reader.columnNumber()) + .arg(reader.errorString())); + uiInitialized = false; + } else if (uiInitialized == false) { + //: Parsing clipboard contents + designerWarning(QCoreApplication::translate("QDesignerResource", "Error while pasting clipboard contents: The root element <ui> is missing.")); + } + + if (!uiInitialized) + return FormBuilderClipboard(); + + FormBuilderClipboard clipBoard = paste(&ui, widgetParent, actionParent); + + return clipBoard; +} + +void QDesignerResource::layoutInfo(DomLayout *layout, QObject *parent, int *margin, int *spacing) +{ + QAbstractFormBuilder::layoutInfo(layout, parent, margin, spacing); +} + +DomCustomWidgets *QDesignerResource::saveCustomWidgets() +{ + if (m_usedCustomWidgets.isEmpty()) + return 0; + + // We would like the list to be in order of the widget database indexes + // to ensure that base classes come first (nice optics) + QDesignerFormEditorInterface *core = m_formWindow->core(); + QDesignerWidgetDataBaseInterface *db = core->widgetDataBase(); + const bool isInternalWidgetDataBase = qobject_cast<const WidgetDataBase *>(db); + typedef QMap<int,DomCustomWidget*> OrderedDBIndexDomCustomWidgetMap; + OrderedDBIndexDomCustomWidgetMap orderedMap; + + const QString global = QLatin1String("global"); + foreach (QDesignerWidgetDataBaseItemInterface *item, m_usedCustomWidgets.keys()) { + const QString name = item->name(); + DomCustomWidget *custom_widget = new DomCustomWidget; + + custom_widget->setElementClass(name); + if (item->isContainer()) + custom_widget->setElementContainer(item->isContainer()); + + if (!item->includeFile().isEmpty()) { + DomHeader *header = new DomHeader; + const IncludeSpecification spec = includeSpecification(item->includeFile()); + header->setText(spec.first); + if (spec.second == IncludeGlobal) { + header->setAttributeLocation(global); + } + custom_widget->setElementHeader(header); + custom_widget->setElementExtends(item->extends()); + } + + if (isInternalWidgetDataBase) { + WidgetDataBaseItem *internalItem = static_cast<WidgetDataBaseItem *>(item); + const QStringList fakeSlots = internalItem->fakeSlots(); + const QStringList fakeSignals = internalItem->fakeSignals(); + if (!fakeSlots.empty() || !fakeSignals.empty()) { + DomSlots *domSlots = new DomSlots(); + domSlots->setElementSlot(fakeSlots); + domSlots->setElementSignal(fakeSignals); + custom_widget->setElementSlots(domSlots); + } + const QString addPageMethod = internalItem->addPageMethod(); + if (!addPageMethod.isEmpty()) + custom_widget->setElementAddPageMethod(addPageMethod); + } + + // Look up static per-class scripts of designer + if (DomScript *domScript = createScript(customWidgetScript(core, name), ScriptCustomWidgetPlugin)) + custom_widget->setElementScript(domScript); + + orderedMap.insert(db->indexOfClassName(name), custom_widget); + } + + DomCustomWidgets *customWidgets = new DomCustomWidgets; + customWidgets->setElementCustomWidget(orderedMap.values()); + return customWidgets; +} + +bool QDesignerResource::canCompressMargins(QObject *object) const +{ + if (QDesignerPropertySheetExtension *sheet = qt_extension<QDesignerPropertySheetExtension*>(core()->extensionManager(), object)) { + if (qobject_cast<QLayout *>(object)) { + const int l = sheet->property(sheet->indexOf(QLatin1String("leftMargin"))).toInt(); + const int t = sheet->property(sheet->indexOf(QLatin1String("topMargin"))).toInt(); + const int r = sheet->property(sheet->indexOf(QLatin1String("rightMargin"))).toInt(); + const int b = sheet->property(sheet->indexOf(QLatin1String("bottomMargin"))).toInt(); + if (l == t && l == r && l == b) + return true; + } + } + return false; +} + +bool QDesignerResource::canCompressSpacings(QObject *object) const +{ + if (QDesignerPropertySheetExtension *sheet = qt_extension<QDesignerPropertySheetExtension*>(core()->extensionManager(), object)) { + if (qobject_cast<QGridLayout *>(object)) { + const int h = sheet->property(sheet->indexOf(QLatin1String("horizontalSpacing"))).toInt(); + const int v = sheet->property(sheet->indexOf(QLatin1String("verticalSpacing"))).toInt(); + if (h == v) + return true; + } + } + return false; +} + +QList<DomProperty*> QDesignerResource::computeProperties(QObject *object) +{ + QList<DomProperty*> properties; + if (QDesignerPropertySheetExtension *sheet = qt_extension<QDesignerPropertySheetExtension*>(core()->extensionManager(), object)) { + QDesignerDynamicPropertySheetExtension *dynamicSheet = qt_extension<QDesignerDynamicPropertySheetExtension*>(core()->extensionManager(), object); + const int count = sheet->count(); + QList<DomProperty *> marginProperties; + QList<DomProperty *> spacingProperties; + const bool compressMargins = canCompressMargins(object); + const bool compressSpacings = canCompressSpacings(object); + for (int index = 0; index < count; ++index) { + if (!sheet->isChanged(index) && (!dynamicSheet || !dynamicSheet->isDynamicProperty(index))) + continue; + + const QString propertyName = sheet->propertyName(index); + // Suppress windowModality in legacy forms that have it set on child widgets + if (propertyName == QLatin1String("windowModality") && !sheet->isVisible(index)) + continue; + + const QVariant value = sheet->property(index); + if (DomProperty *p = createProperty(object, propertyName, value)) { + if (compressMargins && (propertyName == QLatin1String("leftMargin") + || propertyName == QLatin1String("rightMargin") + || propertyName == QLatin1String("topMargin") + || propertyName == QLatin1String("bottomMargin"))) { + marginProperties.append(p); + } else if (compressSpacings && (propertyName == QLatin1String("horizontalSpacing") + || propertyName == QLatin1String("verticalSpacing"))) { + spacingProperties.append(p); + } else { + properties.append(p); + } + } + } + if (compressMargins) { + if (marginProperties.count() == 4) { // if we have 3 it means one is reset so we can't compress + DomProperty *marginProperty = marginProperties.at(0); + marginProperty->setAttributeName(QLatin1String("margin")); + properties.append(marginProperty); + delete marginProperties.at(1); + delete marginProperties.at(2); + delete marginProperties.at(3); + } else { + properties += marginProperties; + } + } + if (compressSpacings) { + if (spacingProperties.count() == 2) { + DomProperty *spacingProperty = spacingProperties.at(0); + spacingProperty->setAttributeName(QLatin1String("spacing")); + properties.append(spacingProperty); + delete spacingProperties.at(1); + } else { + properties += spacingProperties; + } + } + } + return properties; +} + +DomProperty *QDesignerResource::applyProperStdSetAttribute(QObject *object, const QString &propertyName, DomProperty *property) +{ + if (!property) + return 0; + + QExtensionManager *mgr = core()->extensionManager(); + if (const QDesignerPropertySheetExtension *sheet = qt_extension<QDesignerPropertySheetExtension*>(mgr, object)) { + const QDesignerDynamicPropertySheetExtension *dynamicSheet = qt_extension<QDesignerDynamicPropertySheetExtension*>(mgr, object); + const QDesignerPropertySheet *designerSheet = qobject_cast<QDesignerPropertySheet*>(core()->extensionManager()->extension(object, Q_TYPEID(QDesignerPropertySheetExtension))); + const int index = sheet->indexOf(propertyName); + if ((dynamicSheet && dynamicSheet->isDynamicProperty(index)) || (designerSheet && designerSheet->isDefaultDynamicProperty(index))) + property->setAttributeStdset(0); + } + return property; +} + +// Optimistic check for a standard setter function +static inline bool hasSetter(QDesignerFormEditorInterface *core, QObject *object, const QString &propertyName) +{ + const QDesignerMetaObjectInterface *meta = core->introspection()->metaObject(object); + const int pindex = meta->indexOfProperty(propertyName); + if (pindex == -1) + return true; + return meta->property(pindex)->hasSetter(); +} + +DomProperty *QDesignerResource::createProperty(QObject *object, const QString &propertyName, const QVariant &value) +{ + if (!checkProperty(object, propertyName)) { + return 0; + } + + if (value.canConvert<PropertySheetFlagValue>()) { + const PropertySheetFlagValue f = qvariant_cast<PropertySheetFlagValue>(value); + const QString flagString = f.metaFlags.toString(f.value, DesignerMetaFlags::FullyQualified); + if (flagString.isEmpty()) + return 0; + + DomProperty *p = new DomProperty; + // check if we have a standard cpp set function + if (!hasSetter(core(), object, propertyName)) + p->setAttributeStdset(0); + p->setAttributeName(propertyName); + p->setElementSet(flagString); + return applyProperStdSetAttribute(object, propertyName, p); + } else if (value.canConvert<PropertySheetEnumValue>()) { + const PropertySheetEnumValue e = qvariant_cast<PropertySheetEnumValue>(value); + bool ok; + const QString id = e.metaEnum.toString(e.value, DesignerMetaEnum::FullyQualified, &ok); + if (!ok) + designerWarning(e.metaEnum.messageToStringFailed(e.value)); + if (id.isEmpty()) + return 0; + + DomProperty *p = new DomProperty; + // check if we have a standard cpp set function + if (!hasSetter(core(), object, propertyName)) + p->setAttributeStdset(0); + p->setAttributeName(propertyName); + p->setElementEnum(id); + return applyProperStdSetAttribute(object, propertyName, p); + } else if (value.canConvert<PropertySheetStringValue>()) { + const PropertySheetStringValue strVal = qvariant_cast<PropertySheetStringValue>(value); + DomProperty *p = new DomProperty; + if (!hasSetter(core(), object, propertyName)) + p->setAttributeStdset(0); + + p->setAttributeName(propertyName); + + saveStringProperty(p, strVal); + + return applyProperStdSetAttribute(object, propertyName, p); + } else if (value.canConvert<PropertySheetKeySequenceValue>()) { + const PropertySheetKeySequenceValue keyVal = qvariant_cast<PropertySheetKeySequenceValue>(value); + DomProperty *p = new DomProperty; + if (!hasSetter(core(), object, propertyName)) + p->setAttributeStdset(0); + + p->setAttributeName(propertyName); + + saveKeySequenceProperty(p, keyVal); + + return applyProperStdSetAttribute(object, propertyName, p); + } + + return applyProperStdSetAttribute(object, propertyName, QAbstractFormBuilder::createProperty(object, propertyName, value)); +} + +QStringList QDesignerResource::mergeWithLoadedPaths(const QStringList &paths) const +{ + QStringList newPaths = paths; +#ifdef OLD_RESOURCE_FORMAT + QStringList loadedPaths = m_resourceBuilder->loadedQrcFiles(); + QStringListIterator it(loadedPaths); + while (it.hasNext()) { + const QString path = it.next(); + if (!newPaths.contains(path)) + newPaths << path; + } +#endif + return newPaths; +} + + +void QDesignerResource::createResources(DomResources *resources) +{ + QStringList paths; + if (resources != 0) { + const QList<DomResource*> dom_include = resources->elementInclude(); + foreach (DomResource *res, dom_include) { + QString path = QDir::cleanPath(m_formWindow->absoluteDir().absoluteFilePath(res->attributeLocation())); + while (!QFile::exists(path)) { + QWidget *dialogParent = m_formWindow->core()->topLevel(); + const QString promptTitle = QApplication::translate("qdesigner_internal::QDesignerResource", "Loading qrc file", 0, QApplication::UnicodeUTF8); + const QString prompt = QApplication::translate("qdesigner_internal::QDesignerResource", "The specified qrc file <p><b>%1</b></p><p>could not be found. Do you want to update the file location?</p>", 0, QApplication::UnicodeUTF8).arg(path); + + const QMessageBox::StandardButton answer = core()->dialogGui()->message(dialogParent, QDesignerDialogGuiInterface::ResourceLoadFailureMessage, + QMessageBox::Warning, promptTitle, prompt, QMessageBox::Yes|QMessageBox::No, QMessageBox::Yes); + if (answer == QMessageBox::Yes) { + const QFileInfo fi(path); + const QString fileDialogTitle = QApplication::translate("qdesigner_internal::QDesignerResource", "New location for %1", 0, QApplication::UnicodeUTF8).arg(fi.fileName()); + const QString fileDialogPattern = QApplication::translate("qdesigner_internal::QDesignerResource", "Resource files (*.qrc)", 0, QApplication::UnicodeUTF8); + path = core()->dialogGui()->getOpenFileName(dialogParent, fileDialogTitle, fi.absolutePath(), fileDialogPattern); + if (path.isEmpty()) + break; + m_formWindow->setProperty("_q_resourcepathchanged", QVariant(true)); + } else { + break; + } + } + if (!path.isEmpty()) { + paths << path; + m_formWindow->addResourceFile(path); + } + } + } + +#ifdef OLD_RESOURCE_FORMAT + paths = mergeWithLoadedPaths(paths); +#endif + + QtResourceSet *resourceSet = m_formWindow->resourceSet(); + if (resourceSet) { + QStringList oldPaths = resourceSet->activeQrcPaths(); + QStringList newPaths = oldPaths; + QStringListIterator it(paths); + while (it.hasNext()) { + const QString path = it.next(); + if (!newPaths.contains(path)) + newPaths << path; + } + resourceSet->activateQrcPaths(newPaths); + } else { + resourceSet = m_formWindow->core()->resourceModel()->addResourceSet(paths); + m_formWindow->setResourceSet(resourceSet); + QObject::connect(m_formWindow->core()->resourceModel(), SIGNAL(resourceSetActivated(QtResourceSet*,bool)), + m_formWindow, SLOT(resourceSetActivated(QtResourceSet*,bool))); + } +} + +DomResources *QDesignerResource::saveResources() +{ + QStringList paths; + if (m_formWindow->saveResourcesBehaviour() == FormWindowBase::SaveAll) { + QtResourceSet *resourceSet = m_formWindow->resourceSet(); + QList<DomResource*> dom_include; + if (resourceSet) + paths = resourceSet->activeQrcPaths(); + } else if (m_formWindow->saveResourcesBehaviour() == FormWindowBase::SaveOnlyUsedQrcFiles) { + paths = m_resourceBuilder->usedQrcFiles(); + } + + return saveResources(paths); +} + +DomResources *QDesignerResource::saveResources(const QStringList &qrcPaths) +{ + QtResourceSet *resourceSet = m_formWindow->resourceSet(); + QList<DomResource*> dom_include; + if (resourceSet) { + const QStringList activePaths = resourceSet->activeQrcPaths(); + foreach (const QString &path, activePaths) { + if (qrcPaths.contains(path)) { + DomResource *dom_res = new DomResource; + QString conv_path = path; + if (m_resourceBuilder->isSaveRelative()) + conv_path = m_formWindow->absoluteDir().relativeFilePath(path); + dom_res->setAttributeLocation(conv_path.replace(QDir::separator(), QLatin1Char('/'))); + dom_include.append(dom_res); + } + } + } + + DomResources *dom_resources = new DomResources; + dom_resources->setElementInclude(dom_include); + + return dom_resources; +} + +DomAction *QDesignerResource::createDom(QAction *action) +{ + if (!core()->metaDataBase()->item(action) || action->menu()) + return 0; + + return QAbstractFormBuilder::createDom(action); +} + +DomActionGroup *QDesignerResource::createDom(QActionGroup *actionGroup) +{ + if (core()->metaDataBase()->item(actionGroup) != 0) { + return QAbstractFormBuilder::createDom(actionGroup); + } + + return 0; +} + +QAction *QDesignerResource::create(DomAction *ui_action, QObject *parent) +{ + if (QAction *action = QAbstractFormBuilder::create(ui_action, parent)) { + core()->metaDataBase()->add(action); + return action; + } + + return 0; +} + +QActionGroup *QDesignerResource::create(DomActionGroup *ui_action_group, QObject *parent) +{ + if (QActionGroup *actionGroup = QAbstractFormBuilder::create(ui_action_group, parent)) { + core()->metaDataBase()->add(actionGroup); + return actionGroup; + } + + return 0; +} + +DomActionRef *QDesignerResource::createActionRefDom(QAction *action) +{ + if (!core()->metaDataBase()->item(action) + || (!action->isSeparator() && !action->menu() && action->objectName().isEmpty())) + return 0; + + return QAbstractFormBuilder::createActionRefDom(action); +} + +void QDesignerResource::addMenuAction(QAction *action) +{ + core()->metaDataBase()->add(action); +} + +QAction *QDesignerResource::createAction(QObject *parent, const QString &name) +{ + if (QAction *action = QAbstractFormBuilder::createAction(parent, name)) { + core()->metaDataBase()->add(action); + return action; + } + + return 0; +} + +QActionGroup *QDesignerResource::createActionGroup(QObject *parent, const QString &name) +{ + if (QActionGroup *actionGroup = QAbstractFormBuilder::createActionGroup(parent, name)) { + core()->metaDataBase()->add(actionGroup); + return actionGroup; + } + + return 0; +} + +/* Apply the attributes to a widget via property sheet where appropriate, + * that is, the sheet handles attributive fake properties */ +void QDesignerResource::applyAttributesToPropertySheet(const DomWidget *ui_widget, QWidget *widget) +{ + const DomPropertyList attributes = ui_widget->elementAttribute(); + if (attributes.empty()) + return; + QDesignerPropertySheetExtension *sheet = qt_extension<QDesignerPropertySheetExtension*>(m_formWindow->core()->extensionManager(), widget); + const DomPropertyList::const_iterator acend = attributes.constEnd(); + for (DomPropertyList::const_iterator it = attributes.constBegin(); it != acend; ++it) { + const QString name = (*it)->attributeName(); + const int index = sheet->indexOf(name); + if (index == -1) { + const QString msg = QString::fromUtf8("Unable to apply attributive property '%1' to '%2'. It does not exist.").arg(name, widget->objectName()); + designerWarning(msg); + } else { + sheet->setProperty(index, domPropertyToVariant(this, widget->metaObject(), *it)); + sheet->setChanged(index, true); + } + } +} + +void QDesignerResource::loadExtraInfo(DomWidget *ui_widget, QWidget *widget, QWidget *parentWidget) +{ + QAbstractFormBuilder::loadExtraInfo(ui_widget, widget, parentWidget); + // Apply the page id attribute of a QWizardPage (which is an attributive fake property) + if (qobject_cast<const QWizardPage*>(widget)) + applyAttributesToPropertySheet(ui_widget, widget); +} + +// Add user defined scripts (dialog box) belonging to QWidget to DomWidget. +void QDesignerResource::addUserDefinedScripts(QWidget *w, DomWidget *ui_widget) +{ + QDesignerFormEditorInterface *core = m_formWindow->core(); + DomScripts domScripts = ui_widget->elementScript(); + // Look up user-defined scripts of designer + if (const qdesigner_internal::MetaDataBase *metaDataBase = qobject_cast<const qdesigner_internal::MetaDataBase *>(core->metaDataBase())) { + if (const qdesigner_internal::MetaDataBaseItem *metaItem = metaDataBase->metaDataBaseItem(w)) { + addScript(metaItem->script(), ScriptDesigner, domScripts); + } + } + if (!domScripts.empty()) + ui_widget->setElementScript(domScripts); +} +} + +QT_END_NAMESPACE |