X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2FBufferView.cpp;h=e316aab02e2d38319d2d5dd6df5d7cabb106a90d;hb=294e4884ee29585d311177406cd31499e6d81877;hp=2068f050cfeddf283516763aecc57adab3c6a8e6;hpb=d02244c8fbce89ee928af769a686b2b1036c0a36;p=lyx.git diff --git a/src/BufferView.cpp b/src/BufferView.cpp index 2068f050cf..e316aab02e 100644 --- a/src/BufferView.cpp +++ b/src/BufferView.cpp @@ -35,7 +35,6 @@ #include "Language.h" #include "LaTeXFeatures.h" #include "LayoutFile.h" -#include "Length.h" #include "Lexer.h" #include "LyX.h" #include "LyXAction.h" @@ -81,6 +80,7 @@ #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" @@ -110,7 +110,7 @@ T * getInsetByCode(Cursor const & cur, InsetCode code) Inset * inset = it.nextInset(); if (inset && inset->lyxCode() == code) return static_cast(inset); - return 0; + return nullptr; } @@ -179,13 +179,6 @@ bool findInset(DocIterator & dit, vector const & codes, } -/// Looks for next inset with the given code -void findInset(DocIterator & dit, InsetCode code, bool same_content) -{ - findInset(dit, vector(1, code), same_content); -} - - /// Moves cursor to the next inset with one of the given codes. void gotoInset(BufferView * bv, vector const & codes, bool same_content) @@ -202,13 +195,6 @@ void gotoInset(BufferView * bv, vector const & codes, } -/// Moves cursor to the next inset with given code. -void gotoInset(BufferView * bv, InsetCode code, bool same_content) -{ - gotoInset(bv, vector(1, code), same_content); -} - - /// A map from a Text to the associated text metrics typedef map TextMetricsCache; @@ -233,13 +219,11 @@ struct BufferView::Private 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) + cursor_(bv), anchor_pit_(0), anchor_ypos_(0), + wh_(0), inlineCompletionUniqueChars_(0), + last_inset_(nullptr), mouse_position_cache_(), + gui_(nullptr), bookmark_edit_position_(-1), + horiz_scroll_offset_(0), clickable_inset_(false) { xsel_cache_.set = false; } @@ -256,8 +240,6 @@ struct BufferView::Private typedef map MathRows; MathRows math_rows_; - /// Estimated average par height for scrollbar. - int wh_; /// this is used to handle XSelection events in the right manner. struct { CursorSlice cursor; @@ -270,6 +252,8 @@ struct BufferView::Private pit_type anchor_pit_; /// int anchor_ypos_; + /// Estimated average par height for scrollbar. + int wh_; /// vector par_height_; @@ -288,17 +272,12 @@ struct BufferView::Private * Not owned, so don't delete. */ Inset const * last_inset_; - /// are we hovering something that we can click - bool clickable_inset_; /// position of the mouse at the time of the last mouse move /// This is used to update the hovering status of inset in /// cases where the buffer is scrolled, but the mouse didn't move. Point mouse_position_cache_; - // cache for id of the paragraph which was edited the last time - int bookmark_edit_position_; - mutable TextMetricsCache text_metrics_; /// Whom to notify. @@ -314,10 +293,15 @@ struct BufferView::Private /// When the row where the cursor lies is scrolled, this /// contains the scroll offset + // cache for id of the paragraph which was edited the last time + int bookmark_edit_position_; + 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_; + /// are we hovering something that we can click + bool clickable_inset_; }; @@ -345,9 +329,10 @@ BufferView::~BufferView() // That is to say, if a cursor is in a nested inset, it will be // restore to the left of the top level inset. LastFilePosSection::FilePos fp; + fp.file = buffer_.fileName(); fp.pit = d->cursor_.bottom().pit(); fp.pos = d->cursor_.bottom().pos(); - theSession().lastFilePos().save(buffer_.fileName(), fp); + theSession().lastFilePos().save(fp); if (d->last_inset_) d->last_inset_->setMouseHover(this, false); @@ -375,6 +360,20 @@ int BufferView::leftMargin() const } +int BufferView::topMargin() const +{ + // original value was 20px, which is 0.2in at 100dpi + return zoomedPixels(20); +} + + +int BufferView::bottomMargin() const +{ + // original value was 20px, which is 0.2in at 100dpi + return zoomedPixels(20); +} + + int BufferView::inPixels(Length const & len) const { Font const font = buffer().params().getFont(); @@ -526,9 +525,18 @@ void BufferView::processUpdateFlags(Update::flags flags) // Then make sure that the screen contains the cursor if needed if (flags & Update::FitCursor) { if (needsFitCursor()) { - scrollToCursor(d->cursor_, false); + // First try to make the selection start visible + // (which is just the cursor when there is no selection) + scrollToCursor(d->cursor_.selectionBegin(), false); // Metrics have to be recomputed (maybe again) - updateMetrics(flags); + updateMetrics(); + // Is the cursor visible? (only useful if cursor is at end of selection) + if (needsFitCursor()) { + // then try to make cursor visible instead + scrollToCursor(d->cursor_, false); + // Metrics have to be recomputed (maybe again) + updateMetrics(flags); + } } flags = flags & ~Update::FitCursor; } @@ -930,7 +938,10 @@ bool BufferView::scrollToCursor(DocIterator const & dit, bool const recenter) if (height_ == 0) return false; - LYXERR(Debug::SCROLLING, "recentering!"); + if (recenter) + LYXERR(Debug::SCROLLING, "recentering and scrolling to cursor"); + else + LYXERR(Debug::SCROLLING, "scrolling to cursor"); CursorSlice const & bot = dit.bottom(); TextMetrics & tm = d->text_metrics_[bot.text()]; @@ -1259,7 +1270,7 @@ bool BufferView::getStatus(FuncRequest const & cmd, FuncStatus & flag) Inset * BufferView::editedInset(string const & name) const { map::const_iterator it = d->edited_insets_.find(name); - return it == d->edited_insets_.end() ? 0 : it->second; + return it == d->edited_insets_.end() ? nullptr : it->second; } @@ -1519,25 +1530,36 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr) } case LFUN_NOTE_NEXT: - gotoInset(this, NOTE_CODE, false); + gotoInset(this, { NOTE_CODE }, false); + // FIXME: if SinglePar is changed to act on the inner + // paragraph, this will not be OK anymore. The update is + // useful for auto-open collapsible insets. + dr.screenUpdate(Update::SinglePar | Update::FitCursor); break; case LFUN_REFERENCE_NEXT: { - vector tmp; - tmp.push_back(LABEL_CODE); - tmp.push_back(REF_CODE); - gotoInset(this, tmp, true); + gotoInset(this, { LABEL_CODE, REF_CODE }, true); + // FIXME: if SinglePar is changed to act on the inner + // paragraph, this will not be OK anymore. The update is + // useful for auto-open collapsible insets. + dr.screenUpdate(Update::SinglePar | Update::FitCursor); break; } case LFUN_CHANGE_NEXT: findNextChange(this); + if (cur.inset().isTable()) + // In tables, there might be whole changed rows or columns + cur.dispatch(cmd); // FIXME: Move this LFUN to Buffer so that we don't have to do this: dr.screenUpdate(Update::Force | Update::FitCursor); break; case LFUN_CHANGE_PREVIOUS: findPreviousChange(this); + if (cur.inset().isTable()) + // In tables, there might be whole changed rows or columns + cur.dispatch(cmd); // FIXME: Move this LFUN to Buffer so that we don't have to do this: dr.screenUpdate(Update::Force | Update::FitCursor); break; @@ -1550,32 +1572,43 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr) } break; - case LFUN_ALL_CHANGES_ACCEPT: + case LFUN_ALL_CHANGES_ACCEPT: { // select complete document cur.reset(); cur.selHandle(true); buffer_.text().cursorBottom(cur); // accept everything in a single step to support atomic undo + // temporarily disable track changes in order to end with really + // no new (e.g., DPSM-caused) changes (see #7487) + bool const track = buffer_.params().track_changes; + buffer_.params().track_changes = false; buffer_.text().acceptOrRejectChanges(cur, Text::ACCEPT); + buffer_.params().track_changes = track; cur.resetAnchor(); // FIXME: Move this LFUN to Buffer so that we don't have to do this: dr.screenUpdate(Update::Force | Update::FitCursor); dr.forceBufferUpdate(); break; + } - case LFUN_ALL_CHANGES_REJECT: + case LFUN_ALL_CHANGES_REJECT: { // select complete document cur.reset(); cur.selHandle(true); buffer_.text().cursorBottom(cur); // reject everything in a single step to support atomic undo - // Note: reject does not work recursively; the user may have to repeat the operation + // temporarily disable track changes in order to end with really + // no new (e.g., DPSM-caused) changes (see #7487) + bool const track = buffer_.params().track_changes; + buffer_.params().track_changes = false; buffer_.text().acceptOrRejectChanges(cur, Text::REJECT); + buffer_.params().track_changes = track; cur.resetAnchor(); // FIXME: Move this LFUN to Buffer so that we don't have to do this: dr.screenUpdate(Update::Force | Update::FitCursor); dr.forceBufferUpdate(); break; + } case LFUN_WORD_FIND_FORWARD: case LFUN_WORD_FIND_BACKWARD: { @@ -1687,7 +1720,7 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr) case LFUN_BIBTEX_DATABASE_ADD: { Cursor tmpcur = cur; - findInset(tmpcur, BIBTEX_CODE, false); + findInset(tmpcur, { BIBTEX_CODE }, false); InsetBibtex * inset = getInsetByCode(tmpcur, BIBTEX_CODE); if (inset) { @@ -1699,7 +1732,7 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr) case LFUN_BIBTEX_DATABASE_DEL: { Cursor tmpcur = cur; - findInset(tmpcur, BIBTEX_CODE, false); + findInset(tmpcur, { BIBTEX_CODE }, false); InsetBibtex * inset = getInsetByCode(tmpcur, BIBTEX_CODE); if (inset) { @@ -2182,7 +2215,7 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr) } -docstring const BufferView::requestSelection() +docstring BufferView::requestSelection() { Cursor & cur = d->cursor_; @@ -2239,7 +2272,7 @@ Inset const * BufferView::getCoveringInset(Text const & text, TextMetrics & tm = d->text_metrics_[&text]; Inset * inset = tm.checkInsetHit(x, y); if (!inset) - return 0; + return nullptr; if (!inset->descendable(*this)) // No need to go further down if the inset is not @@ -2280,7 +2313,7 @@ void BufferView::updateHoveredInset() const if (d->last_inset_) { // Remove the hint on the last hovered inset (if any). need_redraw |= d->last_inset_->setMouseHover(this, false); - d->last_inset_ = 0; + d->last_inset_ = nullptr; } if (covering_inset && covering_inset->setMouseHover(this, true)) { @@ -2311,7 +2344,7 @@ void BufferView::clearLastInset(Inset * inset) const LYXERR0("Wrong last_inset!"); LATTEST(false); } - d->last_inset_ = 0; + d->last_inset_ = nullptr; } @@ -2744,7 +2777,7 @@ bool BufferView::singleParUpdate() Text & buftext = buffer_.text(); pit_type const bottom_pit = d->cursor_.bottom().pit(); TextMetrics & tm = textMetrics(&buftext); - int old_height = tm.parMetrics(bottom_pit).height(); + Dimension const old_dim = tm.parMetrics(bottom_pit).dim(); // make sure inline completion pointer is ok if (d->inlineCompletionPos_.fixIfBroken()) @@ -2755,11 +2788,16 @@ bool BufferView::singleParUpdate() // (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) + ParagraphMetrics & pm = tm.parMetrics(bottom_pit); + if (pm.height() != old_dim.height()) { // Paragraph height has changed so we cannot proceed to // the singlePar optimisation. return false; + } + // Since position() points to the baseline of the first row, we + // may have to update it. See ticket #11601 for an example where + // the height does not change but the ascent does. + pm.setPosition(pm.position() - old_dim.ascent() + pm.ascent()); tm.updatePosCache(bottom_pit); @@ -2807,7 +2845,7 @@ void BufferView::updateMetrics(Update::flags & update_flags) // Rebreak anchor paragraph. tm.redoParagraph(d->anchor_pit_); - ParagraphMetrics & anchor_pm = tm.par_metrics_[d->anchor_pit_]; + ParagraphMetrics & anchor_pm = tm.parMetrics(d->anchor_pit_); // position anchor if (d->anchor_pit_ == 0) { @@ -2834,7 +2872,7 @@ void BufferView::updateMetrics(Update::flags & update_flags) pit_type pit1 = d->anchor_pit_ - 1; for (; pit1 >= 0 && y1 >= 0; --pit1) { tm.redoParagraph(pit1); - ParagraphMetrics & pm = tm.par_metrics_[pit1]; + ParagraphMetrics & pm = tm.parMetrics(pit1); y1 -= pm.descent(); // Save the paragraph position in the cache. pm.setPosition(y1); @@ -2848,7 +2886,7 @@ void BufferView::updateMetrics(Update::flags & update_flags) pit_type pit2 = d->anchor_pit_ + 1; for (; pit2 < npit && y2 <= height_; ++pit2) { tm.redoParagraph(pit2); - ParagraphMetrics & pm = tm.par_metrics_[pit2]; + ParagraphMetrics & pm = tm.parMetrics(pit2); y2 += pm.ascent(); // Save the paragraph position in the cache. pm.setPosition(y2); @@ -3044,11 +3082,16 @@ void BufferView::caretPosAndHeight(Point & p, int & h) const } -bool BufferView::cursorInView(Point const & p, int h) const +bool BufferView::caretInView() const { - Cursor const & cur = cursor(); + if (!paragraphVisible(cursor())) + return false; + Point p; + int h; + caretPosAndHeight(p, h); + // does the cursor touch the screen ? - if (p.y_ + h < 0 || p.y_ >= workHeight() || !paragraphVisible(cur)) + if (p.y_ + h < 0 || p.y_ >= workHeight()) return false; return true; } @@ -3179,7 +3222,6 @@ void BufferView::draw(frontend::Painter & pain, bool paint_caret) // however, the different coordinates of insets and paragraphs // needs to be updated. LYXERR(Debug::PAINTING, "Strategy: NoScreenUpdate"); - pi.full_repaint = false; if (pain.isNull()) { pi.full_repaint = true; tm.draw(pi, 0, y);