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("<", "<");
496 currentElement.replace(">", ">");
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