[KLF Backend][KLF Tools][KLF Home]
KLatexFormula Project
src/klftools/klfdatautil.cpp
Go to the documentation of this file.
00001 /***************************************************************************
00002  *   file klfdatautil.h
00003  *   This file is part of the KLatexFormula Project.
00004  *   Copyright (C) 2011 by Philippe Faist
00005  *   philippe.faist at bluewin.ch
00006  *                                                                         *
00007  *   This program is free software; you can redistribute it and/or modify  *
00008  *   it under the terms of the GNU General Public License as published by  *
00009  *   the Free Software Foundation; either version 2 of the License, or     *
00010  *   (at your option) any later version.                                   *
00011  *                                                                         *
00012  *   This program is distributed in the hope that it will be useful,       *
00013  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00014  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
00015  *   GNU General Public License for more details.                          *
00016  *                                                                         *
00017  *   You should have received a copy of the GNU General Public License     *
00018  *   along with this program; if not, write to the                         *
00019  *   Free Software Foundation, Inc.,                                       *
00020  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
00021  ***************************************************************************/
00022 /* $Id$ */
00023 
00024 #include <qglobal.h>
00025 #include <QObject>
00026 #include <QByteArray>
00027 #include <QString>
00028 #include <QUrl>
00029 #include <QTextCodec>
00030 #include <QDateTime>
00031 #include <QRect>
00032 #include <QIcon>
00033 #include <QColor>
00034 #include <QBrush>
00035 #include <QDomDocument>
00036 #include <QTextFormat>
00037 
00038 #include "klfdefs.h"
00039 #include "klfpobj.h"
00040 #include "klfutil.h"
00041 #include "klfdatautil.h"
00042 
00043 #include "klfdatautil_p.h"
00044 
00045 
00046 static inline bool klf_is_hex_char(char c)
00047 {
00048   return ('0' <= c && c <= '9') || ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F');
00049 }
00050 
00051 
00052 
00053 #define KLF_BRUSH_STYLE(sty)                    \
00054   { Qt::sty##Pattern, #sty }
00055 
00056 static struct { int brushStyle; const char *style; } klf_brush_styles[] = {
00057   { Qt::NoBrush, "NoBrush" },
00058   { Qt::SolidPattern, "" },
00059   { Qt::SolidPattern, "Solid" },
00060   KLF_BRUSH_STYLE(Dense1),
00061   KLF_BRUSH_STYLE(Dense2),
00062   KLF_BRUSH_STYLE(Dense3),
00063   KLF_BRUSH_STYLE(Dense4),
00064   KLF_BRUSH_STYLE(Dense5),
00065   KLF_BRUSH_STYLE(Dense6),
00066   KLF_BRUSH_STYLE(Dense7),
00067   KLF_BRUSH_STYLE(Hor),
00068   KLF_BRUSH_STYLE(Ver),
00069   KLF_BRUSH_STYLE(Cross),
00070   KLF_BRUSH_STYLE(BDiag),
00071   KLF_BRUSH_STYLE(FDiag),
00072   KLF_BRUSH_STYLE(DiagCross),
00073   { -1, NULL }
00074 };
00075 
00076 
00077 
00078 #define KLF_TEXT_FORMAT_FORMAT(fmt)             \
00079   { QTextFormat::fmt##Format, #fmt "Format" }
00080 
00081 static struct { int formatId; const char *format; } klf_text_format_formats[] = {
00082   KLF_TEXT_FORMAT_FORMAT(Invalid),
00083   KLF_TEXT_FORMAT_FORMAT(Block),
00084   KLF_TEXT_FORMAT_FORMAT(Char),
00085   KLF_TEXT_FORMAT_FORMAT(List),
00086   KLF_TEXT_FORMAT_FORMAT(Table),
00087   KLF_TEXT_FORMAT_FORMAT(Frame),
00088   KLF_TEXT_FORMAT_FORMAT(User),
00089   { -100, NULL }
00090 };
00091 
00092 
00093 #define KLF_TEXT_FORMAT_PROP(p, type)           \
00094   { QTextFormat::p, #p, #type }
00095 
00096 static struct { int propId; const char *key; const char *type; } klf_text_format_props[] = {
00097   KLF_TEXT_FORMAT_PROP(ForegroundBrush, QBrush),
00098   KLF_TEXT_FORMAT_PROP(BackgroundBrush, QBrush),
00099   KLF_TEXT_FORMAT_PROP(FontFamily, QString),
00100   KLF_TEXT_FORMAT_PROP(FontPointSize, int),
00101   KLF_TEXT_FORMAT_PROP(FontWeight, int),
00102   KLF_TEXT_FORMAT_PROP(FontItalic, bool),
00103   KLF_TEXT_FORMAT_PROP(TextUnderlineStyle, int),
00104   // add more keys for short-hands
00105   { QTextFormat::ForegroundBrush, "FG", "QBrush" },
00106   { QTextFormat::BackgroundBrush, "BG", "QBrush" },
00107 
00108   { -1, NULL, NULL }
00109 };
00110 
00111 static struct { const char * keyword; int propId; QVariant fixed_value; } klf_text_format_keywords[] = {
00112     { "NORMALWEIGHT", QTextFormat::FontWeight, QVariant(QFont::Normal) },
00113     { "BOLD", QTextFormat::FontWeight, QVariant(QFont::Bold) },
00114     { "NORMALSTYLE", QTextFormat::FontItalic, QVariant(false) },
00115     { "ITALIC", QTextFormat::FontItalic, QVariant(true) },
00116 
00117     { NULL, -1, QVariant() }
00118 };
00119 
00120 
00121 
00122 
00123 KLF_EXPORT QByteArray klfDataToEscaped(const QByteArray& value_ba, char escapechar)
00124 {
00125   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00126 
00127   klfDbg("len="<<value_ba.size()<<" , data=`"<<value_ba<<"' escapechar="<<klfFmtCC("'\\x%02X'", (int)escapechar));
00128 
00129   QByteArray data;
00130   int k;
00131   for (k = 0; k < value_ba.size(); ++k) {
00132     //    qDebug("\tdata[%d] = %x = %c", k, (uchar)value_ba[k], value_ba[k]);
00133     if (value_ba[k] >= 32 && value_ba[k] <= 126 && value_ba[k] != escapechar) {
00134       // ascii-ok values, not backslash
00135       data += value_ba[k];
00136     } else if (value_ba[k] == escapechar) {
00137       // double the escape char
00138       data += escapechar;
00139       data += escapechar;
00140     } else {
00141       data += escapechar;
00142       data += QString("x%1").arg((uint)(uchar)value_ba[k], 2, 16, QChar('0')).toLatin1();
00143     }
00144   }
00145   return data;
00146 }
00147 
00148 KLF_EXPORT QByteArray klfEscapedToData(const QByteArray& data, char escapechar)
00149 {
00150   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00151   klfDbg("data=`"<<data<<"', escapechar="<<klfFmtCC("'\\x%02X'", (int)escapechar));
00152 
00153   bool convertOk;
00154   int k;
00155   QByteArray value_ba;
00156   k = 0;
00157   while (k < data.size()) {
00158     if (data[k] != escapechar) {
00159       value_ba += data[k];
00160       ++k;
00161       continue;
00162     }
00163     // we have an escapechar
00164     if (data[k] == escapechar && k+1 >= data.size()) {
00165       value_ba += escapechar; // backslash at end of data
00166       ++k;
00167       continue;
00168     }
00169     // not at end of data
00170     if (data[k+1] != 'x') {
00171       // backslash followed by something else than 'x', so see if it is a standard escape sequence (e.g. '\n'),
00172       // or add that escaped 'something else'
00173       if (data[k+1] == 'n')
00174         value_ba += '\n';
00175       if (data[k+1] == '0')
00176         value_ba += '\0';
00177       if (data[k+1] == 't')
00178         value_ba += '\t';
00179       if (data[k+1] == 'a')
00180         value_ba += '\a';
00181       if (data[k+1] == 'b')
00182         value_ba += '\b';
00183       if (data[k+1] == 'f')
00184         value_ba += '\f';
00185       if (data[k+1] == 'r')
00186         value_ba += '\r';
00187       if (data[k+1] == 'v')
00188         value_ba += '\v';
00189       else
00190         value_ba += data[k+1];
00191       k += 2; // had to skip the backslash
00192       continue;
00193     }
00194     // pos k points on '\\', pos k+1 points on 'x'
00195     if (k+3 >= data.size() || !klf_is_hex_char(data[k+2]) || !klf_is_hex_char(data[k+3])) {
00196       // ignore invalid escape sequence
00197       klfDbg("ignoring invalid escape sequence `"<<data.mid(k,4)<<"'") ;
00198       value_ba += data[k];
00199       ++k;
00200       continue;
00201     }
00202     // decode this char
00203     uchar cval = data.mid(k+2, 2).toUInt(&convertOk, 16);
00204     value_ba += (char)cval;
00205     k += 4; // advance of backslash + 'x' + 2 digits
00206   }
00207   return value_ba;
00208 }
00209 
00210 
00211 static QByteArray encaps_list(const QList<QByteArray>& list)
00212 {
00213   QByteArray data = "[";
00214   for (int k = 0; k < list.size(); ++k) {
00215     QByteArray d = list[k];
00216     d.replace("\\", "\\\\");
00217     d.replace(";", "\\;");
00218     d.replace("[", "\\[");
00219     d.replace("]", "\\]");
00220     data += d;
00221     if (k < list.size()-1)
00222       data += ";";
00223   }
00224   data += "]";
00225   return data;
00226 }
00227 
00228 // if 'ignore_empty_values' is TRUE, then the '=' sign is omitted with the value in a section is empty.
00229 static QByteArray encaps_map(const QList<QPair<QByteArray,QByteArray> >& sections, bool ignore_empty_values = false)
00230 {
00231   QByteArray data;
00232   data = "{";
00233   bool first_item = true;
00234   int k;
00235   for (k = 0; k < sections.size(); ++k) {
00236     if (!first_item) {
00237       data += ";";
00238     }
00239     first_item = false;
00240     QByteArray key = sections[k].first;
00241     QByteArray val = sections[k].second;
00242     // prepare the pair  key=value
00243     key.replace("\\", "\\\\");
00244     key.replace(";", "\\;");
00245     key.replace("=", "\\=");
00246     val.replace("\\", "\\\\");
00247     val.replace(";", "\\;");
00248     if (val.isEmpty() && ignore_empty_values)
00249       data += key;
00250     else
00251       data += key + "=" + val;
00252   }
00253   data += "}";
00254   return data;
00255 }
00256 
00257 
00258 static QList<QByteArray> decaps_list(const QByteArray& ba_data)
00259 {
00260   klfDbg("decaps_list, data="<<ba_data);
00261   QByteArray data = ba_data.trimmed();
00262   if (data[0] != '[')
00263     return QList<QByteArray>();
00264 
00265   QList<QByteArray> sections;
00266   QByteArray chunk;
00267   // first, split data.  take into account escaped chars.
00268   // k=1 to skip '['
00269   int k = 1;
00270   while (k < data.size()) {
00271     if (data[k] == ';') { // element separator
00272       // flush chunk as a new section
00273       sections.append(chunk);
00274       // and start a new section
00275       chunk = QByteArray();
00276       ++k;
00277     }
00278     if (data[k] == '\\') {
00279       if (k+1 < data.size()) { // there exists a next char
00280         chunk += data[k+1];
00281         k += 2;
00282       } else {
00283         chunk += data[k];
00284         ++k;
00285       }
00286       continue;
00287     }
00288     if (data[k] == ']') {
00289       // end of list marker.
00290       // flush last chunk into sections, and break.
00291       if (!chunk.isEmpty())
00292         sections.append(chunk);
00293       chunk = "";
00294       break;
00295     }
00296     // regular char, populate current chunk.
00297     chunk += data[k];
00298     ++k;
00299   }
00300   if (!chunk.isEmpty()) {
00301     // missing ']' at end, tolerate this by adding the unfinished chunk to sections
00302     sections.append(chunk);
00303   }
00304 
00305   klfDbg("sections="<<sections);
00306 
00307   return sections;
00308 }
00309 
00310 static QList<QPair<QByteArray,QByteArray> > decaps_map(const QByteArray& ba_data, bool allow_empty_values = false)
00311 {
00312   QByteArray data = ba_data.trimmed();
00313   if (data[0] != '{')
00314     return QList<QPair<QByteArray,QByteArray> >();
00315   if ( !data.contains('}') )
00316     data += '}';
00317         
00318   QList<QPair<QByteArray, QByteArray> > sections;
00319   QByteArray chunkkey;
00320   QByteArray chunkvalue;
00321   QByteArray *curChunk = &chunkkey;
00322   // first, split data.  take into account escaped chars.
00323   // k=1 to skip '{'
00324   int k = 1;
00325   while (k < data.size()) {
00326     if (data[k] == ';') { // separator for next pair
00327       // flush chunk as a new section
00328       if (!allow_empty_values && curChunk == &chunkkey)
00329         qWarning()<<KLF_FUNC_NAME<<": no '=' in pair at pos "<<k<<" in string: "<<data<<"";
00330       sections << QPair<QByteArray,QByteArray>(chunkkey, chunkvalue);
00331       // and start a new section
00332       chunkkey = QByteArray();
00333       chunkvalue = QByteArray();
00334       curChunk = &chunkkey;
00335       ++k;
00336     }
00337     if (data[k] == '\\') {
00338       if (k+1 < data.size()) { // there exists a next char
00339         *curChunk += data[k+1];
00340         k += 2;
00341       } else {
00342         *curChunk += data[k];
00343         ++k;
00344       }
00345       continue;
00346     }
00347     if (curChunk == &chunkkey && data[k] == '=') {
00348       // currently reading key, switch to reading value
00349       curChunk = &chunkvalue;
00350       ++k;
00351       continue;
00352     }
00353     if (data[k] == '}') {
00354       // end of list marker.
00355       // flush last chunk into sections, and break.
00356       if (!allow_empty_values && curChunk == &chunkkey)
00357         qWarning()<<"klfLoadVariantFromText: no '=' in pair at pos "<<k<<" in string: "<<data<<"";
00358       sections << QPair<QByteArray,QByteArray>(chunkkey, chunkvalue);
00359       break;
00360     }
00361     // regular char, populate current chunk.
00362     *curChunk += data[k];
00363     ++k;
00364   }
00365   return sections;
00366 }
00367 
00368 
00369 
00370 // returns root node. get the document with  root.ownerDocument()
00371 static QDomElement make_xml_wrapper(const QString& rootname)
00372 {
00373   QDomDocument xmldoc(rootname);
00374   QDomElement root = xmldoc.createElement(rootname);
00375   xmldoc.appendChild(root);
00376   return root;
00377 }
00378 
00379 static QDomElement parse_xml_wrapper(const QByteArray& xmldata, const QString& shouldBeRootName)
00380 {
00381   QDomDocument xmldoc(shouldBeRootName);
00382   bool result = xmldoc.setContent(xmldata);
00383   KLF_ASSERT_CONDITION(result, "Failed to read wrapper XML for klfLoadVariantFromText()",
00384                        return QDomElement() ) ;
00385 
00386   QDomElement el = xmldoc.documentElement();
00387   KLF_ASSERT_CONDITION( el.nodeName() == shouldBeRootName,
00388                         "Wrong XML root node in wrapper for klfLoadVariantFromText(): "
00389                         <<el.nodeName() ,  ; ) ;
00390   return el;
00391 }
00392 
00393 KLF_EXPORT QByteArray klfSaveVariantToText(const QVariant& value, bool saveListAndMapsAsXML, QByteArray *savedType,
00394                                            QByteArray *savedListOrMapType)
00395 {
00396   QTextCodec *tc = QTextCodec::codecForLocale();
00397 
00398   QString s;
00399   QByteArray data;
00400   int k;
00401 
00402   if (!value.isValid() || value.isNull()) {
00403     klfDbg("saving null variant.");
00404     if (savedType != NULL)
00405       *savedType = QByteArray();
00406     return QByteArray();
00407   }
00408 
00409   // values of value.type() are QMetaType::Type enum entries. See qt's doc.
00410   switch ((int)value.type()) {
00411   case QMetaType::Bool:
00412     data = value.toBool() ? "true" : "false";
00413     break;
00414   case QMetaType::Int:
00415   case QMetaType::UInt:
00416   case QMetaType::Short:
00417   case QMetaType::UShort:
00418   case QMetaType::Long:
00419   case QMetaType::ULong:
00420   case QMetaType::LongLong:
00421   case QMetaType::ULongLong:
00422   case QMetaType::Double:
00423     data = value.toString().toLocal8Bit();
00424     break;
00425   case QMetaType::Char:
00426     {
00427       char c = value.value<char>();
00428       if (c >= 32 && c <= 126 && c != '\\') {
00429         data = QByteArray(1, c);
00430       } else if (c == '\\') {
00431         data = "\\\\";
00432       } else {
00433         data = "\\" + QString::number(c, 16).toUpper().toLatin1();
00434       }
00435       break;
00436     }
00437   case QMetaType::QChar:
00438     {
00439       QChar c = value.toChar();
00440       if (tc->canEncode(c) && c != '\\') {
00441         data = tc->fromUnicode(QString(c));
00442       } else if (c == '\\') {
00443         data = "\\\\";
00444       } else {
00445         data = "\\" + QString::number(c.unicode(), 16).toUpper().toLatin1();
00446       }
00447       break;
00448     }
00449   case QMetaType::QString:
00450     {
00451       s = value.toString();
00452       if (tc->canEncode(s)) {
00453         // replace any `\' by `\\' (ie. escape backslashes)
00454         data = tc->fromUnicode(s.replace("\\", "\\\\"));
00455       } else {
00456         // encode char by char, escaping as needed
00457         data = QByteArray("");
00458         for (k = 0; k < s.length(); ++k) {
00459           if (tc->canEncode(s[k])) {
00460             data += tc->fromUnicode(s.mid(k,1));
00461           } else {
00462             data += QString("\\x%1").arg((uint)s[k].unicode(), 4, 16, QChar('0')).toLatin1();
00463           }
00464         }
00465       }
00466       break;
00467     }
00468   case QMetaType::QStringList:
00469     {
00470       const QStringList list = value.toStringList();
00471       QList<QByteArray> sections;
00472       int k;
00473       for (k = 0; k < list.size(); ++k) {
00474         sections.append(klfDataToEscaped(list[k].toUtf8()));
00475       }
00476       data = encaps_list(sections);
00477       break;
00478     }
00479   case QMetaType::QUrl:
00480     data = value.toUrl().toEncoded(); break;
00481   case QMetaType::QByteArray:
00482     {
00483       data = klfDataToEscaped(value.value<QByteArray>());
00484       break;
00485     }
00486   case QMetaType::QDate:
00487     data = value.value<QDate>().toString(Qt::SystemLocaleShortDate).toLocal8Bit(); break;
00488   case QMetaType::QTime:
00489     data = value.value<QTime>().toString(Qt::SystemLocaleShortDate).toLocal8Bit(); break;
00490   case QMetaType::QDateTime:
00491     data = value.value<QDateTime>().toString(Qt::SystemLocaleShortDate).toLocal8Bit(); break;
00492   case QMetaType::QSize:
00493     { QSize sz = value.toSize();
00494       data = QString("(%1 %2)").arg(sz.width()).arg(sz.height()).toLatin1();
00495       break;
00496     }
00497   case QMetaType::QPoint:
00498     { QPoint pt = value.toPoint();
00499       data = QString("(%1 %2)").arg(pt.x()).arg(pt.y()).toLatin1();
00500       break;
00501     }
00502   case QMetaType::QRect:
00503     { QRect r = value.toRect();
00504       data = QString("(%1 %2 %3x%4)").arg(r.left()).arg(r.top()).arg(r.width()).arg(r.height()).toLatin1();
00505       break;
00506     }
00507   case QMetaType::QColor:
00508     { QColor c = value.value<QColor>();
00509       klfDbg("Saving color "<<c<<": alpha="<<c.alpha()) ;
00510       if (c.alpha() == 255)
00511         data = QString("(%1 %2 %3)").arg(c.red()).arg(c.green()).arg(c.blue()).toLatin1();
00512       else
00513         data = QString("(%1 %2 %3 %4)").arg(c.red()).arg(c.green()).arg(c.blue()).arg(c.alpha()).toLatin1();
00514       break;
00515     }
00516   case QMetaType::QFont:
00517     { QFont f = value.value<QFont>();
00518       data = "'" + f.family().toLocal8Bit() + "'";
00519       switch (f.weight()) {
00520       case QFont::Light: data += " Light"; break;
00521       case QFont::Normal: break; //data += " Normal"; break;
00522       case QFont::DemiBold: data += " DemiBold"; break;
00523       case QFont::Bold: data += " Bold"; break;
00524       case QFont::Black: data += " Black"; break;
00525       default: data += QString(" Wgt=%1").arg(f.weight()); break;
00526       }
00527       switch (f.style()) {
00528       case QFont::StyleNormal: break; //data += " Normal"; break;
00529       case QFont::StyleItalic: data += " Italic"; break;
00530       case QFont::StyleOblique: data += " Oblique"; break;
00531       default: break;
00532       }
00533       // QFontInfo is preferred, if  f  was set with a pixelSize().
00534       data += " " + QString::number(QFontInfo(f).pointSize()).toLatin1();
00535       break;
00536     }
00537   case QMetaType::QBrush:
00538     { QBrush b = value.value<QBrush>();
00539       if (!b.matrix().isIdentity())
00540         break; // forget about saving complex brushes here
00541       int bstyle = b.style();
00542       // find index in our brush style enum
00543       int k;
00544       bool found_style = false;
00545       for (k = 0; klf_brush_styles[k].brushStyle >= 0 && klf_brush_styles[k].style != NULL; ++k) {
00546         if (klf_brush_styles[k].brushStyle == bstyle) {
00547           found_style = true;
00548           break;
00549         }
00550       }
00551       if (!found_style) {
00552         // didn't find this style, this is a complex brush. Need to save it via a datastream.
00553         break;
00554       }
00555       // found brush style. This is a simple brush with just a style and a color.
00556       data = "(";
00557       data += klf_brush_styles[k].style;
00558       if (strlen(klf_brush_styles[k].style))
00559         data += " ";
00560       QColor c = b.color();
00561       data += QString("%1 %2 %3 %4").arg(c.red()).arg(c.green()).arg(c.blue()).arg(c.alpha());
00562       data += ")";
00563       break;
00564     }
00565   case QMetaType::QTextFormat:
00566     {
00567       QTextFormat tf = value.value<QTextFormat>();
00568       const QMap<int,QVariant> props = tf.properties();
00569 
00570       QList<QPair<QByteArray,QByteArray> > sections;
00571 
00572       // first find the QTextFormat type.
00573       int k;
00574       for (k = 0; klf_text_format_formats[k].format != NULL; ++k)
00575         if (klf_text_format_formats[k].formatId == tf.type())
00576           break;
00577       if (klf_text_format_formats[k].format == NULL) {
00578         // didn't find format, something is bound to go wrong, so fall back
00579         // on Qt's datastream saving.
00580         data = QByteArray();
00581         break;
00582       }
00583       // found format. This will be the first (value-less) section.
00584       sections << QPair<QByteArray,QByteArray>(klf_text_format_formats[k].format, QByteArray());
00585 
00586       QMap<int,QVariant>::const_iterator it;
00587       for (it = props.begin(); it != props.end(); ++it) {
00588         int propId = it.key();
00589         QVariant propValue = it.value();
00590         // Add data for this property.
00591 
00592         // first look to see if a keyword is already known to be available
00593         for (k = 0; klf_text_format_keywords[k].keyword != NULL; ++k)
00594           if (klf_text_format_keywords[k].propId == propId &&
00595               klf_text_format_keywords[k].fixed_value == propValue)
00596             break;
00597         const char *kw = klf_text_format_keywords[k].keyword;
00598         if (kw != NULL) {
00599           // found a keyword for this property-value pair
00600           QByteArray key = kw;
00601           sections << QPair<QByteArray,QByteArray>(kw, QByteArray());
00602           continue;
00603         }
00604 
00605         // now look to see if we can name the property
00606         for (k = 0; klf_text_format_props[k].key != NULL; ++k)
00607           if (klf_text_format_props[k].propId == propId)
00608             break;
00609         if (klf_text_format_props[k].key != NULL) {
00610           // make sure the variant has the advertised type
00611           if ( !strcmp(klf_text_format_props[k].type, propValue.typeName()) ) {
00612             // found the property in our list of common properties
00613             QByteArray key = klf_text_format_props[k].key;
00614             QByteArray value = klfSaveVariantToText(propValue, true); // resort to XML for lists/maps...
00615             sections << QPair<QByteArray,QByteArray>(key, value);
00616             continue;
00617           } else {
00618             qWarning()<<KLF_FUNC_NAME<<": QTextFormat property "<<klf_text_format_props[k].key
00619                       <<" 's type is `"<<propValue.typeName()<<"' which is not the known type: "
00620                       <<klf_text_format_props[k].type;
00621           }
00622         }
00623 
00624         // this property is unknown to us. store it as we can.
00625         QByteArray key = QString::number(propId).toLatin1();
00626         QByteArray value;
00627         value = QByteArray("[")+propValue.typeName()+"]"+klfSaveVariantToText(propValue, true);
00628       }
00629       data = encaps_map(sections, true);
00630       break;
00631     }
00632   case QMetaType::QVariantList:
00633     {
00634       klfDbg("Saving list!") ;
00635       const QList<QVariant>& list = value.toList();
00636       if (saveListAndMapsAsXML) {
00637         QDomElement el = make_xml_wrapper("variant-list");
00638         el = klfSaveVariantListToXML(list, el);
00639         data = el.ownerDocument().toByteArray(-1);
00640       } else {
00641         QList<QByteArray> sections;
00642         QByteArray innertype;
00643         for (k = 0; k < list.size(); ++k) {
00644           if (k == 0)
00645             innertype = list[k].typeName();
00646           if (innertype != list[k].typeName()) {
00647             klfWarning("saving list: not all inner QVariants have same type. Found a "<<innertype
00648                        <<" along with a "<<list[k].typeName());
00649           }
00650           sections << klfSaveVariantToText(list[k]);
00651         }
00652         if (savedListOrMapType != NULL)
00653           *savedListOrMapType = innertype;
00654         data = encaps_list(sections);
00655       }
00656       break;
00657     }
00658   case QMetaType::QVariantMap:
00659     {
00660       klfDbg("Saving Map!") ;
00661       const QMap<QString,QVariant>& map = value.toMap();
00662       if (saveListAndMapsAsXML) {
00663         QDomElement el = make_xml_wrapper("variant-map");
00664         klfDbg("map="<<map) ;
00665         el = klfSaveVariantMapToXML(map, el);
00666         data = el.ownerDocument().toByteArray(-1);
00667         klfDbg("saved XML: data="<<data) ;
00668       } else {
00669         QList<QPair<QByteArray, QByteArray> > sections;
00670         QByteArray innertype, thistype;
00671         bool firstround = true;
00672         for (QMap<QString,QVariant>::const_iterator it = map.begin(); it != map.end(); ++it) {
00673           QByteArray k = klfSaveVariantToText(QVariant(it.key()));
00674           QByteArray v = klfSaveVariantToText(it.value());
00675           thistype = it.value().typeName();
00676           if (firstround) {
00677             innertype = thistype;
00678             firstround = false;
00679           }
00680           if (innertype != thistype) {
00681             klfWarning("saving map: not all inner QVariants have same type. Found a "<<innertype
00682                        <<" along with a "<<thistype);
00683           }
00684           sections << QPair<QByteArray,QByteArray>(k, v);
00685         }
00686         if (savedListOrMapType != NULL)
00687           *savedListOrMapType = innertype;
00688         data = encaps_map(sections);
00689       }
00690       break;
00691     }
00692   default:
00693     break;
00694   };
00695 
00696   // -- some other types --
00697 
00698   QByteArray typeName = value.typeName();
00699 
00700   QByteArray typeSpec = QByteArray();
00701   if (KLFSpecifyableRegisteredType::isRegistered(typeName)) {
00702     KLFSpecifyableType * t =
00703       const_cast<KLFSpecifyableType*>(static_cast<const KLFSpecifyableType*>(value.data()));
00704 
00705     typeSpec = t->specification();
00706     if (savedType != NULL) {
00707       *savedType = typeName + "/" + typeSpec;
00708     }
00709   } else {
00710     if (savedType != NULL)
00711       *savedType = typeName;
00712   }
00713 
00714   if (typeName == "KLFEnumType") {
00715     // just save the integer value!
00716     KLFEnumType e = value.value<KLFEnumType>();
00717     data = QByteArray::number(e.value());
00718   }
00719 
00720   if (KLFPObjRegisteredType::isRegistered(typeName)) {
00721    KLFAbstractPropertizedObject * obj =
00722       const_cast<KLFAbstractPropertizedObject*>(static_cast<const KLFAbstractPropertizedObject*>(value.data()));
00723 
00724     bool hasfixedtypes = obj->hasFixedTypes();
00725 
00726     QVariantMap props = obj->allProperties();
00727     if (!hasfixedtypes) {
00728       return klfSaveVariantToText(props, true); // save all with XML
00729     }
00730     // if we have fixed types, convert them all to text (this is human-readable)
00731     QVariantMap propstexts;
00732     for (QVariantMap::const_iterator it = props.begin(); it != props.end(); ++it) {
00733       propstexts[it.key()] = klfSaveVariantToText(it.value(), true); // in case of list/map values, use XML
00734       klfDbg("Saving property "<<it.key()<<" to text, value = "<<propstexts[it.key()]) ;
00735     }
00736     props = propstexts;
00737     return klfSaveVariantToText(props, false); // save all with XML
00738     // NOTE: WE HAVE USED 'return', not 'data = ', because this call to klfSaveVariantToText() is
00739     //       already "finalizing"
00740   }
00741 
00742   // protect data from some special sequences
00743 
00744   if (data.startsWith("[QVariant]") || data.startsWith("\\")) { // protect this special sequence
00745     data = "\\"+data;
00746   }
00747 
00748   // and provide a default encoding scheme in case no one up to now was able to
00749   // format the data (this format is only machine-readable ...)
00750 
00751   if (data.isNull()) {
00752     QByteArray vdata;
00753     {
00754       QDataStream stream(&vdata, QIODevice::WriteOnly);
00755       stream.setVersion(QDataStream::Qt_4_4);
00756       stream << value;
00757     }
00758     QByteArray vdata_esc = klfDataToEscaped(vdata);
00759     qDebug("\tVariant value is %s, len=%d", vdata.constData(), vdata.size());
00760     data = QByteArray("[QVariant]");
00761     data += vdata_esc;
00762   }
00763 
00764   klfDbg( "klfSaveVariantToText("<<value<<"): saved data (len="<<data.size()<<") : "<<data ) ;
00765   return data;
00766 }
00767 
00768 
00769 
00770 
00771 KLF_EXPORT QVariant klfLoadVariantFromText(const QByteArray& stringdata, const char * dataTypeName,
00772                                            const char *listOrMapDataTypeName)
00773 {
00774   KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME) ;
00775 
00776   // SOME REGULAR EXPRESSIONS
00777 
00778 #define RX_INT "-?\\d+"
00779 #define RX_COORD_SEP "\\s*(?:[,;]|\\s)\\s*" // note: non-capturing parenthesis
00780 #define RX_SIZE_SEP "\\s*(?:[,;x]|\\s)\\s*" // note: non-capturing parenthesis
00781 
00782   //                     1                           2
00783   QRegExp v2rx("^\\(?\\s*(" RX_INT ")" RX_COORD_SEP "(" RX_INT ")\\s*\\)?");
00784   static const int V2RX_X = 1, V2RX_Y = 2;
00785 
00786   //                     1                          2
00787   QRegExp szrx("^\\(?\\s*(" RX_INT ")" RX_SIZE_SEP "(" RX_INT ")\\s*\\)?");
00788   static const int SZRX_W = 1, SZRX_H = 2;
00789 
00790   //                       1                           2
00791   QRegExp rectrx("^\\(?\\s*(" RX_INT ")" RX_COORD_SEP "(" RX_INT ")"
00792                  //                       3
00793                  "(?:" RX_COORD_SEP "|\\s*([+])\\s*)"
00794                  //4                                  5         6
00795                  "(" RX_INT ")(?:" RX_COORD_SEP "|\\s*([x])\\s*)(" RX_INT ")\\s*\\)?");
00796   static const int RECTRX_X1 = 1, RECTRX_Y1 = 2, RECTRX_MIDDLESEP_PLUS = 3,
00797     RECTRX_X2orW = 4, RECTRX_LASTSEP_X = 5, RECTRX_Y2orH = 6;
00798 
00799   //                                1                     2                     3
00800   QRegExp colrx("^(?:rgba?)?\\(?\\s*(\\d+)" RX_COORD_SEP "(\\d+)" RX_COORD_SEP "(\\d+)"
00801                 //4               5
00802                 "(" RX_COORD_SEP "(\\d+))?\\s*\\)?", Qt::CaseInsensitive);
00803   static const int COLRX_R = 1, COLRX_G = 2, COLRX_B = 3, COLRX_MAYBE_ALPHA = 4, COLRX_A = 5;
00804 
00805   //                                       1                                2                     3
00806   QRegExp brushrx("^(?:q?brush)?\\(?\\s*(?:([A-Za-z_]\\w*)" RX_COORD_SEP ")?(\\d+)" RX_COORD_SEP "(\\d+)"
00807                   //            4         5                6
00808                   RX_COORD_SEP "(\\d+)"  "(" RX_COORD_SEP "(\\d+))?" "\\s*\\)?", Qt::CaseInsensitive);
00809   static const int BRUSHRX_STYLE = 1, BRUSHRX_R = 2, BRUSHRX_G = 3, BRUSHRX_B = 4, BRUSHRX_A = 6;
00810 
00811   //               1           2
00812   QRegExp fontrx("^([\"']?)\\s*(.+)\\s*\\1"
00813                  //3   4                                             5
00814                  "(\\s+(Light|Normal|DemiBold|Bold|Black|Wgt\\s*=\\s*(\\d+)))?"
00815                  //6   7                        8    9
00816                  "(\\s+(Normal|Italic|Oblique))?(\\s+(\\d+))?$");
00817   fontrx.setMinimal(true); // don't match Light|Normal|DemiBold|... etc as part of font name
00818   static const int FONTRX_FAMILY = 2, FONTRX_WEIGHT_TEXT = 4, FONTRX_WEIGHT_VALUE = 5,
00819     FONTRX_STYLE_TEXT = 7, FONTRX_POINTSIZE = 9;
00820 
00821 
00822   // START DECODING TEXT
00823 
00824   QByteArray data = stringdata; // might need slight modifications before parsing
00825 
00826   // first check: if the type string is empty, we're loading a Null variant... 
00827   if (dataTypeName == NULL || *dataTypeName == 0) {
00828     klfDbg("loading null variant.");
00829     return QVariant();
00830   }
00831 
00832   QVariant value;
00833   if (data.startsWith("[QVariant]")) {
00834     QByteArray vdata_esc = data.mid(strlen("[QVariant]"));
00835     QByteArray vdata = klfEscapedToData(vdata_esc);
00836     klfDbg( "\tAbout to read raw variant from datastr="<<vdata_esc<<", ie. from data len="<<vdata.size() ) ;
00837     QDataStream stream(vdata);
00838     stream.setVersion(QDataStream::Qt_4_4);
00839     stream >> value;
00840     return value;
00841   }
00842   if (data.startsWith("\\"))
00843     data = data.mid(1);
00844 
00845   klfDbg( "Will start loading a `"<<dataTypeName<<"' from data (len="<<data.size()<<") : "<<data ) ;
00846 
00847 
00848   QByteArray tname = dataTypeName;
00849 
00850   int idslash;
00851   QByteArray tspecification = QByteArray();
00852   if ((idslash = tname.indexOf('/')) >= 0) {
00853     tspecification = tname.mid(idslash+1); // extract the specification ...
00854     tname = tname.left(idslash); // ... and truncate the type name at the slash.
00855     klfDbg("tspecification="<<tspecification<<", tname="<<tname) ;
00856   }
00857 
00858   // now, start reading.
00859   int type = QMetaType::type(tname);
00860   klfDbg("Type is "<<type) ;
00861   bool convertOk = false; // in case we break; somewhere, it's (by default) because of failed convertion.
00862   int k;
00863   switch (type) {
00864   case QMetaType::Bool:
00865     {
00866       klfDbg("bool!") ;
00867       QByteArray lowerdata = data.trimmed().toLower();
00868       QChar c = QChar(lowerdata[0]);
00869       // true, yes, on, 1
00870       return QVariant::fromValue<bool>(c == 't' || c == 'y' || c == '1' || lowerdata == "on");
00871     }
00872   case QMetaType::Int:
00873     {
00874       klfDbg("int!") ;
00875       int i = data.toInt(&convertOk);
00876       if (convertOk)
00877         return QVariant::fromValue<int>(i);
00878       break;
00879     }
00880   case QMetaType::UInt:
00881     {
00882       klfDbg("uint!") ;
00883       uint i = data.toUInt(&convertOk);
00884       if (convertOk)
00885         return QVariant::fromValue<uint>(i);
00886       break;
00887     }
00888   case QMetaType::Short:
00889     {
00890       klfDbg("short!") ;
00891       short i = data.toShort(&convertOk);
00892       if (convertOk)
00893         return QVariant::fromValue<short>(i);
00894       break;
00895     }
00896   case QMetaType::UShort:
00897     {
00898       klfDbg("ushort!") ;
00899       ushort i = data.toUShort(&convertOk);
00900       if (convertOk)
00901         return QVariant::fromValue<ushort>(i);
00902       break;
00903     }
00904   case QMetaType::Long:
00905     {
00906       klfDbg("long!") ;
00907       long i = data.toLong(&convertOk);
00908       if (convertOk)
00909         return QVariant::fromValue<long>(i);
00910       break;
00911     }
00912   case QMetaType::ULong:
00913     {
00914       klfDbg("ulong!") ;
00915       ulong i = data.toULong(&convertOk);
00916       if (convertOk)
00917         return QVariant::fromValue<ulong>(i);
00918       break;
00919     }
00920   case QMetaType::LongLong:
00921     {
00922       klfDbg("longlong!") ;
00923       qlonglong i = data.toLongLong(&convertOk);
00924       if (convertOk)
00925         return QVariant::fromValue<qlonglong>(i);
00926       break;
00927     }
00928   case QMetaType::ULongLong:
00929     {
00930       klfDbg("ulonglong!") ;
00931       qulonglong i = data.toULongLong(&convertOk);
00932       if (convertOk)
00933         return QVariant::fromValue<qulonglong>(i);
00934       break;
00935     }
00936   case QMetaType::Double:
00937     {
00938       klfDbg("double!") ;
00939       double val = data.toDouble(&convertOk);
00940       if (convertOk)
00941         return QVariant::fromValue<double>(val);
00942       break;
00943     }
00944   case QMetaType::Char:
00945     {
00946       klfDbg("char!") ;
00947       if (data[0] == '\\') {
00948         if (data.size() < 2)
00949           break;
00950         if (data[1] == '\\')
00951           return QVariant::fromValue<char>('\\');
00952         if (data.size() < 3)
00953           break;
00954         uint c = data.mid(1).toUInt(&convertOk, 16);
00955         if (!convertOk)
00956           break;
00957         convertOk = false; // reset by default convertOk to false
00958         if (c > 255)
00959           break;
00960         return QVariant::fromValue<char>( (char)c );
00961       }
00962       return QVariant::fromValue<char>( (char)data[0] );
00963     }
00964   case QMetaType::QChar:
00965     {
00966       klfDbg("QChar!") ;
00967       if (data[0] == '\\') {
00968         if (data.size() < 2)
00969           break;
00970         if (data[1] == '\\')
00971           return QVariant::fromValue<QChar>(QChar('\\'));
00972         if (data.size() < 3)
00973           break;
00974         uint c = data.mid(1).toUInt(&convertOk, 16);
00975         if (!convertOk)
00976           break;
00977         convertOk = false; // reset by default convertOk to false
00978         if (c > 255)
00979           break;
00980         return QVariant::fromValue<QChar>( QChar(c) );
00981       }
00982       return QVariant::fromValue<QChar>( QChar(data[0]) );
00983     }
00984   case QMetaType::QString:
00985     {
00986       klfDbg("qstring!") ;
00987       QString s;
00988       QByteArray chunk;
00989       k = 0;
00990       while (k < data.size()) {
00991         if (data[k] != '\\') {
00992           chunk += data[k];
00993           ++k;
00994           continue;
00995         }
00996         if (data[k] == '\\' && k+1 >= data.size()) {
00997           chunk += '\\'; // backslash at end of data
00998           ++k;
00999           continue;
01000         }
01001         // not at end of data
01002         if (data[k+1] != 'x') {
01003           // backslash followed by something else than 'x', add that escaped 'something else'
01004           chunk += data[k+1];
01005           k += 2; // had to skip the backslash
01006           continue;
01007         }
01008         // pos k points on '\\', pos k+1 points on 'x'
01009         int nlen = -1;
01010         if (k+5 < data.size() && klf_is_hex_char(data[k+2]) && klf_is_hex_char(data[k+3])
01011             && klf_is_hex_char(data[k+4]) && klf_is_hex_char(data[k+5])) {
01012           nlen = 4; // 4-digit Unicode char
01013         }
01014         if (k+3 < data.size() && klf_is_hex_char(data[k+2]) && klf_is_hex_char(data[k+3])) {
01015           nlen = 2; // 2 last digits of 4-digit unicode char
01016         }
01017         if (nlen < 0) {
01018           // bad format, ignore the escape sequence.
01019           chunk += data[k];
01020           ++k;
01021           continue;
01022         }
01023         // decode this char
01024         ushort cval = data.mid(k+2, nlen).toUShort(&convertOk, 16);
01025         QChar ch(cval);
01026         // dump chunk into string, and add this char
01027         s += QString::fromLocal8Bit(chunk) + ch;
01028         // reset chunk
01029         chunk = QByteArray();
01030         // and advance the corresponding number of characters, point on fresh one
01031         // advance of what we read:   backslash+'x' (=2)  + number of digits
01032         k += 2 + nlen;
01033       }
01034       // dump remaining chunk
01035       s += QString::fromLocal8Bit(chunk);
01036       return QVariant::fromValue<QString>(s);
01037     }
01038   case QMetaType::QStringList:
01039     {
01040       klfDbg("qstringlist!") ;
01041       QList<QByteArray> sections = decaps_list(data);
01042 
01043       // now we separated into bytearray sections. now read those into values.
01044       QStringList list;
01045       for (k = 0; k < sections.size(); ++k) {
01046         list << QString::fromUtf8(klfEscapedToData(sections[k]));
01047       }
01048 
01049       return QVariant::fromValue<QStringList>(list);
01050     }
01051   case QMetaType::QUrl:
01052     {
01053       klfDbg("url!") ;
01054       return QVariant::fromValue<QUrl>(QUrl(QString::fromLocal8Bit(data), QUrl::TolerantMode));
01055     }
01056   case QMetaType::QByteArray:
01057     {
01058       klfDbg("qbytearray!") ;
01059       QByteArray value_ba = klfEscapedToData(data);
01060       return QVariant::fromValue<QByteArray>(value_ba);
01061     }
01062   case QMetaType::QDate:
01063     {
01064       klfDbg("qdate!") ;
01065       QString s = QString::fromLocal8Bit(data);
01066       QDate date = QDate::fromString(s, Qt::SystemLocaleShortDate);
01067       if (!date.isValid()) date = QDate::fromString(s, Qt::ISODate);
01068       if (!date.isValid()) date = QDate::fromString(s, Qt::SystemLocaleLongDate);
01069       if (!date.isValid()) date = QDate::fromString(s, Qt::DefaultLocaleShortDate);
01070       if (!date.isValid()) date = QDate::fromString(s, Qt::TextDate);
01071       if (!date.isValid()) date = QDate::fromString(s, "dd-MM-yyyy");
01072       if (!date.isValid()) date = QDate::fromString(s, "dd.MM.yyyy");
01073       if (!date.isValid()) date = QDate::fromString(s, "dd MM yyyy");
01074       if (!date.isValid()) date = QDate::fromString(s, "yyyy-MM-dd");
01075       if (!date.isValid()) date = QDate::fromString(s, "yyyy.MM.dd");
01076       if (!date.isValid()) date = QDate::fromString(s, "yyyy MM dd");
01077       if (!date.isValid()) date = QDate::fromString(s, "yyyyMMdd");
01078       if (!date.isValid())
01079         break;
01080       return QVariant::fromValue<QDate>(date);
01081     }
01082   case QMetaType::QTime:
01083     {
01084       klfDbg("qtime!") ;
01085       QString s = QString::fromLocal8Bit(data);
01086       QTime time = QTime::fromString(s, Qt::SystemLocaleShortDate);
01087       if (!time.isValid()) time = QTime::fromString(s, Qt::ISODate);
01088       if (!time.isValid()) time = QTime::fromString(s, Qt::SystemLocaleLongDate);
01089       if (!time.isValid()) time = QTime::fromString(s, Qt::DefaultLocaleShortDate);
01090       if (!time.isValid()) time = QTime::fromString(s, Qt::TextDate);
01091       if (!time.isValid()) time = QTime::fromString(s, "hh:mm:ss.z");
01092       if (!time.isValid()) time = QTime::fromString(s, "hh:mm:ss");
01093       if (!time.isValid()) time = QTime::fromString(s, "hh:mm:ss AP");
01094       if (!time.isValid()) time = QTime::fromString(s, "hh.mm.ss");
01095       if (!time.isValid()) time = QTime::fromString(s, "hh.mm.ss AP");
01096       if (!time.isValid()) time = QTime::fromString(s, "hh mm ss");
01097       if (!time.isValid()) time = QTime::fromString(s, "hh mm ss AP");
01098       if (!time.isValid()) time = QTime::fromString(s, "hhmmss");
01099       if (!time.isValid())
01100         break;
01101       return QVariant::fromValue<QTime>(time);
01102     }
01103   case QMetaType::QDateTime:
01104     {
01105       klfDbg("qdatetime!") ;
01106       QString s = QString::fromLocal8Bit(data);
01107       QDateTime dt = QDateTime::fromString(s, Qt::SystemLocaleShortDate);
01108       if (!dt.isValid()) dt = QDateTime::fromString(s, Qt::ISODate);
01109       if (!dt.isValid()) dt = QDateTime::fromString(s, Qt::SystemLocaleLongDate);
01110       if (!dt.isValid()) dt = QDateTime::fromString(s, Qt::DefaultLocaleShortDate);
01111       if (!dt.isValid()) dt = QDateTime::fromString(s, Qt::TextDate);
01112       if (!dt.isValid()) dt = QDateTime::fromString(s, "dd-MM-yyyy hh:mm:ss");
01113       if (!dt.isValid()) dt = QDateTime::fromString(s, "dd-MM-yyyy hh.mm.ss");
01114       if (!dt.isValid()) dt = QDateTime::fromString(s, "dd.MM.yyyy hh:mm:ss");
01115       if (!dt.isValid()) dt = QDateTime::fromString(s, "dd.MM.yyyy hh.mm.ss");
01116       if (!dt.isValid()) dt = QDateTime::fromString(s, "dd MM yyyy hh mm ss");
01117       if (!dt.isValid()) dt = QDateTime::fromString(s, "yyyy-MM-dd hh:mm:ss");
01118       if (!dt.isValid()) dt = QDateTime::fromString(s, "yyyy-MM-dd hh.mm.ss");
01119       if (!dt.isValid()) dt = QDateTime::fromString(s, "yyyy.MM.dd hh:mm:ss");
01120       if (!dt.isValid()) dt = QDateTime::fromString(s, "yyyy.MM.dd hh.mm.ss");
01121       if (!dt.isValid()) dt = QDateTime::fromString(s, "yyyy MM dd hh mm ss");
01122       if (!dt.isValid()) dt = QDateTime::fromString(s, "yyyyMMddhhmmss");
01123       if (!dt.isValid())
01124         break;
01125       return QVariant::fromValue<QDateTime>(dt);
01126     }
01127   case QMetaType::QSize:
01128     {
01129       klfDbg("qsize!") ;
01130       QString s = QString::fromLocal8Bit(data.trimmed());
01131       if (szrx.indexIn(s) < 0)
01132         break;
01133       QStringList vals = szrx.capturedTexts();
01134       return QVariant::fromValue<QSize>(QSize(vals[SZRX_W].toInt(), vals[SZRX_H].toInt()));
01135     }
01136   case QMetaType::QPoint:
01137     {
01138       klfDbg("qpoint!") ;
01139       QString s = QString::fromLocal8Bit(data.trimmed());
01140       if (v2rx.indexIn(s) < 0)
01141         break;
01142       QStringList vals = v2rx.capturedTexts();
01143       return QVariant::fromValue<QPoint>(QPoint(vals[V2RX_X].toInt(), vals[V2RX_Y].toInt()));
01144     }
01145   case QMetaType::QRect:
01146     {
01147       klfDbg("qrect!") ;
01148       QString s = QString::fromLocal8Bit(data.trimmed());
01149       if (rectrx.indexIn(s) < 0)
01150         break;
01151       QStringList vals = rectrx.capturedTexts();
01152       if (vals[RECTRX_MIDDLESEP_PLUS] == "+" || vals[RECTRX_LASTSEP_X] == "x") {
01153         return QVariant::fromValue<QRect>(QRect( QPoint(vals[RECTRX_X1].toInt(), vals[RECTRX_Y1].toInt()),
01154                                                  QSize(vals[RECTRX_X2orW].toInt(), vals[RECTRX_Y2orH].toInt()) ));
01155       }
01156       return QVariant::fromValue<QRect>(QRect( QPoint(vals[RECTRX_X1].toInt(), vals[RECTRX_Y1].toInt()),
01157                                                QPoint(vals[RECTRX_X2orW].toInt(), vals[RECTRX_Y2orH].toInt()) ));
01158     }
01159   case QMetaType::QColor:
01160     {
01161       klfDbg("qcolor!") ;
01162       QString colstr = QString::fromLocal8Bit(data.trimmed());
01163       // try our regexp
01164       if (colrx.indexIn(colstr) < 0) {
01165         klfDbg("color "<<colstr<<" does not match regexp="<<colrx.pattern()<<", trying named...") ;
01166         // try a named color
01167         QColor color;  color.setNamedColor(colstr);
01168         // if we got a valid color, yepee
01169         if (color.isValid())
01170           return color;
01171         break;
01172       }
01173       // our regexp matched
01174       QStringList vals = colrx.capturedTexts();
01175       QColor color = QColor(vals[COLRX_R].toInt(), vals[COLRX_G].toInt(), vals[COLRX_B].toInt(), 255);
01176       if (!vals[COLRX_MAYBE_ALPHA].isEmpty())
01177         color.setAlpha(vals[COLRX_A].toInt());
01178       return QVariant::fromValue<QColor>(color);
01179     }
01180   case QMetaType::QFont:
01181     {
01182       klfDbg("qfont!") ;
01183       if (fontrx.indexIn(QString::fromLocal8Bit(data.trimmed())) < 0) {
01184         klfDbg("malformed font: "<<data);
01185         break;
01186       }
01187       QStringList vals = fontrx.capturedTexts();
01188       klfDbg("parsing font: data="<<data<<"; captured texts are: "<<vals );
01189 
01190       QString family = vals[FONTRX_FAMILY].trimmed();
01191       QString weighttxt = vals[FONTRX_WEIGHT_TEXT];
01192       QString weightval = vals[FONTRX_WEIGHT_VALUE];
01193       QString styletxt = vals[FONTRX_STYLE_TEXT];
01194       QString ptsval = vals[FONTRX_POINTSIZE];
01195 
01196       int weight = QFont::Normal;
01197       if (weighttxt == "Light") weight = QFont::Light;
01198       else if (weighttxt == "Normal") weight = QFont::Normal;
01199       else if (weighttxt == "DemiBold") weight = QFont::DemiBold;
01200       else if (weighttxt == "Bold") weight = QFont::Bold;
01201       else if (weighttxt == "Black") weight = QFont::Black;
01202       else if (weighttxt.startsWith("Wgt")) weight = weightval.toInt();
01203       
01204 
01205       QFont::Style style = QFont::StyleNormal;
01206       if (styletxt == "Normal") style = QFont::StyleNormal;
01207       else if (styletxt == "Italic") style = QFont::StyleItalic;
01208       else if (styletxt == "Oblique") style = QFont::StyleOblique;
01209 
01210       int pt = -1;
01211       if (!ptsval.isEmpty())
01212         pt = ptsval.toInt();
01213 
01214       QFont font(family, pt, weight);
01215       font.setStyle(style);
01216       return QVariant::fromValue<QFont>(font);
01217     }
01218   case QMetaType::QBrush:
01219     {
01220       klfDbg("qbrush!") ;
01221       if (brushrx.indexIn(QString::fromLocal8Bit(data.trimmed())) < 0) {
01222         klfDbg("malformed brush text: "<<data) ;
01223         break;
01224       }
01225       QStringList vals = brushrx.capturedTexts();
01226       QString style = vals[BRUSHRX_STYLE];
01227       // find brush style
01228       int k;
01229       bool style_found = false;
01230       for (k = 0; klf_brush_styles[k].brushStyle >= 0 && klf_brush_styles[k].style != NULL; ++k) {
01231         if (klf_brush_styles[k].style == style) {
01232           style_found = true;
01233           break;
01234         }
01235       }
01236       if (!style_found) {
01237         klfDbg("Can't find style"<<style<<" in brush style list!");
01238         break;
01239       }
01240       int qbrush_style = klf_brush_styles[k].brushStyle;
01241       // read the color and construct QBrush.
01242       QColor c = QColor(vals[BRUSHRX_R].toInt(),  vals[BRUSHRX_G].toInt(),
01243                         vals[BRUSHRX_B].toInt());
01244       if (!vals[BRUSHRX_A].isEmpty())
01245         c.setAlpha(vals[BRUSHRX_A].toInt());
01246       return QBrush(c, static_cast<Qt::BrushStyle>(qbrush_style));
01247     }
01248   case QMetaType::QTextFormat:
01249     {
01250       klfDbg("qtextformat!") ;
01251       int k;
01252       QList<QPair<QByteArray,QByteArray> > sections = decaps_map(data, true);
01253       if (sections.isEmpty()) {
01254         klfDbg("Invalid QTextFormat data.") ;
01255         break;
01256       }
01257       QPair<QByteArray,QByteArray> firstSection = sections.takeFirst();
01258       QString fmttype = QString::fromLatin1(firstSection.first);
01259       // find the format in our list
01260       for (k = 0; klf_text_format_formats[k].format != NULL; ++k)
01261         if (QString::compare(fmttype, QLatin1String(klf_text_format_formats[k].format),
01262                              Qt::CaseInsensitive) == 0)
01263           break;
01264       if (klf_text_format_formats[k].format == NULL) {
01265         klfDbg("QTextFormat: Invalid format type: "<<fmttype) ;
01266         break;
01267       }
01268       int qtextformat_type = klf_text_format_formats[k].formatId;
01269 
01270       // now decode the list of properties
01271       QTextFormat textformat(qtextformat_type);
01272       QList<QPair<QByteArray,QByteArray> >::const_iterator it;
01273       for (it = sections.begin(); it != sections.end(); ++it) {
01274         QByteArray key = (*it).first.trimmed();
01275         QByteArray value = (*it).second;
01276         klfDbg("QTextFormat: considering property pair key="<<key<<"; value="<<value) ;
01277         // see if the key is a keyword
01278         for (k = 0; klf_text_format_keywords[k].keyword != NULL; ++k)
01279           if (QString::compare(QLatin1String(klf_text_format_keywords[k].keyword),
01280                                key, Qt::CaseInsensitive) == 0)
01281             break;
01282         if (klf_text_format_keywords[k].keyword != NULL) {
01283           // this is a keyword.
01284           klfDbg("QTextFormat: is keyword, propId="<<klf_text_format_keywords[k].propId<<", fixed_value="
01285                  <<klf_text_format_keywords[k].fixed_value) ;
01286           textformat.setProperty(klf_text_format_keywords[k].propId,
01287                                  klf_text_format_keywords[k].fixed_value);
01288           continue;
01289         }
01290         // see if the key is a known property name
01291         for (k = 0; klf_text_format_props[k].key != NULL; ++k)
01292           if (QString::compare(QLatin1String(klf_text_format_props[k].key),
01293                                key, Qt::CaseInsensitive) == 0)
01294             break;
01295         if (klf_text_format_props[k].key != NULL) {
01296           klfDbg("QTextFormat: is known property of type "<<klf_text_format_props[k].type) ;
01297           // load property propId, of type type
01298           QVariant vval = klfLoadVariantFromText(value, klf_text_format_props[k].type, "XML");
01299           textformat.setProperty(klf_text_format_props[k].propId, vval);
01300           continue;
01301         }
01302         // load generally-saved qvariant property
01303 
01304         bool tointok = true;
01305         int propid = key.toInt(&tointok);
01306         if (!tointok) {
01307           qWarning()<<KLF_FUNC_NAME<<": QTextFormat bad format for general property key=value pair; "
01308                     <<"key is not a numerical property ID, nor is it a known property name.";
01309         }
01310 
01311         klfDbg("QTextFormat: property is not a known one. propid="<<propid) ;
01312 
01313         // trim space beginning of string
01314         while (value.size() && QChar(value[0]).isSpace())
01315           value.remove(0, 1);
01316         int i;
01317         if (value.isEmpty() || !value.startsWith("[") || ((i = value.indexOf(']')) == -1)) {
01318           qWarning().nospace()<<KLF_FUNC_NAME<<": QTextFormat bad format for general property, value does "
01319                               <<"not begin with \"[type-name]\".";
01320           continue;
01321         }
01322         QByteArray typenm = value.mid(1, i-1);
01323         QByteArray valuedata = value.mid(i+1);
01324         QVariant vval = klfLoadVariantFromText(valuedata, typenm);
01325         klfDbg("setting generalized property "<<propid<<" to value "<<vval) ;
01326         textformat.setProperty(propid, vval);
01327       }
01328       return textformat;
01329     }
01330   case QMetaType::QVariantList:
01331     {
01332       klfDbg("qvariantlist!") ;
01333       if (listOrMapDataTypeName == QLatin1String("XML")) {
01334         QDomElement el = parse_xml_wrapper(data, "variant-list");
01335         return klfLoadVariantListFromXML(el);
01336       } else {
01337         QList<QByteArray> sections = decaps_list(data);
01338 
01339         // now we separated into bytearray sections. now read those into values.
01340         QVariantList list;
01341         for (k = 0; k < sections.size(); ++k) {
01342           QVariant val = klfLoadVariantFromText(sections[k], listOrMapDataTypeName);
01343         list << val;
01344         }
01345 
01346         return QVariant::fromValue<QVariantList>(list);
01347       }
01348     }
01349   case QMetaType::QVariantMap:
01350     {
01351       klfDbg("qvariantmap!") ;
01352       if (listOrMapDataTypeName == QLatin1String("XML")) {
01353         QDomElement el = parse_xml_wrapper(data, "variant-map");
01354         return klfLoadVariantMapFromXML(el);
01355       } else {
01356         const QList<QPair<QByteArray,QByteArray> > sections = decaps_map(data);
01357         QVariantMap vmap;
01358         QList<QPair<QByteArray,QByteArray> >::const_iterator it;
01359         for (it = sections.begin(); it != sections.end(); ++it) {
01360           QString key = klfLoadVariantFromText((*it).first, "QString").toString();
01361           QVariant value = klfLoadVariantFromText((*it).second, listOrMapDataTypeName);
01362           vmap[key] = value;
01363         }
01364         return QVariant::fromValue<QVariantMap>(vmap);
01365       }
01366     }
01367   default:
01368     break;
01369   }
01370 
01371   if (tname == "KLFEnumType") {
01372     // just load the integer value!
01373     KLFEnumType e;
01374     e.setSpecification(tspecification);
01375     e.setValue(data.toInt());
01376     return QVariant::fromValue<KLFEnumType>(e);
01377   }
01378 
01379   klfDbg("other type or failed to load the good type!") ;
01380 
01381   // maybe load a propertized object.
01382   if (KLFPObjRegisteredType::isRegistered(tname)) {
01383     // construct a default such wanted object of requried type
01384     QVariant value(QMetaType::type(dataTypeName), (const void*)NULL);
01385     KLFAbstractPropertizedObject * obj =
01386       const_cast<KLFAbstractPropertizedObject*>(static_cast<const KLFAbstractPropertizedObject*>(value.data()));
01387 
01388     if (tspecification.size()) {
01389       KLFSpecifyableType * st =
01390         const_cast<KLFSpecifyableType*>(static_cast<const KLFSpecifyableType*>(value.data()));
01391       st->setSpecification(tspecification);
01392     }
01393 
01394     bool hasfixedtypes = obj->hasFixedTypes();
01395 
01396     klfDbg("loading an abstr.prop.obj: "<<obj) ;
01397     klfDbg("obj is of type "<<obj->objectKind()<<", fixedtypes="<<hasfixedtypes) ;
01398 
01399     QVariantMap props = klfLoadVariantFromText(data, "QVariantMap", hasfixedtypes ? "QByteArray" : "XML").toMap();
01400     if (!hasfixedtypes) {
01401       obj->setAllProperties(props); // the properties are all as required
01402       return value;
01403     }
01404     // if we have fixed types, convert them all back from text (this is human-readable)
01405     QVariantMap propsconverted;
01406     for (QVariantMap::const_iterator it = props.begin(); it != props.end(); ++it) {
01407       QByteArray tn = obj->typeNameFor(it.key());
01408       // add type specification if needed
01409       QByteArray ts = obj->typeSpecificationFor(it.key());
01410       if (ts.size())
01411         tn += "/"+ts;
01412       propsconverted[it.key()] = klfLoadVariantFromText(it.value().toByteArray(), tn,
01413                                                         "XML"); // in case of list/map values, we have used XML
01414       klfDbg("Loading property "<<it.key()<<" from saved text, value = "<<propsconverted[it.key()]) ;
01415     }
01416     props = propsconverted;
01417     obj->setAllProperties(props);
01418     return value;
01419   }
01420 
01421   qWarning("klfLoadVariantFromText: Can't load a %s from %s !", dataTypeName, stringdata.constData());
01422   return QVariant();
01423 }
01424 
01425 
01426 
01427 
01428 
01429 // ----------------------------------------------------
01430 
01431 
01432 
01433 
01434 
01435 KLF_EXPORT QDomElement klfSaveVariantMapToXML(const QVariantMap& vmap, QDomElement baseNode)
01436 {
01437   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
01438 
01439   QDomDocument doc = baseNode.ownerDocument();
01440 
01441   for (QVariantMap::const_iterator it = vmap.begin(); it != vmap.end(); ++it) {
01442     QString key = it.key();
01443     QVariant value = it.value();
01444     
01445     QDomElement pairNode = doc.createElement("pair");
01446     // * key
01447     QDomElement keyNode = doc.createElement("key");
01448     QDomText keyText = doc.createTextNode(key);
01449     keyNode.appendChild(keyText);
01450     pairNode.appendChild(keyNode);
01451     // * value data
01452     QDomElement vdataNode = doc.createElement("value");
01453     QString vtype = QLatin1String(value.typeName());
01454     if (vtype == "QVariantMap") {
01455       vdataNode.setAttribute(QLatin1String("type"), vtype);
01456       vdataNode = klfSaveVariantMapToXML(value.toMap(), vdataNode);
01457     } else if (vtype == "QVariantList") {
01458       vdataNode.setAttribute(QLatin1String("type"), vtype);
01459       vdataNode = klfSaveVariantListToXML(value.toList(), vdataNode);
01460     } else {
01461       QByteArray savedvtype;
01462       QDomText vdataText = doc.createTextNode(QString::fromLocal8Bit(klfSaveVariantToText(value, false, &savedvtype)));
01463       vdataNode.appendChild(vdataText);
01464       vdataNode.setAttribute(QLatin1String("type"), QString::fromUtf8(savedvtype));
01465     }
01466     pairNode.appendChild(vdataNode);
01467     // now append this pair to our list
01468     baseNode.appendChild(pairNode);
01469   }
01470   return baseNode;
01471 }
01472 
01473 KLF_EXPORT QVariantMap klfLoadVariantMapFromXML(const QDomElement& xmlNode)
01474 {
01475   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
01476 
01477   QVariantMap vmap;
01478 
01479   QDomNode n;
01480   for (n = xmlNode.firstChild(); ! n.isNull(); n = n.nextSibling()) {
01481     QDomElement e = n.toElement(); // try to convert the node to an element.
01482     if ( e.isNull() || n.nodeType() != QDomNode::ElementNode )
01483       continue;
01484     if ( e.nodeName() != "pair" ) {
01485       klfWarning("Ignoring unexpected tag "<<e.nodeName()) ;
01486       continue;
01487     }
01488     // read this pair
01489     QString key;
01490     QByteArray valuetype;
01491     QByteArray valuedata;
01492     QDomElement valueNode;
01493     QDomNode nn;
01494     for (nn = e.firstChild(); ! nn.isNull(); nn = nn.nextSibling()) {
01495       klfDbg("inside <pair>: read node "<<nn.nodeName()) ;
01496       QDomElement ee = nn.toElement();
01497       if ( ee.isNull() || nn.nodeType() != QDomNode::ElementNode )
01498         continue;
01499       if ( ee.nodeName() == "key" ) {
01500         key = ee.text();
01501         continue;
01502       }
01503       if ( ee.nodeName() == "value" ) {
01504         // "local 8-bit"  because klfLoadVariantFromText() assumes local 8-bit encoding
01505         valueNode = ee;
01506         valuedata = ee.text().toLocal8Bit();
01507         valuetype = ee.attribute("type").toUtf8();
01508         continue;
01509       }
01510       klfWarning("Ignoring unexpected tag "<<ee.nodeName()<<" in <pair>!") ;
01511     }
01512     QVariant value;
01513     if (valuetype == "QVariantMap") {
01514       value = QVariant::fromValue<QVariantMap>(klfLoadVariantMapFromXML(valueNode));
01515     } else if (valuetype == "QVariantList") {
01516       value = QVariant::fromValue<QVariantList>(klfLoadVariantListFromXML(valueNode));
01517     } else {
01518       value = klfLoadVariantFromText(valuedata, valuetype.constData());
01519     }
01520     // set this value in our variant map
01521     vmap[key] = value;
01522   }
01523   return vmap;
01524 }
01525 
01526 
01527 KLF_EXPORT QDomElement klfSaveVariantListToXML(const QVariantList& vlist, QDomElement baseNode)
01528 {
01529   QDomDocument doc = baseNode.ownerDocument();
01530 
01531   for (QVariantList::const_iterator it = vlist.begin(); it != vlist.end(); ++it) {
01532     QVariant value = *it;
01533 
01534     QDomElement elNode = doc.createElement(QLatin1String("item"));
01535     QString vtype = QString::fromLatin1(value.typeName()); // "Latin1" encoding by convention
01536     //                                        because type names do not have any special chars
01537     if (vtype == "QVariantMap") {
01538       elNode.setAttribute(QLatin1String("type"), vtype);
01539       elNode = klfSaveVariantMapToXML(value.toMap(), elNode);
01540     } else if (vtype == "QVariantList") {
01541       elNode.setAttribute(QLatin1String("type"), vtype);
01542       elNode = klfSaveVariantListToXML(value.toList(), elNode);
01543     } else {
01544       QByteArray savedvtype;
01545       QDomText vdataText = doc.createTextNode(QString::fromLocal8Bit(klfSaveVariantToText(value, false, &savedvtype)));
01546       elNode.appendChild(vdataText);
01547       elNode.setAttribute(QLatin1String("type"), QString::fromUtf8(savedvtype));
01548     }
01549     // now append this pair to our list
01550     //klfDbg( "... appending node!" ) ;
01551     baseNode.appendChild(elNode);
01552   }
01553 
01554   return baseNode;
01555 }
01556 
01557 KLF_EXPORT QVariantList klfLoadVariantListFromXML(const QDomElement& xmlNode)
01558 {
01559   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
01560 
01561   QVariantList vlist;
01562 
01563   QDomNode n;
01564   for (n = xmlNode.firstChild(); ! n.isNull(); n = n.nextSibling()) {
01565     QDomElement e = n.toElement(); // try to convert the node to an element.
01566     if ( e.isNull() || n.nodeType() != QDomNode::ElementNode )
01567       continue;
01568     if ( e.nodeName() != QLatin1String("item") ) {
01569       qWarning("%s: ignoring unexpected tag `%s'!\n", KLF_FUNC_NAME, qPrintable(e.nodeName()));
01570       continue;
01571     }
01572 
01573     QString vtype = e.attribute(QLatin1String("type"));
01574 
01575     QVariant value;
01576     if (vtype == QLatin1String("QVariantMap")) {
01577       value = QVariant::fromValue<QVariantMap>(klfLoadVariantMapFromXML(e));
01578     } else if (vtype == QLatin1String("QVariantList")) {
01579       value = QVariant::fromValue<QVariantList>(klfLoadVariantListFromXML(e));
01580     } else {
01581       value = klfLoadVariantFromText(e.text().toLocal8Bit(), vtype.toLatin1().constData());
01582     }
01583 
01584     // set this value in our variant map
01585     vlist << value;
01586   }
01587   return vlist;
01588 }
01589 
01590 
01591 
01592 // --------------------------------------------------
01593 
01594 
01595 KLFAbstractPropertizedObjectSaver::KLFAbstractPropertizedObjectSaver()
01596   : KLFFactoryBase(&pFactoryManager)
01597 {
01598 }
01599 KLFAbstractPropertizedObjectSaver::~KLFAbstractPropertizedObjectSaver()
01600 {
01601 }
01602 
01603 // static
01604 KLFFactoryManager KLFAbstractPropertizedObjectSaver::pFactoryManager;
01605 
01606 // static
01607 KLFAbstractPropertizedObjectSaver *
01608 /* */ KLFAbstractPropertizedObjectSaver::findRecognizedFormat(const QByteArray& data, QString * format)
01609 {
01610   QList<KLFFactoryBase*> allFactories = pFactoryManager.registeredFactories();
01611   QString s;
01612   foreach (KLFFactoryBase * ff, allFactories) {
01613     KLFAbstractPropertizedObjectSaver *f = (KLFAbstractPropertizedObjectSaver*) ff;
01614     if ((s = f->recognizeDataFormat(data)).size() != 0) {
01615       // recognized format
01616       if (format != NULL)
01617         *format = s;
01618       return f;
01619     }
01620   }
01621   // failed to recognize format
01622   if (format != NULL)
01623     *format = QString();
01624   return NULL;
01625 }
01626 
01627 // static
01628 KLFAbstractPropertizedObjectSaver *
01629 /* */ KLFAbstractPropertizedObjectSaver::findSaverFor(const QString& format)
01630 {
01631   return dynamic_cast<KLFAbstractPropertizedObjectSaver*>(pFactoryManager.findFactoryFor(format));
01632 }
01633 
01634 // this is not a class member
01635 KLFBaseFormatsPropertizedObjectSaver __klf_baseformats_pobj_saver; // this will automatically register it...
01636 
01637 
01638 KLF_EXPORT QByteArray klfSave(const KLFAbstractPropertizedObject * obj, const QString& format)
01639 {
01640   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
01641   KLFAbstractPropertizedObjectSaver * saver =
01642     KLFAbstractPropertizedObjectSaver::findSaverFor(format);
01643   KLF_ASSERT_NOT_NULL(saver, "Can't find object saver for format="<<format<<" !", return QByteArray(); ) ;
01644   return saver->save(obj, format);
01645 }
01646 
01647 KLF_EXPORT bool klfLoad(const QByteArray& data, KLFAbstractPropertizedObject * obj, const QString& format)
01648 {
01649   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
01650   KLFAbstractPropertizedObjectSaver * saver;
01651   QString f = format;
01652   if (f.isEmpty()) { // need to recognize format
01653     saver = KLFAbstractPropertizedObjectSaver::findRecognizedFormat(data, &f);
01654     KLF_ASSERT_CONDITION(!f.isEmpty(), "Can't recognize data format!", return false; ) ;
01655   } else {
01656     saver = KLFAbstractPropertizedObjectSaver::findSaverFor(f);
01657   }
01658   KLF_ASSERT_NOT_NULL(saver, "Can't find object saver for format="<<f<<" !", return false; ) ;
01659   return saver->load(data, obj, f);
01660 }
01661 
01662 

Generated by doxygen 1.7.6.1. The KLatexFormula website is hosted on sourceforge.net