X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2FBufferView.cpp;h=d5ee51739c26aa8ffddf2034d4a96cdb706857d8;hb=cf15bd840b71a01ccbb2fbbbabc7066237b4bfd2;hp=31486b3ab9775f7760f66ed978f6664dd437b262;hpb=b8be6be26885a205606144785e8e10802edfd318;p=lyx.git diff --git a/src/BufferView.cpp b/src/BufferView.cpp index 31486b3ab9..d5ee51739c 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,7 +68,7 @@ #include "support/convert.h" #include "support/debug.h" -#include "support/FileFilterList.h" +#include "support/ExceptionMessage.h" #include "support/filetools.h" #include "support/gettext.h" #include "support/lstrings.h" @@ -112,7 +113,7 @@ bool findNextInset(DocIterator & dit, vector const & codes, while (tmpdit) { Inset const * inset = tmpdit.nextInset(); if (inset - && find(codes.begin(), codes.end(), inset->lyxCode()) != codes.end() + && std::find(codes.begin(), codes.end(), inset->lyxCode()) != codes.end() && (contents.empty() || static_cast(inset)->getFirstNonOptParam() == contents)) { dit = tmpdit; @@ -138,7 +139,7 @@ bool findInset(DocIterator & dit, vector const & codes, if (same_content) { Inset const * inset = tmpdit.nextInset(); if (inset - && find(codes.begin(), codes.end(), inset->lyxCode()) != codes.end()) { + && std::find(codes.begin(), codes.end(), inset->lyxCode()) != codes.end()) { contents = static_cast(inset)->getFirstNonOptParam(); } } @@ -176,6 +177,7 @@ void gotoInset(BufferView * bv, vector const & codes, tmpcur.clearSelection(); bv->setCursor(tmpcur); + bv->showCursor(); } @@ -236,6 +238,13 @@ struct BufferView::Private /// vector par_height_; + /// + DocIterator inlineCompletionPos; + /// + docstring inlineCompletion; + /// + size_t inlineCompletionUniqueChars; + /// keyboard mapping object. Intl intl_; @@ -255,7 +264,7 @@ struct BufferView::Private BufferView::BufferView(Buffer & buf) - : width_(0), height_(0), buffer_(buf), d(new Private(*this)) + : width_(0), height_(0), full_screen_(false), buffer_(buf), d(new Private(*this)) { d->xsel_cache_.set = false; d->intl_.initKeyMapper(lyxrc.use_kbmap); @@ -285,6 +294,24 @@ BufferView::~BufferView() } +int BufferView::rightMargin() const +{ + // The additional test for the case the outliner is opened. + if (!full_screen_ || + !lyxrc.full_screen_limit || + width_ < lyxrc.full_screen_width + 20) + return 10; + + return (width_ - lyxrc.full_screen_width) / 2; +} + + +int BufferView::leftMargin() const +{ + return rightMargin(); +} + + Intl & BufferView::getIntl() { return d->intl_; @@ -349,9 +376,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(). @@ -416,6 +441,11 @@ void BufferView::updateScrollbar() if (height_ == 0) return; + // We prefer fixed size line scrolling. + d->scrollbarParameters_.single_step = defaultRowHeight(); + // We prefer full screen page scrolling. + d->scrollbarParameters_.page_step = height_; + Text & t = buffer_.text(); TextMetrics & tm = d->text_metrics_[&t]; @@ -424,42 +454,43 @@ void BufferView::updateScrollbar() << " curr par: " << d->cursor_.bottom().pit() << " default height " << defaultRowHeight()); - int const parsize = int(t.paragraphs().size()); + size_t const parsize = t.paragraphs().size(); if (d->par_height_.size() != parsize) { d->par_height_.clear(); - // FIXME: We assume a default paragraph height of 4 rows. This + // 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() * 4); + 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 - // Look at paragraph heights on-screen - 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) - first_visible_pit = pit; + d->par_height_[pit] = tm.parMetrics(pit).height(); + LYXERR(Debug::SCROLLING, "storing height for pit " << pit << " : " + << d->par_height_[pit]); } - LYXERR(Debug::SCROLLING, "first_visible_pit " << first_visible_pit); - - d->scrollbarParameters_.height = 0; - for (size_t i = 0; i != d->par_height_.size(); ++i) { - d->scrollbarParameters_.height += d->par_height_[i]; - if (i != first_visible_pit) - continue; - // FIXME: we should look for the first visible row within - // the deepest inset! - d->scrollbarParameters_.position = d->scrollbarParameters_.height; + int top_pos = first.second->position() - first.second->ascent(); + int bottom_pos = last.second->position() + last.second->descent(); + bool first_visible = first.first == 0 && top_pos >= 0; + bool last_visible = last.first + 1 == int(parsize) && bottom_pos <= height_; + if (first_visible && last_visible) { + d->scrollbarParameters_.min = 0; + d->scrollbarParameters_.max = 0; + return; } - d->scrollbarParameters_.lineScrollHeight = - tm.parMetrics(first_visible_pit).rows()[0].height(); + d->scrollbarParameters_.min = top_pos; + for (size_t i = 0; i != size_t(first.first); ++i) + d->scrollbarParameters_.min -= d->par_height_[i]; + d->scrollbarParameters_.max = bottom_pos; + for (size_t i = last.first + 1; i != parsize; ++i) + d->scrollbarParameters_.max += d->par_height_[i]; + + d->scrollbarParameters_.position = 0; + // The reference is the top position so we remove one page. + d->scrollbarParameters_.max -= d->scrollbarParameters_.page_step; } @@ -469,31 +500,76 @@ ScrollbarParameters const & BufferView::scrollbarParameters() const } +docstring BufferView::toolTip(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) + // No inset, no tooltip... + return docstring(); + return covering_inset->toolTip(*this, x, y); +} + + +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; - //TextMetrics & tm = d->text_metrics_[&buffer_.text()]; - if (abs(offset) <= 3*height_) { + // 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) { + // cut off at the top + if (value <= d->scrollbarParameters_.min) { + DocIterator dit = doc_iterator_begin(buffer_.inset()); + showCursor(dit); + LYXERR(Debug::SCROLLING, "scroll to top"); + return; + } + + // cut off at the bottom + if (value >= d->scrollbarParameters_.max) { + DocIterator dit = doc_iterator_end(buffer_.inset()); + dit.backwardPos(); + showCursor(dit); + LYXERR(Debug::SCROLLING, "scroll to bottom"); + return; + } + + // find paragraph at target position + int par_pos = d->scrollbarParameters_.min; + pit_type i = 0; + for (; i != int(d->par_height_.size()); ++i) { par_pos += d->par_height_[i]; - if (par_pos >= value) { - d->anchor_pit_ = pit_type(i); + if (par_pos >= value) break; - } } - LYXERR(Debug::SCROLLING, "value = " << value - << "\tanchor_ref_ = " << d->anchor_pit_ - << "\tpar_pos = " << par_pos); + if (par_pos < value) { + // It seems we didn't find the correct pit so stay on the safe side and + // scroll to bottom. + LYXERR0("scrolling position not found!"); + scrollDocView(d->scrollbarParameters_.max); + return; + } - d->anchor_ypos_ = par_pos - value; - updateMetrics(); - buffer_.changed(); + DocIterator dit = doc_iterator_begin(buffer_.inset()); + dit.pit() = i; + LYXERR(Debug::SCROLLING, "value = " << value << " -> scroll to pit " << i); + showCursor(dit); } @@ -580,7 +656,7 @@ bool BufferView::moveToPosition(pit_type bottom_pit, pos_type bottom_pos, int top_id, pos_type top_pos) { bool success = false; - DocIterator doc_it; + DocIterator dit; d->cursor_.clearSelection(); @@ -588,19 +664,19 @@ bool BufferView::moveToPosition(pit_type bottom_pit, pos_type bottom_pos, // This is the case for a 'live' bookmark when unique paragraph ID // is used to track bookmarks. if (top_id > 0) { - ParIterator par = buffer_.getParFromID(top_id); - if (par != buffer_.par_iterator_end()) { - doc_it = makeDocIterator(par, min(par->size(), top_pos)); + dit = buffer_.getParFromID(top_id); + if (!dit.atEnd()) { + dit.pos() = min(dit.paragraph().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 = doc_it.depth(); + size_t const n = dit.depth(); for (size_t i = 0; i < n; ++i) - if (doc_it[i].inset().editable() != Inset::HIGHLY_EDITABLE) { - doc_it.resize(i); + if (dit[i].inset().editable() != Inset::HIGHLY_EDITABLE) { + dit.resize(i); break; } success = true; @@ -613,16 +689,17 @@ bool BufferView::moveToPosition(pit_type bottom_pit, pos_type bottom_pos, // restoration is inaccurate. If a bookmark was within an inset, // it will be restored to the left of the outmost inset that contains // the bookmark. - if (static_cast(bottom_pit) < buffer_.paragraphs().size()) { - doc_it = doc_iterator_begin(buffer_.inset()); - doc_it.pit() = bottom_pit; - doc_it.pos() = min(bottom_pos, doc_it.paragraph().size()); + if (bottom_pit < int(buffer_.paragraphs().size())) { + dit = doc_iterator_begin(buffer_.inset()); + + dit.pit() = bottom_pit; + dit.pos() = min(bottom_pos, dit.paragraph().size()); success = true; } if (success) { // Note: only bottom (document) level pit is set. - setCursor(doc_it); + setCursor(dit); // set the current font. d->cursor_.setCurrentFont(); // To center the screen on this new position we need the @@ -660,6 +737,12 @@ int BufferView::workWidth() const void BufferView::showCursor() +{ + showCursor(d->cursor_); +} + + +void BufferView::showCursor(DocIterator const & dit) { // We are not properly started yet, delay until resizing is // done. @@ -668,20 +751,32 @@ void BufferView::showCursor() LYXERR(Debug::SCROLLING, "recentering!"); - CursorSlice & bot = d->cursor_.bottom(); + CursorSlice const & bot = dit.bottom(); TextMetrics & tm = d->text_metrics_[bot.text()]; - int const bot_pit = d->cursor_.bottom().pit(); + pos_type const max_pit = pos_type(bot.text()->paragraphs().size() - 1); + int bot_pit = bot.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(); - if (tm.has(bot_pit)) { + if (tm.contains(bot_pit)) { ParagraphMetrics const & pm = tm.parMetrics(bot_pit); - int offset = coordOffset(d->cursor_, d->cursor_.boundary()).y_; + BOOST_ASSERT(!pm.rows().empty()); + // FIXME: smooth scrolling doesn't work in mathed. + CursorSlice const & cs = dit.innerTextSlice(); + int offset = coordOffset(dit, dit.boundary()).y_; int ypos = pm.position() + offset; - Dimension const & row_dim = d->cursor_.textRow().dimension(); + Dimension const & row_dim = + pm.getRow(cs.pos(), dit.boundary()).dimension(); if (ypos - row_dim.ascent() < 0) scrollUp(- ypos + row_dim.ascent()); else if (ypos + row_dim.descent() > height_) @@ -690,21 +785,25 @@ void BufferView::showCursor() return; } + // fix inline completion position + if (d->inlineCompletionPos.fixIfBroken()) + d->inlineCompletionPos = DocIterator(); + tm.redoParagraph(bot_pit); ParagraphMetrics const & pm = tm.parMetrics(bot_pit); - int offset = coordOffset(d->cursor_, d->cursor_.boundary()).y_; + int offset = coordOffset(dit, dit.boundary()).y_; d->anchor_pit_ = bot_pit; - Dimension const & row_dim = d->cursor_.textRow().dimension(); + CursorSlice const & cs = dit.innerTextSlice(); + Dimension const & row_dim = + pm.getRow(cs.pos(), dit.boundary()).dimension(); if (d->anchor_pit_ == 0) d->anchor_ypos_ = offset + pm.ascent(); - else if (d->anchor_pit_ >= pos_type(bot.text()->paragraphs().size() - 1)) { - d->anchor_pit_ = bot.text()->paragraphs().size() - 1; + else if (d->anchor_pit_ == max_pit) d->anchor_ypos_ = height_ - offset - row_dim.descent(); - } else { - d->anchor_ypos_ = offset + pm.ascent() - height_ / 2; - } + else + d->anchor_ypos_ = defaultRowHeight() * 2 - offset - row_dim.descent(); updateMetrics(); buffer_.changed(); @@ -746,7 +845,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; @@ -790,6 +889,7 @@ FuncStatus BufferView::getStatus(FuncRequest const & cmd) case LFUN_SCREEN_UP: case LFUN_SCREEN_DOWN: + case LFUN_SCROLL: flag.enabled(true); break; @@ -806,7 +906,7 @@ FuncStatus BufferView::getStatus(FuncRequest const & cmd) case LFUN_LAYOUT: case LFUN_LAYOUT_PARAGRAPH: - flag.enabled(cur.inset().forceDefaultParagraphs(cur.idx())); + flag.enabled(cur.inset().allowParagraphCustomization(cur.idx())); break; case LFUN_INSET_SETTINGS: { @@ -862,7 +962,7 @@ FuncStatus BufferView::getStatus(FuncRequest const & cmd) } -Update::flags BufferView::dispatch(FuncRequest const & cmd) +bool BufferView::dispatch(FuncRequest const & cmd) { //lyxerr << [ cmd = " << cmd << "]" << endl; @@ -874,27 +974,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: @@ -929,27 +1027,27 @@ Update::flags BufferView::dispatch(FuncRequest const & cmd) for (Buffer * b = &buffer_; i == 0 || b != &buffer_; b = theBufferList().next(b)) { - ParIterator par = b->getParFromID(id); - if (par == b->par_iterator_end()) { + DocIterator dit = b->getParFromID(id); + if (dit.atEnd()) { LYXERR(Debug::INFO, "No matching paragraph found! [" << id << "]."); + ++i; + continue; + } + LYXERR(Debug::INFO, "Paragraph " << dit.paragraph().id() + << " found in buffer `" + << b->absFileName() << "'."); + + if (b == &buffer_) { + // Set the cursor + setCursor(dit); + processUpdateFlags(Update::Force | Update::FitCursor); } else { - LYXERR(Debug::INFO, "Paragraph " << par->id() - << " found in buffer `" - << b->absFileName() << "'."); - - if (b == &buffer_) { - // Set the cursor - setCursor(makeDocIterator(par, 0)); - } else { - // Switch to other buffer view and resend cmd - theLyXFunc().dispatch(FuncRequest( - LFUN_BUFFER_SWITCH, b->absFileName())); - theLyXFunc().dispatch(cmd); - updateFlags = Update::None; - } - break; + // Switch to other buffer view and resend cmd + theLyXFunc().dispatch(FuncRequest( + LFUN_BUFFER_SWITCH, b->absFileName())); + theLyXFunc().dispatch(cmd); } - ++i; + break; } break; } @@ -1022,7 +1120,10 @@ Update::flags BufferView::dispatch(FuncRequest const & cmd) break; case LFUN_WORD_FIND: - find(this, cmd); + if (find(this, cmd)) + showCursor(); + else + message(_("String not found!")); break; case LFUN_WORD_REPLACE: { @@ -1094,7 +1195,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(); @@ -1103,24 +1204,33 @@ 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:"); + else + message = _("Statistics for the document:"); + message += "\n\n"; + if (words != 1) + message += bformat(_("%1$d words"), words); + else + message += _("One word"); + message += "\n"; + if (chars_blanks != 1) + message += bformat(_("%1$d characters (including blanks)"), + chars_blanks); + else + message += _("One character (including blanks)"); + message += "\n"; + if (chars != 1) + message += bformat(_("%1$d characters (excluding blanks)"), + chars); + else + message += _("One character (excluding blanks)"); + + Alert::information(_("Statistics"), message); } break; @@ -1129,10 +1239,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 @@ -1156,9 +1271,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; } @@ -1175,13 +1292,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()); @@ -1197,15 +1319,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; } @@ -1253,12 +1374,13 @@ void BufferView::resize(int width, int height) // Clear the paragraph height cache. d->par_height_.clear(); - + // Redo the metrics. 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); @@ -1295,6 +1417,7 @@ void BufferView::mouseEventDispatch(FuncRequest const & cmd0) // 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(); @@ -1356,17 +1479,44 @@ void BufferView::mouseEventDispatch(FuncRequest const & cmd0) if (!cur.result().dispatched()) cur.dispatch(cmd); - //Do we have a selection? + // Notify left insets + if (cur != old) { + old.fixIfBroken(); + bool badcursor = notifyCursorLeaves(old, cur); + if (badcursor) + cursor().fixIfBroken(); + } + + // Do we have a selection? theSelection().haveSelection(cursor().selection()); // If the command has been dispatched, - if (cur.result().dispatched() - // an update is asked, - && cur.result().update()) + if (cur.result().dispatched() || cur.result().update()) processUpdateFlags(cur.result().update()); } +void BufferView::lfunScroll(FuncRequest const & cmd) +{ + string const scroll_type = cmd.getArg(0); + int const scroll_step = + (scroll_type == "line")? d->scrollbarParameters_.single_step + : (scroll_type == "page")? d->scrollbarParameters_.page_step : 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) @@ -1443,10 +1593,10 @@ void BufferView::gotoLabel(docstring const & label) { for (InsetIterator it = inset_iterator_begin(buffer_.inset()); it; ++it) { vector labels; - it->getLabelList(buffer_, labels); - if (find(labels.begin(), labels.end(), label) != labels.end()) { + it->getLabelList(labels); + if (std::find(labels.begin(), labels.end(), label) != labels.end()) { setCursor(it); - processUpdateFlags(Update::FitCursor); + showCursor(); return; } } @@ -1510,6 +1660,8 @@ bool BufferView::checkDepm(Cursor & cur, Cursor & old) if (!changed) return false; + d->cursor_ = cur; + updateLabels(buffer_); updateMetrics(); @@ -1530,8 +1682,12 @@ bool BufferView::mouseSetCursor(Cursor & cur, bool select) // Has the cursor just left the inset? bool badcursor = false; bool leftinset = (&d->cursor_.inset() != &cur.inset()); - if (leftinset) + if (leftinset) { + d->cursor_.fixIfBroken(); badcursor = notifyCursorLeaves(d->cursor_, cur); + if (badcursor) + cur.fixIfBroken(); + } // FIXME: shift-mouse selection doesn't work well across insets. bool do_selection = select && &d->cursor_.anchor().inset() == &cur.inset(); @@ -1544,24 +1700,7 @@ bool BufferView::mouseSetCursor(Cursor & cur, bool select) if (!do_selection && !badcursor && d->cursor_.inTexted()) update |= checkDepm(cur, d->cursor_); - // if the cursor was in an empty script inset and the new - // position is in the nucleus of the inset, notifyCursorLeaves - // will kill the script inset itself. So we check all the - // elements of the cursor to make sure that they are correct. - // 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_.setCursor(cur); d->cursor_.boundary(cur.boundary()); if (do_selection) d->cursor_.setSelection(); @@ -1588,6 +1727,9 @@ void BufferView::putSelectionAt(DocIterator const & cur, } else d->cursor_.setSelection(d->cursor_, length); } + // Ensure a redraw happens in any case because the new selection could + // possibly be on the same screen as the previous selection. + processUpdateFlags(Update::Force | Update::FitCursor); } @@ -1616,6 +1758,10 @@ bool BufferView::singleParUpdate() TextMetrics & tm = textMetrics(&buftext); int old_height = tm.parMetrics(bottom_pit).height(); + // make sure inline completion pointer is ok + if (d->inlineCompletionPos.fixIfBroken()) + d->inlineCompletionPos = DocIterator(); + // In Single Paragraph mode, rebreak only // the (main text, not inset!) paragraph containing the cursor. // (if this paragraph contains insets etc., rebreaking will @@ -1652,9 +1798,30 @@ void BufferView::updateMetrics() TextMetrics & tm = textMetrics(&buftext); + // make sure inline completion pointer is ok + if (d->inlineCompletionPos.fixIfBroken()) + d->inlineCompletionPos = DocIterator(); + + if (d->anchor_pit_ >= npit) + // The anchor pit must have been deleted... + d->anchor_pit_ = npit - 1; + // Rebreak anchor paragraph. tm.redoParagraph(d->anchor_pit_); ParagraphMetrics & anchor_pm = tm.par_metrics_[d->anchor_pit_]; + + // position anchor + if (d->anchor_pit_ == 0) { + int scrollRange = d->scrollbarParameters_.max - d->scrollbarParameters_.min; + + // Complete buffer visible? Then it's easy. + if (scrollRange == 0) + d->anchor_ypos_ = anchor_pm.ascent(); + + // FIXME: Some clever handling needed to show + // the _first_ paragraph up to the top if the cursor is + // in the first line. + } anchor_pm.setPosition(d->anchor_ypos_); LYXERR(Debug::PAINTING, "metrics: " @@ -1665,7 +1832,7 @@ void BufferView::updateMetrics() 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) { + for (; pit1 >= 0 && y1 >= 0; --pit1) { tm.redoParagraph(pit1); ParagraphMetrics & pm = tm.par_metrics_[pit1]; y1 -= pm.descent(); @@ -1678,7 +1845,7 @@ void BufferView::updateMetrics() 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) { + for (; pit2 < npit && y2 <= height_; ++pit2) { tm.redoParagraph(pit2); ParagraphMetrics & pm = tm.par_metrics_[pit2]; y2 += pm.ascent(); @@ -1724,7 +1891,7 @@ void BufferView::insertLyXFile(FileName const & fname) el = buf.errorList("Parse"); buffer_.undo().recordUndo(d->cursor_); cap::pasteParagraphList(d->cursor_, buf.paragraphs(), - buf.params().getTextClassPtr(), el); + buf.params().documentClassPtr(), el); res = _("Document %1$s inserted."); } else { res = _("Could not insert document %1$s"); @@ -1832,7 +1999,7 @@ Point BufferView::getPos(DocIterator const & dit, bool boundary) const { CursorSlice const & bot = dit.bottom(); TextMetrics const & tm = textMetrics(bot.text()); - if (!tm.has(bot.pit())) + if (!tm.contains(bot.pit())) return Point(-1, -1); Point p = coordOffset(dit, boundary); // offset from outer paragraph @@ -2002,4 +2169,67 @@ void BufferView::insertPlaintextFile(FileName const & f, bool asParagraph) buffer_.changed(); } + +docstring const & BufferView::inlineCompletion() const +{ + return d->inlineCompletion; +} + + +size_t const & BufferView::inlineCompletionUniqueChars() const +{ + return d->inlineCompletionUniqueChars; +} + + +DocIterator const & BufferView::inlineCompletionPos() const +{ + return d->inlineCompletionPos; +} + + +bool samePar(DocIterator const & a, DocIterator const & b) +{ + if (a.empty() && b.empty()) + return true; + if (a.empty() || b.empty()) + return false; + return &a.innerParagraph() == &b.innerParagraph(); +} + + +void BufferView::setInlineCompletion(Cursor & cur, DocIterator const & pos, + docstring const & completion, size_t uniqueChars) +{ + uniqueChars = min(completion.size(), uniqueChars); + bool changed = d->inlineCompletion != completion + || d->inlineCompletionUniqueChars != uniqueChars; + bool singlePar = true; + d->inlineCompletion = completion; + d->inlineCompletionUniqueChars = min(completion.size(), uniqueChars); + + //lyxerr << "setInlineCompletion pos=" << pos << " completion=" << completion << " uniqueChars=" << uniqueChars << std::endl; + + // at new position? + DocIterator const & old = d->inlineCompletionPos; + if (old != pos) { + //lyxerr << "inlineCompletionPos changed" << std::endl; + // old or pos are in another paragraph? + if ((!samePar(cur, pos) && !pos.empty()) + || (!samePar(cur, old) && !old.empty())) { + singlePar = false; + //lyxerr << "different paragraph" << std::endl; + } + d->inlineCompletionPos = pos; + } + + // set update flags + if (changed) { + if (singlePar && !(cur.disp_.update() & Update::Force)) + cur.updateFlags(cur.disp_.update() | Update::SinglePar); + else + cur.updateFlags(cur.disp_.update() | Update::Force); + } +} + } // namespace lyx