X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2FBufferView.cpp;h=43420209cceab24a9d4101b4855c72ad797a437e;hb=1d6573a330791fb34541f4092cd49e4559972489;hp=8fa8a036bccaf95c33b30c12d4c69026bd5f9eca;hpb=d631fe00017b5ee9bd3c8350c074f9e7567fbaca;p=lyx.git diff --git a/src/BufferView.cpp b/src/BufferView.cpp index 8fa8a036bc..43420209cc 100644 --- a/src/BufferView.cpp +++ b/src/BufferView.cpp @@ -51,10 +51,10 @@ #include "TextMetrics.h" #include "TexRow.h" #include "TocBackend.h" -#include "VSpace.h" #include "WordLangTuple.h" #include "insets/InsetBibtex.h" +#include "insets/InsetCitation.h" #include "insets/InsetCommand.h" // ChangeRefs #include "insets/InsetExternal.h" #include "insets/InsetGraphics.h" @@ -106,6 +106,7 @@ T * getInsetByCode(Cursor const & cur, InsetCode code) return 0; } + /// Note that comparing contents can only be used for InsetCommand bool findNextInset(DocIterator & dit, vector const & codes, docstring const & contents) @@ -562,7 +563,7 @@ docstring BufferView::toolTip(int x, int y) const } -docstring BufferView::contextMenu(int x, int y) const +string BufferView::contextMenu(int x, int y) const { //If there is a selection, return the containing inset menu if (d->cursor_.selection()) @@ -886,29 +887,28 @@ bool BufferView::scrollToCursor(DocIterator const & dit, bool recenter) if (recenter) scrolled = scroll(ypos - height_/2); + // We try to visualize the whole row, if the row height is larger than + // the screen height, we scroll to a heuristic value of height_ / 4. + // FIXME: This heuristic value should be replaced by a recursive search + // for a row in the inset that can be visualized completely. + else if (row_dim.height() > height_) { + if (ypos < defaultRowHeight()) + scrolled = scroll(ypos - height_ / 4); + else if (ypos > height_ - defaultRowHeight()) + scrolled = scroll(ypos - 3 * height_ / 4); + } + // If the top part of the row falls of the screen, we scroll // up to align the top of the row with the top of the screen. else if (ypos - row_dim.ascent() < 0 && ypos < height_) { int ynew = row_dim.ascent(); - if (ynew > height_ - row_dim.descent()) - // FIXME: Recursively find the rowheight of the row in the inset - // until we find a row which can be visualized completely. - ynew = height_ - defaultRowHeight(); - int const scroll = ynew - ypos; - scrolled = scrollUp(scroll); + scrolled = scrollUp(ynew - ypos); } // If the bottom of the row falls of the screen, we scroll down. - // However, we have to be careful not to scroll that much that - // the top falls of the screen. else if (ypos + row_dim.descent() > height_ && ypos > 0) { int ynew = height_ - row_dim.descent(); - if (ynew < row_dim.ascent()) - // FIXME: Recursively find the rowheight of the row in the inset - // until we find a row which can be visualized completely. - ynew = defaultRowHeight(); - int const scroll = ypos - ynew; - scrolled = scrollDown(scroll); + scrolled = scrollDown(ypos - ynew); } // else, nothing to do, the cursor is already visible so we just return. @@ -943,7 +943,15 @@ bool BufferView::scrollToCursor(DocIterator const & dit, bool recenter) } -void BufferView::updateDocumentClass(DocumentClass const * const olddc) +void BufferView::makeDocumentClass() +{ + DocumentClassConstPtr olddc = buffer_.params().documentClassPtr(); + buffer_.params().makeDocumentClass(); + updateDocumentClass(olddc); +} + + +void BufferView::updateDocumentClass(DocumentClassConstPtr olddc) { message(_("Converting document to new document class...")); @@ -958,6 +966,7 @@ void BufferView::updateDocumentClass(DocumentClass const * const olddc) buffer_.errors("Class Switch"); } + /** 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). @@ -1036,9 +1045,17 @@ bool BufferView::getStatus(FuncRequest const & cmd, FuncStatus & flag) // buffer clean/dirty status by itself. flag.setEnabled(!buffer_.isReadonly() && buffer_.undo().hasRedoStack()); break; - case LFUN_FILE_INSERT: case LFUN_FILE_INSERT_PLAINTEXT_PARA: - case LFUN_FILE_INSERT_PLAINTEXT: + case LFUN_FILE_INSERT_PLAINTEXT: { + docstring const fname = cmd.argument(); + if (!FileName::isAbsolute(to_utf8(fname))) { + flag.message(_("Absolute filename expected.")); + return false; + } + flag.setEnabled(cur.inTexted()); + break; + } + case LFUN_FILE_INSERT: case LFUN_BOOKMARK_SAVE: // FIXME: Actually, these LFUNS should be moved to Text flag.setEnabled(cur.inTexted()); @@ -1053,7 +1070,6 @@ bool BufferView::getStatus(FuncRequest const & cmd, FuncStatus & flag) 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: @@ -1071,6 +1087,14 @@ bool BufferView::getStatus(FuncRequest const & cmd, FuncStatus & flag) flag.setEnabled(true); break; + case LFUN_WORD_FINDADV: { + FindAndReplaceOptions opt; + istringstream iss(to_utf8(cmd.argument())); + iss >> opt; + flag.setEnabled(opt.repl_buf_name.empty() + || !buffer_.isReadonly()); + } + case LFUN_LABEL_GOTO: { flag.setEnabled(!cmd.argument().empty() || getInsetByCode(cur, REF_CODE)); @@ -1162,6 +1186,22 @@ bool BufferView::getStatus(FuncRequest const & cmd, FuncStatus & flag) break; } + // FIXME We do not really want this here, but at present we need to + // handle their dispatch here, for reasons explained there, so we'll + // handle this here, too, for consistency. + case LFUN_BRANCH_ACTIVATE: + case LFUN_BRANCH_DEACTIVATE: + case LFUN_BRANCH_MASTER_ACTIVATE: + case LFUN_BRANCH_MASTER_DEACTIVATE: { + bool const master = (cmd.action() == LFUN_BRANCH_MASTER_ACTIVATE + || cmd.action() == LFUN_BRANCH_MASTER_DEACTIVATE); + BranchList const & branchList = master ? buffer().masterBuffer()->params().branchlist() + : buffer().params().branchlist(); + docstring const branchName = cmd.argument(); + flag.setEnabled(!branchName.empty() && branchList.find(branchName)); + break; + } + default: return false; } @@ -1185,14 +1225,7 @@ void BufferView::editInset(string const & name, Inset * inset) void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr) { - //lyxerr << [ cmd = " << cmd << "]" << endl; - - // Make sure that the cached BufferView is correct. - LYXERR(Debug::ACTION, " action[" << cmd.action() << ']' - << " arg[" << to_utf8(cmd.argument()) << ']' - << " x[" << cmd.x() << ']' - << " y[" << cmd.y() << ']' - << " button[" << cmd.button() << ']'); + LYXERR(Debug::ACTION, "BufferView::dispatch: cmd: " << cmd); string const argument = to_utf8(cmd.argument()); Cursor & cur = d->cursor_; @@ -1210,7 +1243,7 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr) switch (act) { case LFUN_BUFFER_PARAMS_APPLY: { - DocumentClass const * const oldClass = buffer_.params().documentClassPtr(); + DocumentClassConstPtr olddc = buffer_.params().documentClassPtr(); cur.recordUndoFullDocument(); istringstream ss(to_utf8(cmd.argument())); Lexer lex; @@ -1221,7 +1254,7 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr) << unknown_tokens << " unknown token" << (unknown_tokens == 1 ? "" : "s")); } - updateDocumentClass(oldClass); + updateDocumentClass(olddc); // 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 @@ -1232,12 +1265,9 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr) } case LFUN_LAYOUT_MODULES_CLEAR: { - DocumentClass const * const oldClass = - buffer_.params().documentClassPtr(); cur.recordUndoFullDocument(); buffer_.params().clearLayoutModules(); - buffer_.params().makeDocumentClass(); - updateDocumentClass(oldClass); + makeDocumentClass(); dr.screenUpdate(Update::Force); dr.forceBufferUpdate(); break; @@ -1245,17 +1275,15 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr) case LFUN_LAYOUT_MODULE_ADD: { BufferParams const & params = buffer_.params(); - if (!params.moduleCanBeAdded(argument)) { + if (!params.layoutModuleCanBeAdded(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(); - updateDocumentClass(oldClass); + makeDocumentClass(); dr.screenUpdate(Update::Force); dr.forceBufferUpdate(); break; @@ -1282,12 +1310,9 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr) 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(); - updateDocumentClass(oldDocClass); + makeDocumentClass(); dr.screenUpdate(Update::Force); dr.forceBufferUpdate(); break; @@ -1308,12 +1333,10 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr) } 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(); - updateDocumentClass(oldClass); + makeDocumentClass(); dr.screenUpdate(Update::Force); dr.forceBufferUpdate(); break; @@ -1631,9 +1654,10 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr) from = doc_iterator_begin(&buffer_); to = doc_iterator_end(&buffer_); } - int const words = countWords(from, to); - int const chars = countChars(from, to, false); - int const chars_blanks = countChars(from, to, true); + buffer_.updateStatistics(from, to); + int const words = buffer_.wordCount(); + int const chars = buffer_.charCount(false); + int const chars_blanks = buffer_.charCount(true); docstring message; if (cur.selection()) message = _("Statistics for the selection:"); @@ -1688,29 +1712,51 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr) p = Point(width_, height_); Cursor old = cur; bool const in_texted = cur.inTexted(); - cur.reset(); + cur.setCursor(doc_iterator_begin(cur.buffer())); + cur.selHandle(false); buffer_.changed(true); updateHoveredInset(); d->text_metrics_[&buffer_.text()].editXY(cur, p.x_, p.y_, true, act == LFUN_SCREEN_UP); - cur.resetAnchor(); //FIXME: what to do with cur.x_target()? bool update = in_texted && cur.bv().checkDepm(cur, old); cur.finishUndo(); - if (update) { - dr.screenUpdate(Update::Force | Update::FitCursor); + + if (update || cur.mark()) + dr.screenUpdate(Update::Force | Update::FitCursor); + if (update) dr.forceBufferUpdate(); - } break; } - case LFUN_SCROLL: - lfunScroll(cmd); + case LFUN_SCROLL: { + string const scroll_type = cmd.getArg(0); + int scroll_step = 0; + if (scroll_type == "line") + scroll_step = d->scrollbarParameters_.single_step; + else if (scroll_type == "page") + scroll_step = d->scrollbarParameters_.page_step; + else + 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); + } + buffer_.changed(true); + updateHoveredInset(); dr.forceBufferUpdate(); break; + } case LFUN_SCREEN_UP_SELECT: { + // FIXME: why is the algorithm different from LFUN_SCREEN_UP? cur.selHandle(true); if (isTopScreen()) { lyx::dispatch(FuncRequest(LFUN_BUFFER_BEGIN_SELECT)); @@ -1728,6 +1774,7 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr) } case LFUN_SCREEN_DOWN_SELECT: { + // FIXME: why is the algorithm different from LFUN_SCREEN_DOWN? cur.selHandle(true); if (isBottomScreen()) { lyx::dispatch(FuncRequest(LFUN_BUFFER_END_SELECT)); @@ -1753,7 +1800,7 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr) FuncRequest const fr = lyxaction.lookupFunc(commandstr); // an arbitrary number to limit number of iterations - const int max_iter = 10000; + const int max_iter = 100000; int iterations = 0; Cursor & cur = d->cursor_; Cursor const savecur = cur; @@ -1762,7 +1809,7 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr) cur.forwardInset(); cur.beginUndoGroup(); while(cur && iterations < max_iter) { - Inset * ins = cur.nextInset(); + Inset * const ins = cur.nextInset(); if (!ins) break; docstring insname = ins->layoutName(); @@ -1778,7 +1825,9 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr) break; insname = insname.substr(0, i); } - cur.forwardInset(); + // if we did not delete the inset, skip it + if (!cur.nextInset() || cur.nextInset() == ins) + cur.forwardInset(); } cur.endUndoGroup(); cur = savecur; @@ -1861,6 +1910,23 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr) arg = token(argument, '|', 0); opt1 = token(argument, '|', 1); } + + // if our cursor is direclty in front of or behind a citation inset, + // we will instead add the new key to it. + Inset * inset = cur.nextInset(); + if (!inset || inset->lyxCode() != CITE_CODE) + inset = cur.prevInset(); + if (inset && inset->lyxCode() == CITE_CODE) { + InsetCitation * icite = static_cast(inset); + if (icite->addKey(arg)) { + dr.forceBufferUpdate(); + dr.screenUpdate(Update::FitCursor | Update::SinglePar); + if (!opt1.empty()) + LYXERR0("Discarding optional argument to citation-insert."); + } + dispatched = true; + break; + } InsetCommandParams icp(CITE_CODE); icp["key"] = from_utf8(arg); if (!opt1.empty()) @@ -1892,6 +1958,78 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr) break; } + // FIXME: + // The change of language of buffer belongs to the Buffer class. + // We have to do it here because we need a cursor for Undo. + // When Undo::recordUndoBufferParams() is implemented someday + // LFUN_BUFFER_LANGUAGE should be handled by the Buffer class. + case LFUN_BUFFER_LANGUAGE: { + Language const * oldL = buffer_.params().language; + Language const * newL = languages.getLanguage(argument); + if (!newL || oldL == newL) + break; + if (oldL->rightToLeft() == newL->rightToLeft()) { + cur.recordUndoFullDocument(); + buffer_.changeLanguage(oldL, newL); + cur.setCurrentFont(); + dr.forceBufferUpdate(); + } + break; + } + + case LFUN_FILE_INSERT_PLAINTEXT_PARA: + case LFUN_FILE_INSERT_PLAINTEXT: { + bool const as_paragraph = (act == LFUN_FILE_INSERT_PLAINTEXT_PARA); + string const fname = to_utf8(cmd.argument()); + if (!FileName::isAbsolute(fname)) + dr.setMessage(_("Absolute filename expected.")); + else + insertPlaintextFile(FileName(fname), as_paragraph); + break; + } + + // FIXME We do not really want this here, but it has to be at present + // because we need a cursor for the recordUndoFullDocument call. What + // we would really like is a recordUndoBufferParams call that did not + // need a cursor, but we do not have that yet. + // So, if this does get fixed, this code can be moved back to Buffer.cpp, + // and the corresponding code in getStatus() should be moved back, too. + case LFUN_BRANCH_ACTIVATE: + case LFUN_BRANCH_DEACTIVATE: + case LFUN_BRANCH_MASTER_ACTIVATE: + case LFUN_BRANCH_MASTER_DEACTIVATE: { + bool const master = (cmd.action() == LFUN_BRANCH_MASTER_ACTIVATE + || cmd.action() == LFUN_BRANCH_MASTER_DEACTIVATE); + Buffer * buf = master ? const_cast(buffer().masterBuffer()) + : &buffer(); + + docstring const branch_name = cmd.argument(); + // the case without a branch name is handled elsewhere + if (branch_name.empty()) { + dispatched = false; + break; + } + Branch * branch = buf->params().branchlist().find(branch_name); + if (!branch) { + LYXERR0("Branch " << branch_name << " does not exist."); + dr.setError(true); + docstring const msg = + bformat(_("Branch \"%1$s\" does not exist."), branch_name); + dr.setMessage(msg); + break; + } + bool activate = (cmd.action() == LFUN_BRANCH_ACTIVATE + || cmd.action() == LFUN_BRANCH_MASTER_ACTIVATE); + if (branch->isSelected() != activate) { + branch->setSelected(activate); + cur.recordUndoFullDocument(); + dr.setError(false); + dr.screenUpdate(Update::Force); + dr.forceBufferUpdate(); + } + break; + } + default: // OK, so try the Buffer itself... buffer_.dispatch(cmd, dr); @@ -2088,8 +2226,8 @@ void BufferView::mouseEventDispatch(FuncRequest const & cmd0) // Notify left insets if (cur != old) { - old.fixIfBroken(); - bool badcursor = notifyCursorLeavesOrEnters(old, cur); + bool badcursor = old.fixIfBroken() | cur.fixIfBroken(); + badcursor |= notifyCursorLeavesOrEnters(old, cur); if (badcursor) cursor().fixIfBroken(); } @@ -2108,31 +2246,6 @@ void BufferView::mouseEventDispatch(FuncRequest const & cmd0) } -void BufferView::lfunScroll(FuncRequest const & cmd) -{ - string const scroll_type = cmd.getArg(0); - int scroll_step = 0; - if (scroll_type == "line") - scroll_step = d->scrollbarParameters_.single_step; - else if (scroll_type == "page") - scroll_step = d->scrollbarParameters_.page_step; - else - 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); - } - buffer_.changed(true); - updateHoveredInset(); -} - - int BufferView::minVisiblePart() { return 2 * defaultRowHeight(); @@ -2347,7 +2460,7 @@ bool BufferView::checkDepm(Cursor & cur, Cursor & old) d->cursor_ = cur; cur.forceBufferUpdate(); - buffer_.changed(true); + cur.screenUpdateFlags(Update::Force); return true; } @@ -2362,6 +2475,8 @@ bool BufferView::mouseSetCursor(Cursor & cur, bool select) cap::saveSelection(cursor()); d->cursor_.macroModeClose(); + // If a macro has been finalized, the cursor might have been broken + cur.fixIfBroken(); // Has the cursor just left the inset? bool const leftinset = (&d->cursor_.inset() != &cur.inset()); @@ -2964,7 +3079,7 @@ bool samePar(DocIterator const & a, DocIterator const & b) } -void BufferView::setInlineCompletion(Cursor & cur, DocIterator const & pos, +void BufferView::setInlineCompletion(Cursor const & cur, DocIterator const & pos, docstring const & completion, size_t uniqueChars) { uniqueChars = min(completion.size(), uniqueChars);