]> git.lyx.org Git - lyx.git/blobdiff - src/BufferView.cpp
GuiSearch did not work with num. keypad enter
[lyx.git] / src / BufferView.cpp
index e316aab02e2d38319d2d5dd6df5d7cabb106a90d..065723621b9dee268f23a40121ccc7814eb48085 100644 (file)
@@ -18,7 +18,6 @@
 
 #include "BranchList.h"
 #include "Buffer.h"
-#include "buffer_funcs.h"
 #include "BufferList.h"
 #include "BufferParams.h"
 #include "CoordCache.h"
 #include "CutAndPaste.h"
 #include "DispatchResult.h"
 #include "ErrorList.h"
-#include "factory.h"
-#include "FloatList.h"
 #include "FuncRequest.h"
 #include "FuncStatus.h"
 #include "Intl.h"
-#include "InsetIterator.h"
 #include "Language.h"
-#include "LaTeXFeatures.h"
 #include "LayoutFile.h"
 #include "Lexer.h"
 #include "LyX.h"
 #include "LyXAction.h"
 #include "lyxfind.h"
-#include "Layout.h"
 #include "LyXRC.h"
 #include "MetricsInfo.h"
 #include "Paragraph.h"
-#include "ParagraphParameters.h"
-#include "ParIterator.h"
-#include "RowPainter.h"
 #include "Session.h"
 #include "Text.h"
-#include "TextClass.h"
 #include "TextMetrics.h"
 #include "TexRow.h"
 #include "TocBackend.h"
-#include "WordLangTuple.h"
 
 #include "insets/InsetBibtex.h"
 #include "insets/InsetCitation.h"
 #include "insets/InsetCommand.h" // ChangeRefs
-#include "insets/InsetExternal.h"
 #include "insets/InsetGraphics.h"
-#include "insets/InsetNote.h"
 #include "insets/InsetRef.h"
 #include "insets/InsetText.h"
 
+#include "mathed/InsetMath.h"
 #include "mathed/MathData.h"
-#include "mathed/InsetMathNest.h"
+#include "mathed/MathRow.h"
 
 #include "frontends/alert.h"
-#include "frontends/Application.h"
+#include "frontends/CaretGeometry.h"
 #include "frontends/Delegates.h"
 #include "frontends/FontMetrics.h"
 #include "frontends/NullPainter.h"
 #include "frontends/Painter.h"
 #include "frontends/Selection.h"
+#include "frontends/Clipboard.h"
 
 #include "support/convert.h"
 #include "support/debug.h"
-#include "support/ExceptionMessage.h"
+#include "support/docstring.h"
 #include "support/filetools.h"
 #include "support/gettext.h"
 #include "support/lassert.h"
 #include "support/Length.h"
 #include "support/lstrings.h"
 #include "support/lyxlib.h"
-#include "support/Package.h"
 #include "support/types.h"
 
+#include <algorithm>
 #include <cerrno>
+#include <cstring>
 #include <fstream>
 #include <functional>
 #include <iterator>
@@ -285,9 +275,6 @@ struct BufferView::Private
          */
        frontend::GuiBufferViewDelegate * gui_;
 
-       /// Cache for Find Next
-       FuncRequest search_request_cache_;
-
        ///
        map<string, Inset *> edited_insets_;
 
@@ -302,6 +289,8 @@ struct BufferView::Private
        CursorSlice current_row_slice_;
        /// are we hovering something that we can click
        bool clickable_inset_;
+       /// shape of the caret
+       frontend::CaretGeometry caret_geometry_;
 };
 
 
@@ -362,15 +351,15 @@ int BufferView::leftMargin() const
 
 int BufferView::topMargin() const
 {
-       // original value was 20px, which is 0.2in at 100dpi
-       return zoomedPixels(20);
+       // Original value was 20px at 100dpi. For internal buffers like in
+       // advanced search and replace, a value of 5px is enough.
+       return zoomedPixels(buffer().isInternal() ? 5 : 20);
 }
 
 
 int BufferView::bottomMargin() const
 {
-       // original value was 20px, which is 0.2in at 100dpi
-       return zoomedPixels(20);
+       return topMargin();
 }
 
 
@@ -458,6 +447,26 @@ Buffer const & BufferView::buffer() const
 }
 
 
+docstring const & BufferView::searchRequestCache() const
+{
+       return theClipboard().getFindBuffer();
+}
+
+
+void BufferView::setSearchRequestCache(docstring const & text)
+{
+       bool casesensitive;
+       bool matchword;
+       bool forward;
+       bool wrap;
+       bool instant;
+       bool onlysel;
+       docstring const search = string2find(text, casesensitive, matchword,
+                                            forward, wrap, instant, onlysel);
+       theClipboard().setFindBuffer(search);
+}
+
+
 bool BufferView::needsFitCursor() const
 {
        if (cursorStatus(d->cursor_) == CUR_INSIDE) {
@@ -518,9 +527,9 @@ void BufferView::processUpdateFlags(Update::flags flags)
        // We handle this before FitCursor because the later will require
        // correct metrics at cursor position.
        if (!(flags & Update::ForceDraw)
-           && (flags & Update::SinglePar)
-               && !singleParUpdate())
-                       updateMetrics(flags);
+                       && (flags & Update::SinglePar)
+                       && !singleParUpdate())
+               updateMetrics(flags);
 
        // Then make sure that the screen contains the cursor if needed
        if (flags & Update::FitCursor) {
@@ -668,24 +677,24 @@ string BufferView::contextMenu(int x, int y) const
 
 
 
-void BufferView::scrollDocView(int const value, bool update)
+void BufferView::scrollDocView(int const pixels, bool update)
 {
        // The scrollbar values are relative to the top of the screen, therefore the
        // offset is equal to the target value.
 
        // No scrolling at all? No need to redraw anything
-       if (value == 0)
+       if (pixels == 0)
                return;
 
        // If the offset is less than 2 screen height, prefer to scroll instead.
-       if (abs(value) <= 2 * height_) {
-               d->anchor_ypos_ -= value;
+       if (abs(pixels) <= 2 * height_) {
+               d->anchor_ypos_ -= pixels;
                processUpdateFlags(Update::Force);
                return;
        }
 
        // cut off at the top
-       if (value <= d->scrollbarParameters_.min) {
+       if (pixels <= d->scrollbarParameters_.min) {
                DocIterator dit = doc_iterator_begin(&buffer_);
                showCursor(dit, false, update);
                LYXERR(Debug::SCROLLING, "scroll to top");
@@ -693,7 +702,7 @@ void BufferView::scrollDocView(int const value, bool update)
        }
 
        // cut off at the bottom
-       if (value >= d->scrollbarParameters_.max) {
+       if (pixels >= d->scrollbarParameters_.max) {
                DocIterator dit = doc_iterator_end(&buffer_);
                dit.backwardPos();
                showCursor(dit, false, update);
@@ -706,11 +715,11 @@ void BufferView::scrollDocView(int const value, bool update)
        pit_type i = 0;
        for (; i != int(d->par_height_.size()); ++i) {
                par_pos += d->par_height_[i];
-               if (par_pos >= value)
+               if (par_pos >= pixels)
                        break;
        }
 
-       if (par_pos < value) {
+       if (par_pos < pixels) {
                // It seems we didn't find the correct pit so stay on the safe side and
                // scroll to bottom.
                LYXERR0("scrolling position not found!");
@@ -720,7 +729,7 @@ void BufferView::scrollDocView(int const value, bool update)
 
        DocIterator dit = doc_iterator_begin(&buffer_);
        dit.pit() = i;
-       LYXERR(Debug::SCROLLING, "value = " << value << " -> scroll to pit " << i);
+       LYXERR(Debug::SCROLLING, "pixels = " << pixels << " -> scroll to pit " << i);
        showCursor(dit, false, update);
 }
 
@@ -805,23 +814,24 @@ void BufferView::bookmarkEditPosition()
 
 void BufferView::saveBookmark(unsigned int idx)
 {
+       if (buffer().isInternal())
+               return;
+
        // tentatively 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
        // when lyx exits.
-       if (!buffer_.isInternal()) {
-               theSession().bookmarks().save(
-                       buffer_.fileName(),
-                       d->cursor_.bottom().pit(),
-                       d->cursor_.bottom().pos(),
-                       d->cursor_.paragraph().id(),
-                       d->cursor_.pos(),
-                       idx
-                       );
-               if (idx)
-                       // emit message signal.
-                       message(_("Save bookmark"));
-       }
+       theSession().bookmarks().save(
+               buffer_.fileName(),
+               d->cursor_.bottom().pit(),
+               d->cursor_.bottom().pos(),
+               d->cursor_.paragraph().id(),
+               d->cursor_.pos(),
+               idx
+       );
+       if (idx)
+               // emit message signal.
+               message(_("Save bookmark"));
 }
 
 
@@ -947,7 +957,7 @@ bool BufferView::scrollToCursor(DocIterator const & dit, bool const recenter)
        TextMetrics & tm = d->text_metrics_[bot.text()];
 
        pos_type const max_pit = pos_type(bot.text()->paragraphs().size() - 1);
-       int bot_pit = bot.pit();
+       pos_type bot_pit = bot.pit();
        if (bot_pit > max_pit) {
                // FIXME: Why does this happen?
                LYXERR0("bottom pit is greater that max pit: "
@@ -967,8 +977,15 @@ bool BufferView::scrollToCursor(DocIterator const & dit, bool const recenter)
                CursorSlice const & cs = dit.innerTextSlice();
                int offset = coordOffset(dit).y_;
                int ypos = pm.position() + offset;
-               Dimension const & row_dim =
-                       pm.getRow(cs.pos(), dit.boundary()).dim();
+               Row const & row = pm.getRow(cs.pos(), dit.boundary());
+               Dimension row_dim = row.dim();
+               // FIXME: the will not be necessary anymore if Row has both a
+               // dim() which is its full dimension and a contentsDim() which
+               // is the dimension of the text only.
+               if (&row == &pm.rows().front())
+                       row_dim.asc = pm.ascent();
+               if (&row == &pm.rows().back())
+                       row_dim.des = pm.descent();
                int scrolled = 0;
                if (recenter)
                        scrolled = scroll(ypos - height_/2);
@@ -1032,7 +1049,7 @@ bool BufferView::scrollToCursor(DocIterator const & dit, bool const recenter)
 void BufferView::makeDocumentClass()
 {
        DocumentClassConstPtr olddc = buffer_.params().documentClassPtr();
-       buffer_.params().makeDocumentClass();
+       buffer_.params().makeDocumentClass(buffer_.isClone(), buffer_.isInternal());
        updateDocumentClass(olddc);
 }
 
@@ -1136,7 +1153,7 @@ bool BufferView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
                break;
        case LFUN_FILE_INSERT_PLAINTEXT_PARA:
        case LFUN_FILE_INSERT_PLAINTEXT: {
-               docstring const fname = cmd.argument();
+               docstring const fname = cmd.argument();
                if (!FileName::isAbsolute(to_utf8(fname))) {
                        flag.message(_("Absolute filename expected."));
                        return false;
@@ -1163,6 +1180,7 @@ bool BufferView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
        case LFUN_MARK_OFF:
        case LFUN_MARK_ON:
        case LFUN_MARK_TOGGLE:
+       case LFUN_SEARCH_STRING_SET:
        case LFUN_SCREEN_RECENTER:
        case LFUN_SCREEN_SHOW_CURSOR:
        case LFUN_BIBTEX_DATABASE_ADD:
@@ -1259,6 +1277,10 @@ bool BufferView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
                break;
        }
 
+       case LFUN_COPY:
+               flag.setEnabled(cur.selection());
+               break;
+
        default:
                return false;
        }
@@ -1457,6 +1479,7 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                break;
 
        case LFUN_BOOKMARK_SAVE:
+               dr.screenUpdate(Update::Force);
                saveBookmark(convert<unsigned int>(to_utf8(cmd.argument())));
                break;
 
@@ -1612,63 +1635,72 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
 
        case LFUN_WORD_FIND_FORWARD:
        case LFUN_WORD_FIND_BACKWARD: {
-               // FIXME THREAD
-               // Would it maybe be better if this variable were view specific anyway?
-               static docstring last_search;
                docstring searched_string;
 
                if (!cmd.argument().empty()) {
-                       last_search = cmd.argument();
+                       setSearchRequestCache(cmd.argument());
                        searched_string = cmd.argument();
                } else {
-                       searched_string = last_search;
+                       searched_string = searchRequestCache();
                }
 
                if (searched_string.empty())
                        break;
 
-               bool const fw = act == LFUN_WORD_FIND_FORWARD;
                docstring const data =
-                       find2string(searched_string, true, false, fw);
+                       find2string(searched_string, false, false,
+                                   act == LFUN_WORD_FIND_FORWARD, false, false, false);
                bool found = lyxfind(this, FuncRequest(LFUN_WORD_FIND, data));
                if (found)
                        dr.screenUpdate(Update::Force | Update::FitCursor);
+               else
+                       dr.setMessage(_("Search string not found!"));
                break;
        }
 
        case LFUN_WORD_FIND: {
-               FuncRequest req = cmd;
-               if (cmd.argument().empty() && !d->search_request_cache_.argument().empty())
-                       req = d->search_request_cache_;
-               if (req.argument().empty()) {
+               docstring arg = cmd.argument();
+               if (arg.empty())
+                       arg = searchRequestCache();
+               if (arg.empty()) {
                        lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, "findreplace"));
                        break;
                }
-               if (lyxfind(this, req))
+               if (lyxfind(this, FuncRequest(act, arg)))
                        dr.screenUpdate(Update::Force | Update::FitCursor);
+               else
+                       dr.setMessage(_("Search string not found!"));
 
-               d->search_request_cache_ = req;
+               setSearchRequestCache(arg);
                break;
        }
 
-       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.inMathed() && cur.paragraph().isDeleted(p)) {
-                                               has_deleted = true;
-                                               break;
-                                       }
-                               }
-                       }
+       case LFUN_SEARCH_STRING_SET: {
+               docstring pattern = cmd.argument();
+               if (!pattern.empty()) {
+                       setSearchRequestCache(pattern);
+                       break;
                }
-               if (lyxreplace(this, cmd, has_deleted)) {
+               if (cur.selection())
+                       pattern = cur.selectionAsString(false);
+               else {
+                       pos_type spos = cur.pos();
+                       cur.innerText()->selectWord(cur, WHOLE_WORD);
+                       pattern = cur.selectionAsString(false);
+                       cur.selection(false);
+                       cur.pos() = spos;
+               }
+               setSearchRequestCache(pattern);
+               break;
+       }
+
+       case LFUN_WORD_REPLACE: {
+               if (lyxreplace(this, cmd)) {
                        dr.forceBufferUpdate();
                        dr.screenUpdate(Update::Force | Update::FitCursor);
                }
+               else
+                       dr.setMessage(_("Search string not found!"));
                break;
        }
 
@@ -1841,7 +1873,7 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                cur.setCursor(doc_iterator_begin(cur.buffer()));
                cur.selHandle(false);
                // Force an immediate computation of metrics because we need it below
-               processUpdateFlags(Update::Force);
+               updateMetrics();
 
                d->text_metrics_[&buffer_.text()].editXY(cur, p.x_, p.y_,
                        true, act == LFUN_SCREEN_UP);
@@ -2188,6 +2220,11 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                break;
        }
 
+       case LFUN_COPY:
+               cap::copySelection(cur);
+               cur.message(_("Copy"));
+               break;
+
        default:
                // OK, so try the Buffer itself...
                buffer_.dispatch(cmd, dr);
@@ -2422,7 +2459,7 @@ void BufferView::mouseEventDispatch(FuncRequest const & cmd0)
        // Do we have a selection?
        theSelection().haveSelection(cursor().selection());
 
-       if (cur.needBufferUpdate()) {
+       if (cur.needBufferUpdate() || buffer().needUpdate()) {
                cur.clearBufferUpdate();
                buffer().updateBuffer();
        }
@@ -2439,21 +2476,21 @@ int BufferView::minVisiblePart()
 }
 
 
-int BufferView::scroll(int y)
+int BufferView::scroll(int pixels)
 {
-       if (y > 0)
-               return scrollDown(y);
-       if (y < 0)
-               return scrollUp(-y);
+       if (pixels > 0)
+               return scrollDown(pixels);
+       if (pixels < 0)
+               return scrollUp(-pixels);
        return 0;
 }
 
 
-int BufferView::scrollDown(int offset)
+int BufferView::scrollDown(int pixels)
 {
        Text * text = &buffer_.text();
        TextMetrics & tm = d->text_metrics_[text];
-       int const ymax = height_ + offset;
+       int const ymax = height_ + pixels;
        while (true) {
                pair<pit_type, ParagraphMetrics const *> last = tm.last();
                int bottom_pos = last.second->position() + last.second->descent();
@@ -2462,38 +2499,38 @@ int BufferView::scrollDown(int offset)
                if (last.first + 1 == int(text->paragraphs().size())) {
                        if (bottom_pos <= height_)
                                return 0;
-                       offset = min(offset, bottom_pos - height_);
+                       pixels = min(pixels, bottom_pos - height_);
                        break;
                }
                if (bottom_pos > ymax)
                        break;
                tm.newParMetricsDown();
        }
-       d->anchor_ypos_ -= offset;
-       return -offset;
+       d->anchor_ypos_ -= pixels;
+       return -pixels;
 }
 
 
-int BufferView::scrollUp(int offset)
+int BufferView::scrollUp(int pixels)
 {
        Text * text = &buffer_.text();
        TextMetrics & tm = d->text_metrics_[text];
-       int ymin = - offset;
+       int ymin = - pixels;
        while (true) {
                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 0;
-                       offset = min(offset, - top_pos);
+                       pixels = min(pixels, - top_pos);
                        break;
                }
                if (top_pos < ymin)
                        break;
                tm.newParMetricsUp();
        }
-       d->anchor_ypos_ += offset;
-       return offset;
+       d->anchor_ypos_ += pixels;
+       return pixels;
 }
 
 
@@ -2948,7 +2985,8 @@ void BufferView::insertLyXFile(FileName const & fname, bool const ignorelang)
                        buf.changeLanguage(buf.language(), d->cursor_.getFont().language());
                buffer_.undo().recordUndo(d->cursor_);
                cap::pasteParagraphList(d->cursor_, pars,
-                                            buf.params().documentClassPtr(), el);
+                                       buf.params().documentClassPtr(),
+                                       buf.params().authors(), el);
                res = _("Document %1$s inserted.");
        } else {
                res = _("Could not insert document %1$s");
@@ -3062,23 +3100,107 @@ bool BufferView::paragraphVisible(DocIterator const & dit) const
 }
 
 
-void BufferView::caretPosAndHeight(Point & p, int & h) const
+void BufferView::caretPosAndDim(Point & p, Dimension & dim) const
 {
-       int asc, des;
        Cursor const & cur = cursor();
        if (cur.inMathed()) {
                MathRow const & mrow = mathRow(&cur.cell());
-               asc = mrow.caret_ascent;
-               des = mrow.caret_descent;
+               dim = mrow.caret_dim;
        } else {
                Font const font = cur.real_current_font;
                frontend::FontMetrics const & fm = theFontMetrics(font);
-               asc = fm.maxAscent();
-               des = fm.maxDescent();
+               dim.wid = fm.lineWidth();
+               dim.asc = fm.maxAscent();
+               dim.des = fm.maxDescent();
        }
-       h = asc + des;
+       if (lyxrc.cursor_width > 0)
+               dim.wid = lyxrc.cursor_width;
+
        p = getPos(cur);
-       p.y_ -= asc;
+       // center fat carets horizontally
+       p.x_ -= dim.wid / 2;
+       // p is top-left
+       p.y_ -= dim.asc;
+}
+
+
+void BufferView::buildCaretGeometry(bool complet)
+{
+       Point p;
+       Dimension dim;
+       caretPosAndDim(p, dim);
+
+       Cursor const & cur = d->cursor_;
+       Font const & realfont = cur.real_current_font;
+       frontend::FontMetrics const & fm = theFontMetrics(realfont.fontInfo());
+       bool const isrtl = realfont.isVisibleRightToLeft();
+       int const dir = isrtl ? -1 : 1;
+
+       frontend::CaretGeometry & cg = d->caret_geometry_;
+       cg.shapes.clear();
+
+       // The caret itself, slanted for italics in text edit mode except
+       // for selections because the selection rect does not slant
+       bool const slant = fm.italic() && cur.inTexted() && !cur.selection();
+       double const slope = slant ? fm.italicSlope() : 0;
+       cg.shapes.push_back(
+               {{iround(p.x_ + dim.asc * slope), p.y_},
+                {iround(p.x_ - dim.des * slope), p.y_ + dim.height()},
+                {iround(p.x_ + dir * dim.wid - dim.des * slope), p.y_ + dim.height()},
+                {iround(p.x_ + dir * dim.wid + dim.asc * slope), p.y_}}
+               );
+
+       // The language indicator _| (if needed)
+       Language const * doclang = buffer().params().language;
+       if (!((realfont.language() == doclang && isrtl == doclang->rightToLeft())
+                 || realfont.language() == latex_language)) {
+               int const lx = dim.height() / 3;
+               int const xx = iround(p.x_ - dim.des * slope);
+               int const yy = p.y_ + dim.height();
+               cg.shapes.push_back(
+                       {{xx, yy - dim.wid},
+                        {xx + dir * (dim.wid + lx - 1), yy - dim.wid},
+                        {xx + dir * (dim.wid + lx - 1), yy},
+                        {xx, yy}}
+                       );
+       }
+
+       // The completion triangle |> (if needed)
+       if (complet) {
+               int const m = p.y_ + dim.height() / 2;
+               int const d = dim.height() / 8;
+               // offset for slanted carret
+               int const sx = iround((dim.asc - (dim.height() / 2 - d)) * slope);
+               // starting position x
+               int const xx = p.x_ + dir * dim.wid + sx;
+               cg.shapes.push_back(
+                       {{xx, m - d},
+                        {xx + dir * d, m},
+                        {xx, m + d},
+                        {xx, m + d - dim.wid},
+                        {xx + dir * d - dim.wid, m},
+                        {xx, m - d + dim.wid}}
+                       );
+       }
+
+       // compute extremal x values
+       cg.left = 1000000;
+       cg.right = -1000000;
+       cg.top = 1000000;
+       cg.bottom = -1000000;
+       for (auto const & shape : cg.shapes)
+               for (Point const & p : shape) {
+                       cg.left = min(cg.left, p.x_);
+                       cg.right = max(cg.right, p.x_);
+                       cg.top = min(cg.top, p.y_);
+                       cg.bottom = max(cg.bottom, p.y_);
+               }
+}
+
+
+frontend::CaretGeometry const &  BufferView::caretGeometry() const
+{
+       return d->caret_geometry_;
 }
 
 
@@ -3087,11 +3209,11 @@ bool BufferView::caretInView() const
        if (!paragraphVisible(cursor()))
                return false;
        Point p;
-       int h;
-       caretPosAndHeight(p, h);
+       Dimension dim;
+       caretPosAndDim(p, dim);
 
        // does the cursor touch the screen ?
-       if (p.y_ + h < 0 || p.y_ >= workHeight())
+       if (p.y_ + dim.height() < 0 || p.y_ >= workHeight())
                return false;
        return true;
 }
@@ -3277,6 +3399,7 @@ void BufferView::draw(frontend::Painter & pain, bool paint_caret)
                                : "\t\t *** END DRAWING ***"));
 
        // The scrollbar needs an update.
+       // FIXME: does it always? see ticket #11947.
        updateScrollbar();
 
        // Normalize anchor for next time
@@ -3305,8 +3428,17 @@ void BufferView::draw(frontend::Painter & pain, bool paint_caret)
         * move at all
         */
        if (paint_caret) {
-               Row const & caret_row = d->cursor_.textRow();
-               caret_row.changed(true);
+               Cursor cur(d->cursor_);
+               while (cur.depth() > 1) {
+                       if (!cur.inTexted())
+                               break;
+                       TextMetrics const & tm = textMetrics(cur.text());
+                       if (d->caret_geometry_.left >= tm.origin().x_
+                               && d->caret_geometry_.right <= tm.origin().x_ + tm.dim().width())
+                               break;
+                       cur.pop();
+               }
+               cur.textRow().changed(true);
        }
 }
 
@@ -3408,7 +3540,7 @@ docstring const & BufferView::inlineCompletion() const
 }
 
 
-size_t const & BufferView::inlineCompletionUniqueChars() const
+size_t BufferView::inlineCompletionUniqueChars() const
 {
        return d->inlineCompletionUniqueChars_;
 }