diff --git a/src/formeditor/formIO.cpp b/src/formeditor/formIO.cpp index ebe1b38f1..813443f33 100644 --- a/src/formeditor/formIO.cpp +++ b/src/formeditor/formIO.cpp @@ -1,1539 +1,1539 @@ /* This file is part of the KDE project Copyright (C) 2004 Cedric Pasteur Copyright (C) 2005-2016 Jarosław Staniek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kformdesigner_export.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "FormWidget.h" #include "form.h" #include "container.h" #include "objecttree.h" #include "widgetlibrary.h" #include "events.h" #include "utils.h" #include "widgetwithsubpropertiesinterface.h" #include "formIO.h" #include "KexiVersion.h" //! @todo KEXI3 TODO pixmapcollection #ifdef KEXI_PIXMAP_COLLECTIONS_SUPPORT #include "pixmapcollection.h" #endif //! @return Value of attribute "name", or "objectName" in absence of "name". //! This is for compatibility with Kexi 1.x. static QString nameAttribute(const QDomElement& el) { QString res( el.attribute("name") ); if (res.isEmpty()) { res = el.attribute("objectName"); } return res; } //! A blank widget used when the class name is not supported CustomWidget::CustomWidget(const QByteArray &className, QWidget *parent) : QWidget(parent), m_className(className) { setBackgroundRole(QPalette::Dark); } CustomWidget::~CustomWidget() { } void CustomWidget::paintEvent(QPaintEvent *) { QPainter p(this); p.setBrush(palette().text()); QRect r(rect()); r.setX(r.x() + 2); p.drawText(r, Qt::AlignTop, m_className); } using namespace KFormDesigner; // FormIO itself KFORMDESIGNER_EXPORT QString KFormDesigner::version() { return QString::fromLatin1("%1.%2").arg(KEXI_STABLE_VERSION_MAJOR).arg(KEXI_STABLE_VERSION_MINOR); } ///////////////////////////////////////////////////////////////////////////// ///////////// Saving/loading functions ////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// FormIO::FormIO() { } FormIO::~FormIO() { } bool FormIO::saveFormToFile(Form *form, const QString &filename) { QString _filename; if (!form->filename().isEmpty() && filename.isEmpty()) { _filename = form->filename(); } if (filename.isEmpty()) { KexiFileDialog dlg(0, KexiFileDialog::SaveFile, "SaveForm"); dlg.setNameFilter("*.ui|" + xi18n("Qt Designer UI Files")); _filename = dlg.filename(); if (_filename.isEmpty()) { return false; } } else { _filename = filename; } form->setFilename(_filename); QDomDocument domDoc; if (!saveFormToDom(form, domDoc)) return false; QFile file(_filename); if (!file.open(QIODevice::WriteOnly)) return false; QTextStream stream(&file); stream << domDoc.toString(3); file.close(); return true; } bool FormIO::saveFormToByteArray(Form *form, QByteArray &dest) { QDomDocument domDoc; if (!saveFormToDom(form, domDoc)) return false; dest = domDoc.toByteArray(); return true; } bool FormIO::saveFormToString(Form *form, QString &dest, int indent) { QDomDocument domDoc; if (!saveFormToDom(form, domDoc)) return false; dest = domDoc.toString(indent); return true; } bool FormIO::saveFormToDom(Form *form, QDomDocument &domDoc) { domDoc = QDomDocument("UI"); QDomElement uiElement = domDoc.createElement("UI"); domDoc.appendChild(uiElement); uiElement.setAttribute("version", "3.1"); uiElement.setAttribute("stdsetdef", 1); //update format version information form->headerProperties()->insert("version", form->formatVersion()); //custom properties QDomElement headerPropertiesEl = domDoc.createElement("kfd:customHeader"); QHash::ConstIterator itEnd = form->headerProperties()->constEnd(); for (QHash::ConstIterator it = form->headerProperties()->constBegin(); it != itEnd; ++it) { headerPropertiesEl.setAttribute(it.key(), it.value()); } uiElement.appendChild(headerPropertiesEl); // We save the savePixmapsInline property in the Form QDomElement inlinePix = domDoc.createElement("pixmapinproject"); uiElement.appendChild(inlinePix); // We create the top class element QDomElement baseClass = domDoc.createElement("class"); uiElement.appendChild(baseClass); QDomText baseClassV = domDoc.createTextNode("QWidget"); baseClass.appendChild(baseClassV); // Save the toplevel widgets, and so the whole Form saveWidget(form->objectTree(), uiElement, domDoc); // We then save the layoutdefaults element QDomElement layoutDefaults = domDoc.createElement("layoutDefaults"); layoutDefaults.setAttribute("spacing", QString::number(form->defaultSpacing())); layoutDefaults.setAttribute("margin", QString::number(form->defaultMargin())); uiElement.appendChild(layoutDefaults); // Save tab Stops if (form->autoTabStops()) form->autoAssignTabStops(); QDomElement tabStops = domDoc.createElement("tabstops"); uiElement.appendChild(tabStops); foreach (ObjectTreeItem *item, *form->tabStops()) { QDomElement tabstop = domDoc.createElement("tabstop"); tabStops.appendChild(tabstop); QDomText tabStopText = domDoc.createTextNode(item->name()); tabstop.appendChild(tabStopText); } //! @todo KEXI3 TODO pixmapcollection #ifdef KEXI_PIXMAP_COLLECTIONS_SUPPORT // Save the Form 's PixmapCollection form->pixmapCollection()->save(uiElement); #endif #ifdef KFD_SIGSLOTS // Save the Form connections form->connectionBuffer()->save(uiElement); #endif form->setUndoStackClean(); return true; } bool FormIO::loadFormFromByteArray(Form *form, QWidget *container, QByteArray &src, bool preview) { QString errMsg; int errLine; int errCol; - QDomDocument inBuf; - bool parsed = inBuf.setContent(src, false, &errMsg, &errLine, &errCol); + QDomDocument domDoc; + bool parsed = domDoc.setContent(src, false, &errMsg, &errLine, &errCol); if (!parsed) { qDebug() << errMsg; qDebug() << "line:" << errLine << "col:" << errCol; return false; } - if (!loadFormFromDom(form, container, inBuf)) { + if (!loadFormFromDom(form, container, domDoc)) { return false; } if (preview) { form->setMode(Form::DataMode); } return true; } bool -FormIO::loadFormFromString(Form *form, QWidget *container, QString *src, bool preview) +FormIO::loadFormFromString(Form *form, QWidget *container, const QString &src, bool preview) { QString errMsg; int errLine; int errCol; #ifdef KEXI_DEBUG_GUI - form->m_recentlyLoadedUICode = *src; + form->m_recentlyLoadedUICode = src; #endif - QDomDocument inBuf; - bool parsed = inBuf.setContent(*src, false, &errMsg, &errLine, &errCol); + QDomDocument domDoc; + //qDebug() << qPrintable(src); + bool parsed = domDoc.setContent(src, false, &errMsg, &errLine, &errCol); if (!parsed) { qWarning() << errMsg; qWarning() << "line:" << errLine << "col: " << errCol; return false; } - if (!loadFormFromDom(form, container, inBuf)) { + if (!loadFormFromDom(form, container, domDoc)) { return false; } if (preview) { form->setMode(Form::DataMode); } return true; } bool FormIO::loadFormFromFile(Form *form, QWidget *container, const QString &filename) { QString errMsg; int errLine; int errCol; QString _filename; if (filename.isEmpty()) { KexiFileDialog dlg(0, KexiFileDialog::OpenFile, "LoadForm"); dlg.setNameFilter("*.ui|" + xi18n("Qt Designer UI Files")); _filename = dlg.filename(); if (_filename.isEmpty()) { return false; } } else { _filename = filename; } QFile file(_filename); if (!file.open(QIODevice::ReadOnly)) { //! @todo show err msg to the user qWarning() << "Cannot open the file " << _filename; return false; } QDomDocument doc; if (!doc.setContent(&file, false/* !namespaceProcessing*/, &errMsg, &errLine, &errCol)) { //! @todo show err msg to the user qWarning() << errMsg; qWarning() << errLine << "col:" << errCol; return false; } return loadFormFromDom(form, container, doc); } -bool -FormIO::loadFormFromDom(Form *form, QWidget *container, QDomDocument &inBuf) +bool FormIO::loadFormFromDom(Form *form, QWidget *container, const QDomDocument &domDoc) { - QDomElement ui = inBuf.firstChildElement("UI"); + QDomElement ui = domDoc.firstChildElement("UI"); //custom properties form->headerProperties()->clear(); QDomElement headerPropertiesEl = ui.firstChildElement("kfd:customHeader"); QDomAttr attr = headerPropertiesEl.firstChild().toAttr(); QDomNamedNodeMap attrs(headerPropertiesEl.attributes()); for(int i = 0; i < attrs.count(); ++i) { const QDomAttr attr(attrs.item(i).toAttr()); if (!attr.isNull()) { form->headerProperties()->insert(attr.name().toLatin1(), attr.value()); } } //update format version information const QString ver = form->headerProperties()->value("version"); //qDebug() << "Original format version:" << ver; form->setOriginalFormatVersion(ver); bool verOk; const double verNum = ver.toDouble(&verOk); const double currentVerNum = KFormDesigner::version().toDouble(); if (verOk) { if (verNum < currentVerNum) { //! @todo We can either 1) convert from old format and later save in a new one or 2) keep old format. //! To do this we may need to look at the original format version number. qDebug() << "The original format version is:" << ver << "current version:" << KFormDesigner::version(); //return false; } } form->setFormatVersion(ver); if (verNum > currentVerNum) { //! @todo display information about too new format and that "some information will not be available". qDebug() << "The original format is version" << ver << "is newer than current version:" << KFormDesigner::version(); } // Load the pixmap collection form->setPixmapsStoredInline(ui.firstChildElement("pixmapinproject").isNull() || !ui.firstChildElement("images").isNull()); //! @todo pixmapcollection #ifdef KEXI_PIXMAP_COLLECTIONS_SUPPORT form->pixmapCollection()->load(ui.namedItem("collection")); #endif QDomElement element = ui.firstChildElement("widget"); createToplevelWidget(form, container, element); // Loading the tabstops QDomElement tabStops = ui.firstChildElement("tabstops"); if (!tabStops.isNull()) { int i = 0; int itemsNotFound = 0; for (QDomNode n = tabStops.firstChild(); !n.isNull(); n = n.nextSibling(), i++) { QString name = n.toElement().text(); ObjectTreeItem *item = form->objectTree()->lookup(name); if (!item) { qWarning() << "ERROR : no ObjectTreeItem "; continue; } const int index = form->tabStops()->indexOf(item); /* Compute a real destination index: "a number of not found items so far". */ const int realIndex = i - itemsNotFound; if ((index != -1) && (index != realIndex)) { // the widget is not in the same place, so we move it form->tabStops()->removeOne(item); form->tabStops()->insert(realIndex, item); } if (index == -1) { itemsNotFound++; qDebug() << "FormIO: item '" << name << "' not in list"; } } } #ifdef KFD_SIGSLOTS // Load the form connections form->connectionBuffer()->load(ui.namedItem("connections")); #endif return true; } ///////////////////////////////////////////////////////////////////////////// ///////////// Functions to save/load properties ///////////////////////////// ///////////////////////////////////////////////////////////////////////////// void FormIO::savePropertyValue(ObjectTreeItem *item, QDomElement &parentNode, QDomDocument &parent, const char *name, const QVariant &value) { // Widget specific properties and attributes // qDebug() << "Saving the property: " << name; Form *form = item->container() ? item->container()->form() : item->parent()->container()->form(); WidgetWithSubpropertiesInterface* subpropIface = dynamic_cast(item->widget()); QWidget *subwidget = item->widget(); bool addSubwidgetFlag = false; int propertyId = item->widget()->metaObject()->indexOfProperty(name); const bool propertyIsName = qstrcmp(name, "objectName") == 0 || qstrcmp(name, "name") == 0; if (!propertyIsName && propertyId == -1 && subpropIface && subpropIface->subwidget()) { // try property from subwidget subwidget = subpropIface->subwidget(); propertyId = subpropIface->subwidget()->metaObject()->indexOfProperty(name); addSubwidgetFlag = true; } if (!propertyIsName && propertyId == -1) { qDebug() << "The object doesn't have this property. Let's try the WidgetLibrary."; if (form->library()) form->library()->saveSpecialProperty(item->widget()->metaObject()->className(), name, value, item->widget(), parentNode, parent); return; } QMetaProperty meta; if (!propertyIsName) { meta = subwidget->metaObject()->property(propertyId); } if (!propertyIsName && (!meta.isValid() || !meta.isStored(subwidget))) //not storable return; QDomElement propertyE = parent.createElement("property"); propertyE.setAttribute("name", propertyIsName ? "name" /* compat with 1.x */ : name); if (addSubwidgetFlag) propertyE.setAttribute("subwidget", "true"); if (meta.isValid() && meta.isEnumType()) { // this property is enum or set type QDomElement type; QDomText valueE; if (meta.isFlagType()) { type = parent.createElement("set"); valueE = parent.createTextNode( meta.enumerator().valueToKeys(value.toInt())); type.appendChild(valueE); } else { QString s = meta.enumerator().valueToKey(value.toInt()); type = parent.createElement("enum"); valueE = parent.createTextNode(s); type.appendChild(valueE); } propertyE.appendChild(type); parentNode.appendChild(propertyE); return; } if (value.type() == QVariant::Pixmap) { QDomText valueE; QDomElement type = parent.createElement("pixmap"); QByteArray property = propertyE.attribute("name").toLatin1(); //! @todo QCString pixmapName = m_currentRecord->widget()->property("pixmapName").toCString(); if (form->pixmapsStoredInline() /* (js)too risky: || m_currentRecord->pixmapName(property).isNull() */) valueE = parent.createTextNode(saveImage(parent, value.value())); else valueE = parent.createTextNode(item->pixmapName(property)); type.appendChild(valueE); propertyE.appendChild(type); parentNode.appendChild(propertyE); return; } // Saving a "normal" property writeVariant(parent, propertyE, value); parentNode.appendChild(propertyE); } void FormIO::writeVariant(QDomDocument &parent, QDomElement &parentNode, const QVariant& value) { QDomElement type; QDomText valueE; switch (value.type()) { case QVariant::String: { type = parent.createElement("string"); valueE = parent.createTextNode(value.toString()); type.appendChild(valueE); break; } case QVariant::ByteArray: { type = parent.createElement("cstring"); valueE = parent.createTextNode(value.toString()); type.appendChild(valueE); break; } case QVariant::Rect: { type = parent.createElement("rect"); QDomElement x = parent.createElement("x"); QDomElement y = parent.createElement("y"); QDomElement w = parent.createElement("width"); QDomElement h = parent.createElement("height"); QDomText valueX = parent.createTextNode(QString::number(value.toRect().x())); QDomText valueY = parent.createTextNode(QString::number(value.toRect().y())); QDomText valueW = parent.createTextNode(QString::number(value.toRect().width())); QDomText valueH = parent.createTextNode(QString::number(value.toRect().height())); x.appendChild(valueX); y.appendChild(valueY); w.appendChild(valueW); h.appendChild(valueH); type.appendChild(x); type.appendChild(y); type.appendChild(w); type.appendChild(h); break; } case QVariant::Color: { type = parent.createElement("color"); QDomElement r = parent.createElement("red"); QDomElement g = parent.createElement("green"); QDomElement b = parent.createElement("blue"); const QColor color(value.value()); QDomText valueR = parent.createTextNode(QString::number(color.red())); QDomText valueG = parent.createTextNode(QString::number(color.green())); QDomText valueB = parent.createTextNode(QString::number(color.blue())); r.appendChild(valueR); g.appendChild(valueG); b.appendChild(valueB); type.appendChild(r); type.appendChild(g); type.appendChild(b); break; } case QVariant::Bool: { type = parent.createElement("bool"); //valueE = parent.createTextNode(QString::number(value.toBool())); valueE = parent.createTextNode(value.toBool() ? "true" : "false"); type.appendChild(valueE); break; } case QVariant::Int: case QVariant::UInt: { type = parent.createElement("number"); valueE = parent.createTextNode(QString::number(value.toInt())); type.appendChild(valueE); break; } case QVariant::Size: { type = parent.createElement("size"); QDomElement w = parent.createElement("width"); QDomElement h = parent.createElement("height"); QDomText valueW = parent.createTextNode(QString::number(value.toSize().width())); QDomText valueH = parent.createTextNode(QString::number(value.toSize().height())); w.appendChild(valueW); h.appendChild(valueH); type.appendChild(w); type.appendChild(h); break; } case QVariant::Point: { type = parent.createElement("point"); QDomElement x = parent.createElement("x"); QDomElement y = parent.createElement("y"); QDomText valueX = parent.createTextNode(QString::number(value.toPoint().x())); QDomText valueY = parent.createTextNode(QString::number(value.toPoint().y())); x.appendChild(valueX); y.appendChild(valueY); type.appendChild(x); type.appendChild(y); break; } case QVariant::Font: { type = parent.createElement("font"); QDomElement f = parent.createElement("family"); QDomElement p = parent.createElement("pointsize"); QDomElement w = parent.createElement("weight"); QDomElement b = parent.createElement("bold"); QDomElement i = parent.createElement("italic"); QDomElement u = parent.createElement("underline"); QDomElement s = parent.createElement("strikeout"); const QFont font(value.value()); QDomText valueF = parent.createTextNode(font.family()); QDomText valueP = parent.createTextNode(QString::number(font.pointSize())); QDomText valueW = parent.createTextNode(QString::number(font.weight())); QDomText valueB = parent.createTextNode(QString::number(font.bold())); QDomText valueI = parent.createTextNode(QString::number(font.italic())); QDomText valueU = parent.createTextNode(QString::number(font.underline())); QDomText valueS = parent.createTextNode(QString::number(font.strikeOut())); f.appendChild(valueF); p.appendChild(valueP); w.appendChild(valueW); b.appendChild(valueB); i.appendChild(valueI); u.appendChild(valueU); s.appendChild(valueS); type.appendChild(f); type.appendChild(p); type.appendChild(w); type.appendChild(b); type.appendChild(i); type.appendChild(u); type.appendChild(s); break; } case QVariant::Cursor: { type = parent.createElement("cursor"); valueE = parent.createTextNode(QString::number(value.value().shape())); type.appendChild(valueE); break; } case QVariant::SizePolicy: { type = parent.createElement("sizepolicy"); QDomElement h(parent.createElement("hsizetype")); QDomElement v(parent.createElement("vsizetype")); QDomElement hs(parent.createElement("horstretch")); QDomElement vs(parent.createElement("verstretch")); const QSizePolicy sizePolicy(value.value()); const QDomText valueH(parent.createTextNode(QString::number(sizePolicy.horizontalPolicy()))); const QDomText valueV(parent.createTextNode(QString::number(sizePolicy.verticalPolicy()))); const QDomText valueHS(parent.createTextNode(QString::number(sizePolicy.horizontalStretch()))); const QDomText valueVS(parent.createTextNode(QString::number(sizePolicy.verticalStretch()))); h.appendChild(valueH); v.appendChild(valueV); hs.appendChild(valueHS); vs.appendChild(valueVS); type.appendChild(h); type.appendChild(v); type.appendChild(hs); type.appendChild(vs); break; } case QVariant::Time: { type = parent.createElement("time"); QDomElement h = parent.createElement("hour"); QDomElement m = parent.createElement("minute"); QDomElement s = parent.createElement("second"); QDomText valueH = parent.createTextNode(QString::number(value.toTime().hour())); QDomText valueM = parent.createTextNode(QString::number(value.toTime().minute())); QDomText valueS = parent.createTextNode(QString::number(value.toTime().second())); h.appendChild(valueH); m.appendChild(valueM); s.appendChild(valueS); type.appendChild(h); type.appendChild(m); type.appendChild(s); break; } case QVariant::Date: { type = parent.createElement("date"); QDomElement y = parent.createElement("year"); QDomElement m = parent.createElement("month"); QDomElement d = parent.createElement("day"); QDomText valueY = parent.createTextNode(QString::number(value.toDate().year())); QDomText valueM = parent.createTextNode(QString::number(value.toDate().month())); QDomText valueD = parent.createTextNode(QString::number(value.toDate().day())); y.appendChild(valueY); m.appendChild(valueM); d.appendChild(valueD); type.appendChild(y); type.appendChild(m); type.appendChild(d); break; } case QVariant::DateTime: { type = parent.createElement("datetime"); QDomElement h = parent.createElement("hour"); QDomElement m = parent.createElement("minute"); QDomElement s = parent.createElement("second"); QDomElement y = parent.createElement("year"); QDomElement mo = parent.createElement("month"); QDomElement d = parent.createElement("day"); QDomText valueH = parent.createTextNode(QString::number(value.toDateTime().time().hour())); QDomText valueM = parent.createTextNode(QString::number(value.toDateTime().time().minute())); QDomText valueS = parent.createTextNode(QString::number(value.toDateTime().time().second())); QDomText valueY = parent.createTextNode(QString::number(value.toDateTime().date().year())); QDomText valueMo = parent.createTextNode(QString::number(value.toDateTime().date().month())); QDomText valueD = parent.createTextNode(QString::number(value.toDateTime().date().day())); h.appendChild(valueH); m.appendChild(valueM); s.appendChild(valueS); y.appendChild(valueY); mo.appendChild(valueMo); d.appendChild(valueD); type.appendChild(h); type.appendChild(m); type.appendChild(s); type.appendChild(y); type.appendChild(mo); type.appendChild(d); break; } default: break; } parentNode.appendChild(type); } void FormIO::savePropertyElement(QDomElement &parentNode, QDomDocument &domDoc, const QString &tagName, const QString &property, const QVariant &value) { QDomElement propertyE = domDoc.createElement(tagName); propertyE.setAttribute("name", property); writeVariant(domDoc, propertyE, value); parentNode.appendChild(propertyE); } QVariant FormIO::readPropertyValue(Form *form, QDomNode node, QObject *obj, const QString &name) { QDomElement tag = node.toElement(); QString text = tag.text(); QString type = tag.tagName(); if (type == "string" || type == "cstring") return text; else if (type == "rect") { QDomElement x = node.firstChildElement("x"); QDomElement y = node.firstChildElement("y"); QDomElement w = node.firstChildElement("width"); QDomElement h = node.firstChildElement("height"); int rx = x.text().toInt(); int ry = y.text().toInt(); int rw = w.text().toInt(); int rh = h.text().toInt(); return QRect(rx, ry, rw, rh); } else if (type == "color") { const QDomElement r(node.firstChildElement("red")); const QDomElement g(node.firstChildElement("green")); const QDomElement b(node.firstChildElement("blue")); return QColor(r.text().toInt(), g.text().toInt(), b.text().toInt()); } else if (type == "bool") { if (text == "true") return true; else if (text == "false") return false; return text.toInt() != 0; } else if (type == "number") { return text.toInt(); } else if (type == "size") { QDomElement w = node.firstChildElement("width"); QDomElement h = node.firstChildElement("height"); return QSize(w.text().toInt(), h.text().toInt()); } else if (type == "point") { QDomElement x = node.firstChildElement("x"); QDomElement y = node.firstChildElement("y"); return QPoint(x.text().toInt(), y.text().toInt()); } else if (type == "font") { QDomElement fa = node.firstChildElement("family"); QDomElement p = node.firstChildElement("pointsize"); QDomElement w = node.firstChildElement("weight"); QDomElement b = node.firstChildElement("bold"); QDomElement i = node.firstChildElement("italic"); QDomElement u = node.firstChildElement("underline"); QDomElement s = node.firstChildElement("strikeout"); QFont f; f.setFamily(fa.text()); f.setPointSize(p.text().toInt()); f.setWeight(w.text().toInt()); f.setBold(b.text().toInt()); f.setItalic(i.text().toInt()); f.setUnderline(u.text().toInt()); f.setStrikeOut(s.text().toInt()); return f; } else if (type == "cursor") { return QCursor((Qt::CursorShape) text.toInt()); } else if (type == "time") { QDomElement h = node.firstChildElement("hour"); QDomElement m = node.firstChildElement("minute"); QDomElement s = node.firstChildElement("second"); return QTime(h.text().toInt(), m.text().toInt(), s.text().toInt()); } else if (type == "date") { QDomElement y = node.firstChildElement("year"); QDomElement m = node.firstChildElement("month"); QDomElement d = node.firstChildElement("day"); return QDate(y.text().toInt(), m.text().toInt(), d.text().toInt()); } else if (type == "datetime") { QDomElement h = node.firstChildElement("hour"); QDomElement m = node.firstChildElement("minute"); QDomElement s = node.firstChildElement("second"); QDomElement y = node.firstChildElement("year"); QDomElement mo = node.firstChildElement("month"); QDomElement d = node.firstChildElement("day"); QTime t(h.text().toInt(), m.text().toInt(), s.text().toInt()); QDate da(y.text().toInt(), mo.text().toInt(), d.text().toInt()); return QDateTime(da, t); } else if (type == "sizepolicy") { QDomElement h = node.firstChildElement("hsizetype"); QDomElement v = node.firstChildElement("vsizetype"); QDomElement hs = node.firstChildElement("horstretch"); QDomElement vs = node.firstChildElement("verstretch"); QSizePolicy s; s.setHorizontalPolicy((QSizePolicy::Policy)h.text().toInt()); s.setVerticalPolicy((QSizePolicy::Policy)v.text().toInt()); s.setHorizontalStretch(hs.text().toInt()); s.setVerticalStretch(vs.text().toInt()); return s; } else if (type == "pixmap") { //! @todo pixmapcollection #ifdef KEXI_PIXMAP_COLLECTIONS_SUPPORT if (form->pixmapsStoredInline() || !m_currentForm || !m_currentRecord || !m_currentForm->pixmapCollection()->contains(text)) return loadImage(tag.ownerDocument(), text); else { m_currentRecord->setPixmapName(name.toLatin1(), text); return form->pixmapCollection()->getPixmap(text); } #else Q_UNUSED(form); #endif return QPixmap(); } else if (type == "enum") { return text; } else if (type == "set") { WidgetWithSubpropertiesInterface* subpropIface = dynamic_cast(obj); QObject *subobject = (subpropIface && subpropIface->subwidget()) ? subpropIface->subwidget() : obj; const QMetaProperty meta(KexiUtils::findPropertyWithSuperclasses(subobject, name.toLatin1())); if (meta.isValid()) { if (meta.isFlagType()) { return meta.enumerator().keysToValue(text.toLatin1()); } else { // Metaproperty not found, probably because subwidget is not created. // We will return a string list here with hope that names will // be resolved and translated into an integer value later when subwidget is created, // e.g. near KexiFormView::updateValuesForSubproperties() return text.split('|'); } } } return QVariant(); } ///////////////////////////////////////////////////////////////////////////// ///////////// Functions to save/load widgets //////////////////////////////// ///////////////////////////////////////////////////////////////////////////// void FormIO::saveWidget(ObjectTreeItem *item, QDomElement &parent, QDomDocument &domDoc, bool insideGridLayout) { if (!item) return; //qDebug() << item->className() << item->widget()->objectName(); Form *form = item->container() ? item->container()->form() : item->parent()->container()->form(); WidgetLibrary *lib = form->library(); // We create the "widget" element QDomElement tclass = domDoc.createElement("widget"); parent.appendChild(tclass); if (insideGridLayout) { tclass.setAttribute("row", item->gridRow()); tclass.setAttribute("column", item->gridCol()); if (item->spanMultipleCells()) { tclass.setAttribute("rowspan", item->gridRowSpan()); tclass.setAttribute("colspan", item->gridColSpan()); } } if (!item->parent()) // Toplevel widget tclass.setAttribute("class", "QWidget"); // For compatibility, HBox, VBox and Grid are saved as "QLayoutWidget" else if (KexiUtils::objectIsA(item->widget(), QList() << "HBox" << "VBox" << "Grid" << "HFlow" << "VFlow")) tclass.setAttribute("class", "QLayoutWidget"); else if (KexiUtils::objectIsA(item->widget(), "CustomWidget")) tclass.setAttribute("class", item->className()); else // Normal widgets tclass.setAttribute("class", lib->savingName(item->widget()->metaObject()->className())); // We save every property in the modifProp list of the ObjectTreeItem QHash hash(*(item->modifiedProperties())); QStringList names(hash.keys()); savePropertyValue(item, tclass, domDoc, "objectName", item->widget()->objectName()); names.removeOne("objectName"); // Important: save dataSource property FIRST before properties like "alignment" // - needed when subproperties are defined after subwidget creation, and subwidget is created after setting "dataSource" // (this is the case for KexiDBAutoField) //! @todo more properties like "dataSource" may needed here... // if (-1 != item->widget()->metaObject()->findProperty("dataSource")) // savePropertyValue(tclass, domDoc, "dataSource", item->widget()->property("dataSource"), item->widget()); // We don't want to save the geometry if the widget is inside a layout (so parent.tagName() == "grid" for example) if (item && !item->parent()) { // save form widget size, but not its position savePropertyValue(item, tclass, domDoc, "geometry", QRect(QPoint(0, 0), item->widget()->size())); } // normal widget (if == "UI', it means we're copying widget) else if (parent.tagName() == "widget" || parent.tagName() == "UI") savePropertyValue(item, tclass, domDoc, "geometry", item->widget()->property("geometry")); names.removeOne("geometry"); names.removeOne("layout"); // Save the buddy widget for a label if ( qobject_cast(item->widget()) && qobject_cast(item->widget())->buddy()) { savePropertyElement( tclass, domDoc, "property", "buddy", qobject_cast(item->widget())->buddy()->objectName()); } if (names.contains("paletteBackgroundColor")) { savePropertyElement( tclass, domDoc, "property", "paletteBackgroundColor", item->widget()->palette().color(item->widget()->backgroundRole())); names.removeOne("paletteBackgroundColor"); } if (names.contains("paletteForegroundColor")) { savePropertyElement( tclass, domDoc, "property", "paletteForegroundColor", item->widget()->palette().color(item->widget()->foregroundRole())); names.removeOne("paletteForegroundColor"); } QStringList alignProperties; alignProperties << "hAlign" << "vAlign" << "wordbreak" << "alignment"; foreach (const QString& name, alignProperties) { if (names.contains(name)) { names.removeOne(name); savePropertyValue( item, tclass, domDoc, "alignment", item->widget()->property("alignment")); break; } } foreach (const QString& name, names) { savePropertyValue(item, tclass, domDoc, name.toLatin1(), item->widget()->property(name.toLatin1())); } hash.clear(); if (KexiUtils::objectIsA(item->widget(), "CustomWidget")) { QDomDocument doc("TEMP"); doc.setContent(item->unknownProperties()); for (QDomNode n = doc.firstChild(); !n.isNull(); n = n.nextSibling()) { tclass.appendChild(n.cloneNode()); } } // Saving container 's layout if there is one QDomElement layout; if (item->container() && item->container()->layoutType() != Form::NoLayout) { if (item->container()->layout()) { // there is a layout layout = domDoc.createElement("temp"); savePropertyValue(item, layout, domDoc, "objectName", "unnamed"); if (item->modifiedProperties()->contains("layoutMargin")) savePropertyElement(layout, domDoc, "property", "margin", item->container()->layoutMargin()); if (item->modifiedProperties()->contains("layoutSpacing")) savePropertyElement(layout, domDoc, "property", "spacing", item->container()->layoutSpacing()); tclass.appendChild(layout); } } Form::LayoutType layoutType = item->container() ? item->container()->layoutType() : Form::NoLayout; switch (layoutType) { case Form::Grid: { // grid layout layout.setTagName("grid"); foreach (ObjectTreeItem *titem, *item->children()) { saveWidget(titem, layout, domDoc, true); } break; } case Form::HBox: case Form::VBox: { // as we don't save geometry, we need to sort widgets in the right order, not creation order CustomSortableWidgetList *list; if (layout.tagName() == "hbox") { list = new HorizontalWidgetList(item->container()->form()->toplevelContainer()->widget()); layout.setTagName("hbox"); } else { list = new HorizontalWidgetList(item->container()->form()->toplevelContainer()->widget()); layout.setTagName("vbox"); } foreach (ObjectTreeItem *titem, *item->children()) { list->append(titem->widget()); } list->sort(); foreach (QWidget *w, *list) { ObjectTreeItem *titem = item->container()->form()->objectTree()->lookup( w->objectName() ); if (item) saveWidget(titem, layout, domDoc); } delete list; break; } case Form::HFlow: case Form::VFlow: { break; } default: { foreach (ObjectTreeItem *titem, *item->children()) { saveWidget(titem, tclass, domDoc); } } } addIncludeFileName(lib->includeFileName( item->widget()->metaObject()->className()), domDoc); } void FormIO::cleanClipboard(QDomElement &uiElement) { // remove includehints element not needed if (!uiElement.firstChildElement("includehints").isNull()) uiElement.removeChild(uiElement.firstChildElement("includehints")); // and ensure images and connection are at the end if (!uiElement.firstChildElement("connections").isNull()) uiElement.insertAfter(uiElement.firstChildElement("connections"), QDomNode()); if (!uiElement.firstChildElement("images").isNull()) uiElement.insertAfter(uiElement.firstChildElement("images"), QDomNode()); } void FormIO::loadWidget(Container *container, const QDomElement &el, QWidget *parent, QHash *buddies) { Form *form = container->form(); // We first look for the widget's name QString wname; for (QDomNode n = el.firstChild(); !n.isNull(); n = n.nextSibling()) { if ( n.toElement().tagName() == "property" && nameAttribute(n.toElement()) == "name" /* compat with 1.x */) { wname = n.toElement().text(); break; } } ObjectTreeItem *item = container->form()->objectTree()->lookup(wname); if (item) { qWarning() << "Widget" << wname << "already exists! Skipping..."; return; } QByteArray classname, alternate; // check if this classname is an alternate one, and replace it if necessary classname = el.attribute("class").toLatin1(); alternate = container->form()->library()->classNameForAlternate(classname); QWidget *w; if (alternate == "CustomWidget") { w = new CustomWidget(classname, container->widget()); w->setObjectName(wname); } else { if (!alternate.isEmpty()) { classname = alternate; } WidgetFactory::CreateWidgetOptions widgetOptions = WidgetFactory::DefaultOptions; if (container->form()->mode() != Form::DesignMode) { widgetOptions ^= WidgetFactory::DesignViewMode; } if (!parent) w = container->form()->library()->createWidget(classname, container->widget(), wname.toLatin1(), container, widgetOptions); else w = container->form()->library()->createWidget(classname, parent, wname.toLatin1(), container, widgetOptions); } if (!w) return; //! @todo allow setting this for data view mode as well if (form->mode() == Form::DesignMode) { //don't generate accelerators for widgets in design mode KAcceleratorManager::setNoAccel(w); } w->show(); // We create and insert the ObjectTreeItem at the good place in the ObjectTree item = container->form()->objectTree()->lookup(wname); //qDebug() << wname << item << classname << (parent ? parent->objectName() : QString()); if (!item) { // not yet created //qDebug() << "Creating ObjectTreeItem:"; item = new ObjectTreeItem(container->form()->library()->displayName(classname), wname, w, container); if (parent) { ObjectTreeItem *titem = container->form()->objectTree()->lookup(parent->objectName()); if (titem) container->form()->objectTree()->addItem(titem, item); else qWarning() << "ERROR no parent widget"; } else container->form()->objectTree()->addItem(container->objectTree(), item); } //assign item for its widget if it supports DesignTimeDynamicChildWidgetHandler interface //(e.g. KexiDBAutoField) DesignTimeDynamicChildWidgetHandler *childHandler = dynamic_cast(w); if (container->form()->mode() == Form::DesignMode && childHandler) { childHandler->assignItem(item); } // if we are inside a Grid, we need to insert the widget in the good cell if (container->layoutType() == Form::Grid) { QGridLayout *layout = (QGridLayout*)container->layout(); if (el.hasAttribute("rowspan")) { // widget spans multiple cells if (layout) { layout->addWidget( w, el.attribute("row").toInt(), el.attribute("column").toInt(), el.attribute("rowspan").toInt(), el.attribute("colspan").toInt()); //! @todo alignment attribute? } item->setGridPos(el.attribute("row").toInt(), el.attribute("column").toInt(), el.attribute("rowspan").toInt(), el.attribute("colspan").toInt()); } else { if (layout) { layout->addWidget(w, el.attribute("row").toInt(), el.attribute("column").toInt()); } item->setGridPos(el.attribute("row").toInt(), el.attribute("column").toInt(), 0, 0); } } else if (container->layout()) container->layout()->addWidget(w); readChildNodes(item, container, el, w, buddies); if (item->container() && item->container()->layout()) item->container()->layout()->activate(); // add the autoSaveProperties in the modifProp list of the ObjectTreeItem, so that they are saved later const QList autoSaveProperties( container->form()->library()->autoSaveProperties(w->metaObject()->className()) ); KFormDesigner::WidgetWithSubpropertiesInterface* subpropIface = dynamic_cast(w); QWidget *subwidget = (subpropIface && subpropIface->subwidget()) ? subpropIface->subwidget() : w; foreach (const QByteArray &propName, autoSaveProperties) { if (subwidget && -1 != subwidget->metaObject()->indexOfProperty(propName)) { item->addModifiedProperty(propName, subwidget->property(propName)); } } const QVariant autoFillBackgroundValue = item->modifiedProperties()->value("autoFillBackground"); const QVariant paletteForegroundColorValue = item->modifiedProperties()->value("paletteForegroundColor"); if (!paletteForegroundColorValue.isNull() && autoFillBackgroundValue.isNull()) { // Sanity: force fill background if there's color but not 'fill background' set w->setAutoFillBackground(true); } } void FormIO::createToplevelWidget(Form *form, QWidget *container, QDomElement &el) { // We first look for the widget's name QString wname; for (QDomNode n = el.firstChild(); !n.isNull(); n = n.nextSibling()) { if ( n.toElement().tagName() == "property" && nameAttribute(n.toElement()) == "name" /* compat with 1.x */) { wname = n.toElement().text(); break; } } // And rename the widget and its ObjectTreeItem container->setObjectName(wname); if (form->objectTree()) form->objectTree()->rename(form->objectTree()->name(), wname); form->setInteractiveMode(false); QHash buddies; readChildNodes(form->objectTree(), form->toplevelContainer(), el, container, &buddies); // Now the Form is fully loaded, we can assign the buddies for (QHash::ConstIterator it(buddies.constBegin()); it!=buddies.constEnd(); ++it) { ObjectTreeItem *item = form->objectTree()->lookup(it.key()); if (!item || !item->widget()) { qDebug() << "Cannot assign buddy for widget " << it.value()->objectName() << " to " << it.key(); continue; } it.value()->setBuddy(item->widget()); } form->setInteractiveMode(true); } void FormIO::readChildNodes(ObjectTreeItem *item, Container *container, const QDomElement &el, QWidget *w, QHash *buddies) { QString eltag = el.tagName(); WidgetWithSubpropertiesInterface* subpropIface = dynamic_cast(w); QWidget *subwidget = (subpropIface && subpropIface->subwidget()) ? subpropIface->subwidget() : w; for (QDomNode n = el.firstChild(); !n.isNull(); n = n.nextSibling()) { QString tag = n.toElement().tagName(); QDomElement node = n.toElement(); if ((tag == "property") || (tag == "attribute")) { const QString name( node.attribute("name") ); const bool isQt3NameProperty = name == QLatin1String("name"); //if(name == "geometry") // hasGeometryProp = true; if ( (eltag == "grid" || eltag == "hbox" || eltag == "vbox") && (isQt3NameProperty || name == "objectName") ) { // we don't care about layout names continue; } if (node.attribute("subwidget") == "true") { //this is property for subwidget: remember it for delayed setting //because now the subwidget could be not created yet (true e.g. for KexiDBAutoField) item->addSubproperty(name.toLatin1(), readPropertyValue(container->form(), node.firstChild(), w, name)); const QVariant val(readPropertyValue(container->form(), node.firstChild(), w, name)); //qDebug() << val.toStringList(); item->addSubproperty(name.toLatin1(), val); //subwidget->setProperty(name.toLatin1(), val); item->addModifiedProperty(name.toLatin1(), val); continue; } // We cannot assign the buddy now as the buddy widget may not be created yet if (name == "buddy") { if (buddies && qobject_cast(w)) { buddies->insert(readPropertyValue( container->form(), node.firstChild(), w, name).toString(), qobject_cast(w)); } } else if ( (eltag == "grid" || eltag == "hbox" || eltag == "vbox") && item->container() && item->container()->layout() ) { // We load the margin of a Layout if (name == "margin") { int margin = readPropertyValue(container->form(), node.firstChild(), w, name).toInt(); item->container()->setLayoutMargin(margin); item->container()->layout()->setMargin(margin); } // We load the spacing of a Layout else if (name == "spacing") { int spacing = readPropertyValue(container->form(), node.firstChild(), w, name).toInt(); item->container()->setLayoutSpacing(spacing); item->container()->layout()->setSpacing(spacing); } } else if (name == "paletteBackgroundColor" || name == "paletteForegroundColor") { QPalette widgetPalette(w->palette()); QVariant val(readPropertyValue(container->form(), node.firstChild(), w, name)); if (!val.isNull()) widgetPalette.setColor( name == "paletteBackgroundColor" ? w->backgroundRole() : w->foregroundRole(), val.value()); w->setPalette(widgetPalette); if (name == "paletteBackgroundColor") { w->setAutoFillBackground(val.value().isValid()); } item->addModifiedProperty(name.toLatin1(), val); } else if (!isQt3NameProperty && -1 == subwidget->metaObject()->indexOfProperty(name.toLatin1())) { // If the object doesn't have this property, we let the Factory handle it (maybe a special property) if (w->metaObject()->className() == QString::fromLatin1("CustomWidget")) item->storeUnknownProperty(node); else { bool read = container->form()->library()->readSpecialProperty( w->metaObject()->className(), node, w, item); if (!read) // the factory doesn't support this property neither item->storeUnknownProperty(node); } } else { // we have a normal property, let's load it QVariant val(readPropertyValue(container->form(), node.firstChild(), w, name)); if (name == "geometry" && dynamic_cast(w)) { //fix geometry if needed - this is top level form widget QRect r(val.toRect()); if (r.left() < 0) //negative X! r.moveLeft(0); if (r.top() < 0) //negative Y! r.moveTop(0); val = r; } QByteArray realName; if (isQt3NameProperty) { realName = "objectName"; } else { realName = name.toLatin1(); } subwidget->setProperty(realName, val); // int count = w->metaObject()->findProperty(name, true); // const QMetaProperty *meta = w->metaObject()->property(count, true); // if(meta && meta->isEnumType()) { // val = w->property(name.toLatin1()); //update: we want a numeric value of enum // } item->addModifiedProperty(realName, val); } } else if (tag == "widget") { // a child widget if (item->container()) // we are a Container loadWidget(item->container(), node, 0, buddies); else loadWidget(container, node, w, buddies); } else if (tag == "spacer") { loadWidget(container, node, w, buddies); } else if (tag == "grid") { // first, see if it is flow layout QString layoutName; for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { if ( child.toElement().tagName() == "property" && child.toElement().attribute("name") == "customLayout") { layoutName = child.toElement().text(); break; } } if (layoutName == "HFlow") { } else if (layoutName == "VFlow") { } else { // grid layout item->container()->setLayoutType(Form::Grid); QGridLayout *layout = new QGridLayout(item->widget()); item->container()->setLayout((QLayout*)layout); } readChildNodes(item, container, node, w, buddies); } else if (tag == "vbox") { item->container()->setLayoutType(Form::VBox); QVBoxLayout *layout = new QVBoxLayout(item->widget()); item->container()->setLayout((QLayout*)layout); readChildNodes(item, container, node, w, buddies); } else if (tag == "hbox") { item->container()->setLayoutType(Form::HBox); QHBoxLayout *layout = new QHBoxLayout(item->widget()); item->container()->setLayout((QLayout*)layout); readChildNodes(item, container, node, w, buddies); } else {// unknown tag, we let the Factory handle it if (w->metaObject()->className() == QString::fromLatin1("CustomWidget")) item->storeUnknownProperty(node); else { bool read = container->form()->library()->readSpecialProperty( w->metaObject()->className(), node, w, item); if (!read) // the factory doesn't suport this property neither item->storeUnknownProperty(node); } } } } ///////////////////////////////////////////////////////////////////////////// ///////////// Helper functions ////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// void FormIO::addIncludeFileName(const QString &include, QDomDocument &domDoc) { if (include.isEmpty()) return; QDomElement includes; QDomElement uiEl = domDoc.firstChildElement("UI"); if (uiEl.firstChildElement("includehints").isNull()) { includes = domDoc.createElement("includehints"); uiEl.appendChild(includes); } else { includes = uiEl.firstChildElement("includehints"); } // Check if this include has already been saved, and return if it is the case for (QDomNode n = includes.firstChild(); !n.isNull(); n = n.nextSibling()) { if (n.toElement().text() == include) return; } QDomElement includeHint = domDoc.createElement("includehint"); includes.appendChild(includeHint); QDomText includeText = domDoc.createTextNode(include); includeHint.appendChild(includeText); } QString FormIO::saveImage(QDomDocument &domDoc, const QPixmap &pixmap) { QDomElement images = domDoc.firstChildElement("images"); if (images.isNull()) { images = domDoc.createElement("images"); QDomElement ui = domDoc.firstChildElement("UI"); ui.appendChild(images); } int count = images.childNodes().count(); QDomElement image = domDoc.createElement("image"); QString name = "image" + QString::number(count); image.setAttribute("name", name); const QImage img(pixmap.toImage()); QByteArray ba; QBuffer buf(&ba); buf.open(QIODevice::WriteOnly | QIODevice::Text); const QByteArray format(img.depth() > 1 ? "XPM" : "XBM"); QImageWriter imageWriter(&buf, format); imageWriter.write(img); buf.close(); const QByteArray bazip = qCompress(ba); const int len = bazip.size(); QDomElement data = domDoc.createElement("data"); data.setAttribute("format", QString(format + ".GZ")); data.setAttribute("length", ba.size()); static const char hexchars[] = "0123456789abcdef"; QString content; for (int i = 4; i < len; i++) { uchar s = (uchar) bazip[i]; content += hexchars[s >> 4]; content += hexchars[s & 0x0f]; } data.appendChild(domDoc.createTextNode(content)); image.appendChild(data); images.appendChild(image); return name; } QPixmap FormIO::loadImage(QDomDocument domDoc, const QString& name) { QDomElement images = domDoc.firstChildElement("UI").firstChildElement("images"); if (images.isNull()) return QPixmap(); QDomElement image; for (QDomNode n = images.firstChild(); !n.isNull(); n = n.nextSibling()) { if ((n.toElement().tagName() == "image") && (n.toElement().attribute("name") == name)) { image = n.toElement(); break; } } QPixmap pix; QString data(image.firstChildElement("data").text()); const int lengthOffset = 4; int baSize = data.length() / 2 + lengthOffset; uchar *ba = new uchar[baSize]; for (int i = lengthOffset; i < baSize; ++i) { char h = data[2 * (i-lengthOffset)].toLatin1(); char l = data[2 * (i-lengthOffset) + 1].toLatin1(); uchar r = 0; if (h <= '9') r += h - '0'; else r += h - 'a' + 10; r = r << 4; if (l <= '9') r += l - '0'; else r += l - 'a' + 10; ba[i] = r; } QString format = image.firstChildElement("data").attribute("format", "PNG"); if ((format == "XPM.GZ") || (format == "XBM.GZ")) { int len = image.attribute("length").toInt(); if (len < data.length() * 5) len = data.length() * 5; // qUncompress() expects the first 4 bytes to be the expected length of // the uncompressed data ba[0] = (len & 0xff000000) >> 24; ba[1] = (len & 0x00ff0000) >> 16; ba[2] = (len & 0x0000ff00) >> 8; ba[3] = (len & 0x000000ff); QByteArray baunzip = qUncompress(ba, baSize); KexiUtils::loadPixmapFromData(&pix, baunzip, format.left(format.indexOf('.')).toLatin1()); } else { QByteArray b(QByteArray::fromRawData((const char*)ba + lengthOffset, baSize - lengthOffset)); KexiUtils::loadPixmapFromData(&pix, b, format.toLatin1()); } delete[] ba; return pix; } diff --git a/src/formeditor/formIO.h b/src/formeditor/formIO.h index 49ce91437..89f136e9d 100644 --- a/src/formeditor/formIO.h +++ b/src/formeditor/formIO.h @@ -1,224 +1,224 @@ /* This file is part of the KDE project Copyright (C) 2004 Cedric Pasteur Copyright (C) 2005-2007 Jarosław Staniek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef FORMIO_H #define FORMIO_H #include "kformdesigner_export.h" #include #include #include #include #include class QString; class QDomElement; class QDomNode; class QDomDocument; class QVariant; class QLabel; //! A blank widget displayed when class is not supported class KFORMDESIGNER_EXPORT CustomWidget : public QWidget { Q_OBJECT public: CustomWidget(const QByteArray &className, QWidget *parent); virtual ~CustomWidget(); virtual void paintEvent(QPaintEvent *ev); private: QByteArray m_className; }; namespace KFormDesigner { class Form; class ObjectTreeItem; class Container; //! KFormDesigner API major version number. Increased on every breaking of backward compatibility. //! Use KFormDesigner::version() to get real version number of the library. #define KFORMDESIGNER_VERSION_MAJOR KEXI_STABLE_VERSION_MAJOR //! KFormDesigner API minor version number. Increased on every compatible change. //! Use KFormDesigner::version() to get real version number of the library. #define KFORMDESIGNER_VERSION_MINOR KEXI_STABLE_VERSION_MINOR //! \return KFormDesigner API version number for this library: "major.minor" KFORMDESIGNER_EXPORT QString version(); /** This class act as a namespace for all .ui files related functions, ie saving/loading .ui files. You don't need to create a FormIO object, as all methods are static.\n This class is able to read and write Forms to .ui files, and to save each type of properties, including set and enum properties, and pixmaps(pixmap-related code was taken from Qt Designer). **/ //! A class to save/load forms from .ui files class KFORMDESIGNER_EXPORT FormIO : public QObject { Q_OBJECT public: FormIO(); ~FormIO(); /*! Save the Form in the \a domDoc QDomDocument. Called by saveForm(). \return true if saving succeeded. \sa saveForm() */ static bool saveFormToDom(Form *form, QDomDocument &domDoc); /*! Save the Form \a form to the file \a filename. If \a filename is null or not given, a Save File dialog will be shown to choose dest file. \return true if saving succeeded. \todo Add errors code and error dialog */ static bool saveFormToFile(Form *form, const QString &filename = QString()); /*! Saves the Form to the \a dest string. \a indent can be specified to apply indentation. \return true if saving succeeded. \sa saveForm() */ static bool saveFormToString(Form *form, QString &dest, int indent = 0); /*! Saves the \a form inside the \a dest QByteArray. \return true if saving succeeded. \sa saveFormToDom(), saveForm() */ static bool saveFormToByteArray(Form *form, QByteArray &dest); /*! Loads a form from the \a domDoc QDomDocument. Called by loadForm() and loadFormData(). \return true if loading succeeded. */ - static bool loadFormFromDom(Form *form, QWidget *container, QDomDocument &domDoc); + static bool loadFormFromDom(Form *form, QWidget *container, const QDomDocument &domDoc); /*! Loads a form from the \a src QByteArray. \sa loadFormFromDom(), loadForm(). \return true if loading succeeded. */ static bool loadFormFromByteArray(Form *form, QWidget *container, QByteArray &src, bool preview = false); - static bool loadFormFromString(Form *form, QWidget *container, QString *src, + static bool loadFormFromString(Form *form, QWidget *container, const QString &src, bool preview = false); /*! Loads the .ui file \a filename in the Form \a form. If \a filename is null or not given, a Open File dialog will be shown to select the file to open. createToplevelWidget() is used to load the Form's toplevel widget. \return true if loading succeeded. \todo Add errors code and error dialog */ static bool loadFormFromFile(Form *form, QWidget *container, const QString &filename = QString()); /*! Saves the widget associated to the ObjectTreeItem \a item into DOM document \a domDoc, with \a parent as parent node. It calls readPropertyValue() for each object property, readAttribute() for each attribute and itself to save child widgets. \return true if saving succeeded. This is used to copy/paste widgets. */ static void saveWidget(ObjectTreeItem *item, QDomElement &parent, QDomDocument &domDoc, bool insideGridLayout = false); /*! Cleans the "UI" QDomElement after saving widget. It deletes the "includes" element not needed when pasting, and make sure all the "widget" elements are at the beginning. Call this after copying a widget, before pasting.*/ static void cleanClipboard(QDomElement &uiElement); /*! Loads the widget associated to the QDomElement \a el into the Container \a container, with \a parent as parent widget. If parent = 0, the Container::widget() is used as parent widget. This is used to copy/paste widgets. */ static void loadWidget(Container *container, const QDomElement &el, QWidget *parent, QHash *buddies); /*! Save an element in the \a domDoc as child of \a parentNode. The element will be saved like this : \code <$(tagName) name = "$(property)">< value_as_XML ><$(tagName)/> \endcode */ static void savePropertyElement(QDomElement &parentNode, QDomDocument &domDoc, const QString &tagName, const QString &property, const QVariant &value); /*! Read an object property in the DOM doc. \param node the QDomNode of the property \param obj the widget whose property is being read \param name the name of the property being read */ static QVariant readPropertyValue(Form *form, QDomNode node, QObject *obj, const QString &name); /*! Write an object property in the DOM doc. \param item the widget item whose property is being saved \param parentNode the DOM document to write to \param name the name of the property being saved \param value the value of this property Properties of subwidget are saved with subwidget="true" arribute added to 'property' XML element. */ static void savePropertyValue(ObjectTreeItem *item, QDomElement &parentNode, QDomDocument &parent, const char *name, const QVariant &value); protected: /*! Saves the QVariant \a value as text to be included in an xml file, with \a parentNode.*/ static void writeVariant(QDomDocument &parent, QDomElement &parentNode, const QVariant& value); /*! Creates a toplevel widget from the QDomElement \a element in the Form \a form, with \a parent as parent widget. It calls readPropertyValue() and loadWidget() to load child widgets. */ static void createToplevelWidget(Form *form, QWidget *container, QDomElement &element); /*! \return the name of the pixmap saved, to use to access it This function save the QPixmap \a pixmap into the DOM document \a domDoc. The pixmap is converted to XPM and compressed for compatibility with Qt Designer. Encoding code is taken from Designer. */ static QString saveImage(QDomDocument &domDoc, const QPixmap &pixmap); /*! \return the loaded pixmap This function loads the pixmap named \a name in the DOM document \a domDoc. Decoding code is taken from QT Designer. */ //! @todo handle result of loading static QPixmap loadImage(QDomDocument domDoc, const QString& name); /*! Reads the child nodes of a "widget" element. */ static void readChildNodes(ObjectTreeItem *tree, Container *container, const QDomElement &el, QWidget *w, QHash *buddies); /*! Adds an include file name to be saved in the "includehints" part of .ui file, which is needed by uic. */ static void addIncludeFileName(const QString &include, QDomDocument &domDoc); private: //! This hash stores buddies associations until the Form is completely loaded. static QHash *m_buddies; }; } #endif