File indexing completed on 2024-05-12 16:39:49

0001 /* This file is part of the KDE project
0002    Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr>
0003    Copyright (C) 2005-2016 Jarosław Staniek <staniek@kde.org>
0004 
0005    This library is free software; you can redistribute it and/or
0006    modify it under the terms of the GNU Library General Public
0007    License as published by the Free Software Foundation; either
0008    version 2 of the License, or (at your option) any later version.
0009 
0010    This library is distributed in the hope that it will be useful,
0011    but WITHOUT ANY WARRANTY; without even the implied warranty of
0012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0013    Library General Public License for more details.
0014 
0015    You should have received a copy of the GNU Library General Public License
0016    along with this library; see the file COPYING.LIB.  If not, write to
0017    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0018  * Boston, MA 02110-1301, USA.
0019 */
0020 
0021 #include "kformdesigner_export.h"
0022 
0023 #include <QDomDocument>
0024 #include <QFile>
0025 #include <QTextStream>
0026 #include <QCursor>
0027 #include <QBuffer>
0028 #include <QImage>
0029 #include <QLayout>
0030 #include <QObject>
0031 #include <QDateTime>
0032 #include <QPainter>
0033 #include <QPaintEvent>
0034 #include <QVBoxLayout>
0035 #include <QHBoxLayout>
0036 #include <QPixmap>
0037 #include <QImageWriter>
0038 #include <QDebug>
0039 
0040 #include <KexiFileDialog.h>
0041 #include <KAcceleratorManager>
0042 #include <KLocalizedString>
0043 
0044 #include <kexiutils/utils.h>
0045 #include "FormWidget.h"
0046 #include "form.h"
0047 #include "container.h"
0048 #include "objecttree.h"
0049 #include "widgetlibrary.h"
0050 #include "events.h"
0051 #include "utils.h"
0052 #include "widgetwithsubpropertiesinterface.h"
0053 #include "formIO.h"
0054 #include "KexiVersion.h"
0055 
0056 //! @todo KEXI3 TODO pixmapcollection
0057 #ifdef KEXI_PIXMAP_COLLECTIONS_SUPPORT
0058 #include "pixmapcollection.h"
0059 #endif
0060 
0061 //! @return Value of attribute "name", or "objectName" in absence of "name".
0062 //!         This is for compatibility with Kexi 1.x.
0063 static QString nameAttribute(const QDomElement& el)
0064 {
0065     QString res( el.attribute("name") );
0066     if (res.isEmpty()) {
0067         res = el.attribute("objectName");
0068     }
0069     return res;
0070 }
0071 
0072 //! A blank widget used when the class name is not supported
0073 CustomWidget::CustomWidget(const QByteArray &className, QWidget *parent)
0074         : QWidget(parent), m_className(className)
0075 {
0076     setBackgroundRole(QPalette::Dark);
0077 }
0078 
0079 CustomWidget::~CustomWidget()
0080 {
0081 }
0082 
0083 void
0084 CustomWidget::paintEvent(QPaintEvent *)
0085 {
0086     QPainter p(this);
0087     p.setBrush(palette().text());
0088     QRect r(rect());
0089     r.setX(r.x() + 2);
0090     p.drawText(r, Qt::AlignTop, m_className);
0091 }
0092 
0093 using namespace KFormDesigner;
0094 
0095 // FormIO itself
0096 
0097 KFORMDESIGNER_EXPORT QString KFormDesigner::version()
0098 {
0099     return QString::fromLatin1("%1.%2").arg(KEXI_STABLE_VERSION_MAJOR).arg(KEXI_STABLE_VERSION_MINOR);
0100 }
0101 
0102 /////////////////////////////////////////////////////////////////////////////
0103 ///////////// Saving/loading functions //////////////////////////////////////
0104 /////////////////////////////////////////////////////////////////////////////
0105 
0106 FormIO::FormIO()
0107 {
0108 }
0109 
0110 FormIO::~FormIO()
0111 {
0112 }
0113 
0114 bool
0115 FormIO::saveFormToFile(Form *form, const QString &filename)
0116 {
0117     QString _filename;
0118     if (!form->fileName().isEmpty() && filename.isEmpty()) {
0119         _filename = form->fileName();
0120     }
0121 
0122     if (filename.isEmpty()) {
0123         KexiFileDialog dlg(0, KexiFileDialog::SaveFile, "SaveForm");
0124         dlg.setNameFilter("*.ui|" + xi18n("Qt Designer UI Files"));
0125         _filename = dlg.fileName();
0126         if (_filename.isEmpty()) {
0127             return false;
0128         }
0129     }
0130     else {
0131         _filename = filename;
0132     }
0133     form->setFileName(_filename);
0134 
0135     QDomDocument domDoc;
0136     if (!saveFormToDom(form, domDoc))
0137         return false;
0138 
0139     QFile file(_filename);
0140     if (!file.open(QIODevice::WriteOnly))
0141         return false;
0142 
0143     QTextStream stream(&file);
0144     stream << domDoc.toString(3);
0145     file.close();
0146 
0147     return true;
0148 }
0149 
0150 bool
0151 FormIO::saveFormToByteArray(Form *form, QByteArray &dest)
0152 {
0153     QDomDocument domDoc;
0154     if (!saveFormToDom(form, domDoc))
0155         return false;
0156     dest = domDoc.toByteArray();
0157     return true;
0158 }
0159 
0160 bool
0161 FormIO::saveFormToString(Form *form, QString &dest, int indent)
0162 {
0163     QDomDocument domDoc;
0164     if (!saveFormToDom(form, domDoc))
0165         return false;
0166     dest = domDoc.toString(indent);
0167     return true;
0168 }
0169 
0170 bool
0171 FormIO::saveFormToDom(Form *form, QDomDocument &domDoc)
0172 {
0173     domDoc = QDomDocument("UI");
0174     QDomElement uiElement = domDoc.createElement("UI");
0175     domDoc.appendChild(uiElement);
0176     uiElement.setAttribute("version", "3.1");
0177     uiElement.setAttribute("stdsetdef", 1);
0178 
0179     //update format version information
0180     form->headerProperties()->insert("version", form->formatVersion());
0181     //custom properties
0182     QDomElement headerPropertiesEl = domDoc.createElement("kfd:customHeader");
0183     QHash<QByteArray, QString>::ConstIterator itEnd = form->headerProperties()->constEnd();
0184     for (QHash<QByteArray, QString>::ConstIterator it = form->headerProperties()->constBegin();
0185         it != itEnd; ++it)
0186     {
0187         headerPropertiesEl.setAttribute(it.key(), it.value());
0188     }
0189     uiElement.appendChild(headerPropertiesEl);
0190 
0191     // We save the savePixmapsInline property in the Form
0192     QDomElement inlinePix = domDoc.createElement("pixmapinproject");
0193     uiElement.appendChild(inlinePix);
0194 
0195     // We create the top class element
0196     QDomElement baseClass = domDoc.createElement("class");
0197     uiElement.appendChild(baseClass);
0198     QDomText baseClassV = domDoc.createTextNode("QWidget");
0199     baseClass.appendChild(baseClassV);
0200 
0201     // Save the toplevel widgets, and so the whole Form
0202     saveWidget(form->objectTree(), uiElement, domDoc);
0203 
0204     // We then save the layoutdefaults element
0205     QDomElement layoutDefaults = domDoc.createElement("layoutDefaults");
0206     layoutDefaults.setAttribute("spacing", QString::number(form->defaultSpacing()));
0207     layoutDefaults.setAttribute("margin", QString::number(form->defaultMargin()));
0208     uiElement.appendChild(layoutDefaults);
0209 
0210     // Save tab Stops
0211     if (form->autoTabStops())
0212         form->autoAssignTabStops();
0213     QDomElement tabStops = domDoc.createElement("tabstops");
0214     uiElement.appendChild(tabStops);
0215     foreach (ObjectTreeItem *item, *form->tabStops()) {
0216         QDomElement tabstop = domDoc.createElement("tabstop");
0217         tabStops.appendChild(tabstop);
0218         QDomText tabStopText = domDoc.createTextNode(item->name());
0219         tabstop.appendChild(tabStopText);
0220     }
0221 
0222 //! @todo KEXI3 TODO pixmapcollection
0223 #ifdef KEXI_PIXMAP_COLLECTIONS_SUPPORT
0224     // Save the Form 's PixmapCollection
0225     form->pixmapCollection()->save(uiElement);
0226 #endif
0227 #ifdef KFD_SIGSLOTS
0228     // Save the Form connections
0229     form->connectionBuffer()->save(uiElement);
0230 #endif
0231 
0232     form->setUndoStackClean();
0233     return true;
0234 }
0235 
0236 bool
0237 FormIO::loadFormFromByteArray(Form *form, QWidget *container, QByteArray &src, bool preview)
0238 {
0239     QString errMsg;
0240     int errLine;
0241     int errCol;
0242 
0243     QDomDocument domDoc;
0244     bool parsed = domDoc.setContent(src, false, &errMsg, &errLine, &errCol);
0245 
0246     if (!parsed) {
0247         qDebug() << errMsg;
0248         qDebug() << "line:" << errLine << "col:" << errCol;
0249         return false;
0250     }
0251 
0252     if (!loadFormFromDom(form, container, domDoc)) {
0253         return false;
0254     }
0255     if (preview) {
0256         form->setMode(Form::DataMode);
0257     }
0258     return true;
0259 }
0260 
0261 bool
0262 FormIO::loadFormFromString(Form *form, QWidget *container, const QString &src, bool preview)
0263 {
0264     QString errMsg;
0265     int errLine;
0266     int errCol;
0267 
0268 #ifdef KEXI_DEBUG_GUI
0269     form->m_recentlyLoadedUICode = src;
0270 #endif
0271 
0272     QDomDocument domDoc;
0273     //qDebug() << qPrintable(src);
0274     bool parsed = domDoc.setContent(src, false, &errMsg, &errLine, &errCol);
0275 
0276     if (!parsed) {
0277         qWarning() << errMsg;
0278         qWarning() << "line:" << errLine << "col: " << errCol;
0279         return false;
0280     }
0281 
0282     if (!loadFormFromDom(form, container, domDoc)) {
0283         return false;
0284     }
0285     if (preview) {
0286         form->setMode(Form::DataMode);
0287     }
0288     return true;
0289 }
0290 
0291 bool
0292 FormIO::loadFormFromFile(Form *form, QWidget *container, const QString &filename)
0293 {
0294     QString errMsg;
0295     int errLine;
0296     int errCol;
0297     QString _filename;
0298 
0299     if (filename.isEmpty()) {
0300         KexiFileDialog dlg(0, KexiFileDialog::OpenFile, "LoadForm");
0301         dlg.setNameFilter("*.ui|" + xi18n("Qt Designer UI Files"));
0302         _filename = dlg.fileName();
0303         if (_filename.isEmpty()) {
0304             return false;
0305         }
0306     }
0307     else {
0308         _filename = filename;
0309     }
0310 
0311     QFile file(_filename);
0312     if (!file.open(QIODevice::ReadOnly)) {
0313 //! @todo show err msg to the user
0314         qWarning() << "Cannot open the file " << _filename;
0315         return false;
0316     }
0317     QDomDocument doc;
0318     if (!doc.setContent(&file, false/* !namespaceProcessing*/,
0319                         &errMsg, &errLine, &errCol)) {
0320 //! @todo show err msg to the user
0321         qWarning() << errMsg;
0322         qWarning() << errLine << "col:" << errCol;
0323         return false;
0324     }
0325 
0326     return loadFormFromDom(form, container, doc);
0327 }
0328 
0329 bool FormIO::loadFormFromDom(Form *form, QWidget *container, const QDomDocument &domDoc)
0330 {
0331     QDomElement ui = domDoc.firstChildElement("UI");
0332 
0333     //custom properties
0334     form->headerProperties()->clear();
0335     QDomElement headerPropertiesEl = ui.firstChildElement("kfd:customHeader");
0336     QDomAttr attr = headerPropertiesEl.firstChild().toAttr();
0337     QDomNamedNodeMap attrs(headerPropertiesEl.attributes());
0338     for(int i = 0; i < attrs.count(); ++i) {
0339         const QDomAttr attr(attrs.item(i).toAttr());
0340         if (!attr.isNull()) {
0341             form->headerProperties()->insert(attr.name().toLatin1(), attr.value());
0342         }
0343     }
0344     //update format version information
0345     QString ver = form->headerProperties()->value("version");
0346     qDebug() << "Original format version: " << ver;
0347     form->setOriginalFormatVersion(ver);
0348     bool verOk;
0349     const double verNum = ver.toDouble(&verOk);
0350     const double currentVerNum = KFormDesigner::version().toDouble();
0351     if (verOk) {
0352         if (verNum < currentVerNum) {
0353             //! @todo We can either 1) convert from old format and later save in a new one or 2) keep old format.
0354             //!     To do this we may need to look at the original format version number.
0355             qDebug() << "The original format version is:" << ver
0356                      << "current version:" << KFormDesigner::version();
0357             //return false;
0358         }
0359     }
0360     form->setFormatVersion(ver);
0361 
0362     if (verNum > currentVerNum) {
0363 //! @todo display information about too new format and that "some information will not be available".
0364         qDebug() << "The original format is version" << ver
0365                  << "is newer than current version:" << KFormDesigner::version();
0366     }
0367 
0368     // Load the pixmap collection
0369     form->setPixmapsStoredInline(ui.firstChildElement("pixmapinproject").isNull()
0370                                  || !ui.firstChildElement("images").isNull());
0371 //! @todo pixmapcollection
0372 #ifdef KEXI_PIXMAP_COLLECTIONS_SUPPORT
0373     form->pixmapCollection()->load(ui.namedItem("collection"));
0374 #endif
0375 
0376     QDomElement element = ui.firstChildElement("widget");
0377     createToplevelWidget(form, container, element);
0378 
0379     // Loading the tabstops
0380     QDomElement tabStops = ui.firstChildElement("tabstops");
0381     if (!tabStops.isNull()) {
0382         int i = 0;
0383         int itemsNotFound = 0;
0384         for (QDomNode n = tabStops.firstChild(); !n.isNull(); n = n.nextSibling(), i++) {
0385             QString name = n.toElement().text();
0386             ObjectTreeItem *item = form->objectTree()->lookup(name);
0387             if (!item) {
0388                 qWarning() << "Tabstops loading: no item" << name;
0389                 continue;
0390             }
0391             const int index = form->tabStops()->indexOf(item);
0392             /* Compute a real destination index: "a number of not found items so far". */
0393             const int realIndex = i - itemsNotFound;
0394             if ((index != -1) && (index != realIndex)) { // the widget is not in the same place, so we move it
0395                 form->tabStops()->removeOne(item);
0396                 form->tabStops()->insert(realIndex, item);
0397             }
0398             if (index == -1) {
0399                 itemsNotFound++;
0400                 qDebug() << "Tabstops loading: item" << name << "not on the list";
0401             }
0402         }
0403     }
0404 
0405 #ifdef KFD_SIGSLOTS
0406     // Load the form connections
0407     form->connectionBuffer()->load(ui.namedItem("connections"));
0408 #endif
0409     return true;
0410 }
0411 
0412 /////////////////////////////////////////////////////////////////////////////
0413 ///////////// Functions to save/load properties /////////////////////////////
0414 /////////////////////////////////////////////////////////////////////////////
0415 
0416 void
0417 FormIO::savePropertyValue(ObjectTreeItem *item, QDomElement &parentNode, QDomDocument &parent,
0418                           const char *name, const QVariant &value)
0419 {
0420     // Widget specific properties and attributes
0421 // qDebug() << "Saving the property: " << name;
0422     Form *form = item->container() ? item->container()->form() : item->parent()->container()->form();
0423     WidgetWithSubpropertiesInterface* subpropIface = dynamic_cast<WidgetWithSubpropertiesInterface*>(item->widget());
0424     QWidget *subwidget = item->widget();
0425     bool addSubwidgetFlag = false;
0426     int propertyId = item->widget()->metaObject()->indexOfProperty(name);
0427     const bool propertyIsName = qstrcmp(name, "objectName") == 0 || qstrcmp(name, "name") == 0;
0428     if (!propertyIsName && propertyId == -1 && subpropIface && subpropIface->subwidget()) { // try property from subwidget
0429         subwidget = subpropIface->subwidget();
0430         propertyId = subpropIface->subwidget()->metaObject()->indexOfProperty(name);
0431         addSubwidgetFlag = true;
0432     }
0433     if (!propertyIsName && propertyId == -1) {
0434         qDebug() << "The object doesn't have this property. Let's try the WidgetLibrary.";
0435         if (form->library())
0436             form->library()->saveSpecialProperty(item->widget()->metaObject()->className(), name, value,
0437                                                  item->widget(), parentNode, parent);
0438         return;
0439     }
0440 
0441     QMetaProperty meta;
0442     if (!propertyIsName) {
0443         meta = subwidget->metaObject()->property(propertyId);
0444     }
0445     if (!propertyIsName && (!meta.isValid() || !meta.isStored(subwidget)))   //not storable
0446         return;
0447     QDomElement propertyE = parent.createElement("property");
0448     propertyE.setAttribute("name", propertyIsName ? "name" /* compat with 1.x */ : name);
0449     if (addSubwidgetFlag)
0450         propertyE.setAttribute("subwidget", "true");
0451 
0452     if (meta.isValid() && meta.isEnumType()) {
0453         // this property is enum or set type
0454         QDomElement type;
0455         QDomText valueE;
0456 
0457         if (meta.isFlagType()) {
0458             type = parent.createElement("set");
0459             valueE = parent.createTextNode(
0460                          meta.enumerator().valueToKeys(value.toInt()));
0461             type.appendChild(valueE);
0462         } else {
0463             QString s = meta.enumerator().valueToKey(value.toInt());
0464             type = parent.createElement("enum");
0465             valueE = parent.createTextNode(s);
0466             type.appendChild(valueE);
0467         }
0468         propertyE.appendChild(type);
0469         parentNode.appendChild(propertyE);
0470         return;
0471     }
0472 
0473     if (value.type() == QVariant::Pixmap) {
0474         QDomText valueE;
0475         QDomElement type = parent.createElement("pixmap");
0476         QByteArray property = propertyE.attribute("name").toLatin1();
0477 //! @todo  QCString pixmapName = m_currentRecord->widget()->property("pixmapName").toCString();
0478         if (form->pixmapsStoredInline() /* (js)too risky: || m_currentRecord->pixmapName(property).isNull() */)
0479             valueE = parent.createTextNode(saveImage(parent, value.value<QPixmap>()));
0480         else
0481             valueE = parent.createTextNode(item->pixmapName(property));
0482         type.appendChild(valueE);
0483         propertyE.appendChild(type);
0484         parentNode.appendChild(propertyE);
0485         return;
0486     }
0487 
0488     // Saving a "normal" property
0489     writeVariant(parent, propertyE, value);
0490     parentNode.appendChild(propertyE);
0491 }
0492 
0493 void
0494 FormIO::writeVariant(QDomDocument &parent, QDomElement &parentNode, const QVariant& value)
0495 {
0496     QDomElement type;
0497     QDomText valueE;
0498 
0499     switch (value.type()) {
0500     case QVariant::String: {
0501         type = parent.createElement("string");
0502         valueE = parent.createTextNode(value.toString());
0503         type.appendChild(valueE);
0504         break;
0505     }
0506     case QVariant::ByteArray: {
0507         type = parent.createElement("cstring");
0508         valueE = parent.createTextNode(value.toString());
0509         type.appendChild(valueE);
0510         break;
0511     }
0512     case QVariant::Rect: {
0513         type = parent.createElement("rect");
0514         QDomElement x = parent.createElement("x");
0515         QDomElement y = parent.createElement("y");
0516         QDomElement w = parent.createElement("width");
0517         QDomElement h = parent.createElement("height");
0518         QDomText valueX = parent.createTextNode(QString::number(value.toRect().x()));
0519         QDomText valueY = parent.createTextNode(QString::number(value.toRect().y()));
0520         QDomText valueW = parent.createTextNode(QString::number(value.toRect().width()));
0521         QDomText valueH = parent.createTextNode(QString::number(value.toRect().height()));
0522 
0523         x.appendChild(valueX);
0524         y.appendChild(valueY);
0525         w.appendChild(valueW);
0526         h.appendChild(valueH);
0527 
0528         type.appendChild(x);
0529         type.appendChild(y);
0530         type.appendChild(w);
0531         type.appendChild(h);
0532         break;
0533     }
0534     case QVariant::Color: {
0535         type = parent.createElement("color");
0536         QDomElement r = parent.createElement("red");
0537         QDomElement g = parent.createElement("green");
0538         QDomElement b = parent.createElement("blue");
0539 
0540         const QColor color(value.value<QColor>());
0541         QDomText valueR = parent.createTextNode(QString::number(color.red()));
0542         QDomText valueG = parent.createTextNode(QString::number(color.green()));
0543         QDomText valueB = parent.createTextNode(QString::number(color.blue()));
0544 
0545         r.appendChild(valueR);
0546         g.appendChild(valueG);
0547         b.appendChild(valueB);
0548 
0549         type.appendChild(r);
0550         type.appendChild(g);
0551         type.appendChild(b);
0552         break;
0553     }
0554     case QVariant::Bool: {
0555         type = parent.createElement("bool");
0556         //valueE = parent.createTextNode(QString::number(value.toBool()));
0557         valueE = parent.createTextNode(value.toBool() ? "true" : "false");
0558         type.appendChild(valueE);
0559         break;
0560     }
0561     case QVariant::Int:
0562     case QVariant::UInt: {
0563         type = parent.createElement("number");
0564         valueE = parent.createTextNode(QString::number(value.toInt()));
0565         type.appendChild(valueE);
0566         break;
0567     }
0568     case QVariant::Size: {
0569         type = parent.createElement("size");
0570         QDomElement w = parent.createElement("width");
0571         QDomElement h = parent.createElement("height");
0572         QDomText valueW = parent.createTextNode(QString::number(value.toSize().width()));
0573         QDomText valueH = parent.createTextNode(QString::number(value.toSize().height()));
0574 
0575         w.appendChild(valueW);
0576         h.appendChild(valueH);
0577 
0578         type.appendChild(w);
0579         type.appendChild(h);
0580         break;
0581     }
0582     case QVariant::Point: {
0583         type = parent.createElement("point");
0584         QDomElement x = parent.createElement("x");
0585         QDomElement y = parent.createElement("y");
0586         QDomText valueX = parent.createTextNode(QString::number(value.toPoint().x()));
0587         QDomText valueY = parent.createTextNode(QString::number(value.toPoint().y()));
0588 
0589         x.appendChild(valueX);
0590         y.appendChild(valueY);
0591 
0592         type.appendChild(x);
0593         type.appendChild(y);
0594         break;
0595     }
0596     case QVariant::Font: {
0597         type = parent.createElement("font");
0598         QDomElement f = parent.createElement("family");
0599         QDomElement p = parent.createElement("pointsize");
0600         QDomElement w = parent.createElement("weight");
0601         QDomElement b = parent.createElement("bold");
0602         QDomElement i = parent.createElement("italic");
0603         QDomElement u = parent.createElement("underline");
0604         QDomElement s = parent.createElement("strikeout");
0605 
0606         const QFont font(value.value<QFont>());
0607         QDomText valueF = parent.createTextNode(font.family());
0608         QDomText valueP = parent.createTextNode(QString::number(font.pointSize()));
0609         QDomText valueW = parent.createTextNode(QString::number(font.weight()));
0610         QDomText valueB = parent.createTextNode(QString::number(font.bold()));
0611         QDomText valueI = parent.createTextNode(QString::number(font.italic()));
0612         QDomText valueU = parent.createTextNode(QString::number(font.underline()));
0613         QDomText valueS = parent.createTextNode(QString::number(font.strikeOut()));
0614 
0615         f.appendChild(valueF);
0616         p.appendChild(valueP);
0617         w.appendChild(valueW);
0618         b.appendChild(valueB);
0619         i.appendChild(valueI);
0620         u.appendChild(valueU);
0621         s.appendChild(valueS);
0622 
0623         type.appendChild(f);
0624         type.appendChild(p);
0625         type.appendChild(w);
0626         type.appendChild(b);
0627         type.appendChild(i);
0628         type.appendChild(u);
0629         type.appendChild(s);
0630         break;
0631     }
0632     case QVariant::Cursor: {
0633         type = parent.createElement("cursor");
0634         valueE = parent.createTextNode(QString::number(value.value<QCursor>().shape()));
0635         type.appendChild(valueE);
0636         break;
0637     }
0638     case QVariant::SizePolicy: {
0639         type = parent.createElement("sizepolicy");
0640         QDomElement h(parent.createElement("hsizetype"));
0641         QDomElement v(parent.createElement("vsizetype"));
0642         QDomElement hs(parent.createElement("horstretch"));
0643         QDomElement vs(parent.createElement("verstretch"));
0644 
0645         const QSizePolicy sizePolicy(value.value<QSizePolicy>());
0646         const QDomText valueH(parent.createTextNode(QString::number(sizePolicy.horizontalPolicy())));
0647         const QDomText valueV(parent.createTextNode(QString::number(sizePolicy.verticalPolicy())));
0648         const QDomText valueHS(parent.createTextNode(QString::number(sizePolicy.horizontalStretch())));
0649         const QDomText valueVS(parent.createTextNode(QString::number(sizePolicy.verticalStretch())));
0650 
0651         h.appendChild(valueH);
0652         v.appendChild(valueV);
0653         hs.appendChild(valueHS);
0654         vs.appendChild(valueVS);
0655 
0656         type.appendChild(h);
0657         type.appendChild(v);
0658         type.appendChild(hs);
0659         type.appendChild(vs);
0660         break;
0661     }
0662     case QVariant::Time: {
0663         type = parent.createElement("time");
0664         QDomElement h = parent.createElement("hour");
0665         QDomElement m = parent.createElement("minute");
0666         QDomElement s = parent.createElement("second");
0667         QDomText valueH = parent.createTextNode(QString::number(value.toTime().hour()));
0668         QDomText valueM = parent.createTextNode(QString::number(value.toTime().minute()));
0669         QDomText valueS = parent.createTextNode(QString::number(value.toTime().second()));
0670 
0671         h.appendChild(valueH);
0672         m.appendChild(valueM);
0673         s.appendChild(valueS);
0674 
0675         type.appendChild(h);
0676         type.appendChild(m);
0677         type.appendChild(s);
0678         break;
0679     }
0680     case QVariant::Date: {
0681         type = parent.createElement("date");
0682         QDomElement y = parent.createElement("year");
0683         QDomElement m = parent.createElement("month");
0684         QDomElement d = parent.createElement("day");
0685         QDomText valueY = parent.createTextNode(QString::number(value.toDate().year()));
0686         QDomText valueM = parent.createTextNode(QString::number(value.toDate().month()));
0687         QDomText valueD = parent.createTextNode(QString::number(value.toDate().day()));
0688 
0689         y.appendChild(valueY);
0690         m.appendChild(valueM);
0691         d.appendChild(valueD);
0692 
0693         type.appendChild(y);
0694         type.appendChild(m);
0695         type.appendChild(d);
0696         break;
0697     }
0698     case QVariant::DateTime: {
0699         type = parent.createElement("datetime");
0700         QDomElement h = parent.createElement("hour");
0701         QDomElement m = parent.createElement("minute");
0702         QDomElement s = parent.createElement("second");
0703         QDomElement y = parent.createElement("year");
0704         QDomElement mo = parent.createElement("month");
0705         QDomElement d = parent.createElement("day");
0706         QDomText valueH = parent.createTextNode(QString::number(value.toDateTime().time().hour()));
0707         QDomText valueM = parent.createTextNode(QString::number(value.toDateTime().time().minute()));
0708         QDomText valueS = parent.createTextNode(QString::number(value.toDateTime().time().second()));
0709         QDomText valueY = parent.createTextNode(QString::number(value.toDateTime().date().year()));
0710         QDomText valueMo = parent.createTextNode(QString::number(value.toDateTime().date().month()));
0711         QDomText valueD = parent.createTextNode(QString::number(value.toDateTime().date().day()));
0712 
0713         h.appendChild(valueH);
0714         m.appendChild(valueM);
0715         s.appendChild(valueS);
0716         y.appendChild(valueY);
0717         mo.appendChild(valueMo);
0718         d.appendChild(valueD);
0719 
0720         type.appendChild(h);
0721         type.appendChild(m);
0722         type.appendChild(s);
0723         type.appendChild(y);
0724         type.appendChild(mo);
0725         type.appendChild(d);
0726         break;
0727     }
0728     default:
0729         break;
0730     }
0731 
0732     parentNode.appendChild(type);
0733 }
0734 
0735 void
0736 FormIO::savePropertyElement(QDomElement &parentNode, QDomDocument &domDoc, const QString &tagName,
0737     const QString &property, const QVariant &value)
0738 {
0739     QDomElement propertyE = domDoc.createElement(tagName);
0740     propertyE.setAttribute("name", property);
0741     writeVariant(domDoc, propertyE, value);
0742     parentNode.appendChild(propertyE);
0743 }
0744 
0745 QVariant FormIO::readPropertyValue(Form *form, QDomNode node, QObject *obj, const QString &name)
0746 {
0747     QDomElement tag = node.toElement();
0748     QString text = tag.text();
0749     QString type = tag.tagName();
0750 
0751     if (type == "string" || type == "cstring")
0752         return text;
0753     else if (type == "rect") {
0754         QDomElement x = node.firstChildElement("x");
0755         QDomElement y = node.firstChildElement("y");
0756         QDomElement w = node.firstChildElement("width");
0757         QDomElement h = node.firstChildElement("height");
0758 
0759         int rx = x.text().toInt();
0760         int ry = y.text().toInt();
0761         int rw = w.text().toInt();
0762         int rh = h.text().toInt();
0763 
0764         return QRect(rx, ry, rw, rh);
0765     } else if (type == "color") {
0766         const QDomElement r(node.firstChildElement("red"));
0767         const QDomElement g(node.firstChildElement("green"));
0768         const QDomElement b(node.firstChildElement("blue"));
0769 
0770         return QColor(r.text().toInt(), g.text().toInt(), b.text().toInt());
0771     } else if (type == "bool") {
0772         if (text == "true")
0773             return true;
0774         else if (text == "false")
0775             return false;
0776         return text.toInt() != 0;
0777     } else if (type == "number") {
0778         return text.toInt();
0779     } else if (type == "size") {
0780         QDomElement w = node.firstChildElement("width");
0781         QDomElement h = node.firstChildElement("height");
0782 
0783         return QSize(w.text().toInt(), h.text().toInt());
0784     } else if (type == "point") {
0785         QDomElement x = node.firstChildElement("x");
0786         QDomElement y = node.firstChildElement("y");
0787 
0788         return QPoint(x.text().toInt(), y.text().toInt());
0789     } else if (type == "font") {
0790         QDomElement fa = node.firstChildElement("family");
0791         QDomElement p = node.firstChildElement("pointsize");
0792         QDomElement w = node.firstChildElement("weight");
0793         QDomElement b = node.firstChildElement("bold");
0794         QDomElement i = node.firstChildElement("italic");
0795         QDomElement u = node.firstChildElement("underline");
0796         QDomElement s = node.firstChildElement("strikeout");
0797 
0798         QFont f;
0799         f.setFamily(fa.text());
0800         f.setPointSize(p.text().toInt());
0801         f.setWeight(w.text().toInt());
0802         f.setBold(b.text().toInt());
0803         f.setItalic(i.text().toInt());
0804         f.setUnderline(u.text().toInt());
0805         f.setStrikeOut(s.text().toInt());
0806 
0807         return f;
0808     } else if (type == "cursor") {
0809         return QCursor((Qt::CursorShape) text.toInt());
0810     } else if (type == "time") {
0811         QDomElement h = node.firstChildElement("hour");
0812         QDomElement m = node.firstChildElement("minute");
0813         QDomElement s = node.firstChildElement("second");
0814 
0815         return QTime(h.text().toInt(), m.text().toInt(), s.text().toInt());
0816     } else if (type == "date") {
0817         QDomElement y = node.firstChildElement("year");
0818         QDomElement m = node.firstChildElement("month");
0819         QDomElement d = node.firstChildElement("day");
0820 
0821         return QDate(y.text().toInt(), m.text().toInt(), d.text().toInt());
0822     } else if (type == "datetime") {
0823         QDomElement h = node.firstChildElement("hour");
0824         QDomElement m = node.firstChildElement("minute");
0825         QDomElement s = node.firstChildElement("second");
0826         QDomElement y = node.firstChildElement("year");
0827         QDomElement mo = node.firstChildElement("month");
0828         QDomElement d = node.firstChildElement("day");
0829 
0830         QTime t(h.text().toInt(), m.text().toInt(), s.text().toInt());
0831         QDate da(y.text().toInt(), mo.text().toInt(), d.text().toInt());
0832 
0833         return QDateTime(da, t);
0834     } else if (type == "sizepolicy") {
0835         QDomElement h = node.firstChildElement("hsizetype");
0836         QDomElement v = node.firstChildElement("vsizetype");
0837         QDomElement hs = node.firstChildElement("horstretch");
0838         QDomElement vs = node.firstChildElement("verstretch");
0839 
0840         QSizePolicy s;
0841         s.setHorizontalPolicy((QSizePolicy::Policy)h.text().toInt());
0842         s.setVerticalPolicy((QSizePolicy::Policy)v.text().toInt());
0843         s.setHorizontalStretch(hs.text().toInt());
0844         s.setVerticalStretch(vs.text().toInt());
0845         return s;
0846     } else if (type == "pixmap") {
0847 //! @todo pixmapcollection
0848 #ifdef KEXI_PIXMAP_COLLECTIONS_SUPPORT
0849         if (form->pixmapsStoredInline() || !m_currentForm || !m_currentRecord || !m_currentForm->pixmapCollection()->contains(text))
0850             return loadImage(tag.ownerDocument(), text);
0851         else {
0852             m_currentRecord->setPixmapName(name.toLatin1(), text);
0853             return form->pixmapCollection()->getPixmap(text);
0854         }
0855 #else
0856         Q_UNUSED(form);
0857 #endif
0858         return QPixmap();
0859     }
0860     else if (type == "enum") {
0861         return text;
0862     }
0863     else if (type == "set") {
0864         WidgetWithSubpropertiesInterface* subpropIface
0865             = dynamic_cast<WidgetWithSubpropertiesInterface*>(obj);
0866         QObject *subobject = (subpropIface && subpropIface->subwidget())
0867                              ? subpropIface->subwidget() : obj;
0868         const QMetaProperty meta(KexiUtils::findPropertyWithSuperclasses(subobject, name.toLatin1()));
0869         if (meta.isValid()) {
0870             if (meta.isFlagType()) {
0871                 return meta.enumerator().keysToValue(text.toLatin1());
0872             } else {
0873                 // Metaproperty not found, probably because subwidget is not created.
0874                 // We will return a string list here with hope that names will
0875                 // be resolved and translated into an integer value later when subwidget is created,
0876                 // e.g. near KexiFormView::updateValuesForSubproperties()
0877                 return text.split('|');
0878             }
0879         }
0880     }
0881     return QVariant();
0882 }
0883 
0884 /////////////////////////////////////////////////////////////////////////////
0885 ///////////// Functions to save/load widgets ////////////////////////////////
0886 /////////////////////////////////////////////////////////////////////////////
0887 
0888 void
0889 FormIO::saveWidget(ObjectTreeItem *item, QDomElement &parent, QDomDocument &domDoc, bool insideGridLayout)
0890 {
0891     if (!item)
0892         return;
0893     //qDebug() << item->className() << item->widget()->objectName();
0894     Form *form = item->container() ? item->container()->form() : item->parent()->container()->form();
0895     WidgetLibrary *lib = form->library();
0896 
0897     // We create the "widget" element
0898     QDomElement tclass = domDoc.createElement("widget");
0899     parent.appendChild(tclass);
0900 
0901     if (insideGridLayout) {
0902         tclass.setAttribute("row", item->gridRow());
0903         tclass.setAttribute("column", item->gridCol());
0904         if (item->spanMultipleCells()) {
0905             tclass.setAttribute("rowspan", item->gridRowSpan());
0906             tclass.setAttribute("colspan", item->gridColSpan());
0907         }
0908     }
0909 
0910     if (!item->parent()) // Toplevel widget
0911         tclass.setAttribute("class", "QWidget");
0912     // For compatibility, HBox, VBox and Grid are saved as "QLayoutWidget"
0913     else if (KexiUtils::objectIsA(item->widget(),
0914                                   QList<QByteArray>() << "HBox" << "VBox" << "Grid" << "HFlow" << "VFlow"))
0915         tclass.setAttribute("class", "QLayoutWidget");
0916     else if (KexiUtils::objectIsA(item->widget(), "CustomWidget"))
0917         tclass.setAttribute("class", item->className());
0918     else // Normal widgets
0919         tclass.setAttribute("class", lib->savingName(item->widget()->metaObject()->className()));
0920 
0921     // We save every property in the modifProp list of the ObjectTreeItem
0922     QHash<QString, QVariant> hash(*(item->modifiedProperties()));
0923     QStringList names(hash.keys());
0924     savePropertyValue(item, tclass, domDoc, "objectName", item->widget()->objectName());
0925     names.removeOne("objectName");
0926 
0927     // Important: save dataSource property FIRST before properties like "alignment"
0928     // - needed when subproperties are defined after subwidget creation, and subwidget is created after setting "dataSource"
0929     //   (this is the case for KexiDBAutoField)
0930 //! @todo more properties like "dataSource" may needed here...
0931 // if (-1 != item->widget()->metaObject()->findProperty("dataSource"))
0932     // savePropertyValue(tclass, domDoc, "dataSource", item->widget()->property("dataSource"), item->widget());
0933 
0934     // We don't want to save the geometry if the widget is inside a layout (so parent.tagName() == "grid" for example)
0935     if (item && !item->parent()) {
0936         // save form widget size, but not its position
0937         savePropertyValue(item, tclass, domDoc, "geometry",
0938                           QRect(QPoint(0, 0), item->widget()->size()));
0939     }
0940     // normal widget (if == "UI', it means we're copying widget)
0941     else if (parent.tagName() == "widget" || parent.tagName() == "UI")
0942         savePropertyValue(item, tclass, domDoc, "geometry", item->widget()->property("geometry"));
0943 
0944     names.removeOne("geometry");
0945     names.removeOne("layout");
0946 
0947     // Save the buddy widget for a label
0948     if (   qobject_cast<QLabel*>(item->widget())
0949         && qobject_cast<QLabel*>(item->widget())->buddy())
0950     {
0951         savePropertyElement(
0952             tclass, domDoc, "property", "buddy",
0953             qobject_cast<QLabel*>(item->widget())->buddy()->objectName());
0954     }
0955 
0956     if (names.contains("paletteBackgroundColor")) {
0957         savePropertyElement(
0958             tclass, domDoc, "property", "paletteBackgroundColor",
0959             item->widget()->palette().color(item->widget()->backgroundRole()));
0960         names.removeOne("paletteBackgroundColor");
0961     }
0962     if (names.contains("paletteForegroundColor")) {
0963         savePropertyElement(
0964             tclass, domDoc, "property", "paletteForegroundColor",
0965             item->widget()->palette().color(item->widget()->foregroundRole()));
0966         names.removeOne("paletteForegroundColor");
0967     }
0968 
0969     QStringList alignProperties;
0970     alignProperties << "hAlign" << "vAlign" << "alignment";
0971     foreach (const QString& name, alignProperties) {
0972         if (names.contains(name)) {
0973             names.removeOne(name);
0974             savePropertyValue(
0975                 item, tclass, domDoc, "alignment",
0976                 item->widget()->property("alignment"));
0977             break;
0978         }
0979     }
0980 
0981     foreach (const QString& name, names) {
0982         savePropertyValue(item, tclass, domDoc, name.toLatin1(),
0983                           item->widget()->property(name.toLatin1()));
0984     }
0985     hash.clear();
0986 
0987     if (KexiUtils::objectIsA(item->widget(), "CustomWidget")) {
0988         QDomDocument doc("TEMP");
0989         doc.setContent(item->unknownProperties());
0990         for (QDomNode n = doc.firstChild(); !n.isNull(); n = n.nextSibling()) {
0991             tclass.appendChild(n.cloneNode());
0992         }
0993 
0994     }
0995     // Saving container 's layout if there is one
0996     QDomElement layout;
0997     if (item->container() && item->container()->layoutType() != Form::NoLayout) {
0998         if (item->container()->layout()) { // there is a layout
0999             layout = domDoc.createElement("temp");
1000             savePropertyValue(item, layout, domDoc, "objectName", "unnamed");
1001             if (item->modifiedProperties()->contains("layoutMargin"))
1002                 savePropertyElement(layout, domDoc, "property", "margin", item->container()->layoutMargin());
1003             if (item->modifiedProperties()->contains("layoutSpacing"))
1004                 savePropertyElement(layout, domDoc, "property", "spacing", item->container()->layoutSpacing());
1005             tclass.appendChild(layout);
1006         }
1007     }
1008 
1009     Form::LayoutType layoutType = item->container() ? item->container()->layoutType() : Form::NoLayout;
1010     switch (layoutType) {
1011     case Form::Grid: { // grid layout
1012         layout.setTagName("grid");
1013         foreach (ObjectTreeItem *titem, *item->children()) {
1014             saveWidget(titem, layout, domDoc, true);
1015         }
1016         break;
1017     }
1018     case Form::HBox:
1019     case Form::VBox: {
1020         // as we don't save geometry, we need to sort widgets in the right order, not creation order
1021         CustomSortableWidgetList *list;
1022         if (layout.tagName() == "hbox") {
1023             list = new HorizontalWidgetList(item->container()->form()->toplevelContainer()->widget());
1024             layout.setTagName("hbox");
1025         } else {
1026             list = new HorizontalWidgetList(item->container()->form()->toplevelContainer()->widget());
1027             layout.setTagName("vbox");
1028         }
1029 
1030         foreach (ObjectTreeItem *titem, *item->children()) {
1031             list->append(titem->widget());
1032         }
1033         list->sort();
1034 
1035         foreach (QWidget *w, *list) {
1036             ObjectTreeItem *titem = item->container()->form()->objectTree()->lookup(
1037                 w->objectName()
1038             );
1039             if (item)
1040                 saveWidget(titem, layout, domDoc);
1041         }
1042         delete list;
1043         break;
1044     }
1045     case Form::HFlow:
1046     case Form::VFlow: {
1047         break;
1048     }
1049     default: {
1050         foreach (ObjectTreeItem *titem, *item->children()) {
1051             saveWidget(titem, tclass, domDoc);
1052         }
1053     }
1054     }
1055 
1056     addIncludeFileName(lib->includeFileName(
1057                            item->widget()->metaObject()->className()), domDoc);
1058 }
1059 
1060 void
1061 FormIO::cleanClipboard(QDomElement &uiElement)
1062 {
1063     // remove includehints element not needed
1064     if (!uiElement.firstChildElement("includehints").isNull())
1065         uiElement.removeChild(uiElement.firstChildElement("includehints"));
1066     // and ensure images and connection are at the end
1067     if (!uiElement.firstChildElement("connections").isNull())
1068         uiElement.insertAfter(uiElement.firstChildElement("connections"), QDomNode());
1069     if (!uiElement.firstChildElement("images").isNull())
1070         uiElement.insertAfter(uiElement.firstChildElement("images"), QDomNode());
1071 }
1072 
1073 void FormIO::loadWidget(Container *container, const QDomElement &el, QWidget *parent,
1074                         QHash<QString, QLabel*> *buddies)
1075 {
1076     Form *form = container->form();
1077 
1078     // We first look for the widget's name
1079     QString wname;
1080     for (QDomNode n = el.firstChild(); !n.isNull(); n = n.nextSibling()) {
1081         if (   n.toElement().tagName() == "property"
1082             && nameAttribute(n.toElement()) == "name" /* compat with 1.x */)
1083         {
1084             wname = n.toElement().text();
1085             break;
1086         }
1087     }
1088     ObjectTreeItem *item = container->form()->objectTree()->lookup(wname);
1089     if (item) {
1090         qWarning() << "Widget" << wname << "already exists! Skipping...";
1091         return;
1092     }
1093 
1094     QByteArray classname, alternate;
1095     // check if this classname is an alternate one, and replace it if necessary
1096     classname = el.attribute("class").toLatin1();
1097     alternate = container->form()->library()->classNameForAlternate(classname);
1098 
1099     QWidget *w;
1100     if (alternate == "CustomWidget") {
1101         w = new CustomWidget(classname, container->widget());
1102         w->setObjectName(wname);
1103     } else {
1104         if (!alternate.isEmpty()) {
1105             classname = alternate;
1106         }
1107 
1108         WidgetFactory::CreateWidgetOptions widgetOptions = WidgetFactory::DefaultOptions;
1109         if (container->form()->mode() != Form::DesignMode) {
1110             widgetOptions ^= WidgetFactory::DesignViewMode;
1111         }
1112 
1113         if (!parent)
1114             w = container->form()->library()->createWidget(classname, container->widget(),
1115                     wname.toLatin1(), container, widgetOptions);
1116         else
1117             w = container->form()->library()->createWidget(classname, parent, wname.toLatin1(),
1118                     container, widgetOptions);
1119     }
1120 
1121     if (!w)
1122         return;
1123 //! @todo allow setting this for data view mode as well
1124     if (form->mode() == Form::DesignMode) {
1125         //don't generate accelerators for widgets in design mode
1126         KAcceleratorManager::setNoAccel(w);
1127     }
1128     w->show();
1129 
1130     // We create and insert the ObjectTreeItem at the good place in the ObjectTree
1131     item = container->form()->objectTree()->lookup(wname);
1132     //qDebug() << wname << item << classname << (parent ? parent->objectName() : QString());
1133     if (!item)  {
1134         // not yet created
1135         //qDebug() << "Creating ObjectTreeItem:";
1136         item =  new ObjectTreeItem(container->form()->library()->displayName(classname),
1137                                    wname, w, container);
1138         if (parent)  {
1139             ObjectTreeItem *titem = container->form()->objectTree()->lookup(parent->objectName());
1140             if (titem)
1141                 container->form()->objectTree()->addItem(titem, item);
1142             else
1143                 qWarning() << "ERROR no parent widget";
1144         } else
1145             container->form()->objectTree()->addItem(container->objectTree(), item);
1146     }
1147     //assign item for its widget if it supports DesignTimeDynamicChildWidgetHandler interface
1148     //(e.g. KexiDBAutoField)
1149     DesignTimeDynamicChildWidgetHandler *childHandler = dynamic_cast<DesignTimeDynamicChildWidgetHandler*>(w);
1150     if (container->form()->mode() == Form::DesignMode && childHandler) {
1151         childHandler->assignItem(item);
1152     }
1153 
1154     // if we are inside a Grid, we need to insert the widget in the good cell
1155     if (container->layoutType() == Form::Grid)  {
1156         QGridLayout *layout = (QGridLayout*)container->layout();
1157         if (el.hasAttribute("rowspan")) { // widget spans multiple cells
1158             if (layout) {
1159                 layout->addWidget(
1160                     w,
1161                     el.attribute("row").toInt(), el.attribute("column").toInt(),
1162                     el.attribute("rowspan").toInt(), el.attribute("colspan").toInt());
1163 //! @todo alignment attribute?
1164             }
1165             item->setGridPos(el.attribute("row").toInt(),  el.attribute("column").toInt(), el.attribute("rowspan").toInt(),
1166                              el.attribute("colspan").toInt());
1167         } else  {
1168             if (layout) {
1169                 layout->addWidget(w, el.attribute("row").toInt(), el.attribute("column").toInt());
1170             }
1171             item->setGridPos(el.attribute("row").toInt(),  el.attribute("column").toInt(), 0, 0);
1172         }
1173     } else if (container->layout())
1174         container->layout()->addWidget(w);
1175 
1176     readChildNodes(item, container, el, w, buddies);
1177 
1178     if (item->container() && item->container()->layout())
1179         item->container()->layout()->activate();
1180 
1181     // add the autoSaveProperties in the modifProp list of the ObjectTreeItem, so that they are saved later
1182     const QList<QByteArray> autoSaveProperties(
1183         container->form()->library()->autoSaveProperties(w->metaObject()->className()) );
1184     KFormDesigner::WidgetWithSubpropertiesInterface* subpropIface
1185         = dynamic_cast<KFormDesigner::WidgetWithSubpropertiesInterface*>(w);
1186     QWidget *subwidget = (subpropIface && subpropIface->subwidget()) ? subpropIface->subwidget() : w;
1187     foreach (const QByteArray &propName, autoSaveProperties) {
1188         if (subwidget && -1 != subwidget->metaObject()->indexOfProperty(propName)) {
1189             item->addModifiedProperty(propName, subwidget->property(propName));
1190         }
1191     }
1192 
1193     const QVariant autoFillBackgroundValue = item->modifiedProperties()->value("autoFillBackground");
1194     const QVariant paletteForegroundColorValue = item->modifiedProperties()->value("paletteForegroundColor");
1195     if (!paletteForegroundColorValue.isNull() && autoFillBackgroundValue.isNull()) {
1196         // Sanity: force fill background if there's color but not 'fill background' set
1197         w->setAutoFillBackground(true);
1198     }
1199 }
1200 
1201 void
1202 FormIO::createToplevelWidget(Form *form, QWidget *container, QDomElement &el)
1203 {
1204     // We first look for the widget's name
1205     QString wname;
1206     for (QDomNode n = el.firstChild(); !n.isNull(); n = n.nextSibling()) {
1207         if (    n.toElement().tagName() == "property"
1208              && nameAttribute(n.toElement()) == "name" /* compat with 1.x */)
1209         {
1210             wname = n.toElement().text();
1211             break;
1212         }
1213     }
1214     // And rename the widget and its ObjectTreeItem
1215     container->setObjectName(wname);
1216     if (form->objectTree())
1217         form->objectTree()->rename(form->objectTree()->name(), wname);
1218     form->setInteractiveMode(false);
1219 
1220     QHash<QString, QLabel*> buddies;
1221     readChildNodes(form->objectTree(), form->toplevelContainer(), el, container, &buddies);
1222 
1223     // Now the Form is fully loaded, we can assign the buddies
1224     for (QHash<QString, QLabel*>::ConstIterator it(buddies.constBegin());
1225         it!=buddies.constEnd(); ++it)
1226     {
1227         ObjectTreeItem *item = form->objectTree()->lookup(it.key());
1228         if (!item || !item->widget()) {
1229             qDebug() << "Cannot assign buddy for widget "
1230                 << it.value()->objectName() << " to " << it.key();
1231             continue;
1232         }
1233         it.value()->setBuddy(item->widget());
1234     }
1235     form->setInteractiveMode(true);
1236 }
1237 
1238 void FormIO::readChildNodes(ObjectTreeItem *item, Container *container, const QDomElement &el,
1239                        QWidget *w, QHash<QString, QLabel*> *buddies)
1240 {
1241     QString eltag = el.tagName();
1242 
1243     WidgetWithSubpropertiesInterface* subpropIface = dynamic_cast<WidgetWithSubpropertiesInterface*>(w);
1244     QWidget *subwidget = (subpropIface && subpropIface->subwidget()) ? subpropIface->subwidget() : w;
1245 
1246     for (QDomNode n = el.firstChild(); !n.isNull(); n = n.nextSibling()) {
1247         QString tag = n.toElement().tagName();
1248         QDomElement node = n.toElement();
1249 
1250         if ((tag == "property") || (tag == "attribute")) {
1251             const QString name( node.attribute("name") );
1252             const bool isQt3NameProperty = name == QLatin1String("name");
1253             //if(name == "geometry")
1254             // hasGeometryProp = true;
1255             if (   (eltag == "grid" || eltag == "hbox" || eltag == "vbox")
1256                 && (isQt3NameProperty || name == "objectName")
1257                )
1258             {
1259                 // we don't care about layout names
1260                 continue;
1261             }
1262 
1263             if (node.attribute("subwidget") == "true") {
1264                 //this is property for subwidget: remember it for delayed setting
1265                 //because now the subwidget could be not created yet (true e.g. for KexiDBAutoField)
1266                 item->addSubproperty(name.toLatin1(),
1267                                      readPropertyValue(container->form(), node.firstChild(), w, name));
1268                 const QVariant val(readPropertyValue(container->form(), node.firstChild(), w, name));
1269                 //qDebug() << val.toStringList();
1270                 item->addSubproperty(name.toLatin1(), val);
1271                 //subwidget->setProperty(name.toLatin1(), val);
1272                 item->addModifiedProperty(name.toLatin1(), val);
1273                 continue;
1274             }
1275 
1276             // We cannot assign the buddy now as the buddy widget may not be created yet
1277             if (name == "buddy") {
1278                 if (buddies && qobject_cast<QLabel*>(w)) {
1279                     buddies->insert(readPropertyValue(
1280                                     container->form(), node.firstChild(), w, name).toString(),
1281                                     qobject_cast<QLabel*>(w));
1282                 }
1283             }
1284             else if (    (eltag == "grid" || eltag == "hbox" || eltag == "vbox")
1285                       && item->container()
1286                       && item->container()->layout() )
1287             {
1288                 // We load the margin of a Layout
1289                 if (name == "margin")  {
1290                     int margin = readPropertyValue(container->form(), node.firstChild(), w, name).toInt();
1291                     item->container()->setLayoutMargin(margin);
1292                     item->container()->layout()->setMargin(margin);
1293                 }
1294                 // We load the spacing of a Layout
1295                 else if (name == "spacing")  {
1296                     int spacing = readPropertyValue(container->form(), node.firstChild(), w, name).toInt();
1297                     item->container()->setLayoutSpacing(spacing);
1298                     item->container()->layout()->setSpacing(spacing);
1299                 }
1300             }
1301             else if (name == "paletteBackgroundColor" || name == "paletteForegroundColor") {
1302                 QPalette widgetPalette(w->palette());
1303                 QVariant val(readPropertyValue(container->form(), node.firstChild(), w, name));
1304                 if (!val.isNull())
1305                     widgetPalette.setColor(
1306                         name == "paletteBackgroundColor" ? w->backgroundRole() : w->foregroundRole(),
1307                         val.value<QColor>());
1308                 w->setPalette(widgetPalette);
1309                 if (name == "paletteBackgroundColor") {
1310                     w->setAutoFillBackground(val.value<QColor>().isValid());
1311                 }
1312                 item->addModifiedProperty(name.toLatin1(), val);
1313             }
1314             else if (!isQt3NameProperty && -1 == subwidget->metaObject()->indexOfProperty(name.toLatin1()))
1315             {
1316                 // If the object doesn't have this property, we let the Factory handle it (maybe a special property)
1317                 if (w->metaObject()->className() == QString::fromLatin1("CustomWidget"))
1318                     item->storeUnknownProperty(node);
1319                 else {
1320                     bool read = container->form()->library()->readSpecialProperty(
1321                                     w->metaObject()->className(), node, w, item);
1322                     if (!read) // the factory doesn't support this property neither
1323                         item->storeUnknownProperty(node);
1324                 }
1325             }
1326             else { // we have a normal property, let's load it
1327                 QVariant val(readPropertyValue(container->form(), node.firstChild(), w, name));
1328                 if (name == "geometry" && dynamic_cast<FormWidget*>(w)) {
1329                     //fix geometry if needed - this is top level form widget
1330                     QRect r(val.toRect());
1331                     if (r.left() < 0) //negative X!
1332                         r.moveLeft(0);
1333                     if (r.top() < 0) //negative Y!
1334                         r.moveTop(0);
1335                     val = r;
1336                 }
1337                 QByteArray realName;
1338                 if (isQt3NameProperty) {
1339                     realName = "objectName";
1340                 }
1341                 else {
1342                     realName = name.toLatin1();
1343                 }
1344                 subwidget->setProperty(realName, val);
1345 //    int count = w->metaObject()->findProperty(name, true);
1346 //    const QMetaProperty *meta = w->metaObject()->property(count, true);
1347 //    if(meta && meta->isEnumType()) {
1348 //     val = w->property(name.toLatin1()); //update: we want a numeric value of enum
1349 //    }
1350                 item->addModifiedProperty(realName, val);
1351             }
1352         }
1353         else if (tag == "widget") { // a child widget
1354             if (item->container()) // we are a Container
1355                 loadWidget(item->container(), node, 0, buddies);
1356             else
1357                 loadWidget(container, node, w, buddies);
1358         }
1359         else if (tag == "spacer")  {
1360             loadWidget(container, node, w, buddies);
1361         }
1362         else if (tag == "grid") {
1363             // first, see if it is flow layout
1364             QString layoutName;
1365             for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling())  {
1366                 if (   child.toElement().tagName() == "property"
1367                     && child.toElement().attribute("name") == "customLayout")
1368                 {
1369                     layoutName = child.toElement().text();
1370                     break;
1371                 }
1372             }
1373 
1374             if (layoutName == "HFlow") {
1375             } else if (layoutName == "VFlow") {
1376             } else { // grid layout
1377                 item->container()->setLayoutType(Form::Grid);
1378                 QGridLayout *layout = new QGridLayout(item->widget());
1379                 item->container()->setLayout(static_cast<QLayout*>(layout));
1380             }
1381             readChildNodes(item, container, node, w, buddies);
1382         } else if (tag == "vbox")  {
1383             item->container()->setLayoutType(Form::VBox);
1384             QVBoxLayout *layout = new QVBoxLayout(item->widget());
1385             item->container()->setLayout(static_cast<QLayout*>(layout));
1386             readChildNodes(item, container, node, w, buddies);
1387         } else if (tag == "hbox") {
1388             item->container()->setLayoutType(Form::HBox);
1389             QHBoxLayout *layout = new QHBoxLayout(item->widget());
1390             item->container()->setLayout(static_cast<QLayout*>(layout));
1391             readChildNodes(item, container, node, w, buddies);
1392         } else {// unknown tag, we let the Factory handle it
1393             if (w->metaObject()->className() == QString::fromLatin1("CustomWidget"))
1394                 item->storeUnknownProperty(node);
1395             else {
1396                 bool read = container->form()->library()->readSpecialProperty(
1397                                 w->metaObject()->className(), node, w, item);
1398                 if (!read) // the factory doesn't suport this property neither
1399                     item->storeUnknownProperty(node);
1400             }
1401         }
1402     }
1403 }
1404 
1405 /////////////////////////////////////////////////////////////////////////////
1406 ///////////// Helper functions //////////////////////////////////////
1407 /////////////////////////////////////////////////////////////////////////////
1408 
1409 void
1410 FormIO::addIncludeFileName(const QString &include, QDomDocument &domDoc)
1411 {
1412     if (include.isEmpty())
1413         return;
1414 
1415     QDomElement includes;
1416     QDomElement uiEl = domDoc.firstChildElement("UI");
1417     if (uiEl.firstChildElement("includehints").isNull()) {
1418         includes = domDoc.createElement("includehints");
1419         uiEl.appendChild(includes);
1420     } else {
1421         includes = uiEl.firstChildElement("includehints");
1422     }
1423 
1424     // Check if this include has already been saved, and return if it is the case
1425     for (QDomNode n = includes.firstChild(); !n.isNull(); n = n.nextSibling()) {
1426         if (n.toElement().text() == include)
1427             return;
1428     }
1429 
1430     QDomElement includeHint = domDoc.createElement("includehint");
1431     includes.appendChild(includeHint);
1432     QDomText includeText = domDoc.createTextNode(include);
1433     includeHint.appendChild(includeText);
1434 }
1435 
1436 QString
1437 FormIO::saveImage(QDomDocument &domDoc, const QPixmap &pixmap)
1438 {
1439     QDomElement images = domDoc.firstChildElement("images");
1440     if (images.isNull()) {
1441         images = domDoc.createElement("images");
1442         QDomElement ui = domDoc.firstChildElement("UI");
1443         ui.appendChild(images);
1444     }
1445 
1446     int count = images.childNodes().count();
1447     QDomElement image = domDoc.createElement("image");
1448     QString name = "image" + QString::number(count);
1449     image.setAttribute("name", name);
1450 
1451     const QImage img(pixmap.toImage());
1452     QByteArray ba;
1453     QBuffer buf(&ba);
1454     buf.open(QIODevice::WriteOnly | QIODevice::Text);
1455     const QByteArray format(img.depth() > 1 ? "XPM" : "XBM");
1456     QImageWriter imageWriter(&buf, format);
1457     imageWriter.write(img);
1458     buf.close();
1459     const QByteArray bazip = qCompress(ba);
1460     const int len = bazip.size();
1461 
1462     QDomElement data = domDoc.createElement("data");
1463     data.setAttribute("format", QString(format + ".GZ"));
1464     data.setAttribute("length", ba.size());
1465 
1466     static const char hexchars[] = "0123456789abcdef";
1467     QString content;
1468     for (int i = 4; i < len; i++) {
1469         uchar s = (uchar) bazip[i];
1470         content += hexchars[s >> 4];
1471         content += hexchars[s & 0x0f];
1472     }
1473 
1474     data.appendChild(domDoc.createTextNode(content));
1475     image.appendChild(data);
1476     images.appendChild(image);
1477 
1478     return name;
1479 }
1480 
1481 QPixmap
1482 FormIO::loadImage(QDomDocument domDoc, const QString& name)
1483 {
1484     QDomElement images = domDoc.firstChildElement("UI").firstChildElement("images");
1485     if (images.isNull())
1486         return QPixmap();
1487 
1488     QDomElement image;
1489     for (QDomNode n = images.firstChild(); !n.isNull(); n = n.nextSibling()) {
1490         if ((n.toElement().tagName() == "image") && (n.toElement().attribute("name") == name)) {
1491             image = n.toElement();
1492             break;
1493         }
1494     }
1495 
1496     QPixmap pix;
1497     QString data(image.firstChildElement("data").text());
1498     const int lengthOffset = 4;
1499     int baSize = data.length() / 2 + lengthOffset;
1500     uchar *ba = new uchar[baSize];
1501     for (int i = lengthOffset; i < baSize; ++i) {
1502         char h = data[2 * (i-lengthOffset)].toLatin1();
1503         char l = data[2 * (i-lengthOffset) + 1].toLatin1();
1504         uchar r = 0;
1505         if (h <= '9')
1506             r += h - '0';
1507         else
1508             r += h - 'a' + 10;
1509         r = r << 4;
1510         if (l <= '9')
1511             r += l - '0';
1512         else
1513             r += l - 'a' + 10;
1514         ba[i] = r;
1515     }
1516 
1517     QString format = image.firstChildElement("data").attribute("format", "PNG");
1518     if ((format == "XPM.GZ") || (format == "XBM.GZ")) {
1519         int len = image.attribute("length").toInt();
1520         if (len < data.length() * 5)
1521             len = data.length() * 5;
1522         // qUncompress() expects the first 4 bytes to be the expected length of
1523         // the uncompressed data
1524         ba[0] = (len & 0xff000000) >> 24;
1525         ba[1] = (len & 0x00ff0000) >> 16;
1526         ba[2] = (len & 0x0000ff00) >> 8;
1527         ba[3] = (len & 0x000000ff);
1528         QByteArray baunzip = qUncompress(ba, baSize);
1529         KexiUtils::loadPixmapFromData(&pix, baunzip, format.left(format.indexOf('.')).toLatin1());
1530     } else {
1531         QByteArray b(QByteArray::fromRawData((const char*)ba + lengthOffset, baSize - lengthOffset));
1532         KexiUtils::loadPixmapFromData(&pix, b, format.toLatin1());
1533     }
1534 
1535     delete[] ba;
1536 
1537     return pix;
1538 }
1539