]> git.lyx.org Git - lyx.git/blobdiff - src/BufferView.cpp
Fix display of single-char macro names
[lyx.git] / src / BufferView.cpp
index 19547ef56448e1d43245a3d973e0a250fff81cd0..ac5c318909b7e74a69a97f05d24fd5e1d56cf152 100644 (file)
 #include "insets/InsetText.h"
 
 #include "mathed/MathData.h"
+#include "mathed/InsetMathNest.h"
 
 #include "frontends/alert.h"
 #include "frontends/Application.h"
 #include "frontends/Delegates.h"
 #include "frontends/FontMetrics.h"
+#include "frontends/NullPainter.h"
 #include "frontends/Painter.h"
 #include "frontends/Selection.h"
 
@@ -227,14 +229,17 @@ enum ScreenUpdateStrategy {
 
 struct BufferView::Private
 {
-       Private(BufferView & bv) : update_strategy_(NoScreenUpdate),
+       Private(BufferView & bv) :
+               update_strategy_(FullScreenUpdate),
+               update_flags_(Update::Force),
                wh_(0), cursor_(bv),
                anchor_pit_(0), anchor_ypos_(0),
                inlineCompletionUniqueChars_(0),
                last_inset_(0), clickable_inset_(false),
                mouse_position_cache_(),
                bookmark_edit_position_(-1), gui_(0),
-               horiz_scroll_offset_(0)
+               horiz_scroll_offset_(0),
+               caret_ascent_(0), caret_descent_(0)
        {
                xsel_cache_.set = false;
        }
@@ -244,6 +249,8 @@ struct BufferView::Private
        ///
        ScreenUpdateStrategy update_strategy_;
        ///
+       Update::flags update_flags_;
+       ///
        CoordCache coord_cache_;
 
        /// Estimated average par height for scrollbar.
@@ -311,6 +318,12 @@ struct BufferView::Private
        /// a slice pointing to the start of the row where cursor was
        /// at previous draw event
        CursorSlice last_row_slice_;
+
+       // The vertical size of the blinking caret. Only used for math
+       // Using it for text could be bad when undo restores the cursor
+       // current font, since the caret size could become wrong.
+       int caret_ascent_;
+       int caret_descent_;
 };
 
 
@@ -438,79 +451,96 @@ bool BufferView::needsFitCursor() const
 }
 
 
-void BufferView::processUpdateFlags(Update::flags flags)
+namespace {
+
+// this is for debugging only.
+string flagsAsString(Update::flags flags)
 {
-       // This is close to a hot-path.
-       LYXERR(Debug::PAINTING, "BufferView::processUpdateFlags()"
-               << "[fitcursor = " << (flags & Update::FitCursor)
-               << ", forceupdate = " << (flags & Update::Force)
-               << ", singlepar = " << (flags & Update::SinglePar)
-               << "]  buffer: " << &buffer_);
+       if (flags == Update::None)
+               return "None ";
+       return string((flags & Update::FitCursor) ? "FitCursor " : "")
+               + ((flags & Update::Force) ? "Force " : "")
+               + ((flags & Update::ForceDraw) ? "ForceDraw " : "")
+               + ((flags & Update::SinglePar) ? "SinglePar " : "")
+               + ((flags & Update::Decoration) ? "Decoration " : "");
+}
 
-       // FIXME Does this really need doing here? It's done in updateBuffer, and
-       // if the Buffer doesn't need updating, then do the macros?
-       buffer_.updateMacros();
+}
 
-       // Now do the first drawing step if needed. This consists on updating
-       // the CoordCache in updateMetrics().
-       // The second drawing step is done in WorkArea::redraw() if needed.
-       // FIXME: is this still true now that Buffer::changed() is used all over?
+void BufferView::processUpdateFlags(Update::flags flags)
+{
+       LYXERR(Debug::PAINTING, "BufferView::processUpdateFlags( "
+                  << flagsAsString(flags) << ")  buffer: " << &buffer_);
 
        // Case when no explicit update is requested.
-       if (!flags) {
-               // no need to redraw anything.
-               d->update_strategy_ = NoScreenUpdate;
+       if (flags == Update::None)
                return;
+
+       /* FIXME We would like to avoid doing this here, since it is very
+        * expensive and is called in updateBuffer already. However, even
+        * inserting a plain character can invalidate the overly fragile
+        * tables of child documents built by updateMacros. Some work is
+        * needed to avoid doing that when not necessary.
+        */
+       buffer_.updateMacros();
+
+       // SinglePar is ignored for now (this should probably change). We
+       // set it ourselves below, at the price of always rebreaking the
+       // paragraph at cursor. This can be expensive for large tables.
+       flags = flags & ~Update::SinglePar;
+
+       // First check whether the metrics and inset positions should be updated
+       if (flags & Update::Force) {
+               // This will update the CoordCache items and replace Force
+               // with ForceDraw in flags.
+               updateMetrics(flags);
        }
 
-       if (flags == Update::Decoration) {
-               d->update_strategy_ = DecorationUpdate;
-               buffer_.changed(false);
-               return;
+       // Detect whether we can only repaint a single paragraph.
+       // We handle this before FitCursor because the later will require
+       // correct metrics at cursor position.
+       if (!(flags & Update::ForceDraw)) {
+               if (singleParUpdate())
+                       flags = flags | Update::SinglePar;
+               else
+                       updateMetrics(flags);
        }
 
-       if (flags == Update::FitCursor
-               || flags == (Update::Decoration | Update::FitCursor)) {
-               // tell the frontend to update the screen if needed.
+       // Then make sure that the screen contains the cursor if needed
+       if (flags & Update::FitCursor) {
                if (needsFitCursor()) {
-                       showCursor();
-                       return;
-               }
-               if (flags & Update::Decoration) {
-                       d->update_strategy_ = DecorationUpdate;
-                       buffer_.changed(false);
-                       return;
+                       scrollToCursor(d->cursor_, false);
+                       // Metrics have to be recomputed (maybe again)
+                       updateMetrics(flags);
                }
-               // no screen update is needed in principle, but this
-               // could change if cursor row needs horizontal scrolling.
-               d->update_strategy_ = NoScreenUpdate;
-               buffer_.changed(false);
-               return;
+               flags = flags & ~Update::FitCursor;
        }
 
-       bool const full_metrics = flags & Update::Force || !singleParUpdate();
-
-       if (full_metrics)
-               // We have to update the full screen metrics.
-               updateMetrics();
+       // Add flags to the the update flags. These will be reset to None
+       // after the redraw is actually done
+       d->update_flags_ = d->update_flags_ | flags;
+       LYXERR(Debug::PAINTING, "Cumulative flags: " << flagsAsString(flags));
 
-       if (!(flags & Update::FitCursor)) {
-               // Nothing to do anymore. Trigger a redraw and return
-               buffer_.changed(false);
-               return;
-       }
+       // Now compute the update strategy
+       // Possibly values in flag are None, Decoration, ForceDraw
+       LATTEST((d->update_flags_ & ~(Update::None | Update::SinglePar
+                                     | Update::Decoration | Update::ForceDraw)) == 0);
 
-       // updateMetrics() does not update paragraph position
-       // This is done at draw() time. So we need a redraw!
-       buffer_.changed(false);
-
-       if (needsFitCursor()) {
-               // The cursor is off screen so ensure it is visible.
-               // refresh it:
-               showCursor();
+       if (d->update_flags_ & Update::ForceDraw)
+               d->update_strategy_ = FullScreenUpdate;
+       else if (d->update_flags_ & Update::Decoration)
+               d->update_strategy_ = DecorationUpdate;
+       else if (d->update_flags_ & Update::SinglePar)
+               d->update_strategy_ = SingleParUpdate;
+       else {
+               // no need to redraw anything.
+               d->update_strategy_ = NoScreenUpdate;
        }
 
        updateHoveredInset();
+
+       // Trigger a redraw.
+       buffer_.changed(false);
 }
 
 
@@ -625,8 +655,7 @@ void BufferView::scrollDocView(int const value, bool update)
        // If the offset is less than 2 screen height, prefer to scroll instead.
        if (abs(value) <= 2 * height_) {
                d->anchor_ypos_ -= value;
-               buffer_.changed(true);
-               updateHoveredInset();
+               processUpdateFlags(Update::Force);
                return;
        }
 
@@ -787,7 +816,7 @@ bool BufferView::moveToPosition(pit_type bottom_pit, pos_type bottom_pos,
                if (!dit.atEnd()) {
                        dit.pos() = min(dit.paragraph().size(), top_pos);
                        // Some slices of the iterator may not be
-                       // reachable (e.g. closed collapsable inset)
+                       // reachable (e.g. closed collapsible inset)
                        // so the dociterator may need to be
                        // shortened. Otherwise, setCursor may crash
                        // lyx when the cursor can not be set to these
@@ -823,12 +852,7 @@ bool BufferView::moveToPosition(pit_type bottom_pit, pos_type bottom_pos,
                d->cursor_.setCurrentFont();
                // Do not forget to reset the anchor (see #9912)
                d->cursor_.resetAnchor();
-               // To center the screen on this new position we need the
-               // paragraph position which is computed at draw() time.
-               // So we need a redraw!
-               buffer_.changed(false);
-               if (needsFitCursor())
-                       showCursor();
+               processUpdateFlags(Update::Force | Update::FitCursor);
        }
 
        return success;
@@ -870,19 +894,15 @@ void BufferView::showCursor()
 void BufferView::showCursor(DocIterator const & dit,
        bool recenter, bool update)
 {
-       if (scrollToCursor(dit, recenter) && update) {
-               buffer_.changed(true);
-               updateHoveredInset();
-       }
+       if (scrollToCursor(dit, recenter) && update)
+               processUpdateFlags(Update::Force);
 }
 
 
 void BufferView::scrollToCursor()
 {
-       if (scrollToCursor(d->cursor_, false)) {
-               buffer_.changed(true);
-               updateHoveredInset();
-       }
+       if (scrollToCursor(d->cursor_, false))
+               processUpdateFlags(Update::Force);
 }
 
 
@@ -1128,6 +1148,10 @@ bool BufferView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
                flag.setEnabled(true);
                break;
 
+       case LFUN_GRAPHICS_UNIFY:
+               flag.setEnabled(cur.selection());
+               break;
+
        case LFUN_WORD_FINDADV: {
                FindAndReplaceOptions opt;
                istringstream iss(to_utf8(cmd.argument()));
@@ -1326,7 +1350,7 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                //  without calling recordUndo. Fix this before using
                //  recordUndoBufferParams().
                cur.recordUndoFullBuffer();
-               buffer_.params().setBaseClass(argument);
+               buffer_.params().setBaseClass(argument, buffer_.layoutPos());
                makeDocumentClass();
                dr.screenUpdate(Update::Force);
                dr.forceBufferUpdate();
@@ -1350,48 +1374,55 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
        case LFUN_LAYOUT_RELOAD: {
                LayoutFileIndex bc = buffer_.params().baseClassID();
                LayoutFileList::get().reset(bc);
-               buffer_.params().setBaseClass(bc);
+               buffer_.params().setBaseClass(bc, buffer_.layoutPos());
                makeDocumentClass();
                dr.screenUpdate(Update::Force);
                dr.forceBufferUpdate();
                break;
        }
 
-       case LFUN_UNDO:
+       case LFUN_UNDO: {
                dr.setMessage(_("Undo"));
                cur.clearSelection();
+               // We need to find out if the bibliography information
+               // has changed. See bug #11055.
+               // So these should not be references...
+               LayoutModuleList const engines = buffer().params().citeEngine();
+               CiteEngineType const enginetype = buffer().params().citeEngineType();
                if (!cur.textUndo())
                        dr.setMessage(_("No further undo information"));
-               else
+               else {
                        dr.screenUpdate(Update::Force | Update::FitCursor);
-               dr.forceBufferUpdate();
-               // we only need to do this if we have deleted or restored a
-               // BiBTeX inset. but there is no other place to do it. one
-               // obvious idea is to try to do it in a copy constructor for
-               // InsetBibTeX, but when that is invoked, the buffer_ member
-               // is not yet set. another idea is to look at the InsetLists
-               // of the various paragraphs. but we'd have to recurse through
-               // the contained insets to make that work. it doesn't seem to
-               // be worth it, as this will not happen that often.
-               buffer().invalidateBibfileCache();
-               buffer().removeBiblioTempFiles();
+                       dr.forceBufferUpdate();
+                       if (buffer().params().citeEngine() != engines ||
+                           buffer().params().citeEngineType() != enginetype)
+                               buffer().invalidateCiteLabels();
+               }
                break;
+       }
 
-       case LFUN_REDO:
+       case LFUN_REDO: {
                dr.setMessage(_("Redo"));
                cur.clearSelection();
+               // We need to find out if the bibliography information
+               // has changed. See bug #11055.
+               // So these should not be references...
+               LayoutModuleList const engines = buffer().params().citeEngine();
+               CiteEngineType const enginetype = buffer().params().citeEngineType();
                if (!cur.textRedo())
                        dr.setMessage(_("No further redo information"));
-               else
+               else {
                        dr.screenUpdate(Update::Force | Update::FitCursor);
-               dr.forceBufferUpdate();
-               // see above
-               buffer().invalidateBibfileCache();
-               buffer().removeBiblioTempFiles();
+                       dr.forceBufferUpdate();
+                       if (buffer().params().citeEngine() != engines ||
+                           buffer().params().citeEngineType() != enginetype)
+                               buffer().invalidateCiteLabels();
+               }
                break;
+       }
 
        case LFUN_FONT_STATE:
-               dr.setMessage(cur.currentState());
+               dr.setMessage(cur.currentState(false));
                break;
 
        case LFUN_BOOKMARK_SAVE:
@@ -1641,7 +1672,6 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                                                BIBTEX_CODE);
                if (inset) {
                        if (inset->addDatabase(cmd.argument())) {
-                               buffer_.invalidateBibfileCache();
                                dr.forceBufferUpdate();
                        }
                }
@@ -1654,11 +1684,48 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                InsetBibtex * inset = getInsetByCode<InsetBibtex>(tmpcur,
                                                BIBTEX_CODE);
                if (inset) {
-                       if (inset->delDatabase(cmd.argument())) {
-                               buffer_.invalidateBibfileCache();
+                       if (inset->delDatabase(cmd.argument()))
                                dr.forceBufferUpdate();
+               }
+               break;
+       }
+
+       case LFUN_GRAPHICS_UNIFY: {
+
+               cur.recordUndoFullBuffer();
+
+               DocIterator from, to;
+               from = cur.selectionBegin();
+               to = cur.selectionEnd();
+
+               string const newId = cmd.getArg(0);
+               bool fetchId = newId.empty(); //if we wait for groupId from first graphics inset
+
+               InsetGraphicsParams grp_par;
+               if (!fetchId)
+                       InsetGraphics::string2params(graphics::getGroupParams(buffer_, newId), buffer_, grp_par);
+
+               if (!from.nextInset())  //move to closest inset
+                       from.forwardInset();
+
+               while (!from.empty() && from < to) {
+                       Inset * inset = from.nextInset();
+                       if (!inset)
+                               break;
+                       if (inset->lyxCode() == GRAPHICS_CODE) {
+                       InsetGraphics & ig = static_cast<InsetGraphics &>(*inset);
+                               InsetGraphicsParams inspar = ig.getParams();
+                               if (fetchId) {
+                                       grp_par = inspar;
+                                       fetchId = false;
+                               } else {
+                                       grp_par.filename = inspar.filename;
+                                       ig.setParams(grp_par);
+                               }
                        }
+                       from.forwardInset();
                }
+               dr.screenUpdate(Update::Force); //needed if triggered from context menu
                break;
        }
 
@@ -1721,8 +1788,8 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                bool const in_texted = cur.inTexted();
                cur.setCursor(doc_iterator_begin(cur.buffer()));
                cur.selHandle(false);
-               buffer_.changed(true);
-               updateHoveredInset();
+               // Force an immediate computation of metrics because we need it below
+               processUpdateFlags(Update::Force);
 
                d->text_metrics_[&buffer_.text()].editXY(cur, p.x_, p.y_,
                        true, act == LFUN_SCREEN_UP);
@@ -1756,8 +1823,7 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                        if (scroll_value)
                                scroll(scroll_step * scroll_value);
                }
-               buffer_.changed(true);
-               updateHoveredInset();
+               dr.screenUpdate(Update::ForceDraw);
                dr.forceBufferUpdate();
                break;
        }
@@ -1840,6 +1906,33 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
        }
 
 
+       case LFUN_UNICODE_INSERT: {
+               if (cmd.argument().empty())
+                       break;
+
+               FuncCode code = cur.inset().currentMode() == Inset::MATH_MODE ?
+                       LFUN_MATH_INSERT : LFUN_SELF_INSERT;
+               int i = 0;
+               while (true) {
+                       docstring const arg = from_utf8(cmd.getArg(i));
+                       if (arg.empty())
+                               break;
+                       if (!isHex(arg)) {
+                               LYXERR0("Not a hexstring: " << arg);
+                               ++i;
+                               continue;
+                       }
+                       char_type c = hexToInt(arg);
+                       if (c >= 32 && c < 0x10ffff) {
+                               LYXERR(Debug::KEY, "Inserting c: " << c);
+                               lyx::dispatch(FuncRequest(code, docstring(1, c)));
+                       }
+                       ++i;
+               }
+               break;
+       }
+
+
        // This would be in Buffer class if only Cursor did not
        // require a bufferview
        case LFUN_INSET_FORALL: {
@@ -1987,6 +2080,8 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                icp["key"] = from_utf8(arg);
                if (!opt1.empty())
                        icp["before"] = from_utf8(opt1);
+               icp["literal"] = 
+                       from_ascii(InsetCitation::last_literal ? "true" : "false");
                string icstr = InsetCommand::params2string(icp);
                FuncRequest fr(LFUN_INSET_INSERT, icstr);
                lyx::dispatch(fr);
@@ -2651,7 +2746,7 @@ bool BufferView::singleParUpdate()
                // the singlePar optimisation.
                return false;
 
-       d->update_strategy_ = SingleParUpdate;
+       tm.updatePosCache(bottom_pit);
 
        LYXERR(Debug::PAINTING, "\ny1: " << pm.position() - pm.ascent()
                << " y2: " << pm.position() + pm.descent()
@@ -2662,6 +2757,13 @@ bool BufferView::singleParUpdate()
 
 
 void BufferView::updateMetrics()
+{
+       updateMetrics(d->update_flags_);
+       d->update_strategy_ = FullScreenUpdate;
+}
+
+
+void BufferView::updateMetrics(Update::flags & update_flags)
 {
        if (height_ == 0 || width_ == 0)
                return;
@@ -2698,12 +2800,13 @@ void BufferView::updateMetrics()
                // Complete buffer visible? Then it's easy.
                if (scrollRange == 0)
                        d->anchor_ypos_ = anchor_pm.ascent();
-
-               // FIXME: Some clever handling needed to show
-               // the _first_ paragraph up to the top if the cursor is
-               // in the first line.
+               else {
+                       // avoid empty space above the first row
+                       d->anchor_ypos_ = min(d->anchor_ypos_, anchor_pm.ascent());
+               }
        }
        anchor_pm.setPosition(d->anchor_ypos_);
+       tm.updatePosCache(d->anchor_pit_);
 
        LYXERR(Debug::PAINTING, "metrics: "
                << " anchor pit = " << d->anchor_pit_
@@ -2719,6 +2822,7 @@ void BufferView::updateMetrics()
                y1 -= pm.descent();
                // Save the paragraph position in the cache.
                pm.setPosition(y1);
+               tm.updatePosCache(pit1);
                y1 -= pm.ascent();
        }
 
@@ -2732,6 +2836,7 @@ void BufferView::updateMetrics()
                y2 += pm.ascent();
                // Save the paragraph position in the cache.
                pm.setPosition(y2);
+               tm.updatePosCache(pit2);
                y2 += pm.descent();
        }
 
@@ -2743,7 +2848,11 @@ void BufferView::updateMetrics()
                << " pit1 = " << pit1
                << " pit2 = " << pit2);
 
-       d->update_strategy_ = FullScreenUpdate;
+       // metrics is done, full drawing is necessary now
+       update_flags = (update_flags & ~Update::Force) | Update::ForceDraw;
+
+       // Now update the positions of insets in the cache.
+       updatePosCache();
 
        if (lyxerr.debugging(Debug::WORKAREA)) {
                LYXERR(Debug::WORKAREA, "BufferView::updateMetrics");
@@ -2752,6 +2861,15 @@ void BufferView::updateMetrics()
 }
 
 
+void BufferView::updatePosCache()
+{
+       // this is the "nodraw" drawing stage: only set the positions of the
+       // insets in metrics cache.
+       frontend::NullPainter np;
+       draw(np, false);
+}
+
+
 void BufferView::insertLyXFile(FileName const & fname)
 {
        LASSERT(d->cursor_.inTexted(), return);
@@ -2886,13 +3004,26 @@ bool BufferView::paragraphVisible(DocIterator const & dit) const
 }
 
 
-void BufferView::cursorPosAndHeight(Point & p, int & h) const
+void BufferView::setCaretAscentDescent(int asc, int des)
 {
+       d->caret_ascent_ = asc;
+       d->caret_descent_ = des;
+}
+
+
+void BufferView::caretPosAndHeight(Point & p, int & h) const
+{
+       int asc, des;
        Cursor const & cur = cursor();
-       Font const font = cur.real_current_font;
-       frontend::FontMetrics const & fm = theFontMetrics(font);
-       int const asc = fm.maxAscent();
-       int const des = fm.maxDescent();
+       if (cur.inMathed()) {
+               asc = d->caret_ascent_;
+               des = d->caret_descent_;
+       } else {
+               Font const font = cur.real_current_font;
+               frontend::FontMetrics const & fm = theFontMetrics(font);
+               asc = fm.maxAscent();
+               des = fm.maxDescent();
+       }
        h = asc + des;
        p = getPos(cur);
        p.y_ -= asc;
@@ -2959,7 +3090,7 @@ void BufferView::setCurrentRowSlice(CursorSlice const & rowSlice)
 }
 
 
-void BufferView::checkCursorScrollOffset(PainterInfo & pi)
+void BufferView::checkCursorScrollOffset()
 {
        CursorSlice rowSlice = d->cursor_.bottom();
        TextMetrics const & tm = textMetrics(rowSlice.text());
@@ -2976,35 +3107,6 @@ void BufferView::checkCursorScrollOffset(PainterInfo & pi)
        // Set the row on which the cursor lives.
        setCurrentRowSlice(rowSlice);
 
-       // If insets referred to by cursor are not all in the cache, the positions
-       // need to be recomputed.
-       if (!d->cursor_.inCoordCache()) {
-               /** FIXME: the code below adds an extraneous computation of
-                * inset positions, and can therefore be bad for performance
-                * (think for example about a very large tabular inset.
-                * Redawing the row where it is means redrawing the whole
-                * screen).
-                *
-                * The bug that this fixes is the following: assume that there
-                * is a very large math inset. Upon entering the inset, when
-                * pressing `End', the row is not scrolled and the cursor is
-                * not visible. The extra row computation makes sure that the
-                * inset positions are correctly computed and set in the
-                * cache. This would not happen if we did not have two-stage
-                * drawing.
-                *
-                * A proper fix would be to always have proper inset positions
-                * at this point.
-                */
-               // Force the recomputation of inset positions
-               bool const drawing = pi.pain.isDrawingEnabled();
-               pi.pain.setDrawingEnabled(false);
-               // No need to care about vertical position.
-               RowPainter rp(pi, buffer().text(), row, -d->horiz_scroll_offset_, 0);
-               rp.paintText();
-               pi.pain.setDrawingEnabled(drawing);
-       }
-
        // Current x position of the cursor in pixels
        int cur_x = getPos(d->cursor_).x_;
 
@@ -3048,12 +3150,12 @@ void BufferView::checkCursorScrollOffset(PainterInfo & pi)
 }
 
 
-void BufferView::draw(frontend::Painter & pain)
+void BufferView::draw(frontend::Painter & pain, bool paint_caret)
 {
        if (height_ == 0 || width_ == 0)
                return;
-       LYXERR(Debug::PAINTING, "\t\t*** START DRAWING ***");
-
+       LYXERR(Debug::PAINTING, (pain.isNull() ? "\t\t--- START NODRAW ---"
+                                : "\t\t*** START DRAWING ***"));
        Text & text = buffer_.text();
        TextMetrics const & tm = d->text_metrics_[&text];
        int const y = tm.first().second->position();
@@ -3061,17 +3163,23 @@ void BufferView::draw(frontend::Painter & pain)
 
        // Check whether the row where the cursor lives needs to be scrolled.
        // Update the drawing strategy if needed.
-       checkCursorScrollOffset(pi);
+       checkCursorScrollOffset();
 
        switch (d->update_strategy_) {
 
        case NoScreenUpdate:
-               // If no screen painting is actually needed, only some the different
-               // coordinates of insets and paragraphs needs to be updated.
+               // no screen painting is actually needed. In nodraw stage
+               // however, the different coordinates of insets and paragraphs
+               // needs to be updated.
                LYXERR(Debug::PAINTING, "Strategy: NoScreenUpdate");
-               pi.full_repaint = true;
-               pi.pain.setDrawingEnabled(false);
-               tm.draw(pi, 0, y);
+               pi.full_repaint = false;
+               if (pain.isNull()) {
+                       pi.full_repaint = true;
+                       tm.draw(pi, 0, y);
+               } else {
+                       pi.full_repaint = false;
+                       tm.draw(pi, 0, y);
+               }
                break;
 
        case SingleParUpdate:
@@ -3116,7 +3224,8 @@ void BufferView::draw(frontend::Painter & pain)
                }
                break;
        }
-       LYXERR(Debug::PAINTING, "\n\t\t*** END DRAWING  ***");
+       LYXERR(Debug::PAINTING, (pain.isNull() ? "\t\t --- END NODRAW ---"
+                               : "\t\t *** END DRAWING ***"));
 
        // The scrollbar needs an update.
        updateScrollbar();
@@ -3127,13 +3236,29 @@ void BufferView::draw(frontend::Painter & pain)
        for (pit_type pit = firstpm.first; pit <= lastpm.first; ++pit) {
                ParagraphMetrics const & pm = tm.parMetrics(pit);
                if (pm.position() + pm.descent() > 0) {
+                       if (d->anchor_pit_ != pit
+                           || d->anchor_ypos_ != pm.position())
+                               LYXERR(Debug::PAINTING, "Found new anchor pit = " << d->anchor_pit_
+                                      << "  anchor ypos = " << d->anchor_ypos_);
                        d->anchor_pit_ = pit;
                        d->anchor_ypos_ = pm.position();
                        break;
                }
        }
-       LYXERR(Debug::PAINTING, "Found new anchor pit = " << d->anchor_pit_
-               << "  anchor ypos = " << d->anchor_ypos_);
+       if (!pain.isNull()) {
+               // reset the update flags, everything has been done
+               d->update_flags_ = Update::None;
+       }
+
+       // If a caret has to be painted, mark its text row as dirty to
+       //make sure that it will be repainted on next redraw.
+       /* FIXME: investigate whether this can be avoided when the cursor did not
+        * move at all
+        */
+       if (paint_caret) {
+               Row const & caret_row = d->cursor_.textRow();
+               caret_row.changed(true);
+       }
 }