X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2FBufferView.cpp;h=9b3ae322bb3a3eec65c958a8035c6e3506ccfa89;hb=2df6a6a37876dcfaa755c9aaf6a5ddc1ad58c63e;hp=c8c3b3501e9367614fecb0744b2dd41140bce50e;hpb=3d3a55386d852a1bd2102a8f7372f30853c2740a;p=lyx.git diff --git a/src/BufferView.cpp b/src/BufferView.cpp index c8c3b3501e..9b3ae322bb 100644 --- a/src/BufferView.cpp +++ b/src/BufferView.cpp @@ -49,11 +49,14 @@ #include "TextClass.h" #include "TextMetrics.h" #include "TexRow.h" +#include "TocBackend.h" #include "VSpace.h" #include "WordLangTuple.h" #include "insets/InsetBibtex.h" #include "insets/InsetCommand.h" // ChangeRefs +#include "insets/InsetExternal.h" +#include "insets/InsetGraphics.h" #include "insets/InsetRef.h" #include "insets/InsetText.h" @@ -238,6 +241,13 @@ struct BufferView::Private /// vector par_height_; + /// + DocIterator inlineCompletionPos; + /// + docstring inlineCompletion; + /// + size_t inlineCompletionUniqueChars; + /// keyboard mapping object. Intl intl_; @@ -305,6 +315,18 @@ int BufferView::leftMargin() const } +bool BufferView::isTopScreen() const +{ + return d->scrollbarParameters_.position == d->scrollbarParameters_.min; +} + + +bool BufferView::isBottomScreen() const +{ + return d->scrollbarParameters_.position == d->scrollbarParameters_.max; +} + + Intl & BufferView::getIntl() { return d->intl_; @@ -511,8 +533,7 @@ docstring BufferView::contextMenu(int x, int y) const if (covering_inset) return covering_inset->contextMenu(*this, x, y); - // FIXME: Do something more elaborate here. - return from_ascii("edit"); + return buffer_.inset().contextMenu(*this, x, y); } @@ -761,7 +782,7 @@ void BufferView::showCursor(DocIterator const & dit) 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); BOOST_ASSERT(!pm.rows().empty()); // FIXME: smooth scrolling doesn't work in mathed. @@ -778,6 +799,10 @@ void BufferView::showCursor(DocIterator const & dit) 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(dit, dit.boundary()).y_; @@ -823,6 +848,8 @@ FuncStatus BufferView::getStatus(FuncRequest const & cmd) case LFUN_FONT_STATE: case LFUN_LABEL_INSERT: case LFUN_INFO_INSERT: + case LFUN_EXTERNAL_EDIT: + case LFUN_GRAPHICS_EDIT: case LFUN_PARAGRAPH_GOTO: case LFUN_NOTE_NEXT: case LFUN_REFERENCE_NEXT: @@ -835,10 +862,51 @@ FuncStatus BufferView::getStatus(FuncRequest const & cmd) case LFUN_BIBTEX_DATABASE_ADD: case LFUN_BIBTEX_DATABASE_DEL: case LFUN_STATISTICS: - case LFUN_NEXT_INSET_TOGGLE: flag.enabled(true); break; + case LFUN_NEXT_INSET_TOGGLE: { + // this is the real function we want to invoke + FuncRequest tmpcmd = FuncRequest(LFUN_INSET_TOGGLE, cmd.argument()); + // if there is an inset at cursor, see whether it + // can be modified. + Inset * inset = cur.nextInset(); + if (inset) { + inset->getStatus(cur, tmpcmd, flag); + return flag; + break; + } + // if it did not work, try the underlying inset. + if (!inset || !cur.result().dispatched()) + getStatus(tmpcmd); + + if (!cur.result().dispatched()) + // else disable + flag.enabled(false); + break; + } + + case LFUN_NEXT_INSET_MODIFY: { + // this is the real function we want to invoke + FuncRequest tmpcmd = FuncRequest(LFUN_INSET_MODIFY, cmd.argument()); + // if there is an inset at cursor, see whether it + // can be modified. + Inset * inset = cur.nextInset(); + if (inset) { + inset->getStatus(cur, tmpcmd, flag); + return flag; + break; + } + // if it did not work, try the underlying inset. + if (!inset || !cur.result().dispatched()) + getStatus(tmpcmd); + + if (!cur.result().dispatched()) + // else disable + flag.enabled(false); + break; + } + case LFUN_LABEL_GOTO: { flag.enabled(!cmd.argument().empty() || getInsetByCode(cur, REF_CODE)); @@ -879,14 +947,9 @@ FuncStatus BufferView::getStatus(FuncRequest const & cmd) case LFUN_SCREEN_UP: case LFUN_SCREEN_DOWN: case LFUN_SCROLL: - 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); + flag.enabled(true); break; case LFUN_LAYOUT_TABULAR: @@ -894,37 +957,29 @@ FuncStatus BufferView::getStatus(FuncRequest const & cmd) break; case LFUN_LAYOUT: + flag.enabled(!cur.inset().forceEmptyLayout(cur.idx())); + break; + case LFUN_LAYOUT_PARAGRAPH: flag.enabled(cur.inset().allowParagraphCustomization(cur.idx())); break; case LFUN_INSET_SETTINGS: { InsetCode code = cur.inset().lyxCode(); + if (cur.nextInset()) + code = cur.nextInset()->lyxCode(); bool enable = false; switch (code) { case TABULAR_CODE: - enable = cmd.argument() == "tabular"; - break; case ERT_CODE: - enable = cmd.argument() == "ert"; - break; case FLOAT_CODE: - enable = cmd.argument() == "float"; - break; case WRAP_CODE: - enable = cmd.argument() == "wrap"; - break; case NOTE_CODE: - enable = cmd.argument() == "note"; - break; case BRANCH_CODE: - enable = cmd.argument() == "branch"; - break; case BOX_CODE: - enable = cmd.argument() == "box"; - break; case LISTINGS_CODE: - enable = cmd.argument() == "listings"; + enable = (cmd.argument().empty() || + cmd.getArg(0) == insetName(code)); break; default: break; @@ -1009,6 +1064,25 @@ bool BufferView::dispatch(FuncRequest const & cmd) gotoLabel(label); break; } + + case LFUN_EXTERNAL_EDIT: { + FuncRequest fr(cmd); + InsetExternal * inset = getInsetByCode(d->cursor_, + EXTERNAL_CODE); + if (inset) + inset->dispatch(d->cursor_, fr); + break; + } + + + case LFUN_GRAPHICS_EDIT: { + FuncRequest fr(cmd); + InsetGraphics * inset = getInsetByCode(d->cursor_, + GRAPHICS_CODE); + if (inset) + inset->dispatch(d->cursor_, fr); + break; + } case LFUN_PARAGRAPH_GOTO: { int const id = convert(to_utf8(cmd.argument())); @@ -1231,7 +1305,7 @@ bool BufferView::dispatch(FuncRequest const & cmd) case LFUN_BUFFER_TOGGLE_EMBEDDING: { // turn embedding on/off try { - buffer_.embeddedFiles().enable(!buffer_.params().embedded, buffer_); + buffer_.embeddedFiles().enable(!buffer_.params().embedded, buffer_, true); } catch (ExceptionMessage const & message) { Alert::error(message.title_, message.details_); } @@ -1257,7 +1331,7 @@ bool BufferView::dispatch(FuncRequest const & cmd) } } // if it did not work, try the underlying inset. - if (!cur.result().dispatched()) + if (!inset || !cur.result().dispatched()) cur.dispatch(tmpcmd); if (!cur.result().dispatched()) @@ -1268,6 +1342,26 @@ bool BufferView::dispatch(FuncRequest const & cmd) break; } + case LFUN_NEXT_INSET_MODIFY: { + // this is the real function we want to invoke + FuncRequest tmpcmd = FuncRequest(LFUN_INSET_MODIFY, cmd.argument()); + // if there is an inset at cursor, see whether it + // can be modified. + Inset * inset = cur.nextInset(); + if (inset) + inset->dispatch(cur, tmpcmd); + // if it did not work, try the underlying inset. + if (!inset || !cur.result().dispatched()) + cur.dispatch(tmpcmd); + + if (!cur.result().dispatched()) + // It did not work too; no action needed. + break; + cur.clearSelection(); + processUpdateFlags(Update::Force | Update::FitCursor); + break; + } + case LFUN_SCREEN_UP: case LFUN_SCREEN_DOWN: { Point p = getPos(cur, cur.boundary()); @@ -1288,26 +1382,37 @@ bool BufferView::dispatch(FuncRequest const & cmd) lfunScroll(cmd); break; - case LFUN_SCREEN_UP_SELECT: - case LFUN_SCREEN_DOWN_SELECT: { - // Those two are not ready yet for consumption. - return false; - + case LFUN_SCREEN_UP_SELECT: { cur.selHandle(true); - size_t initial_depth = cur.depth(); - Point const p = getPos(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()); - d->text_metrics_[&buffer_.text()].editXY(cur, p.x_, p.y_); + if (isTopScreen()) { + lyx::dispatch(FuncRequest(LFUN_BUFFER_BEGIN_SELECT)); + cur.finishUndo(); + break; + } + int y = getPos(cur, cur.boundary()).y_; + int const ymin = y - height_ + defaultRowHeight(); + while (y > ymin && cur.up()) + y = getPos(cur, cur.boundary()).y_; + cur.finishUndo(); - while (cur.depth() > initial_depth) { - cur.forwardInset(); + processUpdateFlags(Update::SinglePar | Update::FitCursor); + break; + } + + case LFUN_SCREEN_DOWN_SELECT: { + cur.selHandle(true); + if (isBottomScreen()) { + lyx::dispatch(FuncRequest(LFUN_BUFFER_END_SELECT)); + cur.finishUndo(); + break; } - // FIXME: we need to do a redraw again because of the selection - // But no screen update is needed. - d->update_strategy_ = NoScreenUpdate; - buffer_.changed(); + int y = getPos(cur, cur.boundary()).y_; + int const ymax = y + height_ - defaultRowHeight(); + while (y < ymax && cur.down()) + y = getPos(cur, cur.boundary()).y_; + + cur.finishUndo(); + processUpdateFlags(Update::SinglePar | Update::FitCursor); break; } @@ -1357,23 +1462,14 @@ void BufferView::clearSelection() void BufferView::resize(int width, int height) { - bool initialResize = (height_ == 0); - // Update from work area width_ = width; height_ = height; // Clear the paragraph height cache. d->par_height_.clear(); - + // Redo the metrics. updateMetrics(); - - // view got his initial size, make sure that - // the cursor has a proper position - if (initialResize) { - updateScrollbar(); - showCursor(); - } } @@ -1415,6 +1511,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(); @@ -1473,16 +1570,22 @@ void BufferView::mouseEventDispatch(FuncRequest const & cmd0) // Now dispatch to the temporary cursor. If the real cursor should // be modified, the inset's dispatch has to do so explicitly. - if (!cur.result().dispatched()) + if (!inset || !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()); } @@ -1582,15 +1685,15 @@ void BufferView::setCursorFromRow(int row) 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()) { - setCursor(it); - showCursor(); - return; - } + Toc & toc = buffer().tocBackend().toc("label"); + TocIterator toc_it = toc.begin(); + TocIterator end = toc.end(); + for (; toc_it != end; ++toc_it) { + if (label == toc_it->str()) + dispatch(toc_it->action()); } + //FIXME: We could do a bit more searching thanks to this: + //InsetLabel const * inset = buffer_.insetLabel(label); } @@ -1673,8 +1776,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(); @@ -1687,24 +1794,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 << endl; - DocIterator dit = doc_iterator_begin(cur.inset()); - dit.bottom() = cur.bottom(); - size_t i = 1; - while (i < cur.depth() && dit.nextInset() == &cur[i].inset()) { - dit.push_back(cur[i]); - ++i; - } - //lyxerr << "5 cur after" << dit <cursor_.setCursor(dit); + d->cursor_.setCursor(cur); d->cursor_.boundary(cur.boundary()); if (do_selection) d->cursor_.setSelection(); @@ -1762,6 +1852,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 @@ -1798,6 +1892,14 @@ 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_]; @@ -1883,7 +1985,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"); @@ -1991,7 +2093,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 @@ -2161,4 +2263,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