]> git.lyx.org Git - lyx.git/blobdiff - src/BufferView.cpp
Fix bug #9813
[lyx.git] / src / BufferView.cpp
index c8b376385d2689c318bb018d8be97a3af5890ef0..663618af36f185d7d08439c72294763bf0b3abf8 100644 (file)
@@ -46,7 +46,6 @@
 #include "Paragraph.h"
 #include "ParagraphParameters.h"
 #include "ParIterator.h"
-#include "RowPainter.h"
 #include "Session.h"
 #include "Text.h"
 #include "TextClass.h"
@@ -235,7 +234,9 @@ struct BufferView::Private
                mouse_position_cache_(),
                bookmark_edit_position_(-1), gui_(0),
                horiz_scroll_offset_(0)
-       {}
+       {
+               xsel_cache_.set = false;
+       }
 
        ///
        ScrollbarParameters scrollbarParameters_;
@@ -413,7 +414,7 @@ Buffer const & BufferView::buffer() const
 }
 
 
-bool BufferView::fitCursor()
+bool BufferView::needsFitCursor() const
 {
        if (cursorStatus(d->cursor_) == CUR_INSIDE) {
                frontend::FontMetrics const & fm =
@@ -444,6 +445,7 @@ void BufferView::processUpdateFlags(Update::flags flags)
        // 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?
 
        // Case when no explicit update is requested.
        if (!flags) {
@@ -461,7 +463,7 @@ void BufferView::processUpdateFlags(Update::flags flags)
        if (flags == Update::FitCursor
                || flags == (Update::Decoration | Update::FitCursor)) {
                // tell the frontend to update the screen if needed.
-               if (fitCursor()) {
+               if (needsFitCursor()) {
                        showCursor();
                        return;
                }
@@ -471,7 +473,7 @@ void BufferView::processUpdateFlags(Update::flags flags)
                        return;
                }
                // no screen update is needed in principle, but this
-               // could change if cursor row needs scrolling.
+               // could change if cursor row needs horizontal scrolling.
                d->update_strategy_ = NoScreenUpdate;
                buffer_.changed(false);
                return;
@@ -493,7 +495,7 @@ void BufferView::processUpdateFlags(Update::flags flags)
        // This is done at draw() time. So we need a redraw!
        buffer_.changed(false);
 
-       if (fitCursor()) {
+       if (needsFitCursor()) {
                // The cursor is off screen so ensure it is visible.
                // refresh it:
                showCursor();
@@ -809,7 +811,7 @@ bool BufferView::moveToPosition(pit_type bottom_pit, pos_type bottom_pos,
                // paragraph position which is computed at draw() time.
                // So we need a redraw!
                buffer_.changed(false);
-               if (fitCursor())
+               if (needsFitCursor())
                        showCursor();
        }
 
@@ -987,7 +989,7 @@ void BufferView::updateDocumentClass(DocumentClassConstPtr olddc)
 }
 
 
-/** Return the change status at cursor position, taking in account the
+/** Return the change status at cursor position, taking into account the
  * status at each level of the document iterator (a table in a deleted
  * footnote is deleted).
  * When \param outer is true, the top slice is not looked at.
@@ -1237,6 +1239,7 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
 
        string const argument = to_utf8(cmd.argument());
        Cursor & cur = d->cursor_;
+       Cursor old = cur;
 
        // Don't dispatch function that does not apply to internal buffers.
        if (buffer_.isInternal()
@@ -1415,20 +1418,21 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                for (Buffer * b = &buffer_; i == 0 || b != &buffer_;
                        b = theBufferList().next(b)) {
 
-                       DocIterator dit = b->getParFromID(id);
-                       if (dit.atEnd()) {
+                       Cursor cur(*this);
+                       cur.setCursor(b->getParFromID(id));
+                       if (cur.atEnd()) {
                                LYXERR(Debug::INFO, "No matching paragraph found! [" << id << "].");
                                ++i;
                                continue;
                        }
-                       LYXERR(Debug::INFO, "Paragraph " << dit.paragraph().id()
+                       LYXERR(Debug::INFO, "Paragraph " << cur.paragraph().id()
                                << " found in buffer `"
                                << b->absFileName() << "'.");
 
                        if (b == &buffer_) {
                                // Set the cursor
-                               dit.pos() = pos;
-                               setCursor(dit);
+                               cur.pos() = pos;
+                               mouseSetCursor(cur);
                                dr.screenUpdate(Update::Force | Update::FitCursor);
                        } else {
                                // Switch to other buffer view and resend cmd
@@ -1744,7 +1748,6 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                        p = Point(0, 0);
                if (act == LFUN_SCREEN_DOWN && scrolled < height_)
                        p = Point(width_, height_);
-               Cursor old = cur;
                bool const in_texted = cur.inTexted();
                cur.setCursor(doc_iterator_begin(cur.buffer()));
                cur.selHandle(false);
@@ -2066,6 +2069,21 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
 
        buffer_.undo().endUndoGroup();
        dr.dispatched(dispatched);
+
+       // NOTE: The code below is copied from Cursor::dispatch. If you
+       // need to modify this, please update the other one too.
+
+       // notify insets we just entered/left
+       if (cursor() != old) {
+               old.beginUndoGroup();
+               old.fixIfBroken();
+               bool badcursor = notifyCursorLeavesOrEnters(old, cursor());
+               if (badcursor) {
+                       cursor().fixIfBroken();
+                       resetInlineCompletionPos();
+               }
+               old.endUndoGroup();
+       }
 }
 
 
@@ -2231,6 +2249,15 @@ void BufferView::mouseEventDispatch(FuncRequest const & cmd0)
 
        // Build temporary cursor.
        Inset * inset = d->text_metrics_[&buffer_.text()].editXY(cur, cmd.x(), cmd.y());
+       if (inset) {
+               // If inset is not editable, cur.pos() might point behind the
+               // inset (depending on cmd.x(), cmd.y()). This is needed for
+               // editing to fix bug 9628, but e.g. the context menu needs a
+               // cursor in front of the inset.
+               if (inset->hasSettings() &&
+                   cur.nextInset() != inset && cur.prevInset() == inset)
+                       cur.backwardPos();
+       }
 
        // Put anchor at the same position.
        cur.resetAnchor();
@@ -2338,13 +2365,19 @@ int BufferView::scrollUp(int offset)
 
 
 void BufferView::setCursorFromRow(int row)
+{
+       setCursorFromRow(row, buffer_.texrow());
+}
+
+
+void BufferView::setCursorFromRow(int row, TexRow const & texrow)
 {
        int tmpid;
        int tmppos;
        pit_type newpit = 0;
        pos_type newpos = 0;
 
-       buffer_.texrow().getIdFromRow(row, tmpid, tmppos);
+       texrow.getIdFromRow(row, tmpid, tmppos);
 
        bool posvalid = (tmpid != -1);
        if (posvalid) {
@@ -2414,9 +2447,9 @@ void BufferView::gotoLabel(docstring const & label)
                Buffer const * buf = *it;
 
                // find label
-               Toc & toc = buf->tocBackend().toc("label");
-               TocIterator toc_it = toc.begin();
-               TocIterator end = toc.end();
+               shared_ptr<Toc> toc = buf->tocBackend().toc("label");
+               TocIterator toc_it = toc->begin();
+               TocIterator end = toc->end();
                for (; toc_it != end; ++toc_it) {
                        if (label == toc_it->str()) {
                                lyx::dispatch(toc_it->action());
@@ -2467,6 +2500,7 @@ void BufferView::setCursor(DocIterator const & dit)
 
        d->cursor_.setCursor(dit);
        d->cursor_.setSelection(false);
+       d->cursor_.setCurrentFont();
        // FIXME
        // It seems on general grounds as if this is probably needed, but
        // it is not yet clear.
@@ -2494,7 +2528,7 @@ bool BufferView::checkDepm(Cursor & cur, Cursor & old)
        d->cursor_ = cur;
 
        // we would rather not do this here, but it needs to be done before
-       // the changed() signal is sent.
+       // the changed() signal is sent.
        buffer_.updateBuffer();
 
        buffer_.changed(true);
@@ -2783,7 +2817,7 @@ Point BufferView::coordOffset(DocIterator const & dit) const
        int lastw = 0;
 
        // Addup contribution of nested insets, from inside to outside,
-       // keeping the outer paragraph for a special handling below
+       // 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;
@@ -2813,8 +2847,7 @@ Point BufferView::coordOffset(DocIterator const & dit) const
                        // 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());
+                       Dimension const & dim = coordCache().getInsets().dim(&sl.inset());
                        lastw = dim.wid;
                } else {
                        Dimension const dim = sl.inset().dimension(*this);
@@ -2953,7 +2986,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());
@@ -2964,42 +2997,19 @@ void BufferView::checkCursorScrollOffset(PainterInfo & pi)
                return;
        ParagraphMetrics const & pm = tm.parMetrics(rowSlice.pit());
        Row const & row = pm.getRow(rowSlice.pos(),
-                                   d->cursor_.boundary()
-                                   && rowSlice == d->cursor_.top());
+                                   d->cursor_.boundary() && rowSlice == d->cursor_.top());
        rowSlice.pos() = row.pos();
 
        // Set the row on which the cursor lives.
        setCurrentRowSlice(rowSlice);
 
-       /** 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. I
-        * am not sure why the extra row computation fixes the problem,
-        * actually.
-        *
-        * A proper fix should be found and this code should be removed.
-        */
-       // 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(), d->cursor_.bottom().pit(), row,
-                     -d->horiz_scroll_offset_, 0);
-       rp.paintText();
-       pi.pain.setDrawingEnabled(drawing);
-       /** END of bad code */
-
        // Current x position of the cursor in pixels
        int const cur_x = getPos(d->cursor_).x_;
 
        // Horizontal scroll offset of the cursor row in pixels
        int offset = d->horiz_scroll_offset_;
-       int const MARGIN = Length(2, Length::EM).inPixels(workWidth());
+       int const MARGIN = 2 * theFontMetrics(d->cursor_.real_current_font).em();
+       //lyxerr << "cur_x=" << cur_x << ", offset=" << offset << ", margin=" << MARGIN << endl;
        if (cur_x < offset + MARGIN) {
                // scroll right
                offset = cur_x - MARGIN;
@@ -3040,7 +3050,7 @@ 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_) {
 
@@ -3050,7 +3060,7 @@ void BufferView::draw(frontend::Painter & pain)
                LYXERR(Debug::PAINTING, "Strategy: NoScreenUpdate");
                pi.full_repaint = true;
                pi.pain.setDrawingEnabled(false);
-               tm.draw(pi, 0, y);
+               tm.draw(pi, 0, y);
                break;
 
        case SingleParUpdate:
@@ -3059,7 +3069,7 @@ void BufferView::draw(frontend::Painter & pain)
                // In general, only the current row of the outermost paragraph
                // will be redrawn. Particular cases where selection spans
                // multiple paragraph are correctly detected in TextMetrics.
-               tm.draw(pi, 0, y);
+               tm.draw(pi, 0, y);
                break;
 
        case DecorationUpdate: