1 /*
2 * Yudit Unicode Editor Source File
3 *
4 * GNU Copyright (C) 1997-2006 Gaspar Sinai <gaspar@yudit.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License, version 2,
8 * dated June 1991. See file COPYYING for details.
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, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20 #include "swidget/STextView.h"
21 #include "stoolkit/SCluster.h"
22
23 #define DEBUG_SPEED 0
24
25
26 /* I could debug speed of redrawing. Most time (98%) is taken in
27 i += font.draw (c, p, dm, &g.array()[i], g.size()-i); */
28
29 #if DEBUG_SPEED
30 #ifndef USE_WINAPI
31 #include <sys/time.h>
32 #else
33 #include <winsock.h>
34 #include <time.h>
35 #endif
36
37
38 static struct timeval thatTime;
39
40 static void
timerStart()41 timerStart()
42 {
43 gettimeofday (&thatTime, 0);
44 }
45
46 static void
timerStop()47 timerStop()
48 {
49 struct timeval thisTime;
50 gettimeofday (&thisTime, 0);
51 if (thisTime.tv_usec < thatTime.tv_usec)
52 {
53 thisTime.tv_sec--;
54 thisTime.tv_usec+=1000000;
55 }
56 thisTime.tv_sec -= thatTime.tv_sec;
57 thisTime.tv_usec -= thatTime.tv_usec;
58 int msec = (int) thisTime.tv_sec * 1000 + thisTime.tv_usec/1000;
59 fprintf (stderr, "Elapsed time: %d msecs\n", msec);
60 }
61 #endif
62
63
64 static unsigned int sane_index (const SV_UINT& array, unsigned int index);
65
66 /**
67 * The text data is mine. I'll delete it.
68 */
STextView(void)69 STextView::STextView (void)
70 : lrpen (SColor (0.0, 0.0, 0.0, 1.0)),
71 rlpen (SColor (0.0, 0.0, 1.0, 1.0)),
72 underlineColor("red")
73 {
74 isHidingText = false;
75 printerPageSize = 0;
76 highlightMode = "";
77 isWordWrapOn = false;
78 isEditable = false;
79 clipx = clipy = 0;
80 clipw = cliph = 0;
81 fontSize = 16.0;
82 lineend = true;
83 multiline = true;
84 syntax.setTextData (&textData);
85 syntax.addTextDataListener (this);
86 textData.addTextDataListener (this);
87 textData.addLineTracker (&syntax);
88 }
89
90 /**
91 * Make a text-view from external text
92 * @param utf8 is the utf8 ancoded text
93 */
STextView(const SString & utf8)94 STextView::STextView (const SString& utf8)
95 : textData (utf8), lrpen (SColor (0.0, 0.0, 0.0, 1.0)),
96 rlpen (SColor (0.0, 0.0, 1.0, 1.0)),
97 underlineColor("red")
98 {
99 isHidingText = false;
100 printerPageSize = 0;
101 highlightMode = "";
102 isWordWrapOn = false;
103 isEditable = false;
104 clipx = clipy = 0;
105 clipw = cliph = 0;
106 fontSize = 16.0;
107 lineend = true;
108 multiline = true;
109 wrapAndPosition ();
110 syntax.setTextData (&textData);
111 syntax.addTextDataListener (this);
112 textData.addTextDataListener (this);
113 textData.addLineTracker (&syntax);
114 textData.clearEvent();
115 }
116
117 /**
118 * Set the text and
119 * Do the reordering, expanding for the whole text.
120 * Not very efficient with large text.
121 */
122 void
setText(const SString & text)123 STextView::setText(const SString& text)
124 {
125 textData.clear();
126 textData.fireEvent();
127 textData.insert(text);
128
129 /* HACK FOR LABELS - they neeed to know their exact size */
130 for (unsigned int i=0; i<textData.size(); i++)
131 {
132 textData.setVisible(i);
133 textData.setReordered(i);
134 }
135 wrapAndPosition();
136 textData.fireEvent();
137 }
138
~STextView()139 STextView::~STextView ()
140 {
141 }
142
143 void
setClippingArea(int _x,int _y,unsigned int _width,unsigned int _height)144 STextView::setClippingArea (int _x, int _y,
145 unsigned int _width, unsigned int _height)
146 {
147 clipx = _x;
148 clipy = _y;
149 clipw = _width;
150 cliph = _height;
151 }
152
153 /**
154 * Set the font and recalculate sizes.
155 * @param _font is the new font.
156 */
157 void
setFont(const SString & _font,double _fontSize)158 STextView::setFont (const SString& _font, double _fontSize)
159 {
160 if (_fontSize > 0.0) fontSize = _fontSize;
161 font = SFont(_font, fontSize);
162 setPen ();
163 setReordered ();
164
165 lineHeight = (unsigned int ) (font.ascent() + font.descent() + font.gap());
166 lineAscent = (unsigned int) font.ascent();
167 preferredSize.height = (textData.size()==0) ? lineHeight
168 : textData.size() * lineHeight;
169 }
170
171 /**
172 * Set the size of the font.
173 * @param size is the size of the font.
174 */
175 void
setFontSize(double newsize)176 STextView::setFontSize (double newsize)
177 {
178 font.setSize(newsize);
179 setPen ();
180 setReordered ();
181
182 lineHeight = (unsigned int ) (font.ascent() + font.descent() + font.gap());
183 lineAscent = (unsigned int) font.ascent();
184 preferredSize.height = (textData.size()==0) ? lineHeight
185 : textData.size() * lineHeight;
186 }
187
188 /**
189 * Set the pen size according to font size.
190 */
191 void
setPen()192 STextView::setPen ()
193 {
194 double pensize = 1.0;
195 double pointsize = font.getSize();
196 if (pointsize <= 16)
197 {
198 pensize = 0.125;
199 }
200 else if (pointsize <=24)
201 {
202 pensize = 0.25;
203 }
204 else if (pointsize <=80)
205 {
206 pensize = 0.5;
207 }
208 else if (pointsize <=100)
209 {
210 pensize = 0.75;
211 }
212 lrpen.setLineWidth (pensize);
213 rlpen.setLineWidth (pensize);
214 }
215
216 /**
217 * Set the text alignment align is true if it is right aligned.
218 */
219 void
setAlignment(bool align)220 STextView::setAlignment (bool align)
221 {
222 alignment = align;
223 }
224 /**
225 */
226 void
setMultiline(bool _multiline)227 STextView::setMultiline (bool _multiline)
228 {
229 multiline = _multiline;
230 setReordered ();
231 }
232 /**
233 */
234 bool
isMultiline() const235 STextView::isMultiline () const
236 {
237 return multiline;
238 }
239 /**
240 * Set the viewport. This is the location that casts to {0;0}
241 * @param _viewPort is the new viewport.
242 */
243 void
setViewPort(const SLocation & _viewPort)244 STextView::setViewPort (const SLocation& _viewPort)
245 {
246 viewPort = _viewPort;
247 }
248
249 const SLocation&
getViewPort()250 STextView::getViewPort()
251 {
252 return viewPort;
253 }
254
255 /**
256 * Resize the component and redraw.
257 */
258 void
resize(const SDimension & _dimension)259 STextView::resize (const SDimension& _dimension)
260 {
261 SComponent::resize (_dimension);
262 setReordered ();
263 }
264
265 unsigned int
getLineIndex(int locy)266 STextView::getLineIndex (int locy)
267 {
268 /* binary search linespan */
269 unsigned int top;
270 unsigned int bottom;
271 unsigned int mid;
272 top = lineSpan.size();
273 bottom = 0;
274 int y = locy - (location.y + viewPort.y) + 1;
275 while (top > bottom)
276 {
277 mid = (top+bottom)/2;
278 unsigned int sindex = lineHeight * sane_index (lineSpan, mid+1);
279 if (y == (int) (sindex))
280 {
281 top = mid;
282 break;
283 }
284 if (y < (int) (sindex))
285 {
286 top = mid;
287 continue;
288 }
289 bottom = mid + 1;
290 }
291 return top;
292 }
293
294 /**
295 * @param l is the location on the canvas.
296 */
297 SCursorIndex
getCursorIndex(const SLocation & l)298 STextView::getCursorIndex (const SLocation& l)
299 {
300 unsigned int line = getLineIndex (l.y);
301
302 if (line >= textData.size())
303 {
304 if (line > 0 && !textData.isProperLine (line-1))
305 {
306 return SCursorIndex (line-1, textData.size(line-1));
307 }
308 return SCursorIndex (line, 0);
309 }
310
311
312 SV_UINT brk;
313 if (line < textData.size())
314 {
315 setVisible (line);
316 brk = breaks[line];
317 }
318
319 /* Linear search. I don't expect long lines. */
320 unsigned int offset = lineHeight * sane_index (lineSpan, line);
321
322 SLocation lm (0, viewPort.y-location.y+(int)offset);
323 bool lr = textData.isLR (line);
324
325 /* Transpose to LR */
326 lm.x = l.x-location.x+viewPort.x;
327
328 /* convert as if it were lr */
329 if (!lr) lm.x = (int)size.width - (int)lm.x;
330
331 /* find where the glyph starts */
332 unsigned int i;
333 unsigned int currExt=0;
334 for (i=0; i<brk.size(); i++)
335 {
336 lm.y += lineHeight;
337 if (lm.y > l.y) break;
338 currExt = brk[i];
339 }
340 unsigned int si = textData.size(line);
341 unsigned int nextExt = si;
342 if (i<brk.size()) nextExt = brk[i];
343 // SGC bool endsline = false;
344
345 /* find the x position witihn the line */
346 SCursorIndex cindex (line, currExt);
347 bool llr = textData.isLR(line);
348 bool before = true;
349 for (i=currExt; i<nextExt && i<si; i++)
350 {
351 unsigned int begpos = posBefore[line][i];
352 unsigned int endpos = posAfter[line][i];
353 unsigned int midpos = (begpos+endpos) / 2;
354
355 const SGlyph& g = textData.glyphAt (STextIndex (line, i));
356 bool glr = g.isLR();
357 before = ((glr && llr) | (!glr && !llr));
358
359 if (g.isEOP())
360 {
361 // SGC endsline = true;
362 }
363 else if (lm.x >= (int)begpos && lm.x < (int)midpos)
364 {
365 cindex = SCursorIndex(line, i, before); /* logical before */
366 break;
367 }
368 else if (lm.x >= (int)midpos && lm.x < (int)endpos)
369 {
370 cindex = SCursorIndex(line, i, !before); /* logical after */
371 break;
372 }
373 }
374 if (i==si && lm.x > 0 && si > 0)
375 {
376 /* rl is initialized already */
377 cindex = SCursorIndex(line, i, before);
378 }
379 /* beginning or found */
380 return SCursorIndex(cindex);
381 }
382
383 SLocation
getCursorLocation(const SCursorIndex & cursorIndex)384 STextView::getCursorLocation (const SCursorIndex& cursorIndex)
385 {
386 return SLocation(getTextLocation (cursorIndex.textIndex, cursorIndex.before));
387 }
388
389 /**
390 * Convert a line and index to a location on the screen
391 * This is the top left(lr) or right(rl) corner of the glyph.
392 * @param line is the line number.
393 * @param index is the index.
394 * @param before is true if we need the index before the glyph -
395 * logical meaning.
396 * Here is an LR glyph:
397 * Paragraph LR A A Paragrph RL
398 * ^ ^ ^ ^
399 * | | | |
400 * Before ----+ +--- After Before ---+ +---- After
401 */
402 SLocation
getTextLocation(const STextIndex & textIndex,bool before)403 STextView::getTextLocation (const STextIndex& textIndex, bool before)
404 {
405 unsigned int line = textIndex.line;
406 unsigned int index = textIndex.index;
407 unsigned int ll = sane_index (lineSpan, line);
408 SLocation ret;
409 if (line >= textData.size())
410 {
411 ret = SLocation (location.x+viewPort.x,
412 location.y+viewPort.y + lineHeight * ll);
413 }
414 else
415 {
416 setVisible (line);
417 SV_UINT brk = breaks[line];
418 /* It may be second or third line. */
419 unsigned int i = 0;
420 int yoffset = 0;
421 unsigned int posafter = 0;
422 for (i=0; i<brk.size()-1; i++)
423 {
424 if (index < brk[i]) break;
425 posafter = brk[i];
426 yoffset++;
427 }
428
429 int si = 0;
430 if (index < textData.size(line))
431 {
432 /* convert logical 'before' to phisical */
433 bool glr = textData.isLR(STextIndex(line, index));
434 bool llr = textData.isLR(line);
435
436 /* paragraph directionality is different from glyph directionality */
437 bool swap = ((llr && !glr) || (!llr && glr));
438
439 si = ((before && !swap) || (!before && swap))
440 ? (int) posBefore[line][index]
441 : (int) posAfter[line][index];
442
443 }
444 else if (index > textData.size(line) && textData.isProperLine(line))
445 {
446 /* last one */
447 si = 0;
448 yoffset++;
449 }
450 else /* Streched beyond the last one. */
451 {
452 SV_UINT v = posAfter[line];
453 unsigned int max = 0;
454 unsigned int maxi = 0;
455 for (unsigned int i=posafter; i<v.size(); i++)
456 {
457 if (v[i] > max)
458 {
459 max = v[i];
460 maxi = i;
461 }
462 }
463 si = posAfter[line][maxi];
464 }
465 ret = SLocation (location.x+si+viewPort.x,
466 location.y+viewPort.y + lineHeight * (ll+yoffset));
467 }
468 bool lr = textData.isLR (line);
469 int nloc = (lr) ? ret.x
470 : 2 * location.x + (int)size.width - (int)ret.x - 2 * viewPort.x;
471 return SLocation(nloc, ret.y);
472 }
473
474 /**
475 * This is coming from the SWindowListener
476 * @param c is the canvas to draw on.
477 * @param x is the upper lect corner.
478 * @param y is the upper lect corner.
479 * @param width is the width of this event.
480 * @param height is the height of this event.
481 */
482 void
redraw(SCanvas * c,int x,int y,unsigned int width,unsigned int height)483 STextView::redraw (SCanvas *c, int x, int y,
484 unsigned int width, unsigned int height)
485 {
486 internalRedraw (c, x, y, width, height);
487 }
488
489 /**
490 * This is coming from the SWindowListener
491 * @param c is the canvas to draw on.
492 * @param x is the upper lect corner.
493 * @param y is the upper lect corner.
494 * @param width is the width of this event.
495 * @param height is the height of this event.
496 */
497 void
internalRedraw(SCanvas * c,int x,int y,unsigned int width,unsigned int height)498 STextView::internalRedraw (SCanvas *c, int x, int y,
499 unsigned int width, unsigned int height)
500 {
501 #if DEBUG_SPEED
502 x = getLocation ().x;
503 y = getLocation ().y;
504 width = getSize().width;
505 height = getSize().height;
506 timerStart();
507 #endif
508 SLocation lb (x, y);
509 SLocation le (x+width, y+height);
510
511 unsigned int line = getLineIndex (lb.y);
512
513 bool islr = textData.isLR (line);
514 SLocation lstart = lb;
515 if (!islr)
516 {
517 lstart.x = le.x;
518 }
519
520 SLocation lleft(location.x+viewPort.x,
521 location.y+viewPort.y + (int) lineHeight
522 * (int) sane_index (lineSpan, line));
523 SLocation lright(location.x+viewPort.x+(int)size.width, lleft.y);
524
525 unsigned int i;
526
527 // expand +/- SD_PRE_EXPAND lines by calling textData.size(i).
528 unsigned int start = (line > SD_PRE_EXPAND) ? line-SD_PRE_EXPAND : 0;
529 for (i=start; i<textData.size(); i++)
530 {
531 // dummy is never negative.
532 textData.size(i);
533 if (i>line+SD_PRE_EXPAND) break;
534 }
535
536 for (i=line; i<textData.size(); i++)
537 {
538 textData.size(i);
539 setVisible (i);
540 islr = textData.isLR (i);
541 unsigned int cs = islr
542 ? drawParagraph (c, islr, i, lleft, lb, le, false)
543 : drawParagraph (c, islr, i, lright, lb, le, false);
544 lleft.y = lleft.y + lineHeight * cs;
545 lright.y = lleft.y;
546 if (lleft.y > location.y + (int) size.height ) break;
547 if (lleft.y > y + (int)height) break;
548 }
549
550 #if DEBUG_SPEED
551 timerStop();
552 #endif
553 }
554
555 /**
556 * Draw a whole line of glyphs.
557 * @param c is the canvas to draw to
558 * @param islr is true if we draw from left to right.
559 * @param line is the line index to draw.
560 * @param l is the beginning upper corner location
561 * @param lb is the beginning exposure
562 * @param le is the end exposure
563 * @param iswindow is true if we want to set the clipping area
564 * this is only if you want to experiment - we dont want to do that.
565 * @return the number of lines drawn.
566 */
567 unsigned int
drawParagraph(SCanvas * c,bool islr,unsigned int line,const SLocation & l,const SLocation & lb,const SLocation & le,bool iswindow)568 STextView::drawParagraph (SCanvas* c, bool islr, unsigned int line,
569 const SLocation& l, const SLocation& lb, const SLocation& le, bool iswindow)
570 {
571 SV_UINT br;
572 if (line < breaks.size()) br = breaks[line];
573 unsigned int currExt = 0;
574 SLocation lm = l;
575 unsigned int ls = textData.size(line);
576 unsigned int mycliph = 0;
577
578 /**
579 * set clip to line so that we won't overflow...
580 */
581 if (iswindow && clipw != 0 && cliph != 0)
582 {
583 int myclipy0 = (clipy > lm.y) ? clipy : lm.y;
584 int myclipy1 = myclipy0 + lineHeight;
585 if (myclipy1 > clipy + (int) cliph)
586 {
587 myclipy1 = clipy + (int) cliph;
588 }
589 mycliph = (myclipy1 > myclipy0) ? myclipy1-myclipy0 : 0;
590 //fprintf (stderr, "clip=%d,%d wh=%u,%u\n", clipx, myclipy0, clipw, mycliph);
591 if (mycliph)
592 {
593 ((SWindow*)c)->setClippingArea (clipx, myclipy0, clipw, mycliph);
594 }
595 }
596 for (unsigned int i=0; i<ls; i++)
597 {
598 /* move the clipping area */
599 if (br[currExt] == i)
600 {
601 // printer can have same extent
602 while (br[currExt] == i)
603 {
604 currExt++;
605 if (currExt >= br.size()) break;
606 }
607
608 lm.y = lineHeight * currExt + l.y;
609 if (iswindow && clipw != 0 && cliph != 0)
610 {
611 int myclipy0 = (clipy > lm.y) ? clipy : lm.y;
612 int myclipy1 = myclipy0 + lineHeight;
613 if (myclipy1 > clipy + (int) cliph)
614 {
615 myclipy1 = clipy + (int) cliph;
616 }
617 mycliph = (myclipy1 > myclipy0) ? myclipy1-myclipy0 : 0;
618 if (mycliph)
619 {
620 ((SWindow*)c)->setClippingArea (clipx, myclipy0, clipw, mycliph);
621 }
622 }
623 }
624 lm.x = islr
625 ? l.x + (int) posBefore[line][i]
626 : l.x-1-(int) posAfter[line][i];
627
628 unsigned int e = posAfter[line][i] - posBefore[line][i];
629
630 /* is it drawable ? */
631 if (lm.x < le.x && lb.x < lm.x + (int) e
632 && lm.y < le.y && lb.y < lm.y + (int) lineHeight)
633 {
634 if (!iswindow || mycliph) drawGlyph (c, lm, e, STextIndex (line,i));
635 }
636 }
637 if (iswindow && clipw!=0 && cliph !=0)
638 {
639 ((SWindow*)c)->setClippingArea (clipx, clipy, clipw, cliph);
640 }
641 return currExt+1;
642 }
643
644 /**
645 * Set Syntax Hilight Mode
646 */
647 void
setSyntax(const SString & hlm)648 STextView::setSyntax (const SString& hlm)
649 {
650 highlightMode = hlm;
651 // these are kept for backward compatibility, hard coded.
652 if (hlm == "simple" || hlm == "simple-dark" || hlm == "none")
653 {
654 syntax.setSyntax ("");
655 }
656 else
657 {
658 syntax.setSyntax (hlm);
659 }
660 }
661
662 /**
663 * Get Syntax Hilight Mode
664 */
665 const SString&
getSyntax() const666 STextView::getSyntax () const
667 {
668 return highlightMode;
669 }
670
671 void
setSyntaxColors(const SSyntaxColors & attr)672 STextView::setSyntaxColors (const SSyntaxColors& attr)
673 {
674 syntaxColors = attr;
675 }
676
677 const SSyntaxColors&
getSyntaxColors() const678 STextView::getSyntaxColors () const
679 {
680 return syntaxColors;
681 }
682
683 /**
684 * Set WordWrap Mode
685 * @param pbm is true if line break is on
686 */
687 void
setWordWrap(bool lbm)688 STextView::setWordWrap (bool lbm)
689 {
690 isWordWrapOn = lbm;
691 setReordered ();
692 }
693
694 /**
695 * Some stuff displays differently if this is editable.
696 */
697 void
setEditable(bool editable)698 STextView::setEditable (bool editable)
699 {
700 isEditable = editable;
701 setReordered ();
702 }
703
704 /**
705 * Get WordWrap Mode
706 * @return true if line break is on.
707 */
708 bool
getWordWrap() const709 STextView::getWordWrap () const
710 {
711 return isWordWrapOn;
712 }
713
714 /**
715 * Syntax Highlighting system.
716 * Added by Maarten van Gompel <proycon@anaproy.homeip.net>
717 * Note: this is a dumb system and merely colors single characters.
718 * For error highlighting, set forground to NONE and error to true.
719 */
720 void
syntaxHighlight(STextIndex index,SPen * pen,bool * isError)721 STextView::syntaxHighlight(STextIndex index, SPen* pen, bool *isError)
722 {
723 *isError = false;
724 if (highlightMode == "simple")
725 {
726 const SGlyph& g = textData.glyphAt (index);
727 if (g.isLetter ())
728 {
729 }
730 else if (g.isNumber())
731 {
732 pen->setForeground(SColor("orange"));
733 }
734 // emoji
735 else if (g.getType() == SD_CC_So)
736 {
737 pen->setForeground(SColor("gray90"));
738 }
739 else // not a letter, nor a number
740 {
741 pen->setForeground(SColor("CornflowerBlue"));
742 }
743 if (getLigatureScriptCode (g.getChar()) == SD_AS_LITERAL) *isError = true;
744 }
745 else if (highlightMode == "simple-dark")
746 {
747 const SGlyph& g = textData.glyphAt (index);
748 if (g.isLetter ())
749 {
750 }
751 else if (g.isNumber())
752 {
753 pen->setForeground(SColor("orange4"));
754 }
755 // emoji
756 else if (g.getType() == SD_CC_So)
757 {
758 pen->setForeground(SColor("gray20"));
759 }
760 else // not a letter, nor a number
761 {
762 pen->setForeground(SColor("DeepSkyBlue4"));
763 }
764 if (getLigatureScriptCode (g.getChar()) == SD_AS_LITERAL) *isError = true;
765 }
766 else if (highlightMode != "none" && highlightMode != "")
767 {
768
769 SSyntax::SS_Tag tag = syntax.getTagByTDI (index);
770 const SGlyph& g = textData.glyphAt (index);
771 if (getLigatureScriptCode (g.getChar()) == SD_AS_LITERAL)
772 {
773 *isError = true;
774 tag = SSyntax::SD_CONTROL;
775 SColor c = syntaxColors.colors[(unsigned int) tag];
776 pen->setForeground(c);
777 }
778 // SD_ERROR and SD_NONE is preserving old color
779 else if (tag == SSyntax::SD_ERROR)
780 {
781 // SColor c = syntaxColors.colors[(unsigned int) SSyntax::SD_NONE];
782 // pen->setForeground(c);
783 *isError = true;
784 }
785 else if (tag == SSyntax::SD_NONE)
786 {
787 // preserve original color
788 }
789 else
790 {
791 SColor c = syntaxColors.colors[(unsigned int) tag];
792 pen->setForeground(c);
793 }
794 }
795 return;
796 }
797
798 /**
799 * Draw one signle glyph on the screen.
800 * @param c is the canvas to draw to
801 * @param l is the location of the glyph.
802 * @return the length of the text drawn
803 */
804 void
drawGlyph(SCanvas * c,SLocation & l,unsigned int ext,STextIndex index)805 STextView::drawGlyph (SCanvas* c,
806 SLocation& l, unsigned int ext, STextIndex index)
807 {
808 const SGlyph& g = textData.glyphAt (index);
809
810 SS_Matrix2D dm;
811 dm.y1 = -dm.y1; /* updown */
812
813
814 dm.translate (0, font.ascent ());
815 dm.translate ((double)l.x, (double)l.y);
816
817 if (isHidingText){
818 SColor fg = lrpen.getForeground();
819 c->bitfill (fg, l.x, l.y, ext, lineHeight);
820 return;
821 }
822
823 SPen p (lrpen);
824 if (!g.isLR())
825 {
826 p = rlpen;
827 }
828
829 unsigned int explevel = g.getExplicitLevel();
830 bool isError = false;
831 if (g.selected)
832 {
833 //SColor fg = p.getForeground();
834 //SColor bg = p.getBackground();
835 SColor fg = SColor("DeepSkyBlue4");
836 SColor bg = SColor("white");
837 p.setForeground (bg);
838 p.setBackground (fg);
839 c->bitfill (fg, l.x, l.y, ext, lineHeight);
840 }
841 /* fade background according to embed level */
842 else if (isEditable && explevel!=0)
843 {
844 SColor bg = p.getBackground();
845 if (explevel > 5) explevel = 5; /* 5 shades max*/
846 /* This funny linear curve is the result of experiments */
847 double alpha = 1.0/2.0 + (1.0/2.0) * 0.9 * ((double)explevel)/5.0;
848 /* we fade gray in with an alpha */
849 double cg = 0.5;
850 SColor grey(cg,cg,cg, alpha);
851 bg.blend (grey);
852 p.setBackground (bg);
853 c->bitfill (bg, l.x, l.y, ext, lineHeight);
854 }
855 if (!g.selected)
856 {
857 /*
858 * Syntax highlighting, an addition
859 * by Maarten van Gompel <proycon@anaproy.homeip.net>
860 */
861 syntaxHighlight(index, &p, &isError); /* change pen color if necessary */
862 }
863
864 if (!lineend && g.isEOP()) return;
865 SS_UCS4 fc = g.getFirstChar();
866 /* I would just check for SD_CC_Mn also */
867 if (!isEditable && (g.isEOL() || fc == SD_CD_LRM || fc == SD_CD_RLM
868 || fc == SD_CD_ZWNJ || fc == SD_CD_ZWJ))
869 {
870 return;
871 }
872 /* Zero width space */
873 if (fc == SD_CD_ZWSP) return;
874
875 if (!g.isTab()) font.draw (c, p, dm, g);
876
877 if (g.underlined)
878 {
879 unsigned int w = ext;
880 unsigned int h = lineHeight/24+1;
881 unsigned int base = (lineAscent + h >= lineHeight) ?
882 lineHeight -1 : lineAscent + h;
883 /* construct a square */
884 // changed in 2.8.2
885 //c->bitfill (underlineColor, l.x, l.y + (int) base - h, w, h);
886 c->bitfill (underlineColor, l.x, l.y + (int) base, w, h);
887 }
888 else if(isError)
889 {
890 SColor errColor = syntaxColors.colors[(unsigned int) SSyntax::SD_ERROR];
891 unsigned int w = ext;
892 unsigned int h = 3; // height occupies 3 pixels
893 unsigned int base = (lineAscent + h >= lineHeight) ?
894 lineHeight - 1 : lineAscent + h;
895 int screenBase = l.y + (int) base;
896 SBinVector<int> x;
897 SBinVector<int> y;
898 for (unsigned i=0; i<w; i++)
899 {
900 x.append (l.x + i);
901 int state = ((l.x+i+clipx) % 6);
902 switch (state)
903 {
904 case 0: y.append (screenBase); break;
905 case 1: y.append (screenBase-1); break;
906 case 2: y.append (screenBase-1); break;
907 case 3: y.append (screenBase); break;
908 case 4: y.append (screenBase+1); break;
909 case 5: y.append (screenBase+1); break;
910 default: y.append (screenBase);
911 }
912 }
913 if (x.size() > 0)
914 {
915 c->bitpoints (errColor, x.array(), y.array(), x.size());
916 }
917 }
918 }
919
920 /**
921 * This can be called by the STextData and SSyntax.
922 */
923 void
textChanged(void * src,const STextDataEvent & unparsedEvent)924 STextView::textChanged (void* src, const STextDataEvent& unparsedEvent)
925 {
926 if (src == &syntax && unparsedEvent.attribute)
927 {
928 STextIndex tb = unparsedEvent.start;
929 STextIndex te = unparsedEvent.remaining;
930 // convert te back to text data coords
931 te.line = te.line >= textData.size() ? 0 : textData.size() - te.line;
932 // Filter out visible range
933 unsigned int firstVisible = getLineIndex (0);
934 int height = (int) getSize().height;
935 int width = (int) getSize().width;
936 unsigned int lastVisible = getLineIndex (height);
937 // filter out nont visible portion
938 if (te.line < firstVisible) return;
939 if (tb.line > lastVisible+1) return;
940
941 if (tb.line < firstVisible) tb.line = firstVisible;
942 tb.index = 0;
943 // non-inclusive
944 if (te.line > lastVisible) te.line = lastVisible+1;
945 te.index = 0;
946 // calculate the screen index, and do a redraw
947 if (te.line > textData.size()) te.line = textData.size();
948 if (tb.line > textData.size()) tb.line = textData.size();
949 int bcoord = (int) lineHeight * sane_index (lineSpan, tb.line);
950 int ecoord = (int) lineHeight * sane_index (lineSpan, te.line);
951 // as we scroll down viewPort.y becomes negative.
952 // y coord of top of starting line.
953 bcoord += (location.y + viewPort.y) - 1;
954 // y coord of top of ending line.
955 ecoord += (location.y + viewPort.y) - 1;
956 SWindow* w = getWindow ();
957 if (w && ecoord > bcoord && bcoord < height && ecoord > 0)
958 {
959 /* request a redraw and clear the whole area + overdraw */
960 w->redraw (true, 0, bcoord, width, ecoord-bcoord + 2);
961 //fprintf (stderr, "redraw 0,%d %u,%u\n", bcoord, getSize().width, ecoord-bcoord);
962 //fprintf (stderr, "tb=%u te=%u\n", tb.line, te.line);
963 }
964 }
965 else
966 {
967 textChangedInternal (src, unparsedEvent);
968 }
969 }
970
971 void
textChangedInternal(void * src,const STextDataEvent & event)972 STextView::textChangedInternal (void* src, const STextDataEvent& event)
973 {
974 /* The whole text has been cleared */
975 if (textData.size()==0)
976 {
977 wrapAndPosition();
978 SWindow* w = getWindow ();
979 if (w)
980 {
981 /* request a redraw and clear the whole area */
982 w->redraw (true, location.x, location.y, size.width, size.height);
983 }
984 return;
985 }
986 /* overdraw */
987 int odw = (int) lineHeight / 3 + 1;
988 STextIndex tb = textData.getMinTextIndex (event);
989 STextIndex te = textData.getMaxTextIndex (event);
990
991
992 unsigned int oldsize = lineSpan.size();
993 unsigned int oldspan = sane_index (lineSpan, oldsize);
994 bool oldlr = textData.isLR(tb.line);
995
996 SV_UINT oldbreaks;
997
998 if (tb.line == te.line && tb.line < oldsize && tb.line < breaks.size())
999 {
1000 /* This is still the old breaks */
1001 oldbreaks = breaks[tb.line];
1002 }
1003
1004 /* change in text contents */
1005 SV_UINT mapBefore = textData.getLogicalMap(tb.line);
1006 SV_UINT mapAfter = mapBefore;
1007 if (!event.attribute)
1008 {
1009 /* For efficiency, multiline guys will make it only partial */
1010 if (multiline)
1011 {
1012 /* was recalc */
1013 wrapAndPosition (tb.line, te.line+1,
1014 (int)textData.size() - (int) lineSpan.size());
1015 }
1016 else
1017 {
1018 wrapAndPosition ();
1019 }
1020 mapAfter = textData.getLogicalMap(tb.line);
1021 /* find the highest and visual index */
1022 }
1023
1024 SWindow* w = getWindow();
1025 if (w == 0)
1026 {
1027 /* This is a strange place to return - but we needed to rebuild indeces */
1028 return;
1029 }
1030
1031 unsigned int newsize = lineSpan.size();
1032
1033 unsigned int newspan = sane_index (lineSpan, newsize);
1034 bool samebreak = false;
1035
1036 bool newlr = textData.isLR(tb.line);
1037 bool drawwholeline = (newlr != oldlr && tb.line == te.line);
1038 if (tb.line == te.line && tb.line < newsize && tb.line <breaks.size())
1039 {
1040 SV_UINT o = oldbreaks;
1041 SV_UINT n = breaks[tb.line];
1042 samebreak = (o.size() == n.size());
1043 if (samebreak)
1044 {
1045 /* of course it break at the end */
1046 for (unsigned int i=0; i+1<n.size(); i++)
1047 {
1048 /* break changed or it was before the text change */
1049 /* for attribute break can not change */
1050 if (!event.attribute && (n[i] != o[i] || tb.index <= n[i]))
1051 {
1052 samebreak = false;
1053 break;
1054 }
1055 /* break is between begin and end */
1056 if (n[i] >= tb.index && n[i] <= te.index)
1057 {
1058 drawwholeline = true;
1059 }
1060 }
1061 }
1062 }
1063 if (tb.line == te.line && drawwholeline)
1064 {
1065 tb.index = 0;
1066 te.index = mapAfter.size();
1067 }
1068
1069 /* adjust tb te */
1070 if (tb.line == te.line && !drawwholeline && samebreak)
1071 {
1072 unsigned int i;
1073 /* find out lowest common stuff in map */
1074 unsigned int min = mapAfter.size() < mapBefore.size()
1075 ? mapAfter.size() : mapBefore.size();
1076
1077 /* make logical to visual maps */
1078 SS_UINT * mapa = new SS_UINT[mapAfter.size()+1];
1079 CHECK_NEW (mapa);
1080 for (i=0; i<mapAfter.size(); i++) mapa[i] = mapAfter[i];
1081
1082 SS_UINT * mapb = new SS_UINT[mapBefore.size()+1];
1083 CHECK_NEW (mapb);
1084 for (i=0; i<mapBefore.size(); i++) mapb[i] = mapBefore[i];
1085
1086 unsigned int lowestvis = min;
1087 for (i=0; i<min; i++)
1088 {
1089 if (mapb[i] != mapa[i])
1090 {
1091 tb.index = mapa[i];
1092 lowestvis = i;
1093 break;
1094 }
1095 /* at least from here it changed yeah... */
1096 if (mapa[i] == tb.index)
1097 {
1098 lowestvis = i;
1099 break;
1100 }
1101 }
1102 if (i==0)
1103 {
1104 if (mapAfter.size()> 0)
1105 {
1106 tb.index = mapAfter[0];
1107 }
1108 else
1109 {
1110 tb.index = 0;
1111 }
1112 lowestvis=0;
1113 }
1114 /* find out if there is something between zero and lowes vis */
1115 for (i=0; i<lowestvis; i++)
1116 {
1117 /* we can have one glyph difference */
1118 if (mapa[i]+1 >= tb.index)
1119 {
1120 tb.index = mapa[i];
1121 lowestvis = i;
1122 break;
1123 }
1124 }
1125 // if still between lowest and end there is a lower index, take 0.
1126 for (i=lowestvis; i<mapAfter.size(); i++)
1127 {
1128 if (mapa[i] <= tb.index)
1129 {
1130 /* find the smallest */
1131 unsigned int smallest = mapa[i];
1132 while (++i < mapAfter.size())
1133 {
1134 if (mapa[i] < smallest) smallest = mapa[i];
1135 }
1136 if (smallest > 0) smallest--;
1137 tb.index = smallest;
1138 break;
1139 }
1140 }
1141
1142 /* for attribute te.index is also used and mapafter = mapbefore */
1143 if (event.attribute && te.index < mapAfter.size())
1144 {
1145 unsigned int vis = mapAfter[te.index];
1146 unsigned int max = mapAfter.size();
1147 for (i=mapAfter.size(); i>vis; i--)
1148 {
1149 if (mapa[i-1] < te.index)
1150 {
1151 te.index= max;
1152 break;
1153 }
1154 max = mapa[i-1];
1155 }
1156 }
1157 else
1158 {
1159 te.index = mapAfter.size();
1160 }
1161 delete [] mapa;
1162 delete [] mapb;
1163 }
1164
1165 SLocation lb = getTextLocation (tb);
1166 SLocation le = getTextLocation (te);
1167
1168 /*
1169 * Get smallest and biggest.
1170 */
1171 if (tb.line == te.line && samebreak && le.y == lb.y)
1172 {
1173 if (le.x < lb.x)
1174 {
1175 int tmp = lb.x; lb.x = le.x; le.x = tmp;
1176 }
1177 for (unsigned int i=tb.index; i<=te.index; i++)
1178 {
1179 SLocation l = getTextLocation (STextIndex(tb.line, i));
1180 if (l.x < lb.x) lb = l;
1181 if (l.x > le.x) le = l;
1182 l = getTextLocation (STextIndex(tb.line, i), false);
1183 if (l.x < lb.x) lb = l;
1184 if (l.x > le.x) le = l;
1185 }
1186 }
1187
1188 //fprintf (stderr, "lb.x =%d, le.x=%u\n", lb.x, le.x);
1189 /* make sure we are inside the window */
1190 if (lb.y + (int)lineHeight < 0) lb.y = -(int)lineHeight;
1191 if (le.y > location.y + (int)size.height) le.y = size.height + location.y;
1192
1193 /* Text content did not change, only the attribute */
1194
1195 int starty = (lb.y > 5) ? lb.y - odw: 0;
1196 unsigned int lheight = lineHeight + 2*odw;
1197
1198 if (event.attribute)
1199 {
1200 /* single */
1201 if (lb.y == le.y && samebreak)
1202 {
1203 /* we add 1 to make sure it is non-null positive */
1204 w->redraw (true, lb.x-odw, starty, (unsigned int) (le.x-lb.x)+2*odw, lheight);
1205 }
1206 else // multiline - redraw whole thing.
1207 {
1208 le = getTextLocation (STextIndex (te.line, textData.size(te.line)));
1209 if (lb.y < le.y) /* always */
1210 {
1211 w->redraw (true, location.x, starty,
1212 size.width, lheight + (unsigned int)(le.y-lb.y));
1213 }
1214 else /* I dont know what happened - redraw */
1215 {
1216 w->redraw (true, location.x, location.y, size.width, size.height);
1217 }
1218 }
1219 return;
1220 }
1221
1222 /* Change is inside a single paragraph */
1223 if (tb.line == te.line && oldsize == newsize && oldspan == newspan)
1224 {
1225 /* The whole change is on the same line (breaks did not change) */
1226 if (lb.y == le.y && samebreak)
1227 {
1228 bool lrline = textData.isLR (tb.line);
1229 int wid = 0;
1230 if (lrline)
1231 {
1232 //lb.x = lb.x;
1233 wid = (int) size.width; /* till end of line */
1234 }
1235 else
1236 {
1237 lb.x = 0;
1238 wid = le.x + location.x;
1239 }
1240 /* redraw till end of line */
1241 w->redraw (true, lb.x-odw, starty, (unsigned int) wid + 2*odw, lheight);
1242 }
1243 else /* This is a multi-line paragraph change. redraw till end */
1244 {
1245 le = getTextLocation (STextIndex (te.line, textData.size(te.line)));
1246 if (le.y > lb.y) /* always */
1247 {
1248 w->redraw (true, location.x, starty,
1249 size.width, lheight + (unsigned int)(le.y-lb.y));
1250 }
1251 else /* I dont know what happened - redraw */
1252 {
1253 w->redraw (true, location.x, location.y, size.width, size.height);
1254 }
1255
1256 }
1257 return;
1258 }
1259 /* Multi-paragraph change. Is it visible? */
1260 if (starty < location.y + (int) size.height)
1261 {
1262 w->redraw (true, location.x, starty,
1263 size.width, location.y + (int)size.height - starty);
1264 }
1265 }
1266
1267 /**
1268 * Makr lines so that they will recalculate
1269 */
1270 void
setReordered()1271 STextView::setReordered()
1272 {
1273 /* HACK FOR LABELS - they neeed to know their exact size */
1274 if (!isEditable)
1275 {
1276 for (unsigned int i=0; i<textData.size(); i++)
1277 {
1278 textData.setVisible(i);
1279 textData.setReordered (i);
1280 }
1281 wrapAndPosition();
1282 return;
1283 }
1284 lineSpan.clear ();
1285 unsigned int sum = 0;
1286 for (unsigned int i=0; i<textData.size(); i++)
1287 {
1288 sum++;
1289 textData.setReordered (i);
1290 lineSpan.append (sum);
1291 }
1292 }
1293 /**
1294 * Walk through the text and remake the linespan.
1295 * Recalculate the preferred sizes.
1296 */
1297 void
wrapAndPosition()1298 STextView::wrapAndPosition ()
1299 {
1300 lineHeight = (unsigned int ) (font.ascent() + font.descent() + font.gap());
1301 lineAscent = (unsigned int) font.ascent();
1302 breaks.clear ();
1303 posAfter.clear ();
1304 posBefore.clear ();
1305 lineSpan.clear ();
1306
1307 SH_UINT hint;
1308 unsigned int sum = 0;
1309 preferredSize.width = 0;
1310 for (unsigned int i=0; i<textData.size(); i++)
1311 {
1312 sum += wrapAndPosition (i, &hint);
1313 lineSpan.append (sum);
1314 }
1315 preferredSize.height = (textData.size()==0) ? lineHeight
1316 : textData.size() * lineHeight;
1317 }
1318
1319 /**
1320 * recalculate partially. This is used for multi-line stuff
1321 * to make it more efficient.
1322 * @param from is the starting index.
1323 * @param until is the index before last
1324 * @paran addcount show how many lines were added. can be negative (removed)
1325 */
1326 void
wrapAndPosition(unsigned int from,unsigned int until,int addcount)1327 STextView::wrapAndPosition (unsigned int from, unsigned int until, int addcount)
1328 {
1329 //unsigned int longestline = 1;
1330 unsigned int sum = sane_index (lineSpan, from);
1331 int mycount=0;
1332 unsigned int i=0;
1333 SH_UINT cache;
1334 for (i=from; i<textData.size() && i<until; i++)
1335 {
1336 sum += wrapAndPosition (i, &cache);
1337 lineSpan.insert (i, sum);
1338 mycount++;
1339 }
1340
1341 unsigned int removesum = sum;
1342 if (i<breaks.size())
1343 {
1344 for (int j=0; j<mycount-addcount; j++)
1345 {
1346 /* yes i ! */
1347 removesum = lineSpan[i];
1348 lineSpan.remove (i);
1349 breaks.remove (i);
1350 posBefore.remove (i);
1351 posAfter.remove (i);
1352 }
1353 }
1354 /* recalibrate the whole linespan array */
1355 if (removesum != sum)
1356 {
1357 while (i < textData.size())
1358 {
1359 unsigned int s = lineSpan[i];
1360 if (removesum > sum)
1361 {
1362 s -= removesum-sum;
1363 }
1364 else
1365 {
1366 s += sum-removesum;
1367 }
1368 lineSpan.replace (i, s);
1369 i++;
1370 }
1371 }
1372 preferredSize.height = (textData.size()==0) ? lineHeight
1373 : textData.size() * lineHeight;
1374 }
1375
1376 /**
1377 * Caclulate the extent as one line.
1378 * It inserts an element at line in positions, and breaks.
1379 * The positions array will have the positions of the end
1380 * of the glyph, ragrdless of paragraph embedding, in LR order.
1381 * @param line is the line to calculate.
1382 * @return the linesspan
1383 */
1384 unsigned int
wrapAndPosition(unsigned int line,SH_UINT * cache)1385 STextView::wrapAndPosition (unsigned int line, SH_UINT* cache)
1386 {
1387 /* first line is always visible - multiline */
1388
1389 if (!textData.isVisible(line))
1390 {
1391 SV_UINT empty;
1392 posAfter.insert(line, empty);
1393 posBefore.insert(line, empty);
1394 breaks.insert(line, empty);
1395 /* make span 1 */
1396 return 1;
1397 }
1398
1399 /* +1 is only because of zero sized arrays */
1400 SS_UCS4* logical = new SS_UCS4[textData.size(line)+1];
1401 CHECK_NEW(logical);
1402 SS_UCS4* logicalBefore = new SS_UCS4[textData.size(line)+1];
1403 CHECK_NEW(logicalBefore);
1404
1405 SS_UCS4* visual = new SS_UCS4[textData.size(line)+1];
1406 CHECK_NEW(visual);
1407
1408 unsigned int ae=0;
1409 unsigned int ce=0;
1410 unsigned int le=0;
1411 unsigned int i;
1412 SV_UINT b;
1413 bool wrapNext = false;
1414 bool wrapPage = false;
1415 unsigned int lastbreak = 0;
1416 /* go through the text in logical order */
1417 for (i=0; i<textData.size(line); i++)
1418 {
1419 const SGlyph& g = textData.glyphAt (STextIndex (line, i));
1420 ce = cache->get (g.charKey());
1421 if (ce ==0)
1422 {
1423 ce = (unsigned int) (0.5 + font.width (g));
1424 SS_UCS4 fc = g.getFirstChar();
1425 if (!isEditable && (g.isEOL() || fc == SD_CD_LRM || fc == SD_CD_RLM
1426 || fc == SD_CD_ZWNJ || fc == SD_CD_ZWJ))
1427 {
1428 ce = 1;
1429 }
1430 else if (fc == SD_CD_ZWSP)
1431 {
1432 ce = 1;
1433 }
1434 else if (g.isTab())
1435 {
1436 int tabsize = (int)(4.0 * font.getSize());
1437 if (tabsize < 1) tabsize = 1;
1438 ce = tabsize - (le % (unsigned int)tabsize);
1439 if (multiline && (le + ce)> size.width && le > 0)
1440 {
1441 /* force line break. */
1442 ce = (int)(4.0 * font.getSize());
1443 }
1444 }
1445 /* Shaped glyphs width and tab may change. */
1446 if (g.getShapeArray()==0 && !g.isTab())
1447 {
1448 cache->put (g.charKey(), ce);
1449 }
1450 }
1451 le += ce;
1452 ae += ce;
1453 logical[i] = ce;
1454
1455 if (multiline && le > size.width && i > 0 && !wrapNext)
1456 {
1457 if (g.isTab())
1458 {
1459 /* nothing to do. we break here */
1460 }
1461 /* we might want to wrap earlier */
1462 else if (isWordWrapOn && !textData.canWrap (STextIndex (line, i-1)))
1463 {
1464 unsigned int oldae = ae;
1465 unsigned int oldi = i;
1466 while (i>lastbreak && !textData.canWrap (STextIndex (line, i-1)))
1467 {
1468 ae -= logical[i];
1469 i--;
1470 }
1471 /* emergency break */
1472 if (i==lastbreak)
1473 {
1474 ae = oldae;
1475 i = oldi;
1476 }
1477 }
1478 le = logical[i];
1479 b.append (i);
1480 lastbreak = i;
1481 }
1482 else if (wrapNext)
1483 {
1484 le = logical[i];
1485 lastbreak = i;
1486 if (wrapPage)
1487 {
1488 if (i == 1 && line == 0) // first line, first char is a FF
1489 {
1490 b.append (i);
1491 }
1492 else if (i > 1) // we have something on the line
1493 {
1494 //const SGlyph& gp = textData.glyphAt (STextIndex (line, i-2));
1495 // the one before FF is an FF too
1496 b.append (i);
1497 }
1498 unsigned int currSpan = (line == 0 || lineSpan.size() < line-1)
1499 ? 0 : lineSpan[line-1];
1500
1501 // how many more we need to add to reach top?
1502 unsigned int lh = (lineHeight == 0) ? 1 : lineHeight;
1503 unsigned int linesPerPage = printerPageSize / lh;
1504 while (((currSpan + b.size()) % linesPerPage) != 0)
1505 {
1506 b.append (i);
1507 }
1508
1509 }
1510 else
1511 {
1512 b.append (i);
1513 }
1514 }
1515 wrapNext = (multiline && g.isEOL() && !g.isEOP());
1516 if (printerPageSize != 0 && wrapNext)
1517 {
1518 wrapPage = g.isFF();
1519 }
1520 }
1521 /* now b contains the logical positions where the glyph should start at 0 */
1522 b.append (textData.size(line));
1523
1524 /* break the text into lines */
1525 textData.setLineBreaks(line, b);
1526
1527 if (preferredSize.width < ae) preferredSize.width = ae;
1528
1529 /* make a visual map */
1530 for (i=0; i<textData.size(line); i++)
1531 {
1532 visual[i] = textData.toLogical (line, i);
1533 }
1534 le = 0;
1535 /* add up visual */
1536 unsigned int nextbreak = 0;
1537
1538 /* go through in visual order */
1539 for (i=0; i<textData.size(line); i++)
1540 {
1541 /* we use visual break here */
1542 while (nextbreak < b.size() && i == b[nextbreak])
1543 {
1544 nextbreak++;
1545 le = 0;
1546 }
1547 /*
1548 * save space - make confusion :).
1549 * logical[visual[i]] will not be used
1550 * any more here so we re-use it
1551 */
1552 logicalBefore[visual[i]] = le;
1553 le += logical[visual[i]];
1554 logical[visual[i]] = le;
1555 }
1556 SV_UINT pb;
1557 SV_UINT pa;
1558 for (i=0; i<textData.size(line); i++)
1559 {
1560 pb.append (logicalBefore[i]);
1561 pa.append (logical[i]);
1562 }
1563
1564 delete[] logicalBefore;
1565 delete[] logical;
1566 delete[] visual;
1567
1568 breaks.insert(line, b);
1569 posAfter.insert(line, pa);
1570 posBefore.insert(line, pb);
1571 /* updating lineSpan is in the calling routine*/
1572
1573 return b.size();
1574 }
1575
1576 /**
1577 * Set the background.
1578 * @param bg is the new background
1579 */
1580 void
setBackground(const SColor & bg)1581 STextView::setBackground (const SColor& bg)
1582 {
1583 lrpen.setBackground (bg);
1584 rlpen.setBackground (bg);
1585 }
1586
1587 /**
1588 * Set the foreground.
1589 * @param fg is the new foreground
1590 */
1591 void
setForeground(const SColor & rlfg,const SColor & lrfg)1592 STextView::setForeground (const SColor& rlfg, const SColor& lrfg)
1593 {
1594 lrpen.setForeground (rlfg);
1595 rlpen.setForeground (lrfg);
1596 }
1597 const SColor&
getBackground()1598 STextView::getBackground ()
1599 {
1600 return lrpen.getBackground();
1601 }
1602
1603 const SColor&
getForeground(bool lr)1604 STextView::getForeground (bool lr)
1605 {
1606 return (lr) ? lrpen.getForeground() : rlpen.getForeground();
1607 }
1608
1609 /**
1610 * If show <- newline characters
1611 * @param _lineend is true if lineend is shown.
1612 */
1613 void
setLineEndMark(bool _lineend)1614 STextView::setLineEndMark (bool _lineend)
1615 {
1616 lineend = _lineend;
1617 setReordered();
1618 SWindow* w = getWindow();
1619 if (w == 0)
1620 {
1621 return;
1622 }
1623 if (!w->isVisible()) return;
1624 w->redraw (true, location.x, location.y, size.width, size.height);
1625 }
1626
1627 /**
1628 * Is new line shown?
1629 * @return true if newline characters are shown.
1630 */
1631 bool
getLineEndMark() const1632 STextView::getLineEndMark () const
1633 {
1634 return lineend;
1635 }
1636
1637 /**
1638 * calculate the height of the document.
1639 */
1640 unsigned int
getDocumentHeight() const1641 STextView::getDocumentHeight() const
1642 {
1643 if (textData.size() == 0)
1644 {
1645 return lineHeight;
1646 }
1647 unsigned int fheight = lineSpan[textData.size()-1];
1648 if (textData.isProperLine (textData.size()-1))
1649 {
1650 fheight += 1;
1651 }
1652 return (fheight * lineHeight);
1653 }
1654
1655 /**
1656 * return the 'sane index'.
1657 * That is, at index 0 it should be 0
1658 * at index at array->size() is should be the last element.
1659 */
1660 static unsigned int
sane_index(const SV_UINT & array,unsigned int index)1661 sane_index (const SV_UINT& array, unsigned int index)
1662 {
1663 if (index == 0 || array.size() < index) return 0;
1664 return array[index-1];
1665 }
1666
1667 void
setUnderlineColor(const SColor & c)1668 STextView::setUnderlineColor (const SColor& c)
1669 {
1670 underlineColor = c;
1671 }
1672
1673 /**
1674 * Mark this visible
1675 */
1676 void
setVisible(unsigned int line)1677 STextView::setVisible (unsigned int line)
1678 {
1679 if (!textData.isVisible (line))
1680 {
1681 textData.setVisible(line);
1682 wrapAndPosition (line, line+1, 0);
1683 }
1684 else if (textData.isReordered(line))
1685 {
1686 wrapAndPosition (line, line+1, 0);
1687 }
1688 }
1689
1690 /**
1691 * return the cursor index that is left (screen-wise) of
1692 * ci.
1693 * @checkembed is true check for embedding boundary
1694 */
1695 SCursorIndex
leftOf(const SCursorIndex & ci)1696 STextView::leftOf (const SCursorIndex& ci)
1697 {
1698 if (ci.textIndex.line >= textData.size())
1699 {
1700 return SCursorIndex(ci.textIndex.line, ci.textIndex.index);
1701 }
1702 setVisible (ci.textIndex.line);
1703 SCursorIndex cn = moveCursor (ci, false);
1704 return SCursorIndex (cn);
1705 }
1706
1707 SCursorIndex
rightOf(const SCursorIndex & ci)1708 STextView::rightOf (const SCursorIndex& ci)
1709 {
1710 if (ci.textIndex.line >= textData.size())
1711 {
1712 return SCursorIndex(ci.textIndex.line, ci.textIndex.index);
1713 }
1714 setVisible (ci.textIndex.line);
1715 SCursorIndex cn = moveCursor (ci, true);
1716 return SCursorIndex (cn);
1717 }
1718
1719 /**
1720 * Move the cursor up or down one slot visuallly.
1721 * You have to expand the paragrapgh before this call.
1722 * @param ci is the input index.
1723 * @praram isup is true if we walk right visuallly.
1724 * @return 1 index up or down.
1725 */
1726 SCursorIndex
moveCursor(const SCursorIndex & ci,bool isup)1727 STextView::moveCursor (const SCursorIndex& ci, bool isup)
1728 {
1729 SV_UINT map = textData.getLogicalMap(ci.textIndex.line);
1730 if (map.size()==0) return SCursorIndex(ci.textIndex.line,0);
1731 if (textData.isProperLine (ci.textIndex.line))
1732 {
1733 map.truncate (map.size()-1);
1734 }
1735 if (map.size()==0) return SCursorIndex(ci.textIndex.line,0);
1736 /* we need to find the current index in the map. */
1737 int current = map.size();
1738 for (unsigned int i=0; i<map.size(); i++)
1739 {
1740 if (map[i] == ci.textIndex.index)
1741 {
1742 current = i;
1743 }
1744 }
1745 /* normalize to our visual index. */
1746 bool llr = textData.isLR(ci.textIndex.line);
1747
1748 /* for lr before is after and vice versa */
1749 bool clr = textData.isLR(ci.textIndex);
1750
1751 /* it is easier to visualize this in visual order */
1752 bool cbefore = clr ? ci.before : !ci.before;
1753 bool resbefore = false;
1754
1755 SEmbedState eold = textData.getEmbedState(ci.textIndex);
1756 SEmbedState enew;
1757 bool samembed = true;
1758 if (isup)
1759 {
1760 if (cbefore) // set it to after
1761 {
1762 resbefore = false;
1763 }
1764 else /* increment visual index and increment one */
1765 {
1766 resbefore = false;
1767 //current = current+1;
1768 current = llr ? current+1 : current-1;
1769 /* check the mbedding state of the next */
1770 unsigned int ei = (current < 0) ? map.size()+1
1771 : map[(unsigned int)current];
1772 enew = textData.getEmbedState(STextIndex (ci.textIndex.line, ei));
1773 samembed = (enew==eold);
1774 }
1775 }
1776 else /* !isup */
1777 {
1778 /* set it to after */
1779 if (!cbefore)
1780 {
1781 resbefore = true;
1782 }
1783 /* set it to next */
1784 else
1785 {
1786 resbefore = true;
1787 /* the map is not visual */
1788 current = llr ? current-1 : current+1;
1789 /* check the mbedding state of the next */
1790 unsigned int ei = (current < 0) ? map.size()+1
1791 : map[(unsigned int)current];
1792 enew = textData.getEmbedState(STextIndex (ci.textIndex.line, ei));
1793 samembed = (enew==eold);
1794 //current = current-1;
1795 }
1796 }
1797 /* check bounds */
1798 unsigned int resindex = 0;
1799 if (current < 0)
1800 {
1801 current = 0;
1802 /* we need to move to the rightmost */
1803 resbefore = llr;
1804 SCursorIndex rc(ci.textIndex.line, map[(unsigned int)current], resbefore);
1805 bool islr = textData.isLR (rc.textIndex);
1806 if (!islr) rc.before = !rc.before;
1807 return SCursorIndex (rc);
1808 }
1809 if (current >= (int)map.size())
1810 {
1811 current = (int)map.size();
1812 resbefore = true;
1813 return SCursorIndex (ci.textIndex.line, (unsigned int) current, resbefore);
1814 }
1815 resindex = map[(unsigned int) current];
1816 /* check changed index */
1817 SCursorIndex res (ci.textIndex.line, resindex, resbefore);
1818 bool nlr = textData.isLR (res.textIndex);
1819
1820 /* rl before is logical after switch */
1821 /* nlr already switched */
1822 /* back to logical order */
1823 if (!nlr) res.before = !res.before;
1824
1825 /* embed changed */
1826 if (!samembed)
1827 {
1828 res.before = !res.before;
1829 return SCursorIndex (res);
1830 }
1831 /* direction changed */
1832 if (clr != nlr)
1833 {
1834 res.before = !res.before;
1835 }
1836 return SCursorIndex (res);
1837 }
1838
1839
1840 void
setHideText(bool is)1841 STextView::setHideText(bool is)
1842 {
1843 isHidingText = is;
1844 }
1845 bool
isHideText()1846 STextView::isHideText()
1847 {
1848 return isHidingText;
1849 }
1850