[KLF Application][KLF Tools][KLF Backend][KLF Home]
KLatexFormula Project
klfutil.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  * file klfutil.cpp
3  * This file is part of the KLatexFormula Project.
4  * Copyright (C) 2011 by Philippe Faist
5  * philippe.faist at bluewin.ch
6  * *
7  * This program is free software; you can redistribute it and/or modify *
8  * it under the terms of the GNU General Public License as published by *
9  * the Free Software Foundation; either version 2 of the License, or *
10  * (at your option) any later version. *
11  * *
12  * This program is distributed in the hope that it will be useful, *
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15  * GNU General Public License for more details. *
16  * *
17  * You should have received a copy of the GNU General Public License *
18  * along with this program; if not, write to the *
19  * Free Software Foundation, Inc., *
20  * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
21  ***************************************************************************/
22 /* $Id: klfutil.cpp 611 2011-03-17 22:43:09Z phfaist $ */
23 
24 #include <stdlib.h>
25 
26 #include <qglobal.h>
27 #include <QByteArray>
28 #include <QString>
29 #include <QDebug>
30 #include <QFile>
31 #include <QDir>
32 #include <QLibraryInfo>
33 #include <QUrl>
34 #include <QMessageBox>
35 #include <QTextCodec>
36 #include <QDateTime>
37 #include <QRect>
38 #include <QIcon>
39 #include <QColor>
40 #include <QBrush>
41 #include <QPushButton>
42 #include <QApplication>
43 #include <QDesktopWidget>
44 #include <QDomDocument>
45 #include <QTextFormat>
46 
47 #include "klfutil.h"
48 #include "klfstyle.h"
49 
50 
51 
52 KLF_EXPORT bool klfEnsureDir(const QString& dir)
53 {
54  if ( ! QDir(dir).exists() ) {
55  bool r = QDir("/").mkpath(dir);
56  if ( ! r ) {
57  qWarning("Can't create local directory %s!", qPrintable(dir));
58  return false;
59  }
60  // set permissions to "rwx------"
61  r = QFile::setPermissions(dir, QFile::ReadOwner|QFile::WriteOwner|QFile::ExeOwner|
62  QFile::ReadUser|QFile::WriteUser|QFile::ExeUser);
63  if ( ! r ) {
64  qWarning("Can't set permissions to local config directory `%s' !", qPrintable(dir));
65  return false;
66  }
67  }
68  return true;
69 }
70 
71 
72 
74  const QStringList& interestQueryItems)
75 {
78  int k;
79  for (k = 0; k < qitems.size(); ++k) {
80  const QPair<QString,QString>& p = qitems[k];
81  if (interestQueryItems.isEmpty() || interestQueryItems.contains(p.first))
82  map[p.first] = p.second;
83  }
84  return map;
85 }
86 
87 
88 
89 KLF_EXPORT uint klfUrlCompare(const QUrl& url1, const QUrl& url2, uint interestFlags,
90  const QStringList& interestQueryItems)
91 {
93  klfDbg( ": 1="<<url1<<"; 2="<<url2<<"; interestflags="<<interestFlags<<"; int.q.i="
94  <<interestQueryItems ) ;
95  uint compareflags = 0x00;
96 
97  Qt::CaseSensitivity queryItemValsCS = Qt::CaseSensitive;
99  queryItemValsCS = Qt::CaseInsensitive;
100 
101  QMap<QString,QString> qitems_map1;
102  QMap<QString,QString> qitems_map2;
103 
104  QUrl u1 = url1;
105  QUrl u2 = url2;
108 
109  klfDbg( " after q-i-stripping: u1="<<u1<<"; u2="<<u2 ) ;
110 
111  if (interestFlags &
113  // have an operation that needs these maps, so load them
114  qitems_map1 = klf_url_query_items_map(url1, interestQueryItems);
115  qitems_map2 = klf_url_query_items_map(url2, interestQueryItems);
116  }
117 
118  if (interestFlags & KlfUrlCompareEqual) {
119  // test equality
120  if (u1 == u2 && qitems_map1 == qitems_map2)
121  compareflags |= KlfUrlCompareEqual;
122  }
123 
124  if (interestFlags & KlfUrlCompareLessSpecific) {
125  // test url1 is less specific than url2 <-> url1 items are included in those of url2
126  if (u1 == u2) {
127  bool ok = klfMapIsIncludedIn(qitems_map1, qitems_map2, queryItemValsCS);
128  if (ok)
129  compareflags |= KlfUrlCompareLessSpecific;
130  }
131  }
132  if (interestFlags & KlfUrlCompareMoreSpecific) {
133  // test url1 is more specific than url2 <-> url2 items are included in those of url1
134  if (u1 == u2) {
135  bool ok = klfMapIsIncludedIn(qitems_map2, qitems_map1, queryItemValsCS);
136  if (ok)
137  compareflags |= KlfUrlCompareMoreSpecific;
138  }
139  }
140 
141  if (interestFlags & KlfUrlCompareBaseEqual) {
142  if (u1 == u2)
143  compareflags |= KlfUrlCompareBaseEqual;
144  }
145 
146  klfDbg( "... and the result is compareflags="<<compareflags ) ;
147  return compareflags;
148 }
149 
150 
151 // ------------------------------------------------------------
152 
153 
154 
155 // ignores: flags: Recurse, Wrap. (!)
156 KLF_EXPORT bool klfMatch(const QVariant& testForHitCandidateValue, const QVariant& queryValue,
157  Qt::MatchFlags flags, const QString& queryStringCache /* = QString()*/)
158 {
159  //
160  // *** NOTE ***
161  // code inspired from Qt's QAbstractItemModel::match() defined in
162  // src/corelib/kernel/qabstractitemmodel.cpp
163  //
164 
165  uint matchType = flags & 0x0F;
166  Qt::CaseSensitivity cs = (flags & Qt::MatchCaseSensitive)
167  ? Qt::CaseSensitive
168  : Qt::CaseInsensitive;
169 
170  const QVariant& v = testForHitCandidateValue; // the name is a bit long :)
171 
172  // QVariant based matching
173  if (matchType == Qt::MatchExactly)
174  return (queryValue == v);
175 
176  // QString based matching
177  QString text = !queryStringCache.isNull() ? queryStringCache : queryValue.toString();
178  QString t = v.toString();
179  switch (matchType) {
180  case Qt::MatchRegExp:
181  return (QRegExp(text, cs).exactMatch(t));
182  case Qt::MatchWildcard:
183  return (QRegExp(text, cs, QRegExp::Wildcard).exactMatch(t));
184  case Qt::MatchStartsWith:
185  return (t.startsWith(text, cs));
186  case Qt::MatchEndsWith:
187  return (t.endsWith(text, cs));
188  case Qt::MatchFixedString:
189  return (QString::compare(t, text, cs) == 0);
190  case Qt::MatchContains:
191  default:
192  return (t.contains(text, cs));
193  }
194 }
195 
196 
197 // -----------------------------------------------------
198 
199 
200 
201 static inline bool klf_is_hex_char(char c)
202 {
203  return ('0' <= c && c <= '9') || ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F');
204 }
205 
206 
207 
208 #define KLF_BRUSH_STYLE(sty) \
209  { Qt::sty##Pattern, #sty }
210 
211 static struct { int brushStyle; const char *style; } klf_brush_styles[] = {
212  { Qt::NoBrush, "NoBrush" },
213  { Qt::SolidPattern, "" },
214  { Qt::SolidPattern, "Solid" },
215  KLF_BRUSH_STYLE(Dense1),
216  KLF_BRUSH_STYLE(Dense2),
217  KLF_BRUSH_STYLE(Dense3),
218  KLF_BRUSH_STYLE(Dense4),
219  KLF_BRUSH_STYLE(Dense5),
220  KLF_BRUSH_STYLE(Dense6),
221  KLF_BRUSH_STYLE(Dense7),
222  KLF_BRUSH_STYLE(Hor),
223  KLF_BRUSH_STYLE(Ver),
224  KLF_BRUSH_STYLE(Cross),
225  KLF_BRUSH_STYLE(BDiag),
226  KLF_BRUSH_STYLE(FDiag),
227  KLF_BRUSH_STYLE(DiagCross),
228  { -1, NULL }
229 };
230 
231 
232 
233 #define KLF_TEXT_FORMAT_FORMAT(fmt) \
234  { QTextFormat::fmt##Format, #fmt "Format" }
235 
236 static struct { int formatId; const char *format; } klf_text_format_formats[] = {
237  KLF_TEXT_FORMAT_FORMAT(Invalid),
238  KLF_TEXT_FORMAT_FORMAT(Block),
241  KLF_TEXT_FORMAT_FORMAT(Table),
242  KLF_TEXT_FORMAT_FORMAT(Frame),
244  { -100, NULL }
245 };
246 
247 
248 #define KLF_TEXT_FORMAT_PROP(p, type) \
249  { QTextFormat::p, #p, #type }
250 
251 static struct { int propId; const char *key; const char *type; } klf_text_format_props[] = {
252  KLF_TEXT_FORMAT_PROP(ForegroundBrush, QBrush),
253  KLF_TEXT_FORMAT_PROP(BackgroundBrush, QBrush),
254  KLF_TEXT_FORMAT_PROP(FontFamily, QString),
255  KLF_TEXT_FORMAT_PROP(FontPointSize, int),
256  KLF_TEXT_FORMAT_PROP(FontWeight, int),
257  KLF_TEXT_FORMAT_PROP(FontItalic, bool),
258  KLF_TEXT_FORMAT_PROP(TextUnderlineStyle, int),
259  // add more keys for short-hands
260  { QTextFormat::ForegroundBrush, "FG", "QBrush" },
261  { QTextFormat::BackgroundBrush, "BG", "QBrush" },
262 
263  { -1, NULL, NULL }
264 };
265 
266 static struct { const char * keyword; int propId; QVariant fixed_value; } klf_text_format_keywords[] = {
267  { "NORMALWEIGHT", QTextFormat::FontWeight, QVariant(QFont::Normal) },
268  { "BOLD", QTextFormat::FontWeight, QVariant(QFont::Bold) },
269  { "NORMALSTYLE", QTextFormat::FontItalic, QVariant(false) },
270  { "ITALIC", QTextFormat::FontItalic, QVariant(true) },
271 
272  { NULL, -1, QVariant() }
273 };
274 
275 
276 
277 
278 KLF_EXPORT QByteArray klfDataToEscaped(const QByteArray& value_ba)
279 {
280  qDebug("klfDataToEscaped: len=%d, data=`%s'", value_ba.size(), value_ba.constData());
281  QByteArray data;
282  int k;
283  for (k = 0; k < value_ba.size(); ++k) {
284  // qDebug("\tdata[%d] = %x = %c", k, (uchar)value_ba[k], value_ba[k]);
285  if (value_ba[k] >= 32 && value_ba[k] <= 126 && value_ba[k] != '\\') {
286  // ascii-ok values, not backslash
287  data += value_ba[k];
288  } else if (value_ba[k] == '\\') {
289  data += "\\\\";
290  } else {
291  data += QString("\\x%1").arg((uint)(uchar)value_ba[k], 2, 16, QChar('0')).toAscii();
292  }
293  }
294  return data;
295 }
296 
297 KLF_EXPORT QByteArray klfEscapedToData(const QByteArray& data)
298 {
299  bool convertOk;
300  int k;
301  QByteArray value_ba;
302  k = 0;
303  while (k < data.size()) {
304  if (data[k] != '\\') {
305  value_ba += data[k];
306  ++k;
307  continue;
308  }
309  if (data[k] == '\\' && k+1 >= data.size()) {
310  value_ba += '\\'; // backslash at end of data
311  ++k;
312  continue;
313  }
314  // not at end of data
315  if (data[k+1] != 'x') {
316  // backslash followed by something else than 'x', add that escaped 'something else'
317  value_ba += data[k+1];
318  k += 2; // had to skip the backslash
319  continue;
320  }
321  // pos k points on '\\', pos k+1 points on 'x'
322  if (k+3 >= data.size() || !klf_is_hex_char(data[k+2]) || !klf_is_hex_char(data[k+3])) {
323  // ignore invalid escape sequence
324  value_ba += data[k];
325  ++k;
326  continue;
327  }
328  // decode this char
329  uchar cval = data.mid(k+2, 2).toUInt(&convertOk, 16);
330  value_ba += (char)cval;
331  k += 4; // advance of backslash + 'x' + 2 digits
332  }
333  return value_ba;
334 }
335 
336 
338 {
339  QByteArray data = "[";
340  for (int k = 0; k < list.size(); ++k) {
341  QByteArray d = list[k];
342  d.replace("\\", "\\\\");
343  d.replace(";", "\\;");
344  d.replace("[", "\\[");
345  d.replace("]", "\\]");
346  data += d;
347  if (k < list.size()-1)
348  data += ";";
349  }
350  data += "]";
351  return data;
352 }
353 
354 // if 'ignore_empty_values' is TRUE, then the '=' sign is omitted with the value in a section is empty.
355 static QByteArray encaps_map(const QList<QPair<QByteArray,QByteArray> >& sections, bool ignore_empty_values = false)
356 {
357  QByteArray data;
358  data = "{";
359  bool first_item = true;
360  int k;
361  for (k = 0; k < sections.size(); ++k) {
362  if (!first_item) {
363  data += ";";
364  }
365  first_item = false;
366  QByteArray key = sections[k].first;
367  QByteArray val = sections[k].second;
368  // prepare the pair key=value
369  key.replace("\\", "\\\\");
370  key.replace(";", "\\;");
371  key.replace("=", "\\=");
372  val.replace("\\", "\\\\");
373  val.replace(";", "\\;");
374  if (val.isEmpty() && ignore_empty_values)
375  data += key;
376  else
377  data += key + "=" + val;
378  }
379  data += "}";
380  return data;
381 }
382 
383 
385 {
386  klfDbg("decaps_list, data="<<ba_data);
387  QByteArray data = ba_data.trimmed();
388  if (data[0] != '[')
389  return QList<QByteArray>();
390 
391  QList<QByteArray> sections;
392  QByteArray chunk;
393  // first, split data. take into account escaped chars.
394  // k=1 to skip '['
395  int k = 1;
396  while (k < data.size()) {
397  if (data[k] == ';') { // element separator
398  // flush chunk as a new section
399  sections.append(chunk);
400  // and start a new section
401  chunk = QByteArray();
402  ++k;
403  }
404  if (data[k] == '\\') {
405  if (k+1 < data.size()) { // there exists a next char
406  chunk += data[k+1];
407  k += 2;
408  } else {
409  chunk += data[k];
410  ++k;
411  }
412  continue;
413  }
414  if (data[k] == ']') {
415  // end of list marker.
416  // flush last chunk into sections, and break.
417  if (!chunk.isEmpty())
418  sections.append(chunk);
419  chunk = "";
420  break;
421  }
422  // regular char, populate current chunk.
423  chunk += data[k];
424  ++k;
425  }
426  if (!chunk.isEmpty()) {
427  // missing ']' at end, tolerate this by adding the unfinished chunk to sections
428  sections.append(chunk);
429  }
430 
431  klfDbg("sections="<<sections);
432 
433  return sections;
434 }
435 
436 static QList<QPair<QByteArray,QByteArray> > decaps_map(const QByteArray& ba_data, bool allow_empty_values = false)
437 {
438  QByteArray data = ba_data.trimmed();
439  if (data[0] != '{')
441  if ( !data.contains('}') )
442  data += '}';
443 
445  QByteArray chunkkey;
446  QByteArray chunkvalue;
447  QByteArray *curChunk = &chunkkey;
448  // first, split data. take into account escaped chars.
449  // k=1 to skip '{'
450  int k = 1;
451  while (k < data.size()) {
452  if (data[k] == ';') { // separator for next pair
453  // flush chunk as a new section
454  if (!allow_empty_values && curChunk == &chunkkey)
455  qWarning()<<KLF_FUNC_NAME<<": no '=' in pair at pos "<<k<<" in string: "<<data<<"";
456  sections << QPair<QByteArray,QByteArray>(chunkkey, chunkvalue);
457  // and start a new section
458  chunkkey = QByteArray();
459  chunkvalue = QByteArray();
460  curChunk = &chunkkey;
461  ++k;
462  }
463  if (data[k] == '\\') {
464  if (k+1 < data.size()) { // there exists a next char
465  *curChunk += data[k+1];
466  k += 2;
467  } else {
468  *curChunk += data[k];
469  ++k;
470  }
471  continue;
472  }
473  if (curChunk == &chunkkey && data[k] == '=') {
474  // currently reading key, switch to reading value
475  curChunk = &chunkvalue;
476  ++k;
477  continue;
478  }
479  if (data[k] == '}') {
480  // end of list marker.
481  // flush last chunk into sections, and break.
482  if (!allow_empty_values && curChunk == &chunkkey)
483  qWarning()<<"klfLoadVariantFromText: no '=' in pair at pos "<<k<<" in string: "<<data<<"";
484  sections << QPair<QByteArray,QByteArray>(chunkkey, chunkvalue);
485  break;
486  }
487  // regular char, populate current chunk.
488  *curChunk += data[k];
489  ++k;
490  }
491  return sections;
492 }
493 
494 
495 
496 // returns root node. get the document with root.ownerDocument()
497 static QDomElement make_xml_wrapper(const QString& rootname)
498 {
499  QDomDocument xmldoc(rootname);
500  QDomElement root = xmldoc.createElement(rootname);
501  xmldoc.appendChild(root);
502  return root;
503 }
504 
505 static QDomElement parse_xml_wrapper(const QByteArray& xmldata, const QString& shouldBeRootName)
506 {
507  QDomDocument xmldoc(shouldBeRootName);
508  bool result = xmldoc.setContent(xmldata);
509  KLF_ASSERT_CONDITION(result, "Failed to read wrapper XML for klfLoadVariantFromText()",
510  return QDomElement() ) ;
511 
512  QDomElement el = xmldoc.documentElement();
513  KLF_ASSERT_CONDITION( el.nodeName() == shouldBeRootName,
514  "Wrong XML root node in wrapper for klfLoadVariantFromText(): "
515  <<el.nodeName() , ) ;
516  return el;
517 }
518 
519 KLF_EXPORT QByteArray klfSaveVariantToText(const QVariant& value, bool saveListAndMapsAsXML)
520 {
522 
523  QString s;
524  QByteArray data;
525  int k;
526 
527  if (!value.isValid() || value.isNull())
528  return QByteArray();
529 
530  // values of value.type() are QMetaType::Type enum entries. See qt's doc.
531  switch ((int)value.type()) {
532  case QMetaType::Bool:
533  data = value.toBool() ? "true" : "false";
534  break;
535  case QMetaType::Int:
536  case QMetaType::UInt:
537  case QMetaType::Short:
538  case QMetaType::UShort:
539  case QMetaType::Long:
540  case QMetaType::ULong:
541  case QMetaType::LongLong:
542  case QMetaType::ULongLong:
543  case QMetaType::Double:
544  data = value.toString().toLocal8Bit();
545  break;
546  case QMetaType::Char:
547  {
548  char c = value.value<char>();
549  if (c >= 32 && c <= 126 && c != '\\')
550  data = QByteArray(1, c);
551  else if (c == '\\')
552  data = "\\\\";
553  else
554  data = "\\" + QString::number(c, 16).toUpper().toAscii();
555  }
556  case QMetaType::QChar:
557  {
558  QChar c = value.toChar();
559  if (tc->canEncode(c) && c != '\\')
560  data = tc->fromUnicode(QString(c));
561  else if (c == '\\')
562  data = "\\\\";
563  else
564  data = "\\" + QString::number(c.unicode(), 16).toUpper().toAscii();
565  break;
566  }
567  case QMetaType::QString:
568  {
569  s = value.toString();
570  if (tc->canEncode(s)) {
571  // replace any `\' by `\\' (ie. escape backslashes)
572  data = tc->fromUnicode(s.replace("\\", "\\\\"));
573  } else {
574  // encode char by char, escaping as needed
575  data = QByteArray("");
576  for (k = 0; k < s.length(); ++k) {
577  if (tc->canEncode(s[k]))
578  data += tc->fromUnicode(s.mid(k,1));
579  else
580  data += QString("\\x%1").arg((uint)s[k].unicode(), 4, 16, QChar('0')).toAscii();
581  }
582  }
583  break;
584  }
585  case QMetaType::QStringList:
586  {
587  const QStringList list = value.toStringList();
588  QList<QByteArray> sections;
589  int k;
590  for (k = 0; k < list.size(); ++k) {
591  sections.append(klfDataToEscaped(list[k].toUtf8()));
592  }
593  data = encaps_list(sections);
594  break;
595  }
596  case QMetaType::QUrl:
597  data = value.toUrl().toEncoded(); break;
598  case QMetaType::QByteArray:
599  {
600  data = klfDataToEscaped(value.value<QByteArray>());
601  break;
602  }
603  case QMetaType::QDate:
604  data = value.value<QDate>().toString(Qt::SystemLocaleShortDate).toLocal8Bit(); break;
605  case QMetaType::QTime:
606  data = value.value<QTime>().toString(Qt::SystemLocaleShortDate).toLocal8Bit(); break;
607  case QMetaType::QDateTime:
608  data = value.value<QDateTime>().toString(Qt::SystemLocaleShortDate).toLocal8Bit(); break;
609  case QMetaType::QSize:
610  { QSize sz = value.toSize();
611  data = QString("(%1 %2)").arg(sz.width()).arg(sz.height()).toAscii();
612  break;
613  }
614  case QMetaType::QPoint:
615  { QPoint pt = value.toPoint();
616  data = QString("(%1 %2)").arg(pt.x()).arg(pt.y()).toAscii();
617  break;
618  }
619  case QMetaType::QRect:
620  { QRect r = value.toRect();
621  data = QString("(%1 %2 %3x%4)").arg(r.left()).arg(r.top()).arg(r.width()).arg(r.height()).toAscii();
622  break;
623  }
624  case QMetaType::QColor:
625  { QColor c = value.value<QColor>();
626  klfDbg("Saving color "<<c<<": alpha="<<c.alpha()) ;
627  if (c.alpha() == 255)
628  data = QString("(%1 %2 %3)").arg(c.red()).arg(c.green()).arg(c.blue()).toAscii();
629  else
630  data = QString("(%1 %2 %3 %4)").arg(c.red()).arg(c.green()).arg(c.blue()).arg(c.alpha()).toAscii();
631  break;
632  }
633  case QMetaType::QFont:
634  { QFont f = value.value<QFont>();
635  data = "'" + f.family().toLocal8Bit() + "'";
636  switch (f.weight()) {
637  case QFont::Light: data += " Light"; break;
638  case QFont::Normal: break; //data += " Normal"; break;
639  case QFont::DemiBold: data += " DemiBold"; break;
640  case QFont::Bold: data += " Bold"; break;
641  case QFont::Black: data += " Black"; break;
642  default: data += QString(" Wgt=%1").arg(f.weight()); break;
643  }
644  switch (f.style()) {
645  case QFont::StyleNormal: break; //data += " Normal"; break;
646  case QFont::StyleItalic: data += " Italic"; break;
647  case QFont::StyleOblique: data += " Oblique"; break;
648  default: break;
649  }
650  // QFontInfo is preferred, if f was set with a pixelSize().
651  data += " " + QString::number(QFontInfo(f).pointSize()).toAscii();
652  break;
653  }
654  case QMetaType::QBrush:
655  { QBrush b = value.value<QBrush>();
656  if (!b.matrix().isIdentity())
657  break; // forget about saving complex brushes here
658  int bstyle = b.style();
659  // find index in our brush style enum
660  int k;
661  bool found_style = false;
662  for (k = 0; klf_brush_styles[k].brushStyle >= 0 && klf_brush_styles[k].style != NULL; ++k) {
663  if (klf_brush_styles[k].brushStyle == bstyle) {
664  found_style = true;
665  break;
666  }
667  }
668  if (!found_style) {
669  // didn't find this style, this is a complex brush. Need to save it via a datastream.
670  break;
671  }
672  // found brush style. This is a simple brush with just a style and a color.
673  data = "(";
674  data += klf_brush_styles[k].style;
675  if (strlen(klf_brush_styles[k].style))
676  data += " ";
677  QColor c = b.color();
678  data += QString("%1 %2 %3 %4").arg(c.red()).arg(c.green()).arg(c.blue()).arg(c.alpha());
679  data += ")";
680  break;
681  }
682  case QMetaType::QTextFormat:
683  {
684  QTextFormat tf = value.value<QTextFormat>();
685  const QMap<int,QVariant> props = tf.properties();
686 
688 
689  // first find the QTextFormat type.
690  int k;
691  for (k = 0; klf_text_format_formats[k].format != NULL; ++k)
692  if (klf_text_format_formats[k].formatId == tf.type())
693  break;
694  if (klf_text_format_formats[k].format == NULL) {
695  // didn't find format, something is bound to go wrong, so fall back
696  // on Qt's datastream saving.
697  data = QByteArray();
698  break;
699  }
700  // found format. This will be the first (value-less) section.
701  sections << QPair<QByteArray,QByteArray>(klf_text_format_formats[k].format, QByteArray());
702 
704  for (it = props.begin(); it != props.end(); ++it) {
705  int propId = it.key();
706  QVariant propValue = it.value();
707  // Add data for this property.
708 
709  // first look to see if a keyword is already known to be available
710  for (k = 0; klf_text_format_keywords[k].keyword != NULL; ++k)
711  if (klf_text_format_keywords[k].propId == propId &&
712  klf_text_format_keywords[k].fixed_value == propValue)
713  break;
714  const char *kw = klf_text_format_keywords[k].keyword;
715  if (kw != NULL) {
716  // found a keyword for this property-value pair
717  QByteArray key = kw;
718  sections << QPair<QByteArray,QByteArray>(kw, QByteArray());
719  continue;
720  }
721 
722  // now look to see if we can name the property
723  for (k = 0; klf_text_format_props[k].key != NULL; ++k)
724  if (klf_text_format_props[k].propId == propId)
725  break;
726  if (klf_text_format_props[k].key != NULL) {
727  // make sure the variant has the advertised type
728  if ( !strcmp(klf_text_format_props[k].type, propValue.typeName()) ) {
729  // found the property in our list of common properties
731  QByteArray value = klfSaveVariantToText(propValue, true); // resort to XML for lists/maps...
732  sections << QPair<QByteArray,QByteArray>(key, value);
733  continue;
734  } else {
735  qWarning()<<KLF_FUNC_NAME<<": QTextFormat property "<<klf_text_format_props[k].key
736  <<" 's type is `"<<propValue.typeName()<<"' which is not the known type: "
737  <<klf_text_format_props[k].type;
738  }
739  }
740 
741  // this property is unknown to us. store it as we can.
742  QByteArray key = QString::number(propId).toLatin1();
743  QByteArray value;
744  value = QByteArray("[")+propValue.typeName()+"]"+klfSaveVariantToText(propValue, true);
745  }
746  data = encaps_map(sections, true);
747  break;
748  }
749  case QMetaType::QVariantList:
750  {
751  const QList<QVariant>& list = value.toList();
752  if (saveListAndMapsAsXML) {
753  QDomElement el = make_xml_wrapper("variant-list");
754  el = klfSaveVariantListToXML(list, el);
755  data = el.ownerDocument().toByteArray(0);
756  } else {
757  QList<QByteArray> sections;
758  for (k = 0; k < list.size(); ++k) {
759  sections << klfSaveVariantToText(list[k]);
760  }
761  data = encaps_list(sections);
762  }
763  break;
764  }
765  case QMetaType::QVariantMap:
766  {
767  const QMap<QString,QVariant>& map = value.toMap();
768  if (saveListAndMapsAsXML) {
769  QDomElement el = make_xml_wrapper("variant-map");
770  el = klfSaveVariantMapToXML(map, el);
771  data = el.ownerDocument().toByteArray(0);
772  } else {
774  for (QMap<QString,QVariant>::const_iterator it = map.begin(); it != map.end(); ++it) {
776  QByteArray v = klfSaveVariantToText(it.value());
777  sections << QPair<QByteArray,QByteArray>(k, v);
778  }
779  data = encaps_map(sections);
780  }
781  break;
782  }
783  default:
784  break;
785  };
786 
787  // -- some other types --
788 
789  QByteArray typeName = value.typeName();
790 
791  if (typeName == "KLFStyle") {
792  KLFStyle style = value.value<KLFStyle>();
793  QVariantMap map;
794  map["name"] = klfSaveVariantToText(style.name);
795  map["fg_color"] = klfSaveVariantToText(QColor(style.fg_color));
796  map["bg_color"] = klfSaveVariantToText(QColor(style.bg_color));
797  map["mathmode"] = klfSaveVariantToText(style.mathmode);
798  map["preamble"] = klfSaveVariantToText(style.preamble);
799  map["dpi"] = klfSaveVariantToText(QVariant::fromValue(style.dpi));
800  // now, save the map itself.
801  // use 'return', not 'data = ', because this call to klfSaveVariantToText() is already "finalizing"
802  return klfSaveVariantToText(map);
803  }
804 
805  // protect data from some special sequences
806 
807  if (data.startsWith("[QVariant]") || data.startsWith("\\")) // protect this special sequence
808  data = "\\"+data;
809 
810  // and provide a default encoding scheme in case no one up to now was able to
811  // format the data (this format is only machine-readable ...)
812 
813  if (data.isNull()) {
814  QByteArray vdata;
815  {
816  QDataStream stream(&vdata, QIODevice::WriteOnly);
817  stream << value;
818  }
819  QByteArray vdata_esc = klfDataToEscaped(vdata);
820  qDebug("\tVariant value is %s, len=%d", vdata.constData(), vdata.size());
821  data = QByteArray("[QVariant]");
822  data += vdata_esc;
823  }
824 
825  klfDbg( "klfSaveVariantToText("<<value<<"): saved data (len="<<data.size()<<") : "<<data ) ;
826  return data;
827 }
828 
829 
830 
831 
832 KLF_EXPORT QVariant klfLoadVariantFromText(const QByteArray& stringdata, const char * dataTypeName,
833  const char *listOrMapDataTypeName)
834 {
836 
837  // SOME REGULAR EXPRESSIONS
838 
839 #define RX_INT "-?\\d+"
840 #define RX_COORD_SEP "\\s*(?:[,;]|\\s)\\s*" // note: non-capturing parenthesis
841 #define RX_SIZE_SEP "\\s*(?:[,;x]|\\s)\\s*" // note: non-capturing parenthesis
842 
843  // 1 2
844  QRegExp v2rx("^\\(?\\s*(" RX_INT ")" RX_COORD_SEP "(" RX_INT ")\\s*\\)?");
845  static const int V2RX_X = 1, V2RX_Y = 2;
846 
847  // 1 2
848  QRegExp szrx("^\\(?\\s*(" RX_INT ")" RX_SIZE_SEP "(" RX_INT ")\\s*\\)?");
849  static const int SZRX_W = 1, SZRX_H = 2;
850 
851  // 1 2
852  QRegExp rectrx("^\\(?\\s*(" RX_INT ")" RX_COORD_SEP "(" RX_INT ")"
853  // 3
854  "(?:" RX_COORD_SEP "|\\s*([+])\\s*)"
855  //4 5 6
856  "(" RX_INT ")(?:"RX_COORD_SEP"|\\s*([x])\\s*)(" RX_INT ")\\s*\\)?");
857  static const int RECTRX_X1 = 1, RECTRX_Y1 = 2, RECTRX_MIDDLESEP_PLUS = 3,
858  RECTRX_X2orW = 4, RECTRX_LASTSEP_X = 5, RECTRX_Y2orH = 6;
859 
860  // 1 2 3
861  QRegExp colrx("^(?:rgba?)?\\(?\\s*(\\d+)" RX_COORD_SEP "(\\d+)" RX_COORD_SEP "(\\d+)"
862  //4 5
863  "(" RX_COORD_SEP "(\\d+))?\\s*\\)?", Qt::CaseInsensitive);
864  static const int COLRX_R = 1, COLRX_G = 2, COLRX_B = 3, COLRX_MAYBE_ALPHA = 4, COLRX_A = 5;
865 
866  // 1 2 3
867  QRegExp brushrx("^(?:q?brush)?\\(?\\s*(?:([A-Za-z_]\\w*)" RX_COORD_SEP ")?(\\d+)" RX_COORD_SEP "(\\d+)"
868  // 4 5 6
869  RX_COORD_SEP "(\\d+)" "("RX_COORD_SEP "(\\d+))?" "\\s*\\)?", Qt::CaseInsensitive);
870  static const int BRUSHRX_STYLE = 1, BRUSHRX_R = 2, BRUSHRX_G = 3, BRUSHRX_B = 4, BRUSHRX_A = 6;
871 
872  // 1 2
873  QRegExp fontrx("^([\"']?)\\s*(.+)\\s*\\1"
874  //3 4 5
875  "(\\s+(Light|Normal|DemiBold|Bold|Black|Wgt\\s*=\\s*(\\d+)))?"
876  //6 7 8 9
877  "(\\s+(Normal|Italic|Oblique))?(\\s+(\\d+))?$");
878  fontrx.setMinimal(true); // don't match Light|Normal|DemiBold|... etc as part of font name
879  static const int FONTRX_FAMILY = 2, FONTRX_WEIGHT_TEXT = 4, FONTRX_WEIGHT_VALUE = 5,
880  FONTRX_STYLE_TEXT = 7, FONTRX_POINTSIZE = 9;
881 
882 
883  // START DECODING TEXT
884 
885  QByteArray data = stringdata; // might need slight modifications before parsing
886 
887  QVariant value;
888  if (data.startsWith("[QVariant]")) {
889  QByteArray vdata_esc = data.mid(strlen("[QVariant]"));
890  QByteArray vdata = klfEscapedToData(vdata_esc);
891  klfDbg( "\tAbout to read raw variant from datastr="<<vdata_esc<<", ie. from data len="<<vdata.size() ) ;
892  QDataStream stream(vdata);
893  stream >> value;
894  return value;
895  }
896  if (data.startsWith("\\"))
897  data = data.mid(1);
898 
899  klfDbg( "Will start loading a `"<<dataTypeName<<"' from data (len="<<data.size()<<") : "<<data ) ;
900 
901  // now, start reading.
902  int type = QVariant::nameToType(dataTypeName);
903  bool convertOk = false; // in case we break; somewhere, it's (by default) because of failed convertion.
904  int k;
905  switch (type) {
906  case QMetaType::Bool:
907  {
908  QByteArray lowerdata = data.trimmed().toLower();
909  QChar c = QChar(lowerdata[0]);
910  // true, yes, on, 1
911  return QVariant::fromValue<bool>(c == 't' || c == 'y' || c == '1' || lowerdata == "on");
912  }
913  case QMetaType::Int:
914  {
915  int i = data.toInt(&convertOk);
916  if (convertOk)
917  return QVariant::fromValue<int>(i);
918  break;
919  }
920  case QMetaType::UInt:
921  {
922  uint i = data.toUInt(&convertOk);
923  if (convertOk)
924  return QVariant::fromValue<uint>(i);
925  break;
926  }
927  case QMetaType::Short:
928  {
929  short i = data.toShort(&convertOk);
930  if (convertOk)
931  return QVariant::fromValue<short>(i);
932  break;
933  }
934  case QMetaType::UShort:
935  {
936  ushort i = data.toUShort(&convertOk);
937  if (convertOk)
938  return QVariant::fromValue<ushort>(i);
939  break;
940  }
941  case QMetaType::Long:
942  {
943  long i = data.toLong(&convertOk);
944  if (convertOk)
945  return QVariant::fromValue<long>(i);
946  break;
947  }
948  case QMetaType::ULong:
949  {
950  ulong i = data.toULong(&convertOk);
951  if (convertOk)
952  return QVariant::fromValue<ulong>(i);
953  break;
954  }
955  case QMetaType::LongLong:
956  {
957  qlonglong i = data.toLongLong(&convertOk);
958  if (convertOk)
959  return QVariant::fromValue<qlonglong>(i);
960  break;
961  }
962  case QMetaType::ULongLong:
963  {
964  qulonglong i = data.toULongLong(&convertOk);
965  if (convertOk)
966  return QVariant::fromValue<qulonglong>(i);
967  break;
968  }
969  case QMetaType::Double:
970  {
971  double val = data.toDouble(&convertOk);
972  if (convertOk)
973  return QVariant::fromValue<double>(val);
974  break;
975  }
976  case QMetaType::Char:
977  {
978  if (data[0] == '\\') {
979  if (data.size() < 2)
980  break;
981  if (data[1] == '\\')
982  return QVariant::fromValue<char>('\\');
983  if (data.size() < 3)
984  break;
985  uint c = data.mid(1).toUInt(&convertOk, 16);
986  if (!convertOk)
987  break;
988  convertOk = false; // reset by default convertOk to false
989  if (c > 255)
990  break;
991  return QVariant::fromValue<char>( (char)c );
992  }
993  return QVariant::fromValue<char>( (char)data[0] );
994  }
995  case QMetaType::QChar:
996  {
997  if (data[0] == '\\') {
998  if (data.size() < 2)
999  break;
1000  if (data[1] == '\\')
1001  return QVariant::fromValue<QChar>(QChar('\\'));
1002  if (data.size() < 3)
1003  break;
1004  uint c = data.mid(1).toUInt(&convertOk, 16);
1005  if (!convertOk)
1006  break;
1007  convertOk = false; // reset by default convertOk to false
1008  if (c > 255)
1009  break;
1010  return QVariant::fromValue<QChar>( QChar(c) );
1011  }
1012  return QVariant::fromValue<QChar>( QChar(data[0]) );
1013  }
1014  case QMetaType::QString:
1015  {
1016  QString s;
1017  QByteArray chunk;
1018  k = 0;
1019  while (k < data.size()) {
1020  if (data[k] != '\\') {
1021  chunk += data[k];
1022  ++k;
1023  continue;
1024  }
1025  if (data[k] == '\\' && k+1 >= data.size()) {
1026  chunk += '\\'; // backslash at end of data
1027  ++k;
1028  continue;
1029  }
1030  // not at end of data
1031  if (data[k+1] != 'x') {
1032  // backslash followed by something else than 'x', add that escaped 'something else'
1033  chunk += data[k+1];
1034  k += 2; // had to skip the backslash
1035  continue;
1036  }
1037  // pos k points on '\\', pos k+1 points on 'x'
1038  int nlen = -1;
1039  if (k+5 < data.size() && klf_is_hex_char(data[k+2]) && klf_is_hex_char(data[k+3])
1040  && klf_is_hex_char(data[k+4]) && klf_is_hex_char(data[k+5]))
1041  nlen = 4; // 4-digit Unicode char
1042  if (k+3 < data.size() && klf_is_hex_char(data[k+2]) && klf_is_hex_char(data[k+3]))
1043  nlen = 2; // 2 last digits of 4-digit unicode char
1044  if (nlen < 0) {
1045  // bad format, ignore the escape sequence.
1046  chunk += data[k];
1047  ++k;
1048  continue;
1049  }
1050  // decode this char
1051  ushort cval = data.mid(k+2, nlen).toUShort(&convertOk, 16);
1052  QChar ch(cval);
1053  // dump chunk into string, and add this char
1054  s += QString::fromLocal8Bit(chunk) + ch;
1055  // reset chunk
1056  chunk = QByteArray();
1057  // and advance the corresponding number of characters, point on fresh one
1058  // advance of what we read: backslash+'x' (=2) + number of digits
1059  k += 2 + nlen;
1060  }
1061  // dump remaining chunk
1062  s += QString::fromLocal8Bit(chunk);
1063  return QVariant::fromValue<QString>(s);
1064  }
1065  case QMetaType::QStringList:
1066  {
1067  QList<QByteArray> sections = decaps_list(data);
1068 
1069  // now we separated into bytearray sections. now read those into values.
1070  QStringList list;
1071  for (k = 0; k < sections.size(); ++k) {
1072  list << QString::fromUtf8(klfEscapedToData(sections[k]));
1073  }
1074 
1075  return QVariant::fromValue<QStringList>(list);
1076  }
1077  case QMetaType::QUrl:
1078  return QVariant::fromValue<QUrl>(QUrl(QString::fromLocal8Bit(data), QUrl::TolerantMode));
1079  case QMetaType::QByteArray:
1080  {
1081  QByteArray value_ba = klfEscapedToData(data);
1082  return QVariant::fromValue<QByteArray>(value_ba);
1083  }
1084  case QMetaType::QDate:
1085  {
1086  QString s = QString::fromLocal8Bit(data);
1087  QDate date = QDate::fromString(s, Qt::SystemLocaleShortDate);
1088  if (!date.isValid()) date = QDate::fromString(s, Qt::ISODate);
1089  if (!date.isValid()) date = QDate::fromString(s, Qt::SystemLocaleLongDate);
1090  if (!date.isValid()) date = QDate::fromString(s, Qt::DefaultLocaleShortDate);
1091  if (!date.isValid()) date = QDate::fromString(s, Qt::TextDate);
1092  if (!date.isValid()) date = QDate::fromString(s, "dd-MM-yyyy");
1093  if (!date.isValid()) date = QDate::fromString(s, "dd.MM.yyyy");
1094  if (!date.isValid()) date = QDate::fromString(s, "dd MM yyyy");
1095  if (!date.isValid()) date = QDate::fromString(s, "yyyy-MM-dd");
1096  if (!date.isValid()) date = QDate::fromString(s, "yyyy.MM.dd");
1097  if (!date.isValid()) date = QDate::fromString(s, "yyyy MM dd");
1098  if (!date.isValid()) date = QDate::fromString(s, "yyyyMMdd");
1099  if (!date.isValid())
1100  break;
1101  return QVariant::fromValue<QDate>(date);
1102  }
1103  case QMetaType::QTime:
1104  {
1105  QString s = QString::fromLocal8Bit(data);
1106  QTime time = QTime::fromString(s, Qt::SystemLocaleShortDate);
1107  if (!time.isValid()) time = QTime::fromString(s, Qt::ISODate);
1108  if (!time.isValid()) time = QTime::fromString(s, Qt::SystemLocaleLongDate);
1109  if (!time.isValid()) time = QTime::fromString(s, Qt::DefaultLocaleShortDate);
1110  if (!time.isValid()) time = QTime::fromString(s, Qt::TextDate);
1111  if (!time.isValid()) time = QTime::fromString(s, "hh:mm:ss.z");
1112  if (!time.isValid()) time = QTime::fromString(s, "hh:mm:ss");
1113  if (!time.isValid()) time = QTime::fromString(s, "hh:mm:ss AP");
1114  if (!time.isValid()) time = QTime::fromString(s, "hh.mm.ss");
1115  if (!time.isValid()) time = QTime::fromString(s, "hh.mm.ss AP");
1116  if (!time.isValid()) time = QTime::fromString(s, "hh mm ss");
1117  if (!time.isValid()) time = QTime::fromString(s, "hh mm ss AP");
1118  if (!time.isValid()) time = QTime::fromString(s, "hhmmss");
1119  if (!time.isValid())
1120  break;
1121  return QVariant::fromValue<QTime>(time);
1122  }
1123  case QMetaType::QDateTime:
1124  {
1125  QString s = QString::fromLocal8Bit(data);
1126  QDateTime dt = QDateTime::fromString(s, Qt::SystemLocaleShortDate);
1127  if (!dt.isValid()) dt = QDateTime::fromString(s, Qt::ISODate);
1128  if (!dt.isValid()) dt = QDateTime::fromString(s, Qt::SystemLocaleLongDate);
1129  if (!dt.isValid()) dt = QDateTime::fromString(s, Qt::DefaultLocaleShortDate);
1130  if (!dt.isValid()) dt = QDateTime::fromString(s, Qt::TextDate);
1131  if (!dt.isValid()) dt = QDateTime::fromString(s, "dd-MM-yyyy hh:mm:ss");
1132  if (!dt.isValid()) dt = QDateTime::fromString(s, "dd-MM-yyyy hh.mm.ss");
1133  if (!dt.isValid()) dt = QDateTime::fromString(s, "dd.MM.yyyy hh:mm:ss");
1134  if (!dt.isValid()) dt = QDateTime::fromString(s, "dd.MM.yyyy hh.mm.ss");
1135  if (!dt.isValid()) dt = QDateTime::fromString(s, "dd MM yyyy hh mm ss");
1136  if (!dt.isValid()) dt = QDateTime::fromString(s, "yyyy-MM-dd hh:mm:ss");
1137  if (!dt.isValid()) dt = QDateTime::fromString(s, "yyyy-MM-dd hh.mm.ss");
1138  if (!dt.isValid()) dt = QDateTime::fromString(s, "yyyy.MM.dd hh:mm:ss");
1139  if (!dt.isValid()) dt = QDateTime::fromString(s, "yyyy.MM.dd hh.mm.ss");
1140  if (!dt.isValid()) dt = QDateTime::fromString(s, "yyyy MM dd hh mm ss");
1141  if (!dt.isValid()) dt = QDateTime::fromString(s, "yyyyMMddhhmmss");
1142  if (!dt.isValid())
1143  break;
1144  return QVariant::fromValue<QDateTime>(dt);
1145  }
1146  case QMetaType::QSize:
1147  {
1149  if (szrx.indexIn(s) < 0)
1150  break;
1151  QStringList vals = szrx.capturedTexts();
1152  return QVariant::fromValue<QSize>(QSize(vals[SZRX_W].toInt(), vals[SZRX_H].toInt()));
1153  }
1154  case QMetaType::QPoint:
1155  {
1157  if (v2rx.indexIn(s) < 0)
1158  break;
1159  QStringList vals = v2rx.capturedTexts();
1160  return QVariant::fromValue<QPoint>(QPoint(vals[V2RX_X].toInt(), vals[V2RX_Y].toInt()));
1161  }
1162  case QMetaType::QRect:
1163  {
1165  if (rectrx.indexIn(s) < 0)
1166  break;
1167  QStringList vals = rectrx.capturedTexts();
1168  if (vals[RECTRX_MIDDLESEP_PLUS] == "+" || vals[RECTRX_LASTSEP_X] == "x") {
1169  return QVariant::fromValue<QRect>(QRect( QPoint(vals[RECTRX_X1].toInt(), vals[RECTRX_Y1].toInt()),
1170  QSize(vals[RECTRX_X2orW].toInt(), vals[RECTRX_Y2orH].toInt()) ));
1171  }
1172  return QVariant::fromValue<QRect>(QRect( QPoint(vals[RECTRX_X1].toInt(), vals[RECTRX_Y1].toInt()),
1173  QPoint(vals[RECTRX_X2orW].toInt(), vals[RECTRX_Y2orH].toInt()) ));
1174  }
1175  case QMetaType::QColor:
1176  {
1177  QString colstr = QString::fromLocal8Bit(data.trimmed());
1178  // try our regexp
1179  if (colrx.indexIn(colstr) < 0) {
1180  klfDbg("color "<<colstr<<" does not match regexp="<<colrx.pattern()<<", trying named...") ;
1181  // try a named color
1182  QColor color; color.setNamedColor(colstr);
1183  // if we got a valid color, yepee
1184  if (color.isValid())
1185  return color;
1186  break;
1187  }
1188  // our regexp matched
1189  QStringList vals = colrx.capturedTexts();
1190  QColor color = QColor(vals[COLRX_R].toInt(), vals[COLRX_G].toInt(), vals[COLRX_B].toInt(), 255);
1191  if (!vals[COLRX_MAYBE_ALPHA].isEmpty())
1192  color.setAlpha(vals[COLRX_A].toInt());
1193  return QVariant::fromValue<QColor>(color);
1194  }
1195  case QMetaType::QFont:
1196  {
1197  if (fontrx.indexIn(QString::fromLocal8Bit(data.trimmed())) < 0) {
1198  klfDbg("malformed font: "<<data);
1199  break;
1200  }
1201  QStringList vals = fontrx.capturedTexts();
1202  klfDbg("parsing font: data="<<data<<"; captured texts are: "<<vals );
1203 
1204  QString family = vals[FONTRX_FAMILY].trimmed();
1205  QString weighttxt = vals[FONTRX_WEIGHT_TEXT];
1206  QString weightval = vals[FONTRX_WEIGHT_VALUE];
1207  QString styletxt = vals[FONTRX_STYLE_TEXT];
1208  QString ptsval = vals[FONTRX_POINTSIZE];
1209 
1210  int weight = QFont::Normal;
1211  if (weighttxt == "Light") weight = QFont::Light;
1212  else if (weighttxt == "Normal") weight = QFont::Normal;
1213  else if (weighttxt == "DemiBold") weight = QFont::DemiBold;
1214  else if (weighttxt == "Bold") weight = QFont::Bold;
1215  else if (weighttxt == "Black") weight = QFont::Black;
1216  else if (weighttxt.startsWith("Wgt")) weight = weightval.toInt();
1217 
1218 
1219  QFont::Style style = QFont::StyleNormal;
1220  if (styletxt == "Normal") style = QFont::StyleNormal;
1221  else if (styletxt == "Italic") style = QFont::StyleItalic;
1222  else if (styletxt == "Oblique") style = QFont::StyleOblique;
1223 
1224  int pt = -1;
1225  if (!ptsval.isEmpty())
1226  pt = ptsval.toInt();
1227 
1228  QFont font(family, pt, weight);
1229  font.setStyle(style);
1230  return QVariant::fromValue<QFont>(font);
1231  }
1232  case QMetaType::QBrush:
1233  {
1234  if (brushrx.indexIn(QString::fromLocal8Bit(data.trimmed())) < 0) {
1235  klfDbg("malformed brush text: "<<data) ;
1236  break;
1237  }
1238  QStringList vals = brushrx.capturedTexts();
1239  QString style = vals[BRUSHRX_STYLE];
1240  // find brush style
1241  int k;
1242  bool style_found = false;
1243  for (k = 0; klf_brush_styles[k].brushStyle >= 0 && klf_brush_styles[k].style != NULL; ++k) {
1244  if (klf_brush_styles[k].style == style) {
1245  style_found = true;
1246  break;
1247  }
1248  }
1249  if (!style_found) {
1250  klfDbg("Can't find style"<<style<<" in brush style list!");
1251  break;
1252  }
1253  int qbrush_style = klf_brush_styles[k].brushStyle;
1254  // read the color and construct QBrush.
1255  QColor c = QColor(vals[BRUSHRX_R].toInt(), vals[BRUSHRX_G].toInt(),
1256  vals[BRUSHRX_B].toInt());
1257  if (!vals[BRUSHRX_A].isEmpty())
1258  c.setAlpha(vals[BRUSHRX_A].toInt());
1259  return QBrush(c, static_cast<Qt::BrushStyle>(qbrush_style));
1260  }
1261  case QMetaType::QTextFormat:
1262  {
1263  int k;
1264  QList<QPair<QByteArray,QByteArray> > sections = decaps_map(data, true);
1265  if (sections.isEmpty()) {
1266  klfDbg("Invalid QTextFormat data.") ;
1267  break;
1268  }
1269  QPair<QByteArray,QByteArray> firstSection = sections.takeFirst();
1270  QString fmttype = QString::fromLatin1(firstSection.first);
1271  // find the format in our list
1272  for (k = 0; klf_text_format_formats[k].format != NULL; ++k)
1274  Qt::CaseInsensitive) == 0)
1275  break;
1276  if (klf_text_format_formats[k].format == NULL) {
1277  klfDbg("QTextFormat: Invalid format type: "<<fmttype) ;
1278  break;
1279  }
1280  int qtextformat_type = klf_text_format_formats[k].formatId;
1281 
1282  // now decode the list of properties
1283  QTextFormat textformat(qtextformat_type);
1284  QList<QPair<QByteArray,QByteArray> >::const_iterator it;
1285  for (it = sections.begin(); it != sections.end(); ++it) {
1286  QByteArray key = (*it).first.trimmed();
1287  QByteArray value = (*it).second;
1288  klfDbg("QTextFormat: considering property pair key="<<key<<"; value="<<value) ;
1289  // see if the key is a keyword
1290  for (k = 0; klf_text_format_keywords[k].keyword != NULL; ++k)
1292  key, Qt::CaseInsensitive) == 0)
1293  break;
1294  if (klf_text_format_keywords[k].keyword != NULL) {
1295  // this is a keyword.
1296  klfDbg("QTextFormat: is keyword, propId="<<klf_text_format_keywords[k].propId<<", fixed_value="
1299  klf_text_format_keywords[k].fixed_value);
1300  continue;
1301  }
1302  // see if the key is a known property name
1303  for (k = 0; klf_text_format_props[k].key != NULL; ++k)
1305  key, Qt::CaseInsensitive) == 0)
1306  break;
1307  if (klf_text_format_props[k].key != NULL) {
1308  klfDbg("QTextFormat: is known property of type "<<klf_text_format_props[k].type) ;
1309  // load property propId, of type type
1310  QVariant vval = klfLoadVariantFromText(value, klf_text_format_props[k].type, "XML");
1311  textformat.setProperty(klf_text_format_props[k].propId, vval);
1312  continue;
1313  }
1314  // load generally-saved qvariant property
1315 
1316  bool tointok = true;
1317  int propid = key.toInt(&tointok);
1318  if (!tointok) {
1319  qWarning()<<KLF_FUNC_NAME<<": QTextFormat bad format for general property key=value pair; "
1320  <<"key is not a numerical property ID, nor is it a known property name.";
1321  }
1322 
1323  klfDbg("QTextFormat: property is not a known one. propid="<<propid) ;
1324 
1325  // trim space beginning of string
1326  while (value.size() && QChar(value[0]).isSpace())
1327  value.remove(0, 1);
1328  int i;
1329  if (value.isEmpty() || !value.startsWith("[") || ((i = value.indexOf(']')) == -1)) {
1330  qWarning().nospace()<<KLF_FUNC_NAME<<": QTextFormat bad format for general property, value does "
1331  <<"not begin with \"[type-name]\".";
1332  continue;
1333  }
1334  QByteArray typenm = value.mid(1, i-1);
1335  QByteArray valuedata = value.mid(i+1);
1336  QVariant vval = klfLoadVariantFromText(valuedata, typenm);
1337  klfDbg("setting generalized property "<<propid<<" to value "<<vval) ;
1338  textformat.setProperty(propid, vval);
1339  }
1340  return textformat;
1341  }
1342  case QMetaType::QVariantList:
1343  {
1344  if (listOrMapDataTypeName == QLatin1String("XML")) {
1345  QDomElement el = parse_xml_wrapper(data, "variant-list");
1346  return klfLoadVariantListFromXML(el);
1347  } else {
1348  QList<QByteArray> sections = decaps_list(data);
1349 
1350  // now we separated into bytearray sections. now read those into values.
1351  QVariantList list;
1352  for (k = 0; k < sections.size(); ++k) {
1353  QVariant val = klfLoadVariantFromText(sections[k], listOrMapDataTypeName);
1354  list << val;
1355  }
1356 
1357  return QVariant::fromValue<QVariantList>(list);
1358  }
1359  }
1360  case QMetaType::QVariantMap:
1361  {
1362  if (listOrMapDataTypeName == QLatin1String("XML")) {
1363  QDomElement el = parse_xml_wrapper(data, "variant-map");
1364  return klfLoadVariantMapFromXML(el);
1365  } else {
1366  const QList<QPair<QByteArray,QByteArray> > sections = decaps_map(data);
1367  QVariantMap vmap;
1368  QList<QPair<QByteArray,QByteArray> >::const_iterator it;
1369  for (it = sections.begin(); it != sections.end(); ++it) {
1370  QString key = klfLoadVariantFromText((*it).first, "QString").toString();
1371  QVariant value = klfLoadVariantFromText((*it).second, listOrMapDataTypeName);
1372  vmap[key] = value;
1373  }
1374  return QVariant::fromValue<QVariantMap>(vmap);
1375  }
1376  }
1377  default:
1378  break;
1379  }
1380 
1381  QByteArray tname = dataTypeName;
1382  if (tname == "KLFStyle") {
1383  KLFStyle style;
1384  QVariantMap map = klfLoadVariantFromText(data, "QByteArray").toMap();
1385  style.name = klfLoadVariantFromText(map["name"].toByteArray(), "QString").toString();
1386  style.fg_color =
1387  klfLoadVariantFromText(map["fg_color"].toByteArray(), "QColor").value<QColor>().rgba();
1388  style.bg_color =
1389  klfLoadVariantFromText(map["bg_color"].toByteArray(), "QColor").value<QColor>().rgba();
1390  style.mathmode = klfLoadVariantFromText(map["mathmode"].toByteArray(), "QString").toString();
1391  style.preamble = klfLoadVariantFromText(map["preamble"].toByteArray(), "QString").toString();
1392  style.dpi = klfLoadVariantFromText(map["dpi"].toByteArray(), "int").toInt();
1393  return QVariant::fromValue<KLFStyle>(style);
1394  }
1395 
1396  qWarning("klfLoadVariantFromText: Can't load a %s from %s !", dataTypeName, stringdata.constData());
1397  return QVariant();
1398 }
1399 
1400 
1401 
1402 
1403 
1404 // ----------------------------------------------------
1405 
1406 
1407 
1408 
1409 
1410 KLF_EXPORT QDomElement klfSaveVariantMapToXML(const QVariantMap& vmap, QDomElement baseNode)
1411 {
1412  QDomDocument doc = baseNode.ownerDocument();
1413 
1414  for (QVariantMap::const_iterator it = vmap.begin(); it != vmap.end(); ++it) {
1415  QString key = it.key();
1416  QVariant value = it.value();
1417 
1418  QDomElement pairNode = doc.createElement("pair");
1419  // * key
1420  QDomElement keyNode = doc.createElement("key");
1421  QDomText keyText = doc.createTextNode(key);
1422  keyNode.appendChild(keyText);
1423  pairNode.appendChild(keyNode);
1424  // * value data
1425  QDomElement vdataNode = doc.createElement("value");
1426  QString vtype = QLatin1String(value.typeName());
1427  vdataNode.setAttribute(QLatin1String("type"), vtype);
1428  if (vtype == "QVariantMap") {
1429  vdataNode = klfSaveVariantMapToXML(value.toMap(), vdataNode);
1430  } else if (vtype == "QVariantList") {
1431  vdataNode = klfSaveVariantListToXML(value.toList(), vdataNode);
1432  } else {
1434  vdataNode.appendChild(vdataText);
1435  }
1436  pairNode.appendChild(vdataNode);
1437  // now append this pair to our list
1438  baseNode.appendChild(pairNode);
1439  }
1440  return baseNode;
1441 }
1442 
1443 KLF_EXPORT QVariantMap klfLoadVariantMapFromXML(const QDomElement& xmlNode)
1444 {
1446 
1447  QVariantMap vmap;
1448 
1449  QDomNode n;
1450  for (n = xmlNode.firstChild(); ! n.isNull(); n = n.nextSibling()) {
1451  QDomElement e = n.toElement(); // try to convert the node to an element.
1452  if ( e.isNull() || n.nodeType() != QDomNode::ElementNode )
1453  continue;
1454  if ( e.nodeName() != "pair" ) {
1455  qWarning("%s: ignoring unexpected tag `%s'!\n", KLF_FUNC_NAME, qPrintable(e.nodeName()));
1456  continue;
1457  }
1458  // read this pair
1459  QString key;
1460  QByteArray valuetype;
1461  QByteArray valuedata;
1462  QDomElement valueNode;
1463  QDomNode nn;
1464  for (nn = e.firstChild(); ! nn.isNull(); nn = nn.nextSibling()) {
1465  klfDbg("inside <pair>: read node "<<nn.nodeName()) ;
1466  QDomElement ee = nn.toElement();
1467  if ( ee.isNull() || nn.nodeType() != QDomNode::ElementNode )
1468  continue;
1469  if ( ee.nodeName() == "key" ) {
1470  key = ee.text();
1471  continue;
1472  }
1473  if ( ee.nodeName() == "value" ) {
1474  // "local 8-bit" because klfLoadVariantFromText() assumes local 8-bit encoding
1475  valueNode = ee;
1476  valuedata = ee.text().toLocal8Bit();
1477  valuetype = ee.attribute("type").toLatin1();
1478  continue;
1479  }
1480  qWarning("%s: ignoring unexpected tag `%s' in <pair>!\n", KLF_FUNC_NAME,
1481  qPrintable(ee.nodeName()));
1482  }
1483  QVariant value;
1484  if (valuetype == "QVariantMap") {
1485  value = QVariant::fromValue<QVariantMap>(klfLoadVariantMapFromXML(valueNode));
1486  } else if (valuetype == "QVariantList") {
1487  value = QVariant::fromValue<QVariantList>(klfLoadVariantListFromXML(valueNode));
1488  } else {
1489  value = klfLoadVariantFromText(valuedata, valuetype.constData());
1490  }
1491  // set this value in our variant map
1492  vmap[key] = value;
1493  }
1494  return vmap;
1495 }
1496 
1497 
1498 KLF_EXPORT QDomElement klfSaveVariantListToXML(const QVariantList& vlist, QDomElement baseNode)
1499 {
1500  QDomDocument doc = baseNode.ownerDocument();
1501 
1502  for (QVariantList::const_iterator it = vlist.begin(); it != vlist.end(); ++it) {
1503  QVariant value = *it;
1504 
1505  QDomElement elNode = doc.createElement(QLatin1String("item"));
1506  QString vtype = QString::fromLatin1(value.typeName()); // "Latin1" encoding by convention
1507  // because type names do not have any special chars
1508  elNode.setAttribute(QLatin1String("type"), vtype);
1509  if (vtype == "QVariantMap") {
1510  elNode = klfSaveVariantMapToXML(value.toMap(), elNode);
1511  } else if (vtype == "QVariantList") {
1512  elNode = klfSaveVariantListToXML(value.toList(), elNode);
1513  } else {
1515  elNode.appendChild(vdataText);
1516  }
1517  // now append this pair to our list
1518  //klfDbg( "... appending node!" ) ;
1519  baseNode.appendChild(elNode);
1520  }
1521 
1522  return baseNode;
1523 }
1524 
1525 KLF_EXPORT QVariantList klfLoadVariantListFromXML(const QDomElement& xmlNode)
1526 {
1528 
1529  QVariantList vlist;
1530 
1531  QDomNode n;
1532  for (n = xmlNode.firstChild(); ! n.isNull(); n = n.nextSibling()) {
1533  QDomElement e = n.toElement(); // try to convert the node to an element.
1534  if ( e.isNull() || n.nodeType() != QDomNode::ElementNode )
1535  continue;
1536  if ( e.nodeName() != QLatin1String("item") ) {
1537  qWarning("%s: ignoring unexpected tag `%s'!\n", KLF_FUNC_NAME, qPrintable(e.nodeName()));
1538  continue;
1539  }
1540 
1541  QString vtype = e.attribute(QLatin1String("type"));
1542 
1543  QVariant value;
1544  if (vtype == QLatin1String("QVariantMap")) {
1545  value = QVariant::fromValue<QVariantMap>(klfLoadVariantMapFromXML(e));
1546  } else if (vtype == QLatin1String("QVariantList")) {
1547  value = QVariant::fromValue<QVariantList>(klfLoadVariantListFromXML(e));
1548  } else {
1549  value = klfLoadVariantFromText(e.text().toLocal8Bit(), vtype.toLatin1().constData());
1550  }
1551 
1552  // set this value in our variant map
1553  vlist << value;
1554  }
1555  return vlist;
1556 }
1557 
1558 // ----------------------------------------------------
1559 
1560 
1561 KLF_EXPORT QString klfPrefixedPath(const QString& path, const QString& reference)
1562 {
1563  klfDbg("path="<<path<<"; reference="<<reference) ;
1564  if (QFileInfo(path).isAbsolute())
1565  return path;
1566 
1567  QString ref = reference;
1568  if (ref.isEmpty())
1570 
1571  klfDbg("reference is "<<ref) ;
1572 
1573  // return path relative to reference
1574  if (!ref.endsWith("/"))
1575  ref += "/";
1576 
1577  return KLF_DEBUG_TEE( ref + path );
1578 }
1579 
1580 
1581 // ----------------------------------------------------
1582 
1583 KLF_EXPORT QString klfUrlLocalFilePath(const QUrl& url)
1584 {
1585 #ifdef Q_OS_WIN32
1586  QString p = url.path();
1587  if (p.startsWith("/"))
1588  p = p.mid(1);
1589  return p;
1590 #else
1591  return url.path();
1592 #endif
1593 }
static struct @2 klf_text_format_props[]
fromUnicode(const QString &str)
#define KLF_BRUSH_STYLE(sty)
Definition: klfutil.cpp:208
#define KLF_ASSERT_CONDITION(expr, msg, failaction)
static struct @1 klf_text_format_formats[]
static QMap< QString, QString > klf_url_query_items_map(const QUrl &url, const QStringList &interestQueryItems)
Definition: klfutil.cpp:73
toUInt(bool *ok=0, int base=10)
KLF_EXPORT QVariantList klfLoadVariantListFromXML(const QDomElement &xmlNode)
Load a list saved with klfSaveVariantListToXML()
Definition: klfutil.cpp:1525
static QByteArray encaps_list(const QList< QByteArray > &list)
Definition: klfutil.cpp:337
setProperty(int propertyId, const QVariant &value)
toInt(bool *ok=0, int base=10)
int dpi
Definition: klfstyle.h:93
toShort(bool *ok=0, int base=10)
toDouble(bool *ok=0)
#define KLF_TEXT_FORMAT_FORMAT(fmt)
Definition: klfutil.cpp:233
setMinimal(bool minimal)
KLF_EXPORT uint klfUrlCompare(const QUrl &url1, const QUrl &url2, uint interestFlags, const QStringList &interestQueryItems)
Compares two URLs and returns some flags as to how they differ.
Definition: klfutil.cpp:89
static struct @0 klf_brush_styles[]
#define KLF_DEBUG_TEE(expr)
queryItems()
bool klfMapIsIncludedIn(const QMap< Key, Value > &a, const QMap< Key, Value > &b, ValCompareFunc cfunc=klfEqualFunc< Value >())
Compares two QMap&#39;s for inclusion.
Definition: klfutil.h:81
toUShort(bool *ok=0, int base=10)
const char * style
Definition: klfutil.cpp:211
contains(const QString &str, Qt::CaseSensitivity cs=Qt::CaseSensitive)
startsWith(const QByteArray &ba)
#define klfDbg(streamableItems)
#define KLF_DEBUG_BLOCK(msg)
setAlpha(int alpha)
QString name
this may not always be set, it&#39;s only important in saved style list.
Definition: klfstyle.h:88
replace(int pos, int len, const QByteArray &after)
fromString(const QString &string, Qt::DateFormat format=Qt::TextDate)
KLF_EXPORT QDomElement klfSaveVariantListToXML(const QVariantList &vlist, QDomElement baseNode)
Lossless save of full list to XML with type information.
Definition: klfutil.cpp:1498
setPermissions(Permissions permissions)
toLong(bool *ok=0, int base=10)
#define RX_SIZE_SEP
int propId
Definition: klfutil.cpp:251
KLF_EXPORT QByteArray klfEscapedToData(const QByteArray &data)
Definition: klfutil.cpp:297
QString mathmode
Definition: klfstyle.h:91
setNamedColor(const QString &name)
KLF_EXPORT QString klfPrefixedPath(const QString &path, const QString &reference)
Returns absolute path to path as seen from reference.
Definition: klfutil.cpp:1561
const char * keyword
Definition: klfutil.cpp:266
QVariant fixed_value
Definition: klfutil.cpp:266
replace(int position, int n, const QString &after)
KLF_EXPORT bool klfMatch(const QVariant &testForHitCandidateValue, const QVariant &queryValue, Qt::MatchFlags flags, const QString &queryStringCache)
Generalized value matching.
Definition: klfutil.cpp:156
int formatId
Definition: klfutil.cpp:236
Urls are equal. The order of query items may be different, but the same are given with the same value...
Definition: klfutil.h:122
indexIn(const QString &str, int offset=0, CaretMode caretMode=CaretAtZero)
number(long n, int base=10)
fromString(const QString &string, Qt::DateFormat format=Qt::TextDate)
indexOf(const QByteArray &ba, int from=0)
fromLocal8Bit(const char *str, int size=-1)
append(const T &value)
fromUtf8(const char *str, int size=-1)
setStyle(Style style)
const char * type
Definition: klfutil.cpp:251
toInt(bool *ok=0)
KLF_EXPORT QVariant klfLoadVariantFromText(const QByteArray &stringdata, const char *dataTypeName, const char *listOrMapDataTypeName)
Definition: klfutil.cpp:832
KLF_EXPORT QString klfUrlLocalFilePath(const QUrl &url)
Definition: klfutil.cpp:1583
contains(const QByteArray &ba)
capturedTexts()
setAttribute(const QString &name, const QString &value)
nameToType(const char *name)
toInt(bool *ok=0, int base=10)
startsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive)
KLF_EXPORT bool klfEnsureDir(const QString &dir)
Ensure existence of a directory.
Definition: klfutil.cpp:52
static QDomElement make_xml_wrapper(const QString &rootname)
Definition: klfutil.cpp:497
endsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive)
compare(const QString &s1, const QString &s2, Qt::CaseSensitivity cs)
const char * key
Definition: klfutil.cpp:251
mid(int pos, int len=-1)
#define KLF_DEBUG_TIME_BLOCK(msg)
This is NOT a specific test. It modifies the behavior of klfUrlCompare() by instructing it to compare...
Definition: klfutil.h:138
Urls have same base URL. All query items in url1 are present in url2 with the same values...
Definition: klfutil.h:126
createTextNode(const QString &value)
#define KLF_FUNC_NAME
contains(const QString &str, Qt::CaseSensitivity cs=Qt::CaseSensitive)
fromString(const QString &string, Qt::DateFormat format=Qt::TextDate)
#define RX_COORD_SEP
fromValue(const T &value)
QString preamble
Definition: klfstyle.h:92
A Formula Style (collection of properties)
Definition: klfstyle.h:37
toLongLong(bool *ok=0, int base=10)
static QDomElement parse_xml_wrapper(const QByteArray &xmldata, const QString &shouldBeRootName)
Definition: klfutil.cpp:505
mid(int position, int n=-1)
takeFirst()
static QByteArray encaps_map(const QList< QPair< QByteArray, QByteArray > > &sections, bool ignore_empty_values=false)
Definition: klfutil.cpp:355
const char * format
Definition: klfutil.cpp:236
#define RX_INT
Urls have same base URL. All query items in url2 are present in url1 with the same values...
Definition: klfutil.h:130
int brushStyle
Definition: klfutil.cpp:211
static bool klf_is_hex_char(char c)
Definition: klfutil.cpp:201
#define KLF_TEXT_FORMAT_PROP(p, type)
Definition: klfutil.cpp:248
static struct @3 klf_text_format_keywords[]
canEncode(QChar ch)
KLF_EXPORT QByteArray klfSaveVariantToText(const QVariant &value, bool saveListAndMapsAsXML)
Definition: klfutil.cpp:519
setQueryItems(const QList< QPair< QString, QString > > &query)
fromLatin1(const char *str, int size=-1)
toULongLong(bool *ok=0, int base=10)
toULong(bool *ok=0, int base=10)
createElement(const QString &tagName)
remove(int pos, int len)
unsigned long bg_color
Definition: klfstyle.h:90
static QList< QPair< QByteArray, QByteArray > > decaps_map(const QByteArray &ba_data, bool allow_empty_values=false)
Definition: klfutil.cpp:436
KLF_EXPORT QDomElement klfSaveVariantMapToXML(const QVariantMap &vmap, QDomElement baseNode)
Lossless save of full map to XML with type information.
Definition: klfutil.cpp:1410
KLF_EXPORT QByteArray klfDataToEscaped(const QByteArray &value_ba)
Definition: klfutil.cpp:278
mkpath(const QString &dirPath)
static QList< QByteArray > decaps_list(const QByteArray &ba_data)
Definition: klfutil.cpp:384
setContent(const QByteArray &data, bool namespaceProcessing, QString *errorMsg=0, int *errorLine=0, int *errorColumn=0)
unsigned long fg_color
Definition: klfstyle.h:89
KLF_EXPORT QVariantMap klfLoadVariantMapFromXML(const QDomElement &xmlNode)
Load a map saved with klfSaveVariantMapToXML()
Definition: klfutil.cpp:1443
Urls have same base URL. Query items are ignored.
Definition: klfutil.h:132
attribute(const QString &name, const QString &defValue=QString()

Generated by doxygen 1.8.11