/* * Yudit Unicode Editor Source File * * GNU Copyright (C) 1997-2006 Gaspar Sinai * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * dated June 1991. See file COPYYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "swidget/STextView.h" #define DEBUG_SPEED 0 /* I could debug speed of redrawing. Most time (98%) is taken in i += font.draw (c, p, dm, &g.array()[i], g.size()-i); */ #if DEBUG_SPEED #ifndef USE_WINAPI #include #else #include #include #endif static struct timeval thatTime; static void timerStart() { gettimeofday (&thatTime, 0); } static void timerStop() { struct timeval thisTime; gettimeofday (&thisTime, 0); if (thisTime.tv_usec < thatTime.tv_usec) { thisTime.tv_sec--; thisTime.tv_usec+=1000000; } thisTime.tv_sec -= thatTime.tv_sec; thisTime.tv_usec -= thatTime.tv_usec; int msec = (int) thisTime.tv_sec * 1000 + thisTime.tv_usec/1000; fprintf (stderr, "Elapsed time: %d msecs\n", msec); } #endif static unsigned int sane_index (const SV_UINT& array, unsigned int index); /** * The text data is mine. I'll delete it. */ STextView::STextView (void) : lrpen (SColor (0.0, 0.0, 0.0, 1.0)), rlpen (SColor (0.0, 0.0, 1.0, 1.0)), underlineColor("red") { highlightMode = HM_None; isWordWrapOn = false; isEditable = false; clipx = clipy = 0; clipw = cliph = 0; fontSize = 16.0; lineend = true; multiline = true; textData.addTextDataListener (this); } /** * Make a text-view from external text * @param utf8 is the utf8 ancoded text */ STextView::STextView (const SString& utf8) : textData (utf8), lrpen (SColor (0.0, 0.0, 0.0, 1.0)), rlpen (SColor (0.0, 0.0, 1.0, 1.0)), underlineColor("red") { highlightMode = HM_None; isWordWrapOn = false; isEditable = false; clipx = clipy = 0; clipw = cliph = 0; fontSize = 16.0; lineend = true; multiline = true; wrapAndPosition (); textData.addTextDataListener (this); textData.clearEvent(); } /** * Set the text and * Do the reordering, expanding for the whole text. * Not very efficient with large text. */ void STextView::setText(const SString& text) { textData.clear(); textData.fireEvent(); textData.insert(text); /* HACK FOR LABELS - they neeed to know their exact size */ for (unsigned int i=0; i 0.0) fontSize = _fontSize; font = SFont(_font, fontSize); setPen (); setReordered (); lineHeight = (unsigned int ) (font.ascent() + font.descent() + font.gap()); lineAscent = (unsigned int) font.ascent(); preferredSize.height = (textData.size()==0) ? lineHeight : textData.size() * lineHeight; } /** * Set the size of the font. * @param size is the size of the font. */ void STextView::setFontSize (double newsize) { font.setSize(newsize); setPen (); setReordered (); lineHeight = (unsigned int ) (font.ascent() + font.descent() + font.gap()); lineAscent = (unsigned int) font.ascent(); preferredSize.height = (textData.size()==0) ? lineHeight : textData.size() * lineHeight; } /** * Set the pen size according to font size. */ void STextView::setPen () { double pensize = 1.0; double pointsize = font.getSize(); if (pointsize <= 16) { pensize = 0.125; } else if (pointsize <=24) { pensize = 0.25; } else if (pointsize <=80) { pensize = 0.5; } else if (pointsize <=100) { pensize = 0.75; } lrpen.setLineWidth (pensize); rlpen.setLineWidth (pensize); } /** * Set the text alignment align is true if it is right aligned. */ void STextView::setAlignment (bool align) { alignment = align; } /** */ void STextView::setMultiline (bool _multiline) { multiline = _multiline; setReordered (); } /** */ bool STextView::isMultiline () const { return multiline; } /** * Set the viewport. This is the location that casts to {0;0} * @param _viewPort is the new viewport. */ void STextView::setViewPort (const SLocation& _viewPort) { viewPort = _viewPort; } const SLocation& STextView::getViewPort() { return viewPort; } /** * Resize the component and redraw. */ void STextView::resize (const SDimension& _dimension) { SComponent::resize (_dimension); setReordered (); } unsigned int STextView::getLineIndex (int locy) { /* binary search linespan */ unsigned int top; unsigned int bottom; unsigned int mid; top = lineSpan.size(); bottom = 0; int y = locy - (location.y + viewPort.y) + 1; while (top > bottom) { mid = (top+bottom)/2; unsigned int sindex = lineHeight * sane_index (lineSpan, mid+1); if (y == (int) (sindex)) { top = mid; break; } if (y < (int) (sindex)) { top = mid; continue; } bottom = mid + 1; } return top; } /** * @param l is the location on the canvas. */ SCursorIndex STextView::getCursorIndex (const SLocation& l) { unsigned int line = getLineIndex (l.y); if (line >= textData.size()) { if (line > 0 && !textData.isProperLine (line-1)) { return SCursorIndex (line-1, textData.size(line-1)); } return SCursorIndex (line, 0); } SV_UINT brk; if (line < textData.size()) { setVisible (line); brk = breaks[line]; } /* Linear search. I don't expect long lines. */ unsigned int offset = lineHeight * sane_index (lineSpan, line); SLocation lm (0, viewPort.y-location.y+(int)offset); bool lr = textData.isLR (line); /* Transpose to LR */ lm.x = l.x-location.x+viewPort.x; /* convert as if it were lr */ if (!lr) lm.x = (int)size.width - (int)lm.x; /* find where the glyph starts */ unsigned int i; unsigned int currExt=0; for (i=0; i l.y) break; currExt = brk[i]; } unsigned int si = textData.size(line); unsigned int nextExt = si; if (i= (int)begpos && lm.x < (int)midpos) { cindex = SCursorIndex(line, i, before); /* logical before */ break; } else if (lm.x >= (int)midpos && lm.x < (int)endpos) { cindex = SCursorIndex(line, i, !before); /* logical after */ break; } } if (i==si && lm.x > 0 && si > 0) { /* rl is initialized already */ cindex = SCursorIndex(line, i, before); } /* beginning or found */ return SCursorIndex(cindex); } SLocation STextView::getCursorLocation (const SCursorIndex& cursorIndex) { return SLocation(getTextLocation (cursorIndex.textIndex, cursorIndex.before)); } /** * Convert a line and index to a location on the screen * This is the top left(lr) or right(rl) corner of the glyph. * @param line is the line number. * @param index is the index. * @param before is true if we need the index before the glyph - * logical meaning. * Here is an LR glyph: * Paragraph LR A A Paragrph RL * ^ ^ ^ ^ * | | | | * Before ----+ +--- After Before ---+ +---- After */ SLocation STextView::getTextLocation (const STextIndex& textIndex, bool before) { unsigned int line = textIndex.line; unsigned int index = textIndex.index; unsigned int ll = sane_index (lineSpan, line); SLocation ret; if (line >= textData.size()) { ret = SLocation (location.x+viewPort.x, location.y+viewPort.y + lineHeight * ll); } else { setVisible (line); SV_UINT brk = breaks[line]; /* It may be second or third line. */ unsigned int i = 0; int yoffset = 0; unsigned int posafter = 0; for (i=0; i textData.size(line) && textData.isProperLine(line)) { /* last one */ si = 0; yoffset++; } else /* Streched beyond the last one. */ { SV_UINT v = posAfter[line]; unsigned int max = 0; unsigned int maxi = 0; for (unsigned int i=posafter; i max) { max = v[i]; maxi = i; } } si = posAfter[line][maxi]; } ret = SLocation (location.x+si+viewPort.x, location.y+viewPort.y + lineHeight * (ll+yoffset)); } bool lr = textData.isLR (line); int nloc = (lr) ? ret.x : 2 * location.x + (int)size.width - (int)ret.x - 2 * viewPort.x; return SLocation(nloc, ret.y); } /** * This is coming from the SWindowListener * @param c is the canvas to draw on. * @param x is the upper lect corner. * @param y is the upper lect corner. * @param width is the width of this event. * @param height is the height of this event. */ void STextView::redraw (SWindow *w, int x, int y, unsigned int width, unsigned int height) { internalRedraw (w, x, y, width, height); } /** * This is coming from the SWindowListener * @param c is the canvas to draw on. * @param x is the upper lect corner. * @param y is the upper lect corner. * @param width is the width of this event. * @param height is the height of this event. */ void STextView::redraw (SCanvas *c, int x, int y, unsigned int width, unsigned int height) { internalRedraw (c, x, y, width, height); } /** * This is coming from the SWindowListener * @param c is the canvas to draw on. * @param x is the upper lect corner. * @param y is the upper lect corner. * @param width is the width of this event. * @param height is the height of this event. */ void STextView::internalRedraw (SCanvas *c, int x, int y, unsigned int width, unsigned int height) { #if DEBUG_SPEED x = getLocation ().x; y = getLocation ().y; width = getSize().width; height = getSize().height; timerStart(); #endif SLocation lb (x, y); SLocation le (x+width, y+height); unsigned int line = getLineIndex (lb.y); bool islr = textData.isLR (line); SLocation lstart = lb; if (!islr) { lstart.x = le.x; } SLocation lleft(location.x+viewPort.x, location.y+viewPort.y + (int) lineHeight * (int) sane_index (lineSpan, line)); SLocation lright(location.x+viewPort.x+(int)size.width, lleft.y); for (unsigned int i=line; i location.y + (int) size.height ) break; if (lleft.y > y + (int)height) break; } #if DEBUG_SPEED timerStop(); #endif } /** * Draw a whole line of glyphs. * @param c is the canvas to draw to * @param islr is true if we draw from left to right. * @param line is the line index to draw. * @param l is the beginning upper corner location * @param lb is the beginning exposure * @param le is the end exposure * @param iswindow is true if we want to set the clipping area * this is only if you want to experiment - we dont want to do that. * @return the number of lines drawn. */ unsigned int STextView::drawParagraph (SCanvas* c, bool islr, unsigned int line, const SLocation& l, const SLocation& lb, const SLocation& le, bool iswindow) { SV_UINT br; if (line < breaks.size()) br = breaks[line]; unsigned int currExt = 0; SLocation lm = l; unsigned int ls = textData.size(line); unsigned int mycliph = 0; /** * set clip to line so that we won't overflow... */ if (iswindow && clipw != 0 && cliph != 0) { int myclipy0 = (clipy > lm.y) ? clipy : lm.y; int myclipy1 = myclipy0 + lineHeight; if (myclipy1 > clipy + (int) cliph) { myclipy1 = clipy + (int) cliph; } mycliph = (myclipy1 > myclipy0) ? myclipy1-myclipy0 : 0; //fprintf (stderr, "clip=%d,%d wh=%u,%u\n", clipx, myclipy0, clipw, mycliph); if (mycliph) { ((SWindow*)c)->setClippingArea (clipx, myclipy0, clipw, mycliph); } } for (unsigned int i=0; i lm.y) ? clipy : lm.y; int myclipy1 = myclipy0 + lineHeight; if (myclipy1 > clipy + (int) cliph) { myclipy1 = clipy + (int) cliph; } mycliph = (myclipy1 > myclipy0) ? myclipy1-myclipy0 : 0; if (mycliph) { ((SWindow*)c)->setClippingArea (clipx, myclipy0, clipw, mycliph); } } } lm.x = islr ? l.x + (int) posBefore[line][i] : l.x-1-(int) posAfter[line][i]; unsigned int e = posAfter[line][i] - posBefore[line][i]; /* is it drawable ? */ if (lm.x < le.x && lb.x < lm.x + (int) e && lm.y < le.y && lb.y < lm.y + (int) lineHeight) { if (!iswindow || mycliph) drawGlyph (c, lm, e, STextIndex (line,i)); } } if (iswindow && clipw!=0 && cliph !=0) { ((SWindow*)c)->setClippingArea (clipx, clipy, clipw, cliph); } return currExt+1; } /** * Set Syntax Hilight Mode */ void STextView::setHighlightMode (SS_HighlightMode hlm) { highlightMode = hlm; } /** * Get Syntax Hilight Mode */ STextView::SS_HighlightMode STextView::getHighlightMode () const { return highlightMode; } /** * Set WordWrap Mode * @param pbm is true if line break is on */ void STextView::setWordWrap (bool lbm) { isWordWrapOn = lbm; setReordered (); } /** * Some stuff displays differently if this is editable. */ void STextView::setEditable (bool editable) { isEditable = editable; setReordered (); } /** * Get WordWrap Mode * @return true if line break is on. */ bool STextView::getWordWrap () const { return isWordWrapOn; } /** * Syntax Highlighting system. * Added by Maarten van Gompel * Note: this is a dumb system and merely colors single characters. */ void STextView::syntaxHighlight(STextIndex index, SPen* pen) const { switch (getHighlightMode()) { case HM_Simple: if (textData.isDelimiter(index)) { pen->setForeground(SColor("CornflowerBlue")); const SGlyph& g = textData.glyphAt (index); if (g.isEOL()) (pen->setForeground(SColor("orange"))); } else if (textData.isNumber(index)) { pen->setForeground(SColor("orange")); } return; case HM_SimpleDark: if (textData.isDelimiter(index)) { pen->setForeground(SColor("DeepSkyBlue4")); const SGlyph& g = textData.glyphAt (index); if (g.isEOL()) (pen->setForeground(SColor("orange4"))); } else if (textData.isNumber(index)) { pen->setForeground(SColor("orange4")); } return; case HM_HTML: default: break; } return; } /** * Draw one signle glyph on the screen. * @param c is the canvas to draw to * @param l is the location of the glyph. * @return the length of the text drawn */ void STextView::drawGlyph (SCanvas* c, SLocation& l, unsigned int ext, STextIndex index) { const SGlyph& g = textData.glyphAt (index); SS_Matrix2D dm; dm.y1 = -dm.y1; /* updown */ dm.translate (0, font.ascent ()); dm.translate ((double)l.x, (double)l.y); SPen p (lrpen); if (!g.isLR()) { p = rlpen; } unsigned int explevel = g.getExplicitLevel(); if (g.selected) { SColor fg = p.getForeground(); SColor bg = p.getBackground(); p.setForeground (bg); p.setBackground (fg); c->bitfill (fg, l.x, l.y, ext, lineHeight); } /* fade background according to embed level */ else if (isEditable && explevel!=0) { SColor bg = p.getBackground(); if (explevel > 5) explevel = 5; /* 5 shades max*/ /* This funny linear curve is the result of experiments */ double alpha = 1.0/2.0 + (1.0/2.0) * 0.9 * ((double)explevel)/5.0; /* we fade gray in with an alpha */ double cg = 0.5; SColor grey(cg,cg,cg, alpha); bg.blend (grey); p.setBackground (bg); c->bitfill (bg, l.x, l.y, ext, lineHeight); } else { /* * Syntax highlighting, an addition * by Maarten van Gompel */ syntaxHighlight(index, &p); /* change pen color if necessary */ } if (!lineend && g.isEOP()) return; SS_UCS4 fc = g.getFirstChar(); /* I would just check for SD_CC_Mn also */ if (!isEditable && (g.isEOL() || fc == SD_CD_LRM || fc == SD_CD_RLM || fc == SD_CD_ZWNJ || fc == SD_CD_ZWJ)) { return; } /* Zero width space */ if (fc == SD_CD_ZWSP) return; if (!g.isTab()) font.draw (c, p, dm, g); if (g.underlined) { unsigned int w = ext; unsigned int h = lineHeight/24+1; unsigned int base = (lineAscent + h >= lineHeight) ? lineHeight -1 : lineAscent + h; /* construct a square */ c->bitfill (underlineColor, l.x, l.y + (int) base - h, w, h); } } /** * This is called by the STextData */ void STextView::textChanged (void* src, const STextDataEvent& event) { /* The whole text has been cleared */ if (textData.size()==0) { wrapAndPosition(); SWindow* w = getWindow (); if (w) { /* request a redraw and clear the whole area */ w->redraw (true, location.x, location.y, size.width, size.height); } return; } /* overdraw */ int odw = (int) lineHeight / 3 + 1; STextIndex tb = textData.getMinTextIndex (event); STextIndex te = textData.getMaxTextIndex (event); unsigned int oldsize = lineSpan.size(); unsigned int oldspan = sane_index (lineSpan, oldsize); bool oldlr = textData.isLR(tb.line); SV_UINT oldbreaks; if (tb.line == te.line && tb.line < oldsize && tb.line < breaks.size()) { /* This is still the old breaks */ oldbreaks = breaks[tb.line]; } /* change in text contents */ SV_UINT mapBefore = textData.getLogicalMap(tb.line); SV_UINT mapAfter = mapBefore; if (!event.attribute) { /* For efficiency, multiline guys will make it only partial */ if (multiline) { /* was recalc */ wrapAndPosition (tb.line, te.line+1, (int)textData.size() - (int) lineSpan.size()); } else { wrapAndPosition (); } mapAfter = textData.getLogicalMap(tb.line); /* find the highest and visual index */ } SWindow* w = getWindow(); if (w == 0) { /* This is a strange place to return - but we needed to rebuild indeces */ return; } unsigned int newsize = lineSpan.size(); unsigned int newspan = sane_index (lineSpan, newsize); bool samebreak = false; bool newlr = textData.isLR(tb.line); bool drawwholeline = (newlr != oldlr && tb.line == te.line); if (tb.line == te.line && tb.line < newsize && tb.line = tb.index && n[i] <= te.index) { drawwholeline = true; } } } } if (tb.line == te.line && drawwholeline) { tb.index = 0; te.index = mapAfter.size(); } /* adjust tb te */ if (tb.line == te.line && !drawwholeline && samebreak) { unsigned int i; /* find out lowest common stuff in map */ unsigned int min = mapAfter.size() < mapBefore.size() ? mapAfter.size() : mapBefore.size(); /* make logical to visual maps */ SS_UINT * mapa = new SS_UINT[mapAfter.size()+1]; CHECK_NEW (mapa); for (i=0; i 0) { tb.index = mapAfter[0]; } else { tb.index = 0; } lowestvis=0; } /* find out if there is something between zero and lowes vis */ for (i=0; i= tb.index) { tb.index = mapa[i]; lowestvis = i; break; } } // if still between lowest and end there is a lower index, take 0. for (i=lowestvis; i 0) smallest--; tb.index = smallest; break; } } /* for attribute te.index is also used and mapafter = mapbefore */ if (event.attribute && te.index < mapAfter.size()) { unsigned int vis = mapAfter[te.index]; unsigned int max = mapAfter.size(); for (i=mapAfter.size(); i>vis; i--) { if (mapa[i-1] < te.index) { te.index= max; break; } max = mapa[i-1]; } } else { te.index = mapAfter.size(); } delete [] mapa; delete [] mapb; } SLocation lb = getTextLocation (tb); SLocation le = getTextLocation (te); /* * Get smallest and biggest. */ if (tb.line == te.line && samebreak && le.y == lb.y) { if (le.x < lb.x) { int tmp = lb.x; lb.x = le.x; le.x = tmp; } for (unsigned int i=tb.index; i<=te.index; i++) { SLocation l = getTextLocation (STextIndex(tb.line, i)); if (l.x < lb.x) lb = l; if (l.x > le.x) le = l; l = getTextLocation (STextIndex(tb.line, i), false); if (l.x < lb.x) lb = l; if (l.x > le.x) le = l; } } //fprintf (stderr, "lb.x =%d, le.x=%u\n", lb.x, le.x); /* make sure we are inside the window */ if (lb.y + (int)lineHeight < 0) lb.y = -(int)lineHeight; if (le.y > location.y + (int)size.height) le.y = size.height + location.y; /* Text content did not change, only the attribute */ int starty = (lb.y > 5) ? lb.y - odw: 0; unsigned int lheight = lineHeight + 2*odw; if (event.attribute) { /* single */ if (lb.y == le.y && samebreak) { /* we add 1 to make sure it is non-null positive */ w->redraw (true, lb.x-odw, starty, (unsigned int) (le.x-lb.x)+2*odw, lheight); } else // multiline - redraw whole thing. { le = getTextLocation (STextIndex (te.line, textData.size(te.line))); if (lb.y < le.y) /* always */ { w->redraw (true, location.x, starty, size.width, lheight + (unsigned int)(le.y-lb.y)); } else /* I dont know what happened - redraw */ { w->redraw (true, location.x, location.y, size.width, size.height); } } return; } /* Change is inside a single paragraph */ if (tb.line == te.line && oldsize == newsize && oldspan == newspan) { /* The whole change is on the same line (breaks did not change) */ if (lb.y == le.y && samebreak) { bool lrline = textData.isLR (tb.line); int wid = 0; if (lrline) { //lb.x = lb.x; wid = (int) size.width; /* till end of line */ } else { lb.x = 0; wid = le.x + location.x; } /* redraw till end of line */ w->redraw (true, lb.x-odw, starty, (unsigned int) wid + 2*odw, lheight); } else /* This is a multi-line paragraph change. redraw till end */ { le = getTextLocation (STextIndex (te.line, textData.size(te.line))); if (le.y > lb.y) /* always */ { w->redraw (true, location.x, starty, size.width, lheight + (unsigned int)(le.y-lb.y)); } else /* I dont know what happened - redraw */ { w->redraw (true, location.x, location.y, size.width, size.height); } } return; } /* Multi-paragraph change. Is it visible? */ if (starty < location.y + (int) size.height) { w->redraw (true, location.x, starty, size.width, location.y + (int)size.height - starty); } } /** * Makr lines so that they will recalculate */ void STextView::setReordered() { /* HACK FOR LABELS - they neeed to know their exact size */ if (!isEditable) { for (unsigned int i=0; i sum) { s -= removesum-sum; } else { s += sum-removesum; } lineSpan.replace (i, s); i++; } } preferredSize.height = (textData.size()==0) ? lineHeight : textData.size() * lineHeight; } /** * Caclulate the extent as one line. * It inserts an element at line in positions, and breaks. * The positions array will have the positions of the end * of the glyph, ragrdless of paragraph embedding, in LR order. * @param line is the line to calculate. * @return the linesspan */ unsigned int STextView::wrapAndPosition (unsigned int line, SH_UINT* cache) { /* first line is always visible - multiline */ if (!textData.isVisible(line)) { SV_UINT empty; posAfter.insert(line, empty); posBefore.insert(line, empty); breaks.insert(line, empty); /* make span 1 */ return 1; } /* +1 is only because of zero sized arrays */ SS_UCS4* logical = new SS_UCS4[textData.size(line)+1]; CHECK_NEW(logical); SS_UCS4* logicalBefore = new SS_UCS4[textData.size(line)+1]; CHECK_NEW(logicalBefore); SS_UCS4* visual = new SS_UCS4[textData.size(line)+1]; CHECK_NEW(visual); unsigned int ae=0; unsigned int ce=0; unsigned int le=0; unsigned int i; SV_UINT b; bool wrapNext = false; unsigned int lastbreak = 0; /* go through the text in logical order */ for (i=0; iget (g.charKey()); if (ce ==0) { ce = (unsigned int) (0.5 + font.width (g)); SS_UCS4 fc = g.getFirstChar(); if (!isEditable && (g.isEOL() || fc == SD_CD_LRM || fc == SD_CD_RLM || fc == SD_CD_ZWNJ || fc == SD_CD_ZWJ)) { ce = 1; } else if (fc == SD_CD_ZWSP) { ce = 1; } else if (g.isTab()) { int tabsize = (int)(4.0 * font.getSize()); if (tabsize < 1) tabsize = 1; ce = tabsize - (le % (unsigned int)tabsize); if (multiline && (le + ce)> size.width && le > 0) { /* force line break. */ ce = (int)(4.0 * font.getSize()); } } /* Shaped glyphs width and tab may change. */ if (g.getShapeArray()==0 && !g.isTab()) { cache->put (g.charKey(), ce); } } le += ce; ae += ce; logical[i] = ce; if (multiline && le > size.width && i > 0) { if (g.isTab()) { /* nothing to do. we break here */ } /* we might want to wrap earlier */ else if (isWordWrapOn && !textData.isDelimiter (STextIndex (line, i-1))) { unsigned int oldae = ae; unsigned int oldi = i; while (i>lastbreak && !textData.isDelimiter (STextIndex (line, i-1))) { ae -= logical[i]; i--; } /* emergency break */ if (i==lastbreak) { ae = oldae; i = oldi; } } le = logical[i]; b.append (i); lastbreak = i; } else if (wrapNext) { le = logical[i]; b.append (i); lastbreak = i; } wrapNext = (multiline && g.isEOL() && !g.isEOP()); } /* now b contains the logical positions where the glyph should start at 0 */ b.append (textData.size(line)); /* break the text into lines */ textData.setLineBreaks(line, b); if (preferredSize.width < ae) preferredSize.width = ae; /* make a visual map */ for (i=0; iisVisible()) return; w->redraw (true, location.x, location.y, size.width, size.height); } /** * Is new line shown? * @return true if newline characters are shown. */ bool STextView::getLineEndMark () const { return lineend; } /** * calculate the height of the document. */ unsigned int STextView::getDocumentHeight() const { if (textData.size() == 0) { return lineHeight; } unsigned int fheight = lineSpan[textData.size()-1]; if (textData.isProperLine (textData.size()-1)) { fheight += 1; } return (fheight * lineHeight); } /** * return the 'sane index'. * That is, at index 0 it should be 0 * at index at array->size() is should be the last element. */ static unsigned int sane_index (const SV_UINT& array, unsigned int index) { if (index == 0 || array.size() < index) return 0; return array[index-1]; } void STextView::setUnderlineColor (const SColor& c) { underlineColor = c; } /** * Mark this visible */ void STextView::setVisible (unsigned int line) { if (!textData.isVisible (line)) { textData.setVisible(line); wrapAndPosition (line, line+1, 0); } else if (textData.isReordered(line)) { wrapAndPosition (line, line+1, 0); } } /** * return the cursor index that is left (screen-wise) of * ci. * @checkembed is true check for embedding boundary */ SCursorIndex STextView::leftOf (const SCursorIndex& ci) { if (ci.textIndex.line >= textData.size()) { return SCursorIndex(ci.textIndex.line, ci.textIndex.index); } setVisible (ci.textIndex.line); SCursorIndex cn = moveCursor (ci, false); return SCursorIndex (cn); } SCursorIndex STextView::rightOf (const SCursorIndex& ci) { if (ci.textIndex.line >= textData.size()) { return SCursorIndex(ci.textIndex.line, ci.textIndex.index); } setVisible (ci.textIndex.line); SCursorIndex cn = moveCursor (ci, true); return SCursorIndex (cn); } /** * Move the cursor up or down one slot visuallly. * You have to expand the paragrapgh before this call. * @param ci is the input index. * @praram isup is true if we walk right visuallly. * @return 1 index up or down. */ SCursorIndex STextView::moveCursor (const SCursorIndex& ci, bool isup) { SV_UINT map = textData.getLogicalMap(ci.textIndex.line); if (map.size()==0) return SCursorIndex(ci.textIndex.line,0); if (textData.isProperLine (ci.textIndex.line)) { map.truncate (map.size()-1); } if (map.size()==0) return SCursorIndex(ci.textIndex.line,0); /* we need to find the current index in the map. */ int current = map.size(); for (unsigned int i=0; i= (int)map.size()) { current = (int)map.size(); resbefore = true; return SCursorIndex (ci.textIndex.line, (unsigned int) current, resbefore); } resindex = map[(unsigned int) current]; /* check changed index */ SCursorIndex res (ci.textIndex.line, resindex, resbefore); bool nlr = textData.isLR (res.textIndex); /* rl before is logical after switch */ /* nlr already switched */ /* back to logical order */ if (!nlr) res.before = !res.before; /* embed changed */ if (!samembed) { res.before = !res.before; return SCursorIndex (res); } /* direction changed */ if (clr != nlr) { res.before = !res.before; } return SCursorIndex (res); }