X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2FText3.cpp;h=2893e080f92800e49360e3af0bf3b8078cacb8b3;hb=14e01a92a4a8b3c861bec41d33a1743870844e63;hp=f3d25d49ebcfb8c1c10f569ea43260d12a1d2137;hpb=824d640227d92f01296d69e85761a790d328b488;p=lyx.git diff --git a/src/Text3.cpp b/src/Text3.cpp index f3d25d49eb..2893e080f9 100644 --- a/src/Text3.cpp +++ b/src/Text3.cpp @@ -51,6 +51,7 @@ #include "frontends/Clipboard.h" #include "frontends/Selection.h" +#include "insets/InsetArgument.h" #include "insets/InsetCollapsable.h" #include "insets/InsetCommand.h" #include "insets/InsetExternal.h" @@ -58,6 +59,7 @@ #include "insets/InsetFloatList.h" #include "insets/InsetGraphics.h" #include "insets/InsetGraphicsParams.h" +#include "insets/InsetIPAMacro.h" #include "insets/InsetNewline.h" #include "insets/InsetQuotes.h" #include "insets/InsetSpecialChar.h" @@ -227,6 +229,15 @@ static void specialChar(Cursor & cur, InsetSpecialChar::Kind kind) } +static void ipaChar(Cursor & cur, InsetIPAChar::Kind kind) +{ + cur.recordUndo(); + cap::replaceSelection(cur); + cur.insert(new InsetIPAChar(kind)); + cur.posForward(); +} + + static bool doInsertInset(Cursor & cur, Text * text, FuncRequest const & cmd, bool edit, bool pastesel) { @@ -247,8 +258,10 @@ static bool doInsertInset(Cursor & cur, Text * text, inset->edit(cur, true); // Now put this into inset Font const f(inherit_font, cur.current_font.language()); - cur.text()->insertStringAsLines(cur, ds, f); - cur.leaveInset(*inset); + if (!ds.empty()) { + cur.text()->insertStringAsLines(cur, ds, f); + cur.leaveInset(*inset); + } return true; } @@ -282,7 +295,14 @@ static bool doInsertInset(Cursor & cur, Text * text, // Merge multiple paragraphs -- hack while (cur.lastpit() > 0) mergeParagraph(bparams, cur.text()->paragraphs(), 0); + if (cmd.action() == LFUN_FLEX_INSERT) + return true; + Cursor old = cur; cur.leaveInset(*inset); + if (cmd.action() == LFUN_PREVIEW_INSERT + || cmd.action() == LFUN_IPA_INSERT) + // trigger preview + notifyCursorLeavesOrEnters(old, cur); } } else { cur.leaveInset(*inset); @@ -326,7 +346,7 @@ static void outline(OutlineOp mode, Cursor & cur) DocumentClass const & tc = buf.params().documentClass(); - int const thistoclevel = start->layout().toclevel; + int const thistoclevel = buf.text().getTocLevel(distance(bgn, start)); int toclevel; // Move out (down) from this section header @@ -335,7 +355,7 @@ static void outline(OutlineOp mode, Cursor & cur) // Seek the one (on same level) below for (; finish != end; ++finish) { - toclevel = finish->layout().toclevel; + toclevel = buf.text().getTocLevel(distance(bgn, finish)); if (toclevel != Layout::NOT_IN_TOC && toclevel <= thistoclevel) break; } @@ -352,7 +372,7 @@ static void outline(OutlineOp mode, Cursor & cur) // Search previous same-level header above do { --dest; - toclevel = dest->layout().toclevel; + toclevel = buf.text().getTocLevel(distance(bgn, dest)); } while(dest != bgn && (toclevel == Layout::NOT_IN_TOC || toclevel > thistoclevel)); @@ -375,7 +395,7 @@ static void outline(OutlineOp mode, Cursor & cur) ParagraphList::iterator dest = boost::next(finish, 1); // Go further down to find header to insert in front of: for (; dest != end; ++dest) { - toclevel = dest->layout().toclevel; + toclevel = buf.text().getTocLevel(distance(bgn, dest)); if (toclevel != Layout::NOT_IN_TOC && toclevel <= thistoclevel) break; @@ -392,7 +412,7 @@ static void outline(OutlineOp mode, Cursor & cur) pit_type const len = distance(start, finish); buf.undo().recordUndo(cur, ATOMIC_UNDO, pit, pit + len - 1); for (; start != finish; ++start) { - toclevel = start->layout().toclevel; + toclevel = buf.text().getTocLevel(distance(bgn, start)); if (toclevel == Layout::NOT_IN_TOC) continue; DocumentClass::const_iterator lit = tc.begin(); @@ -411,7 +431,7 @@ static void outline(OutlineOp mode, Cursor & cur) pit_type const len = distance(start, finish); buf.undo().recordUndo(cur, ATOMIC_UNDO, pit, pit + len - 1); for (; start != finish; ++start) { - toclevel = start->layout().toclevel; + toclevel = buf.text().getTocLevel(distance(bgn, start)); if (toclevel == Layout::NOT_IN_TOC) continue; DocumentClass::const_iterator lit = tc.begin(); @@ -628,8 +648,12 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) // provide it with two different cursors. Cursor dummy = cur; dummy.pos() = dummy.pit() = 0; - if (cur.bv().checkDepm(dummy, cur)) + if (cur.bv().checkDepm(dummy, cur)) { cur.forceBufferUpdate(); + // DEPM may have requested a screen update + cur.screenUpdateFlags( + cur.screenUpdate() | dummy.screenUpdate()); + } } } break; @@ -655,8 +679,12 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) Cursor dummy = cur; dummy.pos() = cur.lastpos(); dummy.pit() = cur.lastpit(); - if (cur.bv().checkDepm(dummy, cur)) + if (cur.bv().checkDepm(dummy, cur)) { cur.forceBufferUpdate(); + // DEPM may have requested a screen update + cur.screenUpdateFlags( + cur.screenUpdate() | dummy.screenUpdate()); + } } } break; @@ -772,7 +800,7 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) ParagraphList::iterator finish = start; ParagraphList::iterator end = pars.end(); - int const thistoclevel = start->layout().toclevel; + int const thistoclevel = buf.text().getTocLevel(distance(bgn, start)); if (thistoclevel == Layout::NOT_IN_TOC) break; @@ -786,7 +814,7 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) // Seek the one (on same level) below for (; finish != end; ++finish, ++cur.pit()) { - int const toclevel = finish->layout().toclevel; + int const toclevel = buf.text().getTocLevel(distance(bgn, finish)); if (toclevel != Layout::NOT_IN_TOC && toclevel <= thistoclevel) break; } @@ -839,8 +867,12 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) // provide it with two different cursors. Cursor dummy = cur; dummy.pos() = dummy.pit() = 0; - if (cur.bv().checkDepm(dummy, cur)) + if (cur.bv().checkDepm(dummy, cur)) { cur.forceBufferUpdate(); + // DEPM may have requested a screen update + cur.screenUpdateFlags( + cur.screenUpdate() | dummy.screenUpdate()); + } } } break; @@ -889,8 +921,12 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) Cursor dummy = cur; dummy.pos() = cur.lastpos(); dummy.pit() = cur.lastpit(); - if (cur.bv().checkDepm(dummy, cur)) + if (cur.bv().checkDepm(dummy, cur)) { cur.forceBufferUpdate(); + // DEPM may have requested a screen update + cur.screenUpdateFlags( + cur.screenUpdate() | dummy.screenUpdate()); + } } } break; @@ -904,15 +940,12 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) case LFUN_NEWLINE_INSERT: { InsetNewlineParams inp; docstring arg = cmd.argument(); - // this avoids a double undo - // FIXME: should not be needed, ideally - if (!cur.selection()) - cur.recordUndo(); - cap::replaceSelection(cur); if (arg == "linebreak") inp.kind = InsetNewlineParams::LINEBREAK; else inp.kind = InsetNewlineParams::NEWLINE; + cap::replaceSelection(cur); + cur.recordUndo(); cur.insert(new InsetNewline(inp)); cur.posForward(); moveCursor(cur, false); @@ -1035,7 +1068,7 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) } break; - case LFUN_BREAK_PARAGRAPH: + case LFUN_PARAGRAPH_BREAK: cap::replaceSelection(cur); breakParagraph(cur, cmd.argument() == "inverse"); cur.resetAnchor(); @@ -1156,6 +1189,35 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) break; } + case LFUN_IPAMACRO_INSERT: { + string const arg = cmd.getArg(0); + if (arg == "deco") { + // Open the inset, and move the current selection + // inside it. + doInsertInset(cur, this, cmd, true, true); + cur.posForward(); + // Some insets are numbered, others are shown in the outline pane so + // let's update the labels and the toc backend. + cur.forceBufferUpdate(); + break; + } + if (arg == "tone-falling") + ipaChar(cur, InsetIPAChar::TONE_FALLING); + else if (arg == "tone-rising") + ipaChar(cur, InsetIPAChar::TONE_RISING); + else if (arg == "tone-high-rising") + ipaChar(cur, InsetIPAChar::TONE_HIGH_RISING); + else if (arg == "tone-low-rising") + ipaChar(cur, InsetIPAChar::TONE_LOW_RISING); + else if (arg == "tone-high-rising-falling") + ipaChar(cur, InsetIPAChar::TONE_HIGH_RISING_FALLING); + else if (arg.empty()) + lyxerr << "LyX function 'ipamacro-insert' needs an argument." << endl; + else + lyxerr << "Wrong argument for LyX function 'ipamacro-insert'." << endl; + break; + } + case LFUN_WORD_UPCASE: changeCase(cur, text_uppercase); break; @@ -1309,22 +1371,49 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) if (change_layout) setLayout(cur, layout); + Layout::LaTeXArgMap args = tclass[layout].args(); + Layout::LaTeXArgMap::const_iterator lait = args.begin(); + Layout::LaTeXArgMap::const_iterator const laend = args.end(); + for (; lait != laend; ++lait) { + Layout::latexarg arg = (*lait).second; + if (arg.autoinsert) { + FuncRequest cmd(LFUN_ARGUMENT_INSERT, (*lait).first); + lyx::dispatch(cmd); + } + } + + break; + } + + case LFUN_ENVIRONMENT_SPLIT: { + Paragraph const & para = cur.paragraph(); + docstring const layout = para.layout().name(); + if (cur.pos() > 0) + lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_BREAK)); + bool const morecont = cur.lastpos() > cur.pos(); + lyx::dispatch(FuncRequest(LFUN_LAYOUT, "Separator")); + lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_BREAK, "inverse")); + if (morecont) + lyx::dispatch(FuncRequest(LFUN_DOWN)); + lyx::dispatch(FuncRequest(LFUN_LAYOUT, layout)); + break; } case LFUN_CLIPBOARD_PASTE: - cur.clearSelection(); + cap::replaceSelection(cur); pasteClipboardText(cur, bv->buffer().errorList("Paste"), cmd.argument() == "paragraph"); bv->buffer().errors("Paste"); break; case LFUN_CLIPBOARD_PASTE_SIMPLE: - cur.clearSelection(); + cap::replaceSelection(cur); pasteSimpleText(cur, cmd.argument() == "paragraph"); break; case LFUN_PRIMARY_SELECTION_PASTE: + cap::replaceSelection(cur); pasteString(cur, theSelection().get(), cmd.argument() == "paragraph"); break; @@ -1333,6 +1422,7 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) // Copy the selection buffer to the clipboard stack, // because we want it to appear in the "Edit->Paste // recent" menu. + cap::replaceSelection(cur); cap::copySelectionToStack(); cap::pasteSelection(bv->cursor(), bv->buffer().errorList("Paste")); bv->buffer().errors("Paste"); @@ -1354,14 +1444,14 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) } case LFUN_QUOTE_INSERT: { - // this avoids a double undo - // FIXME: should not be needed, ideally - if (!cur.selection()) - cur.recordUndo(); cap::replaceSelection(cur); + cur.recordUndo(); Paragraph const & par = cur.paragraph(); pos_type pos = cur.pos(); + // Ignore deleted text before cursor + while (pos > 0 && par.isDeleted(pos - 1)) + --pos; BufferParams const & bufparams = bv->buffer().params(); bool const hebrew = @@ -1620,7 +1710,6 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) case LFUN_CAPTION_INSERT: case LFUN_FOOTNOTE_INSERT: case LFUN_NOTE_INSERT: - case LFUN_FLEX_INSERT: case LFUN_BOX_INSERT: case LFUN_BRANCH_INSERT: case LFUN_PHANTOM_INSERT: @@ -1631,6 +1720,7 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) case LFUN_INDEX_INSERT: case LFUN_PREVIEW_INSERT: case LFUN_SCRIPT_INSERT: + case LFUN_IPA_INSERT: // Open the inset, and move the current selection // inside it. doInsertInset(cur, this, cmd, true, true); @@ -1640,6 +1730,37 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) cur.forceBufferUpdate(); break; + case LFUN_FLEX_INSERT: { + // Open the inset, and move the current selection + // inside it. + bool const sel = cur.selection(); + doInsertInset(cur, this, cmd, true, true); + // Insert auto-insert arguments + bool autoargs = false; + Layout::LaTeXArgMap args = cur.inset().getLayout().latexargs(); + Layout::LaTeXArgMap::const_iterator lait = args.begin(); + Layout::LaTeXArgMap::const_iterator const laend = args.end(); + for (; lait != laend; ++lait) { + Layout::latexarg arg = (*lait).second; + if (arg.autoinsert) { + // The cursor might have been invalidated by the replaceSelection. + cur.buffer()->changed(true); + FuncRequest cmd(LFUN_ARGUMENT_INSERT, (*lait).first); + lyx::dispatch(cmd); + autoargs = true; + } + } + if (!autoargs) { + if (sel) + cur.leaveInset(cur.inset()); + cur.posForward(); + } + // Some insets are numbered, others are shown in the outline pane so + // let's update the labels and the toc backend. + cur.forceBufferUpdate(); + break; + } + case LFUN_TABULAR_INSERT: // if there were no arguments, just open the dialog if (doInsertInset(cur, this, cmd, false, true)) @@ -1652,16 +1773,19 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) case LFUN_FLOAT_INSERT: case LFUN_FLOAT_WIDE_INSERT: case LFUN_WRAP_INSERT: { - // will some text be moved into the inset? - bool content = cur.selection(); + // will some content be moved into the inset? + bool const content = cur.selection(); + // does the content consist of multiple paragraphs? + bool const singlepar = (cur.selBegin().pit() == cur.selEnd().pit()); doInsertInset(cur, this, cmd, true, true); cur.posForward(); - // If some text is moved into the inset, doInsertInset - // puts the cursor outside the inset. To insert the - // caption we put it back into the inset. - if (content) + // If some single-par content is moved into the inset, + // doInsertInset puts the cursor outside the inset. + // To insert the caption we put it back into the inset. + // FIXME cleanup doInsertInset to avoid such dances! + if (content && singlepar) cur.backwardPos(); ParagraphList & pars = cur.text()->paragraphs(); @@ -1894,12 +2018,19 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) } case LFUN_LANGUAGE: { - Language const * lang = languages.getLanguage(to_utf8(cmd.argument())); - if (!lang) + string const lang_arg = cmd.getArg(0); + bool const reset = (lang_arg.empty() || lang_arg == "reset"); + Language const * lang = + reset ? reset_language + : languages.getLanguage(lang_arg); + // we allow reset_language, which is 0, but only if it + // was requested via empty or "reset" arg. + if (!lang && !reset) break; + bool const toggle = (cmd.getArg(1) != "set"); selectWordWhenUnderCursor(cur, WHOLE_WORD_STRICT); Font font(ignore_font, lang); - toggleAndShow(cur, this, font, false); + toggleAndShow(cur, this, font, toggle); break; } @@ -2245,6 +2376,7 @@ bool Text::getStatus(Cursor & cur, FuncRequest const & cmd, FontInfo const & fontinfo = cur.real_current_font.fontInfo(); bool enable = true; + bool allow_in_passthru = false; InsetCode code = NO_CODE; switch (cmd.action()) { @@ -2258,6 +2390,9 @@ bool Text::getStatus(Cursor & cur, FuncRequest const & cmd, break; case LFUN_APPENDIX: + // FIXME We really should not allow this to be put, e.g., + // in a footnote, or in ERT. But it would make sense in a + // branch, so I'm not sure what to do. flag.setOnOff(cur.paragraph().params().startOfAppendix()); break; @@ -2390,11 +2525,21 @@ bool Text::getStatus(Cursor & cur, FuncRequest const & cmd, } break; } - case LFUN_CAPTION_INSERT: + case LFUN_CAPTION_INSERT: { code = CAPTION_CODE; - // not allowed in description items - enable = !inDescriptionItem(cur); + bool varia = true; + if (cur.depth() > 0) { + if (&cur[cur.depth() - 1].inset() + && !cur[cur.depth() - 1].inset().allowsCaptionVariation()) + varia = false; + } + string arg = cmd.getArg(0); + // not allowed in description items, + // and in specific insets + enable = !inDescriptionItem(cur) + && (varia || arg.empty() || arg == "Standard"); break; + } case LFUN_NOTE_INSERT: code = NOTE_CODE; // in commands (sections etc.) and description items, @@ -2424,6 +2569,9 @@ bool Text::getStatus(Cursor & cur, FuncRequest const & cmd, && cur.buffer()->params().branchlist().empty()) enable = false; break; + case LFUN_IPA_INSERT: + code = IPA_CODE; + break; case LFUN_PHANTOM_INSERT: code = PHANTOM_CODE; break; @@ -2435,9 +2583,55 @@ bool Text::getStatus(Cursor & cur, FuncRequest const & cmd, break; case LFUN_ARGUMENT_INSERT: { code = ARG_CODE; + allow_in_passthru = true; + string const arg = cmd.getArg(0); + if (arg.empty()) { + enable = false; + break; + } Layout const & lay = cur.paragraph().layout(); - int const numargs = lay.reqargs + lay.optargs; - enable = cur.paragraph().insetList().count(ARG_CODE) < numargs; + Layout::LaTeXArgMap args = lay.args(); + Layout::LaTeXArgMap::const_iterator const lait = + args.find(arg); + if (lait != args.end()) { + enable = true; + pit_type pit = cur.pit(); + pit_type lastpit = cur.pit(); + if (lay.isEnvironment() && !prefixIs(arg, "item:")) { + // In a sequence of "merged" environment layouts, we only allow + // non-item arguments once. + lastpit = cur.lastpit(); + // get the first paragraph in sequence with this layout + depth_type const current_depth = cur.paragraph().params().depth(); + while (true) { + if (pit == 0) + break; + Paragraph cpar = pars_[pit - 1]; + if (cpar.layout() == lay && cpar.params().depth() == current_depth) + --pit; + else + break; + } + } + 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); + if (ins->name() == arg) { + // we have this already + enable = false; + break; + } + } + } + } + } else + enable = false; break; } case LFUN_INDEX_INSERT: @@ -2467,6 +2661,14 @@ bool Text::getStatus(Cursor & cur, FuncRequest const & cmd, } code = HYPERLINK_CODE; break; + case LFUN_IPAMACRO_INSERT: { + string const arg = cmd.getArg(0); + if (arg == "deco") + code = IPADECO_CODE; + else + code = IPACHAR_CODE; + break; + } case LFUN_QUOTE_INSERT: // always allow this, since we will inset a raw quote // if an inset is not allowed. @@ -2630,7 +2832,7 @@ bool Text::getStatus(Cursor & cur, FuncRequest const & cmd, case LFUN_OUTLINE_OUT: // FIXME: LyX is not ready for outlining within inset. enable = isMainText() - && cur.paragraph().layout().toclevel != Layout::NOT_IN_TOC; + && cur.buffer()->text().getTocLevel(cur.pit()) != Layout::NOT_IN_TOC; break; case LFUN_NEWLINE_INSERT: @@ -2667,10 +2869,10 @@ bool Text::getStatus(Cursor & cur, FuncRequest const & cmd, case LFUN_LANGUAGE: enable = !cur.paragraph().isPassThru(); - flag.setOnOff(to_utf8(cmd.argument()) == cur.real_current_font.language()->lang()); + flag.setOnOff(cmd.getArg(0) == cur.real_current_font.language()->lang()); break; - case LFUN_BREAK_PARAGRAPH: + case LFUN_PARAGRAPH_BREAK: enable = cur.inset().getLayout().isMultiPar(); break; @@ -2683,7 +2885,17 @@ bool Text::getStatus(Cursor & cur, FuncRequest const & cmd, case LFUN_LAYOUT: enable = !cur.inset().forcePlainLayout(); break; - + + case LFUN_ENVIRONMENT_SPLIT: { + if (cur.paragraph().layout().isEnvironment() + && cur.buffer()->params().documentClass().hasLayout(from_ascii("Separator"))) { + enable = true; + break; + } + enable = false; + break; + } + case LFUN_LAYOUT_PARAGRAPH: case LFUN_PARAGRAPH_PARAMS: case LFUN_PARAGRAPH_PARAMS_APPLY: @@ -2800,7 +3012,7 @@ bool Text::getStatus(Cursor & cur, FuncRequest const & cmd, if (code != NO_CODE && (cur.empty() || !cur.inset().insetAllowed(code) - || cur.paragraph().layout().pass_thru)) + || (cur.paragraph().layout().pass_thru && !allow_in_passthru))) enable = false; flag.setEnabled(enable); @@ -2811,7 +3023,6 @@ bool Text::getStatus(Cursor & cur, FuncRequest const & cmd, void Text::pasteString(Cursor & cur, docstring const & clip, bool asParagraphs) { - cur.clearSelection(); if (!clip.empty()) { cur.recordUndo(); if (asParagraphs)