#include "Language.h"
#include "LaTeXFeatures.h"
#include "LayoutFile.h"
+#include "Length.h"
#include "Lexer.h"
#include "LyX.h"
#include "LyXAction.h"
#include "Paragraph.h"
#include "ParagraphParameters.h"
#include "ParIterator.h"
+#include "RowPainter.h"
#include "Session.h"
#include "Text.h"
#include "TextClass.h"
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)
{}
///
///
map<string, Inset *> 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_;
};
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;
}
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);
}
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);
"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);
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);
if (!newL || oldL == newL)
break;
if (oldL->rightToLeft() == newL->rightToLeft()) {
- cur.recordUndoFullDocument();
+ cur.recordUndoFullBuffer();
buffer_.changeLanguage(oldL, newL);
cur.setCurrentFont();
dr.forceBufferUpdate();
}
+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(workWidth());
+ 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)
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);
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.
// 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;