]> git.lyx.org Git - lyx.git/blobdiff - src/BufferView_pimpl.C
make boundary property an iterator property instead of a CursorSlice property
[lyx.git] / src / BufferView_pimpl.C
index 27adc9dcfb9699d2e89532111ed2f659499eb5b7..1c52d6e331492a1cd2398a980ab57fe763a184ee 100644 (file)
 #include "buffer_funcs.h"
 #include "bufferlist.h"
 #include "bufferparams.h"
+#include "coordcache.h"
 #include "cursor.h"
 #include "debug.h"
 #include "dispatchresult.h"
 #include "factory.h"
 #include "FloatList.h"
 #include "funcrequest.h"
+#include "FuncStatus.h"
 #include "gettext.h"
 #include "intl.h"
-#include "iterators.h"
+#include "insetiterator.h"
+#include "LaTeXFeatures.h"
 #include "lyx_cb.h" // added for Dispatch functions
 #include "lyx_main.h"
 #include "lyxfind.h"
 #include "lyxtext.h"
 #include "lyxrc.h"
 #include "lastfiles.h"
+#include "metricsinfo.h"
 #include "paragraph.h"
 #include "paragraph_funcs.h"
 #include "ParagraphParameters.h"
+#include "pariterator.h"
+#include "rowpainter.h"
 #include "undo.h"
 #include "vspace.h"
 
-#include "insets/insetfloatlist.h"
+#include "insets/insetbibtex.h"
 #include "insets/insetref.h"
+#include "insets/insettext.h"
 
 #include "frontends/Alert.h"
 #include "frontends/Dialogs.h"
 #include "frontends/FileDialog.h"
+#include "frontends/font_metrics.h"
 #include "frontends/LyXView.h"
 #include "frontends/LyXScreenFactory.h"
 #include "frontends/screen.h"
 
 #include "graphics/Previews.h"
 
+#include "support/convert.h"
+#include "support/filefilterlist.h"
 #include "support/filetools.h"
-#include "support/globbing.h"
-#include "support/path_defines.h"
-#include "support/tostr.h"
+#include "support/forkedcontr.h"
+#include "support/package.h"
+#include "support/types.h"
 
 #include <boost/bind.hpp>
+#include <boost/current_function.hpp>
 
-using bv_funcs::currentState;
+#include <functional>
+#include <vector>
 
 using lyx::pos_type;
 
@@ -74,15 +86,20 @@ using lyx::support::AddPath;
 using lyx::support::bformat;
 using lyx::support::FileFilterList;
 using lyx::support::FileSearch;
+using lyx::support::ForkedcallsController;
 using lyx::support::IsDirWriteable;
 using lyx::support::MakeDisplayPath;
-using lyx::support::strToUnsignedInt;
-using lyx::support::system_lyxdir;
+using lyx::support::MakeAbsPath;
+using lyx::support::package;
 
 using std::endl;
+using std::istringstream;
 using std::make_pair;
 using std::min;
+using std::max;
 using std::string;
+using std::mem_fun_ref;
+using std::vector;
 
 
 extern BufferList bufferlist;
@@ -106,17 +123,31 @@ boost::signals::connection selectioncon;
 boost::signals::connection lostcon;
 
 
+/// Return an inset of this class if it exists at the current cursor position
+template <class T>
+T * getInsetByCode(LCursor & cur, InsetBase::Code code)
+{
+       T * inset = 0;
+       DocIterator it = cur;
+       if (it.nextInset() &&
+           it.nextInset()->lyxCode() == code) {
+               inset = static_cast<T*>(it.nextInset());
+       }
+       return inset;
+}
+
 } // anon namespace
 
 
 BufferView::Pimpl::Pimpl(BufferView & bv, LyXView * owner,
-            int xpos, int ypos, int width, int height)
+                        int width, int height)
        : bv_(&bv), owner_(owner), buffer_(0), cursor_timeout(400),
-         using_xterm_cursor(false), cursor_(bv)
+         using_xterm_cursor(false), cursor_(bv) ,
+         anchor_ref_(0), offset_ref_(0)
 {
        xsel_cache_.set = false;
 
-       workarea_.reset(WorkAreaFactory::create(xpos, ypos, width, height));
+       workarea_.reset(WorkAreaFactory::create(*owner_, width, height));
        screen_.reset(LyXScreenFactory::create(workarea()));
 
        // Setup the signals
@@ -159,19 +190,32 @@ void BufferView::Pimpl::connectBuffer(Buffer & buf)
                disconnectBuffer();
 
        errorConnection_ =
-               buf.error.connect(boost::bind(&BufferView::Pimpl::addError, this, _1));
+               buf.error.connect(
+                       boost::bind(&BufferView::Pimpl::addError, this, _1));
+
        messageConnection_ =
-               buf.message.connect(boost::bind(&LyXView::message, owner_, _1));
+               buf.message.connect(
+                       boost::bind(&LyXView::message, owner_, _1));
+
        busyConnection_ =
-               buf.busy.connect(boost::bind(&LyXView::busy, owner_, _1));
+               buf.busy.connect(
+                       boost::bind(&LyXView::busy, owner_, _1));
+
        titleConnection_ =
-               buf.updateTitles.connect(boost::bind(&LyXView::updateWindowTitle, owner_));
+               buf.updateTitles.connect(
+                       boost::bind(&LyXView::updateWindowTitle, owner_));
+
        timerConnection_ =
-               buf.resetAutosaveTimers.connect(boost::bind(&LyXView::resetAutosaveTimer, owner_));
+               buf.resetAutosaveTimers.connect(
+                       boost::bind(&LyXView::resetAutosaveTimer, owner_));
+
        readonlyConnection_ =
-               buf.readonly.connect(boost::bind(&BufferView::Pimpl::showReadonly, this, _1));
+               buf.readonly.connect(
+                       boost::bind(&BufferView::Pimpl::showReadonly, this, _1));
+
        closingConnection_ =
-               buf.closing.connect(boost::bind(&BufferView::Pimpl::buffer, this, (Buffer *)0));
+               buf.closing.connect(
+                       boost::bind(&BufferView::Pimpl::setBuffer, this, (Buffer *)0));
 }
 
 
@@ -187,20 +231,17 @@ void BufferView::Pimpl::disconnectBuffer()
 }
 
 
-bool BufferView::Pimpl::newFile(string const & filename,
-                               string const & tname,
-                               bool isNamed)
+void BufferView::Pimpl::newFile(string const & filename, string const & tname,
+       bool isNamed)
 {
-       Buffer * b = ::newFile(filename, tname, isNamed);
-       buffer(b);
-       return true;
+       setBuffer(::newFile(filename, tname, isNamed));
 }
 
 
 bool BufferView::Pimpl::loadLyXFile(string const & filename, bool tolastfiles)
 {
-       // get absolute path of file and add ".lyx" to the filename if
-       // necessary
+       // Get absolute path of file and add ".lyx"
+       // to the filename if necessary
        string s = FileSearch(string(), filename, "lyx");
 
        bool const found = !s.empty();
@@ -208,7 +249,7 @@ bool BufferView::Pimpl::loadLyXFile(string const & filename, bool tolastfiles)
        if (!found)
                s = filename;
 
-       // file already open?
+       // File already open?
        if (bufferlist.exists(s)) {
                string const file = MakeDisplayPath(s, 20);
                string text = bformat(_("The document %1$s is already "
@@ -218,14 +259,13 @@ bool BufferView::Pimpl::loadLyXFile(string const & filename, bool tolastfiles)
                        text, 0, 1,  _("&Revert"), _("&Switch to document"));
 
                if (ret != 0) {
-                       buffer(bufferlist.getBuffer(s));
+                       setBuffer(bufferlist.getBuffer(s));
                        return true;
-               } else {
-                       // FIXME: should be LFUN_REVERT
-                       if (!bufferlist.close(bufferlist.getBuffer(s), false))
-                               return false;
-                       // Fall through to new load. (Asger)
                }
+               // FIXME: should be LFUN_REVERT
+               if (!bufferlist.close(bufferlist.getBuffer(s), false))
+                       return false;
+               // Fall through to new load. (Asger)
        }
 
        Buffer * b;
@@ -250,7 +290,7 @@ bool BufferView::Pimpl::loadLyXFile(string const & filename, bool tolastfiles)
                        return false;
        }
 
-       buffer(b);
+       setBuffer(b);
        bv_->showErrorList(_("Parse"));
 
        if (tolastfiles)
@@ -278,166 +318,86 @@ Painter & BufferView::Pimpl::painter() const
 }
 
 
-void BufferView::Pimpl::top_y(int y)
-{
-       top_y_ = y;
-}
-
-
-int BufferView::Pimpl::top_y() const
+void BufferView::Pimpl::setBuffer(Buffer * b)
 {
-       return top_y_;
-}
+       lyxerr[Debug::INFO] << BOOST_CURRENT_FUNCTION
+                           << "[ b = " << b << "]" << endl;
 
-
-void BufferView::Pimpl::buffer(Buffer * b)
-{
-       lyxerr[Debug::INFO] << "Setting buffer in BufferView ("
-                           << b << ')' << endl;
-       if (buffer_) {
+       if (buffer_)
                disconnectBuffer();
-               //delete bv_->text();
-               //bv_->setText(0);
-       }
 
-       // reset old cursor
-       cursor_.reset();
+       // If we are closing current buffer, switch to the first in
+       // buffer list.
+       if (!b) {
+               lyxerr[Debug::INFO] << BOOST_CURRENT_FUNCTION
+                                   << " No Buffer!" << endl;
+               // We are closing the buffer, use the first buffer as current
+               buffer_ = bufferlist.first();
+               owner_->getDialogs().hideBufferDependent();
+       } else {
+               // Set current buffer
+               buffer_ = b;
+       }
 
-       // set current buffer
-       buffer_ = b;
+       // Reset old cursor
+       cursor_ = LCursor(*bv_);
+       anchor_ref_ = 0;
+       offset_ref_ = 0;
 
-       top_y_ = 0;
 
-       // if we're quitting lyx, don't bother updating stuff
+       // If we're quitting lyx, don't bother updating stuff
        if (quitting)
                return;
 
-       // if we are closing the buffer, use the first buffer as current
-       if (!buffer_)
-               buffer_ = bufferlist.first();
-
        if (buffer_) {
-               lyxerr[Debug::INFO] << "Buffer addr: " << buffer_ << endl;
+               lyxerr[Debug::INFO] << BOOST_CURRENT_FUNCTION
+                                   << "Buffer addr: " << buffer_ << endl;
                connectBuffer(*buffer_);
 
+               cursor_.push(buffer_->inset());
+               cursor_.resetAnchor();
                buffer_->text().init(bv_);
-               buffer_->text().textwidth_ = workarea().workWidth();
-               buffer_->text().fullRebreak();
-
-               // If we don't have a text object for this, we make one
-               if (bv_->text() == 0)
-                       resizeCurrentBuffer();
-
-               // FIXME: needed when ?
-               fitCursor();
+               buffer_->text().setCurrentFont(cursor_);
 
                // Buffer-dependent dialogs should be updated or
                // hidden. This should go here because some dialogs (eg ToC)
                // require bv_->text.
                owner_->getDialogs().updateBufferDependent(true);
-       } else {
-               lyxerr[Debug::INFO] << "  No Buffer!" << endl;
-               owner_->getDialogs().hideBufferDependent();
        }
 
        update();
        updateScrollbar();
        owner_->updateMenubar();
-       owner_->updateToolbar();
+       owner_->updateToolbars();
        owner_->updateLayoutChoice();
        owner_->updateWindowTitle();
 
-       // Don't forget to update the Layout
+       // This is done after the layout combox has been populated
        if (buffer_)
-               owner_->setLayout(bv_->text()->cursorPar()->layout()->name());
+               owner_->setLayout(cursor_.paragraph().layout()->name());
 
-       if (lyx::graphics::Previews::activated() && buffer_)
+       if (buffer_ && lyx::graphics::Previews::status() != LyXRC::PREVIEW_OFF)
                lyx::graphics::Previews::get().generateBufferPreviews(*buffer_);
 }
 
 
-bool BufferView::Pimpl::fitCursor()
-{
-       lyxerr << "BufferView::Pimpl::fitCursor." << endl;
-       if (screen().fitCursor(bv_)) {
-               updateScrollbar();
-               return true;
-       }
-       return false;
-}
-
-
-void BufferView::Pimpl::redoCurrentBuffer()
-{
-       lyxerr[Debug::INFO] << "BufferView::redoCurrentBuffer" << endl;
-       if (buffer_ && bv_->text()) {
-               resizeCurrentBuffer();
-               updateScrollbar();
-               owner_->updateLayoutChoice();
-       }
-}
-
-
 void BufferView::Pimpl::resizeCurrentBuffer()
 {
-       lyxerr[Debug::INFO] << "resizeCurrentBuffer" << endl;
-
-       int par = -1;
-       int selstartpar = -1;
-       int selendpar = -1;
-
-       pos_type pos = 0;
-       pos_type selstartpos = 0;
-       pos_type selendpos = 0;
-       bool sel = false;
-       bool mark_set  = false;
-
+       lyxerr[Debug::DEBUG] << BOOST_CURRENT_FUNCTION << endl;
        owner_->busy(true);
-
        owner_->message(_("Formatting document..."));
 
        LyXText * text = bv_->text();
-       lyxerr << "### resizeCurrentBuffer: text " << text << endl;
        if (!text)
                return;
 
-       LCursor & cur = bv_->cursor();
-       par = cur.par();
-       pos = cur.pos();
-       selstartpar = cur.selBegin().par();
-       selstartpos = cur.selBegin().pos();
-       selendpar = cur.selEnd().par();
-       selendpos = cur.selEnd().pos();
-       sel = cur.selection();
-       mark_set = cur.mark();
-       text->textwidth_ = bv_->workWidth();
-       text->fullRebreak();
+       text->init(bv_);
        update();
 
-       if (par != -1) {
-               cur.selection() = true;
-               // At this point just to avoid the Delete-Empty-Paragraph-
-               // Mechanism when setting the cursor.
-               cur.mark() = mark_set;
-               if (sel) {
-                       text->setCursor(selstartpar, selstartpos);
-                       cur.resetAnchor();
-                       text->setCursor(selendpar, selendpos);
-                       cur.setSelection();
-                       text->setCursor(par, pos);
-               } else {
-                       text->setCursor(par, pos);
-                       cur.resetAnchor();
-                       cur.selection() = false;
-               }
-       }
-
-       fitCursor();
-
        switchKeyMap();
        owner_->busy(false);
 
-       // reset the "Formatting..." message
+       // Reset the "Formatting..." message
        owner_->clearMessage();
 
        updateScrollbar();
@@ -447,76 +407,112 @@ void BufferView::Pimpl::resizeCurrentBuffer()
 void BufferView::Pimpl::updateScrollbar()
 {
        if (!bv_->text()) {
-               lyxerr[Debug::GUI] << "no text in updateScrollbar" << endl;
+               lyxerr[Debug::DEBUG] << BOOST_CURRENT_FUNCTION
+                                    << " no text in updateScrollbar" << endl;
                workarea().setScrollbarParams(0, 0, 0);
                return;
        }
 
-       LyXText const & t = *bv_->text();
-
-       lyxerr[Debug::GUI] << "Updating scrollbar: h " << t.height << ", top_y() "
-               << top_y() << ", default height " << defaultRowHeight() << endl;
+       LyXText & t = *bv_->text();
+       if (anchor_ref_ >  int(t.paragraphs().size()) - 1) {
+               anchor_ref_ = int(t.paragraphs().size()) - 1;
+               offset_ref_ = 0;
+       }
 
-       workarea().setScrollbarParams(t.height, top_y(), defaultRowHeight());
+       lyxerr[Debug::GUI]
+               << BOOST_CURRENT_FUNCTION
+               << " Updating scrollbar: height: " << t.paragraphs().size()
+               << " curr par: " << cursor_.bottom().pit()
+               << " default height " << defaultRowHeight() << endl;
+
+       // It would be better to fix the scrollbar to understand
+       // values in [0..1] and divide everything by wh
+       int const wh = workarea().workHeight() / 4;
+       int const h = t.getPar(anchor_ref_).height();
+       workarea().setScrollbarParams(t.paragraphs().size() * wh, anchor_ref_ * wh + int(offset_ref_ * wh / float(h)), int (wh * defaultRowHeight() / float(h)));
+//     workarea().setScrollbarParams(t.paragraphs().size(), anchor_ref_, 1);
 }
 
 
 void BufferView::Pimpl::scrollDocView(int value)
 {
-       lyxerr[Debug::GUI] << "scrollDocView of " << value << endl;
+       lyxerr[Debug::GUI] << BOOST_CURRENT_FUNCTION
+                          << "[ value = " << value << "]" << endl;
 
        if (!buffer_)
                return;
 
        screen().hideCursor();
 
-       top_y(value);
-       screen().redraw(*bv_);
+       int const wh = workarea().workHeight() / 4;
+
+       LyXText & t = *bv_->text();
+
+       float const bar = value / float(wh * t.paragraphs().size());
+
+       anchor_ref_ = int(bar * t.paragraphs().size());
+       t.redoParagraph(anchor_ref_);
+       int const h = t.getPar(anchor_ref_).height();
+       offset_ref_ = int((bar * t.paragraphs().size() - anchor_ref_) * h);
+       update();
 
        if (!lyxrc.cursor_follows_scrollbar)
                return;
 
-       int const height = defaultRowHeight();
-       int const first = top_y() + height;
-       int const last = top_y() + workarea().workHeight() - height;
+       int const height = 2 * defaultRowHeight();
+       int const first = height;
+       int const last = workarea().workHeight() - height;
+       LCursor & cur = cursor_;
 
-       LyXText * text = bv_->text();
-       int y = text->cursorY(bv_->cursor().cursor_.front());
-       if (y < first)
-               text->setCursorFromCoordinates(0, first);
-       else if (y > last)
-               text->setCursorFromCoordinates(0, last);
+       bv_funcs::CurStatus st = bv_funcs::status(bv_, cur);
 
+       switch (st) {
+       case bv_funcs::CUR_ABOVE:
+               t.setCursorFromCoordinates(cur, 0, first);
+               cur.clearSelection();
+               break;
+       case bv_funcs::CUR_BELOW:
+               t.setCursorFromCoordinates(cur, 0, last);
+               cur.clearSelection();
+               break;
+       case bv_funcs::CUR_INSIDE:
+               int const y = bv_funcs::getPos(cur, cur.boundary()).y_;
+               int const newy = min(last, max(y, first));
+               if (y != newy) {
+                       cur.reset(buffer_->inset());
+                       t.setCursorFromCoordinates(cur, 0, newy);
+               }
+       }
        owner_->updateLayoutChoice();
 }
 
 
-void BufferView::Pimpl::scroll(int lines)
+void BufferView::Pimpl::scroll(int /*lines*/)
 {
-       if (!buffer_)
-               return;
-
-       LyXText const * t = bv_->text();
-       int const line_height = defaultRowHeight();
-
-       // The new absolute coordinate
-       int new_top_y = top_y() + lines * line_height;
-
-       // Restrict to a valid value
-       new_top_y = std::min(t->height - 4 * line_height, new_top_y);
-       new_top_y = std::max(0, new_top_y);
-
-       scrollDocView(new_top_y);
-
-       // Update the scrollbar.
-       workarea().setScrollbarParams(t->height, top_y(), defaultRowHeight());
+//     if (!buffer_)
+//             return;
+//
+//     LyXText const * t = bv_->text();
+//     int const line_height = defaultRowHeight();
+//
+//     // The new absolute coordinate
+//     int new_top_y = top_y() + lines * line_height;
+//
+//     // Restrict to a valid value
+//     new_top_y = std::min(t->height() - 4 * line_height, new_top_y);
+//     new_top_y = std::max(0, new_top_y);
+//
+//     scrollDocView(new_top_y);
+//
+//     // Update the scrollbar.
+//     workarea().setScrollbarParams(t->height(), top_y(), defaultRowHeight());
 }
 
 
 void BufferView::Pimpl::workAreaKeyPress(LyXKeySymPtr key,
                                         key_modifier::state state)
 {
-       bv_->owner()->getLyXFunc().processKeySym(key, state);
+       owner_->getLyXFunc().processKeySym(key, state);
 
        /* This is perhaps a bit of a hack. When we move
         * around, or type, it's nice to be able to see
@@ -525,10 +521,8 @@ void BufferView::Pimpl::workAreaKeyPress(LyXKeySymPtr key,
         * of the cursor. Note we cannot do this inside
         * dispatch() itself, because that's called recursively.
         */
-       if (available()) {
-               cursor_timeout.restart();
+       if (available())
                screen().showCursor(*bv_);
-       }
 }
 
 
@@ -539,7 +533,7 @@ void BufferView::Pimpl::selectionRequested()
        if (!available())
                return;
 
-       LCursor & cur = bv_->cursor();
+       LCursor & cur = cursor_;
 
        if (!cur.selection()) {
                xsel_cache_.set = false;
@@ -547,16 +541,16 @@ void BufferView::Pimpl::selectionRequested()
        }
 
        if (!xsel_cache_.set ||
-           cur.cursor_.back() != xsel_cache_.cursor ||
-           cur.anchor_.back() != xsel_cache_.anchor)
+           cur.top() != xsel_cache_.cursor ||
+           cur.anchor_.top() != xsel_cache_.anchor)
        {
-               xsel_cache_.cursor = cur.cursor_.back();
-               xsel_cache_.anchor = cur.anchor_.back();
+               xsel_cache_.cursor = cur.top();
+               xsel_cache_.anchor = cur.anchor_.top();
                xsel_cache_.set = cur.selection();
-               sel = bv_->getLyXText()->selectionAsString(*bv_->buffer(), false);
+               sel = cur.selectionAsString(false);
                if (!sel.empty())
                        workarea().putClipboard(sel);
-       } 
+       }
 }
 
 
@@ -564,7 +558,7 @@ void BufferView::Pimpl::selectionLost()
 {
        if (available()) {
                screen().hideCursor();
-               bv_->cursor().clearSelection();
+               cursor_.clearSelection();
                xsel_cache_.set = false;
        }
 }
@@ -578,55 +572,99 @@ void BufferView::Pimpl::workAreaResize()
        bool const widthChange = workarea().workWidth() != work_area_width;
        bool const heightChange = workarea().workHeight() != work_area_height;
 
-       // update from work area
+       // Update from work area
        work_area_width = workarea().workWidth();
        work_area_height = workarea().workHeight();
 
-       if (buffer_ != 0) {
-               if (widthChange) {
-                       // The visible LyXView need a resize
-                       resizeCurrentBuffer();
-               }
+       if (buffer_ && widthChange) {
+               // The visible LyXView need a resize
+               resizeCurrentBuffer();
        }
 
        if (widthChange || heightChange)
                update();
 
-       // always make sure that the scrollbar is sane.
+       // Always make sure that the scrollbar is sane.
        updateScrollbar();
        owner_->updateLayoutChoice();
 }
 
 
-void BufferView::Pimpl::update()
+bool BufferView::Pimpl::fitCursor()
 {
-       //lyxerr << "BufferView::update()" << endl;
-       // fix cursor coordinate cache in case something went wrong
-
-       // check needed to survive LyX startup
-       if (bv_->getLyXText()) {
-               // update all 'visible' paragraphs
-               ParagraphList::iterator beg;
-               ParagraphList::iterator end;
-               getParsInRange(buffer_->paragraphs(),
-                              top_y(), top_y() + workarea().workHeight(),
-                              beg, end);
-               bv_->text()->redoParagraphs(beg, end);
-               updateScrollbar();
+       if (bv_funcs::status(bv_, cursor_) == bv_funcs::CUR_INSIDE) {
+               LyXFont const font = cursor_.getFont();
+               int const asc = font_metrics::maxAscent(font);
+               int const des = font_metrics::maxDescent(font);
+               Point const p = bv_funcs::getPos(cursor_, cursor_.boundary());
+               if (p.y_ - asc >= 0 && p.y_ + des < workarea().workHeight())
+                       return false;
        }
-       screen().redraw(*bv_);
+       center();
+       return true;
+}
+
+
+void BufferView::Pimpl::update(Update::flags flags)
+{
+       lyxerr[Debug::DEBUG]
+                << BOOST_CURRENT_FUNCTION
+                << "[fitcursor = " << (flags & Update::FitCursor)
+                << ", forceupdate = " << (flags & Update::Force)
+                << ", singlepar = " << (flags & Update::SinglePar)
+                << "]  buffer: " << buffer_ << endl;
+
+       // Check needed to survive LyX startup
+       if (buffer_) {
+               // Update macro store
+               buffer_->buildMacros();
+
+               CoordCache backup;
+               std::swap(theCoords, backup);
+
+               // This, together with doneUpdating(), verifies (using
+               // asserts) that screen redraw is not called from
+               // within itself.
+               theCoords.startUpdating();
+
+               // First drawing step
+               ViewMetricsInfo vi = metrics();
+               bool forceupdate(flags & Update::Force);
+
+               if ((flags & Update::FitCursor) && fitCursor()) {
+                       forceupdate = true;
+                       vi = metrics(flags & Update::SinglePar);
+               }
+               if (forceupdate) {
+                       // Second drawing step
+                       screen().redraw(*bv_, vi);
+               } else {
+                       // Abort updating of the coord
+                       // cache - just restore the old one
+                       std::swap(theCoords, backup);
+               }
+       } else
+               screen().greyOut();
+
+       // And the scrollbar
+       updateScrollbar();
+       owner_->view_state_changed();
 }
 
 
 // Callback for cursor timer
 void BufferView::Pimpl::cursorToggle()
 {
-       if (!buffer_) {
-               cursor_timeout.restart();
-               return;
+       if (buffer_) {
+               screen().toggleCursor(*bv_);
+
+               // Use this opportunity to deal with any child processes that
+               // have finished but are waiting to communicate this fact
+               // to the rest of LyX.
+               ForkedcallsController & fcc = ForkedcallsController::get();
+               fcc.handleCompletedProcesses();
        }
 
-       screen().toggleCursor(*bv_);
        cursor_timeout.restart();
 }
 
@@ -639,17 +677,17 @@ bool BufferView::Pimpl::available() const
 
 Change const BufferView::Pimpl::getCurrentChange()
 {
-       if (!bv_->buffer()->params().tracking_changes)
+       if (!buffer_->params().tracking_changes)
                return Change(Change::UNCHANGED);
 
        LyXText * text = bv_->getLyXText();
-       LCursor & cur = bv_->cursor();
+       LCursor & cur = cursor_;
 
        if (!cur.selection())
                return Change(Change::UNCHANGED);
 
-       return text->getPar(cur.selBegin())
-               ->lookupChangeFull(cur.selBegin().pos());
+       return text->getPar(cur.selBegin().pit()).
+                       lookupChangeFull(cur.selBegin().pos());
 }
 
 
@@ -657,11 +695,12 @@ void BufferView::Pimpl::savePosition(unsigned int i)
 {
        if (i >= saved_positions_num)
                return;
+       BOOST_ASSERT(cursor_.inTexted());
        saved_positions[i] = Position(buffer_->fileName(),
-                                     bv_->text()->cursorPar()->id(),
-                                     bv_->text()->cursor().pos());
+                                     cursor_.paragraph().id(),
+                                     cursor_.pos());
        if (i > 0)
-               owner_->message(bformat(_("Saved bookmark %1$s"), tostr(i)));
+               owner_->message(bformat(_("Saved bookmark %1$d"), i));
 }
 
 
@@ -672,7 +711,7 @@ void BufferView::Pimpl::restorePosition(unsigned int i)
 
        string const fname = saved_positions[i].filename;
 
-       bv_->cursor().clearSelection();
+       cursor_.clearSelection();
 
        if (fname != buffer_->fileName()) {
                Buffer * b = 0;
@@ -680,21 +719,21 @@ void BufferView::Pimpl::restorePosition(unsigned int i)
                        b = bufferlist.getBuffer(fname);
                else {
                        b = bufferlist.newBuffer(fname);
-                       ::loadLyXFile(b, fname); // don't ask, just load it
+                       // Don't ask, just load it
+                       ::loadLyXFile(b, fname);
                }
                if (b)
-                       buffer(b);
+                       setBuffer(b);
        }
 
        ParIterator par = buffer_->getParFromID(saved_positions[i].par_id);
        if (par == buffer_->par_iterator_end())
                return;
 
-       bv_->text()->setCursor(par.pit(),
-                            min(par->size(), saved_positions[i].par_pos));
+       bv_->setCursor(makeDocIterator(par, min(par->size(), saved_positions[i].par_pos)));
 
        if (i > 0)
-               owner_->message(bformat(_("Moved to bookmark %1$s"), tostr(i)));
+               owner_->message(bformat(_("Moved to bookmark %1$d"), i));
 }
 
 
@@ -722,79 +761,19 @@ void BufferView::Pimpl::switchKeyMap()
 
 void BufferView::Pimpl::center()
 {
-       LyXText * text = bv_->text();
-
-       bv_->cursor().clearSelection();
-       int const half_height = workarea().workHeight() / 2;
-       int new_y = text->cursorY(bv_->cursor().cursor_.front()) - half_height;
-       if (new_y < 0)
-               new_y = 0;
-
-       // FIXME: look at this comment again ...
-       // This updates top_y() but means the fitCursor() call
-       // from the update(FITCUR) doesn't realise that we might
-       // have moved (e.g. from GOTOPARAGRAPH), so doesn't cause
-       // the scrollbar to be updated as it should, so we have
-       // to do it manually. Any operation that does a center()
-       // and also might have moved top_y() must make sure to call
-       // updateScrollbar() currently. Never mind that this is a
-       // pretty obfuscated way of updating text->top_y()
-       top_y(new_y);
+       CursorSlice & bot = cursor_.bottom();
+       lyx::pit_type const pit = bot.pit();
+       bot.text()->redoParagraph(pit);
+       Paragraph const & par = bot.text()->paragraphs()[pit];
+       anchor_ref_ = pit;
+       offset_ref_ = bv_funcs::coordOffset(cursor_, cursor_.boundary()).y_
+               + par.ascent() - workarea().workHeight() / 2;
 }
 
 
-void BufferView::Pimpl::stuffClipboard(string const & stuff) const
+void BufferView::Pimpl::stuffClipboard(string const & content) const
 {
-       workarea().putClipboard(stuff);
-}
-
-
-InsetBase * BufferView::Pimpl::getInsetByCode(InsetBase::Code code)
-{
-#if 0
-       CursorSlice cursor = bv_->getLyXText()->cursor;
-       Buffer::inset_iterator it =
-               find_if(Buffer::inset_iterator(
-                       cursorPar(), cursor().pos()),
-                       buffer_->inset_iterator_end(),
-                       lyx::compare_memfun(&Inset::lyxCode, code));
-       return it != buffer_->inset_iterator_end() ? (*it) : 0;
-#else
-       // Ok, this is a little bit too brute force but it
-       // should work for now. Better infrastructure is coming. (Lgb)
-
-       Buffer * b = bv_->buffer();
-       LyXText * text = bv_->getLyXText();
-
-       Buffer::inset_iterator beg = b->inset_iterator_begin();
-       Buffer::inset_iterator end = b->inset_iterator_end();
-
-       bool cursor_par_seen = false;
-
-       for (; beg != end; ++beg) {
-               if (beg.getPar() == text->cursorPar()) {
-                       cursor_par_seen = true;
-               }
-               if (cursor_par_seen) {
-                       if (beg.getPar() == text->cursorPar()
-                           && beg.getPos() >= text->cursor().pos()) {
-                               break;
-                       }
-                       if (beg.getPar() != text->cursorPar()) {
-                               break;
-                       }
-               }
-
-       }
-       if (beg != end) {
-               // Now find the first inset that matches code.
-               for (; beg != end; ++beg) {
-                       if (beg->lyxCode() == code)
-                               return &(*beg);
-               }
-       }
-       return 0;
-#endif
+       workarea().putClipboard(content);
 }
 
 
@@ -818,7 +797,7 @@ void BufferView::Pimpl::MenuInsertLyXFile(string const & filenm)
                        make_pair(string(_("Documents|#o#O")),
                                  string(lyxrc.document_path)),
                        make_pair(string(_("Examples|#E#e")),
-                                 string(AddPath(system_lyxdir(), "examples"))));
+                                 string(AddPath(package().system_support(), "examples"))));
 
                FileDialog::Result result =
                        fileDlg.open(initpath,
@@ -837,152 +816,224 @@ void BufferView::Pimpl::MenuInsertLyXFile(string const & filenm)
                }
        }
 
-       // get absolute path of file and add ".lyx" to the filename if
-       // necessary
+       // Get absolute path of file and add ".lyx"
+       // to the filename if necessary
        filename = FileSearch(string(), filename, "lyx");
 
        string const disp_fn = MakeDisplayPath(filename);
        owner_->message(bformat(_("Inserting document %1$s..."), disp_fn));
-       if (bv_->insertLyXFile(filename))
-               owner_->message(bformat(_("Document %1$s inserted."),
-                                       disp_fn));
-       else
-               owner_->message(bformat(_("Could not insert document %1$s"),
-                                       disp_fn));
+
+       cursor_.clearSelection();
+       bv_->getLyXText()->breakParagraph(cursor_);
+
+       BOOST_ASSERT(cursor_.inTexted());
+
+       string const fname = MakeAbsPath(filename);
+       bool const res = buffer_->readFile(fname, cursor_.pit());
+       resizeCurrentBuffer();
+
+       string s = res ? _("Document %1$s inserted.")
+                      : _("Could not insert document %1$s");
+       owner_->message(bformat(s, disp_fn));
 }
 
 
 void BufferView::Pimpl::trackChanges()
 {
-       Buffer * buf = bv_->buffer();
-       bool const tracking(buf->params().tracking_changes);
+       bool const tracking = buffer_->params().tracking_changes;
 
        if (!tracking) {
-               ParIterator const end = buf->par_iterator_end();
-               for (ParIterator it = buf->par_iterator_begin(); it != end; ++it)
-                       it->trackChanges();
-               buf->params().tracking_changes = true;
+               for_each(buffer_->par_iterator_begin(),
+                        buffer_->par_iterator_end(),
+                        bind(&Paragraph::trackChanges, _1, Change::UNCHANGED));
+               buffer_->params().tracking_changes = true;
 
-               // we cannot allow undos beyond the freeze point
-               buf->undostack().clear();
+               // We cannot allow undos beyond the freeze point
+               buffer_->undostack().clear();
        } else {
                update();
-               bv_->text()->setCursor(0, 0);
+               bv_->text()->setCursor(cursor_, 0, 0);
+#ifdef WITH_WARNINGS
 #warning changes FIXME
-               bool found = lyx::find::findNextChange(bv_);
+#endif
+               bool const found = lyx::find::findNextChange(bv_);
                if (found) {
                        owner_->getDialogs().show("changes");
                        return;
                }
 
-               ParIterator const end = buf->par_iterator_end();
-               for (ParIterator it = buf->par_iterator_begin(); it != end; ++it)
-                       it->untrackChanges();
-               buf->params().tracking_changes = false;
+               for_each(buffer_->par_iterator_begin(),
+                        buffer_->par_iterator_end(),
+                        mem_fun_ref(&Paragraph::untrackChanges));
+
+               buffer_->params().tracking_changes = false;
        }
 
-       buf->redostack().clear();
+       buffer_->redostack().clear();
 }
 
 
 bool BufferView::Pimpl::workAreaDispatch(FuncRequest const & cmd0)
 {
-       //
-       // this is only called for mouse related events.
-       //
+       //lyxerr << BOOST_CURRENT_FUNCTION << "[ cmd0 " << cmd0 << "]" << endl;
+
+       // This is only called for mouse related events including
+       // LFUN_FILE_OPEN generated by drag-and-drop.
        FuncRequest cmd = cmd0;
-       cmd.y += bv_->top_y();
-       lyxerr << "*** workAreaDispatch: request: " << cmd << std::endl;
-       LCursor cur(*bv_);
-       switch (cmd.action) {
-#if 0
-       case LFUN_MOUSE_MOTION: {
-               if (!available())
-                       return false;
-               FuncRequest cmd1 = cmd;
-               InsetBase * inset = cur.inset();
-               DispatchResult res;
-               if (inset) {
-                       res = inset->dispatch(cur, cmd);
-               } else {
-                       res = cur.innerText()->dispatch(cur, cmd);
-               }
 
-               if (bv_->fitCursor() || res.update()) {
-                       bv_->update();
-                       cur.updatePos();
-               }
+       // Handle drag&drop
+       if (cmd.action == LFUN_FILE_OPEN) {
+               owner_->dispatch(cmd);
                return true;
        }
-#endif
 
-       case LFUN_MOUSE_MOTION:
-       case LFUN_MOUSE_PRESS:
-       case LFUN_MOUSE_RELEASE:
-       case LFUN_MOUSE_DOUBLE:
-       case LFUN_MOUSE_TRIPLE: {
-               // We pass those directly to the Bufferview, since
-               // otherwise selection handling breaks down
+       if (!buffer_)
+               return false;
 
-               // Doesn't go through lyxfunc, so we need to update
-               // the layout choice etc. ourselves
+       LCursor cur(*bv_);
+       cur.push(buffer_->inset());
+       cur.selection() = cursor_.selection();
 
-               // e.g. Qt mouse press when no buffer
-               if (!available())
-                       return false;
+       // Doesn't go through lyxfunc, so we need to update
+       // the layout choice etc. ourselves
 
-               screen().hideCursor();
+       // E.g. Qt mouse press when no buffer
+       if (!available())
+               return false;
 
-               // either the inset under the cursor or the surrounding LyXText will
-               // handle this event.
+       screen().hideCursor();
 
-               // built temporary path to inset
-               LyXText * text = bv_->text();
-               InsetBase * const inset_hit = text->checkInsetHit(cmd.x, cmd.y);
-               if (inset_hit) 
-                       inset_hit->edit(cur, cmd.x, cmd.y);
+       // Either the inset under the cursor or the
+       // surrounding LyXText will handle this event.
+
+       // Build temporary cursor.
+       cmd.y = min(max(cmd.y,-1), workarea().workHeight());
+       InsetBase * inset = bv_->text()->editXY(cur, cmd.x, cmd.y);
+       //lyxerr << BOOST_CURRENT_FUNCTION
+       //       << " * hit inset at tip: " << inset << endl;
+       //lyxerr << BOOST_CURRENT_FUNCTION
+       //       << " * created temp cursor:" << cur << endl;
+
+       // Put anchor at the same position.
+       cur.resetAnchor();
+
+       // Try to dispatch to an non-editable inset near this position
+       // via the temp cursor. If the inset wishes to change the real
+       // cursor it has to do so explicitly by using
+       //  cur.bv().cursor() = cur;  (or similar)
+       if (inset)
+               inset->dispatch(cur, cmd);
+
+       // Now dispatch to the temporary cursor. If the real cursor should
+       // be modified, the inset's dispatch has to do so explicitly.
+       if (!cur.result().dispatched())
+               cur.dispatch(cmd);
+
+       if (cur.result().dispatched()) {
+               // Redraw if requested or necessary.
+               if (cur.result().update())
+                       update(Update::FitCursor | Update::Force);
                else
-                       text->setCursorFromCoordinates(cur.current(), cmd.x, cmd.y);
-               lyxerr << "created temp cursor: " << cur << endl;
+                       update();
+       }
 
-               // Dispatch to the temp cursor.
-               // An inset (or LyXText) can assign this to bv->cursor()
-               // if it wishes to do so.
-               DispatchResult res = cur.dispatch(cmd);
+       // See workAreaKeyPress
+       cursor_timeout.restart();
+       screen().showCursor(*bv_);
 
-               if (bv_->fitCursor() || res.update())
-                       bv_->update();
+       // Skip these when selecting
+       if (cmd.action != LFUN_MOUSE_MOTION) {
+               owner_->updateLayoutChoice();
+               owner_->updateToolbars();
+       }
 
-               // see workAreaKeyPress
-               cursor_timeout.restart();
-               screen().showCursor(*bv_);
+       // Slight hack: this is only called currently when we
+       // clicked somewhere, so we force through the display
+       // of the new status here.
+       owner_->clearMessage();
+       return true;
+}
 
-               // skip these when selecting
-               if (cmd.action != LFUN_MOUSE_MOTION) {
-                       owner_->updateLayoutChoice();
-                       owner_->updateToolbar();
-               }
 
-               // slight hack: this is only called currently when we
-               // clicked somewhere, so we force through the display
-               // of the new status here.
-               owner_->clearMessage();
-               return true;
+FuncStatus BufferView::Pimpl::getStatus(FuncRequest const & cmd)
+{
+       FuncStatus flag;
+
+       switch (cmd.action) {
+
+       case LFUN_UNDO:
+               flag.enabled(!buffer_->undostack().empty());
+               break;
+       case LFUN_REDO:
+               flag.enabled(!buffer_->redostack().empty());
+               break;
+       case LFUN_FILE_INSERT:
+       case LFUN_FILE_INSERT_ASCII_PARA:
+       case LFUN_FILE_INSERT_ASCII:
+       case LFUN_FONT_STATE:
+       case LFUN_INSERT_LABEL:
+       case LFUN_BOOKMARK_SAVE:
+       case LFUN_GOTO_PARAGRAPH:
+       case LFUN_GOTOERROR:
+       case LFUN_GOTONOTE:
+       case LFUN_REFERENCE_GOTO:
+       case LFUN_WORD_FIND:
+       case LFUN_WORD_REPLACE:
+       case LFUN_MARK_OFF:
+       case LFUN_MARK_ON:
+       case LFUN_SETMARK:
+       case LFUN_CENTER:
+       case LFUN_BIBDB_ADD:
+       case LFUN_BIBDB_DEL:
+       case LFUN_WORDS_COUNT:
+               flag.enabled(true);
+               break;
+
+       case LFUN_LABEL_GOTO: {
+               flag.enabled(!cmd.argument.empty()
+                   || getInsetByCode<InsetRef>(cursor_, InsetBase::REF_CODE));
+               break;
+       }
+
+       case LFUN_BOOKMARK_GOTO:
+               flag.enabled(isSavedPosition(convert<unsigned int>(cmd.argument)));
+               break;
+       case LFUN_TRACK_CHANGES:
+               flag.enabled(true);
+               flag.setOnOff(buffer_->params().tracking_changes);
+               break;
+
+       case LFUN_OUTPUT_CHANGES: {
+               LaTeXFeatures features(*buffer_, buffer_->params(), false);
+               flag.enabled(buffer_ && buffer_->params().tracking_changes
+                       && features.isAvailable("dvipost"));
+               flag.setOnOff(buffer_->params().output_changes);
+               break;
        }
 
+       case LFUN_MERGE_CHANGES:
+       case LFUN_ACCEPT_CHANGE: // what about these two
+       case LFUN_REJECT_CHANGE: // what about these two
+       case LFUN_ACCEPT_ALL_CHANGES:
+       case LFUN_REJECT_ALL_CHANGES:
+               flag.enabled(buffer_ && buffer_->params().tracking_changes);
+               break;
        default:
-               lyxerr << "*** UNDISPATCHED: " << cmd;
-               //owner_->dispatch(cmd);
+               flag.enabled(false);
        }
-       return true;
+
+       return flag;
 }
 
 
+
 bool BufferView::Pimpl::dispatch(FuncRequest const & cmd)
 {
-       lyxerr << "*** BufferView::Pimpl: request: " << cmd << std::endl;
+       //lyxerr << BOOST_CURRENT_FUNCTION
+       //       << [ cmd = " << cmd << "]" << endl;
+
        // Make sure that the cached BufferView is correct.
-       lyxerr[Debug::ACTION] << "BufferView::Pimpl::Dispatch:"
+       lyxerr[Debug::ACTION] << BOOST_CURRENT_FUNCTION
                << " action[" << cmd.action << ']'
                << " arg[" << cmd.argument << ']'
                << " x[" << cmd.x << ']'
@@ -990,15 +1041,30 @@ bool BufferView::Pimpl::dispatch(FuncRequest const & cmd)
                << " button[" << cmd.button() << ']'
                << endl;
 
-       LyXTextClass const & tclass = buffer_->params().getLyXTextClass();
-       LCursor & cur = bv_->cursor();
+       LCursor & cur = cursor_;
 
        switch (cmd.action) {
 
-       case LFUN_SCROLL_INSET:
-               // this is not handled here as this function is only active
-               // if we have a locking_inset and that one is (or contains)
-               // a tabular-inset
+       case LFUN_UNDO:
+               if (available()) {
+                       cur.message(_("Undo"));
+                       cur.clearSelection();
+                       if (!textUndo(*bv_))
+                               cur.message(_("No further undo information"));
+                       update();
+                       switchKeyMap();
+               }
+               break;
+
+       case LFUN_REDO:
+               if (available()) {
+                       cur.message(_("Redo"));
+                       cur.clearSelection();
+                       if (!textRedo(*bv_))
+                               cur.message(_("No further redo information"));
+                       update();
+                       switchKeyMap();
+               }
                break;
 
        case LFUN_FILE_INSERT:
@@ -1014,32 +1080,23 @@ bool BufferView::Pimpl::dispatch(FuncRequest const & cmd)
                break;
 
        case LFUN_FONT_STATE:
-               owner_->getLyXFunc().setMessage(currentState(bv_));
-               break;
-
-       case LFUN_INSERT_LABEL: {
-               // Try and generate a valid label
-               string const contents = cmd.argument.empty() ?
-                       getPossibleLabel(*bv_) : cmd.argument;
-               InsetCommandParams icp("label", contents);
-               string data = InsetCommandMailer::params2string("label", icp);
-               owner_->getDialogs().show("label", data, 0);
+               cur.message(cur.currentState());
                break;
-       }
 
        case LFUN_BOOKMARK_SAVE:
-               savePosition(strToUnsignedInt(cmd.argument));
+               savePosition(convert<unsigned int>(cmd.argument));
                break;
 
        case LFUN_BOOKMARK_GOTO:
-               restorePosition(strToUnsignedInt(cmd.argument));
+               restorePosition(convert<unsigned int>(cmd.argument));
                break;
 
-       case LFUN_REF_GOTO: {
+       case LFUN_LABEL_GOTO: {
                string label = cmd.argument;
                if (label.empty()) {
                        InsetRef * inset =
-                               static_cast<InsetRef*>(getInsetByCode(InsetBase::REF_CODE));
+                               getInsetByCode<InsetRef>(cursor_,
+                                                        InsetBase::REF_CODE);
                        if (inset) {
                                label = inset->getContents();
                                savePosition(0);
@@ -1048,92 +1105,42 @@ bool BufferView::Pimpl::dispatch(FuncRequest const & cmd)
 
                if (!label.empty())
                        bv_->gotoLabel(label);
-       }
-       break;
-
-       // --- accented characters ---------------------------
-
-       case LFUN_UMLAUT:
-       case LFUN_CIRCUMFLEX:
-       case LFUN_GRAVE:
-       case LFUN_ACUTE:
-       case LFUN_TILDE:
-       case LFUN_CEDILLA:
-       case LFUN_MACRON:
-       case LFUN_DOT:
-       case LFUN_UNDERDOT:
-       case LFUN_UNDERBAR:
-       case LFUN_CARON:
-       case LFUN_SPECIAL_CARON:
-       case LFUN_BREVE:
-       case LFUN_TIE:
-       case LFUN_HUNG_UMLAUT:
-       case LFUN_CIRCLE:
-       case LFUN_OGONEK:
-               if (cmd.argument.empty()) {
-                       // As always...
-                       owner_->getLyXFunc().handleKeyFunc(cmd.action);
-               } else {
-                       owner_->getLyXFunc().handleKeyFunc(cmd.action);
-                       owner_->getIntl().getTransManager()
-                               .TranslateAndInsert(cmd.argument[0], bv_->getLyXText());
-                       update();
-               }
-               break;
-
-       case LFUN_INSET_INSERT: {
-               // Same as above.
-               BOOST_ASSERT(false);
-               InsetBase * inset = createInset(bv_, cmd);
-               if (!inset || !insertInset(inset))
-                       delete inset;
                break;
        }
 
-       case LFUN_FLOAT_LIST:
-               if (tclass.floats().typeExist(cmd.argument)) {
-                       InsetBase * inset = new InsetFloatList(cmd.argument);
-                       if (!insertInset(inset, tclass.defaultLayoutName()))
-                               delete inset;
+       case LFUN_GOTO_PARAGRAPH: {
+               int const id = convert<int>(cmd.argument);
+               ParIterator par = buffer_->getParFromID(id);
+               if (par == buffer_->par_iterator_end()) {
+                       lyxerr[Debug::INFO] << "No matching paragraph found! ["
+                                           << id << ']' << endl;
+                       break;
                } else {
-                       lyxerr << "Non-existent float type: "
-                              << cmd.argument << endl;
+                       lyxerr[Debug::INFO] << "Paragraph " << par->id()
+                                           << " found." << endl;
                }
-               break;
 
-       case LFUN_LAYOUT_PARAGRAPH: {
-               string data;
-               params2string(*bv_->getLyXText()->cursorPar(), data);
-               data = "show\n" + data;
-               bv_->owner()->getDialogs().show("paragraph", data);
+               // Set the cursor
+               bv_->setCursor(makeDocIterator(par, 0));
+
+               update();
+               switchKeyMap();
                break;
        }
 
-       case LFUN_PARAGRAPH_UPDATE:
-               updateParagraphDialog();
+       case LFUN_GOTOERROR:
+               bv_funcs::gotoInset(bv_, InsetBase::ERROR_CODE, false);
                break;
 
-       case LFUN_PARAGRAPH_APPLY:
-               setParagraphParams(*bv_, cmd.argument);
+       case LFUN_GOTONOTE:
+               bv_funcs::gotoInset(bv_, InsetBase::NOTE_CODE, false);
                break;
 
-       case LFUN_THESAURUS_ENTRY: {
-               string arg = cmd.argument;
-
-               if (arg.empty()) {
-                       arg = bv_->getLyXText()->selectionAsString(*buffer_,
-                                                                  false);
-
-                       // FIXME
-                       if (arg.size() > 100 || arg.empty()) {
-                               // Get word or selection
-                               bv_->getLyXText()->selectWordWhenUnderCursor(lyx::WHOLE_WORD);
-                               arg = bv_->getLyXText()->selectionAsString(*buffer_, false);
-                               // FIXME: where is getLyXText()->unselect(bv_) ?
-                       }
-               }
-
-               bv_->owner()->getDialogs().show("thesaurus", arg);
+       case LFUN_REFERENCE_GOTO: {
+               vector<InsetBase_code> tmp;
+               tmp.push_back(InsetBase::LABEL_CODE);
+               tmp.push_back(InsetBase::REF_CODE);
+               bv_funcs::gotoInset(bv_, tmp, true);
                break;
        }
 
@@ -1141,37 +1148,34 @@ bool BufferView::Pimpl::dispatch(FuncRequest const & cmd)
                trackChanges();
                break;
 
+       case LFUN_OUTPUT_CHANGES: {
+               bool const state = buffer_->params().output_changes;
+               buffer_->params().output_changes = !state;
+               break;
+       }
+
        case LFUN_MERGE_CHANGES:
                owner_->getDialogs().show("changes");
                break;
 
        case LFUN_ACCEPT_ALL_CHANGES: {
-               bv_->text()->setCursor(0, 0);
+               cursor_.reset(buffer_->inset());
+#ifdef WITH_WARNINGS
 #warning FIXME changes
+#endif
                while (lyx::find::findNextChange(bv_))
-                       bv_->getLyXText()->acceptChange();
+                       bv_->getLyXText()->acceptChange(cursor_);
                update();
                break;
        }
 
        case LFUN_REJECT_ALL_CHANGES: {
-               bv_->text()->setCursor(0, 0);
+               cursor_.reset(buffer_->inset());
+#ifdef WITH_WARNINGS
 #warning FIXME changes
+#endif
                while (lyx::find::findNextChange(bv_))
-                       bv_->getLyXText()->rejectChange();
-               update();
-               break;
-       }
-
-       case LFUN_ACCEPT_CHANGE: {
-               bv_->getLyXText()->acceptChange();
-               update();
-               break;
-       }
-
-       case LFUN_REJECT_CHANGE: {
-               bv_->getLyXText()->rejectChange();
-               update();
+                       bv_->getLyXText()->rejectChange(cursor_);
                break;
        }
 
@@ -1185,124 +1189,166 @@ bool BufferView::Pimpl::dispatch(FuncRequest const & cmd)
 
        case LFUN_MARK_OFF:
                cur.clearSelection();
-               bv_->update();
                cur.resetAnchor();
-               cmd.message(N_("Mark off"));
+               cur.message(N_("Mark off"));
                break;
 
        case LFUN_MARK_ON:
                cur.clearSelection();
                cur.mark() = true;
-               bv_->update();
                cur.resetAnchor();
-               cmd.message(N_("Mark on"));
+               cur.message(N_("Mark on"));
                break;
 
        case LFUN_SETMARK:
                cur.clearSelection();
                if (cur.mark()) {
-                       cmd.message(N_("Mark removed"));
+                       cur.mark() = false;
+                       cur.message(N_("Mark removed"));
                } else {
                        cur.mark() = true;
-                       cmd.message(N_("Mark set"));
+                       cur.message(N_("Mark set"));
                }
                cur.resetAnchor();
-               bv_->update();
                break;
 
-       case LFUN_UNKNOWN_ACTION:
-               cmd.errorMessage(N_("Unknown function!"));
+       case LFUN_CENTER:
+               center();
+               break;
+
+       case LFUN_BIBDB_ADD: {
+               LCursor tmpcur = cursor_;
+               bv_funcs::findInset(tmpcur, InsetBase::BIBTEX_CODE, false);
+               InsetBibtex * inset = getInsetByCode<InsetBibtex>(tmpcur,
+                                               InsetBase::BIBTEX_CODE);
+               if (inset)
+                       inset->addDatabase(cmd.argument);
+               break;
+       }
+
+       case LFUN_BIBDB_DEL: {
+               LCursor tmpcur = cursor_;
+               bv_funcs::findInset(tmpcur, InsetBase::BIBTEX_CODE, false);
+               InsetBibtex * inset = getInsetByCode<InsetBibtex>(tmpcur,
+                                               InsetBase::BIBTEX_CODE);
+               if (inset)
+                       inset->delDatabase(cmd.argument);
                break;
+       }
+
+       case LFUN_WORDS_COUNT: {
+               DocIterator from, to;
+               if (cur.selection()) {
+                       from = cur.selectionBegin();
+                       to = cur.selectionEnd();
+               } else {
+                       from = doc_iterator_begin(buffer_->inset());
+                       to = doc_iterator_end(buffer_->inset());
+               }
+               int const count = countWords(from, to);
+               string message;
+               if (count != 1) {
+                       if (cur.selection())
+                               message = bformat(_("%1$d words in selection."),
+                                         count);
+                               else
+                                       message = bformat(_("%1$d words in document."),
+                                                         count);
+               }
+               else {
+                       if (cur.selection())
+                               message = _("One word in selection.");
+                       else
+                               message = _("One word in document.");
+               }
 
+               Alert::information(_("Count words"), message);
+       }
+               break;
        default:
-               return cur.dispatch(cmd).dispatched();
+               return false;
        }
 
        return true;
 }
 
 
-bool BufferView::Pimpl::insertInset(InsetBase * inset, string const & lout)
+ViewMetricsInfo BufferView::Pimpl::metrics(bool singlepar)
 {
-       // not quite sure if we want this...
-       bv_->text()->recUndo(bv_->text()->cursor().par());
-       freezeUndo();
+       // Remove old position cache
+       theCoords.clear();
+       BufferView & bv = *bv_;
+       LyXText * const text = bv.text();
+       if (anchor_ref_ > int(text->paragraphs().size() - 1)) {
+               anchor_ref_ = int(text->paragraphs().size() - 1);
+               offset_ref_ = 0;
+       }
 
-       bv_->cursor().clearSelection();
-       if (!lout.empty()) {
-               bv_->text()->breakParagraph(bv_->buffer()->paragraphs());
+       lyx::pit_type const pit = anchor_ref_;
+       int pit1 = pit;
+       int pit2 = pit;
+       size_t const npit = text->paragraphs().size();
+
+       lyxerr[Debug::DEBUG]
+                << BOOST_CURRENT_FUNCTION
+                << " npit: " << npit
+                << " pit1: " << pit1
+                << " pit2: " << pit2
+                << endl;
+
+       // Rebreak anchor par
+       text->redoParagraph(pit);
+       int y0 = text->getPar(pit1).ascent() - offset_ref_;
+
+       // Redo paragraphs above cursor if necessary
+       int y1 = y0;
+       while (!singlepar && y1 > 0 && pit1 > 0) {
+               y1 -= text->getPar(pit1).ascent();
+               --pit1;
+               text->redoParagraph(pit1);
+               y1 -= text->getPar(pit1).descent();
+       }
 
-               if (!bv_->text()->cursorPar()->empty()) {
-                       bv_->text()->cursorLeft(bv_);
-                       bv_->text()->breakParagraph(bv_->buffer()->paragraphs());
-               }
 
-               string lres = lout;
-               LyXTextClass const & tclass = buffer_->params().getLyXTextClass();
-               bool hasLayout = tclass.hasLayout(lres);
+       // Take care of ascent of first line
+       y1 -= text->getPar(pit1).ascent();
 
-               bv_->text()->setLayout(hasLayout ? lres : tclass.defaultLayoutName());
-               bv_->text()->setParagraph(Spacing(), LYX_ALIGN_LAYOUT, string(), 0);
-       }
-       bv_->cursor().innerText()->insertInset(inset);
-       unFreezeUndo();
-       return true;
-}
+       // Normalize anchor for next time
+       anchor_ref_ = pit1;
+       offset_ref_ = -y1;
 
+       // Grey at the beginning is ugly
+       if (pit1 == 0 && y1 > 0) {
+               y0 -= y1;
+               y1 = 0;
+               anchor_ref_ = 0;
+       }
 
-bool BufferView::Pimpl::ChangeInsets(InsetBase::Code code,
-                                    string const & from, string const & to)
-{
-       bool need_update = false;
-       CursorSlice cur = bv_->text()->cursor();
-
-       ParIterator end = bv_->buffer()->par_iterator_end();
-       for (ParIterator it = bv_->buffer()->par_iterator_begin();
-            it != end; ++it) {
-               bool changed_inset = false;
-               for (InsetList::iterator it2 = it->insetlist.begin();
-                    it2 != it->insetlist.end(); ++it2) {
-                       if (it2->inset->lyxCode() == code) {
-                               InsetCommand * inset = static_cast<InsetCommand *>(it2->inset);
-                               if (inset->getContents() == from) {
-                                       inset->setContents(to);
-                                       changed_inset = true;
-                               }
-                       }
-               }
-               if (changed_inset) {
-                       need_update = true;
+       // Redo paragraphs below cursor if necessary
+       int y2 = y0;
+       while (!singlepar && y2 < bv.workHeight() && pit2 < int(npit) - 1) {
+               y2 += text->getPar(pit2).descent();
+               ++pit2;
+               text->redoParagraph(pit2);
+               y2 += text->getPar(pit2).ascent();
+       }
 
-                       // FIXME
+       // Take care of descent of last line
+       y2 += text->getPar(pit2).descent();
 
-                       // The test it.size() == 1 was needed to prevent crashes.
-                       // How to set the cursor correctly when it.size() > 1 ??
-                       if (it.size() == 1) {
-                               bv_->text()->setCursorIntern(bv_->text()->parOffset(it.pit()), 0);
-                               bv_->text()->redoParagraph(bv_->text()->cursorPar());
-                       }
-               }
+       // The coordinates of all these paragraphs are correct, cache them
+       int y = y1;
+       for (lyx::pit_type pit = pit1; pit <= pit2; ++pit) {
+               y += text->getPar(pit).ascent();
+               theCoords.parPos()[text][pit] = Point(0, y);
+               y += text->getPar(pit).descent();
        }
-       bv_->text()->setCursorIntern(cur.par(), cur.pos());
-       return need_update;
-}
 
+       lyxerr[Debug::DEBUG]
+                << BOOST_CURRENT_FUNCTION
+                << " y1: " << y1
+                << " y2: " << y2
+                << endl;
 
-void BufferView::Pimpl::updateParagraphDialog()
-{
-       if (!bv_->owner()->getDialogs().visible("paragraph"))
-               return;
-       CursorSlice const & cur = bv_->cursor().innerTextSlice();
-       LyXText * text = bv_->cursor().innerText();
-       Paragraph const & par = *text->getPar(cur.par());
-       string data;
-       params2string(par, data);
-
-       // Will the paragraph accept changes from the dialog?
-       InsetBase * const inset = cur.inset();
-       bool const accept =
-               !(inset && inset->forceDefaultParagraphs(inset));
-
-       data = "update " + tostr(accept) + '\n' + data;
-       bv_->owner()->getDialogs().update("paragraph", data);
+       return ViewMetricsInfo(pit1, pit2, y1, y2, singlepar);
 }