1 // XDrawChem
2 // Copyright (C) 2004-2005  Bryan Herger <bherger@users.sourceforge.net>
3 // Copyright (C) 2020  Yaman Qalieh <ybq987@gmail.com>
4 
5 // This program is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 
15 // You should have received a copy of the GNU General Public License
16 // along with this program.  If not, see <https://www.gnu.org/licenses/>.
17 
18 // text.cpp - Text's implementation of functions
19 
20 // (obsolete note!)
21 // NOTE ABOUT TEXTMASK: + marks superscript, - marks subscript
22 // B marks bold, I marks italic, U marks underline
23 
24 #include <QKeyEvent>
25 
26 #include "defs.h"
27 #include "drawable.h"
28 #include "render2d.h"
29 #include "text.h"
30 
31 // lastflag settings
32 
Text(Render2D * r1,QObject * parent)33 Text::Text(Render2D *r1, QObject *parent) : Drawable(parent) {
34     r = r1;
35 
36     highlighted = false;
37     shiftdown = false;
38     cursor = 0;
39     selectMin = -1;
40     selectMax = -1;
41     molecule = 0;
42     tjustify = TEXT_AUTO;
43     whichside = 1;
44     DataType = TEXT_DATA_NORMAL;
45     shape = 0;
46     shapewidth = -1;
47     shapeheight = -1;
48     oshapewidth = -1;
49     oshapeheight = -1;
50     drawBorder = false;
51     drawFill = false;
52     borderColor = QColor(0, 0, 0);
53     fillColor = QColor(0, 255, 255);
54 }
55 
CloneTo(Drawable * target) const56 Text *Text::CloneTo(Drawable *target) const {
57     if (!target)
58         target = new Text(r, parent());
59 
60     Text *t = static_cast<Text *>(this->Drawable::CloneTo(target));
61     t->displayText = displayText;
62     t->displayTextMask = displayTextMask;
63     t->whichside = whichside;
64     t->font = font;
65     t->justify = justify;
66     t->tjustify = tjustify;
67     t->BBox = BBox;
68     t->cursor = cursor;
69     t->selectMin = selectMin;
70     t->selectMax = selectMax;
71     t->shiftdown = shiftdown;
72     t->DataType = DataType;
73     t->shape = shape;
74     t->drawBorder = drawBorder;
75     t->drawFill = drawFill;
76     t->borderColor = borderColor;
77     t->fillColor = fillColor;
78     t->shapewidth = shapewidth;
79     t->shapeheight = shapeheight;
80     t->oshapewidth = oshapewidth;
81     t->oshapeheight = oshapeheight;
82     // t->molecule = molecule;
83     return t;
84 }
85 
ToXML(QString xml_id)86 QString Text::ToXML(QString xml_id) {
87     QString s, n1;
88     int fsize;
89 
90     fsize = font.pointSize();
91     if (fsize < 1)
92         fsize = font.pixelSize();
93 
94     // begin text
95     s.append("<text id=");
96     s.append(xml_id);
97     s.append(">\n");
98 
99     // write Start
100     s.append("<Start>");
101     n1.setNum(start->x);
102     s.append(n1);
103     s.append(" ");
104     n1.setNum(start->y);
105     s.append(n1);
106     s.append("</Start>\n");
107 
108     // write text
109     s.append("<textstring>");
110     s.append(start->element);
111     s.append("</textstring>\n");
112 
113     // write textmask
114     s.append("<richstring>");
115     s.append(displayText);
116     s.append("</richstring>\n");
117 
118     // shape info
119     s.append("<shape>");
120     n1.setNum(shape);
121     s.append(n1);
122     s.append("</shape>\n");
123 
124     // border info
125     s.append("<border>");
126     if (drawBorder == true)
127         s.append("true");
128     else
129         s.append("false");
130     s.append("</border>\n<bordercolor>");
131     n1.setNum(borderColor.red());
132     s.append(n1);
133     s.append(" ");
134     n1.setNum(borderColor.green());
135     s.append(n1);
136     s.append(" ");
137     n1.setNum(borderColor.blue());
138     s.append(n1);
139     s.append("</bordercolor>\n");
140 
141     // fill info
142     s.append("<fill>");
143     if (drawFill == true)
144         s.append("true");
145     else
146         s.append("false");
147     s.append("</fill>\n<fillcolor>");
148     n1.setNum(fillColor.red());
149     s.append(n1);
150     s.append(" ");
151     n1.setNum(fillColor.green());
152     s.append(n1);
153     s.append(" ");
154     n1.setNum(fillColor.blue());
155     s.append(n1);
156     s.append("</fillcolor>\n");
157 
158     // shape dimensions
159     s.append("<shapegeometry>");
160     n1.setNum(shapewidth);
161     s.append(n1);
162     s.append(" ");
163     n1.setNum(shapeheight);
164     s.append(n1);
165     s.append(" ");
166     n1.setNum(oshapewidth);
167     s.append(n1);
168     s.append(" ");
169     n1.setNum(oshapeheight);
170     s.append(n1);
171     s.append("</shapegeometry>\n");
172 
173     // end text
174     s.append("</text>\n");
175 
176     return s;
177 }
178 
ToCDXML(QString xml_id)179 QString Text::ToCDXML(QString xml_id) {
180     QString s, n1;
181 
182     // begin text
183     s.append("<t id=\"");
184     s.append(xml_id);
185     s.append("\" p=\"");
186     n1.setNum(start->x);
187     s.append(n1);
188     s.append(" ");
189     n1.setNum(start->y);
190     s.append(n1);
191     s.append("\"><s font=\"21\" size=\"10\" face=\"96\">");
192     s.append(start->element);
193     s.append("</s></t>\n");
194 
195     return s;
196 }
197 
FromXML(QString xml_tag)198 void Text::FromXML(QString xml_tag) {
199     int i1, i2;
200 
201     i1 = xml_tag.indexOf("<Start>");
202     i2 = xml_tag.indexOf("</Start>") + 8;
203     SetStartFromXML(xml_tag.mid(i1, i2 - i1));
204     i1 = xml_tag.indexOf("<color>");
205     if (i1 >= 0) {
206         i2 = xml_tag.indexOf("</color>") + 8;
207         SetColorFromXML(xml_tag.mid(i1, i2 - i1));
208     }
209     i1 = xml_tag.indexOf("<font>");
210     if (i1 >= 0) {
211         i2 = xml_tag.indexOf("</font>") + 7;
212         SetFontFromXML(xml_tag.mid(i1, i2 - i1));
213     }
214     i1 = xml_tag.indexOf("<textstring>");
215     i2 = xml_tag.indexOf("</textstring>") + 13;
216     SetTextstringFromXML(xml_tag.mid(i1, i2 - i1));
217     i1 = xml_tag.indexOf("<richstring>");
218     i2 = xml_tag.indexOf("</richstring>") + 13;
219     SetRichstringFromXML(xml_tag.mid(i1, i2 - i1));
220     i1 = xml_tag.indexOf("<textmask>");
221     i2 = xml_tag.indexOf("</textmask>") + 11;
222     SetTextmaskFromXML(xml_tag.mid(i1, i2 - i1));
223     i1 = xml_tag.indexOf("<shape>");
224     if (i1 > 0) {
225         i2 = xml_tag.indexOf("</shape>");
226         shape = xml_tag.mid(i1 + 7, i2 - i1 - 7).toInt();
227         qDebug() << "shape:" << shape;
228     }
229     i1 = xml_tag.indexOf("<border>");
230     if (i1 > 0) {
231         i2 = xml_tag.indexOf("</border>");
232         qDebug() << "border:" << xml_tag.mid(i1 + 8, 4);
233         if (xml_tag.mid(i1 + 8, 4) == "true")
234             drawBorder = true;
235         else
236             drawBorder = false;
237     }
238     i1 = xml_tag.indexOf("<fill>");
239     if (i1 > 0) {
240         i2 = xml_tag.indexOf("</fill>");
241         qDebug() << "fill:" << xml_tag.mid(i1 + 6, 4);
242         if (xml_tag.mid(i1 + 6, 4) == "true")
243             drawFill = true;
244         else
245             drawFill = false;
246     }
247     i1 = xml_tag.indexOf("<bordercolor>");
248     if (i1 >= 0) {
249         i2 = xml_tag.indexOf("</bordercolor>") + 14;
250         SetBorderColorFromXML(xml_tag.mid(i1, i2 - i1));
251     }
252     i1 = xml_tag.indexOf("<fillcolor>");
253     if (i1 >= 0) {
254         i2 = xml_tag.indexOf("</fillcolor>") + 12;
255         SetFillColorFromXML(xml_tag.mid(i1, i2 - i1));
256     }
257     i1 = xml_tag.indexOf("<shapegeometry>");
258     if (i1 >= 0) {
259         i2 = xml_tag.indexOf("</shapegeometry>") + 16;
260         SetShapeGeometryFromXML(xml_tag.mid(i1, i2 - i1));
261     }
262 }
263 
SetBorderColorFromXML(QString xml_tag)264 void Text::SetBorderColorFromXML(QString xml_tag) {
265     qDebug() << "SetBColorFromXML:" << xml_tag;
266     int i1, i2;
267     int d1, d2, d3;
268 
269     i1 = xml_tag.indexOf("<bordercolor>");
270     i2 = xml_tag.indexOf("</bordercolor>");
271     xml_tag.remove(i2, 999);
272     xml_tag.remove(i1, 13);
273 
274     QTextStream ts(&xml_tag, QIODevice::ReadOnly);
275 
276     ts >> d1 >> d2 >> d3;
277 
278     borderColor = QColor(d1, d2, d3);
279 }
280 
SetFillColorFromXML(QString xml_tag)281 void Text::SetFillColorFromXML(QString xml_tag) {
282     qDebug() << "SetFColorFromXML:" << xml_tag;
283     int i1, i2;
284     int d1, d2, d3;
285 
286     i1 = xml_tag.indexOf("<fillcolor>");
287     i2 = xml_tag.indexOf("</fillcolor>");
288     xml_tag.remove(i2, 999);
289     xml_tag.remove(i1, 11);
290 
291     QTextStream ts(&xml_tag, QIODevice::ReadOnly);
292 
293     ts >> d1 >> d2 >> d3;
294 
295     fillColor = QColor(d1, d2, d3);
296 }
297 
SetShapeGeometryFromXML(QString xml_tag)298 void Text::SetShapeGeometryFromXML(QString xml_tag) {
299     qDebug() << "SetSGFromXML:" << xml_tag;
300     int i1, i2;
301     int d1, d2, d3, d4;
302 
303     i1 = xml_tag.indexOf("<shapegeometry>");
304     i2 = xml_tag.indexOf("</shapegeometry>");
305     xml_tag.remove(i2, 999);
306     xml_tag.remove(i1, 15);
307 
308     QTextStream ts(&xml_tag, QIODevice::ReadOnly);
309 
310     ts >> d1 >> d2 >> d3 >> d4;
311 
312     shapewidth = d1;
313     shapeheight = d2;
314     oshapewidth = d3;
315     oshapeheight = d4;
316 }
317 
318 // convert XML <font> tag to QFont and set current
SetFontFromXML(QString xml_tag)319 void Text::SetFontFromXML(QString xml_tag) {
320     qDebug() << "SetFontFromXML:" << xml_tag;
321     int i1, i2;
322 
323     // int d1, d2, d3;
324 
325     i1 = xml_tag.indexOf("<font>");
326     i2 = xml_tag.indexOf("</font>");
327     xml_tag.remove(i2, 999);
328     xml_tag.remove(i1, 6);
329     i1 = xml_tag.indexOf("#");
330     qDebug() << xml_tag.mid(0, i1) << "*" << xml_tag.mid(i1 + 1);
331     font = QFont(xml_tag.mid(0, i1), xml_tag.mid(i1 + 1).toInt());
332 }
333 
334 // get Text::text from <textstring> tag
SetTextstringFromXML(QString xml_tag)335 void Text::SetTextstringFromXML(QString xml_tag) {
336     qDebug() << "SetTextstringFromXML:" << xml_tag;
337     int i1, i2;
338 
339     // int d1, d2, d3;
340 
341     i1 = xml_tag.indexOf("<textstring>");
342     i2 = xml_tag.indexOf("</textstring>");
343     xml_tag.remove(i2, 999);
344     xml_tag.remove(i1, 12);
345     if (start != 0)
346         start->element = xml_tag;
347 }
348 
349 // get Text::displayText from <richstring> tag
SetRichstringFromXML(QString xml_tag)350 void Text::SetRichstringFromXML(QString xml_tag) {
351     qDebug() << "SetRichstringFromXML:" << xml_tag;
352     int i1, i2;
353 
354     // int d1, d2, d3;
355 
356     i1 = xml_tag.indexOf("<richstring>");
357     i2 = xml_tag.indexOf("</richstring>");
358     xml_tag.remove(i2, 999);
359     xml_tag.remove(i1, 12);
360     if (start != 0)
361         displayText = xml_tag;
362 }
363 
364 // get Text::textmask from <textmask> tag
SetTextmaskFromXML(QString xml_tag)365 void Text::SetTextmaskFromXML(QString xml_tag) {
366     // not used!!!  either <richstring> or <elementmask> tag used to set display
367 
368     // qDebug() << "SetTextmaskFromXML:" << xml_tag ;
369 
370     // obsolete tag!  silently update...
371     // note that user may need to reformat text
372     // displayText = start->element;
373     // displayText.replace("\n", "</p>");
374 
375     /*
376        int i1, i2;
377        //int d1, d2, d3;
378 
379        i1 = xml_tag.indexOf("<textmask>");
380        i2 = xml_tag.indexOf("</textmask>");
381        xml_tag.remove(i2, 999);
382        xml_tag.remove(i1, 10);
383        if (start != 0)
384        start->elementmask = xml_tag;
385      */
386 }
387 
isWithinRect(QRect n,bool shiftdown)388 bool Text::isWithinRect(QRect n, bool shiftdown) {
389     if (DPointInRect(start, n))
390         highlighted = true;
391     else
392         highlighted = false;
393     return highlighted;
394 }
395 
Render()396 void Text::Render() {
397     qDebug() << "Text::Render begin";
398     //  r->drawString( "foobar", start->toQPoint(), QColor(0,0,0), QFont() );
399     //  return;
400     qDebug() << "displayText.length():" << displayText.length();
401 
402     bool superFlag = false, subFlag = false;
403     QFont currentFont = font;
404     QColor currentColor = color;
405 
406     if (r->getFontKludge()) {
407         currentFont.setPointSize(currentFont.pointSize() * 2);
408         qDebug() << "fontkludge (top)";
409     }
410 
411     QStringList parseList, renderList, lineWidths;
412 
413     int lineheight = 0, linewidth = 0, textheight = 0, textwidth = 0;
414     int cp1, lp1, lp2;
415     QString n1, currentElement = QString();
416     bool flag1 = false;
417 
418     for (cp1 = 0; cp1 < displayText.length(); cp1++) {
419         flag1 = true;
420         if (displayText[cp1] == '<') {
421             parseList.append(currentElement);
422             currentElement = "<";
423             flag1 = false;
424         }
425         if (displayText[cp1] == '>') {
426             currentElement.append(displayText[cp1]);
427             if (!currentElement.contains("!DOCTYPE")) {
428                 parseList.append(currentElement);
429             }
430             currentElement = "";
431             flag1 = false;
432         }
433         if (flag1) {
434             currentElement.append(displayText[cp1]);
435         }
436     }
437 
438     // just in case there were no tags...
439     if (currentElement.length() > 0)
440         parseList.append(currentElement);
441 
442     // mock render to determine width and height
443     QRect b1;
444     int maxfont = 0, thisfont;
445     bool styletag = false;
446 
447     for (QStringList::Iterator it = parseList.begin(); it != parseList.end(); ++it) {
448         currentElement = *it;
449         // qDebug() << "currentElement:" << currentElement;
450         // dump the style tag!
451         if (currentElement == "</style>") {
452             styletag = false;
453             continue;
454         }
455         if (styletag)
456             continue;
457         if (currentElement.left(6) == "<style") {
458             styletag = true;
459             continue;
460         }
461 
462         if (currentElement == "")
463             continue;
464         if (currentElement == " ")
465             continue;
466         if (currentElement == "\n")
467             continue;
468         if (currentElement == "<p>")
469             continue;
470         if (currentElement == "<html>")
471             continue;
472         if (currentElement == "</html>")
473             continue;
474         if (currentElement == "<head>")
475             continue;
476         if (currentElement == "</head>")
477             continue;
478         if (currentElement == "</body>")
479             continue;
480         if (currentElement.left(3) == "<p ")
481             continue;
482         if (currentElement.left(6) == "<body ")
483             continue;
484         if (currentElement.left(6) == "<meta ")
485             continue;
486         if (currentElement == "</span>") {
487             // this likely undoes a super/sub flag
488             if (superFlag == true)
489                 superFlag = false;
490             if (subFlag == true)
491                 subFlag = false;
492             renderList.append(currentElement);
493             continue;
494         }
495         currentElement.replace("&lt;", "<");
496         currentElement.replace("&gt;", ">");
497         renderList.append(currentElement);
498         if (currentElement == "</p>") {
499             n1.setNum(linewidth);
500             lineWidths.append(n1);
501             if (linewidth > textwidth)
502                 textwidth = linewidth;
503             textheight += lineheight;
504             linewidth = 0;
505             lineheight = 0;
506             continue;
507         }
508         if (currentElement.left(6) == "<span ") {
509             // set font
510             // qDebug() << "span: " << currentElement;
511             superFlag = false, subFlag = false;
512             if (currentElement.contains("font-family") > 0) {
513                 lp2 = currentElement.indexOf("font-family");
514                 lp1 = currentElement.indexOf(":", lp2);
515                 lp2 = currentElement.indexOf(";", lp1);
516                 currentFont.setFamily(currentElement.mid(lp1 + 1, lp2 - lp1 - 1));
517             }
518             if (currentElement.contains("font-size") > 0) {
519                 lp2 = currentElement.indexOf("font-size");
520                 lp1 = currentElement.indexOf(":", lp2);
521                 lp2 = currentElement.indexOf("pt", lp1);
522                 thisfont = currentElement.mid(lp1 + 1, lp2 - lp1 - 1).toInt();
523                 currentFont.setPointSize(thisfont);
524                 if (thisfont > maxfont)
525                     maxfont = thisfont;
526                 if (r->getFontKludge() == true) {
527                     currentFont.setPointSize(currentFont.pointSize() * 2);
528                     qDebug() << "fontkludge";
529                 }
530                 qDebug() << "points: " << currentFont.pointSize();
531             }
532             if (currentElement.contains("font-weight") > 0) {
533                 currentFont.setBold(true);
534             }
535             if (currentElement.contains("italic") > 0) {
536                 currentFont.setItalic(true);
537             }
538             if (currentElement.contains("underline") > 0) {
539                 currentFont.setUnderline(true);
540             }
541             if (currentElement.contains("super") > 0) {
542                 superFlag = true;
543             }
544             if (currentElement.contains("sub") > 0) {
545                 subFlag = true;
546             }
547             continue;
548         }
549         b1 = r->GetTextDimensions(currentElement, currentFont);
550         if (b1.height() > lineheight)
551             lineheight = b1.height();
552         linewidth += b1.width();
553     }
554     // end for?
555 
556     if (lineheight > textheight)
557         textheight = lineheight;
558     if (linewidth > textwidth)
559         textwidth = linewidth;
560 
561     qDebug() << displayText;
562     qDebug() << "Text dimensions = " << textwidth << "," << textheight;
563     // qDebug() << "Text check 1 ---";
564 
565     int supersub, subx;
566     QFont supersubfont;
567     QColor drawcolor;
568 
569     if (highlighted)
570         drawcolor = QColor(255, 0, 0);
571     else
572         drawcolor = color;
573     QRect b(0, 0, textwidth, textheight);
574     QPoint t = GetTopLeftPoint();
575 
576     b.translate(t.x(), t.y());
577     // this guarantees that the text will not overlap other objects
578     // original code
579     r->drawFillBox(b.topLeft(), b.bottomRight(), r->getBGColor(), false, QColor(0, 0, 0), 1);
580 
581     // draw shape, if applicable
582     QPoint shapetl, shapebr, textcenter;
583 
584     oshapewidth = b.width() + maxfont;
585     oshapeheight = b.height() + maxfont;
586     textcenter.setX(b.left() + (b.width() / 2));
587     textcenter.setY(b.top() + (b.height() / 2));
588     int lsw, lsh;
589 
590     if (shapewidth < 0) {
591         lsw = oshapewidth;
592         lsh = oshapeheight;
593     } else {
594         lsw = shapewidth;
595         lsh = shapeheight;
596     }
597     if (shape > 0) {
598         shapetl.setX(textcenter.x() - (lsw / 2));
599         shapebr.setX(textcenter.x() + (lsw / 2));
600         shapetl.setY(textcenter.y() - (lsh / 2));
601         shapebr.setY(textcenter.y() + (lsh / 2));
602         if (shape == TEXT_SHAPE_RECTANGLE) {
603             if (drawFill) {
604                 r->drawFillBox(shapetl, shapebr, fillColor, drawBorder, borderColor, 0);
605             } else {
606                 r->drawBox(shapetl, shapebr, borderColor);
607             }
608         }
609         if (shape == TEXT_SHAPE_SQUARE) {
610             if (drawFill) {
611                 r->drawFillBox(shapetl, shapebr, fillColor, drawBorder, borderColor, 0);
612             } else {
613                 r->drawBox(shapetl, shapebr, borderColor);
614             }
615         }
616         if (shape == TEXT_SHAPE_ELLIPSE) {
617             r->drawEllipse(shapetl, shapebr, drawBorder, borderColor, drawFill, fillColor);
618         }
619         if (shape == TEXT_SHAPE_CIRCLE) {
620             r->drawEllipse(shapetl, shapebr, drawBorder, borderColor, drawFill, fillColor);
621         }
622     }
623     // right-align behavior is different for stand-alone text
624     QTextStream dts(&displayTextMask, QIODevice::ReadOnly);
625     QString displayLine;
626     int in1 = 0;
627 
628     // displayLine = dts.readLine();
629     // in1 = r->GetStringWidth(displayLine, font);
630     // qDebug() << b.width() << ":" << in1 ;
631     // in1 = lineWidths[0].toInt();
632     in1 = b.width() - in1;
633     // bool switchpos = false;
634     QPoint curpos(t.x(), t.y() + r->GetTextHeight(font));
635 
636     if ((tjustify == TEXT_RALIGN) && (justify == JUSTIFY_TOPLEFT))
637         curpos.setX(curpos.x() + in1 + 2);
638     if ((tjustify == TEXT_CALIGN) && (justify == JUSTIFY_TOPLEFT))
639         curpos.setX(curpos.x() + (in1 / 2) + 2);
640     bool smaller_font = false;
641     QPoint startpos = curpos;
642 
643     // actual rendering section
644     // qDebug() << "Text check 2 ---";
645 
646     int lw1 = 1;
647 
648     r->resetTextOrigin();
649     foreach (currentElement, renderList) {
650         if (currentElement == "</span>") {
651             superFlag = false, subFlag = false;
652             continue;
653         }
654         if (currentElement == "</p>") {
655             superFlag = false, subFlag = false;
656             currentFont.setBold(false);
657             currentFont.setItalic(false);
658             currentFont.setUnderline(false);
659             if (justify == JUSTIFY_CENTER)
660                 continue;
661             r->resetTextOrigin();
662             // displayLine = dts.readLine();
663             // in1 = r->GetStringWidth(displayLine, font);
664             // qDebug() << b.width() << ":" << in1 ;
665             if (lw1 < lineWidths.size()) {
666                 in1 = lineWidths[lw1].toInt();
667                 lw1++;
668                 in1 = b.width() - in1;
669                 curpos.setX(t.x());
670                 if ((tjustify == TEXT_RALIGN) && (justify == JUSTIFY_TOPLEFT))
671                     curpos.setX(curpos.x() + in1 + 2);
672                 if ((tjustify == TEXT_CALIGN) && (justify == JUSTIFY_TOPLEFT))
673                     curpos.setX(curpos.x() + (in1 / 2) + 2);
674                 curpos.setY(curpos.y() + r->GetTextFullHeight(font));
675             }
676             continue;
677         }
678 
679         if (currentElement.left(6) == "<span ") {
680             // set font
681             // qDebug() << "span: " << currentElement;
682             superFlag = false, subFlag = false;
683             if (currentElement.contains("font-family") > 0) {
684                 lp2 = currentElement.indexOf("font-family");
685                 lp1 = currentElement.indexOf(":", lp2);
686                 lp2 = currentElement.indexOf(";", lp1);
687                 currentFont.setFamily(currentElement.mid(lp1 + 1, lp2 - lp1 - 1));
688             }
689             if (currentElement.contains("font-size") > 0) {
690                 lp2 = currentElement.indexOf("font-size");
691                 lp1 = currentElement.indexOf(":", lp2);
692                 lp2 = currentElement.indexOf("pt", lp1);
693                 currentFont.setPointSize(currentElement.mid(lp1 + 1, lp2 - lp1 - 1).toInt());
694                 if (r->getFontKludge() == true) {
695                     currentFont.setPointSize(currentFont.pointSize() * 2);
696                     qDebug() << "fontkludge";
697                 }
698             }
699             currentFont.setBold(false);
700             currentFont.setItalic(false);
701             currentFont.setUnderline(false);
702             if (currentElement.contains("font-weight") > 0) {
703                 currentFont.setBold(true);
704             }
705             if (currentElement.contains("italic") > 0) {
706                 currentFont.setItalic(true);
707             }
708             if (currentElement.contains("underline") > 0) {
709                 currentFont.setUnderline(true);
710             }
711             if (currentElement.contains("super") > 0) {
712                 superFlag = true;
713             }
714             if (currentElement.contains("sub") > 0) {
715                 subFlag = true;
716             }
717             currentColor = QColor(0, 0, 0);
718             if (currentElement.contains("color") > 0) {
719                 lp2 = currentElement.indexOf("color");
720                 lp1 = currentElement.indexOf(":", lp2);
721                 currentColor.setNamedColor(currentElement.mid(lp1 + 1, 7));
722             }
723             continue;
724         }
725 
726         supersub = r->GetTextHeight(currentFont) / 2;
727         supersubfont = subscriptFont(currentFont);
728         subx = r->GetTextHeight(supersubfont) / 2;
729 
730         if (highlighted)
731             currentColor = QColor(255, 0, 0);
732 
733         if (superFlag) {
734             r->drawString(currentElement, QPoint(curpos.x(), curpos.y() - supersub), currentColor,
735                           supersubfont);
736             smaller_font = true;
737         }
738         if (subFlag) {
739             r->drawString(currentElement, QPoint(curpos.x(), curpos.y() + subx), currentColor,
740                           supersubfont);
741             smaller_font = true;
742         }
743         if (smaller_font == true) {
744             curpos.setX(curpos.x() + r->GetStringWidth(currentElement, supersubfont));
745         } else {
746             r->drawString(currentElement, curpos, currentColor, currentFont);
747             curpos.setX(curpos.x() + r->GetStringWidth(currentElement, currentFont));
748         }
749         smaller_font = false;
750     }
751     qDebug() << "Text::Render end";
752 }
753 
Type()754 int Text::Type() { return TYPE_TEXT; }
755 
Find(DPoint * target)756 bool Text::Find(DPoint *target) {
757     // if (start == target) return true;
758     return false;
759 }
760 
FindNearestPoint(DPoint * target,double & dist)761 DPoint *Text::FindNearestPoint(DPoint *target, double &dist) {
762     dist = 99999.0;
763     return 0;
764 }
765 
FindNearestObject(DPoint * target,double & dist)766 Drawable *Text::FindNearestObject(DPoint *target, double &dist) {
767     if (WithinBounds(target))
768         dist = 0.01;
769     else
770         dist = 99999.0;
771     return this;
772 }
773 
setPoint(DPoint * s)774 void Text::setPoint(DPoint *s) { start = s; }
775 
WithinBounds(DPoint * target)776 bool Text::WithinBounds(DPoint *target) {
777     QRect b = r->GetTextDimensions(start->element, font);
778     QPoint t = GetTopLeftPoint();
779 
780     b.translate(t.x(), t.y());
781     if ((target->x > b.left()) && (target->x < b.right()) && (target->y > b.top()) &&
782         (target->y < b.bottom()))
783         return true;
784     else
785         return false;
786 }
787 
BoundingBox()788 QRect Text::BoundingBox() {
789     if (highlighted == false)
790         return QRect(QPoint(999, 999), QPoint(0, 0));
791 
792     QTextDocument localtextdocument;
793     localtextdocument.setHtml(start->element);
794     QString tmpline = localtextdocument.toPlainText();
795 
796     qInfo() << "Text::BoundingBox:" << start->element << "||" << tmpline;
797 
798     // QRect b = r->GetTextDimensions( start->element, font );
799     QRect b = r->GetTextDimensions(tmpline, font);
800     QPoint t = GetTopLeftPoint();
801 
802     b.translate(t.x(), t.y());
803     return b;
804 }
805 
806 // return nearest center point to m (see ChemData::AutoLayout)
NearestCenter(QPoint m,int di,int & ns)807 QPoint Text::NearestCenter(QPoint m, int di, int &ns) {
808     QRect b = r->GetTextDimensions(start->element, font);
809     QPoint t = GetTopLeftPoint();
810 
811     b.translate(t.x(), t.y());
812     QPoint c1, cmin;
813     double dist = 9999.0, ndist;
814 
815     if (di == ARROW_VERTICAL) {
816         c1.setX(b.left());
817         c1.setY(b.center().y());
818         ndist = DistanceBetween(m, c1);
819         if (ndist < dist) {
820             cmin = c1;
821             dist = ndist;
822             ns = TEXT_LEFT;
823         }
824         c1.setX(b.right());
825         c1.setY(b.center().y());
826         ndist = DistanceBetween(m, c1);
827         if (ndist < dist) {
828             cmin = c1;
829             dist = ndist;
830             ns = TEXT_RIGHT;
831         }
832     } else { // ARROW_HORIZONTAL
833         c1.setX(b.center().x());
834         c1.setY(b.top());
835         ndist = DistanceBetween(m, c1);
836         if (ndist < dist) {
837             cmin = c1;
838             dist = ndist;
839             ns = TEXT_TOP;
840         }
841         c1.setX(b.center().x());
842         c1.setY(b.bottom());
843         ndist = DistanceBetween(m, c1);
844         if (ndist < dist) {
845             cmin = c1;
846             dist = ndist;
847             ns = TEXT_BOTTOM;
848         }
849     }
850     return cmin;
851 }
852 
DeleteKeyPressed()853 void Text::DeleteKeyPressed() {
854     qDebug() << "Delete key";
855     start->element.remove(cursor, 1);
856     start->elementmask.remove(cursor, 1);
857 }
858 
InsertCharacter(QKeyEvent * k1)859 void Text::InsertCharacter(QKeyEvent *k1) {
860     // if shift pressed, start new selection
861     if (k1->modifiers() == Qt::ShiftModifier) {
862         if (shiftdown == false) {
863             shiftdown = true;
864             selectMin = cursor;
865             selectMax = cursor - 1;
866             qDebug() << cursor << "-" << selectMin << "-" << selectMax;
867         } else {
868             shiftdown = true;
869         }
870     } else {
871         shiftdown = false;
872     }
873 
874     // if return pressed, add newline only if JUSTIFY_TOPLEFT (not a label)
875     if ((k1->key() == Qt::Key_Return) && (justify == JUSTIFY_CENTER))
876         return;
877 
878     if (k1->key() == Qt::Key_Return) {
879         qDebug() << "Return";
880         start->element.insert(cursor, (char)10);
881         // start->element = text;
882         start->elementmask.insert(cursor, (char)10);
883         cursor++;
884         return;
885     }
886     // if left or right arrow pressed with shift, make or extend new selection
887     if ((k1->key() == Qt::Key_Left) && shiftdown) {
888         qDebug() << cursor << "-" << selectMin << "-" << selectMax;
889         if (selectMin > 0) {
890             if (cursor > selectMin)
891                 selectMax--;
892             else
893                 selectMin--;
894         }
895         if (cursor > 0)
896             cursor--;
897         return;
898     }
899     if ((k1->key() == Qt::Key_Right) && shiftdown) {
900         qDebug() << cursor << "-" << selectMin << "-" << selectMax;
901         if (selectMax < start->element.length()) {
902             if (cursor <= selectMax)
903                 selectMin++;
904             else
905                 selectMax++;
906         }
907         if (cursor < start->element.length())
908             cursor++;
909         return;
910     }
911     // if Home or End pressed with shift, make or extend new selection
912     if ((k1->key() == Qt::Key_Home) && shiftdown) {
913         qDebug() << cursor << "-" << selectMin << "-" << selectMax;
914         if (selectMin > 0) {
915             if (cursor > selectMin)
916                 selectMax--;
917             else
918                 selectMin = 0;
919         }
920         if (cursor > 0)
921             cursor = 0;
922         return;
923     }
924     if ((k1->key() == Qt::Key_End) && shiftdown) {
925         qDebug() << cursor << "-" << selectMin << "-" << selectMax;
926         if (selectMax < start->element.length()) {
927             if (cursor <= selectMax)
928                 selectMin++;
929             else
930                 selectMax = start->element.length();
931         }
932         if (cursor < start->element.length())
933             cursor = start->element.length();
934         return;
935     }
936     // if left or right arrow pressed w/o shift, clear selection
937     if (k1->key() == Qt::Key_Left) {
938         if (cursor > 0)
939             cursor--;
940         selectMin = -1;
941         selectMax = -1;
942         return;
943     }
944     if (k1->key() == Qt::Key_Right) {
945         if (cursor < start->element.length())
946             cursor++;
947         selectMin = -1;
948         selectMax = -1;
949         return;
950     }
951     // if Home or End was pressed, go home or end
952     if (k1->key() == Qt::Key_Home) {
953         if (cursor > 0)
954             cursor = 0;
955         selectMin = -1;
956         selectMax = -1;
957         return;
958     }
959     if (k1->key() == Qt::Key_End) {
960         if (cursor < start->element.length())
961             cursor = start->element.length();
962         selectMin = -1;
963         selectMax = -1;
964         return;
965     }
966     if (k1->key() == Qt::Key_Backspace) {
967         if (cursor == 0)
968             return;
969         if ((selectMin >= 0) && (selectMax >= 1)) { // something selected
970             start->element.remove(selectMin, selectMax - selectMin + 1);
971             start->elementmask.remove(selectMin, selectMax - selectMin + 1);
972             // start->element = text;
973             cursor = selectMin;
974             selectMin = -1, selectMax = -1;
975             return;
976         }
977         start->element.remove(cursor - 1, 1);
978         // start->element = text;
979         start->elementmask.remove(cursor - 1, 1);
980         cursor--;
981         return;
982     }
983     // if key > 96 and not already handled, ignore (letters/numbers/space < 96)
984     // note that this breaks international layouts and is probably not needed
985     // if (k1->key() > 96) return;
986     //
987     // this is a more useful test in a Unicode world
988     // qDebug() << "Key text:" << k1->text() << ":";
989     QChar kc1 = k1->text().at(0);
990 
991     if (kc1.isPrint() == false)
992         return;
993     // qDebug() << kc1 << "(key accepted)";
994 
995     // regular letter/number pressed
996     start->element.insert(cursor, k1->text());
997     // start->element = text;
998     start->elementmask.insert(cursor, QChar(' '));
999     cursor++;
1000 }
1001 
InsertString(QString pt)1002 void Text::InsertString(QString pt) {
1003     start->element.insert(cursor, pt);
1004     pt.fill(' ');
1005     start->elementmask.insert(cursor, pt);
1006     cursor += pt.length();
1007 }
1008 
1009 // Superscript selected text
DoSuperscript()1010 void Text::DoSuperscript() {
1011     if (selectMin < 0)
1012         return;
1013     for (int i = selectMin; i < selectMax + 1; i++) {
1014         if (start->elementmask[i] == ' ') {
1015             start->elementmask[i] = '+';
1016             continue;
1017         }
1018         if (start->elementmask[i] == '+') {
1019             start->elementmask[i] = ' ';
1020             continue;
1021         }
1022         if (start->elementmask[i] == '-') {
1023             start->elementmask[i] = '+';
1024             continue;
1025         }
1026     }
1027 }
1028 
1029 // Subscript selected text
DoSubscript()1030 void Text::DoSubscript() {
1031     if (selectMin < 0)
1032         return;
1033     for (int i = selectMin; i < selectMax + 1; i++) {
1034         if (start->elementmask[i] == ' ') {
1035             start->elementmask[i] = '-';
1036             continue;
1037         }
1038         if (start->elementmask[i] == '+') {
1039             start->elementmask[i] = '-';
1040             continue;
1041         }
1042         if (start->elementmask[i] == '-') {
1043             start->elementmask[i] = ' ';
1044             continue;
1045         }
1046     }
1047 }
1048 
1049 // Bold selected text
DoBold()1050 void Text::DoBold() {
1051     if (selectMin < 0)
1052         return;
1053     for (int i = selectMin; i < selectMax + 1; i++) {
1054         if (start->elementmask[i] == ' ') {
1055             start->elementmask[i] = 'B';
1056             continue;
1057         }
1058         if (start->elementmask[i] == 'B') {
1059             start->elementmask[i] = ' ';
1060             continue;
1061         }
1062     }
1063 }
1064 
1065 // Italicize selected text
DoItalic()1066 void Text::DoItalic() {
1067     if (selectMin < 0)
1068         return;
1069     for (int i = selectMin; i < selectMax + 1; i++) {
1070         if (start->elementmask[i] == ' ') {
1071             start->elementmask[i] = 'I';
1072             continue;
1073         }
1074         if (start->elementmask[i] == 'I') {
1075             start->elementmask[i] = ' ';
1076             continue;
1077         }
1078     }
1079 }
1080 
1081 // Underline selected text
DoUnderline()1082 void Text::DoUnderline() {
1083     if (selectMin < 0)
1084         return;
1085     for (int i = selectMin; i < selectMax + 1; i++) {
1086         if (start->elementmask[i] == ' ') {
1087             start->elementmask[i] = 'U';
1088             continue;
1089         }
1090         if (start->elementmask[i] == 'U') {
1091             start->elementmask[i] = ' ';
1092             continue;
1093         }
1094     }
1095 }
1096 
1097 // move cursor to target
MoveCursor(DPoint * target)1098 void Text::MoveCursor(DPoint *target) {
1099     selectMin = -1;
1100     selectMax = -1;
1101     double mindist = 99999.0, ldist;
1102     int newcur;
1103     DPoint *e = new DPoint;
1104 
1105     qDebug() << "Move";
1106     if (WithinBounds(target) == false)
1107         return;
1108     QPoint t = GetTopLeftPoint();
1109     int lx = t.x(), ly = t.y() + (r->GetTextFullHeight(font) / 2);
1110 
1111     for (int i = 0; i < start->element.length(); i++) {
1112         e->x = lx;
1113         e->y = ly;
1114         ldist = e->distanceTo(target);
1115         if (ldist < mindist) {
1116             mindist = ldist;
1117             newcur = i;
1118         }
1119         if (QChar(start->element[i]).digitValue() == 10) {
1120             lx = t.x();
1121             ly = ly + r->GetTextFullHeight(font);
1122         } else {
1123             lx = lx + r->GetCharWidth(start->element[i], font);
1124         }
1125     }
1126     cursor = newcur;
1127     delete e;
1128 }
1129 
1130 // Select text between endpoints e1 and e2
Select(DPoint * e1,DPoint * e2)1131 void Text::Select(DPoint *e1, DPoint *e2) {
1132     if (WithinBounds(e1) == false)
1133         return;
1134     if (WithinBounds(e2) == false)
1135         return;
1136     qDebug() << "select";
1137     double mindist = 99999.0, ldist;
1138     int newcur;
1139     int cr1 = 0, cr2 = 0;
1140     QPoint t;
1141     int i, lx, ly;
1142     DPoint *e = new DPoint;
1143 
1144     t = GetTopLeftPoint();
1145     lx = t.x();
1146     ly = t.y() + (r->GetTextFullHeight(font) / 2);
1147     for (i = 0; i < start->element.length(); i++) {
1148         e->x = lx;
1149         e->y = ly;
1150         ldist = e->distanceTo(e1);
1151         if (ldist < mindist) {
1152             mindist = ldist;
1153             newcur = i;
1154         }
1155         if (QChar(start->element[i]).digitValue() == 10) {
1156             cr1++;
1157             lx = t.x();
1158             ly = ly + r->GetTextFullHeight(font);
1159         } else {
1160             lx = lx + r->GetCharWidth(start->element[i], font);
1161         }
1162     }
1163     selectMin = newcur;
1164     mindist = 99999.0;
1165     t = GetTopLeftPoint();
1166     lx = t.x();
1167     ly = t.y() + (r->GetTextFullHeight(font) / 2);
1168     for (i = 0; i < start->element.length(); i++) {
1169         e->x = lx;
1170         e->y = ly;
1171         ldist = e->distanceTo(e2);
1172         if (ldist < mindist) {
1173             mindist = ldist;
1174             newcur = i;
1175         }
1176         if (QChar(start->element[i]).digitValue() == 10) {
1177             cr2++;
1178             lx = t.x();
1179             ly = ly + r->GetTextFullHeight(font);
1180         } else {
1181             lx = lx + r->GetCharWidth(start->element[i], font);
1182         }
1183     }
1184     selectMax = newcur;
1185     if (selectMin > selectMax) {
1186         int swp = selectMin;
1187 
1188         selectMin = selectMax;
1189         selectMax = swp - 1;
1190     }
1191     qDebug() << selectMin << " " << selectMax;
1192     delete e;
1193 }
1194 
GetTopLeftPoint()1195 QPoint Text::GetTopLeftPoint() {
1196     bool leftcenter = true;
1197 
1198     if (justify == JUSTIFY_TOPLEFT) {
1199         return start->toQPoint();
1200     } else {
1201         QPoint a;
1202         QRect b = r->GetTextDimensions(start->element, font);
1203 
1204         // center on first or last character.  Try to guess what user intended
1205         // (a dangerous idea at best :)
1206         if ((start->element.left(1) == "H") && (start->element.length() > 1))
1207             leftcenter = false;
1208         if ((start->element.left(1) == "+") && (start->element.length() > 1))
1209             leftcenter = false;
1210         if ((start->element.left(1) == "-") && (start->element.length() > 1))
1211             leftcenter = false;
1212         if (start->element.at(0).isNumber() == true)
1213             leftcenter = false;
1214         if (whichside == 2)
1215             leftcenter = false;
1216         if (tjustify == TEXT_LALIGN)
1217             leftcenter = true;
1218         if (tjustify == TEXT_CALIGN)
1219             leftcenter = true;
1220         if (tjustify == TEXT_RALIGN)
1221             leftcenter = false;
1222         if (leftcenter) {
1223             int lc = r->GetCharWidth(start->element.at(0), font);
1224 
1225             a.setX(qRound(start->x - lc / 2.0));
1226         } else {
1227             int rc = r->GetCharWidth(start->element.at(start->element.length() - 1), font);
1228 
1229             a.setX(qRound(start->x - b.width() + rc / 2.0));
1230         }
1231         a.setY(qRound(start->y - b.height() / 2.0));
1232         return a;
1233     }
1234 }
1235 
1236 // position atom labels according to hindrance
CheckAlignment(int alignside)1237 void Text::CheckAlignment(int alignside) {
1238     // 1 = left, 2 = right
1239     whichside = alignside;
1240     qDebug() << "whichside = " << whichside;
1241 }
1242 
ForceAlignment(int alignside)1243 void Text::ForceAlignment(int alignside) { tjustify = alignside; }
1244 
1245 // find next smaller font for subscripts
subscriptFont(QFont f_in)1246 QFont Text::subscriptFont(QFont f_in) {
1247     QFont f_out = f_in;
1248 
1249     if (f_out.pointSize() > 1)
1250         f_out.setPointSize(f_out.pointSize() - 2);
1251     else
1252         f_out.setPixelSize(f_out.pixelSize() - 2);
1253 
1254     return f_out;
1255 }
1256 
UpdateDisplayText()1257 void Text::UpdateDisplayText() {
1258     return;
1259     // this function doesn't do anything now that we're using RichText
1260 
1261     // old code follows
1262 
1263     // use user string if justify chosen
1264     if (tjustify != TEXT_AUTO) {
1265         displayText = start->element;
1266         displayTextMask = start->elementmask;
1267         return;
1268     }
1269     // validate saved text, make sure format is 'appropriate'
1270     if (start->element == "MeO")
1271         start->element = "OMe";
1272 
1273     // decide how to display the text string
1274     if (whichside == 1) {
1275         // cerr << "left hindered (normal) version" ;
1276         displayText = start->element;
1277         displayTextMask = start->elementmask;
1278         return;
1279     }
1280     // create right-aligned version.
1281     // cerr << "right hindered version" ;
1282     if (start->element == "OMe") {
1283         displayText = "MeO";
1284         displayTextMask = "   ";
1285         return;
1286     }
1287     displayText = start->element;
1288     displayTextMask = start->elementmask;
1289 }
1290