X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2FBufferView.cpp;h=c93966b2572a838ba79ea99d6626935ccb4e0614;hb=1acedf11da79f509da706bc8d6d2f491c9676087;hp=8ce64641e4e0dfed4dc20e362004ff34f0d66b12;hpb=9383f4c3c6f9cfab2d658701ba66e2b54cd68bea;p=lyx.git diff --git a/src/BufferView.cpp b/src/BufferView.cpp index 8ce64641e4..c93966b257 100644 --- a/src/BufferView.cpp +++ b/src/BufferView.cpp @@ -58,6 +58,7 @@ #include "insets/InsetText.h" #include "frontends/alert.h" +#include "frontends/Application.h" #include "frontends/Delegates.h" #include "frontends/FontMetrics.h" #include "frontends/Painter.h" @@ -67,6 +68,7 @@ #include "support/convert.h" #include "support/debug.h" +#include "support/ExceptionMessage.h" #include "support/FileFilterList.h" #include "support/filetools.h" #include "support/gettext.h" @@ -81,18 +83,10 @@ #include using namespace std; +using namespace lyx::support; namespace lyx { -using support::addPath; -using support::bformat; -using support::FileFilterList; -using support::FileName; -using support::fileSearch; -using support::makeDisplayPath; -using support::makeAbsPath; -using support::package; - namespace Alert = frontend::Alert; namespace { @@ -184,6 +178,7 @@ void gotoInset(BufferView * bv, vector const & codes, tmpcur.clearSelection(); bv->setCursor(tmpcur); + bv->showCursor(); } @@ -195,7 +190,7 @@ void gotoInset(BufferView * bv, InsetCode code, bool same_content) /// A map from a Text to the associated text metrics -typedef std::map TextMetricsCache; +typedef map TextMetricsCache; enum ScreenUpdateStrategy { NoScreenUpdate, @@ -216,7 +211,7 @@ enum ScreenUpdateStrategy { struct BufferView::Private { Private(BufferView & bv): wh_(0), cursor_(bv), - anchor_ref_(0), offset_ref_(0), need_centering_(false), + anchor_pit_(0), anchor_ypos_(0), last_inset_(0), gui_(0) {} @@ -238,11 +233,11 @@ struct BufferView::Private /// Cursor cursor_; /// - pit_type anchor_ref_; + pit_type anchor_pit_; /// - int offset_ref_; + int anchor_ypos_; /// - bool need_centering_; + vector par_height_; /// keyboard mapping object. Intl intl_; @@ -340,7 +335,6 @@ bool BufferView::fitCursor() if (p.y_ - asc >= 0 && p.y_ + des < height_) return false; } - center(); return true; } @@ -358,9 +352,7 @@ void BufferView::processUpdateFlags(Update::flags flags) << ", singlepar = " << (flags & Update::SinglePar) << "] buffer: " << &buffer_); - // Update macro store - if (!(cursor().inMathed() && cursor().inMacroMode())) - buffer_.updateMacros(); + buffer_.updateMacros(); // Now do the first drawing step if needed. This consists on updating // the CoordCache in updateMetrics(). @@ -381,11 +373,9 @@ void BufferView::processUpdateFlags(Update::flags flags) if (flags == Update::FitCursor || flags == (Update::Decoration | Update::FitCursor)) { - bool const fit_cursor = fitCursor(); // tell the frontend to update the screen if needed. - if (fit_cursor) { - updateMetrics(); - buffer_.changed(); + if (fitCursor()) { + showCursor(); return; } if (flags & Update::Decoration) { @@ -398,85 +388,90 @@ void BufferView::processUpdateFlags(Update::flags flags) return; } - bool const full_metrics = flags & Update::Force; + bool const full_metrics = flags & Update::Force || !singleParUpdate(); - if (full_metrics || !singleParUpdate()) + if (full_metrics) // We have to update the full screen metrics. updateMetrics(); if (!(flags & Update::FitCursor)) { + // Nothing to do anymore. Trigger a redraw and return buffer_.changed(); return; } - //FIXME: updateMetrics() does not update paragraph position + // updateMetrics() does not update paragraph position // This is done at draw() time. So we need a redraw! buffer_.changed(); - if (!fitCursor()) - // The screen has already been updated thanks to the - // 'buffer_.changed()' call three line above. So no need - // to redraw again. - return; - // The screen has been recentered around the cursor position so - // refresh it: - updateMetrics(); - buffer_.changed(); + if (fitCursor()) { + // The cursor is off screen so ensure it is visible. + // refresh it: + showCursor(); + } } void BufferView::updateScrollbar() { - Text & t = buffer_.text(); - TextMetrics & tm = d->text_metrics_[&t]; + if (height_ == 0) + return; - int const parsize = int(t.paragraphs().size() - 1); - if (d->anchor_ref_ > parsize) { - d->anchor_ref_ = parsize; - d->offset_ref_ = 0; - } + Text & t = buffer_.text(); + TextMetrics & tm = d->text_metrics_[&t]; LYXERR(Debug::GUI, " Updating scrollbar: height: " << t.paragraphs().size() << " curr par: " << d->cursor_.bottom().pit() << " default height " << defaultRowHeight()); + int const parsize = int(t.paragraphs().size()); + if (d->par_height_.size() != parsize) { + d->par_height_.clear(); + // FIXME: We assume a default paragraph height of 2 rows. This + // should probably be pondered with the screen width. + d->par_height_.resize(parsize, defaultRowHeight() * 2); + } + // It would be better to fix the scrollbar to understand // values in [0..1] and divide everything by wh - // estimated average paragraph height: - if (d->wh_ == 0) - d->wh_ = height_ / 4; - - int h = tm.parMetrics(d->anchor_ref_).height(); - - // Normalize anchor/offset (MV): - while (d->offset_ref_ > h && d->anchor_ref_ < parsize) { - d->anchor_ref_++; - d->offset_ref_ -= h; - h = tm.parMetrics(d->anchor_ref_).height(); - } // Look at paragraph heights on-screen - int sumh = 0; - int nh = 0; - for (pit_type pit = d->anchor_ref_; pit <= parsize; ++pit) { - if (sumh > height_) - break; - int const h2 = tm.parMetrics(pit).height(); - sumh += h2; - nh++; + pit_type first_visible_pit = -1; + pair first = tm.first(); + pair last = tm.last(); + for (pit_type pit = first.first; pit <= last.first; ++pit) { + ParagraphMetrics const & pm = tm.parMetrics(pit); + d->par_height_[pit] = pm.height(); + if (first_visible_pit >= 0 || pm.position() + pm.descent() <= 0) + continue; + first_visible_pit = pit; + LYXERR(Debug::SCROLLING, "first visible pit " << first_visible_pit); + // FIXME: we should look for the first visible row within + // the deepest inset! + int row_pos = pm.position(); + size_t const nrows = pm.rows().size(); + for (size_t i = 0; i != nrows; ++i) { + Row const & row = pm.rows()[i]; + if (row_pos >= 0) { + LYXERR(Debug::SCROLLING, "first visible row " << i + << "(row pos = " << row_pos << ");"); + break; + } + row_pos += row.height(); + } + d->scrollbarParameters_.position = row_pos; } - BOOST_ASSERT(nh); - int const hav = sumh / nh; - // More realistic average paragraph height - if (hav > d->wh_) - d->wh_ = hav; + d->scrollbarParameters_.height = 0; + for (size_t i = 0; i != d->par_height_.size(); ++i) { + if (i == first_visible_pit) + d->scrollbarParameters_.position += d->scrollbarParameters_.height; + d->scrollbarParameters_.height += d->par_height_[i]; + } - BOOST_ASSERT(h); - d->scrollbarParameters_.height = (parsize + 1) * d->wh_; - d->scrollbarParameters_.position = d->anchor_ref_ * d->wh_ + int(d->offset_ref_ * d->wh_ / float(h)); - d->scrollbarParameters_.lineScrollHeight = int(d->wh_ * defaultRowHeight() / float(h)); + // We prefer fixed size line scrolling. + d->scrollbarParameters_.lineScrollHeight = defaultRowHeight(); } @@ -486,27 +481,58 @@ ScrollbarParameters const & BufferView::scrollbarParameters() const } -void BufferView::scrollDocView(int value) +docstring BufferView::toolTip(int x, int y) const { - LYXERR(Debug::GUI, "[ value = " << value << "]"); + // Get inset under mouse, if there is one. + Inset const * covering_inset = getCoveringInset(buffer_.text(), x, y); + if (!covering_inset) + // No inset, no tooltip... + return docstring(); + return covering_inset->toolTip(*this, x, y); +} - Text & t = buffer_.text(); - TextMetrics & tm = d->text_metrics_[&t]; - float const bar = value / float(d->wh_ * t.paragraphs().size()); +docstring BufferView::contextMenu(int x, int y) const +{ + // Get inset under mouse, if there is one. + Inset const * covering_inset = getCoveringInset(buffer_.text(), x, y); + if (covering_inset) + return covering_inset->contextMenu(*this, x, y); + + // FIXME: Do something more elaborate here. + return from_ascii("edit"); +} + + +void BufferView::scrollDocView(int value) +{ + int const offset = value - d->scrollbarParameters_.position; + // If the offset is less than 2 screen height, prefer to scroll instead. + if (abs(offset) <= 2 * height_) { + scroll(offset); + return; + } + + int par_pos = 0; + for (size_t i = 0; i != d->par_height_.size(); ++i) { + par_pos += d->par_height_[i]; + if (par_pos >= value) { + d->anchor_pit_ = pit_type(i); + break; + } + } - d->anchor_ref_ = int(bar * t.paragraphs().size()); - if (d->anchor_ref_ > int(t.paragraphs().size()) - 1) - d->anchor_ref_ = int(t.paragraphs().size()) - 1; + LYXERR(Debug::SCROLLING, "value = " << value + << "\tanchor_ref_ = " << d->anchor_pit_ + << "\tpar_pos = " << par_pos); - tm.redoParagraph(d->anchor_ref_); - int const h = tm.parMetrics(d->anchor_ref_).height(); - d->offset_ref_ = int((bar * t.paragraphs().size() - d->anchor_ref_) * h); + d->anchor_ypos_ = par_pos - value; updateMetrics(); buffer_.changed(); } +// FIXME: this method is not working well. void BufferView::setCursorFromScrollbar() { TextMetrics & tm = d->text_metrics_[&buffer_.text()]; @@ -639,8 +665,7 @@ bool BufferView::moveToPosition(pit_type bottom_pit, pos_type bottom_pos, // So we need a redraw! buffer_.changed(); if (fitCursor()) - // We need another redraw because of the screen recentering. - buffer_.changed(); + showCursor(); } return success; @@ -669,31 +694,61 @@ int BufferView::workWidth() const } -void BufferView::updateOffsetRef() +void BufferView::showCursor() { - // No need to update d->offset_ref_ in this case. - if (!d->need_centering_) - return; - // We are not properly started yet, delay until resizing is // done. if (height_ == 0) return; + LYXERR(Debug::SCROLLING, "recentering!"); + CursorSlice & bot = d->cursor_.bottom(); TextMetrics & tm = d->text_metrics_[bot.text()]; - ParagraphMetrics const & pm = tm.parMetrics(bot.pit()); - int y = coordOffset(d->cursor_, d->cursor_.boundary()).y_; - d->offset_ref_ = y + pm.ascent() - height_ / 2; - d->need_centering_ = false; -} + pos_type const max_pit = pos_type(bot.text()->paragraphs().size() - 1); + int bot_pit = d->cursor_.bottom().pit(); + if (bot_pit > max_pit) { + // FIXME: Why does this happen? + LYXERR0("bottom pit is greater that max pit: " + << bot_pit << " > " << max_pit); + bot_pit = max_pit; + } + if (bot_pit == tm.first().first - 1) + tm.newParMetricsUp(); + else if (bot_pit == tm.last().first + 1) + tm.newParMetricsDown(); -void BufferView::center() -{ - d->anchor_ref_ = d->cursor_.bottom().pit(); - d->need_centering_ = true; + if (tm.has(bot_pit)) { + ParagraphMetrics const & pm = tm.parMetrics(bot_pit); + int offset = coordOffset(d->cursor_, d->cursor_.boundary()).y_; + int ypos = pm.position() + offset; + Dimension const & row_dim = d->cursor_.textRow().dimension(); + if (ypos - row_dim.ascent() < 0) + scrollUp(- ypos + row_dim.ascent()); + else if (ypos + row_dim.descent() > height_) + scrollDown(ypos - height_ + row_dim.descent()); + // else, nothing to do, the cursor is already visible so we just return. + return; + } + + tm.redoParagraph(bot_pit); + ParagraphMetrics const & pm = tm.parMetrics(bot_pit); + int offset = coordOffset(d->cursor_, d->cursor_.boundary()).y_; + + d->anchor_pit_ = bot_pit; + Dimension const & row_dim = d->cursor_.textRow().dimension(); + + if (d->anchor_pit_ == 0) + d->anchor_ypos_ = offset + pm.ascent(); + else if (d->anchor_pit_ == max_pit) + d->anchor_ypos_ = height_ - offset - row_dim.descent(); + else + d->anchor_ypos_ = defaultRowHeight() * 2 - offset - row_dim.descent(); + + updateMetrics(); + buffer_.changed(); } @@ -732,7 +787,7 @@ FuncStatus BufferView::getStatus(FuncRequest const & cmd) case LFUN_SCREEN_RECENTER: case LFUN_BIBTEX_DATABASE_ADD: case LFUN_BIBTEX_DATABASE_DEL: - case LFUN_WORDS_COUNT: + case LFUN_STATISTICS: case LFUN_NEXT_INSET_TOGGLE: flag.enabled(true); break; @@ -776,6 +831,7 @@ FuncStatus BufferView::getStatus(FuncRequest const & cmd) case LFUN_SCREEN_UP: case LFUN_SCREEN_DOWN: + case LFUN_SCROLL: flag.enabled(true); break; @@ -848,7 +904,7 @@ FuncStatus BufferView::getStatus(FuncRequest const & cmd) } -Update::flags BufferView::dispatch(FuncRequest const & cmd) +bool BufferView::dispatch(FuncRequest const & cmd) { //lyxerr << [ cmd = " << cmd << "]" << endl; @@ -860,27 +916,25 @@ Update::flags BufferView::dispatch(FuncRequest const & cmd) << " button[" << cmd.button() << ']'); Cursor & cur = d->cursor_; - // Default Update flags. - Update::flags updateFlags = Update::Force | Update::FitCursor; switch (cmd.action) { case LFUN_UNDO: cur.message(_("Undo")); cur.clearSelection(); - if (!cur.textUndo()) { + if (!cur.textUndo()) cur.message(_("No further undo information")); - updateFlags = Update::None; - } + else + processUpdateFlags(Update::Force | Update::FitCursor); break; case LFUN_REDO: cur.message(_("Redo")); cur.clearSelection(); - if (!cur.textRedo()) { + if (!cur.textRedo()) cur.message(_("No further redo information")); - updateFlags = Update::None; - } + else + processUpdateFlags(Update::Force | Update::FitCursor); break; case LFUN_FONT_STATE: @@ -926,12 +980,12 @@ Update::flags BufferView::dispatch(FuncRequest const & cmd) if (b == &buffer_) { // Set the cursor setCursor(makeDocIterator(par, 0)); + showCursor(); } else { // Switch to other buffer view and resend cmd theLyXFunc().dispatch(FuncRequest( LFUN_BUFFER_SWITCH, b->absFileName())); theLyXFunc().dispatch(cmd); - updateFlags = Update::None; } break; } @@ -1053,7 +1107,7 @@ Update::flags BufferView::dispatch(FuncRequest const & cmd) break; case LFUN_SCREEN_RECENTER: - center(); + showCursor(); break; case LFUN_BIBTEX_DATABASE_ADD: { @@ -1080,7 +1134,7 @@ Update::flags BufferView::dispatch(FuncRequest const & cmd) break; } - case LFUN_WORDS_COUNT: { + case LFUN_STATISTICS: { DocIterator from, to; if (cur.selection()) { from = cur.selectionBegin(); @@ -1089,24 +1143,30 @@ Update::flags BufferView::dispatch(FuncRequest const & cmd) from = doc_iterator_begin(buffer_.inset()); to = doc_iterator_end(buffer_.inset()); } - int const count = countWords(from, to); + int const words = countWords(from, to); + int const chars = countChars(from, to, false); + int const chars_blanks = countChars(from, to, true); docstring message; - if (count != 1) { - if (cur.selection()) - message = bformat(_("%1$d words in selection."), - count); - else - message = bformat(_("%1$d words in document."), - count); - } - else { - if (cur.selection()) - message = _("One word in selection."); - else - message = _("One word in document."); - } - - Alert::information(_("Count words"), message); + if (cur.selection()) + message = _("Statistics for the selection:\n"); + else + message = _("Statistics for the document:\n"); + if (words != 1) + message += bformat(_("\n%1$d words"), words); + else + message += _("\nOne word"); + if (chars_blanks != 1) + message += bformat(_("\n%1$d characters (including blanks)"), + chars_blanks); + else + message += _("\nOne character (including blanks)"); + if (chars != 1) + message += bformat(_("\n%1$d characters (excluding blanks)"), + chars); + else + message += _("\nOne character (excluding blanks)"); + + Alert::information(_("Statistics"), message); } break; @@ -1115,10 +1175,15 @@ Update::flags BufferView::dispatch(FuncRequest const & cmd) buffer_.params().compressed = !buffer_.params().compressed; break; - case LFUN_BUFFER_TOGGLE_EMBEDDING: + case LFUN_BUFFER_TOGGLE_EMBEDDING: { // turn embedding on/off - buffer_.embeddedFiles().enable(!buffer_.params().embedded); + try { + buffer_.embeddedFiles().enable(!buffer_.params().embedded, buffer_); + } catch (ExceptionMessage const & message) { + Alert::error(message.title_, message.details_); + } break; + } case LFUN_NEXT_INSET_TOGGLE: { // this is the real function we want to invoke @@ -1142,9 +1207,11 @@ Update::flags BufferView::dispatch(FuncRequest const & cmd) if (!cur.result().dispatched()) cur.dispatch(tmpcmd); - if (cur.result().dispatched()) - cur.clearSelection(); - + if (!cur.result().dispatched()) + // It did not work too; no action needed. + break; + cur.clearSelection(); + processUpdateFlags(Update::SinglePar | Update::FitCursor); break; } @@ -1153,13 +1220,7 @@ Update::flags BufferView::dispatch(FuncRequest const & cmd) Point p = getPos(cur, cur.boundary()); if (p.y_ < 0 || p.y_ > height_) { // The cursor is off-screen so recenter before proceeding. - center(); - updateMetrics(); - //FIXME: updateMetrics() does not update paragraph position - // This is done at draw() time. So we need a redraw! - // But no screen update is needed. - d->update_strategy_ = NoScreenUpdate; - buffer_.changed(); + showCursor(); p = getPos(cur, cur.boundary()); } scroll(cmd.action == LFUN_SCREEN_UP? - height_ : height_); @@ -1167,13 +1228,18 @@ Update::flags BufferView::dispatch(FuncRequest const & cmd) d->text_metrics_[&buffer_.text()].editXY(cur, p.x_, p.y_); //FIXME: what to do with cur.x_target()? cur.finishUndo(); - // The metrics are already up to date. see scroll() - updateFlags = Update::None; break; } + case LFUN_SCROLL: + lfunScroll(cmd); + break; + case LFUN_SCREEN_UP_SELECT: case LFUN_SCREEN_DOWN_SELECT: { + // Those two are not ready yet for consumption. + return false; + cur.selHandle(true); size_t initial_depth = cur.depth(); Point const p = getPos(cur, cur.boundary()); @@ -1189,15 +1255,14 @@ Update::flags BufferView::dispatch(FuncRequest const & cmd) // But no screen update is needed. d->update_strategy_ = NoScreenUpdate; buffer_.changed(); - updateFlags = Update::Force | Update::FitCursor; break; } default: - updateFlags = Update::None; + return false; } - return updateFlags; + return true; } @@ -1243,11 +1308,15 @@ void BufferView::resize(int width, int height) width_ = width; height_ = height; + // Clear the paragraph height cache. + d->par_height_.clear(); + updateMetrics(); } -Inset const * BufferView::getCoveringInset(Text const & text, int x, int y) +Inset const * BufferView::getCoveringInset(Text const & text, + int x, int y) const { TextMetrics & tm = d->text_metrics_[&text]; Inset * inset = tm.checkInsetHit(x, y); @@ -1356,6 +1425,27 @@ void BufferView::mouseEventDispatch(FuncRequest const & cmd0) } +void BufferView::lfunScroll(FuncRequest const & cmd) +{ + string const scroll_type = cmd.getArg(0); + int const scroll_step = + (scroll_type == "line")? d->scrollbarParameters_.lineScrollHeight + : (scroll_type == "page")? height_ : 0; + if (scroll_step == 0) + return; + string const scroll_quantity = cmd.getArg(1); + if (scroll_quantity == "up") + scrollUp(scroll_step); + else if (scroll_quantity == "down") + scrollDown(scroll_step); + else { + int const scroll_value = convert(scroll_quantity); + if (scroll_value) + scroll(scroll_step * scroll_value); + } +} + + void BufferView::scroll(int y) { if (y > 0) @@ -1371,7 +1461,7 @@ void BufferView::scrollDown(int offset) TextMetrics & tm = d->text_metrics_[text]; int ymax = height_ + offset; while (true) { - std::pair last = tm.last(); + pair last = tm.last(); int bottom_pos = last.second->position() + last.second->descent(); if (last.first + 1 == int(text->paragraphs().size())) { if (bottom_pos <= height_) @@ -1383,7 +1473,7 @@ void BufferView::scrollDown(int offset) break; tm.newParMetricsDown(); } - d->offset_ref_ += offset; + d->anchor_ypos_ -= offset; updateMetrics(); buffer_.changed(); } @@ -1395,7 +1485,7 @@ void BufferView::scrollUp(int offset) TextMetrics & tm = d->text_metrics_[text]; int ymin = - offset; while (true) { - std::pair first = tm.first(); + pair first = tm.first(); int top_pos = first.second->position() - first.second->ascent(); if (first.first == 0) { if (top_pos >= 0) @@ -1407,7 +1497,7 @@ void BufferView::scrollUp(int offset) break; tm.newParMetricsUp(); } - d->offset_ref_ -= offset; + d->anchor_ypos_ += offset; updateMetrics(); buffer_.changed(); } @@ -1433,9 +1523,9 @@ void BufferView::gotoLabel(docstring const & label) for (InsetIterator it = inset_iterator_begin(buffer_.inset()); it; ++it) { vector labels; it->getLabelList(buffer_, labels); - if (std::find(labels.begin(), labels.end(), label) != labels.end()) { + if (find(labels.begin(), labels.end(), label) != labels.end()) { setCursor(it); - processUpdateFlags(Update::FitCursor); + showCursor(); return; } } @@ -1499,6 +1589,8 @@ bool BufferView::checkDepm(Cursor & cur, Cursor & old) if (!changed) return false; + d->cursor_ = cur; + updateLabels(buffer_); updateMetrics(); @@ -1540,7 +1632,7 @@ bool BufferView::mouseSetCursor(Cursor & cur, bool select) // For an example, see bug 2933: // http://bugzilla.lyx.org/show_bug.cgi?id=2933 // The code below could maybe be moved to a DocIterator method. - //lyxerr << "cur before " << cur <cursor_.setCursor(dit); d->cursor_.boundary(cur.boundary()); @@ -1594,7 +1686,7 @@ Cursor const & BufferView::cursor() const pit_type BufferView::anchor_ref() const { - return d->anchor_ref_; + return d->anchor_pit_; } @@ -1631,11 +1723,6 @@ void BufferView::updateMetrics() Text & buftext = buffer_.text(); pit_type const npit = int(buftext.paragraphs().size()); - if (d->anchor_ref_ > int(npit - 1)) { - d->anchor_ref_ = int(npit - 1); - d->offset_ref_ = 0; - } - // Clear out the position cache in case of full screen redraw, d->coord_cache_.clear(); @@ -1646,59 +1733,48 @@ void BufferView::updateMetrics() TextMetrics & tm = textMetrics(&buftext); - pit_type const pit = d->anchor_ref_; - int pit1 = pit; - int pit2 = pit; - // Rebreak anchor paragraph. - tm.redoParagraph(pit); - - // Take care of anchor offset if case a recentering is needed. - updateOffsetRef(); + tm.redoParagraph(d->anchor_pit_); + ParagraphMetrics & anchor_pm = tm.par_metrics_[d->anchor_pit_]; + anchor_pm.setPosition(d->anchor_ypos_); - int y0 = tm.parMetrics(pit).ascent() - d->offset_ref_; + LYXERR(Debug::PAINTING, "metrics: " + << " anchor pit = " << d->anchor_pit_ + << " anchor ypos = " << d->anchor_ypos_); // Redo paragraphs above anchor if necessary. - int y1 = y0; - while (y1 > 0 && pit1 > 0) { - y1 -= tm.parMetrics(pit1).ascent(); - --pit1; + int y1 = d->anchor_ypos_ - anchor_pm.ascent(); + // We are now just above the anchor paragraph. + pit_type pit1 = d->anchor_pit_ - 1; + for (; pit1 >= 0 && y1 >= 0; --pit1) { tm.redoParagraph(pit1); - y1 -= tm.parMetrics(pit1).descent(); - } - - // Take care of ascent of first line - y1 -= tm.parMetrics(pit1).ascent(); - - // Normalize anchor for next time - d->anchor_ref_ = pit1; - d->offset_ref_ = -y1; - - // Grey at the beginning is ugly - if (pit1 == 0 && y1 > 0) { - y0 -= y1; - y1 = 0; - d->anchor_ref_ = 0; + ParagraphMetrics & pm = tm.par_metrics_[pit1]; + y1 -= pm.descent(); + // Save the paragraph position in the cache. + pm.setPosition(y1); + y1 -= pm.ascent(); } // Redo paragraphs below the anchor if necessary. - int y2 = y0; - while (y2 < height_ && pit2 < int(npit) - 1) { - y2 += tm.parMetrics(pit2).descent(); - ++pit2; + int y2 = d->anchor_ypos_ + anchor_pm.descent(); + // We are now just below the anchor paragraph. + pit_type pit2 = d->anchor_pit_ + 1; + for (; pit2 < npit && y2 <= height_; ++pit2) { tm.redoParagraph(pit2); - y2 += tm.parMetrics(pit2).ascent(); + ParagraphMetrics & pm = tm.par_metrics_[pit2]; + y2 += pm.ascent(); + // Save the paragraph position in the cache. + pm.setPosition(y2); + y2 += pm.descent(); } - // Take care of descent of last line - y2 += tm.parMetrics(pit2).descent(); - - LYXERR(Debug::PAINTING, "\n y1: " << y1 - << " y2: " << y2 - << " pit1: " << pit1 - << " pit2: " << pit2 - << " npit: " << npit - << " singlepar: 0"); + LYXERR(Debug::PAINTING, "Metrics: " + << " anchor pit = " << d->anchor_pit_ + << " anchor ypos = " << d->anchor_ypos_ + << " y1 = " << y1 + << " y2 = " << y2 + << " pit1 = " << pit1 + << " pit2 = " << pit2); d->update_strategy_ = FullScreenUpdate; @@ -1805,7 +1881,7 @@ Point BufferView::coordOffset(DocIterator const & dit, bool boundary) const int pos = sl.pos(); if (pos && boundary) --pos; -// lyxerr << "coordOffset: boundary:" << boundary << " depth:" << dit.depth() << " pos:" << pos << " sl.pos:" << sl.pos() << std::endl; +// lyxerr << "coordOffset: boundary:" << boundary << " depth:" << dit.depth() << " pos:" << pos << " sl.pos:" << sl.pos() << endl; rend = pm.pos2row(pos); } else rend = pm.pos2row(sl.pos()); @@ -1851,7 +1927,7 @@ void BufferView::draw(frontend::Painter & pain) LYXERR(Debug::PAINTING, "\t\t*** START DRAWING ***"); Text & text = buffer_.text(); TextMetrics const & tm = d->text_metrics_[&text]; - int const y = - d->offset_ref_ + tm.parMetrics(d->anchor_ref_).ascent(); + int const y = tm.first().second->position(); PainterInfo pi(this, pain); switch (d->update_strategy_) { @@ -1884,17 +1960,35 @@ void BufferView::draw(frontend::Painter & pain) // Clear background. pain.fillRectangle(0, 0, width_, height_, buffer_.inset().backgroundColor()); + + // Draw everything. tm.draw(pi, 0, y); // and possibly grey out below - std::pair lastpm = tm.last(); + pair lastpm = tm.last(); int const y2 = lastpm.second->position() + lastpm.second->descent(); if (y2 < height_) pain.fillRectangle(0, y2, width_, height_ - y2, Color_bottomarea); break; } - LYXERR(Debug::PAINTING, "\n\t\t*** END DRAWING ***"); + + // The scrollbar needs an update. + updateScrollbar(); + + // Normalize anchor for next time + pair firstpm = tm.first(); + pair lastpm = tm.last(); + for (pit_type pit = firstpm.first; pit <= lastpm.first; ++pit) { + ParagraphMetrics const & pm = tm.parMetrics(pit); + if (pm.position() + pm.descent() > 0) { + d->anchor_pit_ = pit; + d->anchor_ypos_ = pm.position(); + break; + } + } + LYXERR(Debug::PAINTING, "Found new anchor pit = " << d->anchor_pit_ + << " anchor ypos = " << d->anchor_ypos_); } @@ -1905,22 +1999,22 @@ void BufferView::message(docstring const & msg) } -void BufferView::showDialog(std::string const & name) +void BufferView::showDialog(string const & name) { if (d->gui_) d->gui_->showDialog(name, string()); } -void BufferView::showDialog(std::string const & name, - std::string const & data, Inset * inset) +void BufferView::showDialog(string const & name, + string const & data, Inset * inset) { if (d->gui_) d->gui_->showDialog(name, data, inset); } -void BufferView::updateDialog(std::string const & name, std::string const & data) +void BufferView::updateDialog(string const & name, string const & data) { if (d->gui_) d->gui_->updateDialog(name, data);