File indexing completed on 2024-12-22 04:17:58

0001 /***************************************************************************
0002                              labelparser.cpp
0003                              ----------------
0004     begin                : Dec 14 2004
0005                            Copyright (C) 2004, The University of Toronto
0006     email                : netterfield@astro.utoronto.ca
0007  ***************************************************************************/
0008 
0009 /***************************************************************************
0010  *                                                                         *
0011  *   This program is free software; you can redistribute it and/or modify  *
0012  *   it under the terms of the GNU General Public License as published by  *
0013  *   the Free Software Foundation; either version 2 of the License, or     *
0014  *   (at your option) any later version.                                   *
0015  *                                                                         *
0016  ***************************************************************************/
0017 
0018 #include "labelparser.h"
0019 
0020 #include <assert.h>
0021 #include <stdlib.h>
0022 
0023 #include <qregexp.h>
0024 #include <qstring.h>
0025 
0026 using namespace Label;
0027 
0028 // Debug output for Parsing - 0 Off 1 On
0029 #define DEBUG_PARSING 0
0030 
0031 #if DEBUG_PARSING
0032 #define dumpattr(node, text) do { printf("%s: bold:%d italic:%d underline:%d\n", text, (node)->attributes.bold, (node)->attributes.italic, (node)->attributes.underline); } while(0)
0033 #else
0034 #define dumpattr(node, text)
0035 #endif
0036 
0037 Chunk::Chunk(Chunk *parent, VOffset dir, bool isGroup, bool inherit)
0038 : next(0L), prev(0L), up(0L), down(0L), group(0L), scalar(false), linebreak(false), tab(false), vector(false), formated(false), vOffset(dir) {
0039   assert(parent || vOffset == None);
0040   if (parent) {  // attach and inherit
0041     switch (vOffset) {
0042       case None:
0043         if (isGroup) {
0044           parent->group = this;
0045         } else {
0046           while (parent->next) {
0047             parent = parent->next;
0048           }
0049           parent->next = this;
0050         }
0051         break;
0052       case Up:
0053         assert(!parent->up);
0054         parent->up = this;
0055         break;
0056       case Down:
0057         assert(!parent->down);
0058         parent->down = this;
0059         break;
0060     }
0061 
0062     if (inherit) {
0063       // inherit these properties from the parent
0064       attributes = parent->attributes;
0065     }
0066 
0067     prev = parent;
0068   }
0069 }
0070 
0071 
0072 Chunk::~Chunk() {
0073   // These are set to 0 by the child if they're non-zero
0074   delete next;
0075   delete up;
0076   delete down;
0077   delete group;
0078   group = 0L;
0079   if (prev) {
0080     switch (vOffset) {
0081       case None:
0082         prev->next = 0L;
0083         // Note: kind of does the wrong thing if we're a group...  no issue though
0084         break;
0085       case Up:
0086         prev->up = 0L;
0087         break;
0088       case Down:
0089         prev->down = 0L;
0090         break;
0091     }
0092     prev = 0L;
0093   }
0094 }
0095 
0096 
0097 bool Chunk::locked() const {
0098   return scalar || group || linebreak || tab || vector;
0099 }
0100 
0101 
0102 Parsed::Parsed() : chunk(0L) {
0103 }
0104 
0105 
0106 Parsed::~Parsed() {
0107   delete chunk;
0108   chunk = 0L;
0109 }
0110 
0111 
0112 inline void setNormalChar(QChar c, Chunk **tail) {
0113   if (*tail && !(*tail)->locked()) {
0114     (*tail)->text += c;
0115   } else {
0116     Chunk *f = new Chunk(*tail, Chunk::None, false, true);
0117     f->text += c;
0118     *tail = f;
0119   }
0120 }
0121 
0122 
0123 inline QColor parseColor(const QString& txt, int *skip) {
0124   const int end = txt.indexOf('}');
0125   if (skip) {
0126     *skip = end;
0127   }
0128 
0129   if (end == -1) {
0130     return QColor();
0131   }
0132 
0133   const QString endPart = txt.left(end);
0134 
0135   QColor color(endPart); // This one is slow.  If we support our own formats
0136                          // outside of QColor, make sure that this is called
0137                          // -after- we try our own formats.  Every cycle
0138                          // counts in here.
0139 #if 0
0140   // This is rr,gg,bb support.  I'm not sure about supporting H,S,V or even
0141   // about compatibility with LaTeX so for now we don't support it.  If it's
0142   // ever re-enabled, make sure that testcases are added.
0143   if (!color.isValid()) {
0144     // the color is in the format "r,g,b"
0145     QStringList components = QStringList::split(',', endPart, true);
0146     if (components.count() == 3) {
0147       int colors[3] = { 0, 0, 0 };
0148       int base = 10;
0149 
0150       // assume the colors are given as decimal numbers unless we have a hex value in the string
0151       if (endPart.find(QRegExp("[A-F]", false)) != -1) {
0152         base = 16;
0153       }
0154 
0155       bool ok = true;
0156       colors[0] = components[0].toInt(&ok, base);
0157       if (ok) {
0158         colors[1] = components[1].toInt(&ok, base);
0159       }
0160       if (ok) {
0161         colors[2] = components[2].toInt(&ok, base);
0162       }
0163 
0164       if (ok) {
0165         color.setRgb(colors[0], colors[1], colors[2]);
0166       } // Should error out?
0167     }
0168   }
0169 #endif
0170   return color;
0171 }
0172 
0173 
0174 static Chunk *parseInternal(Chunk *ctail, const QString& txt, uint& start, uint cnt, bool interpretNewLine);
0175 
0176 #define EXPAND_GREEK(L_U, L_L, REST, SKIP, UCODE)    \
0177   case L_L:                                   \
0178     x=0x20;                                   \
0179   case L_U:                                   \
0180     if (txt.mid(from + 1).startsWith(REST)) { \
0181       *skip = SKIP;                           \
0182       setNormalChar(QChar(UCODE+x), tail);    \
0183       return true;                            \
0184     }                                         \
0185   break;
0186 
0187 
0188 inline bool parseOutChar(const QString& txt, uint from, int *skip, Chunk **tail, bool interpretNewLine) {
0189   // STOP! Changes you make here should be made into cclineedit.cpp as well for completion.
0190   QChar c = txt[from];
0191   bool upper = false;
0192   *skip = 1;
0193   short x = 0;
0194 
0195 #if DEBUG_PARSING
0196   qDebug() << "----- parsing " << txt;
0197 #endif
0198 
0199   switch (c.unicode()) {
0200     EXPAND_GREEK('B', 'b', "eta",  4, 0x392)
0201     EXPAND_GREEK('D', 'd', "elta", 5, 0x394)
0202     EXPAND_GREEK('Z', 'z', "eta",  4, 0x396)
0203     EXPAND_GREEK('K', 'k', "appa", 5, 0x39a)
0204     EXPAND_GREEK('M', 'm', "u",    2, 0x39c)
0205     EXPAND_GREEK('X', 'x', "i",    2, 0x39e)
0206     EXPAND_GREEK('R', 'r', "ho",   3, 0x3a1)
0207 
0208     case 'a':
0209       x = 0x20;
0210     case 'A':
0211       if (txt.mid(from + 1).startsWith(QLatin1String("lpha"))) {
0212         *skip = 5;
0213         setNormalChar(QChar(0x391+x), tail);
0214         return true;
0215       } else if (txt.mid(from + 1).startsWith(QLatin1String("pprox"))) {
0216         *skip = 6;
0217         setNormalChar(QChar(0x2248), tail);
0218         return true;
0219       }
0220       break;
0221 
0222 
0223     case 'c':
0224       x = 0x20;
0225     case 'C':
0226       if (txt.mid(from + 1).startsWith(QLatin1String("hi"))) {
0227         *skip = 3;
0228         setNormalChar(QChar(0x3a7+x), tail);
0229         return true;
0230       } else if (txt.mid(from + 1).startsWith(QLatin1String("dot"))) {
0231         *skip = 4;
0232         setNormalChar(QChar(0x2219), tail);
0233         return true;
0234       }
0235       break;
0236 
0237     case 'e':
0238       x = 0x20;
0239     case 'E':
0240       if (txt.mid(from + 1).startsWith(QLatin1String("psilon"))) {
0241         *skip = 7;
0242         setNormalChar(QChar(0x395+x), tail);
0243         return true;
0244       } else if (txt.mid(from + 1).startsWith(QLatin1String("ta"))) {
0245         *skip = 3;
0246         setNormalChar(QChar(0x397+x), tail);
0247         return true;
0248       } else if (txt.mid(from + 1).startsWith(QLatin1String("ll"))) {
0249         *skip = 3;
0250         setNormalChar(QChar(0x2113), tail);
0251         return true;
0252       }
0253       break;
0254 
0255     case 'g':
0256       x = 0x20;
0257     case 'G':
0258       if (txt.mid(from + 1).startsWith(QLatin1String("amma"))) {
0259         *skip = 5;
0260         setNormalChar(QChar(0x393+x), tail);
0261         return true;
0262       } else if (txt.mid(from + 1).startsWith(QLatin1String("eq"))) {
0263         *skip = 3;
0264         setNormalChar(QChar(0x2265), tail);
0265         return true;
0266       } else if (txt.mid(from + 1).startsWith('e')) {
0267         *skip = 2;
0268         setNormalChar(QChar(0x2265), tail);
0269         return true;
0270       }
0271       break;
0272 
0273     case 'i':
0274       x = 0x20;
0275     case 'I':
0276       if (txt.mid(from + 1).startsWith(QLatin1String("ota"))) {
0277         *skip = 4;
0278         setNormalChar(QChar(0x399+x), tail);
0279         return true;
0280       } else if (!upper && txt.mid(from + 1).startsWith(QLatin1String("nf"))) {
0281         *skip = 3;
0282         setNormalChar(QChar(0x221E), tail);
0283         return true;
0284       } else if (!upper && txt.mid(from + 1).startsWith(QLatin1String("nt"))) {
0285         *skip = 3;
0286         setNormalChar(QChar(0x222B), tail);
0287         return true;
0288       }
0289       break;
0290 
0291     case 'l':
0292       x = 0x20;
0293     case 'L':
0294       if (txt.mid(from + 1).startsWith(QLatin1String("ambda"))) {
0295         *skip = 6;
0296         setNormalChar(QChar(0x39b+x), tail);
0297         return true;
0298       } else if (txt.mid(from + 1).startsWith(QLatin1String("eq"))) {
0299         *skip = 3;
0300         setNormalChar(QChar(0x2264), tail);
0301         return true;
0302       } else if (txt.mid(from + 1).startsWith('e')) {
0303         *skip = 2;
0304         setNormalChar(QChar(0x2264), tail);
0305         return true;
0306       }
0307       break;
0308 
0309     case 'n':
0310       x = 0x20;
0311     case 'N':
0312       if (txt.mid(from + 1).startsWith('u')) {
0313         *skip = 2;
0314         setNormalChar(QChar(0x39D+x), tail);
0315         return true;
0316       } else if (txt.mid(from + 1).startsWith('e')) {
0317         *skip = 2;
0318         setNormalChar(QChar(0x2260), tail);
0319         return true;
0320       } else if (interpretNewLine) {
0321         *skip = 1;
0322         if (!*tail || !(*tail)->text.isEmpty() || (*tail)->locked()) {
0323           *tail = new Chunk(*tail, Chunk::None, false, true);
0324         }
0325         (*tail)->linebreak = true;
0326         *tail = new Chunk(*tail, Chunk::None, false, true);
0327         return true;
0328       } else {
0329         *skip = 1;
0330         setNormalChar(QChar(0x20), tail);  
0331         return true;      
0332       }
0333       break;
0334 
0335     case 'o':
0336       x = 0x20;
0337     case 'O':
0338       if (txt.mid(from + 1).startsWith(QLatin1String("verline{"))) {
0339         if ((*tail)->group) {
0340           *tail = new Chunk(*tail, Chunk::None, false, true);
0341         }
0342         Chunk *working = new Chunk(*tail, Chunk::None, true, true);
0343         dumpattr(working, "start group for overline");
0344         uint parseStart = from + 9;
0345         working->attributes.overline = true;
0346         parseInternal(working, txt, parseStart, txt.length(), interpretNewLine);
0347         *skip = parseStart - from + 1;
0348         dumpattr(working, "end group for overline");
0349         return true;
0350       } else if (txt.mid(from + 1).startsWith(QLatin1String("micron"))) {
0351         *skip = 7;
0352         setNormalChar(QChar(0x39F+x), tail);
0353         return true;
0354       } else if (txt.mid(from + 1).startsWith(QLatin1String("mega"))) {
0355         *skip = 5;
0356         setNormalChar(QChar(0x3A9+x), tail);
0357         return true;
0358       } else if (txt.mid(from + 1).startsWith(QLatin1String("dot"))) {
0359         *skip = 4;
0360         setNormalChar(QChar(0x2299), tail);
0361         return true;
0362       }
0363       break;
0364 
0365     case 'p':
0366       x = 0x20;
0367     case 'P':
0368       if (txt.mid(from + 1).startsWith('i')) {
0369         *skip = 2;
0370         setNormalChar(QChar(0x3a0+x), tail);
0371         return true;
0372       } else if (txt.mid(from + 1).startsWith(QLatin1String("hi"))) {
0373         *skip = 3;
0374         setNormalChar(QChar(0x3A6+x), tail);
0375         return true;
0376       } else if (txt.mid(from + 1).startsWith(QLatin1String("si"))) {
0377         *skip = 3;
0378         setNormalChar(QChar(0x3A8+x), tail);
0379         return true;
0380       } else if (txt.mid(from + 1).startsWith(QLatin1String("artial"))) {
0381         *skip = 7;
0382         setNormalChar(QChar(0x2202), tail);
0383         return true;
0384       } else if (txt.mid(from + 1).startsWith(QLatin1String("rod"))) {
0385         *skip = 4;
0386         setNormalChar(QChar(0x220F), tail);
0387         return true;
0388       } else if (txt.mid(from + 1).startsWith('m')) {
0389         *skip = 2;
0390         setNormalChar(QChar(0xb1), tail);
0391         return true;
0392       }
0393       break;
0394 
0395     case 't':
0396       x = 0x20;
0397     case 'T':
0398       if (txt.mid(from + 1).startsWith(QLatin1String("extcolor{"))) { // \textcolor{color}{text}
0399         if ((*tail)->group) {
0400           *tail = new Chunk(*tail, Chunk::None, false, true);
0401         }
0402         Chunk *working = new Chunk(*tail, Chunk::None, true, true);
0403         dumpattr(working, "start group for textcolor");
0404         uint parseStart = from + 10;
0405         int firstSkip = 0;
0406         working->attributes.color = parseColor(txt.mid(parseStart), &firstSkip);
0407         if (!working->attributes.color.isValid() || txt[parseStart + firstSkip + 1] != '{') {
0408           return false;
0409         }
0410         parseStart += firstSkip + 2;
0411         parseInternal(working, txt, parseStart, txt.length(), interpretNewLine);
0412         *skip = parseStart - from + 1;
0413         dumpattr(working, "end group for textcolor");
0414         return true;
0415       } else if (txt.mid(from + 1).startsWith(QLatin1String("extbf{"))) {
0416         if ((*tail)->group) {
0417           *tail = new Chunk(*tail, Chunk::None, false, true);
0418         }
0419         Chunk *working = new Chunk(*tail, Chunk::None, true, true);
0420         dumpattr(working, "start group for textbf");
0421         uint parseStart = from + 7;
0422         working->attributes.bold = true;
0423         parseInternal(working, txt, parseStart, txt.length(), interpretNewLine);
0424         *skip = parseStart - from + 1;
0425         dumpattr(working, "end group for textbf");
0426         return true;
0427       } else if (txt.mid(from + 1).startsWith(QLatin1String("extit{"))) {
0428         if ((*tail)->group) {
0429           *tail = new Chunk(*tail, Chunk::None, false, true);
0430         }
0431         Chunk *working = new Chunk(*tail, Chunk::None, true, true);
0432         dumpattr(working, "start group for textit");
0433         uint parseStart = from + 7;
0434         working->attributes.italic = true;
0435         parseInternal(working, txt, parseStart, txt.length(), interpretNewLine);
0436         *skip = parseStart - from + 1;
0437         dumpattr(working, "end group for textit");
0438         return true;
0439       } else if (txt.mid(from + 1).startsWith(QLatin1String("heta"))) {
0440         *skip = 5;
0441         setNormalChar(QChar(0x398+x), tail);
0442         return true;
0443       } else if (txt.mid(from + 1).startsWith(QLatin1String("au"))) {
0444         *skip = 3;
0445         setNormalChar(QChar(0x3A4+x), tail);
0446         return true;
0447       } else {
0448         *skip = 1;
0449         if (!*tail || !(*tail)->text.isEmpty() || (*tail)->locked()) {
0450           *tail = new Chunk(*tail, Chunk::None, false, true);
0451         }
0452         (*tail)->tab = true;
0453         *tail = new Chunk(*tail, Chunk::None, false, true);
0454         return true;
0455       }
0456       break;
0457 
0458     case 's':
0459       x = 0x20;
0460     case 'S':
0461       if (txt.mid(from + 1).startsWith(QLatin1String("igma"))) {
0462         *skip = 5;
0463         setNormalChar(QChar(0x3A3+x), tail);
0464         return true;
0465       } else if (!upper && txt.mid(from + 1).startsWith(QLatin1String("um"))) {
0466         *skip = 3;
0467         setNormalChar(QChar(0x2211), tail);
0468         return true;
0469       } else if (!upper && txt.mid(from + 1).startsWith(QLatin1String("qrt"))) {
0470         *skip = 4;
0471         setNormalChar(QChar(0x221A), tail);
0472         return true;
0473       }
0474       break;
0475 
0476     case 'u':
0477       x = 0x20;
0478     case 'U':
0479       if (txt.mid(from + 1).startsWith(QLatin1String("nderline{"))) {
0480         if ((*tail)->group) {
0481           *tail = new Chunk(*tail, Chunk::None, false, true);
0482         }
0483         Chunk *working = new Chunk(*tail, Chunk::None, true, true);
0484         dumpattr(working, "start group for underline");
0485         uint parseStart = from + 10;
0486         working->attributes.underline = true;
0487         parseInternal(working, txt, parseStart, txt.length(), interpretNewLine);
0488         *skip = parseStart - from + 1;
0489         dumpattr(working, "end group for underline");
0490         return true;
0491       } else if (txt.mid(from + 1).startsWith(QLatin1String("psilon"))) {
0492         *skip = 7;
0493         setNormalChar(QChar(0x3A5+x), tail);
0494         return true;
0495       }
0496       break;
0497 
0498     default:
0499       break;
0500   }
0501 
0502   return false;
0503 }
0504 
0505 
0506 static Chunk *parseInternal(Chunk *ctail, const QString& txt, uint& start, uint cnt, bool interpretNewLine) {
0507   Chunk *chead = ctail;
0508 
0509   if (ctail->group) {
0510     ctail = new Chunk(ctail, Chunk::None, false, true);
0511   }
0512   for (uint& i = start; i < cnt; ++i) {
0513     QChar c = txt[i];
0514     Chunk::VOffset dir = Chunk::Down;
0515     switch (c.unicode()) {
0516       case '\n':
0517         if (ctail->vOffset != Chunk::None && (!ctail->text.isEmpty() || ctail->locked())) {
0518           while (ctail->vOffset != Chunk::None && (!ctail->text.isEmpty() || ctail->locked())) {
0519             ctail = ctail->prev;
0520           }
0521           ctail = new Chunk(ctail, Chunk::None, false, true);
0522         }
0523         if (!ctail->text.isEmpty() || ctail->locked()) {
0524           if (ctail->vOffset != Chunk::None) {
0525             ctail = new Chunk(ctail->prev, Chunk::None, false, true);
0526           } else {
0527             ctail = new Chunk(ctail, Chunk::None, false, true);
0528           }
0529         }
0530         ctail->linebreak = true;
0531         ctail = new Chunk(ctail, Chunk::None, false, true);
0532         break;
0533       case '\t':
0534         if (!ctail->text.isEmpty() || ctail->locked()) {
0535           if (ctail->vOffset != Chunk::None) {
0536             ctail = new Chunk(ctail->prev, Chunk::None, false, true);
0537           } else {
0538             ctail = new Chunk(ctail, Chunk::None, false, true);
0539           }
0540         }
0541         ctail->tab = true;
0542         ctail = new Chunk(ctail, Chunk::None, false, true);
0543         break;
0544       case 0x5c:   // \ /**/
0545 
0546         if (ctail->vOffset != Chunk::None && (!ctail->text.isEmpty() || ctail->locked())) {
0547           while (ctail->vOffset != Chunk::None && (!ctail->text.isEmpty() || ctail->locked())) {
0548             ctail = ctail->prev;
0549           }
0550           ctail = new Chunk(ctail, Chunk::None, false, true);
0551         }
0552 
0553 
0554         if (ctail->vOffset != Chunk::None && !ctail->text.isEmpty()) {
0555           ctail = new Chunk(ctail->prev, Chunk::None, false, true);
0556         }
0557         ++i;
0558         if (i == cnt) {
0559           setNormalChar('\\', &ctail);
0560         } else {
0561           int skip = 0;
0562           if (!parseOutChar(txt, i, &skip, &ctail, interpretNewLine)) {
0563             setNormalChar(txt[i], &ctail);
0564           } else {
0565             i += skip - 1;
0566           }
0567         }
0568         break;
0569       case 0x5e:   // ^
0570         dir = Chunk::Up;
0571       case 0x5f:   // _ (dir is set to Down at beginning of loop)
0572         if (ctail->text.isEmpty() && !ctail->group) {
0573           setNormalChar(c, &ctail);
0574         } else {
0575           if (ctail->vOffset != Chunk::None) {
0576             if (ctail->vOffset != dir) {
0577               ctail = new Chunk(ctail->prev, dir, false, true);
0578             } else if (ctail->group) {
0579               ctail = new Chunk(ctail, dir, false, true);
0580             } else {
0581               ctail = new Chunk(ctail, dir, false, true);
0582             }
0583           } else {
0584             ctail = new Chunk(ctail, dir, false, true);
0585           }
0586         }
0587         break;
0588       case 0x7b:   // {
0589         if (ctail->text.isEmpty() && !ctail->group) {
0590           bool rc = false;
0591           new Chunk(ctail, Chunk::None, true, true);
0592           dumpattr(ctail->group, "start group with non-group and empty text");
0593           rc = 0L != parseInternal(ctail->group, txt, ++i, cnt, interpretNewLine);
0594           assert(rc);
0595           dumpattr(ctail->group, "after start group with non-group and empty text");
0596           if (!rc) {
0597             return 0L;
0598           }
0599         } else {
0600           bool rc = false;
0601           if (ctail->vOffset == Chunk::None) {
0602             rc = 0L != parseInternal(new Chunk(ctail, Chunk::None, true, true), txt, ++i, cnt, interpretNewLine);
0603           } else {
0604             rc = 0L != parseInternal(new Chunk(ctail->prev, Chunk::None, true, true), txt, ++i, cnt,  interpretNewLine);
0605           }
0606           if (!rc) {
0607             return 0L;
0608           }
0609         }
0610         break;
0611       case 0x7d:   // }
0612         if (chead->prev && chead->prev->group == chead) {
0613           return chead;
0614         } else {
0615           setNormalChar(c, &ctail);
0616         }
0617         break;
0618       case '[':
0619         {
0620           bool vector = false;
0621           int vectorIndexStart = -1;
0622           int vectorIndexEnd = -1;
0623           int bracketStack = 1;
0624           int pos = -1;
0625           bool format = false;
0626           int formatIndexStart = 0;
0627           int formatIndexEnd = 0;
0628 
0629           bool equation = txt[i + 1] == '=';
0630           for (uint searchPt = i + 1; bracketStack != 0 && searchPt < cnt; ++searchPt) {
0631             if (txt[searchPt] == ']') {
0632               if (--bracketStack == 0) {
0633                 pos = searchPt;
0634                 break;
0635               } else if (bracketStack == 1 && vector && vectorIndexEnd == -1) {
0636                 vectorIndexEnd = searchPt - 1;
0637               }
0638             } else if (txt[searchPt] == '[') {
0639               ++bracketStack;
0640               if (!vector && !equation) {
0641                 vector = true;
0642                 vectorIndexStart = searchPt + 1;
0643               }
0644             }
0645           }
0646 
0647           if (pos < 0 || pos == int(i) + 1 /* empty [] */) {
0648             return 0L;
0649           }
0650 
0651           if (pos+3 < (int)cnt) { // ]{%f} is min size.
0652             if ((txt[pos+1]=='{') && ((txt[pos+2] == '%') || (txt[pos+2] == 'T'))) {
0653               formatIndexStart = pos+1;
0654               for (uint searchPt = pos + 2; searchPt < cnt; ++searchPt) {
0655                 if (txt[searchPt] == '}') {
0656                   formatIndexEnd = searchPt;
0657                   format = true;
0658                   break;
0659                 }
0660               }
0661             }
0662           }
0663 
0664           if (ctail->locked() || !ctail->text.isEmpty()) {
0665             if (ctail->vOffset != Chunk::None) {
0666               ctail = new Chunk(ctail->prev, Chunk::None, false, true);
0667             } else {
0668               ctail = new Chunk(ctail, Chunk::None, false, true);
0669             }
0670           }
0671 
0672           if (vector) {
0673             ctail->text = txt.mid(i + 1, vectorIndexStart - i - 2).trimmed();
0674             ctail->expression = txt.mid(vectorIndexStart, vectorIndexEnd - vectorIndexStart + 1);
0675             ctail->vector = true;
0676           } else {
0677             ctail->text = txt.mid(i + 1, pos - i - 1).trimmed();
0678             ctail->scalar = true;
0679           }
0680           if (format) {
0681             i = uint(formatIndexEnd);
0682             ctail->formated = true;
0683             ctail->format = txt.mid(formatIndexStart+1, formatIndexEnd - formatIndexStart-1);
0684 
0685           } else {
0686             i = uint(pos);
0687           }
0688         }
0689         break;
0690       default:
0691 #if 0
0692         if (ctail->vOffset != Chunk::None && (!ctail->text.isEmpty() || ctail->locked())) {
0693           ctail = new Chunk(ctail->prev, Chunk::None, false, true);
0694         }
0695 #endif
0696         if (ctail->vOffset != Chunk::None && (!ctail->text.isEmpty() || ctail->locked())) {
0697           while (ctail->vOffset != Chunk::None && (!ctail->text.isEmpty() || ctail->locked())) {
0698             ctail = ctail->prev;
0699           }
0700           ctail = new Chunk(ctail, Chunk::None, false, true);
0701         }
0702         setNormalChar(c, &ctail);
0703         break;
0704     }
0705   }
0706 
0707   return chead;
0708 }
0709 
0710 Parsed *Label::parse(const QString& txt, const QColor &color, bool interpret, bool interpretNewLine) {
0711   Parsed *parsed = new Parsed;
0712   Chunk *ctail = parsed->chunk = new Chunk(0L);
0713   ctail->attributes.color = color;
0714   if (!interpret) {
0715     ctail->text = txt;
0716     return parsed;
0717   }
0718 
0719   uint start = 0;
0720   uint cnt = txt.length();
0721   Chunk *rc = parseInternal(ctail, txt, start, cnt, interpretNewLine);
0722   if (!rc) {
0723     // Parse error - how to recover?
0724     delete parsed;
0725     parsed = 0L;
0726   }
0727   return parsed;
0728 }
0729 
0730 
0731 // vim: ts=2 sw=2 et