The branch, str-metrics, has been updated.

- Log -----------------------------------------------------------------

commit 717cf4e0965eba9f51a82ae30b60c8950fed84f0
Author: Jean-Marc Lasgouttes <lasgout...@lyx.org>
Date:   Thu Jul 18 00:25:08 2013 +0200

    More work for setting cursor correctly
    
    * convert cursorX to new scheme; old computation is still there for the 
sake of comparison.
    
    * use Row to compute separator width in computeRowMetrics; again, the old 
code is retained for now.
    
    * Get rid of rowWidth()

diff --git a/00README_STR_METRICS_BRANCH b/00README_STR_METRICS_BRANCH
index 4b6ef10..7c87c0a 100644
--- a/00README_STR_METRICS_BRANCH
+++ b/00README_STR_METRICS_BRANCH
@@ -11,33 +11,33 @@ What is done:
   metrics are computed. The list of elements is stored in the row object
   in visual ordering, not logical.
 
+* re-implement cursorX using row elements
+
 * Implement proper string metrics computation (with cache), when
   lyxrc.force_paint_single_char is false.
 
 Next steps:
 
-* get rid of rowWidth (breakRow does compute this)
-
 * re-implement getColumnNearX using row elements
 
-* re-implement cursorX using row elements
+* get rid of old code of cursorX and getColumnNearX (which have been
+  kept for comparison purpose).
 
 * re-implement row painting using row elements (can it be done?)
 
 * profile and see how performance can be improved.
 
+* Document the code
+
 Difference in behavior
 * end of paragraph markers metrics are computed with the font of the
   actual text, not default font. This will be extended to the other
   methods.
 
+* When cursor is after a LtR separator just before a RtL chunk, the
+  cursor posiiton is computed better with the new code.
+
 Other differences that should be considered as bugs
 * words longer than the screen are no monger broken at an arbitrary
   point. This is a problem for languages like chinese that do not use
   separators.
-
-* there are still some difference in width computation wrt
-  TextMetrics::rowWidth. This happens in particular with Description
-  environment when the row is broken at bodypos. The method rowWidth
-  is kept for now in order to be able to detect row parsing errors,
-  but it could be removed right now.
diff --git a/src/Row.cpp b/src/Row.cpp
index ce1a440..9e40986 100644
--- a/src/Row.cpp
+++ b/src/Row.cpp
@@ -3,11 +3,11 @@
  * This file is part of LyX, the document processor.
  * Licence details can be found in the file COPYING.
  *
- * \author unknown
  * \author Lars Gullik Bjønnes
  * \author John Levon
  * \author André Pönitz
  * \author Jürgen Vigna
+ * \author Jean-Marc Lasgouttes
  *
  * Full author contact details are available in file CREDITS.
  *
@@ -23,13 +23,36 @@
 #include "frontends/FontMetrics.h"
 
 #include "support/debug.h"
+#include "support/lassert.h"
 
+#include <algorithm>
 #include <ostream>
 
 using namespace std;
 
 namespace lyx {
 
+using frontend::FontMetrics;
+
+double Row::Element::pos2x(pos_type const i) const
+{
+       bool const rtl = font.isVisibleRightToLeft();
+
+       // handle first the two bounds of the element
+       if ((!rtl && pos >= i) || (rtl && endpos <= i))
+               return 0;
+       if ((!rtl && endpos <= i) || (rtl && pos >= i))
+               return width();
+
+       FontMetrics const & fm = theFontMetrics(font);
+       // FIXME Avoid caching of metrics there?
+       int const w = fm.width(str.substr(0, i - pos));
+       if (rtl)
+               return width() - w;
+       else
+               return w;
+}
+
 
 Row::Row()
        : separator(0), label_hfill(0), x(0),
@@ -121,28 +144,46 @@ bool Row::selection() const
        return sel_beg != -1 && sel_end != -1;
 }
 
+
+ostream & operator<<(ostream & os, Row::Element const & e)
+{
+       if (e.font.isVisibleRightToLeft())
+               os << e.endpos << "<<" << e.pos << " ";
+       else
+               os << e.pos << ">>" << e.endpos << " ";
+
+       switch (e.type) {
+       case Row::Element::STRING:
+               os << "STRING: `" << to_utf8(e.str) << "'";
+               break;
+       case Row::Element::COMPLETION:
+               os << "COMPLETION: `" << to_utf8(e.str) << "'";
+               break;
+       case Row::Element::INSET:
+               os << "INSET: " << to_utf8(e.inset->layoutName());
+               break;
+       case Row::Element::SEPARATOR:
+               os << "SEPARATOR: " << e.dim.wid << "+" << e.extra;
+               break;
+       case Row::Element::SPACE:
+               os << "SPACE: " << e.dim.wid;
+               break;
+       }
+       return os;
+}
+
+
 ostream & operator<<(ostream & os, Row const & row)
 {
        os << " pos: " << row.pos_ << " end: " << row.end_
           << " width: " << row.dim_.wid
           << " ascent: " << row.dim_.asc
-          << " descent: " << row.dim_.des << "\n";
+          << " descent: " << row.dim_.des
+          << " separator: " << row.separator
+          << " label_hfill : " << row.label_hfill << "\n";
        Row::Elements::const_iterator it = row.elements_.begin();
        for ( ; it != row.elements_.end() ; ++it) {
-               switch (it->type) {
-               case Row::Element::STRING:
-                       os << "**STRING: " << to_utf8(it->str) << endl;
-                       break;
-               case Row::Element::INSET:
-                       os << "**INSET: " << to_utf8(it->inset->layoutName()) 
<< endl;
-                       break;
-               case Row::Element::SEPARATOR:
-                       os << "**SEPARATOR: " << endl;
-                       break;
-               case Row::Element::SPACE:
-                       os << "**SPACE: " << it->dim.wid << endl;
-                       break;
-               }
+               os << "** " << *it << endl;
        }
        return os;
 }
@@ -200,16 +241,16 @@ void Row::add(pos_type const pos, char_type const c,
 }
 
 
-void Row::add(pos_type const pos, docstring const & s,
-             Font const & f, Change const & ch)
+void Row::addCompletion(pos_type const pos, docstring const & s,
+                       Font const & f, Change const & ch)
 {
-       if (!sameString(f, ch)) {
-               finalizeLast();
-               Element e(Element::STRING, pos, f, ch);
-               elements_.push_back(e);
-       }
-       back().str += s;
-       back().endpos = pos + 1;
+       finalizeLast();
+       Element e(Element::COMPLETION, pos, f, ch);
+       e.str = s;
+       // A completion has no size
+       e.endpos = pos;
+       elements_.push_back(e);
+       finalizeLast();
 }
 
 
@@ -229,7 +270,7 @@ void Row::addSpace(pos_type const pos, int const width,
                   Font const & f, Change const & ch)
 {
        finalizeLast();
-       Element e(Element::SEPARATOR, pos, f, ch);
+       Element e(Element::SPACE, pos, f, ch);
        e.dim.wid = width;
        elements_.push_back(e);
        dim_.wid += e.dim.wid;
@@ -250,13 +291,13 @@ void Row::separate_back(pos_type const keep)
        int i = elements_.size();
        int new_end = end_;
        int new_wid = dim_.wid;
-       if (i > 0 && elements_[i - 1].isLineSeparator() && new_end > keep) {
+       if (i > 0 && elements_[i - 1].isSeparator() && new_end > keep) {
                --i;
                new_end = elements_[i].pos;
                new_wid -= elements_[i].dim.wid;
        }
 
-       while (i > 0 && !elements_[i - 1].isLineSeparator() && new_end > keep) {
+       while (i > 0 && !elements_[i - 1].isSeparator() && new_end > keep) {
                --i;
                new_end = elements_[i].pos;
                new_wid -= elements_[i].dim.wid;
@@ -275,14 +316,14 @@ void Row::reverseRtL()
        pos_type const end = elements_.size();
        while (i < end) {
                // skip LtR elements
-               while (!elements_[i].font.isRightToLeft() && i < end)
+               while (i < end && !elements_[i].font.isRightToLeft())
                        ++i;
                if (i >= end)
                        break;
 
                // look for a RtL sequence
                pos_type j = i;
-               while (elements_[j].font.isRightToLeft() && j < end)
+               while (j < end && elements_[j].font.isRightToLeft())
                        ++j;
                reverse(elements_.begin() + i, elements_.begin() + j);
                i = j;
diff --git a/src/Row.h b/src/Row.h
index fc542f9..03aeb74 100644
--- a/src/Row.h
+++ b/src/Row.h
@@ -43,17 +43,23 @@ public:
        struct Element {
                enum Type {
                        STRING,
+                       COMPLETION,
                        SEPARATOR,
                        INSET,
                        SPACE
                };
 
-               Element(Type const t, pos_type p, Font const & f, Change const 
& ch) 
-                       : type(t), pos(p), endpos(p + 1), inset(0), 
final(false),
-                         font(f), change(ch) {}
+               Element(Type const t, pos_type p, Font const & f, Change const 
& ch)
+                       : type(t), pos(p), endpos(p + 1), inset(0),
+                         extra(0), font(f), change(ch), final(false) {}
 
                //
-               bool isLineSeparator() const { return type == SEPARATOR; }
+               bool isSeparator() const { return type == SEPARATOR; }
+               // returns total width of element, including separator overhead
+               double width() const { return dim.wid + extra; };
+               // returns position in pixels (from the left) of position
+               // \param i in the row element
+               double pos2x(pos_type const i) const;
 
                // The kind of row element
                Type type;
@@ -61,20 +67,26 @@ public:
                pos_type pos;
                // first position after the element in the paragraph
                pos_type endpos;
-               // The dimension of the chunk (only width for strings)
+               // The dimension of the chunk (does not contains the
+               // separator correction)
                Dimension dim;
 
-               // Non-zero if element is an inset
+               // Non-zero only if element is an inset
                Inset const * inset;
 
+               // Only non-null for separator elements
+               double extra;
+
                // Non-empty if element is a string or separator
                docstring str;
-               // is it possible to add contents to this element?
-               bool final;
                //
                Font font;
                //
                Change change;
+               // is it possible to add contents to this element?
+               bool final;
+
+               friend std::ostream & operator<<(std::ostream & os, Element 
const & row);
        };
 
 
@@ -127,13 +139,29 @@ public:
        void add(pos_type pos, char_type const c,
                 Font const & f, Change const & ch);
        ///
-       void add(pos_type pos, docstring const & s,
-                Font const & f, Change const & ch);
+       void addCompletion(pos_type pos, docstring const & s,
+                          Font const & f, Change const & ch);
        ///
        void addSeparator(pos_type pos, char_type const c,
                          Font const & f, Change const & ch);
        ///
        void addSpace(pos_type pos, int width, Font const & f, Change const & 
ch);
+
+       ///
+       typedef std::vector<Element> Elements;
+       ///
+       typedef Elements::iterator iterator;
+       ///
+       typedef Elements::const_iterator const_iterator;
+       ///
+       iterator begin() { return elements_.begin(); }
+       ///
+       iterator end() { return elements_.end(); }
+       ///
+       const_iterator begin() const { return elements_.begin(); }
+       ///
+       const_iterator end() const { return elements_.end(); }
+
        ///
        bool empty() const { return elements_.empty(); }
        ///
@@ -198,8 +226,6 @@ private:
        bool sameString(Font const & f, Change const & ch) const;
 
        ///
-       typedef std::vector<Element> Elements;
-       ///
        Elements elements_;
 
        /// has the Row appearance changed since last drawing?
diff --git a/src/TextMetrics.cpp b/src/TextMetrics.cpp
index b89044f..45ab2b6 100644
--- a/src/TextMetrics.cpp
+++ b/src/TextMetrics.cpp
@@ -27,32 +27,25 @@
 #include "CoordCache.h"
 #include "Cursor.h"
 #include "CutAndPaste.h"
-#include "Encoding.h"
 #include "HSpace.h"
 #include "InsetList.h"
 #include "Layout.h"
-#include "Length.h"
 #include "LyXRC.h"
 #include "MetricsInfo.h"
 #include "ParagraphParameters.h"
-#include "ParIterator.h"
 #include "rowpainter.h"
 #include "Text.h"
 #include "TextClass.h"
 #include "VSpace.h"
-#include "WordLangTuple.h"
 
 #include "insets/InsetText.h"
 
-#include "mathed/MacroTable.h"
 #include "mathed/MathMacroTemplate.h"
 
 #include "frontends/FontMetrics.h"
 #include "frontends/Painter.h"
 
 #include "support/debug.h"
-#include "support/docstring_list.h"
-#include "support/gettext.h"
 #include "support/lassert.h"
 
 #include <cstdlib>
@@ -64,20 +57,32 @@ namespace lyx {
 
 using frontend::FontMetrics;
 
-static int numberOfSeparators(Paragraph const & par, Row const & row)
+namespace {
+
+int numberOfSeparators(Row const & row)
 {
-       pos_type const first = max(row.pos(), par.beginOfBody());
-       pos_type const last = row.endpos() - 1;
        int n = 0;
-       for (pos_type p = first; p < last; ++p) {
-               if (par.isSeparator(p))
+       Row::const_iterator cit = row.begin();
+       Row::const_iterator const end = row.end();
+       for ( ; cit != end ; ++cit)
+               if (cit->isSeparator())
                        ++n;
-       }
        return n;
 }
 
 
-static int numberOfLabelHfills(Paragraph const & par, Row const & row)
+void setSeparatorWidth(Row & row, double w)
+{
+       row.separator = w;
+       Row::iterator it = row.begin();
+       Row::iterator const end = row.end();
+       for ( ; it != end ; ++it)
+               if (it->isSeparator())
+                       it->extra = w;
+}
+
+
+int numberOfLabelHfills(Paragraph const & par, Row const & row)
 {
        pos_type last = row.endpos() - 1;
        pos_type first = row.pos();
@@ -98,28 +103,21 @@ static int numberOfLabelHfills(Paragraph const & par, Row 
const & row)
 }
 
 
-static int numberOfHfills(Paragraph const & par, Row const & row)
+int numberOfHfills(Row const & row, pos_type const body_pos)
 {
-       pos_type const last = row.endpos();
-       pos_type first = row.pos();
-
-       // hfill *DO* count at the beginning of paragraphs!
-       if (first) {
-               while (first < last && par.isHfill(first))
-                       ++first;
-       }
-
-       first = max(first, par.beginOfBody());
-
        int n = 0;
-       for (pos_type p = first; p < last; ++p) {
-               if (par.isHfill(p))
+       Row::const_iterator cit = row.begin();
+       Row::const_iterator const end = row.end();
+       for ( ; cit != end ; ++cit)
+               if (cit->pos >= body_pos
+                   && cit->inset && cit->inset->isHfill())
                        ++n;
-       }
        return n;
 }
 
 
+}
+
 /////////////////////////////////////////////////////////////////////
 //
 // TextMetrics
@@ -235,7 +233,7 @@ void TextMetrics::applyOuterFont(Font & font) const
 {
        FontInfo lf(font_.fontInfo());
        lf.reduce(bv_->buffer().params().getFont().fontInfo());
-       font.fontInfo().realize(lf); 
+       font.fontInfo().realize(lf);
 }
 
 
@@ -347,19 +345,19 @@ bool TextMetrics::isRTLBoundary(pit_type pit, pos_type 
pos,
        // no RTL boundary at line break:
        // abc|\n    -> move right ->   abc\n       (and not:    abc\n|
        // FED                          FED|                     FED     )
-       if (startpos == pos && endpos == pos && endpos != par.size() 
-               && (par.isNewline(pos - 1) 
-                       || par.isLineSeparator(pos - 1) 
+       if (startpos == pos && endpos == pos && endpos != par.size()
+               && (par.isNewline(pos - 1)
+                       || par.isLineSeparator(pos - 1)
                        || 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;
 }
 
@@ -467,12 +465,6 @@ bool TextMetrics::redoParagraph(pit_type const pit)
                row.pos(first);
                breakRow(row, right_margin, pit);
                setRowHeight(row, pit);
-               int w = row.width();
-               row.dimension().wid = rowWidth(right_margin, pit, first, 
row.endpos());
-               if (row.width() != w) {
-                       lyxerr << w << " => " << row.width() << ", body=" << 
par.beginOfBody() << ", size=" << par.size()<< ", inset=" << 
par.inInset().layoutName()<< endl;
-                       lyxerr << row;
-               }
                row.setChanged(false);
                if (row_index || row.endpos() < par.size())
                        // If there is more than one row, expand the text to
@@ -499,7 +491,6 @@ bool TextMetrics::redoParagraph(pit_type const pit)
                Row & row = pm.rows()[row_index];
                row.pos(first);
                row.endpos(first);
-               row.dimension().wid = rowWidth(right_margin, pit, first, first);
                setRowHeight(row, pit);
                row.setChanged(false);
                int const max_row_width = max(dim_.wid, row.width());
@@ -516,6 +507,57 @@ bool TextMetrics::redoParagraph(pit_type const pit)
 }
 
 
+int TextMetrics::getAlign(Paragraph const & par, pos_type const pos) const
+{
+       Layout const & layout = par.layout();
+
+       int align;
+       if (par.params().align() == LYX_ALIGN_LAYOUT)
+               align = layout.align;
+       else
+               align = par.params().align();
+
+       // handle alignment inside tabular cells
+       Inset const & owner = text_->inset();
+       switch (owner.contentAlignment()) {
+       case LYX_ALIGN_CENTER:
+       case LYX_ALIGN_LEFT:
+       case LYX_ALIGN_RIGHT:
+               if (align == LYX_ALIGN_NONE || align == LYX_ALIGN_BLOCK)
+                       align = owner.contentAlignment();
+               break;
+       default:
+               // unchanged (use align)
+               break;
+       }
+
+       // Display-style insets should always be on a centered row
+       if (Inset const * inset = par.getInset(pos)) {
+               switch (inset->display()) {
+               case Inset::AlignLeft:
+                       align = LYX_ALIGN_BLOCK;
+                       break;
+               case Inset::AlignCenter:
+                       align = LYX_ALIGN_CENTER;
+                       break;
+               case Inset::Inline:
+                       // unchanged (use align)
+                       break;
+               case Inset::AlignRight:
+                       align = LYX_ALIGN_RIGHT;
+                       break;
+               }
+       }
+
+       // Has the user requested we not justify stuff?
+       if (!bv_->buffer().params().justification
+           && align == LYX_ALIGN_BLOCK)
+               align = LYX_ALIGN_LEFT;
+
+       return align;
+}
+
+
 void TextMetrics::computeRowMetrics(pit_type const pit,
                Row & row, int width) const
 {
@@ -541,10 +583,11 @@ void TextMetrics::computeRowMetrics(pit_type const pit,
        // is there a manual margin with a manual label
        Layout const & layout = par.layout();
 
+       int nlh = 0;
        if (layout.margintype == MARGIN_MANUAL
            && layout.labeltype == LABEL_MANUAL) {
                /// We might have real hfills in the label part
-               int nlh = numberOfLabelHfills(par, row);
+               nlh = numberOfLabelHfills(par, row);
 
                // A manual label par (e.g. List) has an auto-hfill
                // between the label text and the body of the
@@ -560,7 +603,7 @@ void TextMetrics::computeRowMetrics(pit_type const pit,
 
        double hfill = 0;
        // are there any hfills in the row?
-       if (int const nh = numberOfHfills(par, row)) {
+       if (int const nh = numberOfHfills(row, par.beginOfBody())) {
                if (w > 0)
                        hfill = w / double(nh);
        // we don't have to look at the alignment if it is ALIGN_LEFT and
@@ -569,53 +612,11 @@ void TextMetrics::computeRowMetrics(pit_type const pit,
        } else if (int(row.width()) < max_width_) {
                // is it block, flushleft or flushright?
                // set x how you need it
-               int align;
-               if (par.params().align() == LYX_ALIGN_LAYOUT)
-                       align = layout.align;
-               else
-                       align = par.params().align();
-
-               // handle alignment inside tabular cells
-               Inset const & owner = text_->inset();
-               switch (owner.contentAlignment()) {
-                       case LYX_ALIGN_CENTER:
-                       case LYX_ALIGN_LEFT:
-                       case LYX_ALIGN_RIGHT:
-                               if (align == LYX_ALIGN_NONE 
-                                   || align == LYX_ALIGN_BLOCK)
-                                       align = owner.contentAlignment();
-                               break;
-                       default:
-                               // unchanged (use align)
-                               break;
-               }
-
-               // Display-style insets should always be on a centered row
-               if (Inset const * inset = par.getInset(row.pos())) {
-                       switch (inset->display()) {
-                               case Inset::AlignLeft:
-                                       align = LYX_ALIGN_BLOCK;
-                                       break;
-                               case Inset::AlignCenter:
-                                       align = LYX_ALIGN_CENTER;
-                                       break;
-                               case Inset::Inline:
-                                       // unchanged (use align)
-                                       break;
-                               case Inset::AlignRight:
-                                       align = LYX_ALIGN_RIGHT;
-                                       break;
-                       }
-               }
-
-               // Has the user requested we not justify stuff?
-               if (!bv_->buffer().params().justification
-                   && align == LYX_ALIGN_BLOCK)
-                       align = LYX_ALIGN_LEFT;
+               int const align = getAlign(par, row.pos());
 
                switch (align) {
                case LYX_ALIGN_BLOCK: {
-                       int const ns = numberOfSeparators(par, row);
+                       int const ns = numberOfSeparators(row);
                        bool disp_inset = false;
                        if (row.endpos() < par.size()) {
                                Inset const * in = par.getInset(row.endpos());
@@ -625,12 +626,10 @@ void TextMetrics::computeRowMetrics(pit_type const pit,
                        // If we have separators, this is not the last row of a
                        // par, does not end in newline, and is not row above a
                        // display inset... then stretch it
-                       if (ns
-                           && row.endpos() < par.size()
+                       if (ns && row.endpos() < par.size()
                            && !par.isNewline(row.endpos() - 1)
-                           && !disp_inset
-                               ) {
-                               row.separator = w / ns;
+                           && !disp_inset) {
+                               setSeparatorWidth(row, w / ns);
                                //lyxerr << "row.separator " << row.separator 
<< endl;
                                //lyxerr << "ns " << ns << endl;
                        } else if (is_rtl) {
@@ -647,42 +646,44 @@ void TextMetrics::computeRowMetrics(pit_type const pit,
                }
        }
 
+#if 0
        if (is_rtl) {
                pos_type body_pos = par.beginOfBody();
                pos_type end = row.endpos();
 
                if (body_pos > 0
-                   && (body_pos > end || !par.isLineSeparator(body_pos - 1)))
-               {
+                   && (body_pos > end || !par.isLineSeparator(body_pos - 1))) {
                        row.x += theFontMetrics(text_->labelFont(par)).
                                width(layout.labelsep);
                        if (body_pos <= end)
                                row.x += row.label_hfill;
                }
        }
+#endif
 
        pos_type const endpos = row.endpos();
        pos_type body_pos = par.beginOfBody();
        if (body_pos > 0
-               && (body_pos > endpos || !par.isLineSeparator(body_pos - 1)))
+           && (body_pos > endpos || !par.isLineSeparator(body_pos - 1)))
                body_pos = 0;
 
        ParagraphMetrics & pm = par_metrics_[pit];
-       InsetList::const_iterator ii = par.insetList().begin();
-       InsetList::const_iterator iend = par.insetList().end();
-       for ( ; ii != iend; ++ii) {
-               if (ii->pos >= endpos || ii->pos < row.pos()
-                   || !ii->inset->isHfill())
+       Row::iterator cit = row.begin();
+       Row::iterator const cend = row.end();
+       for ( ; cit != cend; ++cit) {
+               if (row.label_hfill && cit->endpos == body_pos
+                   && cit->type == Row::Element::SPACE)
+                       cit->dim.wid -= row.label_hfill * (nlh - 1);
+               if (!cit->inset || !cit->inset->isHfill())
                        continue;
-               Dimension dim = row.dimension();
-               if (pm.hfillExpansion(row, ii->pos))
-                       dim.wid = int(ii->pos >= body_pos ?
-                               max(hfill, 5.0) : row.label_hfill);
+               if (pm.hfillExpansion(row, cit->pos))
+                       cit->dim.wid = int(cit->pos >= body_pos ?
+                                          max(hfill, 5.0) : row.label_hfill);
                else
-                       dim.wid = 5;
+                       cit->dim.wid = 5;
                // Cache the inset dimension.
-               bv_->coordCache().insets().add(ii->inset, dim);
-               pm.setInsetDimension(ii->inset, dim);
+               bv_->coordCache().insets().add(cit->inset, cit->dim);
+               pm.setInsetDimension(cit->inset, cit->dim);
        }
 }
 
@@ -690,20 +691,17 @@ void TextMetrics::computeRowMetrics(pit_type const pit,
 int TextMetrics::labelFill(pit_type const pit, Row const & row) const
 {
        Paragraph const & par = text_->getPar(pit);
-
-       pos_type last = par.beginOfBody();
-       LBUFERR(last > 0);
-
-       // -1 because a label ends with a space that is in the label
-       --last;
-
-       // a separator at this end does not count
-       if (par.isLineSeparator(last))
-               --last;
+       LBUFERR(par.beginOfBody() > 0);
 
        int w = 0;
-       for (pos_type i = row.pos(); i <= last; ++i)
-               w += singleWidth(pit, i);
+       Row::const_iterator cit = row.begin();
+       Row::const_iterator const end = row.end();
+       // iterate over elements before main body (except the last one,
+       // which is extra space).
+       while (cit!= end && cit->endpos < par.beginOfBody()) {
+               w += cit->width();
+               ++cit;
+       }
 
        docstring const & label = par.params().labelWidthString();
        if (label.empty())
@@ -855,7 +853,8 @@ void TextMetrics::breakRow(Row & row, int const 
right_margin, pit_type const pit
                        LATTEST(!par.isInset(i));
                        row.addSeparator(i, c, *fi, par.lookupChange(i));
                } else if (c == '\t')
-                       row.addSpace(i, theFontMetrics(*fi).width(from_ascii("  
  ")), *fi, par.lookupChange(i));
+                       row.addSpace(i, theFontMetrics(*fi).width(from_ascii("  
  ")),
+                                    *fi, par.lookupChange(i));
                else
                        row.add(i, c, *fi, par.lookupChange(i));
 
@@ -869,10 +868,12 @@ void TextMetrics::breakRow(Row & row, int const 
right_margin, pit_type const pit
                }
 
                // add inline completion width
-               if (inlineCompletionLPos == i) {
+               if (inlineCompletionLPos == i &&
+                   !bv_->inlineCompletion().empty()) {
                        Font f = *fi;
                        f.fontInfo().setColor(Color_inlinecompletion);
-                       row.add(i, bv_->inlineCompletion(), f, Change());
+                       row.addCompletion(i, bv_->inlineCompletion(),
+                                         f, Change());
                }
 
                // Handle some situations that abruptly terminate the row
@@ -894,13 +895,15 @@ void TextMetrics::breakRow(Row & row, int const 
right_margin, pit_type const pit
 
                // add the auto-hfill from label end to the body
                if (body_pos && i == body_pos) {
-                       FontMetrics const & fm = theFontMetrics(
-                               text_->labelFont(par));
-                       if (!row.empty() && row.back().isLineSeparator())
+                       FontMetrics const & fm = 
theFontMetrics(text_->labelFont(par));
+                       pos_type j = i;
+                       if (!row.empty() && row.back().isSeparator()) {
                                row.pop_back();
+                               --j;
+                       }
                        int const add = max(fm.width(par.layout().labelsep),
                                            labelEnd(pit) - row.width());
-                       row.addSpace(i, add, *fi, par.lookupChange(i));
+                       row.addSpace(j, add, *fi, par.lookupChange(i));
                }
 
        }
@@ -913,100 +916,18 @@ void TextMetrics::breakRow(Row & row, int const 
right_margin, pit_type const pit
 
        // if the row ends with a separator that is not at end of
        // paragraph, remove it
-       if (!row.empty() && row.back().isLineSeparator()
+       if (!row.empty() && row.back().isSeparator()
            && row.endpos() < par.size())
                row.pop_back();
 
        // make sure that the RtL elements are in reverse ordering
-       lyxerr << ">>>>>>>>>>BEFORE REVERSE" << row;
        row.reverseRtL();
-       lyxerr << "<<<<<<<<<<AFTER REVERSE" << row;
 
        row.dimension().wid += right_margin;
-
-       // manual labels cannot be broken in LaTeX. But we
-       // want to make our on-screen rendering of footnotes
-       // etc. still break
-       // if (body_pos && point < body_pos)
-       //      point = body_pos;
-}
-
-
-int TextMetrics::rowWidth(int right_margin, pit_type const pit,
-               pos_type const first, pos_type const end) const
-{
-       // get the pure distance
-       ParagraphMetrics const & pm = par_metrics_[pit];
-       Paragraph const & par = text_->getPar(pit);
-       int w = leftMargin(max_width_, pit, first);
-       int label_end = labelEnd(pit);
-
-       // check for possible inline completion
-       DocIterator const & inlineCompletionPos = bv_->inlineCompletionPos();
-       pos_type inlineCompletionLPos = -1;
-       if (inlineCompletionPos.inTexted()
-           && inlineCompletionPos.text() == text_
-           && inlineCompletionPos.pit() == pit) {
-               // draw logically behind the previous character
-               inlineCompletionLPos = inlineCompletionPos.pos() - 1;
-       }
-
-       pos_type const body_pos = par.beginOfBody();
-       pos_type i = first;
-
-       if (i < end) {
-               FontIterator fi = FontIterator(*this, par, pit, i);
-               for ( ; i < end; ++i, ++fi) {
-                       if (body_pos > 0 && i == body_pos) {
-                               FontMetrics const & fm = theFontMetrics(
-                                       text_->labelFont(par));
-                               w += fm.width(par.layout().labelsep);
-                               if (par.isLineSeparator(i - 1))
-                                       w -= singleWidth(pit, i - 1);
-                               w = max(w, label_end);
-                       }
-                       
-                       // a line separator at the end of a line (but not at 
the end of a 
-                       // paragraph) will not be drawn and should therefore 
not count for
-                       // the row width.
-                       if (!par.isLineSeparator(i) || i != end - 1 || end == 
par.size())
-                               w += pm.singleWidth(i, *fi);
-
-                       // add inline completion width
-                       if (inlineCompletionLPos == i) {
-                               docstring const & completion = 
bv_->inlineCompletion();
-                               if (completion.length() > 0)
-                                       w += 
theFontMetrics(*fi).width(completion);
-                       }
-               }
-       }
-
-       // 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));
-               w += fm.width(par.layout().labelsep);
-               if (end > 0 && par.isLineSeparator(end - 1))
-                       w -= singleWidth(pit, end - 1);
-               w = max(w, label_end);
-       }
-
-       return w + right_margin;
 }
 
 
-void TextMetrics::setRowHeight(Row & row, pit_type const pit, 
+void TextMetrics::setRowHeight(Row & row, pit_type const pit,
                                    bool topBottomSpace) const
 {
        Paragraph const & par = text_->getPar(pit);
@@ -1098,8 +1019,7 @@ void TextMetrics::setRowHeight(Row & row, pit_type const 
pit,
                // special code for the top label
                if (layout.labelIsAbove()
                    && (!layout.isParagraphGroup() || 
text_->isFirstInSequence(pit))
-                   && !par.labelString().empty())
-               {
+                   && !par.labelString().empty()) {
                        labeladdon = int(
                                  labelfont_metrics.maxHeight()
                                        * layout.spacing.getValue()
@@ -1151,10 +1071,8 @@ void TextMetrics::setRowHeight(Row & row, pit_type const 
pit,
                                usual = pars[cpit].layout().bottomsep * dh;
                                cpit = text_->depthHook(cpit, 
pars[nextpit].getDepth());
                                if (pars[cpit].layout() != 
pars[nextpit].layout()
-                                       || pars[nextpit].getLabelWidthString() 
!= pars[cpit].getLabelWidthString())
-                               {
+                                   || pars[nextpit].getLabelWidthString() != 
pars[cpit].getLabelWidthString())
                                        unusual = pars[cpit].layout().bottomsep 
* dh;
-                               }
                                layoutdesc = max(unusual, usual);
                        } else if (pars[cpit].getDepth() == 
pars[nextpit].getDepth()) {
                                if (pars[cpit].layout() != 
pars[nextpit].layout()
@@ -1488,7 +1406,7 @@ 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);
 
@@ -1518,7 +1436,7 @@ Inset * TextMetrics::editXY(Cursor & cur, int x, int y,
 
        // This should be just before or just behind the
        // cursor position set above.
-       LASSERT(inset == inset_before 
+       LASSERT(inset == inset_before
                || inset == pars[pit].getInset(pos), return 0);
 
        // Make sure the cursor points to the position before
@@ -1603,10 +1521,8 @@ Inset * TextMetrics::checkInsetHit(int x, int y)
                LYXERR(Debug::DEBUG, "xo: " << p.x_ << "..." << p.x_ + dim.wid
                        << " yo: " << p.y_ - dim.asc << "..." << p.y_ + 
dim.des);
 
-               if (x >= p.x_
-                       && x <= p.x_ + dim.wid
-                       && y >= p.y_ - dim.asc
-                       && y <= p.y_ + dim.des) {
+               if (x >= p.x_ && x <= p.x_ + dim.wid
+                   && y >= p.y_ - dim.asc && y <= p.y_ + dim.des) {
                        LYXERR(Debug::DEBUG, "Hit inset: " << inset);
                        return inset;
                }
@@ -1622,23 +1538,60 @@ int TextMetrics::cursorX(CursorSlice const & sl,
 {
        LASSERT(sl.text() == text_, return 0);
        pit_type const pit = sl.pit();
+       pos_type ppos = sl.pos();
+
        Paragraph const & par = text_->paragraphs()[pit];
        ParagraphMetrics const & pm = par_metrics_[pit];
        if (pm.rows().empty())
                return 0;
+       Row const & row = pm.getRow(sl.pos(), boundary);
+
+       double x = row.x;
+
+       /**
+        * When boundary is true, position is on the row element (pos, endpos)
+        * if
+        *    pos < ppos <= endpos
+        * whereas, when boundary is false, the test is
+        *    pos <= ppos < endpos
+        * The correction below allows to handle both cases.
+       */
+       int const boundary_corr = (boundary && ppos) ? -1 : 0;
+
+       if (row.empty() 
+           || (row.begin()->font.isRightToLeft()
+               && ppos == row.begin()->endpos))
+               return int(x);
+
+       Row::const_iterator cit = row.begin();
+       for ( ; cit != row.end() ; ++cit) {
+               // lyxerr << "ppos=" << ppos << "(" << boundary_corr << ")"
+               //        << ", x=" << x << " " << *cit << endl;
+               // lyxerr << "test1=" << (ppos + boundary_corr >= cit->pos)
+               //        << " test2=" << ( ppos + boundary_corr < 
best->endpos) <<endl;
+               if (ppos + boundary_corr >= cit->pos
+                   && ppos + boundary_corr < cit->endpos) {
+                               x += cit->pos2x(ppos);
+                               break;
+               }
+               x += cit->width();
+       }
+
+       if (cit == row.end()
+           && (row.back().font.isRightToLeft() || ppos != row.back().endpos))
+               lyxerr << "NOT FOUND!"
+                      << "ppos=" << ppos << "(" << boundary_corr << ")" << "\n"
+                      << row;
 
-       pos_type ppos = sl.pos();
        // Correct position in front of big insets
        bool const boundary_correction = ppos != 0 && boundary;
        if (boundary_correction)
                --ppos;
 
-       Row const & row = pm.getRow(sl.pos(), boundary);
-
        pos_type cursor_vpos = 0;
 
        Buffer const & buffer = bv_->buffer();
-       double x = row.x;
+       double x2 = row.x;
        Bidi bidi;
        bidi.computeTables(par, buffer, row);
 
@@ -1697,7 +1650,7 @@ int TextMetrics::cursorX(CursorSlice const & sl,
                if (size_type(pit + 1) < pars_.size()) {
                        FontInfo f;
                        docstring const s = docstring(1, char_type(0x00B6));
-                       x += theFontMetrics(f).width(s);
+                       x2 += theFontMetrics(f).width(s);
                }
        }
 
@@ -1707,7 +1660,7 @@ int TextMetrics::cursorX(CursorSlice const & sl,
                font = displayFont(pit, row_pos + 1);
                docstring const & completion = bv_->inlineCompletion();
                if (font.isRightToLeft() && completion.length() > 0)
-                       x += theFontMetrics(font.fontInfo()).width(completion);
+                       x2 += theFontMetrics(font.fontInfo()).width(completion);
        }
 
        for (pos_type vpos = row_pos; vpos < cursor_vpos; ++vpos) {
@@ -1718,9 +1671,9 @@ int TextMetrics::cursorX(CursorSlice const & sl,
                if (body_pos > 0 && pos == body_pos - 1) {
                        FontMetrics const & labelfm = theFontMetrics(
                                text_->labelFont(par));
-                       x += row.label_hfill + 
labelfm.width(par.layout().labelsep);
+                       x2 += row.label_hfill + 
labelfm.width(par.layout().labelsep);
                        if (par.isLineSeparator(body_pos - 1))
-                               x -= singleWidth(pit, body_pos - 1);
+                               x2 -= singleWidth(pit, body_pos - 1);
                }
 
                // Use font span to speed things up, see above
@@ -1729,7 +1682,7 @@ int TextMetrics::cursorX(CursorSlice const & sl,
                        font = displayFont(pit, pos);
                }
 
-               x += pm.singleWidth(pos, font);
+               x2 += pm.singleWidth(pos, font);
 
                // Inline completion RTL case:
                // "a__|b", __ of b => non-boundary a-pos is right of __
@@ -1738,7 +1691,7 @@ int TextMetrics::cursorX(CursorSlice const & sl,
                        font = displayFont(pit, vpos + 1);
                        docstring const & completion = bv_->inlineCompletion();
                        if (font.isRightToLeft() && completion.length() > 0)
-                               x += 
theFontMetrics(font.fontInfo()).width(completion);
+                               x2 += 
theFontMetrics(font.fontInfo()).width(completion);
                }
 
                //  Inline completion LTR case:
@@ -1748,21 +1701,34 @@ int TextMetrics::cursorX(CursorSlice const & sl,
                        font = displayFont(pit, vpos);
                        docstring const & completion = bv_->inlineCompletion();
                        if (!font.isRightToLeft() && completion.length() > 0)
-                               x += 
theFontMetrics(font.fontInfo()).width(completion);
+                               x2 += 
theFontMetrics(font.fontInfo()).width(completion);
                }
 
                if (par.isSeparator(pos) && pos >= body_pos)
-                       x += row.separator;
+                       x2 += row.separator;
        }
 
        // see correction above
        if (boundary_correction) {
                if (isRTL(sl, boundary))
-                       x -= singleWidth(pit, ppos);
+                       x2 -= singleWidth(pit, ppos);
                else
-                       x += singleWidth(pit, ppos);
+                       x2 += singleWidth(pit, ppos);
        }
 
+       if (x2 != x) {
+               lyxerr << "cursorX: x2=" << x2 << ", x=" << x;
+               if (cit == row.end())
+                       lyxerr << "Element not found for "
+                              << ppos - boundary_corr << "(" << boundary_corr 
<< ")";
+               else
+                       lyxerr << " in [" << cit->pos << "/"
+                              << ppos - boundary_corr << "(" << boundary_corr 
<< ")"
+                              << "/" << cit->endpos << "] of " << *cit << "\n";
+               lyxerr << row <<endl;
+       }
+
+
        return int(x);
 }
 
@@ -1975,7 +1941,7 @@ int TextMetrics::leftMargin(int max_width,
                // The left margin depends on the widest row in this paragraph.
                // This code is wrong because it depends on the rows, but at the
                // same time this function is used in redoParagraph to construct
-               // the rows. 
+               // the rows.
                ParagraphMetrics const & pm = par_metrics_[pit];
                RowList::const_iterator rit = pm.rows().begin();
                RowList::const_iterator end = pm.rows().end();
@@ -2005,34 +1971,34 @@ int TextMetrics::leftMargin(int max_width,
        // set the correct parindent
        if (pos == 0
            && (layout.labeltype == LABEL_NO_LABEL
-              || layout.labeltype == LABEL_ABOVE
-              || layout.labeltype == LABEL_CENTERED
-              || (layout.labeltype == LABEL_STATIC
-                  && layout.latextype == LATEX_ENVIRONMENT
-                  && !text_->isFirstInSequence(pit)))
+               || layout.labeltype == LABEL_ABOVE
+               || layout.labeltype == LABEL_CENTERED
+               || (layout.labeltype == LABEL_STATIC
+                   && layout.latextype == LATEX_ENVIRONMENT
+                   && !text_->isFirstInSequence(pit)))
            && (align == LYX_ALIGN_BLOCK || align == LYX_ALIGN_LEFT)
            && !par.params().noindent()
            // in some insets, paragraphs are never indented
            && !text_->inset().neverIndent()
            // display style insets are always centered, omit indentation
            && !(!par.empty()
-                   && par.isInset(pos)
-                   && par.getInset(pos)->display())
-                       && (!(tclass.isDefaultLayout(par.layout())
-                || tclass.isPlainLayout(par.layout()))
-               || buffer.params().paragraph_separation 
-                               == BufferParams::ParagraphIndentSeparation)
-           )
-               {
-                       // use the parindent of the layout when the default 
indentation is
-                       // used otherwise use the indentation set in the 
document settings
+                && par.isInset(pos)
+                && par.getInset(pos)->display())
+           && (!(tclass.isDefaultLayout(par.layout())
+                 || tclass.isPlainLayout(par.layout()))
+               || buffer.params().paragraph_separation
+                               == BufferParams::ParagraphIndentSeparation)) {
+                       // use the parindent of the layout when the
+                       // default indentation is used otherwise use
+                       // the indentation set in the document
+                       // settings
                        if (buffer.params().getIndentation().asLyXCommand() == 
"default")
                                l_margin += theFontMetrics(
                                        
buffer.params().getFont()).signedWidth(parindent);
                        else
                                l_margin += 
buffer.params().getIndentation().inPixels(*bv_);
                }
-       
+
        return l_margin;
 }
 
@@ -2093,7 +2059,7 @@ void TextMetrics::drawParagraph(PainterInfo & pi, 
pit_type pit, int x, int y) co
        // We store the begin and end pos of the selection relative to this par
        DocIterator sel_beg_par = cur.selectionBegin();
        DocIterator sel_end_par = cur.selectionEnd();
-       
+
        // We care only about visible selection.
        if (selection) {
                if (pit != sel_beg.pit()) {
@@ -2122,7 +2088,7 @@ void TextMetrics::drawParagraph(PainterInfo & pi, 
pit_type pit, int x, int y) co
                        row.setSelectionAndMargins(sel_beg_par, sel_end_par);
                else
                        row.setSelection(-1, -1);
-               
+
                // The row knows nothing about the paragraph, so we have to 
check
                // whether this row is the first or last and update the margins.
                if (row.selection()) {
@@ -2157,7 +2123,7 @@ void TextMetrics::drawParagraph(PainterInfo & pi, 
pit_type pit, int x, int y) co
                        pi.pain.fillRectangle(x, y - row.ascent(),
                                width(), row.height(), pi.background_color);
                }
-               
+
                // Instrumentation for testing row cache (see also
                // 12 lines lower):
                if (lyxerr.debugging(Debug::PAINTING) && inside
diff --git a/src/TextMetrics.h b/src/TextMetrics.h
index 75d9c6e..e8cef83 100644
--- a/src/TextMetrics.h
+++ b/src/TextMetrics.h
@@ -111,19 +111,15 @@ public:
        int rightMargin(ParagraphMetrics const & pm) const;
        int rightMargin(pit_type const pit) const;
 
-       /** this calculates the specified parameters. needed when setting
-        * the cursor and when creating a visible row */
-       void computeRowMetrics(pit_type pit, Row & row, int width) const;
-
        ///
        void draw(PainterInfo & pi, int x, int y) const;
-       
+
        void drawParagraph(PainterInfo & pi, pit_type pit, int x, int y) const;
 
        /// Returns the height of the row (width member is set to 0).
        /// If \c topBottomSpace is true, extra space is added for the
        /// top and bottom row.
-       void setRowHeight(Row & row, pit_type const pit, 
+       void setRowHeight(Row & row, pit_type const pit,
                          bool topBottomSpace = true) const;
 
 private:
@@ -140,13 +136,11 @@ private:
        /// for example, the pos after which isNewLine(pos) == true
        void breakRow(Row & row, int right_margin, pit_type const pit) const;
 
-       /// returns the minimum space a row needs on the screen in pixel
-       int rowWidth(
-               int right_margin,
-               pit_type const pit,
-               pos_type const first,
-               pos_type const end
-               ) const;
+       // Expand the alignment of paragraph \param par at position \param pos
+       int getAlign(Paragraph const & par, pos_type pos) const;
+       /** this calculates the specified parameters. needed when setting
+        * the cursor and when creating a visible row */
+       void computeRowMetrics(pit_type pit, Row & row, int width) const;
 
 // Temporary public:
 public:

-----------------------------------------------------------------------

Summary of changes:
 00README_STR_METRICS_BRANCH |   18 +-
 src/Row.cpp                 |  101 +++++++---
 src/Row.h                   |   50 ++++-
 src/TextMetrics.cpp         |  478 ++++++++++++++++++++-----------------------
 src/TextMetrics.h           |   20 +--
 5 files changed, 347 insertions(+), 320 deletions(-)


hooks/post-receive
-- 
Repository for new features

Reply via email to