"Fossies" - the Fresh Open Source Software Archive

Member "yudit-3.0.7/swidget/STextView.cpp" (7 Jun 2020, 45633 Bytes) of package /linux/misc/yudit-3.0.7.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "STextView.cpp" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 3.0.5_vs_3.0.7.

    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
   41 timerStart()
   42 {
   43   gettimeofday (&thatTime, 0);
   44 }
   45 
   46 static void
   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  */
   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  */
   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
  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 
  139 STextView::~STextView ()
  140 {
  141 }
  142 
  143 void
  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
  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
  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
  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
  220 STextView::setAlignment (bool align)
  221 {
  222   alignment = align;
  223 }
  224 /**
  225  */
  226 void
  227 STextView::setMultiline (bool _multiline)
  228 {
  229   multiline = _multiline;
  230   setReordered ();
  231 }
  232 /**
  233  */
  234 bool
  235 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
  244 STextView::setViewPort (const SLocation& _viewPort)
  245 {
  246   viewPort = _viewPort;
  247 }
  248 
  249 const SLocation&
  250 STextView::getViewPort()
  251 {
  252   return viewPort;
  253 }
  254 
  255 /**
  256  * Resize the component and redraw.
  257  */
  258 void
  259 STextView::resize (const SDimension& _dimension)
  260 {
  261   SComponent::resize (_dimension);
  262   setReordered ();
  263 }
  264 
  265 unsigned int
  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
  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
  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
  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
  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
  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 
  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
  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&
  666 STextView::getSyntax () const
  667 {
  668   return highlightMode;
  669 }
  670 
  671 void
  672 STextView::setSyntaxColors (const SSyntaxColors& attr)
  673 {
  674   syntaxColors = attr;
  675 }
  676 
  677 const SSyntaxColors&
  678 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
  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
  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
  709 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
  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
  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
  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
  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 
 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
 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
 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
 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
 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
 1592 STextView::setForeground (const SColor& rlfg, const SColor& lrfg)
 1593 {
 1594   lrpen.setForeground (rlfg);
 1595   rlpen.setForeground (lrfg);
 1596 }
 1597 const SColor&
 1598 STextView::getBackground ()
 1599 {
 1600   return lrpen.getBackground();
 1601 }
 1602 
 1603 const SColor&
 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
 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
 1632 STextView::getLineEndMark () const
 1633 {
 1634   return lineend;
 1635 }
 1636 
 1637 /**
 1638  * calculate the height of the document.
 1639  */
 1640 unsigned int 
 1641 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
 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
 1668 STextView::setUnderlineColor (const SColor& c)
 1669 {
 1670   underlineColor = c;
 1671 }
 1672 
 1673 /**
 1674  * Mark this visible 
 1675  */
 1676 void
 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
 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
 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
 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
 1841 STextView::setHideText(bool is)
 1842 {
 1843     isHidingText = is;
 1844 }
 1845 bool
 1846 STextView::isHideText()
 1847 {
 1848     return isHidingText;
 1849 }