Inset * inset = it.nextInset();
if (inset && inset->lyxCode() == code)
return static_cast<T*>(inset);
- return 0;
+ return nullptr;
}
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),
- caret_ascent_(0), caret_descent_(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;
}
Update::flags update_flags_;
///
CoordCache coord_cache_;
+ ///
+ typedef map<MathData const *, MathRow> 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;
pit_type anchor_pit_;
///
int anchor_ypos_;
+ /// Estimated average par height for scrollbar.
+ int wh_;
///
vector<int> par_height_;
* 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.
/// 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_;
- /// a slice pointing to the start of the row where cursor was
- /// at previous draw event
- CursorSlice last_row_slice_;
-
- // The vertical size of the blinking caret. Only used for math
- // Using it for text could be bad when undo restores the cursor
- // current font, since the caret size could become wrong.
- int caret_ascent_;
- int caret_descent_;
+ /// are we hovering something that we can click
+ bool clickable_inset_;
};
// 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);
}
+MathRow const & BufferView::mathRow(MathData const * cell) const
+{
+ auto it = d->math_rows_.find(cell);
+ LATTEST(it != d->math_rows_.end());
+ return it->second;
+}
+
+
+void BufferView::setMathRow(MathData const * cell, MathRow const & mrow)
+{
+ d->math_rows_[cell] = mrow;
+}
+
+
Buffer & BufferView::buffer()
{
return buffer_;
*/
buffer_.updateMacros();
- // SinglePar is ignored for now (this should probably change). We
- // set it ourselves below, at the price of always rebreaking the
- // paragraph at cursor. This can be expensive for large tables.
- flags = flags & ~Update::SinglePar;
-
// First check whether the metrics and inset positions should be updated
if (flags & Update::Force) {
// This will update the CoordCache items and replace Force
updateMetrics(flags);
}
- // Detect whether we can only repaint a single paragraph.
+ // Detect whether we can only repaint a single paragraph (if we
+ // are not already redrawing all).
// We handle this before FitCursor because the later will require
// correct metrics at cursor position.
- if (!(flags & Update::ForceDraw)) {
- if (singleParUpdate())
- flags = flags | Update::SinglePar;
- else
+ if (!(flags & Update::ForceDraw)
+ && (flags & Update::SinglePar)
+ && !singleParUpdate())
updateMetrics(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);
+ // 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);
}
LYXERR(Debug::PAINTING, "Cumulative flags: " << flagsAsString(flags));
// Now compute the update strategy
- // Possibly values in flag are None, Decoration, ForceDraw
+ // Possibly values in flag are None, SinglePar, Decoration, ForceDraw
LATTEST((d->update_flags_ & ~(Update::None | Update::SinglePar
| Update::Decoration | Update::ForceDraw)) == 0);
int offset = coordOffset(dit).y_;
int ypos = pm.position() + offset;
Dimension const & row_dim =
- pm.getRow(cs.pos(), dit.boundary()).dimension();
+ pm.getRow(cs.pos(), dit.boundary()).dim();
int scrolled = 0;
if (recenter)
scrolled = scroll(ypos - height_/2);
d->anchor_pit_ = bot_pit;
CursorSlice const & cs = dit.innerTextSlice();
Dimension const & row_dim =
- pm.getRow(cs.pos(), dit.boundary()).dimension();
+ pm.getRow(cs.pos(), dit.boundary()).dim();
if (recenter)
d->anchor_ypos_ = height_/2;
Inset * BufferView::editedInset(string const & name) const
{
map<string, Inset *>::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;
}
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;
}
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: {
// At least one complete cell is selected and inset is a table.
// Select all cells
cur.idx() = 0;
+ cur.pit() = 0;
cur.pos() = 0;
cur.resetAnchor();
cur.selection(true);
cur.idx() = cur.lastidx();
+ cur.pit() = cur.lastpit();
cur.pos() = cur.lastpos();
} else {
// select current cell
}
-docstring const BufferView::requestSelection()
+docstring BufferView::requestSelection()
{
Cursor & cur = d->cursor_;
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
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)) {
LYXERR0("Wrong last_inset!");
LATTEST(false);
}
- d->last_inset_ = 0;
+ d->last_inset_ = nullptr;
}
LBUFERR(t);
TextMetricsCache::iterator tmc_it = d->text_metrics_.find(t);
if (tmc_it == d->text_metrics_.end()) {
- tmc_it = d->text_metrics_.insert(
- make_pair(t, TextMetrics(this, const_cast<Text *>(t)))).first;
+ tmc_it = d->text_metrics_.emplace(std::piecewise_construct,
+ std::forward_as_tuple(t),
+ std::forward_as_tuple(this, const_cast<Text *>(t))).first;
}
return tmc_it->second;
}
return false;
bool need_anchor_change = false;
- bool changed = d->cursor_.text()->deleteEmptyParagraphMechanism(cur, old,
+ bool changed = Text::deleteEmptyParagraphMechanism(cur, old,
need_anchor_change);
if (need_anchor_change)
// Clear out the position cache in case of full screen redraw,
d->coord_cache_.clear();
+ d->math_rows_.clear();
// Clear out paragraph metrics to avoid having invalid metrics
// in the cache from paragraphs not relayouted below
}
-void BufferView::insertLyXFile(FileName const & fname)
+void BufferView::insertLyXFile(FileName const & fname, bool const ignorelang)
{
LASSERT(d->cursor_.inTexted(), return);
ErrorList & el = buffer_.errorList("Parse");
// Copy the inserted document error list into the current buffer one.
el = buf.errorList("Parse");
+ ParagraphList & pars = buf.paragraphs();
+ if (ignorelang)
+ // set main language of imported file to context language
+ buf.changeLanguage(buf.language(), d->cursor_.getFont().language());
buffer_.undo().recordUndo(d->cursor_);
- cap::pasteParagraphList(d->cursor_, buf.paragraphs(),
+ cap::pasteParagraphList(d->cursor_, pars,
buf.params().documentClassPtr(), el);
res = _("Document %1$s inserted.");
} else {
}
-void BufferView::setCaretAscentDescent(int asc, int des)
-{
- d->caret_ascent_ = asc;
- d->caret_descent_ = des;
-}
-
-
void BufferView::caretPosAndHeight(Point & p, int & h) const
{
int asc, des;
Cursor const & cur = cursor();
if (cur.inMathed()) {
- asc = d->caret_ascent_;
- des = d->caret_descent_;
+ MathRow const & mrow = mathRow(&cur.cell());
+ asc = mrow.caret_ascent;
+ des = mrow.caret_descent;
} else {
Font const font = cur.real_current_font;
frontend::FontMetrics const & fm = theFontMetrics(font);
}
-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;
}
}
-bool BufferView::hadHorizScrollOffset(Text const * text,
- pit_type pit, pos_type pos) const
-{
- 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();
-}
-
-
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();
+ if (d->current_row_slice_ == rowSlice)
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();
+ if (d->horiz_scroll_offset_ != 0) {
+ // search the old row in cache and mark it changed
+ for (auto & tm_pair : d->text_metrics_) {
+ if (&tm_pair.first->inset() == rowSlice.inset().asInsetText()) {
+ tm_pair.second.setRowChanged(rowSlice.pit(), rowSlice.pos());
+ // We found it, no need to continue.
+ break;
+ }
+ }
+ }
// Since we changed row, the scroll offset is not valid anymore
d->horiz_scroll_offset_ = 0;
<< d->horiz_scroll_offset_ << " to " << offset);
if (d->update_strategy_ == NoScreenUpdate
- && (offset != d->horiz_scroll_offset_
- || !d->last_row_slice_.empty())) {
+ && offset != d->horiz_scroll_offset_) {
// FIXME: if one uses SingleParUpdate, then home/end
// will not work on long rows. Why?
d->update_strategy_ = FullScreenUpdate;
// 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);