X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2FBufferView.cpp;h=6922770f2966c558fe8d28f867c094f43eeeed0d;hb=57b69a5efddf9f3c148007322f00dad6c253a2ed;hp=658fa0e6000225e237618af2789092dfb961857e;hpb=1db691c2f501c07c38ffaa69cd1af18e1f77f4dc;p=lyx.git diff --git a/src/BufferView.cpp b/src/BufferView.cpp index 658fa0e600..6922770f29 100644 --- a/src/BufferView.cpp +++ b/src/BufferView.cpp @@ -351,11 +351,12 @@ BufferView::~BufferView() int BufferView::rightMargin() const { + // The value used to be hardcoded to 10, which is 2.5mm at 100dpi + int const default_margin = Length(2.5, Length::MM).inPixels(0); // The additional test for the case the outliner is opened. - if (!full_screen_ || - !lyxrc.full_screen_limit || - width_ < lyxrc.full_screen_width + 20) - return 10; + if (!full_screen_ || !lyxrc.full_screen_limit + || width_ < lyxrc.full_screen_width + 2 * default_margin) + return default_margin; return (width_ - lyxrc.full_screen_width) / 2; } @@ -369,13 +370,13 @@ int BufferView::leftMargin() const bool BufferView::isTopScreen() const { - return d->scrollbarParameters_.position == d->scrollbarParameters_.min; + return 0 == d->scrollbarParameters_.min; } bool BufferView::isBottomScreen() const { - return d->scrollbarParameters_.position == d->scrollbarParameters_.max; + return 0 == d->scrollbarParameters_.max; } @@ -433,7 +434,7 @@ bool BufferView::needsFitCursor() const void BufferView::processUpdateFlags(Update::flags flags) { // This is close to a hot-path. - LYXERR(Debug::DEBUG, "BufferView::processUpdateFlags()" + LYXERR(Debug::PAINTING, "BufferView::processUpdateFlags()" << "[fitcursor = " << (flags & Update::FitCursor) << ", forceupdate = " << (flags & Update::Force) << ", singlepar = " << (flags & Update::SinglePar) @@ -558,7 +559,6 @@ void BufferView::updateScrollbar() for (size_t i = last.first + 1; i != parsize; ++i) d->scrollbarParameters_.max += d->par_height_[i]; - d->scrollbarParameters_.position = 0; // The reference is the top position so we remove one page. if (lyxrc.scroll_below_document) d->scrollbarParameters_.max -= minVisiblePart(); @@ -599,17 +599,19 @@ string BufferView::contextMenu(int x, int y) const } -void BufferView::scrollDocView(int value, bool update) + +void BufferView::scrollDocView(int const value, bool update) { - int const offset = value - d->scrollbarParameters_.position; + // 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 (offset == 0) + if (value == 0) return; // If the offset is less than 2 screen height, prefer to scroll instead. - if (abs(offset) <= 2 * height_) { - d->anchor_ypos_ -= offset; + if (abs(value) <= 2 * height_) { + d->anchor_ypos_ -= value; buffer_.changed(true); updateHoveredInset(); return; @@ -706,9 +708,7 @@ Change const BufferView::getCurrentChange() const DocIterator dit = d->cursor_.selectionBegin(); // The selected content might have been changed (see #7685) - while (dit.inMathed()) - // Find enclosing text cursor - dit.pop_back(); + dit = dit.getInnerText(); return dit.paragraph().lookupChange(dit.pos()); } @@ -808,6 +808,8 @@ bool BufferView::moveToPosition(pit_type bottom_pit, pos_type bottom_pos, setCursor(dit); // set the current font. 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! @@ -1124,37 +1126,13 @@ bool BufferView::getStatus(FuncRequest const & cmd, FuncStatus & flag) || getInsetByCode(cur, REF_CODE)); break; - case LFUN_CHANGES_TRACK: - flag.setEnabled(true); - flag.setOnOff(buffer_.params().track_changes); - break; - - case LFUN_CHANGES_OUTPUT: - flag.setEnabled(true); - flag.setOnOff(buffer_.params().output_changes); - break; - case LFUN_CHANGES_MERGE: case LFUN_CHANGE_NEXT: case LFUN_CHANGE_PREVIOUS: case LFUN_ALL_CHANGES_ACCEPT: case LFUN_ALL_CHANGES_REJECT: - // TODO: context-sensitive enabling of LFUNs - // In principle, these command should only be enabled if there - // is a change in the document. However, without proper - // optimizations, this will inevitably result in poor performance. - flag.setEnabled(true); - break; - - case LFUN_BUFFER_TOGGLE_COMPRESSION: { - flag.setOnOff(buffer_.params().compressed); + flag.setEnabled(buffer_.areChangesPresent()); break; - } - - case LFUN_BUFFER_TOGGLE_OUTPUT_SYNC: { - flag.setOnOff(buffer_.params().output_sync); - break; - } case LFUN_SCREEN_UP: case LFUN_SCREEN_DOWN: @@ -1458,33 +1436,6 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr) break; } - case LFUN_CHANGES_TRACK: - buffer_.params().track_changes = !buffer_.params().track_changes; - break; - - case LFUN_CHANGES_OUTPUT: - buffer_.params().output_changes = !buffer_.params().output_changes; - if (buffer_.params().output_changes) { - bool dvipost = LaTeXFeatures::isAvailable("dvipost"); - bool xcolorulem = LaTeXFeatures::isAvailable("ulem") && - LaTeXFeatures::isAvailable("xcolor"); - - if (!dvipost && !xcolorulem) { - Alert::warning(_("Changes not shown in LaTeX output"), - _("Changes will not be highlighted in LaTeX output, " - "because neither dvipost nor xcolor/ulem are installed.\n" - "Please install these packages or redefine " - "\\lyxadded and \\lyxdeleted in the LaTeX preamble.")); - } else if (!xcolorulem) { - Alert::warning(_("Changes not shown in LaTeX output"), - _("Changes will not be highlighted in LaTeX output " - "when using pdflatex, because xcolor and ulem are not installed.\n" - "Please install both packages or redefine " - "\\lyxadded and \\lyxdeleted in the LaTeX preamble.")); - } - } - break; - case LFUN_CHANGE_NEXT: findNextChange(this); // FIXME: Move this LFUN to Buffer so that we don't have to do this: @@ -1637,7 +1588,7 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr) break; case LFUN_MARK_TOGGLE: - cur.setSelection(false); + cur.selection(false); if (cur.mark()) { cur.setMark(false); dr.setMessage(from_utf8(N_("Mark removed"))); @@ -1724,15 +1675,6 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr) } break; - case LFUN_BUFFER_TOGGLE_COMPRESSION: - // turn compression on/off - buffer_.params().compressed = !buffer_.params().compressed; - break; - - case LFUN_BUFFER_TOGGLE_OUTPUT_SYNC: - buffer_.params().output_sync = !buffer_.params().output_sync; - break; - case LFUN_SCREEN_UP: case LFUN_SCREEN_DOWN: { Point p = getPos(cur); @@ -1830,26 +1772,30 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr) } - case LFUN_INSET_SELECT_ALL: - if (cur.depth() > 1 + case LFUN_INSET_SELECT_ALL: { + // true if all cells are selected + bool const all_selected = cur.depth() > 1 && cur.selBegin().at_begin() - && cur.selEnd().at_end()) { - // All the contents of the inset if selected. + && cur.selEnd().at_end(); + // true if some cells are selected + bool const cells_selected = cur.depth() > 1 + && cur.selBegin().at_cell_begin() + && cur.selEnd().at_cell_end(); + if (all_selected || (cells_selected && !cur.inset().isTable())) { + // All the contents of the inset if selected, or only at + // least one cell but inset is not a table. // Select the inset from outside. cur.pop(); cur.resetAnchor(); - cur.setSelection(true); + cur.selection(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. + } else if (cells_selected) { + // At least one complete cell is selected and inset is a table. // Select all cells cur.idx() = 0; cur.pos() = 0; cur.resetAnchor(); - cur.setSelection(true); + cur.selection(true); cur.idx() = cur.lastidx(); cur.pos() = cur.lastpos(); } else { @@ -1857,12 +1803,14 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr) cur.pit() = 0; cur.pos() = 0; cur.resetAnchor(); - cur.setSelection(true); + cur.selection(true); cur.pit() = cur.lastpit(); cur.pos() = cur.lastpos(); } + cur.setCurrentFont(); dr.screenUpdate(Update::Force); break; + } // This would be in Buffer class if only Cursor did not @@ -1902,9 +1850,17 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr) if (!cur.nextInset() || cur.nextInset() == ins) cur.forwardInset(); } - cur.endUndoGroup(); cur = savecur; cur.fixIfBroken(); + /** This is a dummy undo record only to remember the cursor + * that has just been set; this will be used on a redo action + * (see ticket #10097) + + * FIXME: a better fix would be to have a way to set the + * cursor value directly, but I am not sure it is worth it. + */ + cur.recordUndo(); + cur.endUndoGroup(); dr.screenUpdate(Update::Force); dr.forceBufferUpdate(); @@ -2176,7 +2132,7 @@ void BufferView::updateHoveredInset() const int const y = d->mouse_position_cache_.y_; Inset const * covering_inset = getCoveringInset(buffer_.text(), x, y); - d->clickable_inset_ = covering_inset && covering_inset->clickable(x, y); + d->clickable_inset_ = covering_inset && covering_inset->clickable(*this, x, y); if (covering_inset == d->last_inset_) // Same inset, no need to do anything... @@ -2232,7 +2188,7 @@ void BufferView::mouseEventDispatch(FuncRequest const & cmd0) Cursor old = cursor(); Cursor cur(*this); cur.push(buffer_.inset()); - cur.setSelection(d->cursor_.selection()); + cur.selection(d->cursor_.selection()); // Either the inset under the cursor or the // surrounding Text will handle this event. @@ -2255,9 +2211,14 @@ void BufferView::mouseEventDispatch(FuncRequest const & cmd0) // 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() && + if ((inset->hasSettings() || !inset->contextMenuName().empty() + || inset->lyxCode() == SEPARATOR_CODE) && cur.nextInset() != inset && cur.prevInset() == inset) - cur.backwardPos(); + cur.posBackward(); + } else if (cur.inTexted() && cur.pos() + && cur.paragraph().isEnvSeparator(cur.pos() - 1)) { + // Always place cursor in front of a separator inset. + cur.posBackward(); } // Put anchor at the same position. @@ -2277,8 +2238,6 @@ void BufferView::mouseEventDispatch(FuncRequest const & cmd0) if (!inset || !cur.result().dispatched()) cur.dispatch(cmd); - cur.endUndoGroup(); - // Notify left insets if (cur != old) { bool badcursor = old.fixIfBroken() | cur.fixIfBroken(); @@ -2287,6 +2246,8 @@ void BufferView::mouseEventDispatch(FuncRequest const & cmd0) cursor().fixIfBroken(); } + cur.endUndoGroup(); + // Do we have a selection? theSelection().haveSelection(cursor().selection()); @@ -2414,7 +2375,7 @@ void BufferView::setCursorFromRow(int row, TexRow const & texrow) } d->cursor_.reset(); buffer_.text().setCursor(d->cursor_, newpit, newpos); - d->cursor_.setSelection(false); + d->cursor_.selection(false); d->cursor_.resetAnchor(); recenter(); } @@ -2449,8 +2410,8 @@ void BufferView::gotoLabel(docstring const & label) // find label shared_ptr toc = buf->tocBackend().toc("label"); - TocIterator toc_it = toc->begin(); - TocIterator end = toc->end(); + Toc::const_iterator toc_it = toc->begin(); + Toc::const_iterator end = toc->end(); for (; toc_it != end; ++toc_it) { if (label == toc_it->str()) { lyx::dispatch(toc_it->action()); @@ -2500,7 +2461,7 @@ void BufferView::setCursor(DocIterator const & dit) dit[i].inset().edit(d->cursor_, true); d->cursor_.setCursor(dit); - d->cursor_.setSelection(false); + d->cursor_.selection(false); d->cursor_.setCurrentFont(); // FIXME // It seems on general grounds as if this is probably needed, but @@ -2629,7 +2590,7 @@ bool BufferView::selectIfEmpty(DocIterator & cur) d->cursor_.setCursor(cur); d->cursor_.pit() = beg_pit; d->cursor_.pos() = 0; - d->cursor_.setSelection(false); + d->cursor_.selection(false); d->cursor_.resetAnchor(); d->cursor_.pit() = end_pit; d->cursor_.pos() = end_pos; @@ -2954,15 +2915,26 @@ int BufferView::horizScrollOffset() const } -CursorSlice const & BufferView::currentRowSlice() const +int BufferView::horizScrollOffset(Text const * text, + pit_type pit, pos_type pos) const { - return d->current_row_slice_; + // Is this a row that is currently scrolled? + if (!d->current_row_slice_.empty() + && &text->inset() == d->current_row_slice_.inset().asInsetText() + && pit == d->current_row_slice_.pit() + && pos == d->current_row_slice_.pos()) + return d->horiz_scroll_offset_; + return 0; } -CursorSlice const & BufferView::lastRowSlice() const +bool BufferView::hadHorizScrollOffset(Text const * text, + pit_type pit, pos_type pos) const { - return d->last_row_slice_; + return !d->last_row_slice_.empty() + && &text->inset() == d->last_row_slice_.inset().asInsetText() + && pit == d->last_row_slice_.pit() + && pos == d->last_row_slice_.pos(); } @@ -3004,13 +2976,9 @@ void BufferView::checkCursorScrollOffset(PainterInfo & pi) // Set the row on which the cursor lives. setCurrentRowSlice(rowSlice); - // Current x position of the cursor in pixels - int cur_x = getPos(d->cursor_).x_; - - // If cursor offset is left margin and offset is not the leftmost - // position of the row, there is a cache problem. - if (cur_x == row.left_margin && !row.empty() - && d->cursor_.pos() != row.front().left_pos()) { + // 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. @@ -3025,35 +2993,44 @@ void BufferView::checkCursorScrollOffset(PainterInfo & pi) * cache. This would not happen if we did not have two-stage * drawing. * - * A proper fix should be found and this code should be removed. + * 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(), d->cursor_.bottom().pit(), row, - -d->horiz_scroll_offset_, 0); + RowPainter rp(pi, buffer().text(), row, -d->horiz_scroll_offset_, 0); rp.paintText(); pi.pain.setDrawingEnabled(drawing); - - // Recompute current Current x position of the cursor in pixels - cur_x = getPos(d->cursor_).x_; } + // Current x position of the cursor in pixels + int cur_x = getPos(d->cursor_).x_; + // Horizontal scroll offset of the cursor row in pixels int offset = d->horiz_scroll_offset_; - 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; - } else if (cur_x > offset + workWidth() - MARGIN) { - // scroll left - offset = cur_x - workWidth() + MARGIN; + int const MARGIN = 2 * theFontMetrics(d->cursor_.real_current_font).em() + + row.right_margin; + if (row.right_x() <= workWidth() - row.right_margin) { + // Row is narrower than the work area, no offset needed. + offset = 0; + } else { + if (cur_x - offset < MARGIN) { + // cursor would be too far right + offset = cur_x - MARGIN; + } else if (cur_x - offset > workWidth() - MARGIN) { + // cursor would be too far left + offset = cur_x - workWidth() + MARGIN; + } + // Correct the offset to make sure that we do not scroll too much + if (offset < 0) + offset = 0; + if (row.right_x() - offset < workWidth() - row.right_margin) + offset = row.right_x() - workWidth() + row.right_margin; } - if (offset < row.left_margin || row.width() <= workWidth()) - offset = 0; + //lyxerr << "cur_x=" << cur_x << ", offset=" << offset << ", row.wid=" << row.width() << ", margin=" << MARGIN << endl; if (offset != d->horiz_scroll_offset_) LYXERR(Debug::PAINTING, "Horiz. scroll offset changed from "