1 /* This file is part of the KDE project
2 Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr>
3 Copyright (C) 2005-2016 Jarosław Staniek <staniek@kde.org>
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public License
16 along with this library; see the file COPYING.LIB. If not, write to
17 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 */
20
21 #include "kformdesigner_export.h"
22
23 #include <QDomDocument>
24 #include <QFile>
25 #include <QTextStream>
26 #include <QCursor>
27 #include <QBuffer>
28 #include <QImage>
29 #include <QLayout>
30 #include <QObject>
31 #include <QDateTime>
32 #include <QPainter>
33 #include <QPaintEvent>
34 #include <QVBoxLayout>
35 #include <QHBoxLayout>
36 #include <QPixmap>
37 #include <QImageWriter>
38 #include <QDebug>
39
40 #include <KexiFileDialog.h>
41 #include <KAcceleratorManager>
42 #include <KLocalizedString>
43
44 #include <kexiutils/utils.h>
45 #include "FormWidget.h"
46 #include "form.h"
47 #include "container.h"
48 #include "objecttree.h"
49 #include "widgetlibrary.h"
50 #include "events.h"
51 #include "utils.h"
52 #include "widgetwithsubpropertiesinterface.h"
53 #include "formIO.h"
54 #include "KexiVersion.h"
55
56 //! @todo KEXI3 TODO pixmapcollection
57 #ifdef KEXI_PIXMAP_COLLECTIONS_SUPPORT
58 #include "pixmapcollection.h"
59 #endif
60
61 //! @return Value of attribute "name", or "objectName" in absence of "name".
62 //! This is for compatibility with Kexi 1.x.
nameAttribute(const QDomElement & el)63 static QString nameAttribute(const QDomElement& el)
64 {
65 QString res( el.attribute("name") );
66 if (res.isEmpty()) {
67 res = el.attribute("objectName");
68 }
69 return res;
70 }
71
72 //! A blank widget used when the class name is not supported
CustomWidget(const QByteArray & className,QWidget * parent)73 CustomWidget::CustomWidget(const QByteArray &className, QWidget *parent)
74 : QWidget(parent), m_className(className)
75 {
76 setBackgroundRole(QPalette::Dark);
77 }
78
~CustomWidget()79 CustomWidget::~CustomWidget()
80 {
81 }
82
83 void
paintEvent(QPaintEvent *)84 CustomWidget::paintEvent(QPaintEvent *)
85 {
86 QPainter p(this);
87 p.setBrush(palette().text());
88 QRect r(rect());
89 r.setX(r.x() + 2);
90 p.drawText(r, Qt::AlignTop, m_className);
91 }
92
93 using namespace KFormDesigner;
94
95 // FormIO itself
96
version()97 KFORMDESIGNER_EXPORT QString KFormDesigner::version()
98 {
99 return QString::fromLatin1("%1.%2").arg(KEXI_STABLE_VERSION_MAJOR).arg(KEXI_STABLE_VERSION_MINOR);
100 }
101
102 /////////////////////////////////////////////////////////////////////////////
103 ///////////// Saving/loading functions //////////////////////////////////////
104 /////////////////////////////////////////////////////////////////////////////
105
FormIO()106 FormIO::FormIO()
107 {
108 }
109
~FormIO()110 FormIO::~FormIO()
111 {
112 }
113
114 bool
saveFormToFile(Form * form,const QString & filename)115 FormIO::saveFormToFile(Form *form, const QString &filename)
116 {
117 QString _filename;
118 if (!form->fileName().isEmpty() && filename.isEmpty()) {
119 _filename = form->fileName();
120 }
121
122 if (filename.isEmpty()) {
123 KexiFileDialog dlg(0, KexiFileDialog::SaveFile, "SaveForm");
124 dlg.setNameFilter("*.ui|" + xi18n("Qt Designer UI Files"));
125 _filename = dlg.fileName();
126 if (_filename.isEmpty()) {
127 return false;
128 }
129 }
130 else {
131 _filename = filename;
132 }
133 form->setFileName(_filename);
134
135 QDomDocument domDoc;
136 if (!saveFormToDom(form, domDoc))
137 return false;
138
139 QFile file(_filename);
140 if (!file.open(QIODevice::WriteOnly))
141 return false;
142
143 QTextStream stream(&file);
144 stream << domDoc.toString(3);
145 file.close();
146
147 return true;
148 }
149
150 bool
saveFormToByteArray(Form * form,QByteArray & dest)151 FormIO::saveFormToByteArray(Form *form, QByteArray &dest)
152 {
153 QDomDocument domDoc;
154 if (!saveFormToDom(form, domDoc))
155 return false;
156 dest = domDoc.toByteArray();
157 return true;
158 }
159
160 bool
saveFormToString(Form * form,QString & dest,int indent)161 FormIO::saveFormToString(Form *form, QString &dest, int indent)
162 {
163 QDomDocument domDoc;
164 if (!saveFormToDom(form, domDoc))
165 return false;
166 dest = domDoc.toString(indent);
167 return true;
168 }
169
170 bool
saveFormToDom(Form * form,QDomDocument & domDoc)171 FormIO::saveFormToDom(Form *form, QDomDocument &domDoc)
172 {
173 domDoc = QDomDocument("UI");
174 QDomElement uiElement = domDoc.createElement("UI");
175 domDoc.appendChild(uiElement);
176 uiElement.setAttribute("version", "3.1");
177 uiElement.setAttribute("stdsetdef", 1);
178
179 //update format version information
180 form->headerProperties()->insert("version", form->formatVersion());
181 //custom properties
182 QDomElement headerPropertiesEl = domDoc.createElement("kfd:customHeader");
183 QHash<QByteArray, QString>::ConstIterator itEnd = form->headerProperties()->constEnd();
184 for (QHash<QByteArray, QString>::ConstIterator it = form->headerProperties()->constBegin();
185 it != itEnd; ++it)
186 {
187 headerPropertiesEl.setAttribute(it.key(), it.value());
188 }
189 uiElement.appendChild(headerPropertiesEl);
190
191 // We save the savePixmapsInline property in the Form
192 QDomElement inlinePix = domDoc.createElement("pixmapinproject");
193 uiElement.appendChild(inlinePix);
194
195 // We create the top class element
196 QDomElement baseClass = domDoc.createElement("class");
197 uiElement.appendChild(baseClass);
198 QDomText baseClassV = domDoc.createTextNode("QWidget");
199 baseClass.appendChild(baseClassV);
200
201 // Save the toplevel widgets, and so the whole Form
202 saveWidget(form->objectTree(), uiElement, domDoc);
203
204 // We then save the layoutdefaults element
205 QDomElement layoutDefaults = domDoc.createElement("layoutDefaults");
206 layoutDefaults.setAttribute("spacing", QString::number(form->defaultSpacing()));
207 layoutDefaults.setAttribute("margin", QString::number(form->defaultMargin()));
208 uiElement.appendChild(layoutDefaults);
209
210 // Save tab Stops
211 if (form->autoTabStops())
212 form->autoAssignTabStops();
213 QDomElement tabStops = domDoc.createElement("tabstops");
214 uiElement.appendChild(tabStops);
215 foreach (ObjectTreeItem *item, *form->tabStops()) {
216 QDomElement tabstop = domDoc.createElement("tabstop");
217 tabStops.appendChild(tabstop);
218 QDomText tabStopText = domDoc.createTextNode(item->name());
219 tabstop.appendChild(tabStopText);
220 }
221
222 //! @todo KEXI3 TODO pixmapcollection
223 #ifdef KEXI_PIXMAP_COLLECTIONS_SUPPORT
224 // Save the Form 's PixmapCollection
225 form->pixmapCollection()->save(uiElement);
226 #endif
227 #ifdef KFD_SIGSLOTS
228 // Save the Form connections
229 form->connectionBuffer()->save(uiElement);
230 #endif
231
232 form->setUndoStackClean();
233 return true;
234 }
235
236 bool
loadFormFromByteArray(Form * form,QWidget * container,QByteArray & src,bool preview)237 FormIO::loadFormFromByteArray(Form *form, QWidget *container, QByteArray &src, bool preview)
238 {
239 QString errMsg;
240 int errLine;
241 int errCol;
242
243 QDomDocument domDoc;
244 bool parsed = domDoc.setContent(src, false, &errMsg, &errLine, &errCol);
245
246 if (!parsed) {
247 qDebug() << errMsg;
248 qDebug() << "line:" << errLine << "col:" << errCol;
249 return false;
250 }
251
252 if (!loadFormFromDom(form, container, domDoc)) {
253 return false;
254 }
255 if (preview) {
256 form->setMode(Form::DataMode);
257 }
258 return true;
259 }
260
261 bool
loadFormFromString(Form * form,QWidget * container,const QString & src,bool preview)262 FormIO::loadFormFromString(Form *form, QWidget *container, const QString &src, bool preview)
263 {
264 QString errMsg;
265 int errLine;
266 int errCol;
267
268 #ifdef KEXI_DEBUG_GUI
269 form->m_recentlyLoadedUICode = src;
270 #endif
271
272 QDomDocument domDoc;
273 //qDebug() << qPrintable(src);
274 bool parsed = domDoc.setContent(src, false, &errMsg, &errLine, &errCol);
275
276 if (!parsed) {
277 qWarning() << errMsg;
278 qWarning() << "line:" << errLine << "col: " << errCol;
279 return false;
280 }
281
282 if (!loadFormFromDom(form, container, domDoc)) {
283 return false;
284 }
285 if (preview) {
286 form->setMode(Form::DataMode);
287 }
288 return true;
289 }
290
291 bool
loadFormFromFile(Form * form,QWidget * container,const QString & filename)292 FormIO::loadFormFromFile(Form *form, QWidget *container, const QString &filename)
293 {
294 QString errMsg;
295 int errLine;
296 int errCol;
297 QString _filename;
298
299 if (filename.isEmpty()) {
300 KexiFileDialog dlg(0, KexiFileDialog::OpenFile, "LoadForm");
301 dlg.setNameFilter("*.ui|" + xi18n("Qt Designer UI Files"));
302 _filename = dlg.fileName();
303 if (_filename.isEmpty()) {
304 return false;
305 }
306 }
307 else {
308 _filename = filename;
309 }
310
311 QFile file(_filename);
312 if (!file.open(QIODevice::ReadOnly)) {
313 //! @todo show err msg to the user
314 qWarning() << "Cannot open the file " << _filename;
315 return false;
316 }
317 QDomDocument doc;
318 if (!doc.setContent(&file, false/* !namespaceProcessing*/,
319 &errMsg, &errLine, &errCol)) {
320 //! @todo show err msg to the user
321 qWarning() << errMsg;
322 qWarning() << errLine << "col:" << errCol;
323 return false;
324 }
325
326 return loadFormFromDom(form, container, doc);
327 }
328
loadFormFromDom(Form * form,QWidget * container,const QDomDocument & domDoc)329 bool FormIO::loadFormFromDom(Form *form, QWidget *container, const QDomDocument &domDoc)
330 {
331 QDomElement ui = domDoc.firstChildElement("UI");
332
333 //custom properties
334 form->headerProperties()->clear();
335 QDomElement headerPropertiesEl = ui.firstChildElement("kfd:customHeader");
336 QDomAttr attr = headerPropertiesEl.firstChild().toAttr();
337 QDomNamedNodeMap attrs(headerPropertiesEl.attributes());
338 for(int i = 0; i < attrs.count(); ++i) {
339 const QDomAttr attr(attrs.item(i).toAttr());
340 if (!attr.isNull()) {
341 form->headerProperties()->insert(attr.name().toLatin1(), attr.value());
342 }
343 }
344 //update format version information
345 QString ver = form->headerProperties()->value("version");
346 qDebug() << "Original format version: " << ver;
347 form->setOriginalFormatVersion(ver);
348 bool verOk;
349 const double verNum = ver.toDouble(&verOk);
350 const double currentVerNum = KFormDesigner::version().toDouble();
351 if (verOk) {
352 if (verNum < currentVerNum) {
353 //! @todo We can either 1) convert from old format and later save in a new one or 2) keep old format.
354 //! To do this we may need to look at the original format version number.
355 qDebug() << "The original format version is:" << ver
356 << "current version:" << KFormDesigner::version();
357 //return false;
358 }
359 }
360 form->setFormatVersion(ver);
361
362 if (verNum > currentVerNum) {
363 //! @todo display information about too new format and that "some information will not be available".
364 qDebug() << "The original format is version" << ver
365 << "is newer than current version:" << KFormDesigner::version();
366 }
367
368 // Load the pixmap collection
369 form->setPixmapsStoredInline(ui.firstChildElement("pixmapinproject").isNull()
370 || !ui.firstChildElement("images").isNull());
371 //! @todo pixmapcollection
372 #ifdef KEXI_PIXMAP_COLLECTIONS_SUPPORT
373 form->pixmapCollection()->load(ui.namedItem("collection"));
374 #endif
375
376 QDomElement element = ui.firstChildElement("widget");
377 createToplevelWidget(form, container, element);
378
379 // Loading the tabstops
380 QDomElement tabStops = ui.firstChildElement("tabstops");
381 if (!tabStops.isNull()) {
382 int i = 0;
383 int itemsNotFound = 0;
384 for (QDomNode n = tabStops.firstChild(); !n.isNull(); n = n.nextSibling(), i++) {
385 QString name = n.toElement().text();
386 ObjectTreeItem *item = form->objectTree()->lookup(name);
387 if (!item) {
388 qWarning() << "Tabstops loading: no item" << name;
389 continue;
390 }
391 const int index = form->tabStops()->indexOf(item);
392 /* Compute a real destination index: "a number of not found items so far". */
393 const int realIndex = i - itemsNotFound;
394 if ((index != -1) && (index != realIndex)) { // the widget is not in the same place, so we move it
395 form->tabStops()->removeOne(item);
396 form->tabStops()->insert(realIndex, item);
397 }
398 if (index == -1) {
399 itemsNotFound++;
400 qDebug() << "Tabstops loading: item" << name << "not on the list";
401 }
402 }
403 }
404
405 #ifdef KFD_SIGSLOTS
406 // Load the form connections
407 form->connectionBuffer()->load(ui.namedItem("connections"));
408 #endif
409 return true;
410 }
411
412 /////////////////////////////////////////////////////////////////////////////
413 ///////////// Functions to save/load properties /////////////////////////////
414 /////////////////////////////////////////////////////////////////////////////
415
416 void
savePropertyValue(ObjectTreeItem * item,QDomElement & parentNode,QDomDocument & parent,const char * name,const QVariant & value)417 FormIO::savePropertyValue(ObjectTreeItem *item, QDomElement &parentNode, QDomDocument &parent,
418 const char *name, const QVariant &value)
419 {
420 // Widget specific properties and attributes
421 // qDebug() << "Saving the property: " << name;
422 Form *form = item->container() ? item->container()->form() : item->parent()->container()->form();
423 WidgetWithSubpropertiesInterface* subpropIface = dynamic_cast<WidgetWithSubpropertiesInterface*>(item->widget());
424 QWidget *subwidget = item->widget();
425 bool addSubwidgetFlag = false;
426 int propertyId = item->widget()->metaObject()->indexOfProperty(name);
427 const bool propertyIsName = qstrcmp(name, "objectName") == 0 || qstrcmp(name, "name") == 0;
428 if (!propertyIsName && propertyId == -1 && subpropIface && subpropIface->subwidget()) { // try property from subwidget
429 subwidget = subpropIface->subwidget();
430 propertyId = subpropIface->subwidget()->metaObject()->indexOfProperty(name);
431 addSubwidgetFlag = true;
432 }
433 if (!propertyIsName && propertyId == -1) {
434 qDebug() << "The object doesn't have this property. Let's try the WidgetLibrary.";
435 if (form->library())
436 form->library()->saveSpecialProperty(item->widget()->metaObject()->className(), name, value,
437 item->widget(), parentNode, parent);
438 return;
439 }
440
441 QMetaProperty meta;
442 if (!propertyIsName) {
443 meta = subwidget->metaObject()->property(propertyId);
444 }
445 if (!propertyIsName && (!meta.isValid() || !meta.isStored(subwidget))) //not storable
446 return;
447 QDomElement propertyE = parent.createElement("property");
448 propertyE.setAttribute("name", propertyIsName ? "name" /* compat with 1.x */ : name);
449 if (addSubwidgetFlag)
450 propertyE.setAttribute("subwidget", "true");
451
452 if (meta.isValid() && meta.isEnumType()) {
453 // this property is enum or set type
454 QDomElement type;
455 QDomText valueE;
456
457 if (meta.isFlagType()) {
458 type = parent.createElement("set");
459 valueE = parent.createTextNode(
460 meta.enumerator().valueToKeys(value.toInt()));
461 type.appendChild(valueE);
462 } else {
463 QString s = meta.enumerator().valueToKey(value.toInt());
464 type = parent.createElement("enum");
465 valueE = parent.createTextNode(s);
466 type.appendChild(valueE);
467 }
468 propertyE.appendChild(type);
469 parentNode.appendChild(propertyE);
470 return;
471 }
472
473 if (value.type() == QVariant::Pixmap) {
474 QDomText valueE;
475 QDomElement type = parent.createElement("pixmap");
476 QByteArray property = propertyE.attribute("name").toLatin1();
477 //! @todo QCString pixmapName = m_currentRecord->widget()->property("pixmapName").toCString();
478 if (form->pixmapsStoredInline() /* (js)too risky: || m_currentRecord->pixmapName(property).isNull() */)
479 valueE = parent.createTextNode(saveImage(parent, value.value<QPixmap>()));
480 else
481 valueE = parent.createTextNode(item->pixmapName(property));
482 type.appendChild(valueE);
483 propertyE.appendChild(type);
484 parentNode.appendChild(propertyE);
485 return;
486 }
487
488 // Saving a "normal" property
489 writeVariant(parent, propertyE, value);
490 parentNode.appendChild(propertyE);
491 }
492
493 void
writeVariant(QDomDocument & parent,QDomElement & parentNode,const QVariant & value)494 FormIO::writeVariant(QDomDocument &parent, QDomElement &parentNode, const QVariant& value)
495 {
496 QDomElement type;
497 QDomText valueE;
498
499 switch (value.type()) {
500 case QVariant::String: {
501 type = parent.createElement("string");
502 valueE = parent.createTextNode(value.toString());
503 type.appendChild(valueE);
504 break;
505 }
506 case QVariant::ByteArray: {
507 type = parent.createElement("cstring");
508 valueE = parent.createTextNode(value.toString());
509 type.appendChild(valueE);
510 break;
511 }
512 case QVariant::Rect: {
513 type = parent.createElement("rect");
514 QDomElement x = parent.createElement("x");
515 QDomElement y = parent.createElement("y");
516 QDomElement w = parent.createElement("width");
517 QDomElement h = parent.createElement("height");
518 QDomText valueX = parent.createTextNode(QString::number(value.toRect().x()));
519 QDomText valueY = parent.createTextNode(QString::number(value.toRect().y()));
520 QDomText valueW = parent.createTextNode(QString::number(value.toRect().width()));
521 QDomText valueH = parent.createTextNode(QString::number(value.toRect().height()));
522
523 x.appendChild(valueX);
524 y.appendChild(valueY);
525 w.appendChild(valueW);
526 h.appendChild(valueH);
527
528 type.appendChild(x);
529 type.appendChild(y);
530 type.appendChild(w);
531 type.appendChild(h);
532 break;
533 }
534 case QVariant::Color: {
535 type = parent.createElement("color");
536 QDomElement r = parent.createElement("red");
537 QDomElement g = parent.createElement("green");
538 QDomElement b = parent.createElement("blue");
539
540 const QColor color(value.value<QColor>());
541 QDomText valueR = parent.createTextNode(QString::number(color.red()));
542 QDomText valueG = parent.createTextNode(QString::number(color.green()));
543 QDomText valueB = parent.createTextNode(QString::number(color.blue()));
544
545 r.appendChild(valueR);
546 g.appendChild(valueG);
547 b.appendChild(valueB);
548
549 type.appendChild(r);
550 type.appendChild(g);
551 type.appendChild(b);
552 break;
553 }
554 case QVariant::Bool: {
555 type = parent.createElement("bool");
556 //valueE = parent.createTextNode(QString::number(value.toBool()));
557 valueE = parent.createTextNode(value.toBool() ? "true" : "false");
558 type.appendChild(valueE);
559 break;
560 }
561 case QVariant::Int:
562 case QVariant::UInt: {
563 type = parent.createElement("number");
564 valueE = parent.createTextNode(QString::number(value.toInt()));
565 type.appendChild(valueE);
566 break;
567 }
568 case QVariant::Size: {
569 type = parent.createElement("size");
570 QDomElement w = parent.createElement("width");
571 QDomElement h = parent.createElement("height");
572 QDomText valueW = parent.createTextNode(QString::number(value.toSize().width()));
573 QDomText valueH = parent.createTextNode(QString::number(value.toSize().height()));
574
575 w.appendChild(valueW);
576 h.appendChild(valueH);
577
578 type.appendChild(w);
579 type.appendChild(h);
580 break;
581 }
582 case QVariant::Point: {
583 type = parent.createElement("point");
584 QDomElement x = parent.createElement("x");
585 QDomElement y = parent.createElement("y");
586 QDomText valueX = parent.createTextNode(QString::number(value.toPoint().x()));
587 QDomText valueY = parent.createTextNode(QString::number(value.toPoint().y()));
588
589 x.appendChild(valueX);
590 y.appendChild(valueY);
591
592 type.appendChild(x);
593 type.appendChild(y);
594 break;
595 }
596 case QVariant::Font: {
597 type = parent.createElement("font");
598 QDomElement f = parent.createElement("family");
599 QDomElement p = parent.createElement("pointsize");
600 QDomElement w = parent.createElement("weight");
601 QDomElement b = parent.createElement("bold");
602 QDomElement i = parent.createElement("italic");
603 QDomElement u = parent.createElement("underline");
604 QDomElement s = parent.createElement("strikeout");
605
606 const QFont font(value.value<QFont>());
607 QDomText valueF = parent.createTextNode(font.family());
608 QDomText valueP = parent.createTextNode(QString::number(font.pointSize()));
609 QDomText valueW = parent.createTextNode(QString::number(font.weight()));
610 QDomText valueB = parent.createTextNode(QString::number(font.bold()));
611 QDomText valueI = parent.createTextNode(QString::number(font.italic()));
612 QDomText valueU = parent.createTextNode(QString::number(font.underline()));
613 QDomText valueS = parent.createTextNode(QString::number(font.strikeOut()));
614
615 f.appendChild(valueF);
616 p.appendChild(valueP);
617 w.appendChild(valueW);
618 b.appendChild(valueB);
619 i.appendChild(valueI);
620 u.appendChild(valueU);
621 s.appendChild(valueS);
622
623 type.appendChild(f);
624 type.appendChild(p);
625 type.appendChild(w);
626 type.appendChild(b);
627 type.appendChild(i);
628 type.appendChild(u);
629 type.appendChild(s);
630 break;
631 }
632 case QVariant::Cursor: {
633 type = parent.createElement("cursor");
634 valueE = parent.createTextNode(QString::number(value.value<QCursor>().shape()));
635 type.appendChild(valueE);
636 break;
637 }
638 case QVariant::SizePolicy: {
639 type = parent.createElement("sizepolicy");
640 QDomElement h(parent.createElement("hsizetype"));
641 QDomElement v(parent.createElement("vsizetype"));
642 QDomElement hs(parent.createElement("horstretch"));
643 QDomElement vs(parent.createElement("verstretch"));
644
645 const QSizePolicy sizePolicy(value.value<QSizePolicy>());
646 const QDomText valueH(parent.createTextNode(QString::number(sizePolicy.horizontalPolicy())));
647 const QDomText valueV(parent.createTextNode(QString::number(sizePolicy.verticalPolicy())));
648 const QDomText valueHS(parent.createTextNode(QString::number(sizePolicy.horizontalStretch())));
649 const QDomText valueVS(parent.createTextNode(QString::number(sizePolicy.verticalStretch())));
650
651 h.appendChild(valueH);
652 v.appendChild(valueV);
653 hs.appendChild(valueHS);
654 vs.appendChild(valueVS);
655
656 type.appendChild(h);
657 type.appendChild(v);
658 type.appendChild(hs);
659 type.appendChild(vs);
660 break;
661 }
662 case QVariant::Time: {
663 type = parent.createElement("time");
664 QDomElement h = parent.createElement("hour");
665 QDomElement m = parent.createElement("minute");
666 QDomElement s = parent.createElement("second");
667 QDomText valueH = parent.createTextNode(QString::number(value.toTime().hour()));
668 QDomText valueM = parent.createTextNode(QString::number(value.toTime().minute()));
669 QDomText valueS = parent.createTextNode(QString::number(value.toTime().second()));
670
671 h.appendChild(valueH);
672 m.appendChild(valueM);
673 s.appendChild(valueS);
674
675 type.appendChild(h);
676 type.appendChild(m);
677 type.appendChild(s);
678 break;
679 }
680 case QVariant::Date: {
681 type = parent.createElement("date");
682 QDomElement y = parent.createElement("year");
683 QDomElement m = parent.createElement("month");
684 QDomElement d = parent.createElement("day");
685 QDomText valueY = parent.createTextNode(QString::number(value.toDate().year()));
686 QDomText valueM = parent.createTextNode(QString::number(value.toDate().month()));
687 QDomText valueD = parent.createTextNode(QString::number(value.toDate().day()));
688
689 y.appendChild(valueY);
690 m.appendChild(valueM);
691 d.appendChild(valueD);
692
693 type.appendChild(y);
694 type.appendChild(m);
695 type.appendChild(d);
696 break;
697 }
698 case QVariant::DateTime: {
699 type = parent.createElement("datetime");
700 QDomElement h = parent.createElement("hour");
701 QDomElement m = parent.createElement("minute");
702 QDomElement s = parent.createElement("second");
703 QDomElement y = parent.createElement("year");
704 QDomElement mo = parent.createElement("month");
705 QDomElement d = parent.createElement("day");
706 QDomText valueH = parent.createTextNode(QString::number(value.toDateTime().time().hour()));
707 QDomText valueM = parent.createTextNode(QString::number(value.toDateTime().time().minute()));
708 QDomText valueS = parent.createTextNode(QString::number(value.toDateTime().time().second()));
709 QDomText valueY = parent.createTextNode(QString::number(value.toDateTime().date().year()));
710 QDomText valueMo = parent.createTextNode(QString::number(value.toDateTime().date().month()));
711 QDomText valueD = parent.createTextNode(QString::number(value.toDateTime().date().day()));
712
713 h.appendChild(valueH);
714 m.appendChild(valueM);
715 s.appendChild(valueS);
716 y.appendChild(valueY);
717 mo.appendChild(valueMo);
718 d.appendChild(valueD);
719
720 type.appendChild(h);
721 type.appendChild(m);
722 type.appendChild(s);
723 type.appendChild(y);
724 type.appendChild(mo);
725 type.appendChild(d);
726 break;
727 }
728 default:
729 break;
730 }
731
732 parentNode.appendChild(type);
733 }
734
735 void
savePropertyElement(QDomElement & parentNode,QDomDocument & domDoc,const QString & tagName,const QString & property,const QVariant & value)736 FormIO::savePropertyElement(QDomElement &parentNode, QDomDocument &domDoc, const QString &tagName,
737 const QString &property, const QVariant &value)
738 {
739 QDomElement propertyE = domDoc.createElement(tagName);
740 propertyE.setAttribute("name", property);
741 writeVariant(domDoc, propertyE, value);
742 parentNode.appendChild(propertyE);
743 }
744
readPropertyValue(Form * form,QDomNode node,QObject * obj,const QString & name)745 QVariant FormIO::readPropertyValue(Form *form, QDomNode node, QObject *obj, const QString &name)
746 {
747 QDomElement tag = node.toElement();
748 QString text = tag.text();
749 QString type = tag.tagName();
750
751 if (type == "string" || type == "cstring")
752 return text;
753 else if (type == "rect") {
754 QDomElement x = node.firstChildElement("x");
755 QDomElement y = node.firstChildElement("y");
756 QDomElement w = node.firstChildElement("width");
757 QDomElement h = node.firstChildElement("height");
758
759 int rx = x.text().toInt();
760 int ry = y.text().toInt();
761 int rw = w.text().toInt();
762 int rh = h.text().toInt();
763
764 return QRect(rx, ry, rw, rh);
765 } else if (type == "color") {
766 const QDomElement r(node.firstChildElement("red"));
767 const QDomElement g(node.firstChildElement("green"));
768 const QDomElement b(node.firstChildElement("blue"));
769
770 return QColor(r.text().toInt(), g.text().toInt(), b.text().toInt());
771 } else if (type == "bool") {
772 if (text == "true")
773 return true;
774 else if (text == "false")
775 return false;
776 return text.toInt() != 0;
777 } else if (type == "number") {
778 return text.toInt();
779 } else if (type == "size") {
780 QDomElement w = node.firstChildElement("width");
781 QDomElement h = node.firstChildElement("height");
782
783 return QSize(w.text().toInt(), h.text().toInt());
784 } else if (type == "point") {
785 QDomElement x = node.firstChildElement("x");
786 QDomElement y = node.firstChildElement("y");
787
788 return QPoint(x.text().toInt(), y.text().toInt());
789 } else if (type == "font") {
790 QDomElement fa = node.firstChildElement("family");
791 QDomElement p = node.firstChildElement("pointsize");
792 QDomElement w = node.firstChildElement("weight");
793 QDomElement b = node.firstChildElement("bold");
794 QDomElement i = node.firstChildElement("italic");
795 QDomElement u = node.firstChildElement("underline");
796 QDomElement s = node.firstChildElement("strikeout");
797
798 QFont f;
799 f.setFamily(fa.text());
800 f.setPointSize(p.text().toInt());
801 f.setWeight(w.text().toInt());
802 f.setBold(b.text().toInt());
803 f.setItalic(i.text().toInt());
804 f.setUnderline(u.text().toInt());
805 f.setStrikeOut(s.text().toInt());
806
807 return f;
808 } else if (type == "cursor") {
809 return QCursor((Qt::CursorShape) text.toInt());
810 } else if (type == "time") {
811 QDomElement h = node.firstChildElement("hour");
812 QDomElement m = node.firstChildElement("minute");
813 QDomElement s = node.firstChildElement("second");
814
815 return QTime(h.text().toInt(), m.text().toInt(), s.text().toInt());
816 } else if (type == "date") {
817 QDomElement y = node.firstChildElement("year");
818 QDomElement m = node.firstChildElement("month");
819 QDomElement d = node.firstChildElement("day");
820
821 return QDate(y.text().toInt(), m.text().toInt(), d.text().toInt());
822 } else if (type == "datetime") {
823 QDomElement h = node.firstChildElement("hour");
824 QDomElement m = node.firstChildElement("minute");
825 QDomElement s = node.firstChildElement("second");
826 QDomElement y = node.firstChildElement("year");
827 QDomElement mo = node.firstChildElement("month");
828 QDomElement d = node.firstChildElement("day");
829
830 QTime t(h.text().toInt(), m.text().toInt(), s.text().toInt());
831 QDate da(y.text().toInt(), mo.text().toInt(), d.text().toInt());
832
833 return QDateTime(da, t);
834 } else if (type == "sizepolicy") {
835 QDomElement h = node.firstChildElement("hsizetype");
836 QDomElement v = node.firstChildElement("vsizetype");
837 QDomElement hs = node.firstChildElement("horstretch");
838 QDomElement vs = node.firstChildElement("verstretch");
839
840 QSizePolicy s;
841 s.setHorizontalPolicy((QSizePolicy::Policy)h.text().toInt());
842 s.setVerticalPolicy((QSizePolicy::Policy)v.text().toInt());
843 s.setHorizontalStretch(hs.text().toInt());
844 s.setVerticalStretch(vs.text().toInt());
845 return s;
846 } else if (type == "pixmap") {
847 //! @todo pixmapcollection
848 #ifdef KEXI_PIXMAP_COLLECTIONS_SUPPORT
849 if (form->pixmapsStoredInline() || !m_currentForm || !m_currentRecord || !m_currentForm->pixmapCollection()->contains(text))
850 return loadImage(tag.ownerDocument(), text);
851 else {
852 m_currentRecord->setPixmapName(name.toLatin1(), text);
853 return form->pixmapCollection()->getPixmap(text);
854 }
855 #else
856 Q_UNUSED(form);
857 #endif
858 return QPixmap();
859 }
860 else if (type == "enum") {
861 return text;
862 }
863 else if (type == "set") {
864 WidgetWithSubpropertiesInterface* subpropIface
865 = dynamic_cast<WidgetWithSubpropertiesInterface*>(obj);
866 QObject *subobject = (subpropIface && subpropIface->subwidget())
867 ? subpropIface->subwidget() : obj;
868 const QMetaProperty meta(KexiUtils::findPropertyWithSuperclasses(subobject, name.toLatin1()));
869 if (meta.isValid()) {
870 if (meta.isFlagType()) {
871 return meta.enumerator().keysToValue(text.toLatin1());
872 } else {
873 // Metaproperty not found, probably because subwidget is not created.
874 // We will return a string list here with hope that names will
875 // be resolved and translated into an integer value later when subwidget is created,
876 // e.g. near KexiFormView::updateValuesForSubproperties()
877 return text.split('|');
878 }
879 }
880 }
881 return QVariant();
882 }
883
884 /////////////////////////////////////////////////////////////////////////////
885 ///////////// Functions to save/load widgets ////////////////////////////////
886 /////////////////////////////////////////////////////////////////////////////
887
888 void
saveWidget(ObjectTreeItem * item,QDomElement & parent,QDomDocument & domDoc,bool insideGridLayout)889 FormIO::saveWidget(ObjectTreeItem *item, QDomElement &parent, QDomDocument &domDoc, bool insideGridLayout)
890 {
891 if (!item)
892 return;
893 //qDebug() << item->className() << item->widget()->objectName();
894 Form *form = item->container() ? item->container()->form() : item->parent()->container()->form();
895 WidgetLibrary *lib = form->library();
896
897 // We create the "widget" element
898 QDomElement tclass = domDoc.createElement("widget");
899 parent.appendChild(tclass);
900
901 if (insideGridLayout) {
902 tclass.setAttribute("row", item->gridRow());
903 tclass.setAttribute("column", item->gridCol());
904 if (item->spanMultipleCells()) {
905 tclass.setAttribute("rowspan", item->gridRowSpan());
906 tclass.setAttribute("colspan", item->gridColSpan());
907 }
908 }
909
910 if (!item->parent()) // Toplevel widget
911 tclass.setAttribute("class", "QWidget");
912 // For compatibility, HBox, VBox and Grid are saved as "QLayoutWidget"
913 else if (KexiUtils::objectIsA(item->widget(),
914 QList<QByteArray>() << "HBox" << "VBox" << "Grid" << "HFlow" << "VFlow"))
915 tclass.setAttribute("class", "QLayoutWidget");
916 else if (KexiUtils::objectIsA(item->widget(), "CustomWidget"))
917 tclass.setAttribute("class", item->className());
918 else // Normal widgets
919 tclass.setAttribute("class", lib->savingName(item->widget()->metaObject()->className()));
920
921 // We save every property in the modifProp list of the ObjectTreeItem
922 QHash<QString, QVariant> hash(*(item->modifiedProperties()));
923 QStringList names(hash.keys());
924 savePropertyValue(item, tclass, domDoc, "objectName", item->widget()->objectName());
925 names.removeOne("objectName");
926
927 // Important: save dataSource property FIRST before properties like "alignment"
928 // - needed when subproperties are defined after subwidget creation, and subwidget is created after setting "dataSource"
929 // (this is the case for KexiDBAutoField)
930 //! @todo more properties like "dataSource" may needed here...
931 // if (-1 != item->widget()->metaObject()->findProperty("dataSource"))
932 // savePropertyValue(tclass, domDoc, "dataSource", item->widget()->property("dataSource"), item->widget());
933
934 // We don't want to save the geometry if the widget is inside a layout (so parent.tagName() == "grid" for example)
935 if (item && !item->parent()) {
936 // save form widget size, but not its position
937 savePropertyValue(item, tclass, domDoc, "geometry",
938 QRect(QPoint(0, 0), item->widget()->size()));
939 }
940 // normal widget (if == "UI', it means we're copying widget)
941 else if (parent.tagName() == "widget" || parent.tagName() == "UI")
942 savePropertyValue(item, tclass, domDoc, "geometry", item->widget()->property("geometry"));
943
944 names.removeOne("geometry");
945 names.removeOne("layout");
946
947 // Save the buddy widget for a label
948 if ( qobject_cast<QLabel*>(item->widget())
949 && qobject_cast<QLabel*>(item->widget())->buddy())
950 {
951 savePropertyElement(
952 tclass, domDoc, "property", "buddy",
953 qobject_cast<QLabel*>(item->widget())->buddy()->objectName());
954 }
955
956 if (names.contains("paletteBackgroundColor")) {
957 savePropertyElement(
958 tclass, domDoc, "property", "paletteBackgroundColor",
959 item->widget()->palette().color(item->widget()->backgroundRole()));
960 names.removeOne("paletteBackgroundColor");
961 }
962 if (names.contains("paletteForegroundColor")) {
963 savePropertyElement(
964 tclass, domDoc, "property", "paletteForegroundColor",
965 item->widget()->palette().color(item->widget()->foregroundRole()));
966 names.removeOne("paletteForegroundColor");
967 }
968
969 QStringList alignProperties;
970 alignProperties << "hAlign" << "vAlign" << "alignment";
971 foreach (const QString& name, alignProperties) {
972 if (names.contains(name)) {
973 names.removeOne(name);
974 savePropertyValue(
975 item, tclass, domDoc, "alignment",
976 item->widget()->property("alignment"));
977 break;
978 }
979 }
980
981 foreach (const QString& name, names) {
982 savePropertyValue(item, tclass, domDoc, name.toLatin1(),
983 item->widget()->property(name.toLatin1()));
984 }
985 hash.clear();
986
987 if (KexiUtils::objectIsA(item->widget(), "CustomWidget")) {
988 QDomDocument doc("TEMP");
989 doc.setContent(item->unknownProperties());
990 for (QDomNode n = doc.firstChild(); !n.isNull(); n = n.nextSibling()) {
991 tclass.appendChild(n.cloneNode());
992 }
993
994 }
995 // Saving container 's layout if there is one
996 QDomElement layout;
997 if (item->container() && item->container()->layoutType() != Form::NoLayout) {
998 if (item->container()->layout()) { // there is a layout
999 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
cleanClipboard(QDomElement & uiElement)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
loadWidget(Container * container,const QDomElement & el,QWidget * parent,QHash<QString,QLabel * > * buddies)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
createToplevelWidget(Form * form,QWidget * container,QDomElement & el)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
readChildNodes(ObjectTreeItem * item,Container * container,const QDomElement & el,QWidget * w,QHash<QString,QLabel * > * buddies)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((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((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((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
addIncludeFileName(const QString & include,QDomDocument & domDoc)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
saveImage(QDomDocument & domDoc,const QPixmap & pixmap)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
loadImage(QDomDocument domDoc,const QString & name)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
1540