#include "BufferList.h"
#include "BufferParams.h"
#include "bufferview_funcs.h"
+#include "callback.h" // added for Dispatch functions
#include "CoordCache.h"
#include "CutAndPaste.h"
#include "debug.h"
#include "InsetIterator.h"
#include "Language.h"
#include "LaTeXFeatures.h"
-#include "callback.h" // added for Dispatch functions
#include "LyX.h"
#include "lyxfind.h"
#include "LyXFunc.h"
#include "Layout.h"
-#include "Text.h"
-#include "TextClass.h"
#include "LyXRC.h"
-#include "Session.h"
+#include "MetricsInfo.h"
#include "Paragraph.h"
#include "paragraph_funcs.h"
#include "ParagraphParameters.h"
#include "ParIterator.h"
+#include "Session.h"
#include "TexRow.h"
+#include "Text.h"
+#include "TextClass.h"
#include "toc.h"
#include "Undo.h"
#include "VSpace.h"
#include "WordLangTuple.h"
-#include "MetricsInfo.h"
#include "insets/InsetBibtex.h"
#include "insets/InsetCommand.h" // ChangeRefs
#include "frontends/alert.h"
#include "frontends/FileDialog.h"
#include "frontends/FontMetrics.h"
+#include "frontends/Painter.h"
#include "frontends/Selection.h"
#include "graphics/Previews.h"
: width_(0), height_(0), buffer_(buf), wh_(0),
cursor_(*this),
multiparsel_cache_(false), anchor_ref_(0), offset_ref_(0),
- intl_(new Intl), last_inset_(0)
+ need_centering_(false), intl_(new Intl), last_inset_(0)
{
xsel_cache_.set = false;
intl_->initKeyMapper(lyxrc.use_kbmap);
cursor_.push(buffer_.inset());
cursor_.resetAnchor();
- buffer_.text().setCurrentFont(cursor_);
+ cursor_.setCurrentFont();
if (graphics::Previews::status() != LyXRC::PREVIEW_OFF)
graphics::Previews::get().generateBufferPreviews(buffer_);
int const h = tm.parMetrics(anchor_ref_).height();
offset_ref_ = int((bar * t.paragraphs().size() - anchor_ref_) * h);
updateMetrics(false);
+ buffer_.changed();
}
void BufferView::setCursorFromScrollbar()
{
- Text & t = buffer_.text();
+ TextMetrics & tm = text_metrics_[&buffer_.text()];
int const height = 2 * defaultRowHeight();
int const first = height;
// We reset the cursor because bv_funcs::status() does not
// work when the cursor is within mathed.
cur.reset(buffer_.inset());
- t.setCursorFromCoordinates(cur, 0, first);
+ tm.setCursorFromCoordinates(cur, 0, first);
cur.clearSelection();
break;
case bv_funcs::CUR_BELOW:
// We reset the cursor because bv_funcs::status() does not
// work when the cursor is within mathed.
cur.reset(buffer_.inset());
- t.setCursorFromCoordinates(cur, 0, last);
+ tm.setCursorFromCoordinates(cur, 0, last);
cur.clearSelection();
break;
case bv_funcs::CUR_INSIDE:
int const newy = min(last, max(y, first));
if (y != newy) {
cur.reset(buffer_.inset());
- t.setCursorFromCoordinates(cur, 0, newy);
+ tm.setCursorFromCoordinates(cur, 0, newy);
}
}
}
}
-boost::tuple<pit_type, pos_type, int> BufferView::moveToPosition(pit_type bottom_pit, pos_type bottom_pos,
+bool BufferView::moveToPosition(pit_type bottom_pit, pos_type bottom_pos,
int top_id, pos_type top_pos)
{
+ bool success = false;
+ DocIterator doc_it;
+
cursor_.clearSelection();
// if a valid par_id is given, try it first
if (top_id > 0) {
ParIterator par = buffer_.getParFromID(top_id);
if (par != buffer_.par_iterator_end()) {
- DocIterator dit = makeDocIterator(par, min(par->size(), top_pos));
+ doc_it = makeDocIterator(par, min(par->size(), top_pos));
// Some slices of the iterator may not be
// reachable (e.g. closed collapsable inset)
// so the dociterator may need to be
// shortened. Otherwise, setCursor may crash
// lyx when the cursor can not be set to these
// insets.
- size_t const n = dit.depth();
+ size_t const n = doc_it.depth();
for (size_t i = 0; i < n; ++i)
- if (dit[i].inset().editable() != Inset::HIGHLY_EDITABLE) {
- dit.resize(i);
+ if (doc_it[i].inset().editable() != Inset::HIGHLY_EDITABLE) {
+ doc_it.resize(i);
break;
}
- setCursor(dit);
- // Note: return bottom (document) level pit.
- return boost::make_tuple(cursor_.bottom().pit(), cursor_.bottom().pos(), top_id);
+ success = true;
}
}
+
// if top_id == 0, or searching through top_id failed
// This is the case for a 'restored' bookmark when only bottom
// (document level) pit was saved. Because of this, bookmark
// it will be restored to the left of the outmost inset that contains
// the bookmark.
if (static_cast<size_t>(bottom_pit) < buffer_.paragraphs().size()) {
- DocIterator it = doc_iterator_begin(buffer_.inset());
- it.pit() = bottom_pit;
- it.pos() = min(bottom_pos, it.paragraph().size());
- setCursor(it);
- return boost::make_tuple(it.pit(), it.pos(),
- it.paragraph().id());
+ doc_it = doc_iterator_begin(buffer_.inset());
+ doc_it.pit() = bottom_pit;
+ doc_it.pos() = min(bottom_pos, doc_it.paragraph().size());
+ success = true;
+ }
+
+ if (success) {
+ // Note: only bottom (document) level pit is set.
+ setCursor(doc_it);
+ // set the current font.
+ cursor_.setCurrentFont();
+ // center the screen on this new position.
+ center();
}
- // both methods fail
- return boost::make_tuple(pit_type(0), pos_type(0), 0);
+
+ return success;
}
void BufferView::translateAndInsert(char_type c, Text * t, Cursor & cur)
{
if (lyxrc.rtl_support) {
- if (cursor_.innerText()->real_current_font.isRightToLeft()) {
+ if (cursor_.real_current_font.isRightToLeft()) {
if (intl_->keymap == Intl::PRIMARY)
intl_->keyMapSec();
} else {
intl_->keyMapPrim();
}
}
-
+
intl_->getTransManager().translateAndInsert(c, t, cur);
}
}
-void BufferView::center()
+void BufferView::updateOffsetRef()
{
+ // No need to update offset_ref_ in this case.
+ if (!need_centering_)
+ return;
+
+ // We are not properly started yet, delay until resizing is
+ // done.
+ if (height_ == 0)
+ return;
+
CursorSlice & bot = cursor_.bottom();
TextMetrics & tm = text_metrics_[bot.text()];
- pit_type const pit = bot.pit();
- tm.redoParagraph(pit);
- ParagraphMetrics const & pm = tm.parMetrics(pit);
- anchor_ref_ = pit;
- offset_ref_ = bv_funcs::coordOffset(*this, cursor_, cursor_.boundary()).y_
- + pm.ascent() - height_ / 2;
+ ParagraphMetrics const & pm = tm.parMetrics(bot.pit());
+ Point p = bv_funcs::coordOffset(*this, cursor_, cursor_.boundary());
+ offset_ref_ = p.y_ + pm.ascent() - height_ / 2;
+
+ need_centering_ = false;
+}
+
+
+void BufferView::center()
+{
+ anchor_ref_ = cursor_.bottom().pit();
+ need_centering_ = true;
}
break;
}
+ case LFUN_SCREEN_UP:
+ case LFUN_SCREEN_DOWN:
+ flag.enabled(true);
+ break;
+
+ // FIXME: LFUN_SCREEN_DOWN_SELECT should be removed from
+ // everywhere else before this can enabled:
+ case LFUN_SCREEN_UP_SELECT:
+ case LFUN_SCREEN_DOWN_SELECT:
+ flag.enabled(false);
+ break;
+
default:
flag.enabled(false);
}
// if there is an inset at cursor, see whether it
// wants to toggle.
Inset * inset = cur.nextInset();
- if (inset && inset->isActive()) {
- Cursor tmpcur = cur;
- tmpcur.pushLeft(*inset);
- inset->dispatch(tmpcur, tmpcmd);
- if (tmpcur.result().dispatched()) {
- cur.dispatched();
+ if (inset) {
+ if (inset->isActive()) {
+ Cursor tmpcur = cur;
+ tmpcur.pushLeft(*inset);
+ inset->dispatch(tmpcur, tmpcmd);
+ if (tmpcur.result().dispatched()) {
+ cur.dispatched();
+ }
+ } else if (inset->editable() == Inset::IS_EDITABLE) {
+ inset->edit(cur, true);
}
}
// if it did not work, try the underlying inset.
break;
}
+ case LFUN_SCREEN_UP:
+ case LFUN_SCREEN_DOWN: {
+ Point p = bv_funcs::getPos(*this, cur, cur.boundary());
+ if (p.y_ < 0 || p.y_ > height_) {
+ // The cursor is off-screen so recenter before proceeding.
+ center();
+ updateMetrics(false);
+ //FIXME: updateMetrics() does not update paragraph position
+ // This is done at draw() time. So we need a redraw!
+ buffer_.changed();
+ p = bv_funcs::getPos(*this, cur, cur.boundary());
+ }
+ scroll(cmd.action == LFUN_SCREEN_UP? - height_ : height_);
+ cur.reset(buffer_.inset());
+ text_metrics_[&buffer_.text()].editXY(cur, p.x_, p.y_);
+ //FIXME: what to do with cur.x_target()?
+ finishUndo();
+ // The metrics are already up to date. see scroll()
+ updateFlags = Update::None;
+ break;
+ }
+
+ case LFUN_SCREEN_UP_SELECT:
+ case LFUN_SCREEN_DOWN_SELECT: {
+ cur.selHandle(true);
+ size_t initial_depth = cur.depth();
+ Point const p = bv_funcs::getPos(*this, cur, cur.boundary());
+ scroll(cmd.action == LFUN_SCREEN_UP_SELECT? - height_ : height_);
+ // FIXME: We need to verify if the cursor stayed within an inset...
+ //cur.reset(buffer_.inset());
+ text_metrics_[&buffer_.text()].editXY(cur, p.x_, p.y_);
+ finishUndo();
+ while (cur.depth() > initial_depth) {
+ cur.forwardInset();
+ }
+ // FIXME: we need to do a redraw again because of the selection
+ buffer_.changed();
+ updateFlags = Update::Force | Update::FitCursor;
+ break;
+ }
+
default:
updateFlags = Update::None;
}
}
-void BufferView::workAreaResize(int width, int height)
+void BufferView::resize(int width, int height)
{
// Update from work area
width_ = width;
height_ = height;
- // The complete text metrics will be redone.
- text_metrics_.clear();
updateMetrics(false);
}
Inset const * BufferView::getCoveringInset(Text const & text, int x, int y)
{
- pit_type pit = text.getPitNearY(*this, y);
+ pit_type pit = text_metrics_[&text].getPitNearY(y);
BOOST_ASSERT(pit != -1);
Paragraph const & par = text.getPar(pit);
cmd.y = min(max(cmd.y, -1), height_);
if (cmd.action == LFUN_MOUSE_MOTION && cmd.button() == mouse_button::none) {
+ //FIXME: disabling mouse hover for now as it is causing funny things
+ // on screen.
+ return false;
// Get inset under mouse, if there is one.
Inset const * covering_inset =
//metrics_info_.y1 = ymin of button;
//metrics_info_.y2 = ymax of button;
//
- // Unfortunately, rowpainter.cpp:paintText() does not distinguish
+ // Unfortunately, BufferView::draw() does not distinguish
// between background updates and text updates. So we use the hammer
// solution for now. We could also avoid the updateMetrics() below
// by using the first and last pit of the CoordCache. Have a look
}
// Build temporary cursor.
- Inset * inset = buffer_.text().editXY(cur, cmd.x, cmd.y);
+ Inset * inset = text_metrics_[&buffer_.text()].editXY(cur, cmd.x, cmd.y);
// Put anchor at the same position.
cur.resetAnchor();
}
-void BufferView::scroll(int /*lines*/)
+void BufferView::scroll(int y)
+{
+ if (y > 0)
+ scrollDown(y);
+ else if (y < 0)
+ scrollUp(-y);
+}
+
+
+void BufferView::scrollDown(int offset)
{
-// Text const * t = buffer_.text();
-// int const line_height = defaultRowHeight();
-//
-// // The new absolute coordinate
-// int new_top_y = top_y() + lines * line_height;
-//
-// // Restrict to a valid value
-// new_top_y = std::min(t->height() - 4 * line_height, new_top_y);
-// new_top_y = std::max(0, new_top_y);
-//
-// scrollDocView(new_top_y);
-//
+ Text * text = &buffer_.text();
+ TextMetrics & tm = text_metrics_[text];
+ int ymax = height_ + offset;
+ while (true) {
+ std::pair<pit_type, ParagraphMetrics> const & last = tm.last();
+ int bottom_pos = last.second.position() + last.second.descent();
+ if (last.first == text->paragraphs().size() - 1) {
+ if (bottom_pos <= height_)
+ return;
+ offset = min(offset, bottom_pos - height_);
+ break;
+ }
+ if (bottom_pos > ymax)
+ break;
+ tm.newParMetricsDown();
+ }
+ offset_ref_ += offset;
+ updateMetrics(false);
+ buffer_.changed();
+}
+
+
+void BufferView::scrollUp(int offset)
+{
+ Text * text = &buffer_.text();
+ TextMetrics & tm = text_metrics_[text];
+ int ymin = - offset;
+ while (true) {
+ std::pair<pit_type, ParagraphMetrics> const & first = tm.first();
+ int top_pos = first.second.position() - first.second.ascent();
+ if (first.first == 0) {
+ if (top_pos >= 0)
+ return;
+ offset = min(offset, - top_pos);
+ break;
+ }
+ if (top_pos < ymin)
+ break;
+ tm.newParMetricsUp();
+ }
+ offset_ref_ -= offset;
+ updateMetrics(false);
+ buffer_.changed();
}
{
BOOST_ASSERT(&cur.bv() == this);
- // this event will clear selection so we save selection for
+ // this event will clear selection so we save selection for
// persistent selection
cap::saveSelection(cursor());
void BufferView::updateMetrics(bool singlepar)
{
Text & buftext = buffer_.text();
- TextMetrics & tm = textMetrics(&buftext);
- pit_type size = int(buftext.paragraphs().size());
+ pit_type const npit = int(buftext.paragraphs().size());
- if (anchor_ref_ > int(buftext.paragraphs().size() - 1)) {
- anchor_ref_ = int(buftext.paragraphs().size() - 1);
+ if (anchor_ref_ > int(npit - 1)) {
+ anchor_ref_ = int(npit - 1);
offset_ref_ = 0;
}
if (!singlepar) {
// Clear out the position cache in case of full screen redraw,
coord_cache_.clear();
-
+
// Clear out paragraph metrics to avoid having invalid metrics
// in the cache from paragraphs not relayouted below
- tm.clear();
+ // The complete text metrics will be redone.
+ text_metrics_.clear();
}
+ TextMetrics & tm = textMetrics(&buftext);
+
+ pit_type const bottom_pit = cursor_.bottom().pit();
// If the paragraph metrics has changed, we can not
// use the singlepar optimisation.
if (singlepar
// the (main text, not inset!) paragraph containing the cursor.
// (if this paragraph contains insets etc., rebreaking will
// recursively descend)
- && tm.redoParagraph(cursor_.bottom().pit()))
- singlepar = false;
+ && !tm.redoParagraph(bottom_pit)) {
+
+ updateOffsetRef();
+ // collect cursor paragraph iter bounds
+ ParagraphMetrics const & pm = tm.parMetrics(bottom_pit);
+ int y1 = pm.position() - pm.ascent();
+ int y2 = pm.position() + pm.descent();
+ metrics_info_ = ViewMetricsInfo(bottom_pit, bottom_pit, y1, y2,
+ SingleParUpdate, npit);
+ LYXERR(Debug::PAINTING)
+ << BOOST_CURRENT_FUNCTION
+ << "\ny1: " << y1
+ << " y2: " << y2
+ << " pit: " << bottom_pit
+ << " singlepar: " << singlepar
+ << endl;
+ return;
+ }
pit_type const pit = anchor_ref_;
int pit1 = pit;
int pit2 = pit;
- size_t const npit = buftext.paragraphs().size();
// Rebreak anchor paragraph.
- if (!singlepar)
- tm.redoParagraph(pit);
+ tm.redoParagraph(pit);
+
+ updateOffsetRef();
int y0 = tm.parMetrics(pit).ascent() - offset_ref_;
while (y1 > 0 && pit1 > 0) {
y1 -= tm.parMetrics(pit1).ascent();
--pit1;
- if (!singlepar)
- tm.redoParagraph(pit1);
+ tm.redoParagraph(pit1);
y1 -= tm.parMetrics(pit1).descent();
}
-
// Take care of ascent of first line
y1 -= tm.parMetrics(pit1).ascent();
while (y2 < height_ && pit2 < int(npit) - 1) {
y2 += tm.parMetrics(pit2).descent();
++pit2;
- if (!singlepar)
- tm.redoParagraph(pit2);
+ tm.redoParagraph(pit2);
y2 += tm.parMetrics(pit2).ascent();
}
// Take care of descent of last line
y2 += tm.parMetrics(pit2).descent();
- // The coordinates of all these paragraphs are correct, cache them
- int y = y1;
- CoordCache::InnerParPosCache & parPos = coord_cache_.parPos()[&buftext];
- for (pit_type pit = pit1; pit <= pit2; ++pit) {
- ParagraphMetrics const & pm = tm.parMetrics(pit);
- y += pm.ascent();
- parPos[pit] = Point(0, y);
- if (singlepar && pit == cursor_.bottom().pit()) {
- // In Single Paragraph mode, collect here the
- // y1 and y2 of the (one) paragraph the cursor is in
- y1 = y - pm.ascent();
- y2 = y + pm.descent();
- }
- y += pm.descent();
- }
-
- if (singlepar) {
- // collect cursor paragraph iter bounds
- pit1 = cursor_.bottom().pit();
- pit2 = cursor_.bottom().pit();
- }
-
- LYXERR(Debug::DEBUG)
+ LYXERR(Debug::PAINTING)
<< BOOST_CURRENT_FUNCTION
- << " y1: " << y1
+ << "\n y1: " << y1
<< " y2: " << y2
<< " pit1: " << pit1
<< " pit2: " << pit2
<< " npit: " << npit
<< " singlepar: " << singlepar
- << "size: " << size
<< endl;
metrics_info_ = ViewMetricsInfo(pit1, pit2, y1, y2,
- singlepar? SingleParUpdate: FullScreenUpdate, size);
+ FullScreenUpdate, npit);
if (lyxerr.debugging(Debug::WORKAREA)) {
LYXERR(Debug::WORKAREA) << "BufferView::updateMetrics" << endl;
el = buf.errorList("Parse");
recordUndo(cursor_);
cap::pasteParagraphList(cursor_, buf.paragraphs(),
- buf.params().textclass, el);
+ buf.params().getTextClassPtr(), el);
res = _("Document %1$s inserted.");
} else
res = _("Could not insert document %1$s");
updateMetrics(false);
}
+
+void BufferView::draw(frontend::Painter & pain)
+{
+ PainterInfo pi(this, pain);
+ // Should the whole screen, including insets, be refreshed?
+ // FIXME: We should also distinguish DecorationUpdate to avoid text
+ // drawing if possible. This is not possible to do easily right now
+ // because of the single backing pixmap.
+ pi.full_repaint = metrics_info_.update_strategy != SingleParUpdate;
+
+ if (pi.full_repaint)
+ // Clear background (if not delegated to rows)
+ pain.fillRectangle(0, metrics_info_.y1, width_,
+ metrics_info_.y2 - metrics_info_.y1,
+ buffer_.inset().backgroundColor());
+
+ LYXERR(Debug::PAINTING) << "\t\t*** START DRAWING ***" << endl;
+ Text & text = buffer_.text();
+ TextMetrics const & tm = text_metrics_[&text];
+ int y = metrics_info_.y1 + tm.parMetrics(metrics_info_.p1).ascent();
+ if (!pi.full_repaint)
+ tm.drawParagraph(pi, metrics_info_.p1, 0, y);
+ else
+ tm.draw(pi, 0, y);
+ LYXERR(Debug::PAINTING) << "\n\t\t*** END DRAWING ***" << endl;
+
+ // and grey out above (should not happen later)
+// lyxerr << "par ascent: " << text.getPar(metrics_info_.p1).ascent() << endl;
+ if (metrics_info_.y1 > 0
+ && metrics_info_.update_strategy == FullScreenUpdate)
+ pain.fillRectangle(0, 0, width_, metrics_info_.y1, Color::bottomarea);
+
+ // and possibly grey out below
+// lyxerr << "par descent: " << text.getPar(metrics_info_.p1).ascent() << endl;
+ if (metrics_info_.y2 < height_
+ && metrics_info_.update_strategy == FullScreenUpdate)
+ pain.fillRectangle(0, metrics_info_.y2, width_,
+ height_ - metrics_info_.y2, Color::bottomarea);
+}
+
} // namespace lyx