Abdelrazak Younes wrote:
Hello,

Here is the first draft of the model/view separation of the Paragraph class. It is mostly working already execept for Copy&Paste and MultipleView.

I've solve the C&P issue and many others. In single window mode, the attached patch is very stable, I did extensive testing but could not make it crash (including with DEPM action triggered). There's no painting problem either. From this point I'd welcome any additional testing. But I really think it is ready for inclusion.

So, objections?

In multiple view mode, there's still one problem related to LyXText::max_width_ which is still BufferView dependant. I can solve it after committing this patch or before, as you wish.

Abdel.

Index: BufferView.C
===================================================================
--- BufferView.C        (revision 16395)
+++ BufferView.C        (working copy)
@@ -420,13 +420,14 @@
        // estimated average paragraph height:
        if (wh_ == 0)
                wh_ = height_ / 4;
-       int h = t.getPar(anchor_ref_).height();
 
+       int h = text_metrics_[&t][anchor_ref_].height();
+
        // Normalize anchor/offset (MV):
        while (offset_ref_ > h && anchor_ref_ < parsize) {
                anchor_ref_++;
                offset_ref_ -= h;
-               h = t.getPar(anchor_ref_).height();
+               h = text_metrics_[&t][anchor_ref_].height();
        }
        // Look at paragraph heights on-screen
        int sumh = 0;
@@ -434,7 +435,7 @@
        for (pit_type pit = anchor_ref_; pit <= parsize; ++pit) {
                if (sumh > height_)
                        break;
-               int const h2 = t.getPar(pit).height();
+               int const h2 = text_metrics_[&t][pit].height();
                sumh += h2;
                nh++;
        }
@@ -473,8 +474,8 @@
        anchor_ref_ = int(bar * t.paragraphs().size());
        if (anchor_ref_ >  int(t.paragraphs().size()) - 1)
                anchor_ref_ = int(t.paragraphs().size()) - 1;
-       t.redoParagraph(*this, anchor_ref_);
-       int const h = t.getPar(anchor_ref_).height();
+       redoParagraph(&t, anchor_ref_);
+       int const h = text_metrics_[&t][anchor_ref_].height();
        offset_ref_ = int((bar * t.paragraphs().size() - anchor_ref_) * h);
        updateMetrics(false);
 }
@@ -572,11 +573,11 @@
 {
        CursorSlice & bot = cursor_.bottom();
        pit_type const pit = bot.pit();
-       bot.text()->redoParagraph(*this, pit);
-       Paragraph const & par = bot.text()->paragraphs()[pit];
+       redoParagraph(bot.text(), pit);
+       ParagraphMetrics const & pm = text_metrics_[bot.text()][pit];
        anchor_ref_ = pit;
        offset_ref_ = bv_funcs::coordOffset(*this, cursor_, 
cursor_.boundary()).y_
-               + par.ascent() - height_ / 2;
+               + pm.ascent() - height_ / 2;
 }
 
 
@@ -1190,6 +1191,31 @@
 }
 
 
+ParagraphMetrics const & BufferView::parMetrics(LyXText const * t,
+               pit_type pit) const
+{
+       return parMetrics(t, pit, true);
+}
+
+
+ParagraphMetrics & BufferView::parMetrics(LyXText const * t,
+               pit_type pit, bool redo) const
+{
+       TextMetricsCache::iterator tmc_it  = text_metrics_.find(t);
+       if (tmc_it == text_metrics_.end()) {
+               tmc_it = text_metrics_.insert(make_pair(t, 
ParMetricsCache())).first; 
+       }
+       ParMetricsCache::iterator pmc_it = tmc_it->second.find(pit);
+       if (pmc_it == tmc_it->second.end()) {
+               pmc_it = tmc_it->second.insert(make_pair(pit, 
ParagraphMetrics(t->getPar(pit)))).first; 
+       }
+       if (pmc_it->second.rows().empty() && redo) {
+               redoParagraph(const_cast<LyXText *>(t), pit);
+       }
+       return pmc_it->second;
+}
+
+
 int BufferView::workHeight() const
 {
        return height_;
@@ -1273,10 +1299,6 @@
 
 void BufferView::updateMetrics(bool singlepar)
 {
-       // Clear out the position cache in case of full screen redraw.
-       if (!singlepar)
-               coord_cache_.clear();
-
        LyXText & buftext = buffer_->text();
        pit_type size = int(buftext.paragraphs().size());
 
@@ -1295,23 +1317,29 @@
        // (if this paragraph contains insets etc., rebreaking will
        // recursively descend)
        if (!singlepar || pit == cursor_.bottom().pit())
-               buftext.redoParagraph(*this, pit);
-       int y0 = buftext.getPar(pit).ascent() - offset_ref_;
+               if (redoParagraph(&buftext, pit))
+                       singlepar = false;
+       
+       // Clear out the position cache in case of full screen redraw.
+       if (!singlepar)
+               coord_cache_.clear();
 
+       int y0 = text_metrics_[&buftext][pit].ascent() - offset_ref_;
+
        // Redo paragraphs above anchor if necessary; again, in Single Par
        // mode, only if we encounter the (main text) one having the cursor.
        int y1 = y0;
        while (y1 > 0 && pit1 > 0) {
-               y1 -= buftext.getPar(pit1).ascent();
+               y1 -= text_metrics_[&buftext][pit1].ascent();
                --pit1;
                if (!singlepar || pit1 == cursor_.bottom().pit())
-                       buftext.redoParagraph(*this, pit1);
-               y1 -= buftext.getPar(pit1).descent();
+                       redoParagraph(&buftext, pit1);
+               y1 -= text_metrics_[&buftext][pit1].descent();
        }
 
 
        // Take care of ascent of first line
-       y1 -= buftext.getPar(pit1).ascent();
+       y1 -= text_metrics_[&buftext][pit1].ascent();
 
        // Normalize anchor for next time
        anchor_ref_ = pit1;
@@ -1328,30 +1356,30 @@
        // only the one containing the cursor if encountered.
        int y2 = y0;
        while (y2 < height_ && pit2 < int(npit) - 1) {
-               y2 += buftext.getPar(pit2).descent();
+               y2 += text_metrics_[&buftext][pit2].descent();
                ++pit2;
                if (!singlepar || pit2 == cursor_.bottom().pit())
-                       buftext.redoParagraph(*this, pit2);
-               y2 += buftext.getPar(pit2).ascent();
+                       redoParagraph(&buftext, pit2);
+               y2 += text_metrics_[&buftext][pit2].ascent();
        }
 
        // Take care of descent of last line
-       y2 += buftext.getPar(pit2).descent();
+       y2 += text_metrics_[&buftext][pit2].descent();
 
        // The coordinates of all these paragraphs are correct, cache them
        int y = y1;
        CoordCache::InnerParPosCache & parPos = coord_cache_.parPos()[&buftext];
        for (pit_type pit = pit1; pit <= pit2; ++pit) {
-               Paragraph const & par = buftext.getPar(pit);
-               y += par.ascent();
+               ParagraphMetrics const & pm = text_metrics_[&buftext][pit];
+               y += pm.ascent();
                parPos[pit] = Point(0, y);
                if (singlepar && pit == cursor_.bottom().pit()) {
                        // In Single Paragraph mode, collect here the
                        // y1 and y2 of the (one) paragraph the cursor is in
-                       y1 = y - par.ascent();
-                       y2 = y + par.descent();
+                       y1 = y - pm.ascent();
+                       y2 = y + pm.descent();
                }
-               y += par.descent();
+               y += pm.descent();
        }
 
        if (singlepar) {
@@ -1450,4 +1478,83 @@
 }
 
 
+bool BufferView::redoParagraph(LyXText * text, pit_type const pit) const
+{
+       // remove rows of paragraph, keep track of height changes
+       Paragraph & par = text->getPar(pit);
+       ParagraphMetrics pm(par);
+
+       bool changed = false;
+
+       // FIXME: this has nothing to do here and is the reason why text is not
+       // const.
+       if (par.checkBiblio(buffer_->params().trackChanges))
+               const_cast<LCursor &>(cursor()).posRight();
+
+       // Optimisation: this is used in the next two loops
+       // so better to calculate that once here.
+       int const right_margin = (text == &buffer_->text())?
+               pm.rightMargin(*buffer_) : 0;
+
+       // redo insets
+       // FIXME: We should always use getFont(), see documentation of
+       // noFontChange() in insetbase.h.
+       LyXFont const bufferfont = buffer_->params().getFont();
+       InsetList::const_iterator ii = par.insetlist.begin();
+       InsetList::const_iterator iend = par.insetlist.end();
+       for (; ii != iend; ++ii) {
+               Dimension dim;
+               int const w = text->maxwidth_ - text->leftMargin(*buffer_, pit, 
ii->pos)
+                       - right_margin;
+               LyXFont const & font = ii->inset->noFontChange() ?
+                       bufferfont : text->getFont(*buffer_, par, ii->pos);
+               // FIXME: MetricsInfo should only need a const BufferView.
+               MetricsInfo mi(const_cast<BufferView *>(this), font, w);
+               changed |= ii->inset->metrics(mi, dim);
+       }
+
+       // rebreak the paragraph
+       pm.rows().clear();
+
+       par.setBeginOfBody();
+       pos_type z = 0;
+       do {
+               Row row(z);
+               text->rowBreakPoint(*buffer_, right_margin, pit, row);
+               text->setRowWidth(*buffer_, right_margin, pit, row);
+               text->setHeightOfRow(*this, pit, row);
+               pm.rows().push_back(row);
+               pm.dim().wid = std::max(pm.dim().wid, row.width());
+               pm.dim().des += row.height();
+               z = row.endpos();
+       } while (z < par.size());
+
+       // Make sure that if a par ends in newline, there is one more row
+       // under it
+       // FIXME this is a dirty trick. Now the _same_ position in the
+       // paragraph occurs in _two_ different rows, and has two different
+       // display positions, leading to weird behaviour when moving up/down.
+       if (z > 0 && par.isNewline(z - 1)) {
+               Row row(z - 1);
+               row.endpos(z - 1);
+               text->setRowWidth(*buffer_, right_margin, pit, row);
+               text->setHeightOfRow(*this, pit, row);
+               pm.rows().push_back(row);
+               pm.dim().des += row.height();
+       }
+
+       pm.dim().asc += pm.rows()[0].ascent();
+       pm.dim().des -= pm.rows()[0].ascent();
+
+       // IMPORTANT NOTE: We pass 'false' explicitely in order to not call
+       // redoParagraph() recursively inside parMetrics.
+       Dimension old_dim = parMetrics(text, pit, false).dim();
+
+       changed |= old_dim.height() != pm.dim().height();
+
+       text_metrics_[text][pit] = pm;
+
+       return changed;
+}
+
 } // namespace lyx
Index: BufferView.h
===================================================================
--- BufferView.h        (revision 16395)
+++ BufferView.h        (working copy)
@@ -42,6 +42,7 @@
 class LCursor;
 class LyXText;
 class ParIterator;
+class ParagraphMetrics;
 class ViewMetricsInfo;
 
 /// Scrollbar Parameters.
@@ -205,6 +206,15 @@
        void updateMetrics(bool singlepar = false);
 
        ///
+       ParagraphMetrics const & parMetrics(LyXText const *, pit_type) const;
+       ParagraphMetrics & parMetrics(LyXText const *, pit_type, bool redo) 
const;
+
+       /// Rebreaks the given paragraph.
+       /// \retval true if a full screen redraw is needed.
+       /// \retval false if a single paragraph redraw is enough.
+       bool redoParagraph(LyXText * text, pit_type const pit) const;
+
+       ///
        CoordCache & coordCache() {
                return coord_cache_;
        }
@@ -281,6 +291,12 @@
 
        /// last visited inset (kept to send setMouseHover(false) )
        InsetBase * last_inset_;
+
+       /// A map from paragraph index number to paragraph metrics
+       typedef std::map<pit_type, ParagraphMetrics> ParMetricsCache;
+       /// A map from a LyXText to the map of paragraphs metrics
+       typedef std::map<LyXText const *, ParMetricsCache> TextMetricsCache;
+       mutable TextMetricsCache text_metrics_;
 };
 
 
Index: bufferview_funcs.C
===================================================================
--- bufferview_funcs.C  (revision 16395)
+++ bufferview_funcs.C  (working copy)
@@ -176,12 +176,9 @@
 
        // Add contribution of initial rows of outermost paragraph
        CursorSlice const & sl = dit[0];
-       Paragraph const & par = sl.text()->getPar(sl.pit());
-       if (par.rows().empty())
-               // FIXME: The special case below happens for empty paragraph 
creation
-               const_cast<LyXText 
*>(sl.text())->redoParagraph(const_cast<BufferView &>(bv), sl.pit());
-       BOOST_ASSERT(!par.rows().empty());
-       y -= par.rows()[0].ascent();
+       ParagraphMetrics const & pm = bv.parMetrics(sl.text(), sl.pit());
+       BOOST_ASSERT(!pm.rows().empty());
+       y -= pm.rows()[0].ascent();
 #if 1
        size_t rend;
        if (sl.pos() > 0 && dit.depth() == 1) {
@@ -189,16 +186,16 @@
                if (pos && boundary)
                        --pos;
 //             lyxerr << "coordOffset: boundary:" << boundary << " depth:" << 
dit.depth() << " pos:" << pos << " sl.pos:" << sl.pos() << std::endl;
-               rend = par.pos2row(pos);
+               rend = pm.pos2row(pos);
        } else
-               rend = par.pos2row(sl.pos());
+               rend = pm.pos2row(sl.pos());
 #else
-       size_t rend = par.pos2row(sl.pos());
+       size_t rend = pm.pos2row(sl.pos());
 #endif
        for (size_t rit = 0; rit != rend; ++rit)
-               y += par.rows()[rit].height();
-       y += par.rows()[rend].ascent();
-       x += dit.bottom().text()->cursorX(*bv.buffer(), dit.bottom(), boundary 
&& dit.depth() == 1);
+               y += pm.rows()[rit].height();
+       y += pm.rows()[rend].ascent();
+       x += dit.bottom().text()->cursorX(bv, dit.bottom(), boundary && 
dit.depth() == 1);
 
        return Point(x, y);
 }
Index: cursor.C
===================================================================
--- cursor.C    (revision 16395)
+++ cursor.C    (working copy)
@@ -403,17 +403,11 @@
 }
 
 
-Row & LCursor::textRow()
-{
-       BOOST_ASSERT(!paragraph().rows().empty());
-       return paragraph().getRow(pos(), boundary());
-}
-
-
 Row const & LCursor::textRow() const
 {
-       BOOST_ASSERT(!paragraph().rows().empty());
-       return paragraph().getRow(pos(), boundary());
+       ParagraphMetrics const & pm = bv().parMetrics(text(), pit());
+       BOOST_ASSERT(!pm.rows().empty());
+       return pm.getRow(pos(), boundary());
 }
 
 
Index: cursor.h
===================================================================
--- cursor.h    (revision 16395)
+++ cursor.h    (working copy)
@@ -104,7 +104,7 @@
        /// returns x,y position
        void getPos(int & x, int & y) const;
        /// the row in the paragraph we're in
-       Row & textRow();
+       //Row & textRow();
        /// the row in the paragraph we're in
        Row const & textRow() const;
 
Index: CutAndPaste.C
===================================================================
--- CutAndPaste.C       (revision 16395)
+++ CutAndPaste.C       (working copy)
@@ -627,7 +627,7 @@
                                             textclass, errorList);
                updateLabels(cur.buffer());
                cur.clearSelection();
-               text->setCursor(cur, ppp.first, ppp.second);
+               text->setCursor(cur.top(), ppp.first, ppp.second);
        }
 
        // mathed is handled in InsetMathNest/InsetMathGrid
Index: frontends/qt4/GuiWorkArea.C
===================================================================
--- frontends/qt4/GuiWorkArea.C (revision 16395)
+++ frontends/qt4/GuiWorkArea.C (working copy)
@@ -268,32 +268,12 @@
        // in BufferList that could be connected to the different tabbars.
        lyx_view_.updateTab();
 
-       //FIXME: Use case: Two windows share the same buffer.
-       // The first window is resize. This modify the inner Buffer
-       // structure because Paragraph has a notion of line break and
-       // thus line width (this is very bad!).
-       // When switching to the other window which does not have the
-       // same size, LyX crashes because the line break is not adapted
-       // the this BufferView width.
-       // The following line fix the crash by resizing the BufferView 
-       // on a focusInEvent(). That is not a good fix but it is a fix
-       // nevertheless. The bad side effect is that when the two
-       // BufferViews show the same portion of the Buffer, the second 
-       // BufferView will show the same line breaks as the first one;
-       // even though those line breaks are not adapted to the second
-       // BufferView width... such is life!
-       resizeBufferView();
-
        startBlinkingCursor();
 }
 
 
 void GuiWorkArea::focusOutEvent(QFocusEvent * /*event*/)
 {
-       // No need to do anything if we didn't change views...
-       if (&lyx_view_ == theApp()->currentView())
-               return;
-
        stopBlinkingCursor();
 }
 
Index: insets/insettabular.C
===================================================================
--- insets/insettabular.C       (revision 16395)
+++ insets/insettabular.C       (working copy)
@@ -603,10 +603,12 @@
                                cur.idx() = tabular.getCellAbove(cur.idx());
                                cur.pit() = cur.lastpit();
                                LyXText const * text = 
cell(cur.idx())->getText(0);
+                               ParagraphMetrics const & pm =
+                                       cur.bv().parMetrics(text, 
cur.lastpit());
                                cur.pos() = text->x2pos(
                                        cur.bv(),
                                        cur.pit(),
-                                       
text->paragraphs().back().rows().size()-1,
+                                       pm.rows().size()-1,
                                        cur.targetX());
                        }
                if (sl == cur.top()) {
Index: insets/insettext.C
===================================================================
--- insets/insettext.C  (revision 16395)
+++ insets/insettext.C  (working copy)
@@ -184,7 +184,6 @@
 
 void InsetText::draw(PainterInfo & pi, int x, int y) const
 {
-       BOOST_ASSERT(!text_.paragraphs().front().rows().empty());
        // update our idea of where we are
        setPosCache(pi, x, y);
 
@@ -340,8 +339,8 @@
 void InsetText::cursorPos(BufferView const & bv,
                CursorSlice const & sl, bool boundary, int & x, int & y) const
 {
-       x = text_.cursorX(*bv.buffer(), sl, boundary) + border_;
-       y = text_.cursorY(sl, boundary);
+       x = text_.cursorX(bv, sl, boundary) + border_;
+       y = text_.cursorY(bv, sl, boundary);
 }
 
 
Index: lyxtext.h
===================================================================
--- lyxtext.h   (revision 16395)
+++ lyxtext.h   (working copy)
@@ -96,11 +96,6 @@
        /// Set font over selection paragraphs and rebreak.
        void setFont(LCursor & cur, LyXFont const &, bool toggleall = false);
 
-       /// Rebreaks the given paragraph.
-       /// \retval true if a full screen redraw is needed.
-       /// \retval false if a single paragraph redraw is enough.
-       bool redoParagraph(BufferView &, pit_type pit);
-
        /// returns pos in given par at given x coord
        pos_type x2pos(BufferView const &, pit_type pit, int row, int x) const;
        int pos2x(pit_type pit, pos_type pos) const;
@@ -153,8 +148,8 @@
        /** returns the column near the specified x-coordinate of the row
         x is set to the real beginning of this column
         */
-       pos_type getColumnNearX(BufferView const & bv, pit_type pit,
-               Row const & row, int & x, bool & boundary) const;
+       pos_type getColumnNearX(BufferView const & bv, int right_margin,
+               pit_type pit, Row const & row, int & x, bool & boundary) const;
 
        /** Find the word under \c from in the relative location
         *  defined by \c word_location.
@@ -304,12 +299,12 @@
        int leftMargin(Buffer const &, pit_type pit, pos_type pos) const;
        int leftMargin(Buffer const &, pit_type pit) const;
        ///
-       int rightMargin(Buffer const &, Paragraph const & par) const;
+       int rightMargin(Buffer const &, ParagraphMetrics const & pm) const;
 
        /** this calculates the specified parameters. needed when setting
         * the cursor and when creating a visible row */
-       RowMetrics computeRowMetrics(Buffer const &, pit_type pit,
-               Row const & row) const;
+       RowMetrics computeRowMetrics(Buffer const &, int right_margin,
+               pit_type pit, Row const & row) const;
 
        /// access to our paragraphs
        ParagraphList const & paragraphs() const { return pars_; }
@@ -317,9 +312,6 @@
        /// return true if this is the main text
        bool isMainText(Buffer const &) const;
 
-       /// return first row of text
-       Row const & firstRow() const;
-
        /// is this row the last in the text?
        bool isLastRow(pit_type pit, Row const & row) const;
        /// is this row the first in the text?
@@ -344,14 +336,26 @@
        ///
        int descent() const;
        ///
-       int cursorX(Buffer const &, CursorSlice const & cursor,
+       int cursorX(BufferView const &, CursorSlice const & cursor,
                bool boundary) const;
        ///
-       int cursorY(CursorSlice const & cursor, bool boundary) const;
+       int cursorY(BufferView const & bv, CursorSlice const & cursor,
+               bool boundary) const;
 
        /// delete double space or empty paragraphs around old cursor
        bool deleteEmptyParagraphMechanism(LCursor & cur, LCursor & old);
 
+       /// sets row.end to the pos value *after* which a row should break.
+       /// for example, the pos after which isNewLine(pos) == true
+       void rowBreakPoint(Buffer const &, int right_margin, pit_type pit,
+               Row & row) const;
+       /// sets row.width to the minimum space a row needs on the screen in 
pixel
+       void setRowWidth(Buffer const &, int right_margin, pit_type pit,
+               Row & row) const;
+
+       /// Calculate and set the height of the row
+       void setHeightOfRow(BufferView const &, pit_type, Row & row);
+
 public:
        ///
        Dimension dim_;
@@ -380,9 +384,6 @@
        /// change on pit
        pit_type undoSpan(pit_type pit);
 
-       /// Calculate and set the height of the row
-       void setHeightOfRow(BufferView const &, pit_type, Row & row);
-
        // fix the cursor `cur' after a characters has been deleted at `where'
        // position. Called by deleteEmptyParagraphMechanism
        void fixCursorAfterDelete(CursorSlice & cur, CursorSlice const & where);
@@ -398,12 +399,6 @@
        ///
        void deleteLineForward(LCursor & cur);
 
-       /// sets row.end to the pos value *after* which a row should break.
-       /// for example, the pos after which isNewLine(pos) == true
-       void rowBreakPoint(Buffer const &, int right_margin, pit_type pit,
-               Row & row) const;
-       /// sets row.width to the minimum space a row needs on the screen in 
pixel
-       void setRowWidth(Buffer const &, pit_type pit, Row & row) const;
        /// the minimum space a manual label needs on the screen in pixels
        int labelFill(Buffer const &, Paragraph const & par, Row const & row) 
const;
        /// FIXME
Index: paragraph.C
===================================================================
--- paragraph.C (revision 16395)
+++ paragraph.C (working copy)
@@ -34,10 +34,15 @@
 #include "outputparams.h"
 #include "paragraph_funcs.h"
 #include "ParagraphList_fwd.h"
+
+#include "rowpainter.h"
+
 #include "sgml.h"
 #include "texrow.h"
 #include "vspace.h"
 
+#include "frontends/FontMetrics.h"
+
 #include "insets/insetbibitem.h"
 #include "insets/insetoptarg.h"
 
@@ -69,6 +74,11 @@
 using std::ostringstream;
 
 
+ParagraphMetrics::ParagraphMetrics(Paragraph const & par): par_(&par)
+{
+}
+
+
 Row & ParagraphMetrics::getRow(pos_type pos, bool boundary)
 {
        BOOST_ASSERT(!rows().empty());
@@ -130,7 +140,23 @@
        }
 }
 
+int ParagraphMetrics::rightMargin(Buffer const & buffer) const
+{
+       BufferParams const & params = buffer.params();
+       LyXTextClass const & tclass = params.getLyXTextClass();
+       docstring trmarg = from_utf8(tclass.rightmargin());
+       docstring lrmarg = from_utf8(par_->layout()->rightmargin);
+       frontend::FontMetrics const & fm = theFontMetrics(params.getFont());
+       int const r_margin =
+               lyx::rightMargin()
+               + fm.signedWidth(trmarg)
+               + fm.signedWidth(lrmarg)
+               * 4 / (par_->getDepth() + 4);
 
+       return r_margin;
+}
+
+
 Paragraph::Paragraph()
        : begin_of_body_(0), pimpl_(new Paragraph::Pimpl(this))
 {
@@ -140,8 +166,7 @@
 
 
 Paragraph::Paragraph(Paragraph const & par)
-       : ParagraphMetrics(par),
-       itemdepth(par.itemdepth), insetlist(par.insetlist),
+       : itemdepth(par.itemdepth), insetlist(par.insetlist),
        layout_(par.layout_),
        text_(par.text_), begin_of_body_(par.begin_of_body_),
        pimpl_(new Paragraph::Pimpl(*par.pimpl_, this))
@@ -172,8 +197,6 @@
 
                delete pimpl_;
                pimpl_ = new Pimpl(*par.pimpl_, this);
-
-               ParagraphMetrics::operator=(par);
        }
        return *this;
 }
@@ -1666,4 +1689,25 @@
 }
 
 
+bool Paragraph::checkBiblio(bool track_changes)
+{
+       // Add bibitem insets if necessary
+       if (layout()->labeltype != LABEL_BIBLIO)
+               return false;
+
+       bool hasbibitem = !insetlist.empty()
+               // Insist on it being in pos 0
+               && getChar(0) == Paragraph::META_INSET
+               && insetlist.begin()->inset->lyxCode() == 
InsetBase::BIBITEM_CODE;
+
+       if (hasbibitem)
+               return false;
+
+       InsetBibitem * inset(new InsetBibitem(InsetCommandParams("bibitem")));
+       insertInset(0, static_cast<InsetBase *>(inset),
+               Change(track_changes ? Change::INSERTED : Change::UNCHANGED));
+
+       return true;
+}
+
 } // namespace lyx
Index: paragraph.h
===================================================================
--- paragraph.h (revision 16395)
+++ paragraph.h (working copy)
@@ -58,53 +58,11 @@
        pos_type first, last;
 };
 
-/// Helper class for Paragraph Metrics.
-/// \todo FIXME: this class deserves its own .[Ch] files.
-/// Then, the storage of such object should be done in \c BufferView 
-/// (most probably in the \c CoordCache class along \c Point objects).
-class ParagraphMetrics  {
-public:
-       ///
-       Row & getRow(pos_type pos, bool boundary);
-       ///
-       Row const & getRow(pos_type pos, bool boundary) const;
-       ///
-       size_t pos2row(pos_type pos) const;
 
-       /// LyXText::redoParagraph updates this
-       Dimension & dim() { return dim_; }
-       /// total height of paragraph
-       unsigned int height() const { return dim_.height(); }
-       /// total width of paragraph, may differ from workwidth
-       unsigned int width() const { return dim_.width(); }
-       /// ascend of paragraph above baseline
-       unsigned int ascent() const { return dim_.ascent(); }
-       /// descend of paragraph below baseline
-       unsigned int descent() const { return dim_.descent(); }
-       /// LyXText updates the rows using this access point
-       RowList & rows() { return rows_; }
-       /// The painter and others use this
-       RowList const & rows() const { return rows_; }
-       ///
-       RowSignature & rowSignature() const { return rowSignature_; }
-
-       /// dump some information to lyxerr
-       void dump() const;
-
-private:
-       ///
-       mutable RowList rows_;
-       ///
-       mutable RowSignature rowSignature_;
-       /// cached dimensions of paragraph
-       Dimension dim_;
-};
-
-
 /// A Paragraph holds all text, attributes and insets in a text paragraph
 /// \todo FIXME: any reference to ParagraphMetrics (including inheritance)
 /// should go in order to complete the Model/View separation of this class.
-class Paragraph: public ParagraphMetrics  {
+class Paragraph  {
 public:
        ///
        enum {
@@ -406,6 +364,10 @@
        ///
        bool hfillExpansion(Row const & row, pos_type pos) const;
 
+       /// Check if we are in a Biblio environment.
+       /// \retval true if the cursor needs to be moved right.
+       bool checkBiblio(bool track_changes);
+
 public:
        ///
        InsetList insetlist;
@@ -430,6 +392,57 @@
        Pimpl * pimpl_;
 };
 
+/// Helper class for Paragraph Metrics.
+/// \todo FIXME: this class deserves its own .[Ch] files.
+/// Then, the storage of such object should be done in \c BufferView 
+/// (most probably in the \c CoordCache class along \c Point objects).
+class ParagraphMetrics  {
+public:
+       ///
+       ParagraphMetrics(): par_(0) {};
+       ///
+       ParagraphMetrics(Paragraph const & par);
+       ///
+       Row & getRow(pos_type pos, bool boundary);
+       ///
+       Row const & getRow(pos_type pos, bool boundary) const;
+       ///
+       size_t pos2row(pos_type pos) const;
+
+       /// BufferView::redoParagraph updates this
+       Dimension const & dim() const { return dim_; }
+       Dimension & dim() { return dim_; }
+       /// total height of paragraph
+       unsigned int height() const { return dim_.height(); }
+       /// total width of paragraph, may differ from workwidth
+       unsigned int width() const { return dim_.width(); }
+       /// ascend of paragraph above baseline
+       unsigned int ascent() const { return dim_.ascent(); }
+       /// descend of paragraph below baseline
+       unsigned int descent() const { return dim_.descent(); }
+       /// LyXText updates the rows using this access point
+       RowList & rows() { return rows_; }
+       /// The painter and others use this
+       RowList const & rows() const { return rows_; }
+       ///
+       RowSignature & rowSignature() const { return rowSignature_; }
+       ///
+       int rightMargin(Buffer const & buffer) const;
+
+       /// dump some information to lyxerr
+       void dump() const;
+
+private:
+       ///
+       mutable RowList rows_;
+       ///
+       mutable RowSignature rowSignature_;
+       /// cached dimensions of paragraph
+       Dimension dim_;
+       ///
+       Paragraph const * par_;
+};
+
 } // namespace lyx
 
 #endif // PARAGRAPH_H
Index: paragraph_funcs.C
===================================================================
--- paragraph_funcs.C   (revision 16395)
+++ paragraph_funcs.C   (working copy)
@@ -68,9 +68,6 @@
 
        Paragraph & par = pars[par_offset];
 
-       // we will invalidate the row cache
-       par.rows().clear();
-
        // without doing that we get a crash when typing <Return> at the
        // end of a paragraph
        tmp->layout(bparams.getLyXTextClass().defaultLayout());
Index: rowpainter.C
===================================================================
--- rowpainter.C        (revision 16395)
+++ rowpainter.C        (working copy)
@@ -106,6 +106,7 @@
        /// Row's paragraph
        pit_type const pit_;
        Paragraph const & par_;
+       ParagraphMetrics const & pm_;
 
        /// is row erased? (change tracking)
        bool erased_;
@@ -125,10 +126,13 @@
        LyXText const & text, pit_type pit, Row const & row, int x, int y)
        : bv_(*pi.base.bv), pain_(pi.pain), text_(text), 
pars_(text.paragraphs()),
          row_(row), pit_(pit), par_(text.paragraphs()[pit]),
+         pm_(pi.base.bv->parMetrics(&text, pit)),
          erased_(pi.erased_),
          xo_(x), yo_(y), width_(text_.width())
 {
-       RowMetrics m = text_.computeRowMetrics(*bv_.buffer(), pit, row_);
+       Buffer const & buffer = *bv_.buffer();
+       int const right_margin = text_.isMainText(buffer)? 
pm_.rightMargin(buffer) : 0;
+       RowMetrics m = text_.computeRowMetrics(*bv_.buffer(), right_margin, 
pit_, row_);
        x_ = m.x + xo_;
 
        //lyxerr << "RowPainter: x: " << x_ << " xo: " << xo_ << " yo: " << yo_ 
<< endl;
@@ -193,7 +197,8 @@
 #ifdef DEBUG_METRICS
        Dimension dim;
        BOOST_ASSERT(text_.maxwidth_ > 0);
-       int const w = text_.maxwidth_ - leftMargin() - 
text_.rightMargin(*bv_.buffer(), par_);
+       int right_margin = text_.isMainText()? pm_.rightMargin(bv_->buffer()) : 
0;
+       int const w = text_.maxwidth_ - leftMargin() - right_margin;
        MetricsInfo mi(&bv_, font, w);
        inset->metrics(mi, dim);
        if (inset->width() > dim.wid) 
@@ -620,7 +625,7 @@
                        if (layout->labeltype == 
LABEL_CENTERED_TOP_ENVIRONMENT) {
                                if (is_rtl)
                                        x = leftMargin();
-                               x += (width_ - text_.rightMargin(buffer, par_) 
- leftMargin()) / 2;
+                               x += (width_ - text_.rightMargin(buffer, pm_) - 
leftMargin()) / 2;
                                x -= fm.width(str) / 2;
                        } else if (is_rtl) {
                                x = width_ - leftMargin() -     fm.width(str);
@@ -872,11 +877,12 @@
        pi.base.bv->coordCache().parPos()[&text][pit] = Point(x, y);
 
        Paragraph const & par = text.paragraphs()[pit];
-       if (par.rows().empty())
+       ParagraphMetrics const & pm = pi.base.bv->parMetrics(&text, pit);
+       if (pm.rows().empty())
                return;
 
-       RowList::const_iterator const rb = par.rows().begin();
-       RowList::const_iterator const re = par.rows().end();
+       RowList::const_iterator const rb = pm.rows().begin();
+       RowList::const_iterator const re = pm.rows().end();
 
        y -= rb->ascent();
        size_type rowno = 0;
@@ -888,7 +894,7 @@
 
                // Row signature; has row changed since last paint?
                size_type const row_sig = calculateRowSignature(*rit, par, x, 
y);
-               bool row_has_changed = par.rowSignature()[rowno] != row_sig;
+               bool row_has_changed = pm.rowSignature()[rowno] != row_sig;
 
                bool cursor_on_row = CursorOnRow(pi, pit, rit, text);
                bool in_inset_alone_on_row = innerCursorOnRow(pi, pit, rit,
@@ -917,7 +923,7 @@
                // then paint the row
                if (repaintAll || row_has_changed || cursor_on_row) {
                        // Add to row signature cache
-                       par.rowSignature()[rowno] = row_sig;
+                       pm.rowSignature()[rowno] = row_sig;
                        
                        bool const inside = (y + rit->descent() >= 0
                                && y - rit->ascent() < ww);
@@ -998,10 +1004,10 @@
        // draw contents
        for (pit_type pit = vi.p1; pit <= vi.p2; ++pit) {
                refreshInside = repaintAll;
-               Paragraph const & par = text.getPar(pit);
-               yy += par.ascent();
+               ParagraphMetrics const & pm = bv.parMetrics(&text, pit);
+               yy += pm.ascent();
                paintPar(pi, text, pit, 0, yy, repaintAll);
-               yy += par.descent();
+               yy += pm.descent();
        }
 
        // and grey out above (should not happen later)
@@ -1020,13 +1026,15 @@
 {
 //     lyxerr << "  paintTextInset: y: " << y << endl;
 
-       y -= text.getPar(0).ascent();
+       y -= pi.base.bv->parMetrics(&text, 0).ascent();
        // This flag cannot be set from within same inset:
        bool repaintAll = refreshInside;
        for (int pit = 0; pit < int(text.paragraphs().size()); ++pit) {
-               y += text.getPar(pit).ascent();
+               ParagraphMetrics const & pmi 
+                       = pi.base.bv->parMetrics(&text, pit);
+               y += pmi.ascent();
                paintPar(pi, text, pit, x, y, repaintAll);
-               y += text.getPar(pit).descent();
+               y += pmi.descent();
        }
 }
 
Index: text.C
===================================================================
--- text.C      (revision 16395)
+++ text.C      (working copy)
@@ -648,27 +648,15 @@
 }
 
 
-int LyXText::rightMargin(Buffer const & buffer, Paragraph const & par) const
+int LyXText::rightMargin(Buffer const & buffer, ParagraphMetrics const & pm) 
const
 {
        // FIXME: the correct way is to only call rightMargin() only
        // within the main LyXText. The following test is thus bogus.
-       LyXText const & text = buffer.text();
+
        // We do not want rightmargins on inner texts.
-       if (&text != this)
-               return 0;
+       int right_margin = isMainText(buffer)? pm.rightMargin(buffer) : 0;
 
-       BufferParams const & params = buffer.params();
-       LyXTextClass const & tclass = params.getLyXTextClass();
-       docstring trmarg = from_utf8(tclass.rightmargin());
-       docstring lrmarg = from_utf8(par.layout()->rightmargin);
-       FontMetrics const & fm = theFontMetrics(params.getFont());
-       int const r_margin =
-               lyx::rightMargin()
-               + fm.signedWidth(trmarg)
-               + fm.signedWidth(lrmarg)
-               * 4 / (par.getDepth() + 4);
-
-       return r_margin;
+       return right_margin;
 }
 
 
@@ -731,6 +719,7 @@
        // or the end of the par, then choose the possible break
        // nearest that.
 
+       int label_end = labelEnd(buffer, pit);
        int const left = leftMargin(buffer, pit, pos);
        int x = left;
 
@@ -752,7 +741,7 @@
                        if (par.isLineSeparator(i - 1))
                                add -= singleWidth(buffer, par, i - 1);
 
-                       add = std::max(add, labelEnd(buffer, pit) - x);
+                       add = std::max(add, label_end - x);
                        thiswidth += add;
                }
 
@@ -815,7 +804,8 @@
 }
 
 
-void LyXText::setRowWidth(Buffer const & buffer, pit_type const pit, Row & 
row) const
+void LyXText::setRowWidth(Buffer const & buffer, int right_margin,
+               pit_type const pit, Row & row) const
 {
        // get the pure distance
        pos_type const end = row.endpos();
@@ -823,6 +813,7 @@
        Paragraph const & par = pars_[pit];
        docstring const labelsep = from_utf8(par.layout()->labelsep);
        int w = leftMargin(buffer, pit, row.pos());
+       int label_end = labelEnd(buffer, pit);
 
        pos_type const body_pos = par.beginOfBody();
        pos_type i = row.pos();
@@ -836,7 +827,7 @@
                                w += fm.width(labelsep);
                                if (par.isLineSeparator(i - 1))
                                        w -= singleWidth(buffer, par, i - 1);
-                               w = max(w, labelEnd(buffer, pit));
+                               w = max(w, label_end);
                        }
                        char_type const c = par.getChar(i);
                        w += singleWidth(par, i, c, *fi);
@@ -847,10 +838,10 @@
                w += fm.width(labelsep);
                if (end > 0 && par.isLineSeparator(end - 1))
                        w -= singleWidth(buffer, par, end - 1);
-               w = max(w, labelEnd(buffer, pit));
+               w = max(w, label_end);
        }
 
-       row.width(w + rightMargin(buffer, par));
+       row.width(w + right_margin);
 }
 
 
@@ -1140,22 +1131,15 @@
 
        updateLabels(cur.buffer(), current_it, last_it);
 
-       // FIXME: Breaking a paragraph has nothing to do with setting a cursor.
-       // Because of the mix between the model (the paragraph contents) and the
-       // view (the paragraph breaking in rows, we have to do this here before
-       // the setCursor() call below.
-       bool changed_height = redoParagraph(cur.bv(), cpit);
-       changed_height |= redoParagraph(cur.bv(), cpit + 1);
-       if (changed_height)
-               // A singlePar update is not enough in this case.
-               cur.updateFlags(Update::Force);
+       // A singlePar update is not enough in this case.
+       cur.updateFlags(Update::Force);
 
        // This check is necessary. Otherwise the new empty paragraph will
        // be deleted automatically. And it is more friendly for the user!
        if (cur.pos() != 0 || isempty)
-               setCursor(cur, cur.pit() + 1, 0);
+               setCursor(cur.top(), cur.pit() + 1, 0);
        else
-               setCursor(cur, cur.pit(), 0);
+               setCursor(cur.top(), cur.pit(), 0);
 }
 
 
@@ -1244,13 +1228,8 @@
 
        par.insertChar(cur.pos(), c, current_font, 
cur.buffer().params().trackChanges);
 
-       // FIXME: Inserting a character has nothing to do with setting a cursor.
-       // Because of the mix between the model (the paragraph contents) and the
-       // view (the paragraph breaking in rows, we have to do this here.
-       if (redoParagraph(cur.bv(), cur.pit()))
-               // A singlePar update is not enough in this case.
-               cur.updateFlags(Update::Force);
-       setCursor(cur, cur.pit(), cur.pos() + 1, false, cur.boundary());
+//             cur.updateFlags(Update::Force);
+       setCursor(cur.top(), cur.pit(), cur.pos() + 1);
        charInserted();
 }
 
@@ -1270,7 +1249,7 @@
 
 
 RowMetrics LyXText::computeRowMetrics(Buffer const & buffer,
-               pit_type const pit, Row const & row) const
+               int right_margin, pit_type const pit, Row const & row) const
 {
        RowMetrics result;
        Paragraph const & par = pars_[pit];
@@ -1279,7 +1258,7 @@
 
        bool const is_rtl = isRTL(buffer, par);
        if (is_rtl)
-               result.x = rightMargin(buffer, par);
+               result.x = right_margin;
        else
                result.x = leftMargin(buffer, pit, row.pos());
 
@@ -1657,17 +1636,10 @@
                }
        }
 
-       // FIXME: Inserting characters has nothing to do with setting a cursor.
-       // Because of the mix between the model (the paragraph contents)
-       // and the view (the paragraph breaking in rows, we have to do this
-       // here before the setCursorIntern() call.
        if (needsUpdate) {
-               if (redoParagraph(cur.bv(), cur.pit()))
-                       // A singlePar update is not enough in this case.
-                       cur.updateFlags(Update::Force);
                // Make sure the cursor is correct. Is this really needed?
                // No, not really... at least not here!
-               cur.text()->setCursorIntern(cur, cur.pit(), cur.pos());
+               cur.text()->setCursor(cur.top(), cur.pit(), cur.pos());
        }
        
        return needsUpdate;
@@ -1760,14 +1732,9 @@
        if (cur.pos() == cur.lastpos())
                setCurrentFont(cur);
 
-       // FIXME: Backspacing has nothing to do with setting a cursor.
-       // Because of the mix between the model (the paragraph contents)
-       // and the view (the paragraph breaking in rows, we have to do this
-       // here before the setCursor() call.
-       if (redoParagraph(cur.bv(), cur.pit()))
-               // A singlePar update is not enough in this case.
-               cur.updateFlags(Update::Force);
-       setCursor(cur, cur.pit(), cur.pos(), false, cur.boundary());
+       // A singlePar update is not enough in this case.
+//             cur.updateFlags(Update::Force);
+       setCursor(cur.top(), cur.pit(), cur.pos());
 
        return needsUpdate;
 }
@@ -1821,104 +1788,6 @@
 }
 
 
-Row const & LyXText::firstRow() const
-{
-       return *paragraphs().front().rows().begin();
-}
-
-
-bool LyXText::redoParagraph(BufferView & bv, pit_type const pit)
-{
-       // remove rows of paragraph, keep track of height changes
-       Paragraph & par = pars_[pit];
-       Buffer const & buffer = *bv.buffer();
-
-       bool changed = false;
-
-       // Add bibitem insets if necessary
-       if (par.layout()->labeltype == LABEL_BIBLIO) {
-               bool hasbibitem(false);
-               if (!par.insetlist.empty()
-                       // Insist on it being in pos 0
-                       && par.getChar(0) == Paragraph::META_INSET) {
-                       InsetBase * inset = par.insetlist.begin()->inset;
-                       if (inset->lyxCode() == InsetBase::BIBITEM_CODE)
-                               hasbibitem = true;
-               }
-               if (!hasbibitem) {
-                       InsetBibitem * inset(new
-                               InsetBibitem(InsetCommandParams("bibitem")));
-                       par.insertInset(0, static_cast<InsetBase *>(inset),
-                                       Change(buffer.params().trackChanges ?
-                                              Change::INSERTED : 
Change::UNCHANGED));
-                       bv.cursor().posRight();
-               }
-       }
-
-       // Optimisation: this is used in the next two loops
-       // so better to calculate that once here.
-       int right_margin = rightMargin(buffer, par);
-
-       // redo insets
-       // FIXME: We should always use getFont(), see documentation of
-       // noFontChange() in insetbase.h.
-       LyXFont const bufferfont = buffer.params().getFont();
-       InsetList::const_iterator ii = par.insetlist.begin();
-       InsetList::const_iterator iend = par.insetlist.end();
-       for (; ii != iend; ++ii) {
-               Dimension dim;
-               int const w = maxwidth_ - leftMargin(buffer, pit, ii->pos)
-                       - right_margin;
-               LyXFont const & font = ii->inset->noFontChange() ?
-                       bufferfont :
-                       getFont(buffer, par, ii->pos);
-               MetricsInfo mi(&bv, font, w);
-               changed |= ii->inset->metrics(mi, dim);
-       }
-
-       // rebreak the paragraph
-       par.rows().clear();
-       Dimension dim;
-
-       par.setBeginOfBody();
-       pos_type z = 0;
-       do {
-               Row row(z);
-               rowBreakPoint(buffer, right_margin, pit, row);
-               setRowWidth(buffer, pit, row);
-               setHeightOfRow(bv, pit, row);
-               par.rows().push_back(row);
-               dim.wid = std::max(dim.wid, row.width());
-               dim.des += row.height();
-               z = row.endpos();
-       } while (z < par.size());
-
-       // Make sure that if a par ends in newline, there is one more row
-       // under it
-       // FIXME this is a dirty trick. Now the _same_ position in the
-       // paragraph occurs in _two_ different rows, and has two different
-       // display positions, leading to weird behaviour when moving up/down.
-       if (z > 0 && par.isNewline(z - 1)) {
-               Row row(z - 1);
-               row.endpos(z - 1);
-               setRowWidth(buffer, pit, row);
-               setHeightOfRow(bv, pit, row);
-               par.rows().push_back(row);
-               dim.des += row.height();
-       }
-
-       dim.asc += par.rows()[0].ascent();
-       dim.des -= par.rows()[0].ascent();
-
-       changed |= dim.height() != par.dim().height();
-
-       par.dim() = dim;
-       //lyxerr << "redoParagraph: " << par.rows().size() << " rows\n";
-
-       return changed;
-}
-
-
 bool LyXText::metrics(MetricsInfo & mi, Dimension & dim)
 {
        //BOOST_ASSERT(mi.base.textwidth);
@@ -1934,15 +1803,16 @@
        unsigned int h = 0;
        unsigned int w = 0;
        for (pit_type pit = 0, n = paragraphs().size(); pit != n; ++pit) {
-               changed |= redoParagraph(*mi.base.bv, pit);
-               Paragraph & par = paragraphs()[pit];
-               h += par.height();
-               if (w < par.width())
-                       w = par.width();
+               changed |= mi.base.bv->redoParagraph(this, pit);
+               ParagraphMetrics const & pm = mi.base.bv->parMetrics(this, pit);
+               h += pm.height();
+               if (w < pm.width())
+                       w = pm.width();
        }
 
+       ParagraphMetrics const & pm0 = mi.base.bv->parMetrics(this, 0);
        dim.wid = w;
-       dim.asc = pars_[0].ascent();
+       dim.asc = pm0.ascent();
        dim.des = h - dim.asc;
 
        changed |= dim_ != dim;
@@ -1987,6 +1857,8 @@
 
        Paragraph const & par1 = pars_[beg.pit()];
        Paragraph const & par2 = pars_[end.pit()];
+       ParagraphMetrics const & pm1 = bv.parMetrics(this, beg.pit());
+       ParagraphMetrics const & pm2 = bv.parMetrics(this, end.pit());
 
        bool const above = (bv_funcs::status(&bv, beg)
                            == bv_funcs::CUR_ABOVE);
@@ -1999,10 +1871,10 @@
                x1 = 0;
                x2 = dim_.wid;
        } else {
-               Row const & row1 = par1.getRow(beg.pos(), beg.boundary());
+               Row const & row1 = pm1.getRow(beg.pos(), beg.boundary());
                y1 = bv_funcs::getPos(bv, beg, beg.boundary()).y_ - 
row1.ascent();
                y2 = y1 + row1.height();
-               int const startx = cursorX(buffer, beg.top(), beg.boundary());
+               int const startx = cursorX(bv, beg.top(), beg.boundary());
                if (!isRTL(buffer, par1)) {
                        x1 = startx;
                        x2 = 0 + dim_.wid;
@@ -2020,10 +1892,10 @@
                X1 = 0;
                X2 = dim_.wid;
        } else {
-               Row const & row2 = par2.getRow(end.pos(), end.boundary());
+               Row const & row2 = pm2.getRow(end.pos(), end.boundary());
                Y1 = bv_funcs::getPos(bv, end, end.boundary()).y_ - 
row2.ascent();
                Y2 = Y1 + row2.height();
-               int const endx = cursorX(buffer, end.top(), end.boundary());
+               int const endx = cursorX(bv, end.top(), end.boundary());
                if (!isRTL(buffer, par2)) {
                        X1 = 0;
                        X2 = endx;
@@ -2034,8 +1906,8 @@
                }
        }
 
-       if (!above && !below && &par1.getRow(beg.pos(), beg.boundary())
-           == &par2.getRow(end.pos(), end.boundary()))
+       if (!above && !below && &pm1.getRow(beg.pos(), beg.boundary())
+           == &pm2.getRow(end.pos(), end.boundary()))
        {
                // paint only one rectangle
                int const b( !isRTL(*bv.buffer(), par1) ? x + x1 : x + X1 );
@@ -2188,12 +2060,13 @@
 }
 
 
-int LyXText::cursorX(Buffer const & buffer, CursorSlice const & sl,
+int LyXText::cursorX(BufferView const & bv, CursorSlice const & sl,
                bool boundary) const
 {
        pit_type const pit = sl.pit();
        Paragraph const & par = pars_[pit];
-       if (par.rows().empty())
+       ParagraphMetrics const & pm = bv.parMetrics(sl.text(), pit);
+       if (pm.rows().empty())
                return 0;
 
        pos_type ppos = sl.pos();
@@ -2202,11 +2075,13 @@
        if (boundary_correction)
                --ppos;
 
-       Row const & row = par.getRow(sl.pos(), boundary);
+       Row const & row = pm.getRow(sl.pos(), boundary);
 
        pos_type cursor_vpos = 0;
 
-       RowMetrics const m = computeRowMetrics(buffer, pit, row);
+       Buffer const & buffer = *bv.buffer();
+       int const right_margin = isMainText(buffer)? pm.rightMargin(buffer) : 0;
+       RowMetrics const m = computeRowMetrics(buffer, right_margin, pit, row);
        double x = m.x;
 
        pos_type const row_pos  = row.pos();
@@ -2279,24 +2154,25 @@
 }
 
 
-int LyXText::cursorY(CursorSlice const & sl, bool boundary) const
+int LyXText::cursorY(BufferView const & bv, CursorSlice const & sl, bool 
boundary) const
 {
        //lyxerr << "LyXText::cursorY: boundary: " << boundary << std::endl;
-       Paragraph const & par = getPar(sl.pit());
-       if (par.rows().empty())
+       ParagraphMetrics const & pm = bv.parMetrics(this, sl.pit());
+       if (pm.rows().empty())
                return 0;
 
        int h = 0;
-       h -= pars_[0].rows()[0].ascent();
-       for (pit_type pit = 0; pit < sl.pit(); ++pit)
-               h += pars_[pit].height();
+       h -= bv.parMetrics(this, 0).rows()[0].ascent();
+       for (pit_type pit = 0; pit < sl.pit(); ++pit) {
+               h += bv.parMetrics(this, pit).height();
+       }
        int pos = sl.pos();
        if (pos && boundary)
                --pos;
-       size_t const rend = par.pos2row(pos);
+       size_t const rend = pm.pos2row(pos);
        for (size_t rit = 0; rit != rend; ++rit)
-               h += par.rows()[rit].height();
-       h += par.rows()[rend].ascent();
+               h += pm.rows()[rit].height();
+       h += pm.rows()[rend].ascent();
        return h;
 }
 
@@ -2447,11 +2323,14 @@
 pos_type LyXText::x2pos(BufferView const & bv, pit_type pit, int row,
                int x) const
 {
-       BOOST_ASSERT(!pars_[pit].rows().empty());
-       BOOST_ASSERT(row < int(pars_[pit].rows().size()));
+       ParagraphMetrics const & pm = bv.parMetrics(this, pit);
+       BOOST_ASSERT(!pm.rows().empty());
+       BOOST_ASSERT(row < int(pm.rows().size()));
        bool bound = false;
-       Row const & r = pars_[pit].rows()[row];
-       return r.pos() + getColumnNearX(bv, pit, r, x, bound);
+       Row const & r = pm.rows()[row];
+       Buffer const & buffer = *bv.buffer();
+       int right_margin = isMainText(buffer)? pm.rightMargin(buffer) : 0;
+       return r.pos() + getColumnNearX(bv, right_margin, pit, r, x, bound);
 }
 
 
@@ -2470,7 +2349,10 @@
 {
        BOOST_ASSERT(this == cur.text());
        pit_type pit = getPitNearY(cur.bv(), y);
-       int yy = cur.bv().coordCache().get(this, pit).y_ - pars_[pit].ascent();
+
+       ParagraphMetrics const & pm = cur.bv().parMetrics(this, pit);
+
+       int yy = cur.bv().coordCache().get(this, pit).y_ - pm.ascent();
        lyxerr[Debug::DEBUG]
                << BOOST_CURRENT_FUNCTION
                << ": x: " << x
@@ -2478,17 +2360,16 @@
                << " pit: " << pit
                << " yy: " << yy << endl;
 
-       Paragraph const & par = pars_[pit];
        int r = 0;
-       BOOST_ASSERT(par.rows().size());
-       for (; r < int(par.rows().size()) - 1; ++r) {
-               Row const & row = par.rows()[r];
+       BOOST_ASSERT(pm.rows().size());
+       for (; r < int(pm.rows().size()) - 1; ++r) {
+               Row const & row = pm.rows()[r];
                if (int(yy + row.height()) > y)
                        break;
                yy += row.height();
        }
 
-       Row const & row = par.rows()[r];
+       Row const & row = pm.rows()[r];
 
        lyxerr[Debug::DEBUG]
                << BOOST_CURRENT_FUNCTION
@@ -2498,8 +2379,10 @@
 
        bool bound = false;
        int xx = x;
-       pos_type const pos = row.pos() + getColumnNearX(cur.bv(), pit, row,
-               xx, bound);
+       Buffer const & buffer = cur.buffer();
+       int right_margin = isMainText(buffer)? pm.rightMargin(buffer) : 0;
+       pos_type const pos = row.pos() + getColumnNearX(cur.bv(), right_margin, 
+               pit, row, xx, bound);
 
        lyxerr[Debug::DEBUG]
                << BOOST_CURRENT_FUNCTION
Index: text2.C
===================================================================
--- text2.C     (revision 16395)
+++ text2.C     (working copy)
@@ -87,10 +87,6 @@
        dim_.asc = 10;
        dim_.des = 10;
 
-       pit_type const end = paragraphs().size();
-       for (pit_type pit = 0; pit != end; ++pit)
-               pars_[pit].rows().clear();
-
        updateLabels(*bv->buffer());
 }
 
@@ -502,7 +498,8 @@
 bool LyXText::cursorHome(LCursor & cur)
 {
        BOOST_ASSERT(this == cur.text());
-       Row const & row = cur.paragraph().getRow(cur.pos(),cur.boundary());
+       ParagraphMetrics const & pm = cur.bv().parMetrics(this, cur.pit());
+       Row const & row = pm.getRow(cur.pos(),cur.boundary());
        return setCursor(cur, cur.pit(), row.pos());
 }
 
@@ -719,7 +716,6 @@
        BOOST_ASSERT(this == cur.text());
        cur.boundary(boundary);
        setCursor(cur.top(), par, pos);
-       cur.setTargetX();
        if (setfont)
                setCurrentFont(cur);
 }
@@ -767,13 +763,13 @@
 // x is an absolute screen coord
 // returns the column near the specified x-coordinate of the row
 // x is set to the real beginning of this column
-pos_type LyXText::getColumnNearX(BufferView const & bv, pit_type const pit,
-                                Row const & row, int & x, bool & boundary) 
const
+pos_type LyXText::getColumnNearX(BufferView const & bv, int right_margin,
+               pit_type const pit, Row const & row, int & x, bool & boundary) 
const
 {
        Buffer const & buffer = *bv.buffer();
        int const xo = bv.coordCache().get(this, pit).x_;
        x -= xo;
-       RowMetrics const r = computeRowMetrics(buffer, pit, row);
+       RowMetrics const r = computeRowMetrics(buffer, right_margin, pit, row);
        Paragraph const & par = pars_[pit];
 
        pos_type vc = row.pos();
@@ -921,35 +917,39 @@
        CoordCache::InnerParPosCache::const_iterator et = cc.end();
        CoordCache::InnerParPosCache::const_iterator last = et; last--;
 
+       ParagraphMetrics const & pm = bv.parMetrics(this, it->first);
+
        // If we are off-screen (before the visible part)
        if (y < 0
                // and even before the first paragraph in the cache.
-               && y < it->second.y_ - int(pars_[it->first].ascent())) {
+               && y < it->second.y_ - int(pm.ascent())) {
                //  and we are not at the first paragraph in the inset.
                if (it->first == 0)
                        return 0;
                // then this is the paragraph we are looking for.
                pit = it->first - 1;
                // rebreak it and update the CoordCache.
-               redoParagraph(bv, pit);
+               bv.redoParagraph(this, pit);
                bv.coordCache().parPos()[this][pit] =
-                       Point(0, it->second.y_ - pars_[it->first].descent());
+                       Point(0, it->second.y_ - pm.descent());
                return pit;
        }
 
+       ParagraphMetrics const & pm_last = bv.parMetrics(this, last->first);
+
        // If we are off-screen (after the visible part)
        if (y > bv.workHeight()
                // and even after the first paragraph in the cache.
-               && y >= last->second.y_ + int(pars_[last->first].descent())) {
+               && y >= last->second.y_ + int(pm_last.descent())) {
                pit = last->first + 1;
                //  and we are not at the last paragraph in the inset.
                if (pit == int(pars_.size()))
                        return last->first;
                // then this is the paragraph we are looking for.
                // rebreak it and update the CoordCache.
-               redoParagraph(bv, pit);
+               bv.redoParagraph(this, pit);
                bv.coordCache().parPos()[this][pit] =
-                       Point(0, last->second.y_ + pars_[last->first].ascent());
+                       Point(0, last->second.y_ + pm_last.ascent());
                return pit;
        }
 
@@ -960,7 +960,9 @@
                        << " y: " << it->second.y_
                        << endl;
 
-               if (it->first >= pit && int(it->second.y_) - 
int(pars_[it->first].ascent()) <= y) {
+               ParagraphMetrics const & pm = bv.parMetrics(this, it->first);
+
+               if (it->first >= pit && int(it->second.y_) - int(pm.ascent()) 
<= y) {
                        pit = it->first;
                        yy = it->second.y_;
                }
@@ -978,10 +980,12 @@
 Row const & LyXText::getRowNearY(BufferView const & bv, int y, pit_type pit) 
const
 {
        Paragraph const & par = pars_[pit];
-       int yy = bv.coordCache().get(this, pit).y_ - par.ascent();
-       BOOST_ASSERT(!par.rows().empty());
-       RowList::const_iterator rit = par.rows().begin();
-       RowList::const_iterator const rlast = boost::prior(par.rows().end());
+       ParagraphMetrics const & pm = bv.parMetrics(this, pit);
+
+       int yy = bv.coordCache().get(this, pit).y_ - pm.ascent();
+       BOOST_ASSERT(!pm.rows().empty());
+       RowList::const_iterator rit = pm.rows().begin();
+       RowList::const_iterator const rlast = boost::prior(pm.rows().end());
        for (; rit != rlast; yy += rit->height(), ++rit)
                if (yy + rit->height() > y)
                        break;
@@ -999,18 +1003,16 @@
        }
        pit_type pit = getPitNearY(cur.bv(), y);
        BOOST_ASSERT(pit != -1);
-       // When another window is opened with the same document, rows()
-       // will be cleared so pars_[pit].rows() might be empty when switching
-       // between windwos. A better solution is that each buffer view
-       // has its own rows() for the same buffer.
-       if (pars_[pit].rows().empty())
-               redoParagraph(cur.bv(), pit);
+
        Row const & row = getRowNearY(cur.bv(), y, pit);
        bool bound = false;
 
+       ParagraphMetrics const & pm = cur.bv().parMetrics(this, pit);
+       Buffer const & buffer = cur.buffer();
+       int right_margin = isMainText(buffer)? pm.rightMargin(buffer) : 0;
        int xx = x; // is modified by getColumnNearX
        pos_type const pos = row.pos()
-               + getColumnNearX(cur.bv(), pit, row, xx, bound);
+               + getColumnNearX(cur.bv(), right_margin, pit, row, xx, bound);
        cur.pit() = pit;
        cur.pos() = pos;
        cur.boundary(bound);
@@ -1131,13 +1133,15 @@
        cur.updateFlags(Update::FitCursor);
 
        Paragraph const & par = cur.paragraph();
+       ParagraphMetrics const & pm = cur.bv().parMetrics(this, cur.pit());
+
        int row;
        int const x = cur.targetX();
 
        if (cur.pos() && cur.boundary())
-               row = par.pos2row(cur.pos()-1);
+               row = pm.pos2row(cur.pos()-1);
        else
-               row = par.pos2row(cur.pos());
+               row = pm.pos2row(cur.pos());
 
        if (!cur.selection()) {
                int const y = bv_funcs::getPos(cur.bv(), cur, 
cur.boundary()).y_;
@@ -1145,7 +1149,7 @@
                // Go to middle of previous row. 16 found to work OK;
                // 12 = top/bottom margin of display math
                int const margin = 3 * InsetMathHull::displayMargin() / 2;
-               editXY(cur, x, y - par.rows()[row].ascent() - margin);
+               editXY(cur, x, y - pm.rows()[row].ascent() - margin);
                cur.clearSelection();
 
                // This happens when you move out of an inset.
@@ -1167,8 +1171,9 @@
        } else if (cur.pit() > 0) {
                --cur.pit();
                //cannot use 'par' now
+               ParagraphMetrics const & pmcur = cur.bv().parMetrics(this, 
cur.pit());
                updateNeeded |= setCursor(cur, cur.pit(),
-                       x2pos(cur.bv(), cur.pit(), 
cur.paragraph().rows().size() - 1, x));
+                       x2pos(cur.bv(), cur.pit(), pmcur.rows().size() - 1, x));
        }
 
        cur.x_target() = x;
@@ -1183,20 +1188,22 @@
        cur.updateFlags(Update::FitCursor);
 
        Paragraph const & par = cur.paragraph();
+       ParagraphMetrics const & pm = cur.bv().parMetrics(this, cur.pit());
+
        int row;
        int const x = cur.targetX();
 
        if (cur.pos() && cur.boundary())
-               row = par.pos2row(cur.pos()-1);
+               row = pm.pos2row(cur.pos()-1);
        else
-               row = par.pos2row(cur.pos());
+               row = pm.pos2row(cur.pos());
 
        if (!cur.selection()) {
                int const y = bv_funcs::getPos(cur.bv(), cur, 
cur.boundary()).y_;
                LCursor old = cur;
                // To middle of next row
                int const margin = 3 * InsetMathHull::displayMargin() / 2;
-               editXY(cur, x, y + par.rows()[row].descent() + margin);
+               editXY(cur, x, y + pm.rows()[row].descent() + margin);
                cur.clearSelection();
 
                // This happens when you move out of an inset.
@@ -1218,7 +1225,7 @@
 
        bool updateNeeded = false;
 
-       if (row + 1 < int(par.rows().size())) {
+       if (row + 1 < int(pm.rows().size())) {
                updateNeeded |= setCursor(cur, cur.pit(),
                        x2pos(cur.bv(), cur.pit(), row + 1, x));
        } else if (cur.pit() + 1 < int(paragraphs().size())) {
@@ -1322,7 +1329,7 @@
                    && !oldpar.isDeleted(old.pos() - 1)) {
                        oldpar.eraseChar(old.pos() - 1, false); // do not track 
changes in DEPM
                        // rebreak it and update the CoordCache.
-                       redoParagraph(cur.bv(), old.pit());
+                       cur.bv().redoParagraph(this, old.pit());
 #ifdef WITH_WARNINGS
 #warning This will not work anymore when we have multiple views of the same 
buffer
 // In this case, we will have to correct also the cursors held by
Index: text3.C
===================================================================
--- text3.C     (revision 16395)
+++ text3.C     (working copy)
@@ -333,7 +333,9 @@
        bool needsUpdate = !(lyxaction.funcHasFlag(cmd.action,
                LyXAction::NoUpdate) || singleParUpdate);
        // Remember the old paragraph metric (_outer_ paragraph!)
-       Dimension olddim = cur.bottom().paragraph().dim();
+       ParagraphMetrics const & pm = cur.bv().parMetrics(
+               cur.bottom().text(), cur.bottom().pit());
+       Dimension olddim = pm.dim();
 
        switch (cmd.action) {
 
@@ -793,8 +795,8 @@
 
        case LFUN_SERVER_GET_XY:
                cur.message(from_utf8(
-                       convert<string>(cursorX(cur.buffer(), cur.top(), 
cur.boundary()))
-                       + ' ' + convert<string>(cursorY(cur.top(), 
cur.boundary()))));
+                       convert<string>(cursorX(cur.bv(), cur.top(), 
cur.boundary()))
+                       + ' ' + convert<string>(cursorY(cur.bv(), cur.top(), 
cur.boundary()))));
                break;
 
        case LFUN_SERVER_SET_XY: {
@@ -1514,9 +1516,11 @@
 
        // FIXME: the following code should go in favor of fine grained
        // update flag treatment.
-       if (singleParUpdate)
+       if (singleParUpdate) {
                // Inserting characters does not change par height
-               if (cur.bottom().paragraph().dim().height()
+               ParagraphMetrics const & pms 
+                       = cur.bv().parMetrics(cur.bottom().text(), 
cur.bottom().pit());
+               if (pms.dim().height()
                    == olddim.height()) {
                        // if so, update _only_ this paragraph
                        cur.updateFlags(Update::SinglePar |
@@ -1525,6 +1529,7 @@
                        return;
                } else
                        needsUpdate = true;
+       }
 
        if (!needsUpdate
            && &oldTopSlice.inset() == &cur.inset()

Reply via email to