X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2FBufferView.cpp;h=8a947c169fb12e30e80f6aff2bd8065a0406cd69;hb=b8f04ae29ad0e484ae010dd2364f61164cffca56;hp=faa08f0320310cd5a3eb4781d8f273f3d755b980;hpb=8fb1aa51f8d14747bd21d9e113a8dd301a2f07dc;p=lyx.git diff --git a/src/BufferView.cpp b/src/BufferView.cpp index faa08f0320..8a947c169f 100644 --- a/src/BufferView.cpp +++ b/src/BufferView.cpp @@ -35,6 +35,7 @@ #include "Language.h" #include "LaTeXFeatures.h" #include "LayoutFile.h" +#include "Length.h" #include "Lexer.h" #include "LyX.h" #include "LyXAction.h" @@ -45,6 +46,7 @@ #include "Paragraph.h" #include "ParagraphParameters.h" #include "ParIterator.h" +#include "RowPainter.h" #include "Session.h" #include "Text.h" #include "TextClass.h" @@ -225,12 +227,14 @@ enum ScreenUpdateStrategy { struct BufferView::Private { - Private(BufferView & bv): wh_(0), cursor_(bv), + Private(BufferView & bv) : update_strategy_(NoScreenUpdate), + 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) + bookmark_edit_position_(-1), gui_(0), + horiz_scroll_offset_(0) {} /// @@ -295,6 +299,16 @@ struct BufferView::Private /// map edited_insets_; + + /// When the row where the cursor lies is scrolled, this + /// contains the scroll offset + int horiz_scroll_offset_; + /// a slice pointing to the start of the row where the cursor + /// is (at last draw time) + CursorSlice current_row_slice_; + /// a slice pointing to the start of the row where cursor was + /// at previous draw event + CursorSlice last_row_slice_; }; @@ -456,8 +470,10 @@ void BufferView::processUpdateFlags(Update::flags flags) buffer_.changed(false); return; } - // no screen update is needed. + // no screen update is needed in principle, but this + // could change if cursor row needs scrolling. d->update_strategy_ = NoScreenUpdate; + buffer_.changed(false); return; } @@ -1087,6 +1103,7 @@ bool BufferView::getStatus(FuncRequest const & cmd, FuncStatus & flag) case LFUN_KEYMAP_PRIMARY: case LFUN_KEYMAP_SECONDARY: case LFUN_KEYMAP_TOGGLE: + case LFUN_INSET_SELECT_ALL: flag.setEnabled(true); break; @@ -1096,13 +1113,13 @@ bool BufferView::getStatus(FuncRequest const & cmd, FuncStatus & flag) iss >> opt; flag.setEnabled(opt.repl_buf_name.empty() || !buffer_.isReadonly()); + break; } - case LFUN_LABEL_GOTO: { + case LFUN_LABEL_GOTO: flag.setEnabled(!cmd.argument().empty() || getInsetByCode(cur, REF_CODE)); break; - } case LFUN_CHANGES_TRACK: flag.setEnabled(true); @@ -1235,7 +1252,7 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr) case LFUN_BUFFER_PARAMS_APPLY: { DocumentClassConstPtr olddc = buffer_.params().documentClassPtr(); - cur.recordUndoFullDocument(); + cur.recordUndoBufferParams(); istringstream ss(to_utf8(cmd.argument())); Lexer lex; lex.setStream(ss); @@ -1256,7 +1273,10 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr) } case LFUN_LAYOUT_MODULES_CLEAR: { - cur.recordUndoFullDocument(); + // FIXME: this modifies the document in cap::switchBetweenClasses + // without calling recordUndo. Fix this before using + // recordUndoBufferParams(). + cur.recordUndoFullBuffer(); buffer_.params().clearLayoutModules(); makeDocumentClass(); dr.screenUpdate(Update::Force); @@ -1272,7 +1292,10 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr) "conflicts with installed modules."); break; } - cur.recordUndoFullDocument(); + // FIXME: this modifies the document in cap::switchBetweenClasses + // without calling recordUndo. Fix this before using + // recordUndoBufferParams(). + cur.recordUndoFullBuffer(); buffer_.params().addLayoutModule(argument); makeDocumentClass(); dr.screenUpdate(Update::Force); @@ -1301,7 +1324,10 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr) break; // Save the old, possibly modular, layout for use in conversion. - cur.recordUndoFullDocument(); + // FIXME: this modifies the document in cap::switchBetweenClasses + // without calling recordUndo. Fix this before using + // recordUndoBufferParams(). + cur.recordUndoFullBuffer(); buffer_.params().setBaseClass(argument); makeDocumentClass(); dr.screenUpdate(Update::Force); @@ -1389,20 +1415,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 @@ -1721,6 +1748,8 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr) Cursor old = cur; bool const in_texted = cur.inTexted(); cur.setCursor(doc_iterator_begin(cur.buffer())); + if (cur != old) + notifyCursorLeavesOrEnters(old, cur); cur.selHandle(false); buffer_.changed(true); updateHoveredInset(); @@ -1800,6 +1829,41 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr) } + case LFUN_INSET_SELECT_ALL: + if (cur.depth() > 1 + && cur.selBegin().at_begin() + && cur.selEnd().at_end()) { + // All the contents of the inset if selected. + // Select the inset from outside. + cur.pop(); + cur.resetAnchor(); + cur.setSelection(true); + cur.posForward(); + } else if (cur.selBegin().idx() != cur.selEnd().idx() + || (cur.depth() > 1 + && cur.selBegin().at_cell_begin() + && cur.selEnd().at_cell_end())) { + // At least one complete cell is selected. + // Select all cells + cur.idx() = 0; + cur.pos() = 0; + cur.resetAnchor(); + cur.setSelection(true); + cur.idx() = cur.lastidx(); + cur.pos() = cur.lastpos(); + } else { + // select current cell + cur.pit() = 0; + cur.pos() = 0; + cur.resetAnchor(); + cur.setSelection(true); + cur.pit() = cur.lastpit(); + cur.pos() = cur.lastpos(); + } + dr.screenUpdate(Update::Force); + break; + + // This would be in Buffer class if only Cursor did not // require a bufferview case LFUN_INSET_FORALL: { @@ -1977,7 +2041,7 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr) if (!newL || oldL == newL) break; if (oldL->rightToLeft() == newL->rightToLeft()) { - cur.recordUndoFullDocument(); + cur.recordUndoFullBuffer(); buffer_.changeLanguage(oldL, newL); cur.setCurrentFont(); dr.forceBufferUpdate(); @@ -2853,6 +2917,119 @@ bool BufferView::cursorInView(Point const & p, int h) const } +int BufferView::horizScrollOffset() const +{ + return d->horiz_scroll_offset_; +} + + +CursorSlice const & BufferView::currentRowSlice() const +{ + return d->current_row_slice_; +} + + +CursorSlice const & BufferView::lastRowSlice() const +{ + return d->last_row_slice_; +} + + +void BufferView::setCurrentRowSlice(CursorSlice const & rowSlice) +{ + // nothing to do if the cursor was already on this row + if (d->current_row_slice_ == rowSlice) { + d->last_row_slice_ = CursorSlice(); + return; + } + + // if the (previous) current row was scrolled, we have to + // remember it in order to repaint it next time. + if (d->horiz_scroll_offset_ != 0) + d->last_row_slice_ = d->current_row_slice_; + else + d->last_row_slice_ = CursorSlice(); + + // Since we changed row, the scroll offset is not valid anymore + d->horiz_scroll_offset_ = 0; + d->current_row_slice_ = rowSlice; +} + + +void BufferView::checkCursorScrollOffset(PainterInfo & pi) +{ + CursorSlice rowSlice = d->cursor_.bottom(); + TextMetrics const & tm = textMetrics(rowSlice.text()); + + // Stop if metrics have not been computed yet, since it means + // that there is nothing to do. + if (!tm.contains(rowSlice.pit())) + return; + ParagraphMetrics const & pm = tm.parMetrics(rowSlice.pit()); + Row const & row = pm.getRow(rowSlice.pos(), + 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(pi.base); + if (cur_x < offset + MARGIN) { + // scroll right + offset = cur_x - MARGIN; + } else if (cur_x > offset + workWidth() - MARGIN) { + // scroll left + offset = cur_x - workWidth() + MARGIN; + } + + if (offset < row.left_margin || row.width() <= workWidth()) + offset = 0; + + if (offset != d->horiz_scroll_offset_) + LYXERR(Debug::PAINTING, "Horiz. scroll offset changed from " + << d->horiz_scroll_offset_ << " to " << offset); + + if (d->update_strategy_ == NoScreenUpdate + && (offset != d->horiz_scroll_offset_ + || !d->last_row_slice_.empty())) { + // FIXME: if one uses SingleParUpdate, then home/end + // will not work on long rows. Why? + d->update_strategy_ = FullScreenUpdate; + } + + d->horiz_scroll_offset_ = offset; +} + + void BufferView::draw(frontend::Painter & pain) { if (height_ == 0 || width_ == 0) @@ -2864,11 +3041,16 @@ void BufferView::draw(frontend::Painter & pain) int const y = tm.first().second->position(); PainterInfo pi(this, pain); + // Check whether the row where the cursor lives needs to be scrolled. + // Update the drawing strategy if needed. + checkCursorScrollOffset(pi); + 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. + LYXERR(Debug::PAINTING, "Strategy: NoScreenUpdate"); pi.full_repaint = true; pi.pain.setDrawingEnabled(false); tm.draw(pi, 0, y); @@ -2876,6 +3058,7 @@ void BufferView::draw(frontend::Painter & pain) case SingleParUpdate: pi.full_repaint = false; + LYXERR(Debug::PAINTING, "Strategy: SingleParUpdate"); // 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. @@ -2888,6 +3071,12 @@ void BufferView::draw(frontend::Painter & pain) // because of the single backing pixmap. case FullScreenUpdate: + + LYXERR(Debug::PAINTING, + ((d->update_strategy_ == FullScreenUpdate) + ? "Strategy: FullScreenUpdate" + : "Strategy: DecorationUpdate")); + // The whole screen, including insets, will be refreshed. pi.full_repaint = true;