]> git.lyx.org Git - lyx.git/blobdiff - src/text2.C
more dialog merging
[lyx.git] / src / text2.C
index bf67799b0e981779e1e2ccc52c21570afd04460d..a570b387d498bc6d98110e482c25e1f2a57ad88a 100644 (file)
-/* This file is part of
- * ======================================================
+/**
+ * \file text2.C
+ * This file is part of LyX, the document processor.
+ * Licence details can be found in the file COPYING.
  *
- *           LyX, The Document Processor
+ * \author Asger Alstrup
+ * \author Lars Gullik Bjønnes
+ * \author Alfredo Braunstein
+ * \author Jean-Marc Lasgouttes
+ * \author Angus Leeming
+ * \author John Levon
+ * \author André Pönitz
+ * \author Allan Rae
+ * \author Dekel Tsur
+ * \author Jürgen Vigna
  *
- *           Copyright 1995 Matthias Ettrich
- *           Copyright 1995-2001 The LyX Team.
- *
- * ====================================================== */
+ * Full author contact details are available in file CREDITS.
+ */
 
 #include <config.h>
 
 #include "lyxtext.h"
-#include "LString.h"
-#include "Lsstream.h"
-#include "paragraph.h"
-#include "funcrequest.h"
-#include "frontends/LyXView.h"
-#include "undo_funcs.h"
+
 #include "buffer.h"
 #include "buffer_funcs.h"
+#include "bufferlist.h"
 #include "bufferparams.h"
-#include "errorlist.h"
-#include "gettext.h"
 #include "BufferView.h"
+#include "bufferview_funcs.h"
+#include "Bullet.h"
+#include "coordcache.h"
+#include "cursor.h"
 #include "CutAndPaste.h"
-#include "frontends/Painter.h"
-#include "frontends/font_metrics.h"
 #include "debug.h"
-#include "lyxrc.h"
-#include "FloatList.h"
+#include "dispatchresult.h"
+#include "errorlist.h"
+#include "funcrequest.h"
+#include "gettext.h"
 #include "language.h"
-#include "ParagraphParameters.h"
-#include "counters.h"
-#include "lyxrow_funcs.h"
-#include "metricsinfo.h"
+#include "LColor.h"
+#include "lyxfunc.h"
+#include "lyxrc.h"
+#include "lyxrow.h"
+#include "paragraph.h"
+#include "TextMetrics.h"
 #include "paragraph_funcs.h"
+#include "ParagraphParameters.h"
+#include "pariterator.h"
+#include "lyxserver.h"
+#include "lyxsocket.h"
+#include "undo.h"
+#include "vspace.h"
+
+#include "frontends/FontMetrics.h"
 
-#include "insets/insetbibitem.h"
-#include "insets/insetenv.h"
-#include "insets/insetfloat.h"
-#include "insets/insetwrap.h"
+#include "insets/InsetEnv.h"
+
+#include "mathed/InsetMathHull.h"
 
-#include "support/LAssert.h"
 #include "support/textutils.h"
-#include "support/lstrings.h"
 
-#include <boost/tuple/tuple.hpp>
+#include <boost/current_function.hpp>
+
+#include <sstream>
 
-#include <algorithm>
 
-using namespace lyx::support;
+namespace lyx {
 
-using std::vector;
-using std::copy;
 using std::endl;
-using std::find;
-using std::pair;
-using lyx::pos_type;
+using std::ostringstream;
+using std::string;
+using std::max;
+using std::min;
 
 
-LyXText::LyXText(BufferView * bv)
-       : height(0), width(0), anchor_row_offset_(0),
-         inset_owner(0), the_locking_inset(0), bv_owner(bv)
-{
-       anchor_row_ = endRow();
-}
+LyXText::LyXText()
+       : current_font(LyXFont::ALL_INHERIT),
+         background_color_(LColor::background),
+         autoBreakRows_(false)
+{}
 
 
-LyXText::LyXText(BufferView * bv, InsetText * inset)
-       : height(0), width(0), anchor_row_offset_(0),
-         inset_owner(inset), the_locking_inset(0), bv_owner(bv)
+bool LyXText::isMainText(Buffer const & buffer) const
 {
-       anchor_row_ = endRow();
+       return &buffer.text() == this;
 }
 
 
-void LyXText::init(BufferView * bview)
+//takes screen x,y coordinates
+InsetBase * LyXText::checkInsetHit(BufferView & bv, int x, int y)
 {
-       bv_owner = bview;
-
-       ParagraphList::iterator const beg = ownerParagraphs().begin();
-       ParagraphList::iterator const end = ownerParagraphs().end();
-       for (ParagraphList::iterator pit = beg; pit != end; ++pit)
-               pit->rows.clear();
-
-       width = 0;
-       height = 0;
-
-       anchor_row_ = endRow();
-       anchor_row_offset_ = 0;
-
-       current_font = getFont(beg, 0);
-
-       redoParagraphs(beg, end);
-       setCursorIntern(beg, 0);
-       selection.cursor = cursor;
-
-       updateCounters();
+       pit_type pit = getPitNearY(bv, y);
+       BOOST_ASSERT(pit != -1);
+
+       Paragraph const & par = pars_[pit];
+
+       LYXERR(Debug::DEBUG)
+               << BOOST_CURRENT_FUNCTION
+               << ": x: " << x
+               << " y: " << y
+               << "  pit: " << pit
+               << endl;
+       InsetList::const_iterator iit = par.insetlist.begin();
+       InsetList::const_iterator iend = par.insetlist.end();
+       for (; iit != iend; ++iit) {
+               InsetBase * inset = iit->inset;
+#if 1
+               LYXERR(Debug::DEBUG)
+                       << BOOST_CURRENT_FUNCTION
+                       << ": examining inset " << inset << endl;
+
+               if (bv.coordCache().getInsets().has(inset))
+                       LYXERR(Debug::DEBUG)
+                               << BOOST_CURRENT_FUNCTION
+                               << ": xo: " << inset->xo(bv) << "..."
+                               << inset->xo(bv) + inset->width()
+                               << " yo: " << inset->yo(bv) - inset->ascent()
+                               << "..."
+                               << inset->yo(bv) + inset->descent()
+                               << endl;
+               else
+                       LYXERR(Debug::DEBUG)
+                               << BOOST_CURRENT_FUNCTION
+                               << ": inset has no cached position" << endl;
+#endif
+               if (inset->covers(bv, x, y)) {
+                       LYXERR(Debug::DEBUG)
+                               << BOOST_CURRENT_FUNCTION
+                               << ": Hit inset: " << inset << endl;
+                       return inset;
+               }
+       }
+       LYXERR(Debug::DEBUG)
+               << BOOST_CURRENT_FUNCTION
+               << ": No inset hit. " << endl;
+       return 0;
 }
 
 
+
 // Gets the fully instantiated font at a given position in a paragraph
 // Basically the same routine as Paragraph::getFont() in paragraph.C.
 // The difference is that this one is used for displaying, and thus we
 // are allowed to make cosmetic improvements. For instance make footnotes
 // smaller. (Asger)
-LyXFont LyXText::getFont(ParagraphList::iterator pit, pos_type pos) const
+LyXFont LyXText::getFont(Buffer const & buffer, Paragraph const & par,
+               pos_type const pos) const
 {
-       Assert(pos >= 0);
+       BOOST_ASSERT(pos >= 0);
 
-       LyXLayout_ptr const & layout = pit->layout();
+       LyXLayout_ptr const & layout = par.layout();
+#ifdef WITH_WARNINGS
 #warning broken?
-       BufferParams const & params = bv()->buffer()->params;
+#endif
+       BufferParams const & params = buffer.params();
+       pos_type const body_pos = par.beginOfBody();
 
        // We specialize the 95% common case:
-       if (!pit->getDepth()) {
-               if (layout->labeltype == LABEL_MANUAL
-                   && pos < pit->beginningOfBody()) {
-                       // 1% goes here
-                       LyXFont f = pit->getFontSettings(params, pos);
-                       if (pit->inInset())
-                               pit->inInset()->getDrawFont(f);
-                       return f.realize(layout->reslabelfont);
+       if (!par.getDepth()) {
+               LyXFont f = par.getFontSettings(params, pos);
+               if (!isMainText(buffer))
+                       applyOuterFont(buffer, f);
+               LyXFont lf;
+               LyXFont rlf;
+               if (layout->labeltype == LABEL_MANUAL && pos < body_pos) {
+                       lf = layout->labelfont;
+                       rlf = layout->reslabelfont;
                } else {
-                       LyXFont f = pit->getFontSettings(params, pos);
-                       if (pit->inInset())
-                               pit->inInset()->getDrawFont(f);
-                       return f.realize(layout->resfont);
+                       lf = layout->font;
+                       rlf = layout->resfont;
                }
+               // In case the default family has been customized
+               if (lf.family() == LyXFont::INHERIT_FAMILY)
+                       rlf.setFamily(params.getFont().family());
+               return f.realize(rlf);
        }
 
        // The uncommon case need not be optimized as much
-
        LyXFont layoutfont;
-
-       if (pos < pit->beginningOfBody()) {
-               // 1% goes here
+       if (pos < body_pos)
                layoutfont = layout->labelfont;
-       } else {
-               // 99% goes here
+       else
                layoutfont = layout->font;
-       }
 
-       LyXFont tmpfont = pit->getFontSettings(params, pos);
-       tmpfont.realize(layoutfont);
+       LyXFont font = par.getFontSettings(params, pos);
+       font.realize(layoutfont);
 
-       if (pit->inInset())
-               pit->inInset()->getDrawFont(tmpfont);
+       if (!isMainText(buffer))
+               applyOuterFont(buffer, font);
+
+       // Find the pit value belonging to paragraph. This will not break
+       // even if pars_ would not be a vector anymore.
+       // Performance appears acceptable.
+
+       pit_type pit = pars_.size();
+       for (pit_type it = 0; it < pit; ++it)
+               if (&pars_[it] == &par) {
+                       pit = it;
+                       break;
+               }
+       // Realize against environment font information
+       // NOTE: the cast to pit_type should be removed when pit_type
+       // changes to a unsigned integer.
+       if (pit < pit_type(pars_.size()))
+               font.realize(outerFont(pit, pars_));
 
        // Realize with the fonts of lesser depth.
-       tmpfont.realize(outerFont(pit, ownerParagraphs()));
-       tmpfont.realize(defaultfont_);
+       font.realize(params.getFont());
 
-       return tmpfont;
+       return font;
 }
 
+// There are currently two font mechanisms in LyX:
+// 1. The font attributes in a lyxtext, and
+// 2. The inset-specific font properties, defined in an inset's
+// metrics() and draw() methods and handed down the inset chain through
+// the pi/mi parameters, and stored locally in a lyxtext in font_.
+// This is where the two are integrated in the final fully realized
+// font.
+void LyXText::applyOuterFont(Buffer const & buffer, LyXFont & font) const {
+       LyXFont lf(font_);
+       lf.reduce(buffer.params().getFont());
+       lf.realize(font);
+       lf.setLanguage(font.language());
+       font = lf;
+}
 
-LyXFont LyXText::getLayoutFont(ParagraphList::iterator pit) const
-{
-       LyXLayout_ptr const & layout = pit->layout();
 
-       if (!pit->getDepth())
-               return layout->resfont;
+LyXFont LyXText::getLayoutFont(Buffer const & buffer, pit_type const pit) const
+{
+       LyXLayout_ptr const & layout = pars_[pit].layout();
+
+       if (!pars_[pit].getDepth())  {
+               LyXFont lf = layout->resfont;
+               // In case the default family has been customized
+               if (layout->font.family() == LyXFont::INHERIT_FAMILY)
+                       lf.setFamily(buffer.params().getFont().family());
+               return lf;
+       }
 
        LyXFont font = layout->font;
        // Realize with the fonts of lesser depth.
-       font.realize(outerFont(pit, ownerParagraphs()));
-       font.realize(defaultfont_);
+       //font.realize(outerFont(pit, paragraphs()));
+       font.realize(buffer.params().getFont());
 
        return font;
 }
 
 
-LyXFont LyXText::getLabelFont(ParagraphList::iterator pit) const
+LyXFont LyXText::getLabelFont(Buffer const & buffer, Paragraph const & par) const
 {
-       LyXLayout_ptr const & layout = pit->layout();
+       LyXLayout_ptr const & layout = par.layout();
 
-       if (!pit->getDepth())
-               return layout->reslabelfont;
+       if (!par.getDepth()) {
+               LyXFont lf = layout->reslabelfont;
+               // In case the default family has been customized
+               if (layout->labelfont.family() == LyXFont::INHERIT_FAMILY)
+                       lf.setFamily(buffer.params().getFont().family());
+               return lf;
+       }
 
        LyXFont font = layout->labelfont;
        // Realize with the fonts of lesser depth.
-       font.realize(outerFont(pit, ownerParagraphs()));
-       font.realize(defaultfont_);
+       font.realize(buffer.params().getFont());
 
        return font;
 }
 
 
-void LyXText::setCharFont(ParagraphList::iterator pit,
-                         pos_type pos, LyXFont const & fnt,
-                         bool toggleall)
-{
-       BufferParams const & params = bv()->buffer()->params;
-       LyXFont font = getFont(pit, pos);
-       font.update(fnt, params.language, toggleall);
-       // Let the insets convert their font
-       if (pit->isInset(pos)) {
-               InsetOld * inset = pit->getInset(pos);
-               if (isEditableInset(inset)) {
-                       static_cast<UpdatableInset *>(inset)
-                               ->setFont(bv(), fnt, toggleall, true);
-               }
-       }
-
-       // Plug through to version below:
-       setCharFont(pit, pos, font);
-}
-
-
-void LyXText::setCharFont(
-       ParagraphList::iterator pit, pos_type pos, LyXFont const & fnt)
+void LyXText::setCharFont(Buffer const & buffer, pit_type pit,
+               pos_type pos, LyXFont const & fnt)
 {
        LyXFont font = fnt;
-       LyXLayout_ptr const & layout = pit->layout();
+       LyXLayout_ptr const & layout = pars_[pit].layout();
 
        // Get concrete layout font to reduce against
        LyXFont layoutfont;
 
-       if (pos < pit->beginningOfBody())
+       if (pos < pars_[pit].beginOfBody())
                layoutfont = layout->labelfont;
        else
                layoutfont = layout->font;
 
        // Realize against environment font information
-       if (pit->getDepth()) {
-               ParagraphList::iterator tp = pit;
+       if (pars_[pit].getDepth()) {
+               pit_type tp = pit;
                while (!layoutfont.resolved() &&
-                      tp != ownerParagraphs().end() &&
-                      tp->getDepth()) {
-                       tp = outerHook(tp, ownerParagraphs());
-                       if (tp != ownerParagraphs().end())
-                               layoutfont.realize(tp->layout()->font);
+                      tp != pit_type(paragraphs().size()) &&
+                      pars_[tp].getDepth()) {
+                       tp = outerHook(tp, paragraphs());
+                       if (tp != pit_type(paragraphs().size()))
+                               layoutfont.realize(pars_[tp].layout()->font);
                }
        }
 
-       layoutfont.realize(defaultfont_);
+       // Inside inset, apply the inset's font attributes if any
+       // (charstyle!)
+       if (!isMainText(buffer))
+               layoutfont.realize(font_);
+
+       layoutfont.realize(buffer.params().getFont());
 
        // Now, reduce font against full layout font
        font.reduce(layoutfont);
 
-       pit->setFont(pos, font);
-}
-
-
-InsetOld * LyXText::getInset() const
-{
-       ParagraphList::iterator pit = cursor.par();
-       pos_type const pos = cursor.pos();
-
-       if (pos < pit->size() && pit->isInset(pos)) {
-               return pit->getInset(pos);
-       }
-       return 0;
+       pars_[pit].setFont(pos, font);
 }
 
 
-void LyXText::toggleInset()
+// return past-the-last paragraph influenced by a layout change on pit
+pit_type LyXText::undoSpan(pit_type pit)
 {
-       InsetOld * inset = getInset();
-       // is there an editable inset at cursor position?
-       if (!isEditableInset(inset)) {
-               // No, try to see if we are inside a collapsable inset
-               if (inset_owner && inset_owner->owner()
-                   && inset_owner->owner()->isOpen()) {
-                       bv()->unlockInset(inset_owner->owner());
-                       inset_owner->owner()->close(bv());
-                       bv()->getLyXText()->cursorRight(bv());
-               }
-               return;
+       pit_type end = paragraphs().size();
+       pit_type nextpit = pit + 1;
+       if (nextpit == end)
+               return nextpit;
+       //because of parindents
+       if (!pars_[pit].getDepth())
+               return boost::next(nextpit);
+       //because of depth constrains
+       for (; nextpit != end; ++pit, ++nextpit) {
+               if (!pars_[pit].getDepth())
+                       break;
        }
-       //bv()->owner()->message(inset->editMessage());
-
-       // do we want to keep this?? (JMarc)
-       if (!isHighlyEditableInset(inset))
-               recordUndo(bv(), Undo::ATOMIC);
-
-       if (inset->isOpen())
-               inset->close(bv());
-       else
-               inset->open(bv());
-
-       bv()->updateInset();
+       return nextpit;
 }
 
 
-/* used in setlayout */
-// Asger is not sure we want to do this...
-void LyXText::makeFontEntriesLayoutSpecific(BufferParams const & params,
-                                           Paragraph & par)
+void LyXText::setLayout(Buffer const & buffer, pit_type start, pit_type end,
+               string const & layout)
 {
-       LyXLayout_ptr const & layout = par.layout();
-       pos_type const psize = par.size();
+       BOOST_ASSERT(start != end);
 
-       LyXFont layoutfont;
-       for (pos_type pos = 0; pos < psize; ++pos) {
-               if (pos < par.beginningOfBody())
-                       layoutfont = layout->labelfont;
-               else
-                       layoutfont = layout->font;
+       BufferParams const & bufparams = buffer.params();
+       LyXLayout_ptr const & lyxlayout = bufparams.getLyXTextClass()[layout];
 
-               LyXFont tmpfont = par.getFontSettings(params, pos);
-               tmpfont.reduce(layoutfont);
-               par.setFont(pos, tmpfont);
-       }
-}
-
-
-ParagraphList::iterator
-LyXText::setLayout(LyXCursor & cur, LyXCursor & sstart_cur,
-                  LyXCursor & send_cur,
-                  string const & layout)
-{
-       ParagraphList::iterator endpit = boost::next(send_cur.par());
-       ParagraphList::iterator undoendpit = endpit;
-       ParagraphList::iterator pars_end = ownerParagraphs().end();
-
-       if (endpit != pars_end && endpit->getDepth()) {
-               while (endpit != pars_end && endpit->getDepth()) {
-                       ++endpit;
-                       undoendpit = endpit;
-               }
-       } else if (endpit != pars_end) {
-               // because of parindents etc.
-               ++endpit;
-       }
-
-       recordUndo(bv(), Undo::ATOMIC, sstart_cur.par(), boost::prior(undoendpit));
-
-       // ok we have a selection. This is always between sstart_cur
-       // and sel_end cursor
-       cur = sstart_cur;
-       ParagraphList::iterator pit = sstart_cur.par();
-       ParagraphList::iterator epit = boost::next(send_cur.par());
-
-       LyXLayout_ptr const & lyxlayout =
-               bv()->buffer()->params.getLyXTextClass()[layout];
-
-       do {
-               pit->applyLayout(lyxlayout);
-               makeFontEntriesLayoutSpecific(bv()->buffer()->params, *pit);
-               pit->params().spaceTop(lyxlayout->fill_top ?
-                                        VSpace(VSpace::VFILL)
-                                        : VSpace(VSpace::NONE));
-               pit->params().spaceBottom(lyxlayout->fill_bottom ?
-                                           VSpace(VSpace::VFILL)
-                                           : VSpace(VSpace::NONE));
+       for (pit_type pit = start; pit != end; ++pit) {
+               Paragraph & par = pars_[pit];
+               par.applyLayout(lyxlayout);
                if (lyxlayout->margintype == MARGIN_MANUAL)
-                       pit->setLabelWidthString(lyxlayout->labelstring());
-               cur.par(pit);
-               ++pit;
-       } while (pit != epit);
-
-       return endpit;
+                       par.setLabelWidthString(par.translateIfPossible(
+                               lyxlayout->labelstring(), buffer.params()));
+       }
 }
 
 
 // set layout over selection and make a total rebreak of those paragraphs
-void LyXText::setLayout(string const & layout)
+void LyXText::setLayout(LCursor & cur, string const & layout)
 {
-       LyXCursor tmpcursor = cursor;  // store the current cursor
-
-       // if there is no selection just set the layout
-       // of the current paragraph
-       if (!selection.set()) {
-               selection.start = cursor;  // dummy selection
-               selection.end = cursor;
-       }
-
+       BOOST_ASSERT(this == cur.text());
        // special handling of new environment insets
-       BufferParams const & params = bv()->buffer()->params;
+       BufferView & bv = cur.bv();
+       BufferParams const & params = bv.buffer()->params();
        LyXLayout_ptr const & lyxlayout = params.getLyXTextClass()[layout];
        if (lyxlayout->is_environment) {
                // move everything in a new environment inset
-               lyxerr << "setting layout " << layout << endl;
-               bv()->owner()->dispatch(FuncRequest(LFUN_HOME));
-               bv()->owner()->dispatch(FuncRequest(LFUN_ENDSEL));
-               bv()->owner()->dispatch(FuncRequest(LFUN_CUT));
-               InsetOld * inset = new InsetEnvironment(params, layout);
-               if (bv()->insertInset(inset)) {
-                       //inset->edit(bv());
-                       //bv()->owner()->dispatch(FuncRequest(LFUN_PASTE));
-               }
-               else
-                       delete inset;
+               LYXERR(Debug::DEBUG) << "setting layout " << layout << endl;
+               lyx::dispatch(FuncRequest(LFUN_LINE_BEGIN));
+               lyx::dispatch(FuncRequest(LFUN_LINE_END_SELECT));
+               lyx::dispatch(FuncRequest(LFUN_CUT));
+               InsetBase * inset = new InsetEnvironment(params, layout);
+               insertInset(cur, inset);
+               //inset->edit(cur, true);
+               //lyx::dispatch(FuncRequest(LFUN_PASTE));
                return;
        }
 
-       ParagraphList::iterator endpit = setLayout(cursor, selection.start,
-                                                  selection.end, layout);
-       redoParagraphs(selection.start.par(), endpit);
-
-       // we have to reset the selection, because the
-       // geometry could have changed
-       setCursor(selection.start.par(), selection.start.pos(), false);
-       selection.cursor = cursor;
-       setCursor(selection.end.par(), selection.end.pos(), false);
-       updateCounters();
-       clearSelection();
-       setSelection();
-       setCursor(tmpcursor.par(), tmpcursor.pos(), true);
+       pit_type start = cur.selBegin().pit();
+       pit_type end = cur.selEnd().pit() + 1;
+       pit_type undopit = undoSpan(end - 1);
+       recUndo(cur, start, undopit - 1);
+       setLayout(cur.buffer(), start, end, layout);
+       updateLabels(cur.buffer());
 }
 
 
-bool LyXText::changeDepth(bv_funcs::DEPTH_CHANGE type, bool test_only)
+static bool changeDepthAllowed(LyXText::DEPTH_CHANGE type,
+                       Paragraph const & par, int max_depth)
 {
-       ParagraphList::iterator pit = cursor.par();
-       ParagraphList::iterator end = cursor.par();
-       ParagraphList::iterator start = pit;
-
-       if (selection.set()) {
-               pit = selection.start.par();
-               end = selection.end.par();
-               start = pit;
-       }
+       if (par.layout()->labeltype == LABEL_BIBLIO)
+               return false;
+       int const depth = par.params().depth();
+       if (type == LyXText::INC_DEPTH && depth < max_depth)
+               return true;
+       if (type == LyXText::DEC_DEPTH && depth > 0)
+               return true;
+       return false;
+}
 
-       ParagraphList::iterator pastend = boost::next(end);
 
-       if (!test_only)
-               recordUndo(bv(), Undo::ATOMIC, start, end);
+bool LyXText::changeDepthAllowed(LCursor & cur, DEPTH_CHANGE type) const
+{
+       BOOST_ASSERT(this == cur.text());
+       // this happens when selecting several cells in tabular (bug 2630)
+       if (cur.selBegin().idx() != cur.selEnd().idx())
+               return false;
 
-       bool changed = false;
+       pit_type const beg = cur.selBegin().pit();
+       pit_type const end = cur.selEnd().pit() + 1;
+       int max_depth = (beg != 0 ? pars_[beg - 1].getMaxDepthAfter() : 0);
 
-       int prev_after_depth = 0;
-#warning parlist ... could be nicer ?
-       if (start != ownerParagraphs().begin()) {
-               prev_after_depth = boost::prior(start)->getMaxDepthAfter();
+       for (pit_type pit = beg; pit != end; ++pit) {
+               if (lyx::changeDepthAllowed(type, pars_[pit], max_depth))
+                       return true;
+               max_depth = pars_[pit].getMaxDepthAfter();
        }
+       return false;
+}
 
-       while (true) {
-               int const depth = pit->params().depth();
-               if (type == bv_funcs::INC_DEPTH) {
-                       if (depth < prev_after_depth
-                           && pit->layout()->labeltype != LABEL_BIBLIO) {
-                               changed = true;
-                               if (!test_only)
-                                       pit->params().depth(depth + 1);
-                       }
-               } else if (depth) {
-                       changed = true;
-                       if (!test_only)
-                               pit->params().depth(depth - 1);
-               }
-
-               prev_after_depth = pit->getMaxDepthAfter();
 
-               if (pit == end) {
-                       break;
+void LyXText::changeDepth(LCursor & cur, DEPTH_CHANGE type)
+{
+       BOOST_ASSERT(this == cur.text());
+       pit_type const beg = cur.selBegin().pit();
+       pit_type const end = cur.selEnd().pit() + 1;
+       recordUndoSelection(cur);
+       int max_depth = (beg != 0 ? pars_[beg - 1].getMaxDepthAfter() : 0);
+
+       for (pit_type pit = beg; pit != end; ++pit) {
+               Paragraph & par = pars_[pit];
+               if (lyx::changeDepthAllowed(type, par, max_depth)) {
+                       int const depth = par.params().depth();
+                       if (type == INC_DEPTH)
+                               par.params().depth(depth + 1);
+                       else
+                               par.params().depth(depth - 1);
                }
-
-               ++pit;
+               max_depth = par.getMaxDepthAfter();
        }
-
-       if (test_only)
-               return changed;
-
-       redoParagraphs(start, pastend);
-
-       // We need to actually move the text->cursor. I don't
-       // understand why ...
-       LyXCursor tmpcursor = cursor;
-
-       // we have to reset the visual selection because the
-       // geometry could have changed
-       if (selection.set()) {
-               setCursor(selection.start.par(), selection.start.pos());
-               selection.cursor = cursor;
-               setCursor(selection.end.par(), selection.end.pos());
-       }
-
        // this handles the counter labels, and also fixes up
        // depth values for follow-on (child) paragraphs
-       updateCounters();
-
-       setSelection();
-       setCursor(tmpcursor.par(), tmpcursor.pos());
-
-       return changed;
+       updateLabels(cur.buffer());
 }
 
 
-// set font over selection and make a total rebreak of those paragraphs
-void LyXText::setFont(LyXFont const & font, bool toggleall)
+// set font over selection
+void LyXText::setFont(LCursor & cur, LyXFont const & font, bool toggleall)
 {
+       BOOST_ASSERT(this == cur.text());
        // if there is no selection just set the current_font
-       if (!selection.set()) {
+       if (!cur.selection()) {
                // Determine basis font
                LyXFont layoutfont;
-               if (cursor.pos() < cursor.par()->beginningOfBody()) {
-                       layoutfont = getLabelFont(cursor.par());
-               } else {
-                       layoutfont = getLayoutFont(cursor.par());
-               }
+               pit_type pit = cur.pit();
+               if (cur.pos() < pars_[pit].beginOfBody())
+                       layoutfont = getLabelFont(cur.buffer(), pars_[pit]);
+               else
+                       layoutfont = getLayoutFont(cur.buffer(), pit);
+
                // Update current font
                real_current_font.update(font,
-                                        bv()->buffer()->params.language,
+                                        cur.buffer().params().language,
                                         toggleall);
 
                // Reduce to implicit settings
@@ -506,325 +461,152 @@ void LyXText::setFont(LyXFont const & font, bool toggleall)
                return;
        }
 
-       LyXCursor tmpcursor = cursor; // store the current cursor
-
-       // ok we have a selection. This is always between sel_start_cursor
-       // and sel_end cursor
-
-       recordUndo(bv(), Undo::ATOMIC, selection.start.par(), selection.end.par());
-       freezeUndo();
-       cursor = selection.start;
-       while (cursor.par() != selection.end.par() ||
-              cursor.pos() < selection.end.pos())
-       {
-               if (cursor.pos() < cursor.par()->size()) {
-                       // an open footnote should behave like a closed one
-                       setCharFont(cursor.par(), cursor.pos(),
-                                   font, toggleall);
-                       cursor.pos(cursor.pos() + 1);
-               } else {
-                       cursor.pos(0);
-                       cursor.par(boost::next(cursor.par()));
-               }
-       }
-       unFreezeUndo();
-
-       redoParagraph(selection.start.par());
-
-       // we have to reset the selection, because the
-       // geometry could have changed, but we keep
-       // it for user convenience
-       setCursor(selection.start.par(), selection.start.pos());
-       selection.cursor = cursor;
-       setCursor(selection.end.par(), selection.end.pos());
-       setSelection();
-       setCursor(tmpcursor.par(), tmpcursor.pos(), true,
-                 tmpcursor.boundary());
-}
-
+       // Ok, we have a selection.
+       recordUndoSelection(cur);
 
-// rebreaks all paragraphs between the specified pars
-// This function is needed after SetLayout and SetFont etc.
-void LyXText::redoParagraphs(ParagraphList::iterator start,
-  ParagraphList::iterator end)
-{
-       for ( ; start != end; ++start)
-               redoParagraph(start);
-}
-
-
-void LyXText::redoParagraph(ParagraphList::iterator pit)
-{
-       RowList::iterator rit = pit->rows.begin();
-       RowList::iterator end = pit->rows.end();
-
-       // remove rows of paragraph
-       for ( ; rit != end; ++rit) {
-               if (rit == anchor_row_)
-                       anchor_row_ = endRow();
-               height -= rit->height();
-       }
-       pit->rows.clear();
+       DocIterator dit = cur.selectionBegin();
+       DocIterator ditend = cur.selectionEnd();
 
-       // rebreak the paragraph
-       // insert a new row, starting at position 0
+       BufferParams const & params = cur.buffer().params();
 
-       pos_type z = 0;
-       pit->rows.push_back(Row(z));
-       bool done = false;
-       while (!done) {
-               z = rowBreakPoint(pit, pit->rows.back());
-
-               RowList::iterator tmprow = boost::prior(pit->rows.end());
-
-               if (z >= pit->size())
-                       done = true;
-               else {
-                       ++z;
-                       pit->rows.push_back(Row(z));
+       // Don't use forwardChar here as ditend might have
+       // pos() == lastpos() and forwardChar would miss it.
+       // Can't use forwardPos either as this descends into
+       // nested insets.
+       for (; dit != ditend; dit.forwardPosNoDescend()) {
+               if (dit.pos() != dit.lastpos()) {
+                       LyXFont f = getFont(cur.buffer(), dit.paragraph(), dit.pos());
+                       f.update(font, params.language, toggleall);
+                       setCharFont(cur.buffer(), dit.pit(), dit.pos(), f);
                }
-
-               tmprow->fill(fill(pit, tmprow, workWidth()));
-               setHeightOfRow(pit, tmprow);
        }
-
-       //lyxerr << "redoParagraph: " << pit->rows.size() << " rows\n";
 }
 
 
-void LyXText::fullRebreak()
-{
-       redoParagraphs(ownerParagraphs().begin(), ownerParagraphs().end());
-       redoCursor();
-       selection.cursor = cursor;
-}
-
-
-void LyXText::metrics(MetricsInfo & mi, Dimension & dim)
-{
-       //lyxerr << "LyXText::metrics: width: " << mi.base.textwidth << endl;
-       //Assert(mi.base.textwidth);
-
-       // rebuild row cache
-       width = 0;
-       height = 0;
-
-       anchor_row_ = endRow();
-       anchor_row_offset_ = 0;
-
-       ParagraphList::iterator pit = ownerParagraphs().begin();
-       ParagraphList::iterator end = ownerParagraphs().end();
-
-       for (; pit != end; ++pit) {
-               pit->rows.clear();
-
-               InsetList::iterator ii = pit->insetlist.begin();
-               InsetList::iterator iend = pit->insetlist.end();
-               for (; ii != iend; ++ii) {
-                       Dimension dim;
-                       MetricsInfo m = mi;
-#warning FIXME: pos != 0
-                       m.base.font = getFont(pit, 0);
-                       ii->inset->metrics(m, dim);
-               }
-
-               redoParagraph(pit);
-       }
-
-       // final dimension
-       dim.asc = firstRow()->ascent_of_text();
-       dim.des = height - dim.asc;
-       dim.wid = std::max(mi.base.textwidth, int(width));
-}
-
-
-// important for the screen
-
-
 // the cursor set functions have a special mechanism. When they
-// realize, that you left an empty paragraph, they will delete it.
-// They also delete the corresponding row
-
-// need the selection cursor:
-void LyXText::setSelection()
-{
-       TextCursor::setSelection();
-}
-
-
-
-void LyXText::clearSelection()
-{
-       TextCursor::clearSelection();
+// realize you left an empty paragraph, they will delete it.
 
-       // reset this in the bv_owner!
-       if (bv_owner && bv_owner->text)
-               bv_owner->text->xsel_cache.set(false);
-}
-
-
-void LyXText::cursorHome()
+bool LyXText::cursorHome(LCursor & cur)
 {
-       setCursor(cursor.par(), cursorRow()->pos());
+       BOOST_ASSERT(this == cur.text());
+       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());
 }
 
 
-void LyXText::cursorEnd()
+bool LyXText::cursorEnd(LCursor & cur)
 {
-       if (cursor.par()->empty())
-               return;
-
-       RowList::iterator rit = cursorRow();
-       RowList::iterator next_rit = boost::next(rit);
-       RowList::iterator end = boost::next(rit);
-       ParagraphList::iterator pit = cursor.par();
-       pos_type last_pos = lastPos(*pit, rit);
-
-       if (next_rit == end) {
-               ++last_pos;
-       } else {
-               if (pit->empty() ||
-                   (pit->getChar(last_pos) != ' ' && !pit->isNewline(last_pos))) {
-                       ++last_pos;
-               }
+       BOOST_ASSERT(this == cur.text());
+       // if not on the last row of the par, put the cursor before
+       // the final space exept if I have a spanning inset or one string
+       // is so long that we force a break.
+       pos_type end = cur.textRow().endpos();
+       if (end == 0)
+               // empty text, end-1 is no valid position
+               return false;
+       bool boundary = false;
+       if (end != cur.lastpos()) {
+               if (!cur.paragraph().isLineSeparator(end-1)
+                   && !cur.paragraph().isNewline(end-1))
+                       boundary = true;
+               else
+                       --end;
        }
-
-       setCursor(pit, last_pos);
+       return setCursor(cur, cur.pit(), end, true, boundary);
 }
 
 
-void LyXText::cursorTop()
+bool LyXText::cursorTop(LCursor & cur)
 {
-       setCursor(ownerParagraphs().begin(), 0);
+       BOOST_ASSERT(this == cur.text());
+       return setCursor(cur, 0, 0);
 }
 
 
-void LyXText::cursorBottom()
+bool LyXText::cursorBottom(LCursor & cur)
 {
-       ParagraphList::iterator lastpit =
-               boost::prior(ownerParagraphs().end());
-       setCursor(lastpit, lastpit->size());
+       BOOST_ASSERT(this == cur.text());
+       return setCursor(cur, cur.lastpit(), boost::prior(paragraphs().end())->size());
 }
 
 
-void LyXText::toggleFree(LyXFont const & font, bool toggleall)
+void LyXText::toggleFree(LCursor & cur, LyXFont const & font, bool toggleall)
 {
+       BOOST_ASSERT(this == cur.text());
        // If the mask is completely neutral, tell user
        if (font == LyXFont(LyXFont::ALL_IGNORE)) {
                // Could only happen with user style
-               bv()->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
+               cur.message(_("No font change defined. "
+                                          "Use Character under the Layout menu to define font change."));
                return;
        }
 
        // Try implicit word selection
        // If there is a change in the language the implicit word selection
        // is disabled.
-       LyXCursor resetCursor = cursor;
-       bool implicitSelection = (font.language() == ignore_language
-                                 && font.number() == LyXFont::IGNORE)
-               ? selectWordWhenUnderCursor(lyx::WHOLE_WORD_STRICT) : false;
+       CursorSlice resetCursor = cur.top();
+       bool implicitSelection =
+               font.language() == ignore_language
+               && font.number() == LyXFont::IGNORE
+               && selectWordWhenUnderCursor(cur, WHOLE_WORD_STRICT);
 
        // Set font
-       setFont(font, toggleall);
+       setFont(cur, font, toggleall);
 
        // Implicit selections are cleared afterwards
-       //and cursor is set to the original position.
+       // and cursor is set to the original position.
        if (implicitSelection) {
-               clearSelection();
-               cursor = resetCursor;
-               setCursor(cursor.par(), cursor.pos());
-               selection.cursor = cursor;
+               cur.clearSelection();
+               cur.top() = resetCursor;
+               cur.resetAnchor();
        }
 }
 
 
-string LyXText::getStringToIndex()
+docstring LyXText::getStringToIndex(LCursor const & cur)
 {
-       // Try implicit word selection
-       // If there is a change in the language the implicit word selection
-       // is disabled.
-       LyXCursor const reset_cursor = cursor;
-       bool const implicitSelection =
-               selectWordWhenUnderCursor(lyx::PREVIOUS_WORD);
-
-       string idxstring;
-       if (!selection.set())
-               bv()->owner()->message(_("Nothing to index!"));
-       else if (selection.start.par() != selection.end.par())
-               bv()->owner()->message(_("Cannot index more than one paragraph!"));
-       else
-               idxstring = selectionAsString(bv()->buffer(), false);
-
-       // Reset cursors to their original position.
-       cursor = reset_cursor;
-       setCursor(cursor.par(), cursor.pos());
-       selection.cursor = cursor;
-
-       // Clear the implicit selection.
-       if (implicitSelection)
-               clearSelection();
+       BOOST_ASSERT(this == cur.text());
+
+       docstring idxstring;
+       if (cur.selection())
+               idxstring = cur.selectionAsString(false);
+       else {
+               // Try implicit word selection. If there is a change
+               // in the language the implicit word selection is
+               // disabled.
+               LCursor tmpcur = cur;
+               selectWord(tmpcur, PREVIOUS_WORD);
+
+               if (!tmpcur.selection())
+                       cur.message(_("Nothing to index!"));
+               else if (tmpcur.selBegin().pit() != tmpcur.selEnd().pit())
+                       cur.message(_("Cannot index more than one paragraph!"));
+               else
+                       idxstring = tmpcur.selectionAsString(false);
+       }
 
        return idxstring;
 }
 
 
-// the DTP switches for paragraphs. LyX will store them in the first
-// physical paragraph. When a paragraph is broken, the top settings rest,
-// the bottom settings are given to the new one. So I can make sure,
-// they do not duplicate themself and you cannnot make dirty things with
-// them!
-
-void LyXText::setParagraph(bool line_top, bool line_bottom,
-                          bool pagebreak_top, bool pagebreak_bottom,
-                          VSpace const & space_top,
-                          VSpace const & space_bottom,
-                          Spacing const & spacing,
-                          LyXAlignment align,
-                          string const & labelwidthstring,
-                          bool noindent)
+void LyXText::setParagraph(LCursor & cur,
+                          Spacing const & spacing, LyXAlignment align,
+                          docstring const & labelwidthstring, bool noindent)
 {
-       LyXCursor tmpcursor = cursor;
-       if (!selection.set()) {
-               selection.start = cursor;
-               selection.end = cursor;
-       }
-
+       BOOST_ASSERT(cur.text());
        // make sure that the depth behind the selection are restored, too
-       ParagraphList::iterator endpit = boost::next(selection.end.par());
-       ParagraphList::iterator undoendpit = endpit;
-       ParagraphList::iterator pars_end = ownerParagraphs().end();
-
-       if (endpit != pars_end && endpit->getDepth()) {
-               while (endpit != pars_end && endpit->getDepth()) {
-                       ++endpit;
-                       undoendpit = endpit;
-               }
-       } else if (endpit != pars_end) {
-               // because of parindents etc.
-               ++endpit;
-       }
-
-       recordUndo(bv(), Undo::ATOMIC, selection.start.par(),
-               boost::prior(undoendpit));
-
-
-       ParagraphList::iterator tmppit = selection.end.par();
-
-       while (tmppit != boost::prior(selection.start.par())) {
-               setCursor(tmppit, 0);
-
-               ParagraphList::iterator pit = cursor.par();
-               ParagraphParameters & params = pit->params();
+       pit_type undopit = undoSpan(cur.selEnd().pit());
+       recUndo(cur, cur.selBegin().pit(), undopit - 1);
 
-               params.lineTop(line_top);
-               params.lineBottom(line_bottom);
-               params.pagebreakTop(pagebreak_top);
-               params.pagebreakBottom(pagebreak_bottom);
-               params.spaceTop(space_top);
-               params.spaceBottom(space_bottom);
+       for (pit_type pit = cur.selBegin().pit(), end = cur.selEnd().pit();
+            pit <= end; ++pit) {
+               Paragraph & par = pars_[pit];
+               ParagraphParameters & params = par.params();
                params.spacing(spacing);
+
                // does the layout allow the new alignment?
-               LyXLayout_ptr const & layout = pit->layout();
+               LyXLayout_ptr const & layout = par.layout();
 
                if (align == LYX_ALIGN_LAYOUT)
                        align = layout->align;
@@ -834,484 +616,43 @@ void LyXText::setParagraph(bool line_top, bool line_bottom,
                        else
                                params.align(align);
                }
-               pit->setLabelWidthString(labelwidthstring);
+               par.setLabelWidthString(labelwidthstring);
                params.noindent(noindent);
-               tmppit = boost::prior(pit);
-       }
-
-       redoParagraphs(selection.start.par(), endpit);
-
-       clearSelection();
-       setCursor(selection.start.par(), selection.start.pos());
-       selection.cursor = cursor;
-       setCursor(selection.end.par(), selection.end.pos());
-       setSelection();
-       setCursor(tmpcursor.par(), tmpcursor.pos());
-       if (inset_owner)
-               bv()->updateInset();
-}
-
-
-// set the counter of a paragraph. This includes the labels
-void LyXText::setCounter(Buffer const * buf, ParagraphList::iterator pit)
-{
-       LyXTextClass const & textclass = buf->params.getLyXTextClass();
-       LyXLayout_ptr const & layout = pit->layout();
-
-       if (pit != ownerParagraphs().begin()) {
-
-               pit->params().appendix(boost::prior(pit)->params().appendix());
-               if (!pit->params().appendix() &&
-                   pit->params().startOfAppendix()) {
-                       pit->params().appendix(true);
-                       textclass.counters().reset();
-               }
-               pit->enumdepth = boost::prior(pit)->enumdepth;
-               pit->itemdepth = boost::prior(pit)->itemdepth;
-       } else {
-               pit->params().appendix(pit->params().startOfAppendix());
-               pit->enumdepth = 0;
-               pit->itemdepth = 0;
-       }
-
-       // Maybe we have to increment the enumeration depth.
-       // BUT, enumeration in a footnote is considered in isolation from its
-       //      surrounding paragraph so don't increment if this is the
-       //      first line of the footnote
-       // AND, bibliographies can't have their depth changed ie. they
-       //      are always of depth 0
-       if (pit != ownerParagraphs().begin()
-           && boost::prior(pit)->getDepth() < pit->getDepth()
-           && boost::prior(pit)->layout()->labeltype == LABEL_COUNTER_ENUMI
-           && pit->enumdepth < 3
-           && layout->labeltype != LABEL_BIBLIO) {
-               pit->enumdepth++;
-       }
-
-       // Maybe we have to decrement the enumeration depth, see note above
-       if (pit != ownerParagraphs().begin()
-           && boost::prior(pit)->getDepth() > pit->getDepth()
-           && layout->labeltype != LABEL_BIBLIO) {
-               pit->enumdepth = depthHook(pit, ownerParagraphs(),
-                                          pit->getDepth())->enumdepth;
-       }
-
-       if (!pit->params().labelString().empty()) {
-               pit->params().labelString(string());
-       }
-
-       if (layout->margintype == MARGIN_MANUAL) {
-               if (pit->params().labelWidthString().empty())
-                       pit->setLabelWidthString(layout->labelstring());
-       } else {
-               pit->setLabelWidthString(string());
-       }
-
-       // is it a layout that has an automatic label?
-       if (layout->labeltype >= LABEL_COUNTER_CHAPTER) {
-               int const i = layout->labeltype - LABEL_COUNTER_CHAPTER;
-
-               ostringstream s;
-
-               if (i >= 0 && i <= buf->params.secnumdepth) {
-                       string numbertype;
-                       string langtype;
-
-                       textclass.counters().step(layout->latexname());
-
-                       // Is there a label? Useful for Chapter layout
-                       if (!pit->params().appendix()) {
-                               s << buf->B_(layout->labelstring());
-                       } else {
-                               s << buf->B_(layout->labelstring_appendix());
-                       }
-
-                       // Use of an integer is here less than elegant. For now.
-                       int head = textclass.maxcounter() - LABEL_COUNTER_CHAPTER;
-                       if (!pit->params().appendix()) {
-                               numbertype = "sectioning";
-                       } else {
-                               numbertype = "appendix";
-                               if (pit->isRightToLeftPar(buf->params))
-                                       langtype = "hebrew";
-                               else
-                                       langtype = "latin";
-                       }
-
-                       s << " "
-                         << textclass.counters()
-                               .numberLabel(layout->latexname(),
-                                            numbertype, langtype, head);
-
-                       pit->params().labelString(STRCONV(s.str()));
-
-                       // reset enum counters
-                       textclass.counters().reset("enum");
-               } else if (layout->labeltype < LABEL_COUNTER_ENUMI) {
-                       textclass.counters().reset("enum");
-               } else if (layout->labeltype == LABEL_COUNTER_ENUMI) {
-                       // FIXME
-                       // Yes I know this is a really, really! bad solution
-                       // (Lgb)
-                       string enumcounter("enum");
-
-                       switch (pit->enumdepth) {
-                       case 2:
-                               enumcounter += 'i';
-                       case 1:
-                               enumcounter += 'i';
-                       case 0:
-                               enumcounter += 'i';
-                               break;
-                       case 3:
-                               enumcounter += "iv";
-                               break;
-                       default:
-                               // not a valid enumdepth...
-                               break;
-                       }
-
-                       textclass.counters().step(enumcounter);
-
-                       s << textclass.counters()
-                               .numberLabel(enumcounter, "enumeration");
-                       pit->params().labelString(STRCONV(s.str()));
-               }
-       } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
-               textclass.counters().step("bibitem");
-               int number = textclass.counters().value("bibitem");
-               if (pit->bibitem()) {
-                       pit->bibitem()->setCounter(number);
-                       pit->params().labelString(layout->labelstring());
-               }
-               // In biblio should't be following counters but...
-       } else {
-               string s = buf->B_(layout->labelstring());
-
-               // the caption hack:
-               if (layout->labeltype == LABEL_SENSITIVE) {
-                       ParagraphList::iterator end = ownerParagraphs().end();
-                       ParagraphList::iterator tmppit = pit;
-                       InsetOld * in = 0;
-                       bool isOK = false;
-                       while (tmppit != end && tmppit->inInset()
-                              // the single '=' is intended below
-                              && (in = tmppit->inInset()->owner()))
-                       {
-                               if (in->lyxCode() == InsetOld::FLOAT_CODE ||
-                                   in->lyxCode() == InsetOld::WRAP_CODE) {
-                                       isOK = true;
-                                       break;
-                               } else {
-                                       tmppit = ownerParagraphs().begin();
-                                       for ( ; tmppit != end; ++tmppit)
-                                               if (&*tmppit == in->parOwner())
-                                                       break;
-                               }
-                       }
-
-                       if (isOK) {
-                               string type;
-
-                               if (in->lyxCode() == InsetOld::FLOAT_CODE)
-                                       type = static_cast<InsetFloat*>(in)->params().type;
-                               else if (in->lyxCode() == InsetOld::WRAP_CODE)
-                                       type = static_cast<InsetWrap*>(in)->params().type;
-                               else
-                                       Assert(0);
-
-                               Floating const & fl = textclass.floats().getType(type);
-
-                               textclass.counters().step(fl.type());
-
-                               // Doesn't work... yet.
-                               s = bformat(_("%1$s #:"), buf->B_(fl.name()));
-                       } else {
-                               // par->SetLayout(0);
-                               // s = layout->labelstring;
-                               s = _("Senseless: ");
-                       }
-               }
-               pit->params().labelString(s);
-
-               // reset the enumeration counter. They are always reset
-               // when there is any other layout between
-               // Just fall-through between the cases so that all
-               // enum counters deeper than enumdepth is also reset.
-               switch (pit->enumdepth) {
-               case 0:
-                       textclass.counters().reset("enumi");
-               case 1:
-                       textclass.counters().reset("enumii");
-               case 2:
-                       textclass.counters().reset("enumiii");
-               case 3:
-                       textclass.counters().reset("enumiv");
-               }
-       }
-}
-
-
-// Updates all counters. Paragraphs with changed label string will be rebroken
-void LyXText::updateCounters()
-{
-       // start over
-       bv()->buffer()->params.getLyXTextClass().counters().reset();
-
-       ParagraphList::iterator beg = ownerParagraphs().begin();
-       ParagraphList::iterator end = ownerParagraphs().end();
-       for (ParagraphList::iterator pit = beg; pit != end; ++pit) {
-               string const oldLabel = pit->params().labelString();
-
-               size_t maxdepth = 0;
-               if (pit != beg)
-                       maxdepth = boost::prior(pit)->getMaxDepthAfter();
-
-               if (pit->params().depth() > maxdepth)
-                       pit->params().depth(maxdepth);
-
-               // setCounter can potentially change the labelString.
-               setCounter(bv()->buffer(), pit);
-
-               string const & newLabel = pit->params().labelString();
-
-               if (oldLabel != newLabel)
-                       redoParagraph(pit);
        }
 }
 
 
-void LyXText::insertInset(InsetOld * inset)
+// this really should just insert the inset and not move the cursor.
+void LyXText::insertInset(LCursor & cur, InsetBase * inset)
 {
-       if (!cursor.par()->insetAllowed(inset->lyxCode()))
-               return;
-       recordUndo(bv(), Undo::ATOMIC, cursor.par());
-       freezeUndo();
-       cursor.par()->insertInset(cursor.pos(), inset);
-       // Just to rebreak and refresh correctly.
-       // The character will not be inserted a second time
-       insertChar(Paragraph::META_INSET);
-       // If we enter a highly editable inset the cursor should be before
-       // the inset. After an Undo LyX tries to call inset->edit(...) 
-       // and fails if the cursor is behind the inset and getInset
-       // does not return the inset!
-       if (isHighlyEditableInset(inset))
-               cursorLeft(true);
-       unFreezeUndo();
-}
-
-
-void LyXText::cutSelection(bool doclear, bool realcut)
-{
-       // Stuff what we got on the clipboard. Even if there is no selection.
-
-       // There is a problem with having the stuffing here in that the
-       // larger the selection the slower LyX will get. This can be
-       // solved by running the line below only when the selection has
-       // finished. The solution used currently just works, to make it
-       // faster we need to be more clever and probably also have more
-       // calls to stuffClipboard. (Lgb)
-       bv()->stuffClipboard(selectionAsString(bv()->buffer(), true));
-
-       // This doesn't make sense, if there is no selection
-       if (!selection.set())
-               return;
-
-       // OK, we have a selection. This is always between selection.start
-       // and selection.end
-
-       // make sure that the depth behind the selection are restored, too
-       ParagraphList::iterator endpit = boost::next(selection.end.par());
-       ParagraphList::iterator undoendpit = endpit;
-       ParagraphList::iterator pars_end = ownerParagraphs().end();
-
-       if (endpit != pars_end && endpit->getDepth()) {
-               while (endpit != pars_end && endpit->getDepth()) {
-                       ++endpit;
-                       undoendpit = endpit;
-               }
-       } else if (endpit != pars_end) {
-               // because of parindents etc.
-               ++endpit;
-       }
-
-       recordUndo(bv(), Undo::DELETE, selection.start.par(),
-                  boost::prior(undoendpit));
-
-       endpit = selection.end.par();
-       int endpos = selection.end.pos();
-
-       boost::tie(endpit, endpos) = realcut ?
-               CutAndPaste::cutSelection(bv()->buffer()->params,
-                                         ownerParagraphs(),
-                                         selection.start.par(), endpit,
-                                         selection.start.pos(), endpos,
-                                         bv()->buffer()->params.textclass,
-                                         doclear)
-               : CutAndPaste::eraseSelection(bv()->buffer()->params,
-                                             ownerParagraphs(),
-                                             selection.start.par(), endpit,
-                                             selection.start.pos(), endpos,
-                                             doclear);
-       // sometimes necessary
-       if (doclear)
-               selection.start.par()->stripLeadingSpaces();
-
-       redoParagraphs(selection.start.par(), boost::next(endpit));
-       // cutSelection can invalidate the cursor so we need to set
-       // it anew. (Lgb)
-       // we prefer the end for when tracking changes
-       cursor.pos(endpos);
-       cursor.par(endpit);
-
-       // need a valid cursor. (Lgb)
-       clearSelection();
-
-       setCursor(cursor.par(), cursor.pos());
-       selection.cursor = cursor;
-       updateCounters();
-}
-
-
-void LyXText::copySelection()
-{
-       // stuff the selection onto the X clipboard, from an explicit copy request
-       bv()->stuffClipboard(selectionAsString(bv()->buffer(), true));
-
-       // this doesnt make sense, if there is no selection
-       if (!selection.set())
-               return;
-
-       // ok we have a selection. This is always between selection.start
-       // and sel_end cursor
-
-       // copy behind a space if there is one
-       while (selection.start.par()->size() > selection.start.pos()
-              && selection.start.par()->isLineSeparator(selection.start.pos())
-              && (selection.start.par() != selection.end.par()
-                  || selection.start.pos() < selection.end.pos()))
-               selection.start.pos(selection.start.pos() + 1);
-
-       CutAndPaste::copySelection(selection.start.par(),
-                                  selection.end.par(),
-                                  selection.start.pos(), selection.end.pos(),
-                                  bv()->buffer()->params.textclass);
-}
-
-
-void LyXText::pasteSelection(size_t sel_index)
-{
-       // this does not make sense, if there is nothing to paste
-       if (!CutAndPaste::checkPastePossible())
-               return;
-
-       recordUndo(bv(), Undo::INSERT, cursor.par());
-
-       ParagraphList::iterator endpit;
-       PitPosPair ppp;
-
-       ErrorList el;
-
-       boost::tie(ppp, endpit) =
-               CutAndPaste::pasteSelection(*bv()->buffer(),
-                                           ownerParagraphs(),
-                                           cursor.par(), cursor.pos(),
-                                           bv()->buffer()->params.textclass,
-                                           sel_index, el);
-       bufferErrors(*bv()->buffer(), el);
-       bv()->showErrorList(_("Paste"));
-
-       redoParagraphs(cursor.par(), endpit);
-
-       setCursor(cursor.par(), cursor.pos());
-       clearSelection();
-
-       selection.cursor = cursor;
-       setCursor(ppp.first, ppp.second);
-       setSelection();
-       updateCounters();
-}
-
-
-void LyXText::setSelectionRange(lyx::pos_type length)
-{
-       if (!length)
-               return;
-
-       selection.cursor = cursor;
-       while (length--)
-               cursorRight(bv());
-       setSelection();
-}
-
-
-// simple replacing. The font of the first selected character is used
-void LyXText::replaceSelectionWithString(string const & str)
-{
-       recordUndo(bv(), Undo::ATOMIC);
-       freezeUndo();
-
-       if (!selection.set()) { // create a dummy selection
-               selection.end = cursor;
-               selection.start = cursor;
-       }
-
-       // Get font setting before we cut
-       pos_type pos = selection.end.pos();
-       LyXFont const font = selection.start.par()
-               ->getFontSettings(bv()->buffer()->params,
-                                 selection.start.pos());
-
-       // Insert the new string
-       string::const_iterator cit = str.begin();
-       string::const_iterator end = str.end();
-       for (; cit != end; ++cit) {
-               selection.end.par()->insertChar(pos, (*cit), font);
-               ++pos;
-       }
-
-       // Cut the selection
-       cutSelection(true, false);
-
-       unFreezeUndo();
+       BOOST_ASSERT(this == cur.text());
+       BOOST_ASSERT(inset);
+       cur.paragraph().insertInset(cur.pos(), inset, 
+                                   Change(cur.buffer().params().trackChanges ?
+                                          Change::INSERTED : Change::UNCHANGED));
 }
 
 
 // needed to insert the selection
-void LyXText::insertStringAsLines(string const & str)
+void LyXText::insertStringAsLines(LCursor & cur, docstring const & str)
 {
-       ParagraphList::iterator pit = cursor.par();
-       pos_type pos = cursor.pos();
-       ParagraphList::iterator endpit = boost::next(cursor.par());
-
-       recordUndo(bv(), Undo::ATOMIC);
-
-       // only to be sure, should not be neccessary
-       clearSelection();
-
-       bv()->buffer()->insertStringAsLines(pit, pos, current_font, str);
-
-       redoParagraphs(cursor.par(), endpit);
-       setCursor(cursor.par(), cursor.pos());
-       selection.cursor = cursor;
-       setCursor(pit, pos);
-       setSelection();
+       cur.buffer().insertStringAsLines(pars_, cur.pit(), cur.pos(),
+                                        current_font, str, autoBreakRows_);
 }
 
 
-// turns double-CR to single CR, others where converted into one
-// blank. Then InsertStringAsLines is called
-void LyXText::insertStringAsParagraphs(string const & str)
+// turn double CR to single CR, others are converted into one
+// blank. Then insertStringAsLines is called
+void LyXText::insertStringAsParagraphs(LCursor & cur, docstring const & str)
 {
-       string linestr(str);
+       docstring linestr = str;
        bool newline_inserted = false;
-       string::size_type const siz = linestr.length();
 
-       for (string::size_type i = 0; i < siz; ++i) {
+       for (string::size_type i = 0, siz = linestr.size(); i < siz; ++i) {
                if (linestr[i] == '\n') {
                        if (newline_inserted) {
                                // we know that \r will be ignored by
-                               // InsertStringA. Of course, it is a dirty
+                               // insertStringAsLines. Of course, it is a dirty
                                // trick, but it works...
                                linestr[i - 1] = '\r';
                                linestr[i] = '\n';
@@ -1319,206 +660,89 @@ void LyXText::insertStringAsParagraphs(string const & str)
                                linestr[i] = ' ';
                                newline_inserted = true;
                        }
-               } else if (IsPrintable(linestr[i])) {
+               } else if (isPrintable(linestr[i])) {
                        newline_inserted = false;
                }
        }
-       insertStringAsLines(linestr);
+       insertStringAsLines(cur, linestr);
 }
 
 
-bool LyXText::setCursor(ParagraphList::iterator pit,
-                       pos_type pos,
+bool LyXText::setCursor(LCursor & cur, pit_type par, pos_type pos,
                        bool setfont, bool boundary)
 {
-       LyXCursor old_cursor = cursor;
-       setCursorIntern(pit, pos, setfont, boundary);
-       return deleteEmptyParagraphMechanism(old_cursor);
+       LCursor old = cur;
+       setCursorIntern(cur, par, pos, setfont, boundary);
+       return cur.bv().checkDepm(cur, old);
 }
 
 
-void LyXText::redoCursor()
+void LyXText::setCursor(CursorSlice & cur, pit_type par, pos_type pos)
 {
-#warning maybe the same for selections?
-       setCursor(cursor, cursor.par(), cursor.pos(), cursor.boundary());
-}
-
+       BOOST_ASSERT(par != int(paragraphs().size()));
+       cur.pit() = par;
+       cur.pos() = pos;
 
-void LyXText::setCursor(LyXCursor & cur, ParagraphList::iterator pit,
-                       pos_type pos, bool boundary)
-{
-       Assert(pit != ownerParagraphs().end());
-
-       cur.par(pit);
-       cur.pos(pos);
-       cur.boundary(boundary);
-       if (noRows())
-               return;
-
-       // get the cursor y position in text
-       int y = 0;
-       RowList::iterator row = getRow(pit, pos, y);
-       RowList::iterator old_row = row;
-       // if we are before the first char of this row and are still in the
-       // same paragraph and there is a previous row then put the cursor on
-       // the end of the previous row
-       cur.iy(y + row->baseline());
-       if (row != pit->rows.begin()
-           && pos
-           && pos < pit->size()
-           && pit->getChar(pos) == Paragraph::META_INSET) {
-               InsetOld * ins = pit->getInset(pos);
-               if (ins && (ins->needFullRow() || ins->display())) {
-                       --row;
-                       y -= row->height();
-               }
-       }
-
-       // y is now the beginning of the cursor row
-       y += row->baseline();
-       // y is now the cursor baseline
-       cur.y(y);
-
-       pos_type last = lastPrintablePos(*pit, old_row);
+       // now some strict checking
+       Paragraph & para = getPar(par);
 
        // None of these should happen, but we're scaredy-cats
-       if (pos > pit->size()) {
-               lyxerr << "dont like 1, pos: " << pos << " size: " << pit->size() << endl;
-               pos = 0;
-               cur.pos(0);
-       } else if (pos > last + 1) {
-               lyxerr << "dont like 2 please report" << endl;
-               // This shouldn't happen.
-               pos = last + 1;
-               cur.pos(pos);
-       } else if (pos < row->pos()) {
-               lyxerr << "dont like 3 please report" << endl;
-               pos = row->pos();
-               cur.pos(pos);
+       if (pos < 0) {
+               lyxerr << "dont like -1" << endl;
+               BOOST_ASSERT(false);
        }
 
-       // now get the cursors x position
-       float x = getCursorX(pit, row, pos, last, boundary);
-       cur.x(int(x));
-       cur.x_fix(cur.x());
-       if (old_row != row) {
-               x = getCursorX(pit, old_row, pos, last, boundary);
-               cur.ix(int(x));
-       } else
-               cur.ix(cur.x());
-/* We take out this for the time being because 1) the redraw code is not
-   prepared to this yet and 2) because some good policy has yet to be decided
-   while editting: for instance how to act on rows being created/deleted
-   because of DEPM.
-*/
-#if 0
-       //if the cursor is in a visible row, anchor to it
-       int topy = top_y();
-       if (topy < y && y < topy + bv()->workHeight())
-               anchor_row(row);
-#endif
-}
-
-
-float LyXText::getCursorX(ParagraphList::iterator pit, RowList::iterator rit,
-                         pos_type pos, pos_type last, bool boundary) const
-{
-       pos_type cursor_vpos = 0;
-       double x;
-       double fill_separator;
-       double fill_hfill;
-       double fill_label_hfill;
-       // This call HAS to be here because of the BidiTables!!!
-       prepareToPrint(pit, rit, x, fill_separator, fill_hfill,
-                      fill_label_hfill);
-
-       pos_type const rit_pos = rit->pos();
-
-       if (last < rit_pos)
-               cursor_vpos = rit_pos;
-       else if (pos > last && !boundary)
-               cursor_vpos = (pit->isRightToLeftPar(bv()->buffer()->params))
-                       ? rit_pos : last + 1;
-       else if (pos > rit_pos && (pos > last || boundary))
-               // Place cursor after char at (logical) position pos - 1
-               cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
-                       ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
-       else
-               // Place cursor before char at (logical) position pos
-               cursor_vpos = (bidi_level(pos) % 2 == 0)
-                       ? log2vis(pos) : log2vis(pos) + 1;
-
-       pos_type body_pos = pit->beginningOfBody();
-       if (body_pos > 0 &&
-           (body_pos - 1 > last || !pit->isLineSeparator(body_pos - 1)))
-               body_pos = 0;
-
-       for (pos_type vpos = rit_pos; vpos < cursor_vpos; ++vpos) {
-               pos_type pos = vis2log(vpos);
-               if (body_pos > 0 && pos == body_pos - 1) {
-                       x += fill_label_hfill +
-                               font_metrics::width(
-                                       pit->layout()->labelsep, getLabelFont(pit));
-                       if (pit->isLineSeparator(body_pos - 1))
-                               x -= singleWidth(pit, body_pos - 1);
-               }
-
-               if (hfillExpansion(*pit, rit, pos)) {
-                       x += singleWidth(pit, pos);
-                       if (pos >= body_pos)
-                               x += fill_hfill;
-                       else
-                               x += fill_label_hfill;
-               } else if (pit->isSeparator(pos)) {
-                       x += singleWidth(pit, pos);
-                       if (pos >= body_pos)
-                               x += fill_separator;
-               } else
-                       x += singleWidth(pit, pos);
+       if (pos > para.size()) {
+               lyxerr << "dont like 1, pos: " << pos
+                      << " size: " << para.size()
+                      << " par: " << par << endl;
+               BOOST_ASSERT(false);
        }
-       return x;
 }
 
 
-void LyXText::setCursorIntern(ParagraphList::iterator pit,
-                             pos_type pos, bool setfont, bool boundary)
+void LyXText::setCursorIntern(LCursor & cur,
+                             pit_type par, pos_type pos, bool setfont, bool boundary)
 {
-       setCursor(cursor, pit, pos, boundary);
+       BOOST_ASSERT(this == cur.text());
+       cur.boundary(boundary);
+       setCursor(cur.top(), par, pos);
        if (setfont)
-               setCurrentFont();
+               setCurrentFont(cur);
 }
 
 
-void LyXText::setCurrentFont()
+void LyXText::setCurrentFont(LCursor & cur)
 {
-       pos_type pos = cursor.pos();
-       ParagraphList::iterator pit = cursor.par();
+       BOOST_ASSERT(this == cur.text());
+       pos_type pos = cur.pos();
+       Paragraph & par = cur.paragraph();
 
-       if (cursor.boundary() && pos > 0)
+       if (cur.boundary() && pos > 0)
                --pos;
 
        if (pos > 0) {
-               if (pos == pit->size())
+               if (pos == cur.lastpos())
                        --pos;
                else // potentional bug... BUG (Lgb)
-                       if (pit->isSeparator(pos)) {
-                               if (pos > cursorRow()->pos() &&
-                                   bidi_level(pos) % 2 ==
-                                   bidi_level(pos - 1) % 2)
+                       if (par.isSeparator(pos)) {
+                               if (pos > cur.textRow().pos() &&
+                                   bidi.level(pos) % 2 ==
+                                   bidi.level(pos - 1) % 2)
                                        --pos;
-                               else if (pos + 1 < pit->size())
+                               else if (pos + 1 < cur.lastpos())
                                        ++pos;
                        }
        }
 
-       current_font = pit->getFontSettings(bv()->buffer()->params, pos);
-       real_current_font = getFont(pit, pos);
+       BufferParams const & bufparams = cur.buffer().params();
+       current_font = par.getFontSettings(bufparams, pos);
+       real_current_font = getFont(cur.buffer(), par, pos);
 
-       if (cursor.pos() == pit->size() &&
-           isBoundary(bv()->buffer(), *pit, cursor.pos()) &&
-           !cursor.boundary()) {
-               Language const * lang =
-                       pit->getParLanguage(bv()->buffer()->params);
+       if (cur.pos() == cur.lastpos()
+           && bidi.isBoundary(cur.buffer(), par, cur.pos())
+           && !cur.boundary()) {
+               Language const * lang = par.getParLanguage(bufparams);
                current_font.setLanguage(lang);
                current_font.setNumber(LyXFont::OFF);
                real_current_font.setLanguage(lang);
@@ -1526,450 +750,550 @@ void LyXText::setCurrentFont()
        }
 }
 
-
-// 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(ParagraphList::iterator pit,
-       RowList::iterator rit, int & x, bool & boundary) const
+// y is screen coordinate
+pit_type LyXText::getPitNearY(BufferView & bv, int y) const
 {
-       double tmpx = 0;
-       double fill_separator;
-       double fill_hfill;
-       double fill_label_hfill;
-
-       prepareToPrint(pit, rit, tmpx, fill_separator, fill_hfill, fill_label_hfill);
-
-       pos_type vc = rit->pos();
-       pos_type last = lastPrintablePos(*pit, rit);
-       pos_type c = 0;
-       LyXLayout_ptr const & layout = pit->layout();
-
-       bool left_side = false;
-
-       pos_type body_pos = pit->beginningOfBody();
-       double last_tmpx = tmpx;
-
-       if (body_pos > 0 &&
-           (body_pos - 1 > last ||
-            !pit->isLineSeparator(body_pos - 1)))
-               body_pos = 0;
-
-       // check for empty row
-       if (!pit->size()) {
-               x = int(tmpx);
-               return 0;
+       BOOST_ASSERT(!paragraphs().empty());
+       BOOST_ASSERT(bv.coordCache().getParPos().find(this) != bv.coordCache().getParPos().end());
+       CoordCache::InnerParPosCache const & cc = bv.coordCache().getParPos().find(this)->second;
+       LYXERR(Debug::DEBUG)
+               << BOOST_CURRENT_FUNCTION
+               << ": y: " << y << " cache size: " << cc.size()
+               << endl;
+
+       // look for highest numbered paragraph with y coordinate less than given y
+       pit_type pit = 0;
+       int yy = -1;
+       CoordCache::InnerParPosCache::const_iterator it = cc.begin();
+       CoordCache::InnerParPosCache::const_iterator et = cc.end();
+       CoordCache::InnerParPosCache::const_iterator last = et; last--;
+
+       TextMetrics & tm = bv.textMetrics(this);
+       ParagraphMetrics const & pm = tm.parMetrics(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(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.
+               tm.redoParagraph(pit);
+               bv.coordCache().parPos()[this][pit] =
+                       Point(0, it->second.y_ - pm.descent());
+               return pit;
        }
 
-       while (vc <= last && tmpx <= x) {
-               c = vis2log(vc);
-               last_tmpx = tmpx;
-               if (body_pos > 0 && c == body_pos - 1) {
-                       tmpx += fill_label_hfill +
-                               font_metrics::width(layout->labelsep, getLabelFont(pit));
-                       if (pit->isLineSeparator(body_pos - 1))
-                               tmpx -= singleWidth(pit, body_pos - 1);
-               }
-
-               if (hfillExpansion(*pit, rit, c)) {
-                       tmpx += singleWidth(pit, c);
-                       if (c >= body_pos)
-                               tmpx += fill_hfill;
-                       else
-                               tmpx += fill_label_hfill;
-               } else if (pit->isSeparator(c)) {
-                       tmpx += singleWidth(pit, c);
-                       if (c >= body_pos)
-                               tmpx += fill_separator;
-               } else {
-                       tmpx += singleWidth(pit, c);
-               }
-               ++vc;
+       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(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.
+               tm.redoParagraph(pit);
+               bv.coordCache().parPos()[this][pit] =
+                       Point(0, last->second.y_ + pm_last.ascent());
+               return pit;
        }
 
-       if ((tmpx + last_tmpx) / 2 > x) {
-               tmpx = last_tmpx;
-               left_side = true;
-       }
+       for (; it != et; ++it) {
+               LYXERR(Debug::DEBUG)
+                       << BOOST_CURRENT_FUNCTION
+                       << "  examining: pit: " << it->first
+                       << " y: " << it->second.y_
+                       << endl;
+
+               ParagraphMetrics const & pm = bv.parMetrics(this, it->first);
 
-       if (vc > last + 1)  // This shouldn't happen.
-               vc = last + 1;
-
-       boundary = false;
-       // This (rtl_support test) is not needed, but gives
-       // some speedup if rtl_support == false
-       bool const lastrow = lyxrc.rtl_support
-                       && boost::next(rit) == pit->rows.end();
-
-       // If lastrow is false, we don't need to compute
-       // the value of rtl.
-       bool const rtl = (lastrow)
-               ? pit->isRightToLeftPar(bv()->buffer()->params)
-               : false;
-       if (lastrow &&
-                ((rtl  &&  left_side && vc == rit->pos() && x < tmpx - 5) ||
-                 (!rtl && !left_side && vc == last + 1   && x > tmpx + 5)))
-               c = last + 1;
-       else if (vc == rit->pos()) {
-               c = vis2log(vc);
-               if (bidi_level(c) % 2 == 1)
-                       ++c;
-       } else {
-               c = vis2log(vc - 1);
-               bool const rtl = (bidi_level(c) % 2 == 1);
-               if (left_side == rtl) {
-                       ++c;
-                       boundary = isBoundary(bv()->buffer(), *pit, c);
+               if (it->first >= pit && int(it->second.y_) - int(pm.ascent()) <= y) {
+                       pit = it->first;
+                       yy = it->second.y_;
                }
        }
 
-       if (rit->pos() <= last && c > last && pit->isNewline(last)) {
-               if (bidi_level(last) % 2 == 0)
-                       tmpx -= singleWidth(pit, last);
-               else
-                       tmpx += singleWidth(pit, last);
-               c = last;
-       }
+       LYXERR(Debug::DEBUG)
+               << BOOST_CURRENT_FUNCTION
+               << ": found best y: " << yy << " for pit: " << pit
+               << endl;
 
-       c -= rit->pos();
-       x = int(tmpx);
-       return c;
+       return pit;
 }
 
 
-void LyXText::setCursorFromCoordinates(int x, int y)
+Row const & LyXText::getRowNearY(BufferView const & bv, int y, pit_type pit) const
 {
-       //LyXCursor old_cursor = cursor;
-       setCursorFromCoordinates(cursor, x, y);
-       setCurrentFont();
-#warning DEPM disabled, otherwise crash when entering new table
-       //deleteEmptyParagraphMechanism(old_cursor);
+       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;
+       return *rit;
 }
 
 
-namespace {
+// x,y are absolute screen coordinates
+// sets cursor recursively descending into nested editable insets
+InsetBase * LyXText::editXY(LCursor & cur, int x, int y)
+{
+       if (lyxerr.debugging(Debug::WORKAREA)) {
+               lyxerr << "LyXText::editXY(cur, " << x << ", " << y << ")" << std::endl;
+               cur.bv().coordCache().dump();
+       }
+       pit_type pit = getPitNearY(cur.bv(), y);
+       BOOST_ASSERT(pit != -1);
 
-       /**
-        * return true if the cursor given is at the end of a row,
-        * and the next row is filled by an inset that spans an entire
-        * row.
-        */
-       bool beforeFullRowInset(LyXText & lt, LyXCursor const & cur)
-       {
-               RowList::iterator row = lt.getRow(cur);
-               RowList::iterator next = boost::next(row);
+       Row const & row = getRowNearY(cur.bv(), y, pit);
+       bool bound = false;
 
-               if (next == cur.par()->rows.end() || next->pos() != cur.pos())
-                       return false;
+       TextMetrics const & tm = cur.bv().textMetrics(this);
+       int xx = x; // is modified by getColumnNearX
+       pos_type const pos = row.pos()
+               + tm.getColumnNearX(pit, row, xx, bound);
+       cur.pit() = pit;
+       cur.pos() = pos;
+       cur.boundary(bound);
+       cur.x_target() = x;
+
+       // try to descend into nested insets
+       InsetBase * inset = checkInsetHit(cur.bv(), x, y);
+       //lyxerr << "inset " << inset << " hit at x: " << x << " y: " << y << endl;
+       if (!inset) {
+               // Either we deconst editXY or better we move current_font
+               // and real_current_font to LCursor
+               setCurrentFont(cur);
+               return 0;
+       }
 
-               if (cur.pos() == cur.par()->size() || !cur.par()->isInset(cur.pos()))
-                       return false;
+       InsetBase * insetBefore = pos? pars_[pit].getInset(pos - 1): 0;
+       //InsetBase * insetBehind = pars_[pit].getInset(pos);
 
-               InsetOld const * inset = cur.par()->getInset(cur.pos());
-               if (inset->needFullRow() || inset->display())
-                       return true;
+       // This should be just before or just behind the
+       // cursor position set above.
+       BOOST_ASSERT((pos != 0 && inset == insetBefore)
+               || inset == pars_[pit].getInset(pos));
 
-               return false;
-       }
+       // Make sure the cursor points to the position before
+       // this inset.
+       if (inset == insetBefore)
+               --cur.pos();
+
+       // Try to descend recursively inside the inset.
+       inset = inset->editXY(cur, x, y);
+
+       if (cur.top().text() == this)
+               setCurrentFont(cur);
+       return inset;
 }
 
 
-void LyXText::setCursorFromCoordinates(LyXCursor & cur, int x, int y)
+bool LyXText::checkAndActivateInset(LCursor & cur, bool front)
 {
-       // Get the row first.
-       ParagraphList::iterator pit;
-       RowList::iterator row = getRowNearY(y, pit);
-       bool bound = false;
-       pos_type const column = getColumnNearX(pit, row, x, bound);
-       cur.par(pit);
-       cur.pos(row->pos() + column);
-       cur.x(x);
-       cur.y(y + row->baseline());
-
-//     if (beforeFullRowInset(*this, cur)) {
-//             pos_type const last = lastPrintablePos(*this, pit, row);
-//             RowList::iterator next_row = nextRow(row);
-//             cur.ix(int(getCursorX(pit, next_row, cur.pos(), last, bound)));
-//             cur.iy(y + row->height() + next_row->baseline());
-//     } else {
-               cur.iy(cur.y());
-               cur.ix(cur.x());
-//     }
-       cur.boundary(bound);
+       if (cur.selection())
+               return false;
+       if (cur.pos() == cur.lastpos())
+               return false;
+       InsetBase * inset = cur.nextInset();
+       if (!isHighlyEditableInset(inset))
+               return false;
+       inset->edit(cur, front);
+       return true;
 }
 
 
-void LyXText::cursorLeft(bool internal)
+bool LyXText::cursorLeft(LCursor & cur)
 {
-       if (cursor.pos() > 0) {
-               bool boundary = cursor.boundary();
-               setCursor(cursor.par(), cursor.pos() - 1, true, false);
-               if (!internal && !boundary &&
-                   isBoundary(bv()->buffer(), *cursor.par(), cursor.pos() + 1))
-                       setCursor(cursor.par(), cursor.pos() + 1, true, true);
-       } else if (cursor.par() != ownerParagraphs().begin()) {
-               // steps into the paragraph above
-               ParagraphList::iterator pit = boost::prior(cursor.par());
-               setCursor(pit, pit->size());
+       // Tell BufferView to test for FitCursor in any case!
+       cur.updateFlags(Update::FitCursor);
+
+       if (!cur.boundary() && cur.pos() > 0 &&
+           cur.textRow().pos() == cur.pos() &&
+           !cur.paragraph().isLineSeparator(cur.pos()-1) &&
+           !cur.paragraph().isNewline(cur.pos()-1)) {
+               return setCursor(cur, cur.pit(), cur.pos(), true, true);
        }
+       if (cur.pos() != 0) {
+               bool updateNeeded = setCursor(cur, cur.pit(), cur.pos() - 1, true, false);
+               if (!checkAndActivateInset(cur, false)) {
+                       /** FIXME: What's this cause purpose???
+                       bool boundary = cur.boundary();
+                       if (false && !boundary &&
+                           bidi.isBoundary(cur.buffer(), cur.paragraph(), cur.pos() + 1))
+                               updateNeeded |=
+                                       setCursor(cur, cur.pit(), cur.pos() + 1, true, true);
+                       */
+               }
+               return updateNeeded;
+       }
+
+       if (cur.pit() != 0) {
+               // Steps into the paragraph above
+               return setCursor(cur, cur.pit() - 1, getPar(cur.pit() - 1).size());
+       }
+       return false;
 }
 
 
-void LyXText::cursorRight(bool internal)
+bool LyXText::cursorRight(LCursor & cur)
 {
-       bool const at_end = (cursor.pos() == cursor.par()->size());
-       bool const at_newline = !at_end &&
-               cursor.par()->isNewline(cursor.pos());
-
-       if (!internal && cursor.boundary() && !at_newline)
-               setCursor(cursor.par(), cursor.pos(), true, false);
-       else if (!at_end) {
-               setCursor(cursor.par(), cursor.pos() + 1, true, false);
-               if (!internal &&
-                   isBoundary(bv()->buffer(), *cursor.par(), cursor.pos()))
-                       setCursor(cursor.par(), cursor.pos(), true, true);
-       } else if (boost::next(cursor.par()) != ownerParagraphs().end())
-               setCursor(boost::next(cursor.par()), 0);
+       // Tell BufferView to test for FitCursor in any case!
+       cur.updateFlags(Update::FitCursor);
+
+       if (cur.pos() != cur.lastpos()) {
+               if (cur.boundary())
+                       return setCursor(cur, cur.pit(), cur.pos(),
+                                        true, false);
+
+               bool updateNeeded = false;
+               if (!checkAndActivateInset(cur, true)) {
+                       if (cur.textRow().endpos() == cur.pos() + 1 &&
+                           cur.textRow().endpos() != cur.lastpos() &&
+                           !cur.paragraph().isLineSeparator(cur.pos()) &&
+                           !cur.paragraph().isNewline(cur.pos())) {
+                               cur.boundary(true);
+                       }
+                       updateNeeded |= setCursor(cur, cur.pit(), cur.pos() + 1, true, cur.boundary());
+                       if (false && bidi.isBoundary(cur.buffer(), cur.paragraph(),
+                                                    cur.pos()))
+                               updateNeeded |= setCursor(cur, cur.pit(), cur.pos(), true, true);
+               }
+               return updateNeeded;
+       }
+
+       if (cur.pit() != cur.lastpit())
+               return setCursor(cur, cur.pit() + 1, 0);
+       return false;
 }
 
 
-void LyXText::cursorUp(bool selecting)
+bool LyXText::cursorUp(LCursor & cur)
 {
-#if 1
-       int x = cursor.x_fix();
-       int y = cursor.y() - cursorRow()->baseline() - 1;
-       setCursorFromCoordinates(x, y);
-       if (!selecting) {
-               int topy = top_y();
-               int y1 = cursor.iy() - topy;
-               int y2 = y1;
-               y -= topy;
-               InsetOld * inset_hit = checkInsetHit(x, y1);
-               if (inset_hit && isHighlyEditableInset(inset_hit)) {
-                       inset_hit->localDispatch(
-                               FuncRequest(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none));
-               }
+       // Tell BufferView to test for FitCursor in any case!
+       cur.updateFlags(Update::FitCursor);
+
+       TextMetrics const & tm = cur.bv().textMetrics(this);
+       ParagraphMetrics const & pm = tm.parMetrics(cur.pit());
+
+       int row;
+       if (cur.pos() && cur.boundary())
+               row = pm.pos2row(cur.pos()-1);
+       else
+               row = pm.pos2row(cur.pos());
+
+       // remember current position only if we are not at the end of a row.
+       if (cur.pos() != pm.rows()[row].endpos())
+               cur.setTargetX();
+       int const x = cur.targetX();
+
+       if (!cur.selection()) {
+               int const y = bv_funcs::getPos(cur.bv(), cur, cur.boundary()).y_;
+               LCursor old = cur;
+               // 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 - pm.rows()[row].ascent() - margin);
+               cur.clearSelection();
+
+               // This happens when you move out of an inset.
+               // And to give the DEPM the possibility of doing
+               // something we must provide it with two different
+               // cursors. (Lgb)
+               LCursor dummy = cur;
+               if (dummy == old)
+                       ++dummy.pos();
+
+               cur.bv().checkDepm(dummy, old);
+               return false;
        }
-#else
-       lyxerr << "cursorUp: y " << cursor.y() << " bl: " <<
-               cursorRow()->baseline() << endl;
-       setCursorFromCoordinates(cursor.x_fix(),
-               cursor.y() - cursorRow()->baseline() - 1);
-#endif
+
+       bool updateNeeded = false;
+
+       if (row > 0) {
+               updateNeeded |= setCursor(cur, cur.pit(),
+                       tm.x2pos(cur.pit(), row - 1, x));
+       } 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(),
+                       tm.x2pos(cur.pit(), pmcur.rows().size() - 1, x));
+       }
+
+       cur.x_target() = x;
+
+       return updateNeeded;
 }
 
 
-void LyXText::cursorDown(bool selecting)
+bool LyXText::cursorDown(LCursor & cur)
 {
-#if 1
-       int x = cursor.x_fix();
-       int y = cursor.y() - cursorRow()->baseline() + cursorRow()->height() + 1;
-       setCursorFromCoordinates(x, y);
-       if (!selecting && cursorRow() == cursorIRow()) {
-               int topy = top_y();
-               int y1 = cursor.iy() - topy;
-               int y2 = y1;
-               y -= topy;
-               InsetOld * inset_hit = checkInsetHit(x, y1);
-               if (inset_hit && isHighlyEditableInset(inset_hit)) {
-                       FuncRequest cmd(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none);
-                       inset_hit->localDispatch(cmd);
-               }
+       // Tell BufferView to test for FitCursor in any case!
+       cur.updateFlags(Update::FitCursor);
+
+       TextMetrics const & tm = cur.bv().textMetrics(this);
+       ParagraphMetrics const & pm = tm.parMetrics(cur.pit());
+
+       int row;
+       if (cur.pos() && cur.boundary())
+               row = pm.pos2row(cur.pos()-1);
+       else
+               row = pm.pos2row(cur.pos());
+
+       // remember current position only if we are not at the end of a row.
+       if (cur.pos() != pm.rows()[row].endpos())
+               cur.setTargetX();
+       int const x = cur.targetX();
+
+       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 + pm.rows()[row].descent() + margin);
+               cur.clearSelection();
+
+               // This happens when you move out of an inset.
+               // And to give the DEPM the possibility of doing
+               // something we must provide it with two different
+               // cursors. (Lgb)
+               LCursor dummy = cur;
+               if (dummy == old)
+                       ++dummy.pos();
+               
+               bool const changed = cur.bv().checkDepm(dummy, old);
+
+               // Make sure that cur gets back whatever happened to dummy(Lgb)
+               if (changed)
+                       cur = dummy;
+
+               return false;
        }
-#else
-       setCursorFromCoordinates(cursor.x_fix(),
-                cursor.y() - cursorRow()->baseline() + cursorRow()->height() + 1);
-#endif
+
+       bool updateNeeded = false;
+
+       if (row + 1 < int(pm.rows().size())) {
+               updateNeeded |= setCursor(cur, cur.pit(),
+                       tm.x2pos(cur.pit(), row + 1, x));
+       } else if (cur.pit() + 1 < int(paragraphs().size())) {
+               ++cur.pit();
+               updateNeeded |= setCursor(cur, cur.pit(),
+                       tm.x2pos(cur.pit(), 0, x));
+       }
+
+       cur.x_target() = x;
+
+       return updateNeeded;
 }
 
 
-void LyXText::cursorUpParagraph()
+bool LyXText::cursorUpParagraph(LCursor & cur)
 {
-       if (cursor.pos() > 0)
-               setCursor(cursor.par(), 0);
-       else if (cursor.par() != ownerParagraphs().begin())
-               setCursor(boost::prior(cursor.par()), 0);
+       bool updated = false;
+       if (cur.pos() > 0)
+               updated = setCursor(cur, cur.pit(), 0);
+       else if (cur.pit() != 0)
+               updated = setCursor(cur, cur.pit() - 1, 0);
+       return updated;
 }
 
 
-void LyXText::cursorDownParagraph()
+bool LyXText::cursorDownParagraph(LCursor & cur)
 {
-       ParagraphList::iterator par = cursor.par();
-       ParagraphList::iterator next_par = boost::next(par);
-
-       if (next_par != ownerParagraphs().end())
-               setCursor(next_par, 0);
+       bool updated = false;
+       if (cur.pit() != cur.lastpit())
+               updated = setCursor(cur, cur.pit() + 1, 0);
        else
-               setCursor(par, par->size());
+               updated = setCursor(cur, cur.pit(), cur.lastpos());
+       return updated;
 }
 
 
 // fix the cursor `cur' after a characters has been deleted at `where'
 // position. Called by deleteEmptyParagraphMechanism
-void LyXText::fixCursorAfterDelete(LyXCursor & cur, LyXCursor const & where)
+void LyXText::fixCursorAfterDelete(CursorSlice & cur, CursorSlice const & where)
 {
-       // if cursor is not in the paragraph where the delete occured,
-       // do nothing
-       if (cur.par() != where.par())
+       // Do nothing if cursor is not in the paragraph where the
+       // deletion occured,
+       if (cur.pit() != where.pit())
                return;
 
-       // if cursor position is after the place where the delete occured,
-       // update it
+       // If cursor position is after the deletion place update it
        if (cur.pos() > where.pos())
-               cur.pos(cur.pos()-1);
+               --cur.pos();
 
-       // check also if we don't want to set the cursor on a spot behind the
+       // Check also if we don't want to set the cursor on a spot behind the
        // pagragraph because we erased the last character.
-       if (cur.pos() > cur.par()->size())
-               cur.pos(cur.par()->size());
-
-       // recompute row et al. for this cursor
-       setCursor(cur, cur.par(), cur.pos(), cur.boundary());
+       if (cur.pos() > cur.lastpos())
+               cur.pos() = cur.lastpos();
 }
 
 
-bool LyXText::deleteEmptyParagraphMechanism(LyXCursor const & old_cursor)
+bool LyXText::deleteEmptyParagraphMechanism(LCursor & cur,
+               LCursor & old, bool & need_anchor_change)
 {
-       // Would be wrong to delete anything if we have a selection.
-       if (selection.set())
-               return false;
+       //LYXERR(Debug::DEBUG) << "DEPM: cur:\n" << cur << "old:\n" << old << endl;
+
+       Paragraph & oldpar = old.paragraph();
 
        // We allow all kinds of "mumbo-jumbo" when freespacing.
-       if (old_cursor.par()->isFreeSpacing())
+       if (oldpar.isFreeSpacing())
                return false;
 
        /* Ok I'll put some comments here about what is missing.
-          I have fixed BackSpace (and thus Delete) to not delete
-          double-spaces automagically. I have also changed Cut,
-          Copy and Paste to hopefully do some sensible things.
           There are still some small problems that can lead to
           double spaces stored in the document file or space at
-          the beginning of paragraphs. This happens if you have
-          the cursor betwenn to spaces and then save. Or if you
+          the beginning of paragraphs(). This happens if you have
+          the cursor between to spaces and then save. Or if you
           cut and paste and the selection have a space at the
-          beginning and then save right after the paste. I am
-          sure none of these are very hard to fix, but I will
-          put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
-          that I can get some feedback. (Lgb)
+          beginning and then save right after the paste. (Lgb)
        */
 
-       // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
+       // If old.pos() == 0 and old.pos()(1) == LineSeparator
        // delete the LineSeparator.
        // MISSING
 
-       // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
+       // If old.pos() == 1 and old.pos()(0) == LineSeparator
        // delete the LineSeparator.
        // MISSING
 
-       // If the pos around the old_cursor were spaces, delete one of them.
-       if (old_cursor.par() != cursor.par()
-           || old_cursor.pos() != cursor.pos()) {
-
-               // Only if the cursor has really moved
-               if (old_cursor.pos() > 0
-                   && old_cursor.pos() < old_cursor.par()->size()
-                   && old_cursor.par()->isLineSeparator(old_cursor.pos())
-                   && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
-                       bool erased = old_cursor.par()->erase(old_cursor.pos() - 1);
-                       redoParagraph(old_cursor.par());
-
-                       if (!erased)
-                               return false;
+       bool const same_inset = &old.inset() == &cur.inset();
+       bool const same_par = same_inset && old.pit() == cur.pit();
+       bool const same_par_pos = same_par && old.pos() == cur.pos();
+
+       // If the chars around the old cursor were spaces, delete one of them.
+       if (!same_par_pos) {
+               // Only if the cursor has really moved.
+               if (old.pos() > 0
+                   && old.pos() < oldpar.size()
+                   && oldpar.isLineSeparator(old.pos())
+                   && oldpar.isLineSeparator(old.pos() - 1)
+                   && !oldpar.isDeleted(old.pos() - 1)) {
+                       oldpar.eraseChar(old.pos() - 1, cur.buffer().params().trackChanges);
 #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
 // other bufferviews. It will probably be easier to do that in a more
-// automated way in LyXCursor code. (JMarc 26/09/2001)
+// automated way in CursorSlice code. (JMarc 26/09/2001)
 #endif
-                       // correct all cursors held by the LyXText
-                       fixCursorAfterDelete(cursor, old_cursor);
-                       fixCursorAfterDelete(selection.cursor, old_cursor);
-                       fixCursorAfterDelete(selection.start, old_cursor);
-                       fixCursorAfterDelete(selection.end, old_cursor);
-                       return false;
+                       // correct all cursor parts
+                       if (same_par) {
+                               fixCursorAfterDelete(cur.top(), old.top());
+                               need_anchor_change = true;
+                       }
+                       return true;
                }
        }
 
+       // only do our magic if we changed paragraph
+       if (same_par)
+               return false;
+
        // don't delete anything if this is the ONLY paragraph!
-       if (ownerParagraphs().size() == 1)
+       if (old.lastpit() == 0)
                return false;
 
        // Do not delete empty paragraphs with keepempty set.
-       if (old_cursor.par()->allowEmpty())
+       if (oldpar.allowEmpty())
                return false;
 
-       // only do our magic if we changed paragraph
-       if (old_cursor.par() == cursor.par())
-               return false;
+       if (oldpar.empty() || (oldpar.size() == 1 && oldpar.isLineSeparator(0))) {
+               // Delete old par.
+               recordUndo(old, Undo::ATOMIC,
+                          max(old.pit() - 1, pit_type(0)),
+                          min(old.pit() + 1, old.lastpit()));
+               ParagraphList & plist = old.text()->paragraphs();
+               plist.erase(boost::next(plist.begin(), old.pit()));
+
+               // see #warning above
+               if (cur.depth() >= old.depth()) {
+                       CursorSlice & curslice = cur[old.depth() - 1];
+                       if (&curslice.inset() == &old.inset()
+                           && curslice.pit() > old.pit()) {
+                               --curslice.pit();
+                               // since a paragraph has been deleted, all the
+                               // insets after `old' have been copied and
+                               // their address has changed. Therefore we
+                               // need to `regenerate' cur. (JMarc)
+                               cur.updateInsets(&(cur.bottom().inset()));
+                               need_anchor_change = true;
+                       }
+               }
+               return true;
+       }
 
-       // record if we have deleted a paragraph
-       // we can't possibly have deleted a paragraph before this point
-       bool deleted = false;
+       if (oldpar.stripLeadingSpaces(cur.buffer().params().trackChanges)) {
+               need_anchor_change = true;
+               // We return true here because the Paragraph contents changed and
+               // we need a redraw before further action is processed.
+               return true;
+       }
 
-       if (old_cursor.par()->empty() ||
-           (old_cursor.par()->size() == 1 &&
-            old_cursor.par()->isLineSeparator(0))) {
-               // ok, we will delete anything
-               LyXCursor tmpcursor;
+       return false;
+}
 
-               deleted = true;
 
-               bool selection_position_was_oldcursor_position = (
-                       selection.cursor.par()  == old_cursor.par()
-                       && selection.cursor.pos() == old_cursor.pos());
+void LyXText::deleteEmptyParagraphMechanism(pit_type first, pit_type last, bool trackChanges)
+{
+       BOOST_ASSERT(first >= 0 && first <= last && last < (int) pars_.size());
 
-               tmpcursor = cursor;
-               cursor = old_cursor; // that undo can restore the right cursor position
+       for (pit_type pit = first; pit <= last; ++pit) {
+               Paragraph & par = pars_[pit];
 
-               ParagraphList::iterator endpit = boost::next(old_cursor.par());
-               while (endpit != ownerParagraphs().end() && endpit->getDepth())
-                       ++endpit;
-       
-               recordUndo(bv(), Undo::DELETE, old_cursor.par(), boost::prior(endpit));
-               cursor = tmpcursor;
+               // We allow all kinds of "mumbo-jumbo" when freespacing.
+               if (par.isFreeSpacing())
+                       continue;
 
-               // delete old par
-               ownerParagraphs().erase(old_cursor.par());
-               redoParagraph();
+               for (pos_type pos = 1; pos < par.size(); ++pos) {
+                       if (par.isLineSeparator(pos) && par.isLineSeparator(pos - 1)
+                           && !par.isDeleted(pos - 1)) {
+                               if (par.eraseChar(pos - 1, trackChanges)) {
+                                       --pos;
+                               }
+                       }
+               }
 
-               // correct cursor y
-               setCursorIntern(cursor.par(), cursor.pos());
+               // don't delete anything if this is the only remaining paragraph within the given range
+               // note: LyXText::acceptOrRejectChanges() sets the cursor to 'first' after calling DEPM 
+               if (first == last)
+                       continue;
 
-               if (selection_position_was_oldcursor_position) {
-                       // correct selection
-                       selection.cursor = cursor;
-               }
-       }
-       if (!deleted) {
-               if (old_cursor.par()->stripLeadingSpaces()) {
-                       redoParagraph(old_cursor.par());
-                       // correct cursor y
-                       setCursorIntern(cursor.par(), cursor.pos());
-                       selection.cursor = cursor;
-               }
-       }
-       return deleted;
-}
+               // don't delete empty paragraphs with keepempty set
+               if (par.allowEmpty())
+                       continue;
 
+               if (par.empty() || (par.size() == 1 && par.isLineSeparator(0))) {
+                       pars_.erase(boost::next(pars_.begin(), pit));
+                       --pit;
+                       --last;
+                       continue;
+               }
 
-ParagraphList & LyXText::ownerParagraphs() const
-{
-       if (inset_owner) {
-               return inset_owner->paragraphs;
+               par.stripLeadingSpaces(trackChanges);
        }
-       return bv_owner->buffer()->paragraphs;
 }
 
 
-bool LyXText::isInInset() const
+void LyXText::recUndo(LCursor & cur, pit_type first, pit_type last) const
 {
-       // Sub-level has non-null bv owner and non-null inset owner.
-       return inset_owner != 0 && bv_owner != 0;
+       recordUndo(cur, Undo::ATOMIC, first, last);
 }
 
 
-int defaultRowHeight()
+void LyXText::recUndo(LCursor & cur, pit_type par) const
 {
-       LyXFont const font(LyXFont::ALL_SANE);
-       return int(font_metrics::maxAscent(font)
-                + font_metrics::maxDescent(font) * 1.5);
+       recordUndo(cur, Undo::ATOMIC, par, par);
 }
+
+} // namespace lyx