1 /***************************************************************************
2 labelparser.cpp
3 ----------------
4 begin : Dec 14 2004
5 Copyright (C) 2004, The University of Toronto
6 email : netterfield@astro.utoronto.ca
7 ***************************************************************************/
8
9 /***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18 #include "labelparser.h"
19
20 #include <assert.h>
21 #include <stdlib.h>
22
23 #include <qregexp.h>
24 #include <qstring.h>
25
26 using namespace Label;
27
28 // Debug output for Parsing - 0 Off 1 On
29 #define DEBUG_PARSING 0
30
31 #if DEBUG_PARSING
32 #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)
33 #else
34 #define dumpattr(node, text)
35 #endif
36
Chunk(Chunk * parent,VOffset dir,bool isGroup,bool inherit)37 Chunk::Chunk(Chunk *parent, VOffset dir, bool isGroup, bool inherit)
38 : next(0L), prev(0L), up(0L), down(0L), group(0L), scalar(false), linebreak(false), tab(false), vector(false), formated(false), vOffset(dir) {
39 assert(parent || vOffset == None);
40 if (parent) { // attach and inherit
41 switch (vOffset) {
42 case None:
43 if (isGroup) {
44 parent->group = this;
45 } else {
46 while (parent->next) {
47 parent = parent->next;
48 }
49 parent->next = this;
50 }
51 break;
52 case Up:
53 assert(!parent->up);
54 parent->up = this;
55 break;
56 case Down:
57 assert(!parent->down);
58 parent->down = this;
59 break;
60 }
61
62 if (inherit) {
63 // inherit these properties from the parent
64 attributes = parent->attributes;
65 }
66
67 prev = parent;
68 }
69 }
70
71
~Chunk()72 Chunk::~Chunk() {
73 // These are set to 0 by the child if they're non-zero
74 delete next;
75 delete up;
76 delete down;
77 delete group;
78 group = 0L;
79 if (prev) {
80 switch (vOffset) {
81 case None:
82 prev->next = 0L;
83 // Note: kind of does the wrong thing if we're a group... no issue though
84 break;
85 case Up:
86 prev->up = 0L;
87 break;
88 case Down:
89 prev->down = 0L;
90 break;
91 }
92 prev = 0L;
93 }
94 }
95
96
locked() const97 bool Chunk::locked() const {
98 return scalar || group || linebreak || tab || vector;
99 }
100
101
Parsed()102 Parsed::Parsed() : chunk(0L) {
103 }
104
105
~Parsed()106 Parsed::~Parsed() {
107 delete chunk;
108 chunk = 0L;
109 }
110
111
setNormalChar(QChar c,Chunk ** tail)112 inline void setNormalChar(QChar c, Chunk **tail) {
113 if (*tail && !(*tail)->locked()) {
114 (*tail)->text += c;
115 } else {
116 Chunk *f = new Chunk(*tail, Chunk::None, false, true);
117 f->text += c;
118 *tail = f;
119 }
120 }
121
122
parseColor(const QString & txt,int * skip)123 inline QColor parseColor(const QString& txt, int *skip) {
124 const int end = txt.indexOf('}');
125 if (skip) {
126 *skip = end;
127 }
128
129 if (end == -1) {
130 return QColor();
131 }
132
133 const QString endPart = txt.left(end);
134
135 QColor color(endPart); // This one is slow. If we support our own formats
136 // outside of QColor, make sure that this is called
137 // -after- we try our own formats. Every cycle
138 // counts in here.
139 #if 0
140 // This is rr,gg,bb support. I'm not sure about supporting H,S,V or even
141 // about compatibility with LaTeX so for now we don't support it. If it's
142 // ever re-enabled, make sure that testcases are added.
143 if (!color.isValid()) {
144 // the color is in the format "r,g,b"
145 QStringList components = QStringList::split(',', endPart, true);
146 if (components.count() == 3) {
147 int colors[3] = { 0, 0, 0 };
148 int base = 10;
149
150 // assume the colors are given as decimal numbers unless we have a hex value in the string
151 if (endPart.find(QRegExp("[A-F]", false)) != -1) {
152 base = 16;
153 }
154
155 bool ok = true;
156 colors[0] = components[0].toInt(&ok, base);
157 if (ok) {
158 colors[1] = components[1].toInt(&ok, base);
159 }
160 if (ok) {
161 colors[2] = components[2].toInt(&ok, base);
162 }
163
164 if (ok) {
165 color.setRgb(colors[0], colors[1], colors[2]);
166 } // Should error out?
167 }
168 }
169 #endif
170 return color;
171 }
172
173
174 static Chunk *parseInternal(Chunk *ctail, const QString& txt, uint& start, uint cnt, bool interpretNewLine);
175
176 #define EXPAND_GREEK(L_U, L_L, REST, SKIP, UCODE) \
177 case L_L: \
178 x=0x20; \
179 case L_U: \
180 if (txt.mid(from + 1).startsWith(REST)) { \
181 *skip = SKIP; \
182 setNormalChar(QChar(UCODE+x), tail); \
183 return true; \
184 } \
185 break;
186
187
parseOutChar(const QString & txt,uint from,int * skip,Chunk ** tail,bool interpretNewLine)188 inline bool parseOutChar(const QString& txt, uint from, int *skip, Chunk **tail, bool interpretNewLine) {
189 // STOP! Changes you make here should be made into cclineedit.cpp as well for completion.
190 QChar c = txt[from];
191 bool upper = false;
192 *skip = 1;
193 short x = 0;
194
195 #if DEBUG_PARSING
196 qDebug() << "----- parsing " << txt;
197 #endif
198
199 switch (c.unicode()) {
200 EXPAND_GREEK('B', 'b', "eta", 4, 0x392)
201 EXPAND_GREEK('D', 'd', "elta", 5, 0x394)
202 EXPAND_GREEK('Z', 'z', "eta", 4, 0x396)
203 EXPAND_GREEK('K', 'k', "appa", 5, 0x39a)
204 EXPAND_GREEK('M', 'm', "u", 2, 0x39c)
205 EXPAND_GREEK('X', 'x', "i", 2, 0x39e)
206 EXPAND_GREEK('R', 'r', "ho", 3, 0x3a1)
207
208 case 'a':
209 x = 0x20;
210 case 'A':
211 if (txt.mid(from + 1).startsWith(QLatin1String("lpha"))) {
212 *skip = 5;
213 setNormalChar(QChar(0x391+x), tail);
214 return true;
215 } else if (txt.mid(from + 1).startsWith(QLatin1String("pprox"))) {
216 *skip = 6;
217 setNormalChar(QChar(0x2248), tail);
218 return true;
219 }
220 break;
221
222
223 case 'c':
224 x = 0x20;
225 case 'C':
226 if (txt.mid(from + 1).startsWith(QLatin1String("hi"))) {
227 *skip = 3;
228 setNormalChar(QChar(0x3a7+x), tail);
229 return true;
230 } else if (txt.mid(from + 1).startsWith(QLatin1String("dot"))) {
231 *skip = 4;
232 setNormalChar(QChar(0x2219), tail);
233 return true;
234 }
235 break;
236
237 case 'e':
238 x = 0x20;
239 case 'E':
240 if (txt.mid(from + 1).startsWith(QLatin1String("psilon"))) {
241 *skip = 7;
242 setNormalChar(QChar(0x395+x), tail);
243 return true;
244 } else if (txt.mid(from + 1).startsWith(QLatin1String("ta"))) {
245 *skip = 3;
246 setNormalChar(QChar(0x397+x), tail);
247 return true;
248 } else if (txt.mid(from + 1).startsWith(QLatin1String("ll"))) {
249 *skip = 3;
250 setNormalChar(QChar(0x2113), tail);
251 return true;
252 }
253 break;
254
255 case 'g':
256 x = 0x20;
257 case 'G':
258 if (txt.mid(from + 1).startsWith(QLatin1String("amma"))) {
259 *skip = 5;
260 setNormalChar(QChar(0x393+x), tail);
261 return true;
262 } else if (txt.mid(from + 1).startsWith(QLatin1String("eq"))) {
263 *skip = 3;
264 setNormalChar(QChar(0x2265), tail);
265 return true;
266 } else if (txt.mid(from + 1).startsWith('e')) {
267 *skip = 2;
268 setNormalChar(QChar(0x2265), tail);
269 return true;
270 }
271 break;
272
273 case 'i':
274 x = 0x20;
275 case 'I':
276 if (txt.mid(from + 1).startsWith(QLatin1String("ota"))) {
277 *skip = 4;
278 setNormalChar(QChar(0x399+x), tail);
279 return true;
280 } else if (!upper && txt.mid(from + 1).startsWith(QLatin1String("nf"))) {
281 *skip = 3;
282 setNormalChar(QChar(0x221E), tail);
283 return true;
284 } else if (!upper && txt.mid(from + 1).startsWith(QLatin1String("nt"))) {
285 *skip = 3;
286 setNormalChar(QChar(0x222B), tail);
287 return true;
288 }
289 break;
290
291 case 'l':
292 x = 0x20;
293 case 'L':
294 if (txt.mid(from + 1).startsWith(QLatin1String("ambda"))) {
295 *skip = 6;
296 setNormalChar(QChar(0x39b+x), tail);
297 return true;
298 } else if (txt.mid(from + 1).startsWith(QLatin1String("eq"))) {
299 *skip = 3;
300 setNormalChar(QChar(0x2264), tail);
301 return true;
302 } else if (txt.mid(from + 1).startsWith('e')) {
303 *skip = 2;
304 setNormalChar(QChar(0x2264), tail);
305 return true;
306 }
307 break;
308
309 case 'n':
310 x = 0x20;
311 case 'N':
312 if (txt.mid(from + 1).startsWith('u')) {
313 *skip = 2;
314 setNormalChar(QChar(0x39D+x), tail);
315 return true;
316 } else if (txt.mid(from + 1).startsWith('e')) {
317 *skip = 2;
318 setNormalChar(QChar(0x2260), tail);
319 return true;
320 } else if (interpretNewLine) {
321 *skip = 1;
322 if (!*tail || !(*tail)->text.isEmpty() || (*tail)->locked()) {
323 *tail = new Chunk(*tail, Chunk::None, false, true);
324 }
325 (*tail)->linebreak = true;
326 *tail = new Chunk(*tail, Chunk::None, false, true);
327 return true;
328 } else {
329 *skip = 1;
330 setNormalChar(QChar(0x20), tail);
331 return true;
332 }
333 break;
334
335 case 'o':
336 x = 0x20;
337 case 'O':
338 if (txt.mid(from + 1).startsWith(QLatin1String("verline{"))) {
339 if ((*tail)->group) {
340 *tail = new Chunk(*tail, Chunk::None, false, true);
341 }
342 Chunk *working = new Chunk(*tail, Chunk::None, true, true);
343 dumpattr(working, "start group for overline");
344 uint parseStart = from + 9;
345 working->attributes.overline = true;
346 parseInternal(working, txt, parseStart, txt.length(), interpretNewLine);
347 *skip = parseStart - from + 1;
348 dumpattr(working, "end group for overline");
349 return true;
350 } else if (txt.mid(from + 1).startsWith(QLatin1String("micron"))) {
351 *skip = 7;
352 setNormalChar(QChar(0x39F+x), tail);
353 return true;
354 } else if (txt.mid(from + 1).startsWith(QLatin1String("mega"))) {
355 *skip = 5;
356 setNormalChar(QChar(0x3A9+x), tail);
357 return true;
358 } else if (txt.mid(from + 1).startsWith(QLatin1String("dot"))) {
359 *skip = 4;
360 setNormalChar(QChar(0x2299), tail);
361 return true;
362 }
363 break;
364
365 case 'p':
366 x = 0x20;
367 case 'P':
368 if (txt.mid(from + 1).startsWith('i')) {
369 *skip = 2;
370 setNormalChar(QChar(0x3a0+x), tail);
371 return true;
372 } else if (txt.mid(from + 1).startsWith(QLatin1String("hi"))) {
373 *skip = 3;
374 setNormalChar(QChar(0x3A6+x), tail);
375 return true;
376 } else if (txt.mid(from + 1).startsWith(QLatin1String("si"))) {
377 *skip = 3;
378 setNormalChar(QChar(0x3A8+x), tail);
379 return true;
380 } else if (txt.mid(from + 1).startsWith(QLatin1String("artial"))) {
381 *skip = 7;
382 setNormalChar(QChar(0x2202), tail);
383 return true;
384 } else if (txt.mid(from + 1).startsWith(QLatin1String("rod"))) {
385 *skip = 4;
386 setNormalChar(QChar(0x220F), tail);
387 return true;
388 } else if (txt.mid(from + 1).startsWith('m')) {
389 *skip = 2;
390 setNormalChar(QChar(0xb1), tail);
391 return true;
392 }
393 break;
394
395 case 't':
396 x = 0x20;
397 case 'T':
398 if (txt.mid(from + 1).startsWith(QLatin1String("extcolor{"))) { // \textcolor{color}{text}
399 if ((*tail)->group) {
400 *tail = new Chunk(*tail, Chunk::None, false, true);
401 }
402 Chunk *working = new Chunk(*tail, Chunk::None, true, true);
403 dumpattr(working, "start group for textcolor");
404 uint parseStart = from + 10;
405 int firstSkip = 0;
406 working->attributes.color = parseColor(txt.mid(parseStart), &firstSkip);
407 if (!working->attributes.color.isValid() || txt[parseStart + firstSkip + 1] != '{') {
408 return false;
409 }
410 parseStart += firstSkip + 2;
411 parseInternal(working, txt, parseStart, txt.length(), interpretNewLine);
412 *skip = parseStart - from + 1;
413 dumpattr(working, "end group for textcolor");
414 return true;
415 } else if (txt.mid(from + 1).startsWith(QLatin1String("extbf{"))) {
416 if ((*tail)->group) {
417 *tail = new Chunk(*tail, Chunk::None, false, true);
418 }
419 Chunk *working = new Chunk(*tail, Chunk::None, true, true);
420 dumpattr(working, "start group for textbf");
421 uint parseStart = from + 7;
422 working->attributes.bold = true;
423 parseInternal(working, txt, parseStart, txt.length(), interpretNewLine);
424 *skip = parseStart - from + 1;
425 dumpattr(working, "end group for textbf");
426 return true;
427 } else if (txt.mid(from + 1).startsWith(QLatin1String("extit{"))) {
428 if ((*tail)->group) {
429 *tail = new Chunk(*tail, Chunk::None, false, true);
430 }
431 Chunk *working = new Chunk(*tail, Chunk::None, true, true);
432 dumpattr(working, "start group for textit");
433 uint parseStart = from + 7;
434 working->attributes.italic = true;
435 parseInternal(working, txt, parseStart, txt.length(), interpretNewLine);
436 *skip = parseStart - from + 1;
437 dumpattr(working, "end group for textit");
438 return true;
439 } else if (txt.mid(from + 1).startsWith(QLatin1String("heta"))) {
440 *skip = 5;
441 setNormalChar(QChar(0x398+x), tail);
442 return true;
443 } else if (txt.mid(from + 1).startsWith(QLatin1String("au"))) {
444 *skip = 3;
445 setNormalChar(QChar(0x3A4+x), tail);
446 return true;
447 } else {
448 *skip = 1;
449 if (!*tail || !(*tail)->text.isEmpty() || (*tail)->locked()) {
450 *tail = new Chunk(*tail, Chunk::None, false, true);
451 }
452 (*tail)->tab = true;
453 *tail = new Chunk(*tail, Chunk::None, false, true);
454 return true;
455 }
456 break;
457
458 case 's':
459 x = 0x20;
460 case 'S':
461 if (txt.mid(from + 1).startsWith(QLatin1String("igma"))) {
462 *skip = 5;
463 setNormalChar(QChar(0x3A3+x), tail);
464 return true;
465 } else if (!upper && txt.mid(from + 1).startsWith(QLatin1String("um"))) {
466 *skip = 3;
467 setNormalChar(QChar(0x2211), tail);
468 return true;
469 } else if (!upper && txt.mid(from + 1).startsWith(QLatin1String("qrt"))) {
470 *skip = 4;
471 setNormalChar(QChar(0x221A), tail);
472 return true;
473 }
474 break;
475
476 case 'u':
477 x = 0x20;
478 case 'U':
479 if (txt.mid(from + 1).startsWith(QLatin1String("nderline{"))) {
480 if ((*tail)->group) {
481 *tail = new Chunk(*tail, Chunk::None, false, true);
482 }
483 Chunk *working = new Chunk(*tail, Chunk::None, true, true);
484 dumpattr(working, "start group for underline");
485 uint parseStart = from + 10;
486 working->attributes.underline = true;
487 parseInternal(working, txt, parseStart, txt.length(), interpretNewLine);
488 *skip = parseStart - from + 1;
489 dumpattr(working, "end group for underline");
490 return true;
491 } else if (txt.mid(from + 1).startsWith(QLatin1String("psilon"))) {
492 *skip = 7;
493 setNormalChar(QChar(0x3A5+x), tail);
494 return true;
495 }
496 break;
497
498 default:
499 break;
500 }
501
502 return false;
503 }
504
505
parseInternal(Chunk * ctail,const QString & txt,uint & start,uint cnt,bool interpretNewLine)506 static Chunk *parseInternal(Chunk *ctail, const QString& txt, uint& start, uint cnt, bool interpretNewLine) {
507 Chunk *chead = ctail;
508
509 if (ctail->group) {
510 ctail = new Chunk(ctail, Chunk::None, false, true);
511 }
512 for (uint& i = start; i < cnt; ++i) {
513 QChar c = txt[i];
514 Chunk::VOffset dir = Chunk::Down;
515 switch (c.unicode()) {
516 case '\n':
517 if (ctail->vOffset != Chunk::None && (!ctail->text.isEmpty() || ctail->locked())) {
518 while (ctail->vOffset != Chunk::None && (!ctail->text.isEmpty() || ctail->locked())) {
519 ctail = ctail->prev;
520 }
521 ctail = new Chunk(ctail, Chunk::None, false, true);
522 }
523 if (!ctail->text.isEmpty() || ctail->locked()) {
524 if (ctail->vOffset != Chunk::None) {
525 ctail = new Chunk(ctail->prev, Chunk::None, false, true);
526 } else {
527 ctail = new Chunk(ctail, Chunk::None, false, true);
528 }
529 }
530 ctail->linebreak = true;
531 ctail = new Chunk(ctail, Chunk::None, false, true);
532 break;
533 case '\t':
534 if (!ctail->text.isEmpty() || ctail->locked()) {
535 if (ctail->vOffset != Chunk::None) {
536 ctail = new Chunk(ctail->prev, Chunk::None, false, true);
537 } else {
538 ctail = new Chunk(ctail, Chunk::None, false, true);
539 }
540 }
541 ctail->tab = true;
542 ctail = new Chunk(ctail, Chunk::None, false, true);
543 break;
544 case 0x5c: // \ /**/
545
546 if (ctail->vOffset != Chunk::None && (!ctail->text.isEmpty() || ctail->locked())) {
547 while (ctail->vOffset != Chunk::None && (!ctail->text.isEmpty() || ctail->locked())) {
548 ctail = ctail->prev;
549 }
550 ctail = new Chunk(ctail, Chunk::None, false, true);
551 }
552
553
554 if (ctail->vOffset != Chunk::None && !ctail->text.isEmpty()) {
555 ctail = new Chunk(ctail->prev, Chunk::None, false, true);
556 }
557 ++i;
558 if (i == cnt) {
559 setNormalChar('\\', &ctail);
560 } else {
561 int skip = 0;
562 if (!parseOutChar(txt, i, &skip, &ctail, interpretNewLine)) {
563 setNormalChar(txt[i], &ctail);
564 } else {
565 i += skip - 1;
566 }
567 }
568 break;
569 case 0x5e: // ^
570 dir = Chunk::Up;
571 case 0x5f: // _ (dir is set to Down at beginning of loop)
572 if (ctail->text.isEmpty() && !ctail->group) {
573 setNormalChar(c, &ctail);
574 } else {
575 if (ctail->vOffset != Chunk::None) {
576 if (ctail->vOffset != dir) {
577 ctail = new Chunk(ctail->prev, dir, false, true);
578 } else if (ctail->group) {
579 ctail = new Chunk(ctail, dir, false, true);
580 } else {
581 ctail = new Chunk(ctail, dir, false, true);
582 }
583 } else {
584 ctail = new Chunk(ctail, dir, false, true);
585 }
586 }
587 break;
588 case 0x7b: // {
589 if (ctail->text.isEmpty() && !ctail->group) {
590 bool rc = false;
591 new Chunk(ctail, Chunk::None, true, true);
592 dumpattr(ctail->group, "start group with non-group and empty text");
593 rc = 0L != parseInternal(ctail->group, txt, ++i, cnt, interpretNewLine);
594 assert(rc);
595 dumpattr(ctail->group, "after start group with non-group and empty text");
596 if (!rc) {
597 return 0L;
598 }
599 } else {
600 bool rc = false;
601 if (ctail->vOffset == Chunk::None) {
602 rc = 0L != parseInternal(new Chunk(ctail, Chunk::None, true, true), txt, ++i, cnt, interpretNewLine);
603 } else {
604 rc = 0L != parseInternal(new Chunk(ctail->prev, Chunk::None, true, true), txt, ++i, cnt, interpretNewLine);
605 }
606 if (!rc) {
607 return 0L;
608 }
609 }
610 break;
611 case 0x7d: // }
612 if (chead->prev && chead->prev->group == chead) {
613 return chead;
614 } else {
615 setNormalChar(c, &ctail);
616 }
617 break;
618 case '[':
619 {
620 bool vector = false;
621 int vectorIndexStart = -1;
622 int vectorIndexEnd = -1;
623 int bracketStack = 1;
624 int pos = -1;
625 bool format = false;
626 int formatIndexStart = 0;
627 int formatIndexEnd = 0;
628
629 bool equation = txt[i + 1] == '=';
630 for (uint searchPt = i + 1; bracketStack != 0 && searchPt < cnt; ++searchPt) {
631 if (txt[searchPt] == ']') {
632 if (--bracketStack == 0) {
633 pos = searchPt;
634 break;
635 } else if (bracketStack == 1 && vector && vectorIndexEnd == -1) {
636 vectorIndexEnd = searchPt - 1;
637 }
638 } else if (txt[searchPt] == '[') {
639 ++bracketStack;
640 if (!vector && !equation) {
641 vector = true;
642 vectorIndexStart = searchPt + 1;
643 }
644 }
645 }
646
647 if (pos < 0 || pos == int(i) + 1 /* empty [] */) {
648 return 0L;
649 }
650
651 if (pos+3 < (int)cnt) { // ]{%f} is min size.
652 if ((txt[pos+1]=='{') && ((txt[pos+2] == '%') || (txt[pos+2] == 'T'))) {
653 formatIndexStart = pos+1;
654 for (uint searchPt = pos + 2; searchPt < cnt; ++searchPt) {
655 if (txt[searchPt] == '}') {
656 formatIndexEnd = searchPt;
657 format = true;
658 break;
659 }
660 }
661 }
662 }
663
664 if (ctail->locked() || !ctail->text.isEmpty()) {
665 if (ctail->vOffset != Chunk::None) {
666 ctail = new Chunk(ctail->prev, Chunk::None, false, true);
667 } else {
668 ctail = new Chunk(ctail, Chunk::None, false, true);
669 }
670 }
671
672 if (vector) {
673 ctail->text = txt.mid(i + 1, vectorIndexStart - i - 2).trimmed();
674 ctail->expression = txt.mid(vectorIndexStart, vectorIndexEnd - vectorIndexStart + 1);
675 ctail->vector = true;
676 } else {
677 ctail->text = txt.mid(i + 1, pos - i - 1).trimmed();
678 ctail->scalar = true;
679 }
680 if (format) {
681 i = uint(formatIndexEnd);
682 ctail->formated = true;
683 ctail->format = txt.mid(formatIndexStart+1, formatIndexEnd - formatIndexStart-1);
684
685 } else {
686 i = uint(pos);
687 }
688 }
689 break;
690 default:
691 #if 0
692 if (ctail->vOffset != Chunk::None && (!ctail->text.isEmpty() || ctail->locked())) {
693 ctail = new Chunk(ctail->prev, Chunk::None, false, true);
694 }
695 #endif
696 if (ctail->vOffset != Chunk::None && (!ctail->text.isEmpty() || ctail->locked())) {
697 while (ctail->vOffset != Chunk::None && (!ctail->text.isEmpty() || ctail->locked())) {
698 ctail = ctail->prev;
699 }
700 ctail = new Chunk(ctail, Chunk::None, false, true);
701 }
702 setNormalChar(c, &ctail);
703 break;
704 }
705 }
706
707 return chead;
708 }
709
parse(const QString & txt,const QColor & color,bool interpret,bool interpretNewLine)710 Parsed *Label::parse(const QString& txt, const QColor &color, bool interpret, bool interpretNewLine) {
711 Parsed *parsed = new Parsed;
712 Chunk *ctail = parsed->chunk = new Chunk(0L);
713 ctail->attributes.color = color;
714 if (!interpret) {
715 ctail->text = txt;
716 return parsed;
717 }
718
719 uint start = 0;
720 uint cnt = txt.length();
721 Chunk *rc = parseInternal(ctail, txt, start, cnt, interpretNewLine);
722 if (!rc) {
723 // Parse error - how to recover?
724 delete parsed;
725 parsed = 0L;
726 }
727 return parsed;
728 }
729
730
731 // vim: ts=2 sw=2 et
732