X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2FText3.cpp;h=b86819b237328f2762118890de3664f91bb40286;hb=76c15f51ea82c978f877fc106be07c418bc93a58;hp=7f94185fa2eefd2e4706dd99fdc20ae198ec8b32;hpb=b63421b7dc65e0c721f8928d1330b9bb2cff43d8;p=lyx.git diff --git a/src/Text3.cpp b/src/Text3.cpp index 7f94185fa2..b86819b237 100644 --- a/src/Text3.cpp +++ b/src/Text3.cpp @@ -53,7 +53,7 @@ #include "frontends/Selection.h" #include "insets/InsetArgument.h" -#include "insets/InsetCollapsable.h" +#include "insets/InsetCollapsible.h" #include "insets/InsetCommand.h" #include "insets/InsetExternal.h" #include "insets/InsetFloat.h" @@ -78,7 +78,7 @@ #include "support/regex.h" #include "mathed/InsetMathHull.h" -#include "mathed/MathMacroTemplate.h" +#include "mathed/InsetMathMacroTemplate.h" #include #include @@ -89,14 +89,18 @@ using namespace lyx::support; namespace lyx { using cap::copySelection; +using cap::copySelectionToTemp; using cap::cutSelection; +using cap::cutSelectionToTemp; using cap::pasteFromStack; +using cap::pasteFromTemp; using cap::pasteClipboardText; using cap::pasteClipboardGraphics; using cap::replaceSelection; using cap::grabAndEraseSelection; using cap::selClearOrDel; using cap::pasteSimpleText; +using frontend::Clipboard; // globals... static Font freefont(ignore_font, ignore_language); @@ -132,7 +136,7 @@ static void finishChange(Cursor & cur, bool selecting) } -static void mathDispatch(Cursor & cur, FuncRequest const & cmd, bool display) +static void mathDispatch(Cursor & cur, FuncRequest const & cmd) { cur.recordUndo(); docstring sel = cur.selectionAsString(false); @@ -152,45 +156,39 @@ static void mathDispatch(Cursor & cur, FuncRequest const & cmd, bool display) LATTEST(old_pos == cur.pos()); #endif cur.nextInset()->edit(cur, true); - // don't do that also for LFUN_MATH_MODE - // unless you want end up with always changing - // to mathrm when opening an inlined inset -- - // I really hate "LyXfunc overloading"... - if (display) - cur.dispatch(FuncRequest(LFUN_MATH_DISPLAY)); - // Avoid an unnecessary undo step if cmd.argument - // is empty - if (!cmd.argument().empty()) - cur.dispatch(FuncRequest(LFUN_MATH_INSERT, - cmd.argument())); + if (cmd.action() != LFUN_MATH_MODE) + // LFUN_MATH_MODE has a different meaning in math mode + cur.dispatch(cmd); } else { - // create a macro if we see "\\newcommand" - // somewhere, and an ordinary formula - // otherwise - if (sel.find(from_ascii("\\newcommand")) == string::npos - && sel.find(from_ascii("\\newlyxcommand")) == string::npos - && sel.find(from_ascii("\\def")) == string::npos) - { - InsetMathHull * formula = new InsetMathHull(cur.buffer()); - string const selstr = to_utf8(sel); - istringstream is(selstr); - Lexer lex; + InsetMathHull * formula = new InsetMathHull(cur.buffer()); + string const selstr = to_utf8(sel); + istringstream is(selstr); + Lexer lex; + lex.setStream(is); + if (!formula->readQuiet(lex)) { + // No valid formula, let's try with delims + is.str("$" + selstr + "$"); lex.setStream(is); if (!formula->readQuiet(lex)) { - // No valid formula, let's try with delims - is.str("$" + selstr + "$"); - lex.setStream(is); - if (!formula->readQuiet(lex)) { - // Still not valid, leave it as is - valid = false; - delete formula; - cur.insert(sel); - } else - cur.insert(formula); - } else - cur.insert(formula); - } else { - cur.insert(new MathMacroTemplate(cur.buffer(), sel)); + // Still not valid, leave it as is + valid = false; + delete formula; + cur.insert(sel); + } + } + if (valid) { + cur.insert(formula); + cur.nextInset()->edit(cur, true); + LASSERT(cur.inMathed(), return); + cur.pos() = 0; + cur.resetAnchor(); + cur.selection(true); + cur.pos() = cur.lastpos(); + if (cmd.action() != LFUN_MATH_MODE) + // LFUN_MATH_MODE has a different meaning in math mode + cur.dispatch(cmd); + cur.clearSelection(); + cur.pos() = cur.lastpos(); } } if (valid) @@ -248,24 +246,11 @@ static bool doInsertInset(Cursor & cur, Text * text, if (!inset) return false; - if (InsetCollapsable * ci = inset->asInsetCollapsable()) + if (InsetCollapsible * ci = inset->asInsetCollapsible()) ci->setButtonLabel(); cur.recordUndo(); - if (cmd.action() == LFUN_INDEX_INSERT) { - docstring ds = subst(text->getStringToIndex(cur), '\n', ' '); - text->insertInset(cur, inset); - if (edit) - inset->edit(cur, true); - // Now put this into inset - Font const f(inherit_font, cur.current_font.language()); - if (!ds.empty()) { - cur.text()->insertStringAsLines(cur, ds, f); - cur.leaveInset(*inset); - } - return true; - } - else if (cmd.action() == LFUN_ARGUMENT_INSERT) { + if (cmd.action() == LFUN_ARGUMENT_INSERT) { bool cotextinsert = false; InsetArgument const * const ia = static_cast(inset); Layout const & lay = cur.paragraph().layout(); @@ -304,9 +289,16 @@ static bool doInsertInset(Cursor & cur, Text * text, bool gotsel = false; if (cur.selection()) { - cutSelection(cur, false, pastesel); + if (cmd.action() == LFUN_INDEX_INSERT) + copySelectionToTemp(cur); + else + cutSelectionToTemp(cur, pastesel); + cur.clearSelection(); + gotsel = true; + } else if (cmd.action() == LFUN_INDEX_INSERT) { + gotsel = text->selectWordWhenUnderCursor(cur, WHOLE_WORD); + copySelectionToTemp(cur); cur.clearSelection(); - gotsel = true; } text->insertInset(cur, inset); @@ -316,7 +308,7 @@ static bool doInsertInset(Cursor & cur, Text * text, if (!gotsel || !pastesel) return true; - pasteFromStack(cur, cur.buffer()->errorList("Paste"), 0); + pasteFromTemp(cur, cur.buffer()->errorList("Paste")); cur.buffer()->errors("Paste"); cur.clearSelection(); // bug 393 cur.finishUndo(); @@ -369,17 +361,34 @@ enum OutlineOp { }; -static void outline(OutlineOp mode, Cursor & cur) +static void insertSeparator(Cursor & cur, depth_type const depth) +{ + Buffer & buf = *cur.buffer(); + lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_BREAK)); + DocumentClass const & tc = buf.params().documentClass(); + lyx::dispatch(FuncRequest(LFUN_LAYOUT, from_ascii("\"") + tc.plainLayout().name() + + from_ascii("\" ignoreautonests"))); + // FIXME: Bibitem mess! + if (cur.prevInset() && cur.prevInset()->lyxCode() == BIBITEM_CODE) + lyx::dispatch(FuncRequest(LFUN_CHAR_DELETE_BACKWARD)); + lyx::dispatch(FuncRequest(LFUN_SEPARATOR_INSERT, "plain")); + while (cur.paragraph().params().depth() > depth) + lyx::dispatch(FuncRequest(LFUN_DEPTH_DECREMENT)); +} + + +static void outline(OutlineOp mode, Cursor & cur, Text * text) { Buffer & buf = *cur.buffer(); pit_type & pit = cur.pit(); ParagraphList & pars = buf.text().paragraphs(); ParagraphList::iterator const bgn = pars.begin(); // The first paragraph of the area to be copied: - ParagraphList::iterator start = next(bgn, pit); + ParagraphList::iterator start = lyx::next(bgn, pit); // The final paragraph of area to be copied: ParagraphList::iterator finish = start; ParagraphList::iterator const end = pars.end(); + depth_type const current_depth = cur.paragraph().params().depth(); int const thistoclevel = buf.text().getTocLevel(distance(bgn, start)); int toclevel; @@ -414,10 +423,47 @@ static void outline(OutlineOp mode, Cursor & cur) // Not found; do nothing if (toclevel == Layout::NOT_IN_TOC || toclevel > thistoclevel) return; - pit_type const newpit = distance(bgn, dest); + pit_type newpit = distance(bgn, dest); pit_type const len = distance(start, finish); pit_type const deletepit = pit + len; buf.undo().recordUndo(cur, newpit, deletepit - 1); + // If we move an environment upwards, make sure it is + // separated from its new neighbour below: + // If an environment of the same layout follows, and the moved + // paragraph sequence does not end with a separator, insert one. + ParagraphList::iterator lastmoved = finish; + --lastmoved; + if (start->layout().isEnvironment() + && dest->layout() == start->layout() + && !lastmoved->isEnvSeparator(lastmoved->beginOfBody())) { + cur.pit() = distance(bgn, lastmoved); + cur.pos() = cur.lastpos(); + insertSeparator(cur, current_depth); + cur.pit() = pit; + } + // Likewise, if we moved an environment upwards, make sure it + // is separated from its new neighbour above. + // The paragraph before the target of movement + if (dest != bgn) { + ParagraphList::iterator before = dest; + --before; + // Get the parent paragraph (outer in nested context) + pit_type const parent = + before->params().depth() > current_depth + ? text->depthHook(distance(bgn, before), current_depth) + : distance(bgn, before); + // If a environment with same layout preceeds the moved one in the new + // position, and there is no separator yet, insert one. + if (start->layout().isEnvironment() + && pars[parent].layout() == start->layout() + && !before->isEnvSeparator(before->beginOfBody())) { + cur.pit() = distance(bgn, before); + cur.pos() = cur.lastpos(); + insertSeparator(cur, current_depth); + cur.pit() = pit; + } + } + newpit = distance(bgn, dest); pars.splice(dest, start, finish); cur.pit() = newpit; break; @@ -435,9 +481,45 @@ static void outline(OutlineOp mode, Cursor & cur) && toclevel <= thistoclevel) break; } - // One such was found: + // One such was found, so go on... + // If we move an environment downwards, make sure it is + // separated from its new neighbour above. pit_type newpit = distance(bgn, dest); buf.undo().recordUndo(cur, pit, newpit - 1); + // The paragraph before the target of movement + ParagraphList::iterator before = dest; + --before; + // Get the parent paragraph (outer in nested context) + pit_type const parent = + before->params().depth() > current_depth + ? text->depthHook(distance(bgn, before), current_depth) + : distance(bgn, before); + // If a environment with same layout preceeds the moved one in the new + // position, and there is no separator yet, insert one. + if (start->layout().isEnvironment() + && pars[parent].layout() == start->layout() + && !before->isEnvSeparator(before->beginOfBody())) { + cur.pit() = distance(bgn, before); + cur.pos() = cur.lastpos(); + insertSeparator(cur, current_depth); + cur.pit() = pit; + } + // Likewise, make sure moved environments are separated + // from their new neighbour below: + // If an environment of the same layout follows, and the moved + // paragraph sequence does not end with a separator, insert one. + ParagraphList::iterator lastmoved = finish; + --lastmoved; + if (dest != end + && start->layout().isEnvironment() + && dest->layout() == start->layout() + && !lastmoved->isEnvSeparator(lastmoved->beginOfBody())) { + cur.pit() = distance(bgn, lastmoved); + cur.pos() = cur.lastpos(); + insertSeparator(cur, current_depth); + cur.pit() = pit; + } + newpit = distance(bgn, dest); pit_type const len = distance(start, finish); pars.splice(dest, start, finish); cur.pit() = newpit - len; @@ -455,7 +537,7 @@ static void outline(OutlineOp mode, Cursor & cur) DocumentClass const & tc = buf.params().documentClass(); DocumentClass::const_iterator lit = tc.begin(); DocumentClass::const_iterator len = tc.end(); - int const newtoclevel = + int const newtoclevel = (mode == OutlineIn ? toclevel + 1 : toclevel - 1); LabelType const oldlabeltype = start->layout().labeltype; @@ -487,14 +569,14 @@ bool Text::isRTL(Paragraph const & par) const return par.isRTL(buffer.params()); } - + namespace { - + Language const * getLanguage(Cursor const & cur, string const & lang) { return lang.empty() ? cur.getFont().language() : languages.getLanguage(lang); } -} +} // namespace void Text::dispatch(Cursor & cur, FuncRequest & cmd) @@ -521,7 +603,7 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) cur.noScreenUpdate(); LBUFERR(this == cur.text()); - + // NOTE: This should NOT be a reference. See commit 94a5481a. CursorSlice const oldTopSlice = cur.top(); bool const oldBoundary = cur.boundary(); @@ -587,23 +669,23 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) case LFUN_WORD_DELETE_FORWARD: if (cur.selection()) - cutSelection(cur, true, false); + cutSelection(cur, false); else - deleteWordForward(cur); + deleteWordForward(cur, cmd.getArg(0) == "force"); finishChange(cur, false); break; case LFUN_WORD_DELETE_BACKWARD: if (cur.selection()) - cutSelection(cur, true, false); + cutSelection(cur, false); else - deleteWordBackward(cur); + deleteWordBackward(cur, cmd.getArg(0) == "force"); finishChange(cur, false); break; case LFUN_LINE_DELETE_FORWARD: if (cur.selection()) - cutSelection(cur, true, false); + cutSelection(cur, false); else tm->deleteLineForward(cur); finishChange(cur, false); @@ -773,6 +855,18 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) cur.upDownInText(up, needsUpdate); needsUpdate |= cur.beforeDispatchCursor().inMathed(); } else { + pos_type newpos = up ? 0 : cur.lastpos(); + if (lyxrc.mac_like_cursor_movement && cur.pos() != newpos) { + needsUpdate |= cur.selHandle(select); + // we do not reset the targetx of the cursor + cur.pos() = newpos; + needsUpdate |= bv->checkDepm(cur, bv->cursor()); + cur.updateTextTargetOffset(); + if (needsUpdate) + cur.forceBufferUpdate(); + break; + } + // if the cursor cannot be moved up or down do not remove // the selection right now, but wait for the next dispatch. if (select) @@ -814,7 +908,7 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) ParagraphList & pars = buf.text().paragraphs(); ParagraphList::iterator bgn = pars.begin(); // The first paragraph of the area to be selected: - ParagraphList::iterator start = next(bgn, pit); + ParagraphList::iterator start = lyx::next(bgn, pit); // The final paragraph of area to be selected: ParagraphList::iterator finish = start; ParagraphList::iterator end = pars.end(); @@ -838,6 +932,8 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) break; } cur.pos() = cur.lastpos(); + cur.boundary(false); + cur.setCurrentFont(); needsUpdate |= cur != old_cur; break; @@ -1054,24 +1150,20 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) case LFUN_CHAR_DELETE_FORWARD: if (!cur.selection()) { - bool was_separator = cur.paragraph().isEnvSeparator(cur.pos()); if (cur.pos() == cur.paragraph().size()) // Par boundary, force full-screen update singleParUpdate = false; - needsUpdate |= erase(cur); - cur.resetAnchor(); - if (was_separator && cur.pos() == cur.paragraph().size() - && (!cur.paragraph().layout().isEnvironment() - || cur.paragraph().size() > 0)) { - // Force full-screen update - singleParUpdate = false; - needsUpdate |= erase(cur); + else if (cmd.getArg(0) != "force" && cur.confirmDeletion()) { cur.resetAnchor(); + cur.selection(true); + cur.posForward(); + cur.setSelection(); + break; } - // It is possible to make it a lot faster still - // just comment out the line below... + needsUpdate |= erase(cur); + cur.resetAnchor(); } else { - cutSelection(cur, true, false); + cutSelection(cur, false); singleParUpdate = false; } moveCursor(cur, false); @@ -1085,6 +1177,13 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) // Par boundary, full-screen update if (par_boundary) singleParUpdate = false; + else if (cmd.getArg(0) != "force" && cur.confirmDeletion(true)) { + cur.resetAnchor(); + cur.selection(true); + cur.posBackward(); + cur.setSelection(); + break; + } needsUpdate |= backspace(cur); cur.resetAnchor(); if (par_boundary && !first_par && cur.pos() > 0 @@ -1094,7 +1193,7 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) } } } else { - cutSelection(cur, true, false); + cutSelection(cur, false); singleParUpdate = false; } break; @@ -1103,34 +1202,33 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) cap::replaceSelection(cur); pit_type pit = cur.pit(); Paragraph const & par = pars_[pit]; - pit_type prev = pit; - if (pit > 0) { - if (!pars_[pit - 1].layout().isEnvironment()) - prev = depthHook(pit, par.getDepth()); - else if (pars_[pit - 1].getDepth() >= par.getDepth()) - prev = pit - 1; - } + bool lastpar = (pit == pit_type(pars_.size() - 1)); + Paragraph const & nextpar = lastpar ? par : pars_[pit + 1]; + pit_type prev = pit > 0 ? depthHook(pit, par.getDepth()) : pit; if (prev < pit && cur.pos() == par.beginOfBody() - && !par.isEnvSeparator(cur.pos()) + && !par.size() && !par.isEnvSeparator(cur.pos()) && !par.layout().isCommand() && pars_[prev].layout() != par.layout() - && pars_[prev].layout().isEnvironment()) { + && pars_[prev].layout().isEnvironment() + && !nextpar.isEnvSeparator(nextpar.beginOfBody())) { if (par.layout().isEnvironment() && pars_[prev].getDepth() == par.getDepth()) { docstring const layout = par.layout().name(); DocumentClass const & tc = bv->buffer().params().documentClass(); lyx::dispatch(FuncRequest(LFUN_LAYOUT, tc.plainLayout().name())); - lyx::dispatch(FuncRequest(LFUN_SEPARATOR_INSERT, "parbreak")); + lyx::dispatch(FuncRequest(LFUN_SEPARATOR_INSERT, "plain")); lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_BREAK, "inverse")); lyx::dispatch(FuncRequest(LFUN_LAYOUT, layout)); } else { - lyx::dispatch(FuncRequest(LFUN_SEPARATOR_INSERT, "parbreak")); + lyx::dispatch(FuncRequest(LFUN_SEPARATOR_INSERT, "plain")); breakParagraph(cur); } Font const f(inherit_font, cur.current_font.language()); pars_[cur.pit() - 1].resetFonts(f); } else { - breakParagraph(cur, cmd.argument() == "inverse"); + if (par.isEnvSeparator(cur.pos()) && cmd.getArg(1) != "ignoresep") + cur.posForward(); + breakParagraph(cur, cmd.getArg(0) == "inverse"); } cur.resetAnchor(); // If we have a list and autoinsert item insets, @@ -1182,8 +1280,9 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) } */ if (cur.selection()) - cutSelection(cur, true, false); + cutSelection(cur, false); cur.insert(inset); + cur.forceBufferUpdate(); if (inset->editable() && inset->asInsetText()) inset->edit(cur, true); else @@ -1228,6 +1327,7 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) } ins->setParams(inspar); + break; } case LFUN_SPACE_INSERT: @@ -1244,6 +1344,8 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) string const name = to_utf8(cmd.argument()); if (name == "hyphenation") specialChar(cur, InsetSpecialChar::HYPHENATION); + else if (name == "allowbreak") + specialChar(cur, InsetSpecialChar::ALLOWBREAK); else if (name == "ligature-break") specialChar(cur, InsetSpecialChar::LIGATURE_BREAK); else if (name == "slash") @@ -1370,7 +1472,7 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) } case LFUN_CUT: - cutSelection(cur, true, true); + cutSelection(cur, true); cur.message(_("Cut")); break; @@ -1403,7 +1505,8 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) break; case LFUN_LAYOUT: { - docstring layout = cmd.argument(); + bool const ignoreautonests = cmd.getArg(1) == "ignoreautonests"; + docstring layout = ignoreautonests ? from_utf8(cmd.getArg(0)) : cmd.argument(); LYXERR(Debug::INFO, "LFUN_LAYOUT: (arg) " << to_utf8(layout)); Paragraph const & para = cur.paragraph(); @@ -1457,8 +1560,18 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) } } - if (change_layout) + if (change_layout) { setLayout(cur, layout); + if (cur.pit() > 0 && !ignoreautonests) { + set const & autonests = + pars_[cur.pit() - 1].layout().autonests(); + set const & autonested = + pars_[cur.pit()].layout().isAutonestedBy(); + if (autonests.find(layout) != autonests.end() + || autonested.find(old_layout) != autonested.end()) + lyx::dispatch(FuncRequest(LFUN_DEPTH_INCREMENT)); + } + } Layout::LaTeXArgMap args = tclass[layout].args(); Layout::LaTeXArgMap::const_iterator lait = args.begin(); @@ -1476,36 +1589,80 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) case LFUN_ENVIRONMENT_SPLIT: { bool const outer = cmd.argument() == "outer"; + bool const previous = cmd.argument() == "previous"; + bool const before = cmd.argument() == "before"; + bool const normal = cmd.argument().empty(); Paragraph const & para = cur.paragraph(); - docstring layout = para.layout().name(); + docstring layout; + if (para.layout().isEnvironment()) + layout = para.layout().name(); depth_type split_depth = cur.paragraph().params().depth(); - if (outer) { - // check if we have an environment in our nesting hierarchy + depth_type nextpar_depth = 0; + if (outer || previous) { + // check if we have an environment in our scope pit_type pit = cur.pit(); Paragraph cpar = pars_[pit]; while (true) { - if (pit == 0 || cpar.params().depth() == 0) + if (pit == 0) break; --pit; cpar = pars_[pit]; + if (layout.empty() && previous + && cpar.layout().isEnvironment() + && cpar.params().depth() <= split_depth) + layout = cpar.layout().name(); if (cpar.params().depth() < split_depth && cpar.layout().isEnvironment()) { - layout = cpar.layout().name(); + if (!previous) + layout = cpar.layout().name(); split_depth = cpar.params().depth(); } + if (cpar.params().depth() == 0) + break; } } - if (cur.pos() > 0) + if ((outer || normal) && cur.pit() < cur.lastpit()) { + // save nesting of following paragraph + Paragraph cpar = pars_[cur.pit() + 1]; + nextpar_depth = cpar.params().depth(); + } + if (before) + cur.top().setPitPos(cur.pit(), 0); + if (before || cur.pos() > 0) lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_BREAK)); + else if (previous && cur.nextInset() && cur.nextInset()->lyxCode() == SEPARATOR_CODE) + lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_BREAK, "inverse ignoresep")); if (outer) { while (cur.paragraph().params().depth() > split_depth) lyx::dispatch(FuncRequest(LFUN_DEPTH_DECREMENT)); } DocumentClass const & tc = bv->buffer().params().documentClass(); - lyx::dispatch(FuncRequest(LFUN_LAYOUT, tc.plainLayout().name())); + lyx::dispatch(FuncRequest(LFUN_LAYOUT, from_ascii("\"") + tc.plainLayout().name() + + from_ascii("\" ignoreautonests"))); lyx::dispatch(FuncRequest(LFUN_SEPARATOR_INSERT, "plain")); - lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_BREAK, "inverse")); + if (before) { + cur.backwardPos(); + lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_BREAK, "inverse ignoresep")); + while (cur.paragraph().params().depth() < split_depth) + lyx::dispatch(FuncRequest(LFUN_DEPTH_INCREMENT)); + } + else + lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_BREAK, "inverse")); lyx::dispatch(FuncRequest(LFUN_LAYOUT, layout)); + if ((outer || normal) && nextpar_depth > 0) { + // restore nesting of following paragraph + DocIterator scur = cur; + depth_type const max_depth = cur.paragraph().params().depth() + 1; + cur.forwardPar(); + while (cur.paragraph().params().depth() < min(nextpar_depth, max_depth)) { + depth_type const olddepth = cur.paragraph().params().depth(); + lyx::dispatch(FuncRequest(LFUN_DEPTH_INCREMENT)); + if (olddepth == cur.paragraph().params().depth()) + // leave loop if no incrementation happens + break; + } + cur.setCursor(scur); + } break; } @@ -1538,21 +1695,6 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) bv->buffer().errors("Paste"); break; - case LFUN_UNICODE_INSERT: { - if (cmd.argument().empty()) - break; - docstring hexstring = cmd.argument(); - if (isHex(hexstring)) { - char_type c = hexToInt(hexstring); - if (c >= 32 && c < 0x10ffff) { - lyxerr << "Inserting c: " << c << endl; - docstring s = docstring(1, c); - lyx::dispatch(FuncRequest(LFUN_SELF_INSERT, s)); - } - } - break; - } - case LFUN_QUOTE_INSERT: { cap::replaceSelection(cur); cur.recordUndo(); @@ -1563,26 +1705,44 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) while (pos > 0 && par.isDeleted(pos - 1)) --pos; - BufferParams const & bufparams = bv->buffer().params(); - bool const hebrew = - par.getFontSettings(bufparams, pos).language()->lang() == "hebrew"; - bool const allow_inset_quote = !(par.isPassThru() || hebrew); - - string const arg = to_utf8(cmd.argument()); - if (allow_inset_quote) { - char_type c = ' '; - if (pos > 0 && (!cur.prevInset() || !cur.prevInset()->isSpace())) + bool const inner = (cmd.getArg(0) == "single" || cmd.getArg(0) == "inner"); + + // Guess quote side. + // A space triggers an opening quote. This is passed if the preceding + // char/inset is a space or at paragraph start. + char_type c = ' '; + if (pos > 0 && !par.isSpace(pos - 1)) { + if (cur.prevInset() && cur.prevInset()->lyxCode() == QUOTE_CODE) { + // If an opening double quotation mark precedes, and this + // is a single quote, make it opening as well + InsetQuotes & ins = + static_cast(*cur.prevInset()); + string const type = ins.getType(); + if (!suffixIs(type, "ld") || !inner) + c = par.getChar(pos - 1); + } + else if (!cur.prevInset() + || (cur.prevInset() && cur.prevInset()->isChar())) + // If a char precedes, pass that and let InsetQuote decide c = par.getChar(pos - 1); - InsetQuotes::QuoteTimes const quote_type = (arg == "single") - ? InsetQuotes::SingleQuotes : InsetQuotes::DoubleQuotes; - cur.insert(new InsetQuotes(cur.buffer(), c, quote_type)); - cur.posForward(); - } else { - // The cursor might have been invalidated by the replaceSelection. - cur.buffer()->changed(true); - string const quote_string = (arg == "single") ? "'" : "\""; - lyx::dispatch(FuncRequest(LFUN_SELF_INSERT, quote_string)); + else { + while (pos > 0) { + if (par.getInset(pos - 1) + && !par.getInset(pos - 1)->isPartOfTextSequence()) { + // skip "invisible" insets + --pos; + continue; + } + c = par.getChar(pos - 1); + break; + } + } } + InsetQuotesParams::QuoteLevel const quote_level = inner + ? InsetQuotesParams::SecondaryQuotes : InsetQuotesParams::PrimaryQuotes; + cur.insert(new InsetQuotes(cur.buffer(), c, quote_level, cmd.getArg(1), cmd.getArg(2))); + cur.buffer()->updateBuffer(); + cur.posForward(); break; } @@ -1618,8 +1778,10 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) bvcur.setMark(false); switch (cmd.button()) { case mouse_button::button1: - // Set the cursor - if (!bv->mouseSetCursor(cur, cmd.argument() == "region-select")) + if (!bvcur.selection()) + // Set the cursor + bvcur.resetAnchor(); + if (!bv->mouseSetCursor(cur, cmd.modifier() == ShiftModifier)) cur.screenUpdateFlags(Update::SinglePar | Update::FitCursor); if (bvcur.wordSelection()) selectWord(bvcur, WHOLE_WORD); @@ -1645,7 +1807,7 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) return; } if (!bv->mouseSetCursor(cur, false)) - cur.screenUpdateFlags(Update::SinglePar | Update::FitCursor); + cur.screenUpdateFlags(Update::FitCursor); break; } @@ -1673,6 +1835,9 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) tm->setCursorFromCoordinates(cur, cmd.x(), y); cur.setTargetX(cmd.x()); + // Don't allow selecting a separator inset + if (cur.pos() && cur.paragraph().isEnvSeparator(cur.pos() - 1)) + cur.posBackward(); if (cmd.y() >= wh) lyx::dispatch(FuncRequest(LFUN_DOWN_SELECT)); else if (cmd.y() < 0) @@ -1687,7 +1852,8 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) // We continue with our existing selection or start a new one, so don't // reset the anchor. bvcur.setCursor(cur); - bvcur.setSelection(true); + bvcur.selection(true); + bvcur.setCurrentFont(); if (cur.top() == old) { // We didn't move one iota, so no need to update the screen. cur.screenUpdateFlags(Update::SinglePar | Update::FitCursor); @@ -1750,14 +1916,11 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) // true (on). if (lyxrc.auto_region_delete && cur.selection()) - cutSelection(cur, false, false); - + cutSelection(cur, false); cur.clearSelection(); - docstring::const_iterator cit = cmd.argument().begin(); - docstring::const_iterator const end = cmd.argument().end(); - for (; cit != end; ++cit) - bv->translateAndInsert(*cit, this, cur); + for (char_type c : cmd.argument()) + bv->translateAndInsert(c, this, cur); cur.resetAnchor(); moveCursor(cur, false); @@ -1767,13 +1930,9 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) } case LFUN_HREF_INSERT: { - // FIXME If we're actually given an argument, shouldn't - // we use it, whether or not we have a selection? docstring content = cmd.argument(); - if (cur.selection()) { + if (content.empty() && cur.selection()) content = cur.selectionAsString(false); - cutSelection(cur, true, false); - } InsetCommandParams p(HYPERLINK_CODE); if (!content.empty()){ @@ -1838,7 +1997,7 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) if (cmd.argument().empty() && cur.selection()) { // if command argument is empty use current selection as parameter. docstring ds = cur.selectionAsString(false); - cutSelection(cur, true, false); + cutSelection(cur, false); FuncRequest cmd0(cmd, ds); inset = createInset(cur.buffer(), cmd0); } else { @@ -1848,6 +2007,7 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) break; cur.recordUndo(); insertInset(cur, inset); + cur.forceBufferUpdate(); cur.posForward(); break; } @@ -2015,22 +2175,38 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) changeDepth(cur, INC_DEPTH); break; - case LFUN_MATH_DISPLAY: - mathDispatch(cur, cmd, true); - break; - case LFUN_REGEXP_MODE: regexpDispatch(cur, cmd); break; - case LFUN_MATH_MODE: - if (cmd.argument() == "on") + case LFUN_MATH_MODE: { + if (cmd.argument() == "on" || cmd.argument() == "") { // don't pass "on" as argument // (it would appear literally in the first cell) - mathDispatch(cur, FuncRequest(LFUN_MATH_MODE), false); - else - mathDispatch(cur, cmd, false); + docstring sel = cur.selectionAsString(false); + InsetMathMacroTemplate * macro = new InsetMathMacroTemplate(cur.buffer()); + // create a macro template if we see "\\newcommand" somewhere, and + // an ordinary formula otherwise + if (!sel.empty() + && (sel.find(from_ascii("\\newcommand")) != string::npos + || sel.find(from_ascii("\\newlyxcommand")) != string::npos + || sel.find(from_ascii("\\def")) != string::npos) + && macro->fromString(sel)) { + cur.recordUndo(); + replaceSelection(cur); + cur.insert(macro); + } else { + // no meaningful macro template was found + delete macro; + mathDispatch(cur,FuncRequest(LFUN_MATH_MODE)); + } + } else + // The argument is meaningful + // We replace cmd with LFUN_MATH_INSERT because LFUN_MATH_MODE + // has a different meaning in math mode + mathDispatch(cur, FuncRequest(LFUN_MATH_INSERT,cmd.argument())); break; + } case LFUN_MATH_MACRO: if (cmd.argument().empty()) @@ -2044,7 +2220,7 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) MacroType type = MacroTypeNewcommand; if (s2 == "def") type = MacroTypeDef; - MathMacroTemplate * inset = new MathMacroTemplate(cur.buffer(), + InsetMathMacroTemplate * inset = new InsetMathMacroTemplate(cur.buffer(), from_utf8(token(s, ' ', 0)), nargs, false, type); inset->setBuffer(bv->buffer()); insertInset(cur, inset); @@ -2053,32 +2229,21 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) cur.push(*inset); cur.top().pos() = cur.top().lastpos(); cur.resetAnchor(); - cur.setSelection(true); + cur.selection(true); cur.top().pos() = 0; } break; - // passthrough hat and underscore outside mathed: + case LFUN_MATH_DISPLAY: case LFUN_MATH_SUBSCRIPT: - mathDispatch(cur, FuncRequest(LFUN_SELF_INSERT, "_"), false); - break; case LFUN_MATH_SUPERSCRIPT: - mathDispatch(cur, FuncRequest(LFUN_SELF_INSERT, "^"), false); - break; - case LFUN_MATH_INSERT: case LFUN_MATH_AMS_MATRIX: case LFUN_MATH_MATRIX: case LFUN_MATH_DELIM: - case LFUN_MATH_BIGDELIM: { - cur.recordUndo(); - cap::replaceSelection(cur); - cur.insert(new InsetMathHull(cur.buffer(), hullSimple)); - checkAndActivateInset(cur, true); - LASSERT(cur.inMathed(), break); - cur.dispatch(cmd); + case LFUN_MATH_BIGDELIM: + mathDispatch(cur, cmd); break; - } case LFUN_FONT_EMPH: { Font font(ignore_font, ignore_language); @@ -2143,6 +2308,13 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) break; } + case LFUN_FONT_CROSSOUT: { + Font font(ignore_font, ignore_language); + font.fontInfo().setXout(FONT_TOGGLE); + toggleAndShow(cur, this, font); + break; + } + case LFUN_FONT_UNDERUNDERLINE: { Font font(ignore_font, ignore_language); font.fontInfo().setUuline(FONT_TOGGLE); @@ -2268,6 +2440,7 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) case LFUN_ACCENT_GRAVE: case LFUN_ACCENT_ACUTE: case LFUN_ACCENT_TILDE: + case LFUN_ACCENT_PERISPOMENI: case LFUN_ACCENT_CEDILLA: case LFUN_ACCENT_MACRON: case LFUN_ACCENT_DOT: @@ -2291,7 +2464,7 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) if (tclass.floats().typeExist(to_utf8(cmd.argument()))) { cur.recordUndo(); if (cur.selection()) - cutSelection(cur, true, false); + cutSelection(cur, false); breakParagraph(cur); if (cur.lastpos() != 0) { @@ -2439,7 +2612,7 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) case LFUN_ESCAPE: if (cur.selection()) { - cur.setSelection(false); + cur.selection(false); } else { cur.undispatched(); // This used to be LFUN_FINISHED_RIGHT, I think FORWARD is more @@ -2449,27 +2622,27 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) break; case LFUN_OUTLINE_UP: - outline(OutlineUp, cur); + outline(OutlineUp, cur, this); setCursor(cur, cur.pit(), 0); cur.forceBufferUpdate(); needsUpdate = true; break; case LFUN_OUTLINE_DOWN: - outline(OutlineDown, cur); + outline(OutlineDown, cur, this); setCursor(cur, cur.pit(), 0); cur.forceBufferUpdate(); needsUpdate = true; break; case LFUN_OUTLINE_IN: - outline(OutlineIn, cur); + outline(OutlineIn, cur, this); cur.forceBufferUpdate(); needsUpdate = true; break; case LFUN_OUTLINE_OUT: - outline(OutlineOut, cur); + outline(OutlineOut, cur, this); cur.forceBufferUpdate(); needsUpdate = true; break; @@ -2724,7 +2897,7 @@ bool Text::getStatus(Cursor & cur, FuncRequest const & cmd, case LFUN_CAPTION_INSERT: { code = CAPTION_CODE; string arg = cmd.getArg(0); - bool varia = arg != "LongTableNoNumber" + bool varia = arg != "Unnumbered" && cur.inset().allowsCaptionVariation(arg); // not allowed in description items, // and in specific insets @@ -2808,19 +2981,13 @@ bool Text::getStatus(Cursor & cur, FuncRequest const & cmd, for (; pit <= lastpit; ++pit) { if (pars_[pit].layout() != lay) break; - InsetList::const_iterator it = pars_[pit].insetList().begin(); - InsetList::const_iterator end = pars_[pit].insetList().end(); - for (; it != end; ++it) { - if (it->inset->lyxCode() == ARG_CODE) { - InsetArgument const * ins = - static_cast(it->inset); + for (auto const & table : pars_[pit].insetList()) + if (InsetArgument const * ins = table.inset->asInsetArgument()) if (ins->name() == arg) { // we have this already enable = false; break; } - } - } } } else enable = false; @@ -2864,6 +3031,7 @@ bool Text::getStatus(Cursor & cur, FuncRequest const & cmd, case LFUN_QUOTE_INSERT: // always allow this, since we will inset a raw quote // if an inset is not allowed. + allow_in_passthru = true; break; case LFUN_SPECIALCHAR_INSERT: code = SPECIALCHAR_CODE; @@ -3108,13 +3276,13 @@ bool Text::getStatus(Cursor & cur, FuncRequest const & cmd, break; case LFUN_LAYOUT: { - enable = !cur.inset().forcePlainLayout(); - - docstring layout = cmd.argument(); - if (layout.empty()) { - DocumentClass const & tclass = cur.buffer()->params().documentClass(); + DocumentClass const & tclass = cur.buffer()->params().documentClass(); + bool const ignoreautonests = cmd.getArg(1) == "ignoreautonests"; + docstring layout = ignoreautonests ? from_utf8(cmd.getArg(0)) : cmd.argument(); + if (layout.empty()) layout = tclass.defaultLayoutName(); - } + enable = !owner_->forcePlainLayout() && tclass.hasLayout(layout); + flag.setOnOff(layout == cur.paragraph().layout().name()); break; } @@ -3137,8 +3305,22 @@ bool Text::getStatus(Cursor & cur, FuncRequest const & cmd, enable = res; break; } + else if (cmd.argument() == "previous") { + // look if we have an environment in the previous par + pit_type pit = cur.pit(); + Paragraph cpar = pars_[pit]; + if (pit > 0) { + --pit; + cpar = pars_[pit]; + enable = cpar.layout().isEnvironment(); + break; + } + enable = false; + break; + } else if (cur.paragraph().layout().isEnvironment()) { - enable = true; + enable = cmd.argument() == "before" + || cur.pos() > 0 || !isFirstInSequence(cur.pit()); break; } enable = false; @@ -3149,10 +3331,13 @@ bool Text::getStatus(Cursor & cur, FuncRequest const & cmd, case LFUN_PARAGRAPH_PARAMS: case LFUN_PARAGRAPH_PARAMS_APPLY: case LFUN_PARAGRAPH_UPDATE: - enable = cur.inset().allowParagraphCustomization(); + enable = owner_->allowParagraphCustomization(); break; // FIXME: why are accent lfuns forbidden with pass_thru layouts? + // Because they insert COMBINING DIACRITICAL Unicode characters, + // that cannot be handled by LaTeX but must be converted according + // to the definition in lib/unicodesymbols? case LFUN_ACCENT_ACUTE: case LFUN_ACCENT_BREVE: case LFUN_ACCENT_CARON: @@ -3166,6 +3351,7 @@ bool Text::getStatus(Cursor & cur, FuncRequest const & cmd, case LFUN_ACCENT_OGONEK: case LFUN_ACCENT_TIE: case LFUN_ACCENT_TILDE: + case LFUN_ACCENT_PERISPOMENI: case LFUN_ACCENT_UMLAUT: case LFUN_ACCENT_UNDERBAR: case LFUN_ACCENT_UNDERDOT: @@ -3175,6 +3361,7 @@ bool Text::getStatus(Cursor & cur, FuncRequest const & cmd, case LFUN_FONT_STATE: case LFUN_FONT_UNDERLINE: case LFUN_FONT_STRIKEOUT: + case LFUN_FONT_CROSSOUT: case LFUN_FONT_UNDERUNDERLINE: case LFUN_FONT_UNDERWAVE: case LFUN_TEXTSTYLE_APPLY: