[KLF Application][KLF Tools][KLF Backend][KLF Home]
KLatexFormula Project

src/klftools/klfutil.cpp

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

Generated by doxygen 1.7.3