]> git.lyx.org Git - lyx.git/blobdiff - src/BufferView.cpp
Fix scons and a file inclusion problem in ControlCommand.h
[lyx.git] / src / BufferView.cpp
index b4022ab5f01d971ac9e7a693d4624e9ba4e66a54..180c2b1aa768476a5a483e836d483eff9eb4bfc3 100644 (file)
@@ -20,7 +20,6 @@
 #include "buffer_funcs.h"
 #include "BufferList.h"
 #include "BufferParams.h"
-#include "bufferview_funcs.h"
 #include "CoordCache.h"
 #include "CutAndPaste.h"
 #include "debug.h"
 #include "InsetIterator.h"
 #include "Language.h"
 #include "LaTeXFeatures.h"
-#include "callback.h" // added for Dispatch functions
 #include "LyX.h"
 #include "lyxfind.h"
 #include "LyXFunc.h"
 #include "Layout.h"
-#include "Text.h"
-#include "TextClass.h"
 #include "LyXRC.h"
-#include "Session.h"
+#include "MetricsInfo.h"
 #include "Paragraph.h"
 #include "paragraph_funcs.h"
 #include "ParagraphParameters.h"
 #include "ParIterator.h"
+#include "Session.h"
 #include "TexRow.h"
-#include "toc.h"
+#include "Text.h"
+#include "TextClass.h"
 #include "Undo.h"
 #include "VSpace.h"
 #include "WordLangTuple.h"
-#include "MetricsInfo.h"
 
 #include "insets/InsetBibtex.h"
 #include "insets/InsetCommand.h" // ChangeRefs
 #include "insets/InsetText.h"
 
 #include "frontends/alert.h"
+#include "frontends/Delegates.h"
 #include "frontends/FileDialog.h"
 #include "frontends/FontMetrics.h"
+#include "frontends/Painter.h"
+#include "frontends/Selection.h"
 
 #include "graphics/Previews.h"
 
 #include "support/filetools.h"
 #include "support/Package.h"
 #include "support/types.h"
+#include "support/fs_extras.h"
 
 #include <boost/bind.hpp>
 #include <boost/current_function.hpp>
+#include <boost/next_prior.hpp>
+#include <boost/filesystem/operations.hpp>
 
+#include <cerrno>
+#include <fstream>
 #include <functional>
 #include <vector>
 
+using std::distance;
+using std::endl;
+using std::ifstream;
+using std::istringstream;
+using std::istream_iterator;
+using std::make_pair;
+using std::min;
+using std::max;
+using std::mem_fun_ref;
+using std::string;
+using std::vector;
+
+namespace fs = boost::filesystem;
 
 namespace lyx {
 
@@ -89,234 +107,295 @@ using support::fileSearch;
 using support::isDirWriteable;
 using support::isFileReadable;
 using support::makeDisplayPath;
+using support::makeAbsPath;
 using support::package;
 
-using std::distance;
-using std::endl;
-using std::istringstream;
-using std::make_pair;
-using std::min;
-using std::max;
-using std::mem_fun_ref;
-using std::string;
-using std::vector;
-
 namespace Alert = frontend::Alert;
 
 namespace {
 
 /// Return an inset of this class if it exists at the current cursor position
 template <class T>
-T * getInsetByCode(Cursor & cur, Inset::Code code)
+T * getInsetByCode(Cursor const & cur, Inset::Code code)
 {
-       T * inset = 0;
        DocIterator it = cur;
-       if (it.nextInset() &&
-           it.nextInset()->lyxCode() == code) {
-               inset = static_cast<T*>(it.nextInset());
-       }
-       return inset;
+       Inset * inset = it.nextInset();
+       if (inset && inset->lyxCode() == code)
+               return static_cast<T*>(inset);
+       return 0;
 }
 
-} // anon namespace
 
+bool findInset(DocIterator & dit, vector<Inset_code> const & codes,
+       bool same_content);
 
-BufferView::BufferView()
-       : width_(0), height_(0), buffer_(0), wh_(0),
-         cursor_(*this),
-         multiparsel_cache_(false), anchor_ref_(0), offset_ref_(0),
-         intl_(new Intl), last_inset_(0)
+bool findNextInset(DocIterator & dit, vector<Inset_code> const & codes,
+       string const & contents)
 {
-       xsel_cache_.set = false;
-       intl_->initKeyMapper(lyxrc.use_kbmap);
+       DocIterator tmpdit = dit;
+
+       while (tmpdit) {
+               Inset const * inset = tmpdit.nextInset();
+               if (inset
+                   && find(codes.begin(), codes.end(), inset->lyxCode()) != codes.end()
+                   && (contents.empty() ||
+                   static_cast<InsetCommand const *>(inset)->getContents() == contents)) {
+                       dit = tmpdit;
+                       return true;
+               }
+               tmpdit.forwardInset();
+       }
+
+       return false;
 }
 
 
-BufferView::~BufferView()
+/// Looks for next inset with one of the the given code
+bool findInset(DocIterator & dit, vector<Inset_code> const & codes,
+       bool same_content)
 {
+       string contents;
+       DocIterator tmpdit = dit;
+       tmpdit.forwardInset();
+       if (!tmpdit)
+               return false;
+
+       if (same_content) {
+               Inset const * inset = tmpdit.nextInset();
+               if (inset
+                   && find(codes.begin(), codes.end(), inset->lyxCode()) != codes.end()) {
+                       contents = static_cast<InsetCommand const *>(inset)->getContents();
+               }
+       }
+
+       if (!findNextInset(tmpdit, codes, contents)) {
+               if (dit.depth() != 1 || dit.pit() != 0 || dit.pos() != 0) {
+                       tmpdit  = doc_iterator_begin(tmpdit.bottom().inset());
+                       if (!findNextInset(tmpdit, codes, contents))
+                               return false;
+               } else
+                       return false;
+       }
+
+       dit = tmpdit;
+       return true;
 }
 
 
-Buffer * BufferView::buffer() const
+/// Looks for next inset with the given code
+void findInset(DocIterator & dit, Inset_code code, bool same_content)
 {
-       return buffer_;
+       findInset(dit, vector<Inset_code>(1, code), same_content);
 }
 
 
-void BufferView::setBuffer(Buffer * b)
-{
-       LYXERR(Debug::INFO) << BOOST_CURRENT_FUNCTION
-                           << "[ b = " << b << "]" << endl;
-
-       if (buffer_) {
-               // Save the actual cursor position and anchor inside the
-               // buffer so that it can be restored in case we rechange
-               // to this buffer later on.
-               buffer_->saveCursor(cursor_.selectionBegin(),
-                                   cursor_.selectionEnd());
-               // update bookmark pit of the current buffer before switch
-               for (size_t i = 0; i < LyX::ref().session().bookmarks().size(); ++i) {
-                       BookmarksSection::Bookmark const & bm = LyX::ref().session().bookmarks().bookmark(i);
-                       if (buffer()->fileName() != bm.filename.absFilename())
-                               continue;
-                       // if top_id or bottom_pit, bottom_pos has been changed, update bookmark
-                       // see http://bugzilla.lyx.org/show_bug.cgi?id=3092
-                       pit_type new_pit;
-                       pos_type new_pos;
-                       int new_id;
-                       boost::tie(new_pit, new_pos, new_id) = moveToPosition(bm.bottom_pit, bm.bottom_pos, bm.top_id, bm.top_pos);
-                       if (bm.bottom_pit != new_pit || bm.bottom_pos != new_pos || bm.top_id != new_id )
-                               const_cast<BookmarksSection::Bookmark &>(bm).updatePos(new_pit, new_pos, new_id);
-               }
-               // current buffer is going to be switched-off, save cursor pos
-               // Ideally, the whole cursor stack should be saved, but session
-               // currently can only handle bottom (whole document) level pit and pos.
-               // That is to say, if a cursor is in a nested inset, it will be
-               // restore to the left of the top level inset.
-               LyX::ref().session().lastFilePos().save(FileName(buffer_->fileName()),
-                       boost::tie(cursor_.bottom().pit(), cursor_.bottom().pos()) );
-       }
-
-       // If we're quitting lyx, don't bother updating stuff
-       if (quitting) {
-               buffer_ = 0;
+/// Moves cursor to the next inset with one of the given codes.
+void gotoInset(BufferView * bv, vector<Inset_code> const & codes,
+              bool same_content)
+{
+       Cursor tmpcur = bv->cursor();
+       if (!findInset(tmpcur, codes, same_content)) {
+               bv->cursor().message(_("No more insets"));
                return;
        }
 
-       // 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_ = theBufferList().first();
-       } else {
-               // Set current buffer
-               buffer_ = b;
-       }
-
-       // Reset old cursor
-       cursor_ = Cursor(*this);
-       anchor_ref_ = 0;
-       offset_ref_ = 0;
-
-       if (buffer_) {
-               LYXERR(Debug::INFO) << BOOST_CURRENT_FUNCTION
-                                   << "Buffer addr: " << buffer_ << endl;
-               cursor_.push(buffer_->inset());
-               cursor_.resetAnchor();
-               buffer_->text().setCurrentFont(cursor_);
-               if (buffer_->getCursor().size() > 0 &&
-                   buffer_->getAnchor().size() > 0)
-               {
-                       cursor_.setCursor(buffer_->getAnchor().asDocIterator(&(buffer_->inset())));
-                       cursor_.resetAnchor();
-                       cursor_.setCursor(buffer_->getCursor().asDocIterator(&(buffer_->inset())));
-                       cursor_.setSelection();
-                       // do not set selection to the new buffer because we
-                       // only paste recent selection.
-               }
-       }
+       tmpcur.clearSelection();
+       bv->setCursor(tmpcur);
+}
 
-       if (buffer_)
-               updateMetrics(false);    
 
-       if (buffer_ && graphics::Previews::status() != LyXRC::PREVIEW_OFF)
-               graphics::Previews::get().generateBufferPreviews(*buffer_);
+/// Moves cursor to the next inset with given code.
+void gotoInset(BufferView * bv, Inset_code code, bool same_content)
+{
+       gotoInset(bv, vector<Inset_code>(1, code), same_content);
 }
 
 
-bool BufferView::loadLyXFile(FileName const & filename, bool tolastfiles)
-{
-       // File already open?
-       if (theBufferList().exists(filename.absFilename())) {
-               docstring const file = makeDisplayPath(filename.absFilename(), 20);
-               docstring text = bformat(_("The document %1$s is already "
-                                                    "loaded.\n\nDo you want to revert "
-                                                    "to the saved version?"), file);
-               int const ret = Alert::prompt(_("Revert to saved document?"),
-                       text, 0, 1,  _("&Revert"), _("&Switch to document"));
 
-               if (ret != 0) {
-                       setBuffer(theBufferList().getBuffer(filename.absFilename()));
-                       return true;
-               }
-               // FIXME: should be LFUN_REVERT
-               if (!theBufferList().close(theBufferList().getBuffer(filename.absFilename()), false))
-                       return false;
-               // Fall through to new load. (Asger)
-               buffer_ = 0;
-       }
+/// the type of outline operation
+enum OutlineOp {
+       OutlineUp, // Move this header with text down
+       OutlineDown,   // Move this header with text up
+       OutlineIn, // Make this header deeper
+       OutlineOut // Make this header shallower
+};
 
-       Buffer * b = 0;
 
-       if (isFileReadable(filename)) {
-               b = theBufferList().newBuffer(filename.absFilename());
-               if (!lyx::loadLyXFile(b, filename)) {
-                       theBufferList().release(b);
-                       return false;
+void outline(OutlineOp mode, Cursor & cur)
+{
+       Buffer & buf = cur.buffer();
+       pit_type & pit = cur.pit();
+       ParagraphList & pars = buf.text().paragraphs();
+       ParagraphList::iterator bgn = pars.begin();
+       // The first paragraph of the area to be copied:
+       ParagraphList::iterator start = boost::next(bgn, pit);
+       // The final paragraph of area to be copied:
+       ParagraphList::iterator finish = start;
+       ParagraphList::iterator end = pars.end();
+
+       TextClass::const_iterator lit =
+               buf.params().getTextClass().begin();
+       TextClass::const_iterator const lend =
+               buf.params().getTextClass().end();
+
+       int const thistoclevel = start->layout()->toclevel;
+       int toclevel;
+       switch (mode) {
+               case OutlineUp: {
+                       // Move out (down) from this section header
+                       if (finish != end)
+                               ++finish;
+                       // Seek the one (on same level) below
+                       for (; finish != end; ++finish) {
+                               toclevel = finish->layout()->toclevel;
+                               if (toclevel != Layout::NOT_IN_TOC
+                                   && toclevel <= thistoclevel) {
+                                       break;
+                               }
+                       }
+                       ParagraphList::iterator dest = start;
+                       // Move out (up) from this header
+                       if (dest == bgn)
+                               break;
+                       // Search previous same-level header above
+                       do {
+                               --dest;
+                               toclevel = dest->layout()->toclevel;
+                       } while(dest != bgn
+                               && (toclevel == Layout::NOT_IN_TOC
+                                   || toclevel > thistoclevel));
+                       // Not found; do nothing
+                       if (toclevel == Layout::NOT_IN_TOC || toclevel > thistoclevel)
+                               break;
+                       pit_type const newpit = std::distance(bgn, dest);
+                       pit_type const len = std::distance(start, finish);
+                       pit_type const deletepit = pit + len;
+                       recordUndo(cur, Undo::ATOMIC, newpit, deletepit - 1);
+                       pars.insert(dest, start, finish);
+                       start = boost::next(pars.begin(), deletepit);
+                       pit = newpit;
+                       pars.erase(start, finish);
+                       break;
                }
-       } else {
-               docstring text = bformat(_("The document %1$s does not yet "
-                                                    "exist.\n\nDo you want to create "
-                                                    "a new document?"), from_utf8(filename.absFilename()));
-               int const ret = Alert::prompt(_("Create new document?"),
-                        text, 0, 1, _("&Create"), _("Cancel"));
-
-               if (ret == 0) {
-                       b = newFile(filename.absFilename(), string(), true);
-                       if (!b)
-                               return false;
-               } else
-                       return false;
-       }
-
-       setBuffer(b);
-       // Send the "errors" signal in case of parsing errors
-       b->errors("Parse");
-
-       // Update the labels and section numbering.
-       updateLabels(*buffer_);
-       // scroll to the position when the file was last closed
-       if (lyxrc.use_lastfilepos) {
-               pit_type pit;
-               pos_type pos;
-               boost::tie(pit, pos) = LyX::ref().session().lastFilePos().load(filename);
-               // if successfully move to pit (returned par_id is not zero), update metrics and reset font
-               if (moveToPosition(pit, pos, 0, 0).get<1>()) {
-                       if (fitCursor())
-                               updateMetrics(false);
-                       buffer_->text().setCurrentFont(cursor_);
+               case OutlineDown: {
+                       // Go down out of current header:
+                       if (finish != end)
+                               ++finish;
+                       // Find next same-level header:
+                       for (; finish != end; ++finish) {
+                               toclevel = finish->layout()->toclevel;
+                               if (toclevel != Layout::NOT_IN_TOC && toclevel <= thistoclevel)
+                                       break;
+                       }
+                       ParagraphList::iterator dest = finish;
+                       // Go one down from *this* header:
+                       if (dest != end)
+                               ++dest;
+                       else
+                               break;
+                       // Go further down to find header to insert in front of:
+                       for (; dest != end; ++dest) {
+                               toclevel = dest->layout()->toclevel;
+                               if (toclevel != Layout::NOT_IN_TOC && toclevel <= thistoclevel)
+                                       break;
+                       }
+                       // One such was found:
+                       pit_type newpit = std::distance(bgn, dest);
+                       pit_type const len = std::distance(start, finish);
+                       recordUndo(cur, Undo::ATOMIC, pit, newpit - 1);
+                       pars.insert(dest, start, finish);
+                       start = boost::next(bgn, pit);
+                       pit = newpit - len;
+                       pars.erase(start, finish);
+                       break;
                }
+               case OutlineIn:
+                       recordUndo(cur);
+                       for (; lit != lend; ++lit) {
+                               if ((*lit)->toclevel == thistoclevel + 1 &&
+                                   start->layout()->labeltype == (*lit)->labeltype) {
+                                       start->layout((*lit));
+                                       break;
+                               }
+                       }
+                       break;
+               case OutlineOut:
+                       recordUndo(cur);
+                       for (; lit != lend; ++lit) {
+                               if ((*lit)->toclevel == thistoclevel - 1 &&
+                                   start->layout()->labeltype == (*lit)->labeltype) {
+                                       start->layout((*lit));
+                                       break;
+                               }
+                       }
+                       break;
+               default:
+                       break;
        }
+}
 
-       if (tolastfiles)
-               LyX::ref().session().lastFiles().add(FileName(b->fileName()));
+} // anon namespace
 
-       return true;
+
+/////////////////////////////////////////////////////////////////////
+//
+// BufferView
+//
+/////////////////////////////////////////////////////////////////////
+
+
+BufferView::BufferView(Buffer & buf)
+       : width_(0), height_(0), buffer_(buf), wh_(0),
+         cursor_(*this),
+         multiparsel_cache_(false), anchor_ref_(0), offset_ref_(0),
+         need_centering_(false), intl_(new Intl), last_inset_(0),
+         gui_(0)
+{
+       xsel_cache_.set = false;
+       intl_->initKeyMapper(lyxrc.use_kbmap);
+
+       cursor_.push(buffer_.inset());
+       cursor_.resetAnchor();
+       cursor_.setCurrentFont();
+
+       if (graphics::Previews::status() != LyXRC::PREVIEW_OFF)
+               graphics::Previews::get().generateBufferPreviews(buffer_);
 }
 
 
-void BufferView::resize()
+BufferView::~BufferView()
 {
-       if (!buffer_)
-               return;
+       // current buffer is going to be switched-off, save cursor pos
+       // Ideally, the whole cursor stack should be saved, but session
+       // currently can only handle bottom (whole document) level pit and pos.
+       // That is to say, if a cursor is in a nested inset, it will be
+       // restore to the left of the top level inset.
+       LyX::ref().session().lastFilePos().save(
+               support::FileName(buffer_.fileName()),
+               boost::tie(cursor_.bottom().pit(), cursor_.bottom().pos()) );
+}
 
-       LYXERR(Debug::DEBUG) << BOOST_CURRENT_FUNCTION << endl;
 
-       updateMetrics(false);
-       switchKeyMap();
+Buffer & BufferView::buffer()
+{
+       return buffer_;
+}
+
+
+Buffer const & BufferView::buffer() const
+{
+       return buffer_;
 }
 
 
 bool BufferView::fitCursor()
 {
-       if (bv_funcs::status(this, cursor_) == bv_funcs::CUR_INSIDE) {
+       if (cursorStatus(cursor_) == CUR_INSIDE) {
                frontend::FontMetrics const & fm =
                        theFontMetrics(cursor_.getFont());
                int const asc = fm.maxAscent();
                int const des = fm.maxDescent();
-               Point const p = bv_funcs::getPos(*this, cursor_, cursor_.boundary());
+               Point const p = getPos(cursor_, cursor_.boundary());
                if (p.y_ - asc >= 0 && p.y_ + des < height_)
                        return false;
        }
@@ -348,17 +427,11 @@ bool BufferView::update(Update::flags flags)
                << "[fitcursor = " << (flags & Update::FitCursor)
                << ", forceupdate = " << (flags & Update::Force)
                << ", singlepar = " << (flags & Update::SinglePar)
-               << "]  buffer: " << buffer_ << endl;
-
-       // Check needed to survive LyX startup
-       if (!buffer_)
-               return false;
-
-       LYXERR(Debug::WORKAREA) << "BufferView::update" << std::endl;
+               << "]  buffer: " << &buffer_ << endl;
 
        // Update macro store
        if (!(cursor().inMathed() && cursor().inMacroMode()))
-               buffer_->buildMacros();
+               buffer_.buildMacros();
 
        // Now do the first drawing step if needed. This consists on updating
        // the CoordCache in updateMetrics().
@@ -376,7 +449,7 @@ bool BufferView::update(Update::flags flags)
                return true;
        }
 
-       if (flags == Update::FitCursor 
+       if (flags == Update::FitCursor
                || flags == (Update::Decoration | Update::FitCursor)) {
                bool const fit_cursor = fitCursor();
                // tell the frontend to update the screen if needed.
@@ -400,8 +473,18 @@ bool BufferView::update(Update::flags flags)
        bool const single_par = !full_metrics;
        updateMetrics(single_par);
 
-       if (flags & Update::FitCursor && fitCursor())
-               updateMetrics(false);
+       if (flags & Update::FitCursor) {
+               //FIXME: updateMetrics() does not update paragraph position
+               // This is done at draw() time. So we need a redraw!
+               buffer_.changed();
+               if (fitCursor())
+                       updateMetrics(false);
+               else
+                       // The screen has already been updated thanks to the
+                       // 'buffer_.changed()' call three line above. So no need
+                       // to redraw again.
+                       return false;
+       }
 
        // tell the frontend to update the screen.
        return true;
@@ -410,14 +493,7 @@ bool BufferView::update(Update::flags flags)
 
 void BufferView::updateScrollbar()
 {
-       if (!buffer_) {
-               LYXERR(Debug::DEBUG) << BOOST_CURRENT_FUNCTION
-                                    << " no text in updateScrollbar" << endl;
-               scrollbarParameters_.reset();
-               return;
-       }
-
-       Text & t = buffer_->text();
+       Text & t = buffer_.text();
        TextMetrics & tm = text_metrics_[&t];
 
        int const parsize = int(t.paragraphs().size() - 1);
@@ -482,10 +558,7 @@ void BufferView::scrollDocView(int value)
        LYXERR(Debug::GUI) << BOOST_CURRENT_FUNCTION
                           << "[ value = " << value << "]" << endl;
 
-       if (!buffer_)
-               return;
-
-       Text & t = buffer_->text();
+       Text & t = buffer_.text();
        TextMetrics & tm = text_metrics_[&t];
 
        float const bar = value / float(wh_ * t.paragraphs().size());
@@ -498,44 +571,40 @@ void BufferView::scrollDocView(int value)
        int const h = tm.parMetrics(anchor_ref_).height();
        offset_ref_ = int((bar * t.paragraphs().size() - anchor_ref_) * h);
        updateMetrics(false);
+       buffer_.changed();
 }
 
 
 void BufferView::setCursorFromScrollbar()
 {
-       if (!buffer_)
-               return;
-
-       Text & t = buffer_->text();
+       TextMetrics & tm = text_metrics_[&buffer_.text()];
 
        int const height = 2 * defaultRowHeight();
        int const first = height;
        int const last = height_ - height;
        Cursor & cur = cursor_;
 
-       bv_funcs::CurStatus st = bv_funcs::status(this, cur);
-
-       switch (st) {
-       case bv_funcs::CUR_ABOVE:
-               // We reset the cursor because bv_funcs::status() does not
+       switch (cursorStatus(cur)) {
+       case CUR_ABOVE:
+               // We reset the cursor because cursorStatus() does not
                // work when the cursor is within mathed.
-               cur.reset(buffer_->inset());
-               t.setCursorFromCoordinates(cur, 0, first);
+               cur.reset(buffer_.inset());
+               tm.setCursorFromCoordinates(cur, 0, first);
                cur.clearSelection();
                break;
-       case bv_funcs::CUR_BELOW:
-               // We reset the cursor because bv_funcs::status() does not
+       case CUR_BELOW:
+               // We reset the cursor because cursorStatus() does not
                // work when the cursor is within mathed.
-               cur.reset(buffer_->inset());
-               t.setCursorFromCoordinates(cur, 0, last);
+               cur.reset(buffer_.inset());
+               tm.setCursorFromCoordinates(cur, 0, last);
                cur.clearSelection();
                break;
-       case bv_funcs::CUR_INSIDE:
-               int const y = bv_funcs::getPos(*this, cur, cur.boundary()).y_;
+       case CUR_INSIDE:
+               int const y = 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);
+                       cur.reset(buffer_.inset());
+                       tm.setCursorFromCoordinates(cur, 0, newy);
                }
        }
 }
@@ -551,14 +620,27 @@ Change const BufferView::getCurrentChange() const
 }
 
 
+// this could be used elsewhere as well?
+// FIXME: This does not work within mathed!
+CursorStatus BufferView::cursorStatus(DocIterator const & dit) const
+{
+       Point const p = getPos(dit, dit.boundary());
+       if (p.y_ < 0)
+               return CUR_ABOVE;
+       if (p.y_ > workHeight())
+               return CUR_BELOW;
+       return CUR_INSIDE;
+}
+
+
 void BufferView::saveBookmark(unsigned int idx)
 {
        // tenatively save bookmark, id and pos will be used to
        // acturately locate a bookmark in a 'live' lyx session.
-       // pit and pos will be updated with bottom level pit/pos 
+       // pit and pos will be updated with bottom level pit/pos
        // when lyx exits.
        LyX::ref().session().bookmarks().save(
-               FileName(buffer_->fileName()),
+               FileName(buffer_.fileName()),
                cursor_.bottom().pit(),
                cursor_.bottom().pos(),
                cursor_.paragraph().id(),
@@ -571,65 +653,76 @@ void BufferView::saveBookmark(unsigned int idx)
 }
 
 
-boost::tuple<pit_type, pos_type, int> BufferView::moveToPosition(pit_type bottom_pit, pos_type bottom_pos,
+bool BufferView::moveToPosition(pit_type bottom_pit, pos_type bottom_pos,
        int top_id, pos_type top_pos)
 {
+       bool success = false;
+       DocIterator doc_it;
+
        cursor_.clearSelection();
 
        // if a valid par_id is given, try it first
        // This is the case for a 'live' bookmark when unique paragraph ID
        // is used to track bookmarks.
        if (top_id > 0) {
-               ParIterator par = buffer_->getParFromID(top_id);
-               if (par != buffer_->par_iterator_end()) {
-                       DocIterator dit = makeDocIterator(par, min(par->size(), top_pos));
-                       // Some slices of the iterator may not be reachable (e.g. closed collapsable inset)
-                       // so the dociterator may need to be shortened. Otherwise, setCursor may
-                       // crash lyx when the cursor can not be set to these insets.
-                       size_t const n = dit.depth();
+               ParIterator par = buffer_.getParFromID(top_id);
+               if (par != buffer_.par_iterator_end()) {
+                       doc_it = makeDocIterator(par, min(par->size(), top_pos));
+                       // Some slices of the iterator may not be
+                       // reachable (e.g. closed collapsable inset)
+                       // so the dociterator may need to be
+                       // shortened. Otherwise, setCursor may crash
+                       // lyx when the cursor can not be set to these
+                       // insets.
+                       size_t const n = doc_it.depth();
                        for (size_t i = 0; i < n; ++i)
-                               if (dit[i].inset().editable() != Inset::HIGHLY_EDITABLE) {
-                                       dit.resize(i);
+                               if (doc_it[i].inset().editable() != Inset::HIGHLY_EDITABLE) {
+                                       doc_it.resize(i);
                                        break;
                                }
-                       setCursor(dit);
-                       // Note: return bottom (document) level pit.
-                       return boost::make_tuple(cursor_.bottom().pit(), cursor_.bottom().pos(), top_id);
+                       success = true;
                }
        }
+
        // if top_id == 0, or searching through top_id failed
-       // This is the case for a 'restored' bookmark when only bottom 
+       // This is the case for a 'restored' bookmark when only bottom
        // (document level) pit was saved. Because of this, bookmark
        // restoration is inaccurate. If a bookmark was within an inset,
        // it will be restored to the left of the outmost inset that contains
        // the bookmark.
-       if (static_cast<size_t>(bottom_pit) < buffer_->paragraphs().size()) {
-               ParIterator it = buffer_->par_iterator_begin();
-               ParIterator const end = buffer_->par_iterator_end();
-               for (; it != end; ++it)
-                       if (it.pit() == bottom_pit) {
-                               // restored pos may be bigger than it->size
-                               setCursor(makeDocIterator(it, min(bottom_pos, it->size())));
-                               return boost::make_tuple(bottom_pit, bottom_pos, it->id());
-                       }
+       if (static_cast<size_t>(bottom_pit) < buffer_.paragraphs().size()) {
+               doc_it = doc_iterator_begin(buffer_.inset());
+               doc_it.pit() = bottom_pit;
+               doc_it.pos() = min(bottom_pos, doc_it.paragraph().size());
+               success = true;
+       }
+
+       if (success) {
+               // Note: only bottom (document) level pit is set.
+               setCursor(doc_it);
+               // set the current font.
+               cursor_.setCurrentFont();
+               // center the screen on this new position.
+               center();
        }
-       // both methods fail
-       return boost::make_tuple(pit_type(0), pos_type(0), 0);
+
+       return success;
 }
 
 
-void BufferView::switchKeyMap()
+void BufferView::translateAndInsert(char_type c, Text * t, Cursor & cur)
 {
-       if (!lyxrc.rtl_support)
-               return;
-
-       if (cursor_.innerText()->real_current_font.isRightToLeft()) {
-               if (intl_->keymap == Intl::PRIMARY)
-                       intl_->keyMapSec();
-       } else {
-               if (intl_->keymap == Intl::SECONDARY)
-                       intl_->keyMapPrim();
+       if (lyxrc.rtl_support) {
+               if (cursor_.real_current_font.isRightToLeft()) {
+                       if (intl_->keymap == Intl::PRIMARY)
+                               intl_->keyMapSec();
+               } else {
+                       if (intl_->keymap == Intl::SECONDARY)
+                               intl_->keyMapPrim();
+               }
        }
+
+       intl_->getTransManager().translateAndInsert(c, t, cur);
 }
 
 
@@ -639,16 +732,31 @@ int BufferView::workWidth() const
 }
 
 
-void BufferView::center()
+void BufferView::updateOffsetRef()
 {
+       // No need to update offset_ref_ in this case.
+       if (!need_centering_)
+               return;
+
+       // We are not properly started yet, delay until resizing is
+       // done.
+       if (height_ == 0)
+               return;
+
        CursorSlice & bot = cursor_.bottom();
        TextMetrics & tm = text_metrics_[bot.text()];
-       pit_type const pit = bot.pit();
-       tm.redoParagraph(pit);
-       ParagraphMetrics const & pm = tm.parMetrics(pit);
-       anchor_ref_ = pit;
-       offset_ref_ = bv_funcs::coordOffset(*this, cursor_, cursor_.boundary()).y_
-               + pm.ascent() - height_ / 2;
+       ParagraphMetrics const & pm = tm.parMetrics(bot.pit());
+       int y = coordOffset(cursor_, cursor_.boundary()).y_;
+       offset_ref_ = y + pm.ascent() - height_ / 2;
+
+       need_centering_ = false;
+}
+
+
+void BufferView::center()
+{
+       anchor_ref_ = cursor_.bottom().pit();
+       need_centering_ = true;
 }
 
 
@@ -656,20 +764,22 @@ FuncStatus BufferView::getStatus(FuncRequest const & cmd)
 {
        FuncStatus flag;
 
+       Cursor & cur = cursor_;
+
        switch (cmd.action) {
 
        case LFUN_UNDO:
-               flag.enabled(!buffer_->undostack().empty());
+               flag.enabled(!buffer_.undostack().empty());
                break;
        case LFUN_REDO:
-               flag.enabled(!buffer_->redostack().empty());
+               flag.enabled(!buffer_.redostack().empty());
                break;
        case LFUN_FILE_INSERT:
        case LFUN_FILE_INSERT_PLAINTEXT_PARA:
        case LFUN_FILE_INSERT_PLAINTEXT:
        case LFUN_BOOKMARK_SAVE:
                // FIXME: Actually, these LFUNS should be moved to Text
-               flag.enabled(cursor_.inTexted());
+               flag.enabled(cur.inTexted());
                break;
        case LFUN_FONT_STATE:
        case LFUN_LABEL_INSERT:
@@ -696,18 +806,18 @@ FuncStatus BufferView::getStatus(FuncRequest const & cmd)
 
        case LFUN_LABEL_GOTO: {
                flag.enabled(!cmd.argument().empty()
-                   || getInsetByCode<InsetRef>(cursor_, Inset::REF_CODE));
+                   || getInsetByCode<InsetRef>(cur, Inset::REF_CODE));
                break;
        }
 
        case LFUN_CHANGES_TRACK:
                flag.enabled(true);
-               flag.setOnOff(buffer_->params().trackChanges);
+               flag.setOnOff(buffer_.params().trackChanges);
                break;
 
        case LFUN_CHANGES_OUTPUT:
-               flag.enabled(buffer_);
-               flag.setOnOff(buffer_->params().outputChanges);
+               flag.enabled(true);
+               flag.setOnOff(buffer_.params().outputChanges);
                break;
 
        case LFUN_CHANGES_MERGE:
@@ -718,14 +828,26 @@ FuncStatus BufferView::getStatus(FuncRequest const & cmd)
                // In principle, these command should only be enabled if there
                // is a change in the document. However, without proper
                // optimizations, this will inevitably result in poor performance.
-               flag.enabled(buffer_);
+               flag.enabled(true);
                break;
 
        case LFUN_BUFFER_TOGGLE_COMPRESSION: {
-               flag.setOnOff(buffer_->params().compressed);
+               flag.setOnOff(buffer_.params().compressed);
                break;
        }
 
+       case LFUN_SCREEN_UP:
+       case LFUN_SCREEN_DOWN:
+               flag.enabled(true);
+               break;
+
+       // FIXME: LFUN_SCREEN_DOWN_SELECT should be removed from
+       // everywhere else before this can enabled:
+       case LFUN_SCREEN_UP_SELECT:
+       case LFUN_SCREEN_DOWN_SELECT:
+               flag.enabled(false);
+               break;
+
        default:
                flag.enabled(false);
        }
@@ -748,10 +870,6 @@ Update::flags BufferView::dispatch(FuncRequest const & cmd)
                << " button[" << cmd.button() << ']'
                << endl;
 
-       // FIXME: this should not be possible.
-       if (!buffer_)
-               return Update::None;
-
        Cursor & cur = cursor_;
        // Default Update flags.
        Update::flags updateFlags = Update::Force | Update::FitCursor;
@@ -765,7 +883,6 @@ Update::flags BufferView::dispatch(FuncRequest const & cmd)
                        cur.message(_("No further undo information"));
                        updateFlags = Update::None;
                }
-               switchKeyMap();
                break;
 
        case LFUN_REDO:
@@ -775,7 +892,6 @@ Update::flags BufferView::dispatch(FuncRequest const & cmd)
                        cur.message(_("No further redo information"));
                        updateFlags = Update::None;
                }
-               switchKeyMap();
                break;
 
        case LFUN_FILE_INSERT:
@@ -785,12 +901,12 @@ Update::flags BufferView::dispatch(FuncRequest const & cmd)
 
        case LFUN_FILE_INSERT_PLAINTEXT_PARA:
                // FIXME UNICODE
-               insertPlaintextFile(this, to_utf8(cmd.argument()), true);
+               insertPlaintextFile(to_utf8(cmd.argument()), true);
                break;
 
        case LFUN_FILE_INSERT_PLAINTEXT:
                // FIXME UNICODE
-               insertPlaintextFile(this, to_utf8(cmd.argument()), false);
+               insertPlaintextFile(to_utf8(cmd.argument()), false);
                break;
 
        case LFUN_FONT_STATE:
@@ -822,7 +938,9 @@ Update::flags BufferView::dispatch(FuncRequest const & cmd)
        case LFUN_PARAGRAPH_GOTO: {
                int const id = convert<int>(to_utf8(cmd.argument()));
                int i = 0;
-               for (Buffer * b = buffer_; i == 0 || b != buffer_; b = theBufferList().next(b)) {
+               for (Buffer * b = &buffer_; i == 0 || b != &buffer_;
+                       b = theBufferList().next(b)) {
+
                        ParIterator par = b->getParFromID(id);
                        if (par == b->par_iterator_end()) {
                                LYXERR(Debug::INFO)
@@ -834,10 +952,9 @@ Update::flags BufferView::dispatch(FuncRequest const & cmd)
                                        << " found in buffer `"
                                        << b->fileName() << "'." << endl;
 
-                               if (b == buffer_) {
+                               if (b == &buffer_) {
                                        // Set the cursor
                                        setCursor(makeDocIterator(par, 0));
-                                       switchKeyMap();
                                } else {
                                        // Switch to other buffer view and resend cmd
                                        theLyXFunc().dispatch(FuncRequest(
@@ -853,55 +970,59 @@ Update::flags BufferView::dispatch(FuncRequest const & cmd)
        }
 
        case LFUN_OUTLINE_UP:
-               toc::outline(toc::Up, cursor_);
+               outline(OutlineUp, cursor_);
                cursor_.text()->setCursor(cursor_, cursor_.pit(), 0);
-               updateLabels(*buffer_);
+               updateLabels(buffer_);
                break;
        case LFUN_OUTLINE_DOWN:
-               toc::outline(toc::Down, cursor_);
+               outline(OutlineDown, cursor_);
                cursor_.text()->setCursor(cursor_, cursor_.pit(), 0);
-               updateLabels(*buffer_);
+               updateLabels(buffer_);
                break;
        case LFUN_OUTLINE_IN:
-               toc::outline(toc::In, cursor_);
-               updateLabels(*buffer_);
+               outline(OutlineIn, cursor_);
+               updateLabels(buffer_);
                break;
        case LFUN_OUTLINE_OUT:
-               toc::outline(toc::Out, cursor_);
-               updateLabels(*buffer_);
+               outline(OutlineOut, cursor_);
+               updateLabels(buffer_);
                break;
 
        case LFUN_NOTE_NEXT:
-               bv_funcs::gotoInset(this, Inset::NOTE_CODE, false);
+               gotoInset(this, Inset::NOTE_CODE, false);
                break;
 
        case LFUN_REFERENCE_NEXT: {
                vector<Inset_code> tmp;
                tmp.push_back(Inset::LABEL_CODE);
                tmp.push_back(Inset::REF_CODE);
-               bv_funcs::gotoInset(this, tmp, true);
+               gotoInset(this, tmp, true);
                break;
        }
 
        case LFUN_CHANGES_TRACK:
-               buffer_->params().trackChanges = !buffer_->params().trackChanges;
+               buffer_.params().trackChanges = !buffer_.params().trackChanges;
                break;
 
        case LFUN_CHANGES_OUTPUT:
-               buffer_->params().outputChanges = !buffer_->params().outputChanges;
-               if (buffer_->params().outputChanges) {
-                       if (!LaTeXFeatures::isAvailable("dvipost")) {
+               buffer_.params().outputChanges = !buffer_.params().outputChanges;
+               if (buffer_.params().outputChanges) {
+                       bool dvipost    = LaTeXFeatures::isAvailable("dvipost");
+                       bool xcolorsoul = LaTeXFeatures::isAvailable("soul") &&
+                                         LaTeXFeatures::isAvailable("xcolor");
+
+                       if (!dvipost && !xcolorsoul) {
                                Alert::warning(_("Changes not shown in LaTeX output"),
-                                              _("Changes will not be highlighted in LaTeX output, "
-                                                "because dvipost is not installed.\n"
-                                                "If you are familiar with TeX, consider redefining "
-                                                "\\lyxinserted and \\lyxdeleted in the LaTeX preamble."));
-                       } else {
+                                              _("Changes will not be highlighted in LaTeX output, "
+                                                "because neither dvipost nor xcolor/soul are installed.\n"
+                                                "Please install these packages or redefine "
+                                                "\\lyxadded and \\lyxdeleted in the LaTeX preamble."));
+                       } else if (!xcolorsoul) {
                                Alert::warning(_("Changes not shown in LaTeX output"),
-                                              _("Changes will not be highlighted in LaTeX output "
-                                                "when using pdflatex.\n"
-                                                "If you are familiar with TeX, consider redefining "
-                                                "\\lyxinserted and \\lyxdeleted in the LaTeX preamble."));
+                                              _("Changes will not be highlighted in LaTeX output "
+                                                "when using pdflatex, because xcolor and soul are not installed.\n"
+                                                "Please install both packages or redefine "
+                                                "\\lyxadded and \\lyxdeleted in the LaTeX preamble."));
                        }
                }
                break;
@@ -917,30 +1038,42 @@ Update::flags BufferView::dispatch(FuncRequest const & cmd)
 
        case LFUN_ALL_CHANGES_ACCEPT:
                // select complete document
-               cursor_.reset(buffer_->inset());
+               cursor_.reset(buffer_.inset());
                cursor_.selHandle(true);
-               buffer_->text().cursorBottom(cursor_);
+               buffer_.text().cursorBottom(cursor_);
                // accept everything in a single step to support atomic undo
-               buffer_->text().acceptOrRejectChanges(cursor_, Text::ACCEPT);
+               buffer_.text().acceptOrRejectChanges(cursor_, Text::ACCEPT);
                break;
 
        case LFUN_ALL_CHANGES_REJECT:
                // select complete document
-               cursor_.reset(buffer_->inset());
+               cursor_.reset(buffer_.inset());
                cursor_.selHandle(true);
-               buffer_->text().cursorBottom(cursor_);
+               buffer_.text().cursorBottom(cursor_);
                // reject everything in a single step to support atomic undo
                // Note: reject does not work recursively; the user may have to repeat the operation
-               buffer_->text().acceptOrRejectChanges(cursor_, Text::REJECT);
+               buffer_.text().acceptOrRejectChanges(cursor_, Text::REJECT);
                break;
 
        case LFUN_WORD_FIND:
                find(this, cmd);
                break;
 
-       case LFUN_WORD_REPLACE:
-               replace(this, cmd);
+       case LFUN_WORD_REPLACE: {
+               bool has_deleted = false;
+               if (cur.selection()) {
+                       DocIterator beg = cur.selectionBegin();
+                       DocIterator end = cur.selectionEnd();
+                       if (beg.pit() == end.pit()) {
+                               for (pos_type p = beg.pos() ; p < end.pos() ; ++p) {
+                                       if (cur.paragraph().isDeleted(p))
+                                               has_deleted = true;
+                               }
+                       }
+               }
+               replace(this, cmd, has_deleted);
                break;
+       }
 
        case LFUN_MARK_OFF:
                cur.clearSelection();
@@ -973,24 +1106,24 @@ Update::flags BufferView::dispatch(FuncRequest const & cmd)
 
        case LFUN_BIBTEX_DATABASE_ADD: {
                Cursor tmpcur = cursor_;
-               bv_funcs::findInset(tmpcur, Inset::BIBTEX_CODE, false);
+               findInset(tmpcur, Inset::BIBTEX_CODE, false);
                InsetBibtex * inset = getInsetByCode<InsetBibtex>(tmpcur,
                                                Inset::BIBTEX_CODE);
                if (inset) {
                        if (inset->addDatabase(to_utf8(cmd.argument())))
-                               buffer_->updateBibfilesCache();
+                               buffer_.updateBibfilesCache();
                }
                break;
        }
 
        case LFUN_BIBTEX_DATABASE_DEL: {
                Cursor tmpcur = cursor_;
-               bv_funcs::findInset(tmpcur, Inset::BIBTEX_CODE, false);
+               findInset(tmpcur, Inset::BIBTEX_CODE, false);
                InsetBibtex * inset = getInsetByCode<InsetBibtex>(tmpcur,
                                                Inset::BIBTEX_CODE);
                if (inset) {
                        if (inset->delDatabase(to_utf8(cmd.argument())))
-                               buffer_->updateBibfilesCache();
+                               buffer_.updateBibfilesCache();
                }
                break;
        }
@@ -1001,8 +1134,8 @@ Update::flags BufferView::dispatch(FuncRequest const & cmd)
                        from = cur.selectionBegin();
                        to = cur.selectionEnd();
                } else {
-                       from = doc_iterator_begin(buffer_->inset());
-                       to = doc_iterator_end(buffer_->inset());
+                       from = doc_iterator_begin(buffer_.inset());
+                       to = doc_iterator_end(buffer_.inset());
                }
                int const count = countWords(from, to);
                docstring message;
@@ -1027,7 +1160,7 @@ Update::flags BufferView::dispatch(FuncRequest const & cmd)
 
        case LFUN_BUFFER_TOGGLE_COMPRESSION:
                // turn compression on/off
-               buffer_->params().compressed = !buffer_->params().compressed;
+               buffer_.params().compressed = !buffer_.params().compressed;
                break;
 
        case LFUN_NEXT_INSET_TOGGLE: {
@@ -1036,12 +1169,16 @@ Update::flags BufferView::dispatch(FuncRequest const & cmd)
                // if there is an inset at cursor, see whether it
                // wants to toggle.
                Inset * inset = cur.nextInset();
-               if (inset && inset->isActive()) {
-                       Cursor tmpcur = cur;
-                       tmpcur.pushLeft(*inset);
-                       inset->dispatch(tmpcur, tmpcmd);
-                       if (tmpcur.result().dispatched()) {
-                               cur.dispatched();
+               if (inset) {
+                       if (inset->isActive()) {
+                               Cursor tmpcur = cur;
+                               tmpcur.pushLeft(*inset);
+                               inset->dispatch(tmpcur, tmpcmd);
+                               if (tmpcur.result().dispatched()) {
+                                       cur.dispatched();
+                               }
+                       } else if (inset->editable() == Inset::IS_EDITABLE) {
+                               inset->edit(cur, true);
                        }
                }
                // if it did not work, try the underlying inset.
@@ -1054,6 +1191,47 @@ Update::flags BufferView::dispatch(FuncRequest const & cmd)
                break;
        }
 
+       case LFUN_SCREEN_UP:
+       case LFUN_SCREEN_DOWN: {
+               Point p = getPos(cur, cur.boundary());
+               if (p.y_ < 0 || p.y_ > height_) {
+                       // The cursor is off-screen so recenter before proceeding.
+                       center();
+                       updateMetrics(false);
+                       //FIXME: updateMetrics() does not update paragraph position
+                       // This is done at draw() time. So we need a redraw!
+                       buffer_.changed();
+                       p = getPos(cur, cur.boundary());
+               }
+               scroll(cmd.action == LFUN_SCREEN_UP? - height_ : height_);
+               cur.reset(buffer_.inset());
+               text_metrics_[&buffer_.text()].editXY(cur, p.x_, p.y_);
+               //FIXME: what to do with cur.x_target()?
+               finishUndo();
+               // The metrics are already up to date. see scroll()
+               updateFlags = Update::None;
+               break;
+       }
+
+       case LFUN_SCREEN_UP_SELECT:
+       case LFUN_SCREEN_DOWN_SELECT: {
+               cur.selHandle(true);
+               size_t initial_depth = cur.depth();
+               Point const p = getPos(cur, cur.boundary());
+               scroll(cmd.action == LFUN_SCREEN_UP_SELECT? - height_ : height_);
+               // FIXME: We need to verify if the cursor stayed within an inset...
+               //cur.reset(buffer_.inset());
+               text_metrics_[&buffer_.text()].editXY(cur, p.x_, p.y_);
+               finishUndo();
+               while (cur.depth() > initial_depth) {
+                       cur.forwardInset();
+               }
+               // FIXME: we need to do a redraw again because of the selection
+               buffer_.changed();
+               updateFlags = Update::Force | Update::FitCursor;
+               break;
+       }
+
        default:
                updateFlags = Update::None;
        }
@@ -1064,9 +1242,6 @@ Update::flags BufferView::dispatch(FuncRequest const & cmd)
 
 docstring const BufferView::requestSelection()
 {
-       if (!buffer_)
-               return docstring();
-
        Cursor & cur = cursor_;
 
        if (!cur.selection()) {
@@ -1089,83 +1264,58 @@ docstring const BufferView::requestSelection()
 
 void BufferView::clearSelection()
 {
-       if (buffer_) {
-               cursor_.clearSelection();
-               // Clear the selection buffer. Otherwise a subsequent
-               // middle-mouse-button paste would use the selection buffer,
-               // not the more current external selection.
-               cap::clearSelection();
-               xsel_cache_.set = false;
-               // The buffer did not really change, but this causes the
-               // redraw we need because we cleared the selection above.
-               buffer_->changed();
-       }
+       cursor_.clearSelection();
+       // Clear the selection buffer. Otherwise a subsequent
+       // middle-mouse-button paste would use the selection buffer,
+       // not the more current external selection.
+       cap::clearSelection();
+       xsel_cache_.set = false;
+       // The buffer did not really change, but this causes the
+       // redraw we need because we cleared the selection above.
+       buffer_.changed();
 }
 
 
-void BufferView::workAreaResize(int width, int height)
+void BufferView::resize(int width, int height)
 {
        // Update from work area
        width_ = width;
        height_ = height;
 
-       // The complete text metrics will be redone.
-       text_metrics_.clear();
-
-       if (buffer_)
-               resize();
+       updateMetrics(false);
 }
 
 
 Inset const * BufferView::getCoveringInset(Text const & text, int x, int y)
 {
-       pit_type pit = text.getPitNearY(*this, y);
-       BOOST_ASSERT(pit != -1);
-       Paragraph const & par = text.getPar(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) {
-               Inset * const inset = iit->inset;
-               if (inset->covers(*this, x, y)) {
-                       if (!inset->descendable())
-                               // No need to go further down if the inset is not 
-                               // descendable.
-                               return inset;
-
-                       size_t cell_number = inset->nargs();
-                       // Check all the inner cell.
-                       for (size_t i = 0; i != cell_number; ++i) {
-                               Text const * inner_text = inset->getText(i);
-                               if (inner_text) {
-                                       // Try deeper.
-                                       Inset const * inset_deeper = 
-                                               getCoveringInset(*inner_text, x, y);
-                                       if (inset_deeper)
-                                               return inset_deeper;
-                               }
-                       }
-
-                       LYXERR(Debug::DEBUG)
-                               << BOOST_CURRENT_FUNCTION
-                               << ": Hit inset: " << inset << endl;
-                       return inset;
+       TextMetrics & tm = text_metrics_[&text];
+       Inset * inset = tm.checkInsetHit(x, y);
+       if (!inset)
+               return 0;
+
+       if (!inset->descendable())
+               // No need to go further down if the inset is not
+               // descendable.
+               return inset;
+
+       size_t cell_number = inset->nargs();
+       // Check all the inner cell.
+       for (size_t i = 0; i != cell_number; ++i) {
+               Text const * inner_text = inset->getText(i);
+               if (inner_text) {
+                       // Try deeper.
+                       Inset const * inset_deeper =
+                               getCoveringInset(*inner_text, x, y);
+                       if (inset_deeper)
+                               return inset_deeper;
                }
        }
-       LYXERR(Debug::DEBUG)
-               << BOOST_CURRENT_FUNCTION
-               << ": No inset hit. " << endl;
-       return 0;
+
+       return inset;
 }
 
 
-bool BufferView::workAreaDispatch(FuncRequest const & cmd0)
+void BufferView::mouseEventDispatch(FuncRequest const & cmd0)
 {
        //lyxerr << BOOST_CURRENT_FUNCTION << "[ cmd0 " << cmd0 << "]" << endl;
 
@@ -1173,12 +1323,8 @@ bool BufferView::workAreaDispatch(FuncRequest const & cmd0)
        // LFUN_FILE_OPEN generated by drag-and-drop.
        FuncRequest cmd = cmd0;
 
-       // E.g. Qt mouse press when no buffer
-       if (!buffer_)
-               return false;
-
        Cursor cur(*this);
-       cur.push(buffer_->inset());
+       cur.push(buffer_.inset());
        cur.selection() = cursor_.selection();
 
        // Either the inset under the cursor or the
@@ -1186,15 +1332,15 @@ bool BufferView::workAreaDispatch(FuncRequest const & cmd0)
 
        // make sure we stay within the screen...
        cmd.y = min(max(cmd.y, -1), height_);
-       
+
        if (cmd.action == LFUN_MOUSE_MOTION && cmd.button() == mouse_button::none) {
-               
+
                // Get inset under mouse, if there is one.
-               Inset const * covering_inset = 
-                       getCoveringInset(buffer_->text(), cmd.x, cmd.y);
+               Inset const * covering_inset =
+                       getCoveringInset(buffer_.text(), cmd.x, cmd.y);
                if (covering_inset == last_inset_)
                        // Same inset, no need to do anything...
-                       return false;
+                       return;
 
                bool need_redraw = false;
                // const_cast because of setMouseHover().
@@ -1206,42 +1352,35 @@ bool BufferView::workAreaDispatch(FuncRequest const & cmd0)
                        // Highlighted the newly hovered inset (if any).
                        need_redraw |= inset->setMouseHover(true);
                last_inset_ = inset;
+               if (!need_redraw)
+                       return;
 
                // if last metrics update was in singlepar mode, WorkArea::redraw() will
                // not expose the button for redraw. We adjust here the metrics dimension
-               // to enable a full redraw.
-               // FIXME: It is possible to redraw only the area around the button!
-               if (need_redraw 
-                       && metrics_info_.update_strategy == SingleParUpdate) {
-                       // FIXME: It should be possible to redraw only the area around 
-                       // the button by doing this:
-                       //
-                       //metrics_info_.singlepar = false;
-                       //metrics_info_.y1 = ymin of button;
-                       //metrics_info_.y2 = ymax of button;
-                       //
-                       // Unfortunately, rowpainter.cpp:paintText() does not distinguish
-                       // between background updates and text updates. So we use the hammer
-                       // solution for now. We could also avoid the updateMetrics() below
-                       // by using the first and last pit of the CoordCache. Have a look
-                       // at Text::getPitNearY() to see what I mean.
-                       //
-                       //metrics_info_.pit1 = first pit of CoordCache;
-                       //metrics_info_.pit2 = last pit of CoordCache;
-                       //metrics_info_.singlepar = false;
-                       //metrics_info_.y1 = 0;
-                       //metrics_info_.y2 = height_;
-                       //
-                       updateMetrics(false);
-               }
+               // to enable a full redraw in any case as this is not costly.
+               TextMetrics & tm = text_metrics_[&buffer_.text()];
+               std::pair<pit_type, ParagraphMetrics const *> firstpm = tm.first();
+               std::pair<pit_type, ParagraphMetrics const *> lastpm = tm.last();
+               int y1 = firstpm.second->position() - firstpm.second->ascent();
+               int y2 = lastpm.second->position() + lastpm.second->descent();
+               metrics_info_ = ViewMetricsInfo(firstpm.first, lastpm.first, y1, y2,
+                       FullScreenUpdate, buffer_.text().paragraphs().size());
+               // Reinitialize anchor to first pit.
+               anchor_ref_ = firstpm.first;
+               offset_ref_ = -y1;
+               LYXERR(Debug::PAINTING)
+                       << "Mouse hover detected at: (" << cmd.x << ", " << cmd.y << ")"
+                       << "\nTriggering redraw: y1: " << y1 << " y2: " << y2
+                       << " pit1: " << firstpm.first << " pit2: " << lastpm.first << endl;
 
                // This event (moving without mouse click) is not passed further.
                // This should be changed if it is further utilized.
-               return need_redraw;
+               buffer_.changed();
+               return;
        }
-       
+
        // Build temporary cursor.
-       Inset * inset = buffer_->text().editXY(cur, cmd.x, cmd.y);
+       Inset * inset = text_metrics_[&buffer_.text()].editXY(cur, cmd.x, cmd.y);
 
        // Put anchor at the same position.
        cur.resetAnchor();
@@ -1250,40 +1389,82 @@ bool BufferView::workAreaDispatch(FuncRequest const & cmd0)
        // 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) {
+       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);
 
-       // Redraw if requested and necessary.
-       if (cur.result().dispatched() && cur.result().update())
-               return update(cur.result().update());
+       //Do we have a selection?
+       theSelection().haveSelection(cursor().selection());
+
+       // If the command has been dispatched,
+       if (cur.result().dispatched()
+               // an update is asked,
+               && cur.result().update()
+               // and redraw is needed,
+               && update(cur.result().update()))
+               // then trigger a redraw.
+               buffer_.changed();
+}
 
-       return false;
+
+void BufferView::scroll(int y)
+{
+       if (y > 0)
+               scrollDown(y);
+       else if (y < 0)
+               scrollUp(-y);
 }
 
 
-void BufferView::scroll(int /*lines*/)
+void BufferView::scrollDown(int offset)
 {
-//     if (!buffer_)
-//             return;
-//
-//     Text const * t = &buffer_->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);
-//
+       Text * text = &buffer_.text();
+       TextMetrics & tm = text_metrics_[text];
+       int ymax = height_ + offset;
+       while (true) {
+               std::pair<pit_type, ParagraphMetrics const *> last = tm.last();
+               int bottom_pos = last.second->position() + last.second->descent();
+               if (last.first + 1 == int(text->paragraphs().size())) {
+                       if (bottom_pos <= height_)
+                               return;
+                       offset = min(offset, bottom_pos - height_);
+                       break;
+               }
+               if (bottom_pos > ymax)
+                       break;
+               tm.newParMetricsDown();
+       }
+       offset_ref_ += offset;
+       updateMetrics(false);
+       buffer_.changed();
+}
+
+
+void BufferView::scrollUp(int offset)
+{
+       Text * text = &buffer_.text();
+       TextMetrics & tm = text_metrics_[text];
+       int ymin = - offset;
+       while (true) {
+               std::pair<pit_type, ParagraphMetrics const *> first = tm.first();
+               int top_pos = first.second->position() - first.second->ascent();
+               if (first.first == 0) {
+                       if (top_pos >= 0)
+                               return;
+                       offset = min(offset, - top_pos);
+                       break;
+               }
+               if (top_pos < ymin)
+                       break;
+               tm.newParMetricsUp();
+       }
+       offset_ref_ -= offset;
+       updateMetrics(false);
+       buffer_.changed();
 }
 
 
@@ -1292,20 +1473,21 @@ void BufferView::setCursorFromRow(int row)
        int tmpid = -1;
        int tmppos = -1;
 
-       buffer_->texrow().getIdFromRow(row, tmpid, tmppos);
+       buffer_.texrow().getIdFromRow(row, tmpid, tmppos);
 
+       cursor_.reset(buffer_.inset());
        if (tmpid == -1)
-               buffer_->text().setCursor(cursor_, 0, 0);
+               buffer_.text().setCursor(cursor_, 0, 0);
        else
-               buffer_->text().setCursor(cursor_, buffer_->getParFromID(tmpid).pit(), tmppos);
+               buffer_.text().setCursor(cursor_, buffer_.getParFromID(tmpid).pit(), tmppos);
 }
 
 
 void BufferView::gotoLabel(docstring const & label)
 {
-       for (InsetIterator it = inset_iterator_begin(buffer_->inset()); it; ++it) {
+       for (InsetIterator it = inset_iterator_begin(buffer_.inset()); it; ++it) {
                vector<docstring> labels;
-               it->getLabelList(*buffer_, labels);
+               it->getLabelList(buffer_, labels);
                if (std::find(labels.begin(), labels.end(), label) != labels.end()) {
                        setCursor(it);
                        update();
@@ -1327,7 +1509,7 @@ TextMetrics & BufferView::textMetrics(Text const * t)
        if (tmc_it == text_metrics_.end()) {
                tmc_it = text_metrics_.insert(
                        make_pair(t, TextMetrics(this, const_cast<Text *>(t)))).first;
-       }       
+       }
        return tmc_it->second;
 }
 
@@ -1368,14 +1550,14 @@ bool BufferView::checkDepm(Cursor & cur, Cursor & old)
 
        if (need_anchor_change)
                cur.resetAnchor();
-       
+
        if (!changed)
                return false;
 
-       updateLabels(*buffer_);
+       updateLabels(buffer_);
 
        updateMetrics(false);
-       buffer_->changed();
+       buffer_.changed();
        return true;
 }
 
@@ -1384,11 +1566,15 @@ bool BufferView::mouseSetCursor(Cursor & cur)
 {
        BOOST_ASSERT(&cur.bv() == this);
 
+       // this event will clear selection so we save selection for
+       // persistent selection
+       cap::saveSelection(cursor());
+
        // Has the cursor just left the inset?
        bool badcursor = false;
        bool leftinset = (&cursor_.inset() != &cur.inset());
        if (leftinset)
-               badcursor = cursor_.inset().notifyCursorLeaves(cursor_);
+               badcursor = notifyCursorLeaves(cursor_, cur);
 
        // do the dEPM magic if needed
        // FIXME: (1) move this to InsetText::notifyCursorLeaves?
@@ -1402,8 +1588,8 @@ bool BufferView::mouseSetCursor(Cursor & cur)
        // position is in the nucleus of the inset, notifyCursorLeaves
        // will kill the script inset itself. So we check all the
        // elements of the cursor to make sure that they are correct.
-       // For an example, see bug 2933: 
-       // http://bugzilla.lyx.org/show_bug.cgi?id=2933
+       // For an example, see bug 2933:
+       // http://bugzilla.lyx.org/show_bug.cgi?id=2933
        // The code below could maybe be moved to a DocIterator method.
        //lyxerr << "cur before " << cur <<std::endl;
        DocIterator dit(cur.inset());
@@ -1416,6 +1602,7 @@ bool BufferView::mouseSetCursor(Cursor & cur)
        //lyxerr << "5 cur after" << dit <<std::endl;
 
        cursor_.setCursor(dit);
+       cursor_.boundary(cur.boundary());
        cursor_.clearSelection();
        finishUndo();
        return update;
@@ -1435,7 +1622,6 @@ void BufferView::putSelectionAt(DocIterator const & cur,
                        cursor_.setSelection(cursor_, -length);
                } else
                        cursor_.setSelection(cursor_, length);
-               cap::saveSelection(cursor_);
        }
 }
 
@@ -1464,40 +1650,72 @@ ViewMetricsInfo const & BufferView::viewMetricsInfo()
 }
 
 
-// FIXME: We should split-up updateMetrics() for the singlepar case.
-void BufferView::updateMetrics(bool singlepar)
+bool BufferView::singleParUpdate()
 {
-       Text & buftext = buffer_->text();
+       Text & buftext = buffer_.text();
+       pit_type const bottom_pit = cursor_.bottom().pit();
        TextMetrics & tm = textMetrics(&buftext);
-       pit_type size = int(buftext.paragraphs().size());
+       int old_height = tm.parMetrics(bottom_pit).height();
+
+       // In Single Paragraph mode, rebreak only
+       // the (main text, not inset!) paragraph containing the cursor.
+       // (if this paragraph contains insets etc., rebreaking will
+       // recursively descend)
+       tm.redoParagraph(bottom_pit);
+       ParagraphMetrics const & pm = tm.parMetrics(bottom_pit);                
+       if (pm.height() != old_height)
+               // Paragraph height has changed so we cannot proceed to
+               // the singlePar optimisation.
+               return false;
+
+       int y1 = pm.position() - pm.ascent();
+       int y2 = pm.position() + pm.descent();
+       metrics_info_ = ViewMetricsInfo(bottom_pit, bottom_pit, y1, y2,
+               SingleParUpdate, buftext.paragraphs().size());
+       LYXERR(Debug::PAINTING)
+               << BOOST_CURRENT_FUNCTION
+               << "\ny1: " << y1
+               << " y2: " << y2
+               << " pit: " << bottom_pit
+               << " singlepar: 1"
+               << endl;
+       return true;
+}
 
-       if (anchor_ref_ > int(buftext.paragraphs().size() - 1)) {
-               anchor_ref_ = int(buftext.paragraphs().size() - 1);
+
+void BufferView::updateMetrics(bool singlepar)
+{
+       if (singlepar && singleParUpdate())
+               // No need to update the full screen metrics.
+               return;
+
+       Text & buftext = buffer_.text();
+       pit_type const npit = int(buftext.paragraphs().size());
+
+       if (anchor_ref_ > int(npit - 1)) {
+               anchor_ref_ = int(npit - 1);
                offset_ref_ = 0;
        }
-       
-       // If the paragraph metrics has changed, we can not
-       // use the singlepar optimisation.
-       if (singlepar
-               // In Single Paragraph mode, rebreak only
-               // the (main text, not inset!) paragraph containing the cursor.
-               // (if this paragraph contains insets etc., rebreaking will
-               // recursively descend)
-               && tm.redoParagraph(cursor_.bottom().pit()))
-               singlepar = false;
+
+       // Clear out the position cache in case of full screen redraw,
+       coord_cache_.clear();
+
+       // Clear out paragraph metrics to avoid having invalid metrics
+       // in the cache from paragraphs not relayouted below
+       // The complete text metrics will be redone.
+       text_metrics_.clear();
+
+       TextMetrics & tm = textMetrics(&buftext);
 
        pit_type const pit = anchor_ref_;
        int pit1 = pit;
        int pit2 = pit;
-       size_t const npit = buftext.paragraphs().size();
 
        // Rebreak anchor paragraph.
-       if (!singlepar)
-               tm.redoParagraph(pit);
-       
-       // Clear out the position cache in case of full screen redraw.
-       if (!singlepar)
-               coord_cache_.clear();
+       tm.redoParagraph(pit);
+
+       // Take care of anchor offset if case a recentering is needed.
+       updateOffsetRef();
 
        int y0 = tm.parMetrics(pit).ascent() - offset_ref_;
 
@@ -1506,12 +1724,10 @@ void BufferView::updateMetrics(bool singlepar)
        while (y1 > 0 && pit1 > 0) {
                y1 -= tm.parMetrics(pit1).ascent();
                --pit1;
-               if (!singlepar)
-                       tm.redoParagraph(pit1);
+               tm.redoParagraph(pit1);
                y1 -= tm.parMetrics(pit1).descent();
        }
 
-
        // Take care of ascent of first line
        y1 -= tm.parMetrics(pit1).ascent();
 
@@ -1531,49 +1747,25 @@ void BufferView::updateMetrics(bool singlepar)
        while (y2 < height_ && pit2 < int(npit) - 1) {
                y2 += tm.parMetrics(pit2).descent();
                ++pit2;
-               if (!singlepar)
-                       tm.redoParagraph(pit2);
+               tm.redoParagraph(pit2);
                y2 += tm.parMetrics(pit2).ascent();
        }
 
        // Take care of descent of last line
        y2 += tm.parMetrics(pit2).descent();
 
-       // The coordinates of all these paragraphs are correct, cache them
-       int y = y1;
-       CoordCache::InnerParPosCache & parPos = coord_cache_.parPos()[&buftext];
-       for (pit_type pit = pit1; pit <= pit2; ++pit) {
-               ParagraphMetrics const & pm = tm.parMetrics(pit);
-               y += pm.ascent();
-               parPos[pit] = Point(0, y);
-               if (singlepar && pit == cursor_.bottom().pit()) {
-                       // In Single Paragraph mode, collect here the
-                       // y1 and y2 of the (one) paragraph the cursor is in
-                       y1 = y - pm.ascent();
-                       y2 = y + pm.descent();
-               }
-               y += pm.descent();
-       }
-
-       if (singlepar) {
-               // collect cursor paragraph iter bounds
-               pit1 = cursor_.bottom().pit();
-               pit2 = cursor_.bottom().pit();
-       }
-
-       LYXERR(Debug::DEBUG)
+       LYXERR(Debug::PAINTING)
                << BOOST_CURRENT_FUNCTION
-               << " y1: " << y1
+               << "\n y1: " << y1
                << " y2: " << y2
                << " pit1: " << pit1
                << " pit2: " << pit2
                << " npit: " << npit
-               << " singlepar: " << singlepar
-               << "size: " << size
+               << " singlepar: 0"
                << endl;
 
-       metrics_info_ = ViewMetricsInfo(pit1, pit2, y1, y2, 
-               singlepar? SingleParUpdate: FullScreenUpdate, size);
+       metrics_info_ = ViewMetricsInfo(pit1, pit2, y1, y2,
+               FullScreenUpdate, npit);
 
        if (lyxerr.debugging(Debug::WORKAREA)) {
                LYXERR(Debug::WORKAREA) << "BufferView::updateMetrics" << endl;
@@ -1591,19 +1783,18 @@ void BufferView::menuInsertLyXFile(string const & filenm)
                // Launch a file browser
                // FIXME UNICODE
                string initpath = lyxrc.document_path;
-
-               if (buffer_) {
-                       string const trypath = buffer_->filePath();
-                       // If directory is writeable, use this as default.
-                       if (isDirWriteable(FileName(trypath)))
-                               initpath = trypath;
-               }
+               string const trypath = buffer_.filePath();
+               // If directory is writeable, use this as default.
+               if (isDirWriteable(FileName(trypath)))
+                       initpath = trypath;
 
                // FIXME UNICODE
                FileDialog fileDlg(_("Select LyX document to insert"),
                        LFUN_FILE_INSERT,
                        make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
-                       make_pair(_("Examples|#E#e"), from_utf8(addPath(package().system_support().absFilename(), "examples"))));
+                       make_pair(_("Examples|#E#e"),
+                                   from_utf8(addPath(package().system_support().absFilename(),
+                                   "examples"))));
 
                FileDialog::Result result =
                        fileDlg.open(from_utf8(initpath),
@@ -1635,20 +1826,303 @@ void BufferView::menuInsertLyXFile(string const & filenm)
        docstring res;
        Buffer buf("", false);
        if (lyx::loadLyXFile(&buf, FileName(filename))) {
-               ErrorList & el = buffer_->errorList("Parse");
+               ErrorList & el = buffer_.errorList("Parse");
                // Copy the inserted document error list into the current buffer one.
                el = buf.errorList("Parse");
                recordUndo(cursor_);
                cap::pasteParagraphList(cursor_, buf.paragraphs(),
-                                            buf.params().textclass, el);
+                                            buf.params().getTextClassPtr(), el);
                res = _("Document %1$s inserted.");
        } else
                res = _("Could not insert document %1$s");
 
        // emit message signal.
        message(bformat(res, disp_fn));
-       buffer_->errors("Parse");
-       resize();
+       buffer_.errors("Parse");
+       updateMetrics(false);
+}
+
+
+Point BufferView::coordOffset(DocIterator const & dit, bool boundary) const
+{
+       int x = 0;
+       int y = 0;
+       int lastw = 0;
+
+       // Addup contribution of nested insets, from inside to outside,
+       // keeping the outer paragraph for a special handling below
+       for (size_t i = dit.depth() - 1; i >= 1; --i) {
+               CursorSlice const & sl = dit[i];
+               int xx = 0;
+               int yy = 0;
+               
+               // get relative position inside sl.inset()
+               sl.inset().cursorPos(*this, sl, boundary && (i + 1 == dit.depth()), xx, yy);
+               
+               // Make relative position inside of the edited inset relative to sl.inset()
+               x += xx;
+               y += yy;
+               
+               // In case of an RTL inset, the edited inset will be positioned to the left
+               // of xx:yy
+               if (sl.text()) {
+                       bool boundary_i = boundary && i + 1 == dit.depth();
+                       bool rtl = textMetrics(sl.text()).isRTL(sl, boundary_i);
+                       if (rtl)
+                               x -= lastw;
+               }
+
+               // remember width for the case that sl.inset() is positioned in an RTL inset
+               if (i && dit[i - 1].text()) {
+                       // If this Inset is inside a Text Inset, retrieve the Dimension
+                       // from the containing text instead of using Inset::dimension() which
+                       // might not be implemented.
+                       // FIXME (Abdel 23/09/2007): this is a bit messy because of the
+                       // elimination of Inset::dim_ cache. This coordOffset() method needs
+                       // to be rewritten in light of the new design.
+                       Dimension const & dim = parMetrics(dit[i - 1].text(),
+                               dit[i - 1].pit()).insetDimension(&sl.inset());
+                       lastw = dim.wid;
+               } else {
+                       Dimension const dim = sl.inset().dimension(*this);
+                       lastw = dim.wid;
+               }
+               
+               //lyxerr << "Cursor::getPos, i: "
+               // << i << " x: " << xx << " y: " << y << endl;
+       }
+
+       // Add contribution of initial rows of outermost paragraph
+       CursorSlice const & sl = dit[0];
+       TextMetrics const & tm = textMetrics(sl.text());
+       ParagraphMetrics const & pm = tm.parMetrics(sl.pit());
+       BOOST_ASSERT(!pm.rows().empty());
+       y -= pm.rows()[0].ascent();
+#if 1
+       // FIXME: document this mess
+       size_t rend;
+       if (sl.pos() > 0 && dit.depth() == 1) {
+               int pos = sl.pos();
+               if (pos && boundary)
+                       --pos;
+//             lyxerr << "coordOffset: boundary:" << boundary << " depth:" << dit.depth() << " pos:" << pos << " sl.pos:" << sl.pos() << std::endl;
+               rend = pm.pos2row(pos);
+       } else
+               rend = pm.pos2row(sl.pos());
+#else
+       size_t rend = pm.pos2row(sl.pos());
+#endif
+       for (size_t rit = 0; rit != rend; ++rit)
+               y += pm.rows()[rit].height();
+       y += pm.rows()[rend].ascent();
+       
+       TextMetrics const & bottom_tm = textMetrics(dit.bottom().text());
+       
+       // Make relative position from the nested inset now bufferview absolute.
+       int xx = bottom_tm.cursorX(dit.bottom(), boundary && dit.depth() == 1);
+       x += xx;
+       
+       // In the RTL case place the nested inset at the left of the cursor in 
+       // the outer paragraph
+       bool boundary_1 = boundary && 1 == dit.depth();
+       bool rtl = bottom_tm.isRTL(dit.bottom(), boundary_1);
+       if (rtl)
+               x -= lastw;
+       
+       return Point(x, y);
+}
+
+
+Point BufferView::getPos(DocIterator const & dit, bool boundary) const
+{
+       CursorSlice const & bot = dit.bottom();
+       TextMetrics const & tm = textMetrics(bot.text());
+       if (!tm.has(bot.pit()))
+               return Point(-1, -1);
+
+       Point p = coordOffset(dit, boundary); // offset from outer paragraph
+       p.y_ += tm.parMetrics(bot.pit()).position();
+       return p;
+}
+
+
+void BufferView::draw(frontend::Painter & pain)
+{
+       PainterInfo pi(this, pain);
+       // Should the whole screen, including insets, be refreshed?
+       // FIXME: We should also distinguish DecorationUpdate to avoid text
+       // drawing if possible. This is not possible to do easily right now
+       // because of the single backing pixmap.
+       pi.full_repaint = metrics_info_.update_strategy != SingleParUpdate;
+
+       if (pi.full_repaint)
+               // Clear background (if not delegated to rows)
+               pain.fillRectangle(0, metrics_info_.y1, width_,
+                       metrics_info_.y2 - metrics_info_.y1,
+                       buffer_.inset().backgroundColor());
+
+       LYXERR(Debug::PAINTING) << "\t\t*** START DRAWING ***" << endl;
+       Text & text = buffer_.text();
+       TextMetrics const & tm = text_metrics_[&text];
+       int y = metrics_info_.y1 + tm.parMetrics(metrics_info_.p1).ascent();
+       if (!pi.full_repaint)
+               tm.drawParagraph(pi, metrics_info_.p1, 0, y);
+       else
+               tm.draw(pi, 0, y);
+       LYXERR(Debug::PAINTING) << "\n\t\t*** END DRAWING  ***" << endl;
+
+       // and grey out above (should not happen later)
+//     lyxerr << "par ascent: " << text.getPar(metrics_info_.p1).ascent() << endl;
+       if (metrics_info_.y1 > 0
+               && metrics_info_.update_strategy == FullScreenUpdate)
+               pain.fillRectangle(0, 0, width_, metrics_info_.y1, Color::bottomarea);
+
+       // and possibly grey out below
+//     lyxerr << "par descent: " << text.getPar(metrics_info_.p1).ascent() << endl;
+       if (metrics_info_.y2 < height_
+               && metrics_info_.update_strategy == FullScreenUpdate)
+               pain.fillRectangle(0, metrics_info_.y2, width_,
+                       height_ - metrics_info_.y2, Color::bottomarea);
+}
+
+
+void BufferView::message(docstring const & msg)
+{
+       if (gui_)
+               gui_->message(msg);
+}
+
+
+void BufferView::showDialog(std::string const & name)
+{
+       if (gui_)
+               gui_->showDialog(name);
+}
+
+
+void BufferView::showDialogWithData(std::string const & name,
+       std::string const & data)
+{
+       if (gui_)
+               gui_->showDialogWithData(name, data);
+}
+
+
+void BufferView::showInsetDialog(std::string const & name,
+       std::string const & data, Inset * inset)
+{
+       if (gui_)
+               gui_->showInsetDialog(name, data, inset);
+}
+
+
+void BufferView::updateDialog(std::string const & name, std::string const & data)
+{
+       if (gui_)
+               gui_->updateDialog(name, data);
+}
+
+
+void BufferView::setGuiDelegate(frontend::GuiBufferViewDelegate * gui)
+{
+       gui_ = gui;
 }
 
+
+// FIXME: Move this out of BufferView again
+docstring BufferView::contentsOfPlaintextFile(string const & f,
+       bool asParagraph)
+{
+       FileName fname(f);
+
+       if (fname.empty()) {
+               FileDialog fileDlg(_("Select file to insert"),
+                                  ( asParagraph
+                                    ? LFUN_FILE_INSERT_PLAINTEXT_PARA 
+                                    : LFUN_FILE_INSERT_PLAINTEXT) );
+
+               FileDialog::Result result =
+                       fileDlg.open(from_utf8(buffer().filePath()),
+                                    FileFilterList(), docstring());
+
+               if (result.first == FileDialog::Later)
+                       return docstring();
+
+               fname = makeAbsPath(to_utf8(result.second));
+
+               if (fname.empty())
+                       return docstring();
+       }
+
+       if (!fs::is_readable(fname.toFilesystemEncoding())) {
+               docstring const error = from_ascii(strerror(errno));
+               docstring const file = makeDisplayPath(fname.absFilename(), 50);
+               docstring const text =
+                 bformat(_("Could not read the specified document\n"
+                           "%1$s\ndue to the error: %2$s"), file, error);
+               Alert::error(_("Could not read file"), text);
+               return docstring();
+       }
+
+       ifstream ifs(fname.toFilesystemEncoding().c_str());
+       if (!ifs) {
+               docstring const error = from_ascii(strerror(errno));
+               docstring const file = makeDisplayPath(fname.absFilename(), 50);
+               docstring const text =
+                 bformat(_("Could not open the specified document\n"
+                           "%1$s\ndue to the error: %2$s"), file, error);
+               Alert::error(_("Could not open file"), text);
+               return docstring();
+       }
+
+       ifs.unsetf(std::ios::skipws);
+       istream_iterator<char> ii(ifs);
+       istream_iterator<char> end;
+#if !defined(USE_INCLUDED_STRING) && !defined(STD_STRING_IS_GOOD)
+       // We use this until the compilers get better...
+       std::vector<char> tmp;
+       copy(ii, end, back_inserter(tmp));
+       string const tmpstr(tmp.begin(), tmp.end());
+#else
+       // This is what we want to use and what we will use once the
+       // compilers get good enough.
+       //string tmpstr(ii, end); // yet a reason for using std::string
+       // alternate approach to get the file into a string:
+       string tmpstr;
+       copy(ii, end, back_inserter(tmpstr));
+#endif
+
+       // FIXME UNICODE: We don't know the encoding of the file
+       docstring file_content = from_utf8(tmpstr);
+       if (file_content.empty()) {
+               Alert::error(_("Reading not UTF-8 encoded file"),
+                            _("The file is not UTF-8 encoded.\n"
+                              "It will be read as local 8Bit-encoded.\n"
+                              "If this does not give the correct result\n"
+                              "then please change the encoding of the file\n"
+                              "to UTF-8 with a program other than LyX.\n"));
+               file_content = from_local8bit(tmpstr);
+       }
+
+       return normalize_c(file_content);
+}
+
+
+void BufferView::insertPlaintextFile(string const & f, bool asParagraph)
+{
+       docstring const tmpstr = contentsOfPlaintextFile(f, asParagraph);
+
+       if (tmpstr.empty())
+               return;
+
+       Cursor & cur = cursor();
+       cap::replaceSelection(cur);
+       recordUndo(cur);
+       if (asParagraph)
+               cur.innerText()->insertStringAsParagraphs(cur, tmpstr);
+       else
+               cur.innerText()->insertStringAsLines(cur, tmpstr);
+}
+
+
 } // namespace lyx