X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2FBufferView.cpp;h=de2d71a1234aff48dbd5efd5ab33c5af8bd70616;hb=4b7f1b3c3918cd32070c72b6d8e95a888981c7a2;hp=02d8fcdc98339f68903a4c0c9fe1451506f3b759;hpb=754ead5010b6caeb401712e4f4e9c7cecf1b5ecf;p=lyx.git diff --git a/src/BufferView.cpp b/src/BufferView.cpp index 02d8fcdc98..de2d71a123 100644 --- a/src/BufferView.cpp +++ b/src/BufferView.cpp @@ -4,10 +4,10 @@ * Licence details can be found in the file COPYING. * * \author Alfredo Braunstein - * \author Lars Gullik Bjønnes + * \author Lars Gullik Bjønnes * \author John Levon - * \author André Pönitz - * \author Jürgen Vigna + * \author André Pönitz + * \author Jürgen Vigna * * Full author contact details are available in file CREDITS. */ @@ -34,14 +34,16 @@ #include "InsetIterator.h" #include "Language.h" #include "LaTeXFeatures.h" +#include "LayoutFile.h" +#include "Lexer.h" #include "LyX.h" +#include "LyXAction.h" #include "lyxfind.h" #include "LyXFunc.h" #include "Layout.h" #include "LyXRC.h" #include "MetricsInfo.h" #include "Paragraph.h" -#include "paragraph_funcs.h" #include "ParagraphParameters.h" #include "ParIterator.h" #include "Session.h" @@ -83,6 +85,7 @@ #include #include #include +#include #include using namespace std; @@ -150,11 +153,13 @@ bool findInset(DocIterator & dit, vector const & codes, if (!findNextInset(tmpdit, codes, contents)) { if (dit.depth() != 1 || dit.pit() != 0 || dit.pos() != 0) { - tmpdit = doc_iterator_begin(tmpdit.bottom().inset()); + Inset * inset = &tmpdit.bottom().inset(); + tmpdit = doc_iterator_begin(&inset->buffer(), inset); if (!findNextInset(tmpdit, codes, contents)) return false; - } else + } else { return false; + } } dit = tmpdit; @@ -216,7 +221,7 @@ struct BufferView::Private Private(BufferView & bv): wh_(0), cursor_(bv), anchor_pit_(0), anchor_ypos_(0), inlineCompletionUniqueChars_(0), - last_inset_(0), gui_(0) + last_inset_(0), bookmark_edit_position_(0), gui_(0) {} /// @@ -259,6 +264,9 @@ struct BufferView::Private */ Inset * last_inset_; + // cache for id of the paragraph which was edited the last time + int bookmark_edit_position_; + mutable TextMetricsCache text_metrics_; /// Whom to notify. @@ -268,15 +276,20 @@ struct BufferView::Private /// Cache for Find Next FuncRequest search_request_cache_; + + /// + map edited_insets_; }; BufferView::BufferView(Buffer & buf) - : width_(0), height_(0), full_screen_(false), 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); + d->cursor_.setBuffer(&buf); d->cursor_.push(buffer_.inset()); d->cursor_.resetAnchor(); d->cursor_.setCurrentFont(); @@ -411,7 +424,7 @@ void BufferView::processUpdateFlags(Update::flags flags) if (flags == Update::Decoration) { d->update_strategy_ = DecorationUpdate; - buffer_.changed(); + buffer_.changed(false); return; } @@ -424,7 +437,7 @@ void BufferView::processUpdateFlags(Update::flags flags) } if (flags & Update::Decoration) { d->update_strategy_ = DecorationUpdate; - buffer_.changed(); + buffer_.changed(false); return; } // no screen update is needed. @@ -440,13 +453,13 @@ void BufferView::processUpdateFlags(Update::flags flags) if (!(flags & Update::FitCursor)) { // Nothing to do anymore. Trigger a redraw and return - buffer_.changed(); + buffer_.changed(false); return; } // updateMetrics() does not update paragraph position // This is done at draw() time. So we need a redraw! - buffer_.changed(); + buffer_.changed(false); if (fitCursor()) { // The cursor is off screen so ensure it is visible. @@ -510,7 +523,10 @@ void BufferView::updateScrollbar() d->scrollbarParameters_.position = 0; // The reference is the top position so we remove one page. - d->scrollbarParameters_.max -= d->scrollbarParameters_.page_step; + if (lyxrc.scroll_below_document) + d->scrollbarParameters_.max -= minVisiblePart(); + else + d->scrollbarParameters_.max -= d->scrollbarParameters_.page_step; } @@ -549,17 +565,21 @@ docstring BufferView::contextMenu(int x, int y) const void BufferView::scrollDocView(int value) { int const offset = value - d->scrollbarParameters_.position; + + // No scrolling at all? No need to redraw anything + if (offset == 0) + return; + // If the offset is less than 2 screen height, prefer to scroll instead. if (abs(offset) <= 2 * height_) { - scroll(offset); - updateMetrics(); - buffer_.changed(); + d->anchor_ypos_ -= offset; + buffer_.changed(true); return; } // cut off at the top if (value <= d->scrollbarParameters_.min) { - DocIterator dit = doc_iterator_begin(buffer_.inset()); + DocIterator dit = doc_iterator_begin(&buffer_); showCursor(dit); LYXERR(Debug::SCROLLING, "scroll to top"); return; @@ -567,7 +587,7 @@ void BufferView::scrollDocView(int value) // cut off at the bottom if (value >= d->scrollbarParameters_.max) { - DocIterator dit = doc_iterator_end(buffer_.inset()); + DocIterator dit = doc_iterator_end(&buffer_); dit.backwardPos(); showCursor(dit); LYXERR(Debug::SCROLLING, "scroll to bottom"); @@ -591,7 +611,7 @@ void BufferView::scrollDocView(int value) return; } - DocIterator dit = doc_iterator_begin(buffer_.inset()); + DocIterator dit = doc_iterator_begin(&buffer_); dit.pit() = i; LYXERR(Debug::SCROLLING, "value = " << value << " -> scroll to pit " << i); showCursor(dit); @@ -625,9 +645,16 @@ void BufferView::setCursorFromScrollbar() // We reset the cursor because cursorStatus() does not // work when the cursor is within mathed. Cursor cur(*this); - cur.reset(buffer_.inset()); + cur.reset(); tm.setCursorFromCoordinates(cur, 0, newy); + + // update the bufferview cursor and notify insets + // FIXME: Care about the d->cursor_ flags to redraw if needed + Cursor old = d->cursor_; mouseSetCursor(cur); + bool badcursor = notifyCursorLeavesOrEnters(old, d->cursor_); + if (badcursor) + d->cursor_.fixIfBroken(); } @@ -654,9 +681,19 @@ CursorStatus BufferView::cursorStatus(DocIterator const & dit) const } +void BufferView::bookmarkEditPosition() +{ + // Don't eat cpu time for each keystroke + if (d->cursor_.paragraph().id() == d->bookmark_edit_position_) + return; + saveBookmark(0); + d->bookmark_edit_position_ = d->cursor_.paragraph().id(); +} + + void BufferView::saveBookmark(unsigned int idx) { - // tenatively save bookmark, id and pos will be used to + // tentatively save bookmark, id and pos will be used to // acturately locate a bookmark in a 'live' lyx session. // pit and pos will be updated with bottom level pit/pos // when lyx exits. @@ -697,7 +734,7 @@ bool BufferView::moveToPosition(pit_type bottom_pit, pos_type bottom_pos, // insets. size_t const n = dit.depth(); for (size_t i = 0; i < n; ++i) - if (dit[i].inset().editable() != Inset::HIGHLY_EDITABLE) { + if (!dit[i].inset().editable()) { dit.resize(i); break; } @@ -712,7 +749,7 @@ bool BufferView::moveToPosition(pit_type bottom_pit, pos_type bottom_pos, // it will be restored to the left of the outmost inset that contains // the bookmark. if (bottom_pit < int(buffer_.paragraphs().size())) { - dit = doc_iterator_begin(buffer_.inset()); + dit = doc_iterator_begin(&buffer_); dit.pit() = bottom_pit; dit.pos() = min(bottom_pos, dit.paragraph().size()); @@ -727,7 +764,7 @@ bool BufferView::moveToPosition(pit_type bottom_pit, pos_type bottom_pos, // To center the screen on this new position we need the // paragraph position which is computed at draw() time. // So we need a redraw! - buffer_.changed(); + buffer_.changed(false); if (fitCursor()) showCursor(); } @@ -758,18 +795,37 @@ int BufferView::workWidth() const } +void BufferView::recenter() +{ + showCursor(d->cursor_, true); +} + + void BufferView::showCursor() { - showCursor(d->cursor_); + showCursor(d->cursor_, false); +} + + +void BufferView::showCursor(DocIterator const & dit, bool recenter) +{ + if (scrollToCursor(dit, recenter)) + buffer_.changed(false); +} + + +void BufferView::scrollToCursor() +{ + scrollToCursor(d->cursor_, false); } -void BufferView::showCursor(DocIterator const & dit) +bool BufferView::scrollToCursor(DocIterator const & dit, bool recenter) { // We are not properly started yet, delay until resizing is // done. if (height_ == 0) - return; + return false; LYXERR(Debug::SCROLLING, "recentering!"); @@ -800,16 +856,19 @@ void BufferView::showCursor(DocIterator const & dit) Dimension const & row_dim = pm.getRow(cs.pos(), dit.boundary()).dimension(); int scrolled = 0; - if (ypos - row_dim.ascent() < 0) + if (recenter) + scrolled = scroll(ypos - height_/2); + else if (ypos - row_dim.ascent() < 0) scrolled = scrollUp(- ypos + row_dim.ascent()); else if (ypos + row_dim.descent() > height_) - scrolled = scrollDown(ypos - height_ + row_dim.descent()); + scrolled = scrollDown(ypos - height_ + defaultRowHeight() ); + // else, nothing to do, the cursor is already visible so we just return. if (scrolled != 0) { updateMetrics(); - buffer_.changed(); + return true; } - return; + return false; } // fix inline completion position @@ -825,26 +884,103 @@ void BufferView::showCursor(DocIterator const & dit) Dimension const & row_dim = pm.getRow(cs.pos(), dit.boundary()).dimension(); - if (d->anchor_pit_ == 0) + if (recenter) + d->anchor_ypos_ = height_/2; + else 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 if (offset > height_) + d->anchor_ypos_ = height_ - offset - defaultRowHeight(); else - d->anchor_ypos_ = defaultRowHeight() * 2 - offset - row_dim.descent(); + d->anchor_ypos_ = defaultRowHeight() * 2; updateMetrics(); - buffer_.changed(); + return true; } -FuncStatus BufferView::getStatus(FuncRequest const & cmd) +void BufferView::updateLayout(DocumentClass const * const oldlayout) +{ + message(_("Converting document to new document class...")); + + StableDocIterator backcur(d->cursor_); + ErrorList & el = buffer_.errorList("Class Switch"); + cap::switchBetweenClasses( + oldlayout, buffer_.params().documentClassPtr(), + static_cast(buffer_.inset()), el); + + setCursor(backcur.asDocIterator(&buffer_)); + + buffer_.errors("Class Switch"); + buffer_.updateLabels(); +} + +/** Return the change status at cursor position, taking in account the + * status at each level of the document iterator (a table in a deleted + * footnote is deleted). + * When \param outer is true, the top slice is not looked at. + */ +static Change::Type lookupChangeType(DocIterator const & dit, bool outer = false) { - FuncStatus flag; + size_t const depth = dit.depth() - (outer ? 1 : 0); + + for (size_t i = 0 ; i < depth ; ++i) { + CursorSlice const & slice = dit[i]; + if (!slice.inset().inMathed() + && slice.pos() < slice.paragraph().size()) { + Change::Type const ch = slice.paragraph().lookupChange(slice.pos()).type; + if (ch != Change::UNCHANGED) + return ch; + } + } + return Change::UNCHANGED; +} + + +bool BufferView::getStatus(FuncRequest const & cmd, FuncStatus & flag) +{ + // Can we use a readonly buffer? + if (buffer_.isReadonly() + && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly) + && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) { + flag.message(from_utf8(N_("Document is read-only"))); + flag.setEnabled(false); + return true; + } + // Are we in a DELETED change-tracking region? + if (lookupChangeType(d->cursor_, true) == Change::DELETED + && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly) + && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) { + flag.message(from_utf8(N_("This portion of the document is deleted."))); + flag.setEnabled(false); + return true; + } Cursor & cur = d->cursor_; + if (cur.getStatus(cmd, flag)) + return true; + switch (cmd.action) { + // FIXME: This is a bit problematic because we don't check is this is a + // document BufferView or not for these LFUNs. We probably have to + // dispatch both to currentBufferView() and, if that fails, + // to documentBufferView(); same as we do know for current Buffer and + // document Buffer. Ideally those LFUN should go to Buffer as they* + // operate on the full Buffer and the cursor is only needed either for + // an Undo record or to restore a cursor position. But we don't know + // how to do that inside Buffer of course. + case LFUN_BUFFER_PARAMS_APPLY: + case LFUN_LAYOUT_MODULES_CLEAR: + case LFUN_LAYOUT_MODULE_ADD: + case LFUN_LAYOUT_RELOAD: + case LFUN_TEXTCLASS_APPLY: + case LFUN_TEXTCLASS_LOAD: + flag.setEnabled(!buffer_.isReadonly()); + break; + case LFUN_UNDO: flag.setEnabled(buffer_.undo().hasUndoStack()); break; @@ -858,6 +994,7 @@ FuncStatus BufferView::getStatus(FuncRequest const & cmd) // FIXME: Actually, these LFUNS should be moved to Text flag.setEnabled(cur.inTexted()); break; + case LFUN_FONT_STATE: case LFUN_LABEL_INSERT: case LFUN_INFO_INSERT: @@ -866,30 +1003,38 @@ FuncStatus BufferView::getStatus(FuncRequest const & cmd) case LFUN_NOTE_NEXT: case LFUN_REFERENCE_NEXT: case LFUN_WORD_FIND: + case LFUN_WORD_FIND_FORWARD: + case LFUN_WORD_FIND_BACKWARD: + case LFUN_WORD_FINDADV: case LFUN_WORD_REPLACE: case LFUN_MARK_OFF: case LFUN_MARK_ON: case LFUN_MARK_TOGGLE: case LFUN_SCREEN_RECENTER: + case LFUN_SCREEN_SHOW_CURSOR: case LFUN_BIBTEX_DATABASE_ADD: case LFUN_BIBTEX_DATABASE_DEL: - case LFUN_NOTES_MUTATE: case LFUN_ALL_INSETS_TOGGLE: case LFUN_STATISTICS: + case LFUN_BRANCH_ADD_INSERT: + case LFUN_KEYMAP_OFF: + case LFUN_KEYMAP_PRIMARY: + case LFUN_KEYMAP_SECONDARY: + case LFUN_KEYMAP_TOGGLE: flag.setEnabled(true); break; - case LFUN_NEXT_INSET_TOGGLE: - case LFUN_NEXT_INSET_MODIFY: { - // this is the real function we want to invoke - FuncRequest tmpcmd = cmd; - tmpcmd.action = (cmd.action == LFUN_NEXT_INSET_TOGGLE) - ? LFUN_INSET_TOGGLE : LFUN_INSET_MODIFY; + case LFUN_REGEXP_MODE: + flag.setEnabled(buffer().isInternal() && !cur.inRegexped() + && cur.inset().lyxCode() != ERT_CODE); + break; + + case LFUN_LABEL_COPY_AS_REF: { // if there is an inset at cursor, see whether it - // handles the lfun, other start from scratch + // handles the lfun Inset * inset = cur.nextInset(); - if (!inset || !inset->getStatus(cur, tmpcmd, flag)) - flag = lyx::getStatus(tmpcmd); + if (!inset || !inset->getStatus(cur, cmd, flag)) + flag.setEnabled(false); break; } @@ -911,6 +1056,7 @@ FuncStatus BufferView::getStatus(FuncRequest const & cmd) case LFUN_CHANGES_MERGE: case LFUN_CHANGE_NEXT: + case LFUN_CHANGE_PREVIOUS: case LFUN_ALL_CHANGES_ACCEPT: case LFUN_ALL_CHANGES_REJECT: // TODO: context-sensitive enabling of LFUNs @@ -930,6 +1076,7 @@ FuncStatus BufferView::getStatus(FuncRequest const & cmd) case LFUN_SCROLL: case LFUN_SCREEN_UP_SELECT: case LFUN_SCREEN_DOWN_SELECT: + case LFUN_INSET_FORALL: flag.setEnabled(true); break; @@ -945,64 +1092,63 @@ FuncStatus BufferView::getStatus(FuncRequest const & cmd) flag.setEnabled(cur.inset().allowParagraphCustomization(cur.idx())); break; - case LFUN_INSET_SETTINGS: { - InsetCode code = cur.inset().lyxCode(); - if (cmd.getArg(0) == insetName(code)) { - flag.setEnabled(true); - break; - } - bool enable = false; - InsetCode next_code = cur.nextInset() - ? cur.nextInset()->lyxCode() : NO_CODE; - //FIXME: remove these special cases: - switch (next_code) { - case TABULAR_CODE: - case ERT_CODE: - case FLOAT_CODE: - case WRAP_CODE: - case NOTE_CODE: - case BRANCH_CODE: - case BOX_CODE: - case LISTINGS_CODE: - enable = (cmd.argument().empty() || - cmd.getArg(0) == insetName(next_code)); - break; - default: - break; - } - flag.setEnabled(enable); - break; - } - case LFUN_DIALOG_SHOW_NEW_INSET: - flag.setEnabled(cur.inset().lyxCode() != ERT_CODE && - cur.inset().lyxCode() != LISTINGS_CODE); - if (cur.inset().lyxCode() == CAPTION_CODE) { - FuncStatus flag; - if (cur.inset().getStatus(cur, cmd, flag)) - return flag; - } + // FIXME: this is wrong, but I do not understand the + // intent (JMarc) + if (cur.inset().lyxCode() == CAPTION_CODE) + return cur.inset().getStatus(cur, cmd, flag); + // FIXME we should consider passthru paragraphs too. + flag.setEnabled(!cur.inset().getLayout().isPassThru()); break; - case LFUN_BRANCH_ACTIVATE: - case LFUN_BRANCH_DEACTIVATE: { - bool enable = false; - docstring const branchName = cmd.argument(); - if (!branchName.empty()) - enable = buffer_.params().branchlist().find(branchName); - flag.setEnabled(enable); + case LFUN_CITATION_INSERT: { + FuncRequest fr(LFUN_INSET_INSERT, "citation"); + // FIXME: This could turn in a recursive hell. + // Shouldn't we use Buffer::getStatus() instead? + flag.setEnabled(lyx::getStatus(fr).enabled()); + break; + } + case LFUN_INSET_APPLY: { + string const name = cmd.getArg(0); + Inset * inset = editedInset(name); + if (inset) { + FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument()); + FuncStatus fs; + if (!inset->getStatus(cur, fr, fs)) { + // Every inset is supposed to handle this + LASSERT(false, break); + } + flag |= fs; + } else { + FuncRequest fr(LFUN_INSET_INSERT, cmd.argument()); + flag |= lyx::getStatus(fr); + } break; } default: flag.setEnabled(false); + return false; } - return flag; + return true; +} + + +Inset * BufferView::editedInset(string const & name) const +{ + map::const_iterator it = d->edited_insets_.find(name); + return it == d->edited_insets_.end() ? 0 : it->second; +} + + +void BufferView::editInset(string const & name, Inset * inset) +{ + d->edited_insets_[name] = inset; } -bool BufferView::dispatch(FuncRequest const & cmd) +void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr) { //lyxerr << [ cmd = " << cmd << "]" << endl; @@ -1013,30 +1159,128 @@ bool BufferView::dispatch(FuncRequest const & cmd) << " y[" << cmd.y << ']' << " button[" << cmd.button() << ']'); + string const argument = to_utf8(cmd.argument()); Cursor & cur = d->cursor_; + // Don't dispatch function that does not apply to internal buffers. + if (buffer_.isInternal() + && lyxaction.funcHasFlag(cmd.action, LyXAction::NoInternal)) + return; + + // We'll set this back to false if need be. + bool dispatched = true; + buffer_.undo().beginUndoGroup(); + switch (cmd.action) { + case LFUN_BUFFER_PARAMS_APPLY: { + DocumentClass const * const oldClass = buffer_.params().documentClassPtr(); + cur.recordUndoFullDocument(); + istringstream ss(to_utf8(cmd.argument())); + Lexer lex; + lex.setStream(ss); + int const unknown_tokens = buffer_.readHeader(lex); + if (unknown_tokens != 0) { + LYXERR0("Warning in LFUN_BUFFER_PARAMS_APPLY!\n" + << unknown_tokens << " unknown token" + << (unknown_tokens == 1 ? "" : "s")); + } + updateLayout(oldClass); + + // We are most certainly here because of a change in the document + // It is then better to make sure that all dialogs are in sync with + // current document settings. + dr.update(Update::Force | Update::FitCursor); + break; + } + + case LFUN_LAYOUT_MODULES_CLEAR: { + DocumentClass const * const oldClass = + buffer_.params().documentClassPtr(); + cur.recordUndoFullDocument(); + buffer_.params().clearLayoutModules(); + buffer_.params().makeDocumentClass(); + updateLayout(oldClass); + dr.update(Update::Force | Update::FitCursor); + break; + } + + case LFUN_LAYOUT_MODULE_ADD: { + BufferParams const & params = buffer_.params(); + if (!params.moduleCanBeAdded(argument)) { + LYXERR0("Module `" << argument << + "' cannot be added due to failed requirements or " + "conflicts with installed modules."); + break; + } + DocumentClass const * const oldClass = params.documentClassPtr(); + cur.recordUndoFullDocument(); + buffer_.params().addLayoutModule(argument); + buffer_.params().makeDocumentClass(); + updateLayout(oldClass); + dr.update(Update::Force | Update::FitCursor); + break; + } + + case LFUN_TEXTCLASS_APPLY: { + if (!LayoutFileList::get().load(argument, buffer_.temppath()) && + !LayoutFileList::get().load(argument, buffer_.filePath())) + break; + + LayoutFile const * old_layout = buffer_.params().baseClass(); + LayoutFile const * new_layout = &(LayoutFileList::get()[argument]); + + if (old_layout == new_layout) + // nothing to do + break; + + //Save the old, possibly modular, layout for use in conversion. + DocumentClass const * const oldDocClass = + buffer_.params().documentClassPtr(); + cur.recordUndoFullDocument(); + buffer_.params().setBaseClass(argument); + buffer_.params().makeDocumentClass(); + updateLayout(oldDocClass); + dr.update(Update::Force | Update::FitCursor); + break; + } + + case LFUN_TEXTCLASS_LOAD: + LayoutFileList::get().load(argument, buffer_.temppath()) || + LayoutFileList::get().load(argument, buffer_.filePath()); + break; + + case LFUN_LAYOUT_RELOAD: { + DocumentClass const * const oldClass = buffer_.params().documentClassPtr(); + LayoutFileIndex bc = buffer_.params().baseClassID(); + LayoutFileList::get().reset(bc); + buffer_.params().setBaseClass(bc); + buffer_.params().makeDocumentClass(); + updateLayout(oldClass); + dr.update(Update::Force | Update::FitCursor); + break; + } + case LFUN_UNDO: - cur.message(_("Undo")); + dr.setMessage(_("Undo")); cur.clearSelection(); if (!cur.textUndo()) - cur.message(_("No further undo information")); + dr.setMessage(_("No further undo information")); else - processUpdateFlags(Update::Force | Update::FitCursor); + dr.update(Update::Force | Update::FitCursor); break; case LFUN_REDO: - cur.message(_("Redo")); + dr.setMessage(_("Redo")); cur.clearSelection(); if (!cur.textRedo()) - cur.message(_("No further redo information")); + dr.setMessage(_("No further redo information")); else - processUpdateFlags(Update::Force | Update::FitCursor); + dr.update(Update::Force | Update::FitCursor); break; case LFUN_FONT_STATE: - cur.message(cur.currentState()); + dr.setMessage(cur.currentState()); break; case LFUN_BOOKMARK_SAVE: @@ -1047,16 +1291,14 @@ bool BufferView::dispatch(FuncRequest const & cmd) docstring label = cmd.argument(); if (label.empty()) { InsetRef * inset = - getInsetByCode(d->cursor_, - REF_CODE); + getInsetByCode(cur, REF_CODE); if (inset) { label = inset->getParam("reference"); // persistent=false: use temp_bookmark saveBookmark(0); } } - - if (!label.empty()) + if (!label.empty()) gotoLabel(label); break; } @@ -1101,7 +1343,7 @@ bool BufferView::dispatch(FuncRequest const & cmd) // Set the cursor dit.pos() = pos; setCursor(dit); - processUpdateFlags(Update::Force | Update::FitCursor); + dr.update(Update::Force | Update::FitCursor); } else { // Switch to other buffer view and resend cmd theLyXFunc().dispatch(FuncRequest( @@ -1133,19 +1375,19 @@ bool BufferView::dispatch(FuncRequest const & cmd) buffer_.params().outputChanges = !buffer_.params().outputChanges; if (buffer_.params().outputChanges) { bool dvipost = LaTeXFeatures::isAvailable("dvipost"); - bool xcolorsoul = LaTeXFeatures::isAvailable("soul") && + bool xcolorulem = LaTeXFeatures::isAvailable("ulem") && LaTeXFeatures::isAvailable("xcolor"); - if (!dvipost && !xcolorsoul) { + if (!dvipost && !xcolorulem) { Alert::warning(_("Changes not shown in LaTeX output"), _("Changes will not be highlighted in LaTeX output, " - "because neither dvipost nor xcolor/soul are installed.\n" + "because neither dvipost nor xcolor/ulem are installed.\n" "Please install these packages or redefine " "\\lyxadded and \\lyxdeleted in the LaTeX preamble.")); - } else if (!xcolorsoul) { + } else if (!xcolorulem) { Alert::warning(_("Changes not shown in LaTeX output"), _("Changes will not be highlighted in LaTeX output " - "when using pdflatex, because xcolor and soul are not installed.\n" + "when using pdflatex, because xcolor and ulem are not installed.\n" "Please install both packages or redefine " "\\lyxadded and \\lyxdeleted in the LaTeX preamble.")); } @@ -1155,39 +1397,67 @@ bool BufferView::dispatch(FuncRequest const & cmd) case LFUN_CHANGE_NEXT: findNextChange(this); // FIXME: Move this LFUN to Buffer so that we don't have to do this: - processUpdateFlags(Update::Force | Update::FitCursor); + dr.update(Update::Force | Update::FitCursor); + break; + + case LFUN_CHANGE_PREVIOUS: + findPreviousChange(this); + // FIXME: Move this LFUN to Buffer so that we don't have to do this: + dr.update(Update::Force | Update::FitCursor); break; case LFUN_CHANGES_MERGE: - if (findNextChange(this)) { - processUpdateFlags(Update::Force | Update::FitCursor); + if (findNextChange(this) || findPreviousChange(this)) { + dr.update(Update::Force | Update::FitCursor); showDialog("changes"); } break; case LFUN_ALL_CHANGES_ACCEPT: // select complete document - d->cursor_.reset(buffer_.inset()); - d->cursor_.selHandle(true); - buffer_.text().cursorBottom(d->cursor_); + cur.reset(); + cur.selHandle(true); + buffer_.text().cursorBottom(cur); // accept everything in a single step to support atomic undo - buffer_.text().acceptOrRejectChanges(d->cursor_, Text::ACCEPT); + buffer_.text().acceptOrRejectChanges(cur, Text::ACCEPT); // FIXME: Move this LFUN to Buffer so that we don't have to do this: - processUpdateFlags(Update::Force | Update::FitCursor); + dr.update(Update::Force | Update::FitCursor); break; case LFUN_ALL_CHANGES_REJECT: // select complete document - d->cursor_.reset(buffer_.inset()); - d->cursor_.selHandle(true); - buffer_.text().cursorBottom(d->cursor_); + cur.reset(); + cur.selHandle(true); + buffer_.text().cursorBottom(cur); // reject everything in a single step to support atomic undo // Note: reject does not work recursively; the user may have to repeat the operation - buffer_.text().acceptOrRejectChanges(d->cursor_, Text::REJECT); + buffer_.text().acceptOrRejectChanges(cur, Text::REJECT); // FIXME: Move this LFUN to Buffer so that we don't have to do this: - processUpdateFlags(Update::Force | Update::FitCursor); + dr.update(Update::Force | Update::FitCursor); break; + case LFUN_WORD_FIND_FORWARD: + case LFUN_WORD_FIND_BACKWARD: { + static docstring last_search; + docstring searched_string; + + if (!cmd.argument().empty()) { + last_search = cmd.argument(); + searched_string = cmd.argument(); + } else { + searched_string = last_search; + } + + if (searched_string.empty()) + break; + + bool const fw = cmd.action == LFUN_WORD_FIND_FORWARD; + docstring const data = + find2string(searched_string, true, false, fw); + find(this, FuncRequest(LFUN_WORD_FIND, data)); + break; + } + case LFUN_WORD_FIND: { FuncRequest req = cmd; if (cmd.argument().empty() && !d->search_request_cache_.argument().empty()) @@ -1211,7 +1481,8 @@ bool BufferView::dispatch(FuncRequest const & cmd) DocIterator end = cur.selectionEnd(); if (beg.pit() == end.pit()) { for (pos_type p = beg.pos() ; p < end.pos() ; ++p) { - if (cur.paragraph().isDeleted(p)) + if (!cur.inMathed() + && cur.paragraph().isDeleted(p)) has_deleted = true; } } @@ -1220,37 +1491,50 @@ bool BufferView::dispatch(FuncRequest const & cmd) break; } + case LFUN_WORD_FINDADV: { + FindAndReplaceOptions opt; + istringstream iss(to_utf8(cmd.argument())); + iss >> opt; + if (findAdv(this, opt)) + cur.dispatched(); + else + cur.undispatched(); + break; + } + case LFUN_MARK_OFF: cur.clearSelection(); - cur.resetAnchor(); - cur.message(from_utf8(N_("Mark off"))); + dr.setMessage(from_utf8(N_("Mark off"))); break; case LFUN_MARK_ON: cur.clearSelection(); cur.setMark(true); - cur.resetAnchor(); - cur.message(from_utf8(N_("Mark on"))); + dr.setMessage(from_utf8(N_("Mark on"))); break; case LFUN_MARK_TOGGLE: - cur.clearSelection(); + cur.setSelection(false); if (cur.mark()) { cur.setMark(false); - cur.message(from_utf8(N_("Mark removed"))); + dr.setMessage(from_utf8(N_("Mark removed"))); } else { cur.setMark(true); - cur.message(from_utf8(N_("Mark set"))); + dr.setMessage(from_utf8(N_("Mark set"))); } cur.resetAnchor(); break; - case LFUN_SCREEN_RECENTER: + case LFUN_SCREEN_SHOW_CURSOR: showCursor(); break; + + case LFUN_SCREEN_RECENTER: + recenter(); + break; case LFUN_BIBTEX_DATABASE_ADD: { - Cursor tmpcur = d->cursor_; + Cursor tmpcur = cur; findInset(tmpcur, BIBTEX_CODE, false); InsetBibtex * inset = getInsetByCode(tmpcur, BIBTEX_CODE); @@ -1262,7 +1546,7 @@ bool BufferView::dispatch(FuncRequest const & cmd) } case LFUN_BIBTEX_DATABASE_DEL: { - Cursor tmpcur = d->cursor_; + Cursor tmpcur = cur; findInset(tmpcur, BIBTEX_CODE, false); InsetBibtex * inset = getInsetByCode(tmpcur, BIBTEX_CODE); @@ -1279,8 +1563,8 @@ bool BufferView::dispatch(FuncRequest const & cmd) from = cur.selectionBegin(); to = cur.selectionEnd(); } else { - from = doc_iterator_begin(buffer_.inset()); - to = doc_iterator_end(buffer_.inset()); + from = doc_iterator_begin(&buffer_); + to = doc_iterator_end(&buffer_); } int const words = countWords(from, to); int const chars = countChars(from, to, false); @@ -1316,77 +1600,51 @@ bool BufferView::dispatch(FuncRequest const & cmd) // turn compression on/off buffer_.params().compressed = !buffer_.params().compressed; break; - - case LFUN_NEXT_INSET_TOGGLE: { - // create the the real function we want to invoke - FuncRequest tmpcmd = cmd; - tmpcmd.action = LFUN_INSET_TOGGLE; - // if there is an inset at cursor, see whether it - // wants to toggle. - Inset * inset = cur.nextInset(); - if (inset) { - if (inset->isActive()) { - Cursor tmpcur = cur; - tmpcur.pushBackward(*inset); - inset->dispatch(tmpcur, tmpcmd); - if (tmpcur.result().dispatched()) - cur.dispatched(); - } else - 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::SinglePar | Update::FitCursor); - break; - } - case LFUN_NEXT_INSET_MODIFY: { - // create the the real function we want to invoke - FuncRequest tmpcmd = cmd; - tmpcmd.action = LFUN_INSET_MODIFY; - // if there is an inset at cursor, see whether it - // can be modified. - Inset * inset = cur.nextInset(); - if (inset) + case LFUN_LABEL_COPY_AS_REF: { + // if there is an inset at cursor, try to copy it + Inset * inset = &cur.inset(); + if (!inset || !inset->asInsetMath()) + inset = cur.nextInset(); + if (inset) { + FuncRequest tmpcmd = cmd; 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); + dr.update(Update::SinglePar | Update::FitCursor); break; } case LFUN_SCREEN_UP: case LFUN_SCREEN_DOWN: { Point p = getPos(cur, cur.boundary()); - if (p.y_ < 0 || p.y_ > height_) { + // 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_) { // The cursor is off-screen so recenter before proceeding. showCursor(); p = getPos(cur, cur.boundary()); - } + }*/ int const scrolled = scroll(cmd.action == LFUN_SCREEN_UP - ? - height_ : height_); - if (cmd.action == LFUN_SCREEN_UP && scrolled > - height_) + ? -height_ : height_); + if (cmd.action == LFUN_SCREEN_UP && scrolled > -height_) p = Point(0, 0); if (cmd.action == LFUN_SCREEN_DOWN && scrolled < height_) p = Point(width_, height_); - cur.reset(buffer_.inset()); - updateMetrics(); - buffer_.changed(); - d->text_metrics_[&buffer_.text()].editXY(cur, p.x_, p.y_); + Cursor old = cur; + bool const in_texted = cur.inTexted(); + cur.reset(); + buffer_.changed(true); + d->text_metrics_[&buffer_.text()].editXY(cur, p.x_, p.y_, + true, cmd.action == LFUN_SCREEN_UP); //FIXME: what to do with cur.x_target()? + bool update = in_texted && cur.bv().checkDepm(cur, old); cur.finishUndo(); + if (update) + dr.update(Update::Force | Update::FitCursor); break; } @@ -1407,7 +1665,7 @@ bool BufferView::dispatch(FuncRequest const & cmd) y = getPos(cur, cur.boundary()).y_; cur.finishUndo(); - processUpdateFlags(Update::SinglePar | Update::FitCursor); + dr.update(Update::SinglePar | Update::FitCursor); break; } @@ -1424,28 +1682,60 @@ bool BufferView::dispatch(FuncRequest const & cmd) y = getPos(cur, cur.boundary()).y_; cur.finishUndo(); - processUpdateFlags(Update::SinglePar | Update::FitCursor); + dr.update(Update::SinglePar | Update::FitCursor); break; } - case LFUN_BRANCH_ACTIVATE: - case LFUN_BRANCH_DEACTIVATE: - buffer_.dispatch(cmd); - processUpdateFlags(Update::Force); - break; - - // This could be rewriten using some command like forall - // once the insets refactoring is done. - case LFUN_NOTES_MUTATE: { - if (cmd.argument().empty()) - break; - if (mutateNotes(cur, cmd.getArg(0), cmd.getArg(1))) { - processUpdateFlags(Update::Force); + // This would be in Buffer class if only Cursor did not + // require a bufferview + case LFUN_INSET_FORALL: { + docstring const name = from_utf8(cmd.getArg(0)); + string const commandstr = cmd.getLongArg(1); + FuncRequest const fr = lyxaction.lookupFunc(commandstr); + + // an arbitrary number to limit number of iterations + const int max_iter = 10000; + int iterations = 0; + Cursor & cur = d->cursor_; + Cursor const savecur = cur; + cur.reset(); + if (!cur.nextInset()) + cur.forwardInset(); + cur.beginUndoGroup(); + while(cur && iterations < max_iter) { + Inset * ins = cur.nextInset(); + if (!ins) + break; + docstring insname = ins->name(); + while (!insname.empty()) { + if (insname == name || name == from_utf8("*")) { + cur.recordUndo(); + lyx::dispatch(fr); + ++iterations; + break; + } + size_t const i = insname.rfind(':'); + if (i == string::npos) + break; + insname = insname.substr(0, i); + } + cur.forwardInset(); } + cur.endUndoGroup(); + cur = savecur; + cur.fixIfBroken(); + dr.update(Update::Force); + + if (iterations >= max_iter) { + dr.setError(true); + dr.setMessage(bformat(_("`inset-forall' interrupted because number of actions is larger than %1$d"), max_iter)); + } else + dr.setMessage(bformat(_("Applied \"%1$s\" to %2$d insets"), from_utf8(commandstr), iterations)); break; } + case LFUN_ALL_INSETS_TOGGLE: { string action; string const name = split(to_utf8(cmd.argument()), action, ' '); @@ -1453,7 +1743,7 @@ bool BufferView::dispatch(FuncRequest const & cmd) FuncRequest fr(LFUN_INSET_TOGGLE, action); - Inset & inset = cur.buffer().inset(); + Inset & inset = cur.buffer()->inset(); InsetIterator it = inset_iterator_begin(inset); InsetIterator const end = inset_iterator_end(inset); for (; it != end; ++it) { @@ -1465,15 +1755,106 @@ bool BufferView::dispatch(FuncRequest const & cmd) it->dispatch(tmpcur, fr); } } - processUpdateFlags(Update::Force | Update::FitCursor); + dr.update(Update::Force | Update::FitCursor); + break; + } + + case LFUN_BRANCH_ADD_INSERT: { + docstring branch_name = from_utf8(cmd.getArg(0)); + if (branch_name.empty()) + if (!Alert::askForText(branch_name, _("Branch name")) || + branch_name.empty()) + break; + + DispatchResult drtmp; + buffer_.dispatch(FuncRequest(LFUN_BRANCH_ADD, branch_name), drtmp); + if (drtmp.error()) { + Alert::warning(_("Branch already exists"), drtmp.message()); + break; + } + lyx::dispatch(FuncRequest(LFUN_BRANCH_INSERT, branch_name)); + break; + } + + case LFUN_KEYMAP_OFF: + getIntl().keyMapOn(false); + break; + + case LFUN_KEYMAP_PRIMARY: + getIntl().keyMapPrim(); + break; + + case LFUN_KEYMAP_SECONDARY: + getIntl().keyMapSec(); + break; + + case LFUN_KEYMAP_TOGGLE: + getIntl().toggleKeyMap(); + break; + + case LFUN_DIALOG_SHOW_NEW_INSET: { + string const name = cmd.getArg(0); + string data = trim(to_utf8(cmd.argument()).substr(name.size())); + if (decodeInsetParam(name, data, buffer_)) + lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, name + " " + data)); + else + lyxerr << "Inset type '" << name << + "' not recognized in LFUN_DIALOG_SHOW_NEW_INSET" << endl; + break; + } + + case LFUN_CITATION_INSERT: { + if (argument.empty()) { + lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW_NEW_INSET, "citation")); + break; + } + // we can have one optional argument, delimited by '|' + // citation-insert | + // this should be enhanced to also support text_after + // and citation style + string arg = argument; + string opt1; + if (contains(argument, "|")) { + arg = token(argument, '|', 0); + opt1 = token(argument, '|', 1); + } + InsetCommandParams icp(CITE_CODE); + icp["key"] = from_utf8(arg); + if (!opt1.empty()) + icp["before"] = from_utf8(opt1); + string icstr = InsetCommand::params2string("citation", icp); + FuncRequest fr(LFUN_INSET_INSERT, icstr); + lyx::dispatch(fr); + break; + } + + case LFUN_INSET_APPLY: { + string const name = cmd.getArg(0); + Inset * inset = editedInset(name); + if (!inset) { + FuncRequest fr(LFUN_INSET_INSERT, cmd.argument()); + lyx::dispatch(fr); + break; + } + // put cursor in front of inset. + if (!setCursorFromInset(inset)) { + LASSERT(false, break); + } + cur.recordUndo(); + FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument()); + inset->dispatch(cur, fr); + dr.update(Update::SinglePar | Update::FitCursor); break; } default: - return false; + dispatched = false; + break; } - return true; + buffer_.undo().endUndoGroup(); + dr.dispatched(dispatched); + return; } @@ -1511,7 +1892,7 @@ void BufferView::clearSelection() d->xsel_cache_.set = false; // The buffer did not really change, but this causes the // redraw we need because we cleared the selection above. - buffer_.changed(); + buffer_.changed(false); } @@ -1536,7 +1917,7 @@ Inset const * BufferView::getCoveringInset(Text const & text, if (!inset) return 0; - if (!inset->descendable()) + if (!inset->descendable(*this)) // No need to go further down if the inset is not // descendable. return inset; @@ -1606,7 +1987,7 @@ void BufferView::mouseEventDispatch(FuncRequest const & cmd0) // This event (moving without mouse click) is not passed further. // This should be changed if it is further utilized. - buffer_.changed(); + buffer_.changed(false); return; } @@ -1635,7 +2016,7 @@ void BufferView::mouseEventDispatch(FuncRequest const & cmd0) // Notify left insets if (cur != old) { old.fixIfBroken(); - bool badcursor = notifyCursorLeaves(old, cur); + bool badcursor = notifyCursorLeavesOrEnters(old, cur); if (badcursor) cursor().fixIfBroken(); } @@ -1667,8 +2048,13 @@ void BufferView::lfunScroll(FuncRequest const & cmd) if (scroll_value) scroll(scroll_step * scroll_value); } - updateMetrics(); - buffer_.changed(); + buffer_.changed(true); +} + + +int BufferView::minVisiblePart() +{ + return 2 * defaultRowHeight(); } @@ -1686,10 +2072,12 @@ int BufferView::scrollDown(int offset) { Text * text = &buffer_.text(); TextMetrics & tm = d->text_metrics_[text]; - int ymax = height_ + offset; + int const ymax = height_ + offset; while (true) { pair last = tm.last(); int bottom_pos = last.second->position() + last.second->descent(); + if (lyxrc.scroll_below_document) + bottom_pos += height_ - minVisiblePart(); if (last.first + 1 == int(text->paragraphs().size())) { if (bottom_pos <= height_) return 0; @@ -1735,11 +2123,12 @@ void BufferView::setCursorFromRow(int row) buffer_.texrow().getIdFromRow(row, tmpid, tmppos); - d->cursor_.reset(buffer_.inset()); + d->cursor_.reset(); if (tmpid == -1) buffer_.text().setCursor(d->cursor_, 0, 0); else buffer_.text().setCursor(d->cursor_, buffer_.getParFromID(tmpid).pit(), tmppos); + recenter(); } @@ -1751,7 +2140,7 @@ bool BufferView::setCursorFromInset(Inset const * inset) // Inset is not at cursor position. Find it in the document. Cursor cur(*this); - cur.reset(buffer().inset()); + cur.reset(); while (cur && cur.nextInset() != inset) cur.forwardInset(); @@ -1765,15 +2154,22 @@ bool BufferView::setCursorFromInset(Inset const * inset) void BufferView::gotoLabel(docstring const & label) { - 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()); + std::vector bufs = buffer().allRelatives(); + std::vector::iterator it = bufs.begin(); + for (; it != bufs.end(); ++it) { + Buffer const * buf = *it; + + // find label + Toc & toc = buf->tocBackend().toc("label"); + TocIterator toc_it = toc.begin(); + TocIterator end = toc.end(); + for (; toc_it != end; ++toc_it) { + if (label == toc_it->str()) { + lyx::dispatch(toc_it->action()); + return; + } + } } - //FIXME: We could do a bit more searching thanks to this: - //InsetLabel const * inset = buffer_.insetLabel(label); } @@ -1809,6 +2205,7 @@ int BufferView::workHeight() const void BufferView::setCursor(DocIterator const & dit) { + d->cursor_.reset(); size_t const n = dit.depth(); for (size_t i = 0; i < n; ++i) dit[i].inset().edit(d->cursor_, true); @@ -1836,10 +2233,8 @@ bool BufferView::checkDepm(Cursor & cur, Cursor & old) d->cursor_ = cur; - updateLabels(buffer_); - - updateMetrics(); - buffer_.changed(); + buffer_.updateLabels(); + buffer_.changed(true); return true; } @@ -1853,15 +2248,12 @@ bool BufferView::mouseSetCursor(Cursor & cur, bool select) // persistent selection cap::saveSelection(cursor()); + d->cursor_.macroModeClose(); + // 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(); @@ -1871,9 +2263,11 @@ bool BufferView::mouseSetCursor(Cursor & cur, bool select) // FIXME: (2) if we had a working InsetText::notifyCursorLeaves, // the leftinset bool would not be necessary (badcursor instead). bool update = leftinset; - if (!do_selection && !badcursor && d->cursor_.inTexted()) + if (!do_selection && d->cursor_.inTexted()) update |= checkDepm(cur, d->cursor_); + if (!do_selection) + d->cursor_.resetAnchor(); d->cursor_.setCursor(cur); d->cursor_.boundary(cur.boundary()); if (do_selection) @@ -2074,8 +2468,7 @@ void BufferView::insertLyXFile(FileName const & fname) res = _("Could not insert document %1$s"); } - updateMetrics(); - buffer_.changed(); + buffer_.changed(true); // emit message signal. message(bformat(res, disp_fn)); buffer_.errors("Parse"); @@ -2174,10 +2567,11 @@ Point BufferView::coordOffset(DocIterator const & dit, bool boundary) const Point BufferView::getPos(DocIterator const & dit, bool boundary) const { + if (!paragraphVisible(dit)) + return Point(-1, -1); + CursorSlice const & bot = dit.bottom(); TextMetrics const & tm = textMetrics(bot.text()); - if (!tm.contains(bot.pit())) - return Point(-1, -1); Point p = coordOffset(dit, boundary); // offset from outer paragraph p.y_ += tm.parMetrics(bot.pit()).position(); @@ -2185,11 +2579,44 @@ Point BufferView::getPos(DocIterator const & dit, bool boundary) const } +bool BufferView::paragraphVisible(DocIterator const & dit) const +{ + CursorSlice const & bot = dit.bottom(); + TextMetrics const & tm = textMetrics(bot.text()); + + return tm.contains(bot.pit()); +} + + +void BufferView::cursorPosAndHeight(Point & p, int & h) const +{ + Cursor const & cur = cursor(); + Font const font = cur.getFont(); + frontend::FontMetrics const & fm = theFontMetrics(font); + int const asc = fm.maxAscent(); + int const des = fm.maxDescent(); + h = asc + des; + p = getPos(cur, cur.boundary()); + p.y_ -= asc; +} + + +bool BufferView::cursorInView(Point const & p, int h) const +{ + Cursor const & cur = cursor(); + // does the cursor touch the screen ? + if (p.y_ + h < 0 || p.y_ >= workHeight() || !paragraphVisible(cur)) + return false; + return true; +} + + void BufferView::draw(frontend::Painter & pain) { if (height_ == 0 || width_ == 0) return; LYXERR(Debug::PAINTING, "\t\t*** START DRAWING ***"); + Text & text = buffer_.text(); TextMetrics const & tm = d->text_metrics_[&text]; int const y = tm.first().second->position(); @@ -2224,7 +2651,7 @@ void BufferView::draw(frontend::Painter & pain) // Clear background. pain.fillRectangle(0, 0, width_, height_, - buffer_.inset().backgroundColor()); + pi.backgroundColor(&buffer_.inset())); // Draw everything. tm.draw(pi, 0, y); @@ -2232,8 +2659,12 @@ void BufferView::draw(frontend::Painter & pain) // and possibly grey out below 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); + + if (y2 < height_) { + Color color = buffer().isInternal() + ? Color_background : Color_bottomarea; + pain.fillRectangle(0, y2, width_, height_ - y2, color); + } break; } LYXERR(Debug::PAINTING, "\n\t\t*** END DRAWING ***"); @@ -2340,12 +2771,11 @@ void BufferView::insertPlaintextFile(FileName const & f, bool asParagraph) cap::replaceSelection(cur); buffer_.undo().recordUndo(cur); if (asParagraph) - cur.innerText()->insertStringAsParagraphs(cur, tmpstr); + cur.innerText()->insertStringAsParagraphs(cur, tmpstr, cur.current_font); else - cur.innerText()->insertStringAsLines(cur, tmpstr); + cur.innerText()->insertStringAsLines(cur, tmpstr, cur.current_font); - updateMetrics(); - buffer_.changed(); + buffer_.changed(true); } @@ -2373,6 +2803,8 @@ bool samePar(DocIterator const & a, DocIterator const & b) return true; if (a.empty() || b.empty()) return false; + if (a.depth() != b.depth()) + return false; return &a.innerParagraph() == &b.innerParagraph(); }