#include "Intl.h"
#include "Language.h"
#include "LayoutFile.h"
-#include "Lexer.h"
#include "LyX.h"
#include "LyXAction.h"
#include "lyxfind.h"
#include "mathed/MathRow.h"
#include "frontends/alert.h"
+#include "frontends/Application.h"
#include "frontends/CaretGeometry.h"
#include "frontends/Delegates.h"
#include "frontends/FontMetrics.h"
#include "support/gettext.h"
#include "support/lassert.h"
#include "support/Length.h"
+#include "support/Lexer.h"
#include "support/lstrings.h"
#include "support/lyxlib.h"
#include "support/types.h"
Private(BufferView & bv) :
update_strategy_(FullScreenUpdate),
update_flags_(Update::Force),
- cursor_(bv), anchor_pit_(0), anchor_ypos_(0),
+ cursor_(bv), anchor_pit_(0), anchor_ypos_(10000),
wh_(0), inlineCompletionUniqueChars_(0),
last_inset_(nullptr), mouse_position_cache_(),
gui_(nullptr), bookmark_edit_position_(-1),
frontend::CaretGeometry caret_geometry_;
///
bool mouse_selecting_ = false;
+ /// Reference value for statistics (essentially subtract this from the actual value to see relative counts)
+ /// (words/chars/chars no blanks)
+ int stats_ref_value_w_ = 0;
+ int stats_ref_value_c_ = 0;
+ int stats_ref_value_nb_ = 0;
+ bool stats_update_trigger_ = false;
+
};
int const asc = fm.maxAscent();
int const des = fm.maxDescent();
Point const p = getPos(d->cursor_);
- if (p.y_ - asc >= 0 && p.y_ + des < height_)
+ if (p.y - asc >= 0 && p.y + des < height_)
return false;
}
return true;
updateMetrics(true);
// metrics is done, full drawing is necessary now
flags = (flags & ~Update::Force) | Update::ForceDraw;
- } else if (flags & Update::ForceDraw)
+ }
+ /* If a single paragraph update has been requested and we are not
+ * already repainting all, check whether this update changes the
+ * paragraph metrics. If it does, then compute all metrics (in
+ * case the paragraph is in an inset)
+ *
+ * We handle this before FitCursor because the later will require
+ * correct metrics at cursor position.
+ */
+ else if ((flags & Update::SinglePar) && !(flags & Update::ForceDraw)) {
+ if (!singleParUpdate())
+ updateMetrics(true);
+ }
+ else if (flags & Update::ForceDraw)
// This will compute only the needed metrics and update positions.
updateMetrics(false);
- // 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)
- && (flags & Update::SinglePar)
- && !singleParUpdate())
- updateMetrics(true);
-
// Then make sure that the screen contains the cursor if needed
if (flags & Update::FitCursor) {
if (needsFitCursor()) {
// First try to make the selection start visible
// (which is just the cursor when there is no selection)
scrollToCursor(d->cursor_.selectionBegin(), SCROLL_VISIBLE);
- // Metrics have to be recomputed (maybe again)
- updateMetrics(true);
+ // Metrics have to be updated
+ updateMetrics(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_, SCROLL_VISIBLE);
// Metrics have to be recomputed (maybe again)
- updateMetrics(true);
+ updateMetrics(false);
}
}
- flags = flags & ~Update::FitCursor;
+ flags = (flags & ~Update::FitCursor) | Update::ForceDraw;
}
+ if (theApp()->drawStrategy() == DrawStrategy::Full)
+ flags = flags | Update::ForceDraw;
+
// Add flags to the the update flags. These will be reset to None
// after the redraw is actually done
d->update_flags_ = d->update_flags_ | flags;
newy = last;
break;
case CUR_INSIDE:
- int const y = getPos(oldcur).y_;
+ int const y = getPos(oldcur).y;
newy = min(last, max(y, first));
if (y == newy)
return;
CursorStatus BufferView::cursorStatus(DocIterator const & dit) const
{
Point const p = getPos(dit);
- if (p.y_ < 0)
+ if (p.y < 0)
return CUR_ABOVE;
- if (p.y_ > workHeight())
+ if (p.y > workHeight())
return CUR_BELOW;
return CUR_INSIDE;
}
bool update)
{
if (scrollToCursor(dit, how) && update)
- processUpdateFlags(Update::Force);
+ processUpdateFlags(Update::ForceDraw);
}
LBUFERR(!pm.rows().empty());
// FIXME: smooth scrolling doesn't work in mathed.
CursorSlice const & cs = dit.innerTextSlice();
- int const ypos = pm.position() + coordOffset(dit).y_;
+ int const ypos = pm.position() + coordOffset(dit).y;
ParagraphMetrics const & inner_pm =
textMetrics(cs.text()).parMetrics(cs.pit());
Dimension const & row_dim =
d->inlineCompletionPos_ = DocIterator();
tm.redoParagraph(bot_pit);
- int const offset = coordOffset(dit).y_;
+ int const offset = coordOffset(dit).y;
pit_type const old_pit = d->anchor_pit_;
d->anchor_pit_ = bot_pit;
flag.setEnabled(cur.selection());
break;
+ case LFUN_STATISTICS_REFERENCE_CLAMP: {
+ // disable optitem reset if clamp not used
+ if (cmd.argument() == "reset" && d->stats_ref_value_c_ == 0) {
+ flag.setEnabled(false);
+ break;
+ }
+ flag.setEnabled(true);
+ break;
+
+ }
+
default:
return false;
}
break;
case LFUN_ALL_CHANGES_ACCEPT: {
+ UndoGroupHelper helper(cur);
// select complete document
cur.reset();
cur.selHandle(true);
}
case LFUN_ALL_CHANGES_REJECT: {
+ UndoGroupHelper helper(cur);
// select complete document
cur.reset();
cur.selHandle(true);
}
break;
+ case LFUN_STATISTICS_REFERENCE_CLAMP: {
+ d->stats_update_trigger_ = true;
+ if (cmd.argument() == "reset") {
+ d->stats_ref_value_w_ = d->stats_ref_value_c_ = d->stats_ref_value_nb_ = 0;
+ break;
+ }
+
+ DocIterator from, to;
+ from = doc_iterator_begin(&buffer_);
+ to = doc_iterator_end(&buffer_);
+ buffer_.updateStatistics(from, to);
+
+ d->stats_ref_value_w_ = buffer_.wordCount();
+ d->stats_ref_value_c_ = buffer_.charCount(true);
+ d->stats_ref_value_nb_ = buffer_.charCount(false);
+ break;
+ }
+
+
case LFUN_SCREEN_UP:
case LFUN_SCREEN_DOWN: {
Point p = getPos(cur);
// This code has been commented out to enable to scroll down a
// document, even if there are large insets in it (see bug #5465).
- /*if (p.y_ < 0 || p.y_ > height_) {
+ /*if (p.y < 0 || p.y > height_) {
// The cursor is off-screen so recenter before proceeding.
showCursor();
p = getPos(cur);
if (scrolled)
processUpdateFlags(Update::Force);
- d->text_metrics_[&buffer_.text()].editXY(cur, p.x_, p.y_,
+ d->text_metrics_[&buffer_.text()].editXY(cur, p.x, p.y,
true, act == LFUN_SCREEN_UP);
//FIXME: what to do with cur.x_target()?
bool update = in_texted && cur.bv().checkDepm(cur, old);
cur.finishUndo();
break;
}
- int y = getPos(cur).y_;
+ int y = getPos(cur).y;
int const ymin = y - height_ + defaultRowHeight();
while (y > ymin && cur.up())
- y = getPos(cur).y_;
+ y = getPos(cur).y;
cur.finishUndo();
dr.screenUpdate(Update::SinglePar | Update::FitCursor);
cur.finishUndo();
break;
}
- int y = getPos(cur).y_;
+ int y = getPos(cur).y;
int const ymax = y + height_ - defaultRowHeight();
while (y < ymax && cur.down())
- y = getPos(cur).y_;
+ y = getPos(cur).y;
cur.finishUndo();
dr.screenUpdate(Update::SinglePar | Update::FitCursor);
void BufferView::resize(int width, int height)
{
- // Update from work area
- width_ = width;
height_ = height;
+ // Update metrics only if width has changed
+ if (width != width_) {
+ width_ = width;
- // Clear the paragraph height cache.
- d->par_height_.clear();
- // Redo the metrics.
- updateMetrics();
+ // Clear the paragraph height cache.
+ d->par_height_.clear();
+ // Redo the metrics.
+ updateMetrics(true);
+ }
+ // metrics is OK, full drawing is necessary now
+ d->update_flags_ = (d->update_flags_ & ~Update::Force) | Update::ForceDraw;
+ d->update_strategy_ = FullScreenUpdate;
}
void BufferView::updateHoveredInset() const
{
// Get inset under mouse, if there is one.
- int const x = d->mouse_position_cache_.x_;
- int const y = d->mouse_position_cache_.y_;
+ int const x = d->mouse_position_cache_.x;
+ int const y = d->mouse_position_cache_.y;
Inset const * covering_inset = getCoveringInset(buffer_.text(), x, y);
if (covering_inset && covering_inset->asInsetMath()) {
Inset const * inner_inset = clickableMathInset(
if (need_redraw) {
LYXERR(Debug::PAINTING, "Mouse hover detected at: ("
- << d->mouse_position_cache_.x_ << ", "
- << d->mouse_position_cache_.y_ << ")");
+ << d->mouse_position_cache_.x << ", "
+ << d->mouse_position_cache_.y << ")");
d->update_strategy_ = DecorationUpdate;
}
+int BufferView::stats_ref_value_w() const
+{
+ return d->stats_ref_value_w_;
+}
+
+
+int BufferView::stats_ref_value_c() const
+{
+ return d->stats_ref_value_c_;
+}
+
+
+int BufferView::stats_ref_value_nb() const
+{
+ return d->stats_ref_value_nb_;
+}
+
+
void BufferView::mouseEventDispatch(FuncRequest const & cmd0)
{
//lyxerr << "[ cmd0 " << cmd0 << "]" << endl;
// LFUN_FILE_OPEN generated by drag-and-drop.
FuncRequest cmd = cmd0;
- Cursor old = cursor();
- Cursor cur(*this);
- cur.push(buffer_.inset());
- cur.selection(d->cursor_.selection());
-
// Either the inset under the cursor or the
// surrounding Text will handle this event.
// make sure we stay within the screen...
cmd.set_y(min(max(cmd.y(), -1), height_));
- d->mouse_position_cache_.x_ = cmd.x();
- d->mouse_position_cache_.y_ = cmd.y();
+ d->mouse_position_cache_.x = cmd.x();
+ d->mouse_position_cache_.y = cmd.y();
d->mouse_selecting_ =
cmd.action() == LFUN_MOUSE_MOTION && cmd.button() == mouse_button::button1;
return;
}
+ Cursor old = cursor();
+ Cursor cur(*this);
+ cur.push(buffer_.inset());
+ cur.selection(d->cursor_.selection());
+
// Build temporary cursor.
Inset * inset = d->text_metrics_[&buffer_.text()].editXY(cur, cmd.x(), cmd.y());
if (inset) {
if (!ready())
return;
+ //LYXERR0("updateMetrics " << _v_(force));
+
Text & buftext = buffer_.text();
pit_type const lastpit = int(buftext.paragraphs().size()) - 1;
d->text_metrics_.clear();
}
+ // This should not be moved earlier
TextMetrics & tm = textMetrics(&buftext);
// make sure inline completion pointer is ok
// Check that the end of the document is not too high
int const min_visible = lyxrc.scroll_below_document ? minVisiblePart() : height_;
- if (tm.last().first == lastpit && tm.last().second->bottom() < min_visible) {
+ if (tm.last().first == lastpit && tm.last().second->hasPosition()
+ && tm.last().second->bottom() < min_visible) {
d->anchor_ypos_ += min_visible - tm.last().second->bottom();
LYXERR(Debug::SCROLLING, "Too high, adjusting anchor ypos to " << d->anchor_ypos_);
tm.updateMetrics(d->anchor_pit_, d->anchor_ypos_, height_);
}
// Check that the start of the document is not too low
- if (tm.first().first == 0 && tm.first().second->top() > 0) {
+ if (tm.first().first == 0 && tm.first().second->hasPosition()
+ && tm.first().second->top() > 0) {
d->anchor_ypos_ -= tm.first().second->top();
LYXERR(Debug::SCROLLING, "Too low, adjusting anchor ypos to " << d->anchor_ypos_);
tm.updateMetrics(d->anchor_pit_, d->anchor_ypos_, height_);
/* FIXME: do we want that? It avoids potential issues with old
* paragraphs that should have been recomputed but have not, at
- * the price of potential extra metrics computaiton. I do not
+ * the price of potential extra metrics computation. I do not
* think that the performance gain is high, so that for now the
* extra paragraphs are removed
*/
// Remove paragraphs that are outside of screen
- while(tm.first().second->bottom() <= 0) {
+ while(!tm.first().second->hasPosition() || tm.first().second->bottom() <= 0) {
//LYXERR0("Forget pit: " << tm.first().first);
tm.forget(tm.first().first);
}
- while(tm.last().second->top() > height_) {
+ while(!tm.last().second->hasPosition() || tm.last().second->top() > height_) {
//LYXERR0("Forget pit: " << tm.first().first);
tm.forget(tm.last().first);
}
// offset from outer paragraph
Point p = coordOffset(dit);
- p.y_ += tm.parMetrics(bot.pit()).position();
+ p.y += tm.parMetrics(bot.pit()).position();
return p;
}
p = getPos(cur);
// center fat carets horizontally
- p.x_ -= dim.wid / 2;
+ p.x -= dim.wid / 2;
// p is top-left
- p.y_ -= dim.asc;
+ p.y -= dim.asc;
}
bool const slant = fm.italic() && cur.inTexted() && !cur.selection();
double const slope = slant ? fm.italicSlope() : 0;
cg.shapes.push_back(
- {{iround(p.x_ + dim.asc * slope), p.y_},
- {iround(p.x_ - dim.des * slope), p.y_ + dim.height()},
- {iround(p.x_ + dir * dim.wid - dim.des * slope), p.y_ + dim.height()},
- {iround(p.x_ + dir * dim.wid + dim.asc * slope), p.y_}}
+ {{iround(p.x + dim.asc * slope), p.y},
+ {iround(p.x - dim.des * slope), p.y + dim.height()},
+ {iround(p.x + dir * dim.wid - dim.des * slope), p.y + dim.height()},
+ {iround(p.x + dir * dim.wid + dim.asc * slope), p.y}}
);
// The language indicator _| (if needed)
if (!((realfont.language() == doclang && isrtl == doclang->rightToLeft())
|| realfont.language() == latex_language)) {
int const lx = dim.height() / 3;
- int const xx = iround(p.x_ - dim.des * slope);
- int const yy = p.y_ + dim.height();
+ int const xx = iround(p.x - dim.des * slope);
+ int const yy = p.y + dim.height();
cg.shapes.push_back(
{{xx, yy - dim.wid},
{xx + dir * (dim.wid + lx - 1), yy - dim.wid},
// The completion triangle |> (if needed)
if (complet) {
- int const m = p.y_ + dim.height() / 2;
+ int const m = p.y + dim.height() / 2;
int const d = dim.height() / 8;
// offset for slanted carret
int const sx = iround((dim.asc - (dim.height() / 2 - d)) * slope);
// starting position x
- int const xx = p.x_ + dir * dim.wid + sx;
+ int const xx = p.x + dir * dim.wid + sx;
cg.shapes.push_back(
{{xx, m - d},
{xx + dir * d, m},
cg.bottom = -1000000;
for (auto const & shape : cg.shapes)
for (Point const & p : shape) {
- cg.left = min(cg.left, p.x_);
- cg.right = max(cg.right, p.x_);
- cg.top = min(cg.top, p.y_);
- cg.bottom = max(cg.bottom, p.y_);
+ cg.left = min(cg.left, p.x);
+ cg.right = max(cg.right, p.x);
+ cg.top = min(cg.top, p.y);
+ cg.bottom = max(cg.bottom, p.y);
}
}
caretPosAndDim(p, dim);
// does the cursor touch the screen ?
- if (p.y_ + dim.height() < 0 || p.y_ >= workHeight())
+ if (p.y + dim.height() < 0 || p.y >= workHeight())
return false;
return true;
}
setCurrentRowSlice(rowSlice);
// Current x position of the cursor in pixels
- int cur_x = getPos(d->cursor_).x_;
+ int cur_x = getPos(d->cursor_).x;
// Horizontal scroll offset of the cursor row in pixels
int offset = d->horiz_scroll_offset_;
// Draw everything.
tm.draw(pi, 0, y);
- // and possibly grey out below
+ break;
+ }
+
+ // Possibly grey out below
+ if (d->update_strategy_ != NoScreenUpdate) {
pair<pit_type, ParagraphMetrics const *> lastpm = tm.last();
int const y2 = lastpm.second->bottom();
? Color_background : Color_bottomarea;
pain.fillRectangle(0, y2, width_, height_ - y2, color);
}
- break;
}
+
LYXERR(Debug::PAINTING, (pain.isNull() ? "\t\t --- END NODRAW ---"
: "\t\t *** END DRAWING ***"));
if (!cur.inTexted())
break;
TextMetrics const & tm = textMetrics(cur.text());
- if (d->caret_geometry_.left >= tm.origin().x_
- && d->caret_geometry_.right <= tm.origin().x_ + tm.dim().width())
+ if (d->caret_geometry_.left >= tm.origin().x
+ && d->caret_geometry_.right <= tm.origin().x + tm.dim().width())
break;
cur.pop();
}
return d->clickable_inset_;
}
+
+bool BufferView::stats_update_trigger()
+{
+ if (d->stats_update_trigger_) {
+ d->stats_update_trigger_ = false;
+ return true;
+ }
+ return false;
+}
+
} // namespace lyx