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