]> git.lyx.org Git - lyx.git/blobdiff - src/TextMetrics.cpp
* doxy
[lyx.git] / src / TextMetrics.cpp
index 722b7d6b274a1dbb54b95e8061402fda1b8b5dce..19a71ffbb99154ad41ed597c0ab4431929aaf0b5 100644 (file)
@@ -40,6 +40,7 @@
 #include "Text.h"
 #include "TextClass.h"
 #include "VSpace.h"
+#include "WordLangTuple.h"
 
 #include "insets/InsetText.h"
 
 #include "frontends/Painter.h"
 
 #include "support/debug.h"
-#include <cstdlib>
+#include "support/docstring_list.h"
 #include "support/lassert.h"
 
+#include <cstdlib>
+
 using namespace std;
 
 
@@ -132,7 +135,7 @@ TextMetrics::TextMetrics(BufferView * bv, Text * text)
        dim_.asc = 10;
        dim_.des = 10;
 
-       //text_->updateLabels(bv->buffer());
+       //text_->updateBuffer(bv->buffer());
 }
 
 
@@ -176,17 +179,6 @@ ParagraphMetrics & TextMetrics::parMetrics(pit_type pit, bool redo)
 }
 
 
-int TextMetrics::parPosition(pit_type pit) const
-{
-       if (pit < par_metrics_.begin()->first)
-               return -1000000;
-       if (pit > par_metrics_.rbegin()->first)
-               return +1000000;
-
-       return par_metrics_[pit].position();
-}
-
-
 bool TextMetrics::metrics(MetricsInfo & mi, Dimension & dim, int min_width)
 {
        LASSERT(mi.base.textwidth > 0, /**/);
@@ -241,11 +233,9 @@ int TextMetrics::rightMargin(pit_type const pit) const
 
 void TextMetrics::applyOuterFont(Font & font) const
 {
-       Font lf(font_);
-       lf.fontInfo().reduce(bv_->buffer().params().getFont().fontInfo());
-       lf.fontInfo().realize(font.fontInfo());
-       lf.setLanguage(font.language());
-       font = lf;
+       FontInfo lf(font_.fontInfo());
+       lf.reduce(bv_->buffer().params().getFont().fontInfo());
+       font.fontInfo().realize(lf); 
 }
 
 
@@ -332,13 +322,14 @@ bool TextMetrics::isRTLBoundary(pit_type pit, pos_type pos) const
 bool TextMetrics::isRTLBoundary(pit_type pit, pos_type pos,
                Font const & font) const
 {
-       if (!lyxrc.rtl_support)
+       if (!lyxrc.rtl_support
+           // no RTL boundary at paragraph start
+           || pos == 0
+           // if the metrics have not been calculated, then we are not
+           // on screen and can safely ignore issues about boundaries.
+           || !contains(pit))
                return false;
 
-       // no RTL boundary at paragraph start
-       if (pos == 0)
-               return false;
-       
        ParagraphMetrics & pm = par_metrics_[pit];
        // no RTL boundary in empty paragraph
        if (pm.rows().empty())
@@ -353,13 +344,6 @@ bool TextMetrics::isRTLBoundary(pit_type pit, pos_type pos,
                return false;
 
        Paragraph const & par = text_->getPar(pit);
-       bool left = font.isVisibleRightToLeft();
-       bool right;
-       if (pos == par.size())
-               right = par.isRTL(bv_->buffer().params());
-       else
-               right = displayFont(pit, pos).isVisibleRightToLeft();
-       
        // no RTL boundary at line break:
        // abc|\n    -> move right ->   abc\n       (and not:    abc\n|
        // FED                          FED|                     FED     )
@@ -369,6 +353,13 @@ bool TextMetrics::isRTLBoundary(pit_type pit, pos_type pos,
                        || par.isSeparator(pos - 1)))
                return false;
        
+       bool left = font.isVisibleRightToLeft();
+       bool right;
+       if (pos == par.size())
+               right = par.isRTL(bv_->buffer().params());
+       else
+               right = displayFont(pit, pos).isVisibleRightToLeft();
+       
        return left != right;
 }
 
@@ -376,7 +367,7 @@ bool TextMetrics::isRTLBoundary(pit_type pit, pos_type pos,
 bool TextMetrics::redoParagraph(pit_type const pit)
 {
        Paragraph & par = text_->getPar(pit);
-       // IMPORTANT NOTE: We pass 'false' explicitely in order to not call
+       // IMPORTANT NOTE: We pass 'false' explicitly in order to not call
        // redoParagraph() recursively inside parMetrics.
        Dimension old_dim = parMetrics(pit, false).dim();
        ParagraphMetrics & pm = par_metrics_[pit];
@@ -413,7 +404,7 @@ bool TextMetrics::redoParagraph(pit_type const pit)
                LYXERR(Debug::INFO, "MacroContext not initialised!"
                        << " Going through the buffer again and hope"
                        << " the context is better then.");
-               bv_->buffer().updateLabels();
+               bv_->buffer().updateBuffer();
                parPos = text_->macrocontextPosition();
                LASSERT(!parPos.empty(), /**/);
                parPos.pit() = pit;
@@ -444,7 +435,7 @@ bool TextMetrics::redoParagraph(pit_type const pit)
                        - right_margin;
                Font const & font = ii->inset->noFontChange() ?
                        bufferfont : displayFont(pit, ii->pos);
-               MacroContext mc(buffer, parPos);
+               MacroContext mc(&buffer, parPos);
                MetricsInfo mi(bv_, font.fontInfo(), w, mc);
                ii->inset->metrics(mi, dim);
                Dimension const old_dim = pm.insetDimension(ii->inset);
@@ -791,8 +782,8 @@ private:
 
 } // anon namespace
 
-pit_type TextMetrics::rowBreakPoint(int width, pit_type const pit,
-               pit_type pos) const
+pos_type TextMetrics::rowBreakPoint(int width, pit_type const pit,
+               pos_type pos) const
 {
        ParagraphMetrics const & pm = par_metrics_[pit];
        Paragraph const & par = text_->getPar(pit);
@@ -828,11 +819,24 @@ pit_type TextMetrics::rowBreakPoint(int width, pit_type const pit,
        // pixel width since last breakpoint
        int chunkwidth = 0;
 
+       docstring const s(1, char_type(0x00B6));
+       Font f;
+       int par_marker_width = theFontMetrics(f).width(s);
+
        FontIterator fi = FontIterator(*this, par, pit, pos);
        pos_type point = end;
        pos_type i = pos;
+
+       ParagraphList const & pars_ = text_->paragraphs();
+       bool const draw_par_end_marker = lyxrc.paragraph_markers
+               && size_type(pit + 1) < pars_.size();
+                               
        for ( ; i < end; ++i, ++fi) {
                int thiswidth = pm.singleWidth(i, *fi);
+               
+               if (draw_par_end_marker && i == end - 1)
+                       // enlarge the last character to hold the end-of-par marker
+                       thiswidth += par_marker_width;
 
                // add inline completion width
                if (inlineCompletionLPos == i) {
@@ -962,6 +966,18 @@ int TextMetrics::rowWidth(int right_margin, pit_type const pit,
                }
        }
 
+       // count the paragraph end marker.
+       if (end == par.size() && lyxrc.paragraph_markers) {
+               ParagraphList const & pars_ = text_->paragraphs();
+               if (size_type(pit + 1) < pars_.size()) {
+                       // enlarge the last character to hold the
+                       // end-of-par marker
+                       docstring const s(1, char_type(0x00B6));
+                       Font f;
+                       w += theFontMetrics(f).width(s);
+               }
+       }
+
        if (body_pos > 0 && body_pos >= end) {
                FontMetrics const & fm = theFontMetrics(
                        text_->labelFont(par));
@@ -1206,6 +1222,12 @@ pos_type TextMetrics::getColumnNearX(pit_type const pit,
                return 0;
        }
 
+       // if the first character is a separator, we are in RTL
+       // text. This character will not be painted on screen
+       // and thus we should not count it and skip to the next.
+       if (par.isSeparator(bidi.vis2log(vc)))
+               ++vc;
+
        while (vc < end && tmpx <= x) {
                c = bidi.vis2log(vc);
                last_tmpx = tmpx;
@@ -1403,7 +1425,7 @@ pit_type TextMetrics::getPitNearY(int y)
 }
 
 
-Row const & TextMetrics::getPitAndRowNearY(int y, pit_type & pit,
+Row const & TextMetrics::getPitAndRowNearY(int y, pit_type & pit,
        bool assert_in_view, bool up)
 {
        ParagraphMetrics const & pm = par_metrics_[pit];
@@ -1417,25 +1439,29 @@ Row const & TextMetrics::getPitAndRowNearY(int y, pit_type & pit,
                if (yy + rit->height() > y)
                        break;
 
-       if (assert_in_view && yy + rit->height() != y) {
-               if (!up) {
-                       if (rit != pm.rows().begin())
+       if (assert_in_view) {
+               if (!up && yy + rit->height() > y) {
+                       if (rit != pm.rows().begin()) {
+                               y = yy;
                                --rit;
-                       else if (pit != 0) {
+                       else if (pit != 0) {
                                --pit;
                                newParMetricsUp();
                                ParagraphMetrics const & pm2 = par_metrics_[pit];
                                rit = pm2.rows().end();
                                --rit;
+                               y = yy;
                        }
-               } else  {
-                       if (rit != rlast)
+               } else if (up && yy != y) {
+                       if (rit != rlast) {
+                               y = yy + rit->height();
                                ++rit;
-                       else if (pit != int(par_metrics_.size())) {
+                       } else if (pit < int(text_->paragraphs().size()) - 1) {
                                ++pit;
                                newParMetricsDown();
                                ParagraphMetrics const & pm2 = par_metrics_[pit];
                                rit = pm2.rows().begin();
+                               y = pm2.position();
                        }
                }
        }
@@ -1454,10 +1480,11 @@ Inset * TextMetrics::editXY(Cursor & cur, int x, int y,
        }
        pit_type pit = getPitNearY(y);
        LASSERT(pit != -1, return 0);
+       
+       int yy = y; // is modified by getPitAndRowNearY
+       Row const & row = getPitAndRowNearY(yy, pit, assert_in_view, up);
 
-       Row const & row = getPitAndRowNearY(y, pit, assert_in_view, up);
-       bool bound = false;
-
+       bool bound = false; // is modified by getColumnNearX
        int xx = x; // is modified by getColumnNearX
        pos_type const pos = row.pos()
                + getColumnNearX(pit, row, xx, bound);
@@ -1467,7 +1494,7 @@ Inset * TextMetrics::editXY(Cursor & cur, int x, int y,
        cur.setTargetX(x);
 
        // try to descend into nested insets
-       Inset * inset = checkInsetHit(x, y);
+       Inset * inset = checkInsetHit(x, yy);
        //lyxerr << "inset " << inset << " hit at x: " << x << " y: " << y << endl;
        if (!inset) {
                // Either we deconst editXY or better we move current_font
@@ -1479,23 +1506,22 @@ Inset * TextMetrics::editXY(Cursor & cur, int x, int y,
        }
 
        ParagraphList const & pars = text_->paragraphs();
-       Inset const * insetBefore = pos ? pars[pit].getInset(pos - 1) : 0;
-       //Inset * insetBehind = pars[pit].getInset(pos);
+       Inset const * inset_before = pos ? pars[pit].getInset(pos - 1) : 0;
 
        // This should be just before or just behind the
        // cursor position set above.
-       LASSERT((pos != 0 && inset == insetBefore)
+       LASSERT(inset == inset_before 
                || inset == pars[pit].getInset(pos), /**/);
 
        // Make sure the cursor points to the position before
        // this inset.
-       if (inset == insetBefore) {
+       if (inset == inset_before) {
                --cur.pos();
                cur.boundary(false);
        }
 
        // Try to descend recursively inside the inset.
-       inset = inset->editXY(cur, x, y);
+       inset = inset->editXY(cur, x, yy);
 
        if (cur.top().text() == text_)
                cur.setCurrentFont();
@@ -1622,6 +1648,8 @@ int TextMetrics::cursorX(CursorSlice const & sl,
        else if (ppos >= end)
                cursor_vpos = text_->isRTL(par) ? row_pos : end;
        else if (ppos > row_pos && ppos >= end)
+               //FIXME: this code is never reached!
+               //       (see http://www.lyx.org/trac/changeset/8251)
                // Place cursor after char at (logical) position pos - 1
                cursor_vpos = (bidi.level(ppos - 1) % 2 == 0)
                        ? bidi.log2vis(ppos - 1) + 1 : bidi.log2vis(ppos - 1);
@@ -1656,6 +1684,15 @@ int TextMetrics::cursorX(CursorSlice const & sl,
        if (end > 0 && end < par.size() && par.isSeparator(end - 1))
                skipped_sep_vpos = bidi.log2vis(end - 1);
 
+       if (lyxrc.paragraph_markers && text_->isRTL(par)) {
+               ParagraphList const & pars_ = text_->paragraphs();
+               if (size_type(pit + 1) < pars_.size()) {
+                       FontInfo f;
+                       docstring const s = docstring(1, char_type(0x00B6));
+                       x += theFontMetrics(f).width(s);
+               }
+       }
+
        // Inline completion RTL special case row_pos == cursor_pos:
        // "__|b" => cursor_pos is right of __
        if (row_pos == inlineCompletionVPos && row_pos == cursor_vpos) {
@@ -2093,6 +2130,19 @@ void TextMetrics::drawParagraph(PainterInfo & pi, pit_type pit, int x, int y) co
                row.setCrc(pm.computeRowSignature(row, bparams));
                bool row_has_changed = row.changed();
 
+               // Take this opportunity to spellcheck the row contents.
+               if (row_has_changed && lyxrc.spellcheck_continuously) {
+                       WordLangTuple wl;
+                       // dummy variable, not used.
+                       static docstring_list suggestions;
+                       pos_type from = row.pos();
+                       pos_type to = row.endpos();
+                       while (from < row.endpos()) {
+                               text_->getPar(pit).spellCheck(from, to, wl, suggestions, false);
+                               from = to + 1;
+                       }
+               }
+
                // Don't paint the row if a full repaint has not been requested
                // and if it has not changed.
                if (!pi.full_repaint && !row_has_changed) {
@@ -2132,11 +2182,16 @@ void TextMetrics::drawParagraph(PainterInfo & pi, pit_type pit, int x, int y) co
                rp.paintAppendix();
                rp.paintDepthBar();
                rp.paintChangeBar();
-               if (i == 0)
+               bool const is_rtl = text_->isRTL(text_->getPar(pit));
+               if (i == 0 && !is_rtl)
                        rp.paintFirst();
+               if (i == nrows - 1 && is_rtl)
+                       rp.paintLast();
                rp.paintText();
-               if (i == nrows - 1)
+               if (i == nrows - 1 && !is_rtl)
                        rp.paintLast();
+               if (i == 0 && is_rtl)
+                       rp.paintFirst();
                y += row.descent();
                // Restore full_repaint status.
                pi.full_repaint = tmp;
@@ -2201,7 +2256,7 @@ void TextMetrics::drawRowSelection(PainterInfo & pi, int x, Row const & row,
 
        // go through row and draw from RTL boundary to RTL boundary
        while (cur < end) {
-               bool drawNow = false;
+               bool draw_now = false;
 
                // simplified cursorForward code below which does not
                // descend into insets and which does not go into the
@@ -2216,7 +2271,7 @@ void TextMetrics::drawRowSelection(PainterInfo & pi, int x, Row const & row,
                        // because:  ab|cDDEEFFghi -> abc|DDEEFFghi
                        ++cur.pos();
                        cur.boundary(true);
-                       drawNow = true;
+                       draw_now = true;
                } else {
                        // move right
                        ++cur.pos();
@@ -2231,7 +2286,7 @@ void TextMetrics::drawRowSelection(PainterInfo & pi, int x, Row const & row,
                        x1 = cursorX(cur.top(), cur.boundary());
                }
 
-               if (!(cur < end) || drawNow) {
+               if (!(cur < end) || draw_now) {
                        x2 = cursorX(cur.top(), cur.boundary());
                        pi.pain.fillRectangle(x + min(x1,x2), y1, abs(x2 - x1), y2 - y1,
                                Color_selection);