X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2FText3.cpp;h=6e6e9e6f3b57fdabc296757dce530de5d6e7495d;hb=3d4076b598deb18660e50ec9c327efc3b15f15d0;hp=4dd686a19624c6fb2241f50f24102109662ffb65;hpb=6a80ce62cab3b2623479da70fa90e65db346d152;p=lyx.git diff --git a/src/Text3.cpp b/src/Text3.cpp index 4dd686a196..6e6e9e6f3b 100644 --- a/src/Text3.cpp +++ b/src/Text3.cpp @@ -45,13 +45,13 @@ #include "SpellChecker.h" #include "TextClass.h" #include "TextMetrics.h" -#include "VSpace.h" #include "WordLangTuple.h" #include "frontends/Application.h" #include "frontends/Clipboard.h" #include "frontends/Selection.h" +#include "insets/InsetArgument.h" #include "insets/InsetCollapsable.h" #include "insets/InsetCommand.h" #include "insets/InsetExternal.h" @@ -59,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" @@ -68,6 +69,7 @@ #include "support/convert.h" #include "support/debug.h" #include "support/gettext.h" +#include "support/lassert.h" #include "support/lstrings.h" #include "support/lyxtime.h" #include "support/os.h" @@ -93,6 +95,7 @@ using cap::pasteClipboardGraphics; using cap::replaceSelection; using cap::grabAndEraseSelection; using cap::selClearOrDel; +using cap::pasteSimpleText; // globals... static Font freefont(ignore_font, ignore_language); @@ -145,7 +148,7 @@ static void mathDispatch(Cursor & cur, FuncRequest const & cmd, bool display) #endif cur.insert(new InsetMathHull(cur.buffer(), hullSimple)); #ifdef ENABLE_ASSERTIONS - LASSERT(old_pos == cur.pos(), /**/); + LATTEST(old_pos == cur.pos()); #endif cur.nextInset()->edit(cur, true); // don't do that also for LFUN_MATH_MODE @@ -226,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) { @@ -245,8 +257,11 @@ static bool doInsertInset(Cursor & cur, Text * text, if (edit) inset->edit(cur, true); // Now put this into inset - cur.text()->insertStringAsLines(cur, ds, cur.current_font); - cur.leaveInset(*inset); + Font const f(inherit_font, cur.current_font.language()); + if (!ds.empty()) { + cur.text()->insertStringAsLines(cur, ds, f); + cur.leaveInset(*inset); + } return true; } @@ -268,10 +283,10 @@ static bool doInsertInset(Cursor & cur, Text * text, cur.buffer()->errors("Paste"); cur.clearSelection(); // bug 393 cur.finishUndo(); - InsetText * insetText = dynamic_cast(inset); - if (insetText) { - insetText->fixParagraphsFont(); - if (!insetText->allowMultiPar() || cur.lastpit() == 0) { + InsetText * inset_text = inset->asInsetText(); + if (inset_text) { + inset_text->fixParagraphsFont(); + if (!inset_text->allowMultiPar() || cur.lastpit() == 0) { // reset first par to default cur.text()->paragraphs().begin() ->setPlainOrDefaultLayout(bparams.documentClass()); @@ -280,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); @@ -324,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 @@ -333,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; } @@ -350,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)); @@ -373,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; @@ -390,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(); @@ -409,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(); @@ -466,10 +488,10 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) // at the end? cur.noScreenUpdate(); - LASSERT(cur.text() == this, /**/); - CursorSlice oldTopSlice = cur.top(); - bool oldBoundary = cur.boundary(); - bool sel = cur.selection(); + LBUFERR(this == cur.text()); + CursorSlice const oldTopSlice = cur.top(); + bool const oldBoundary = cur.boundary(); + bool const oldSelection = cur.selection(); // Signals that, even if needsUpdate == false, an update of the // cursor paragraph is required bool singleParUpdate = lyxaction.funcHasFlag(cmd.action(), @@ -477,7 +499,9 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) // Signals that a full-screen update is required bool needsUpdate = !(lyxaction.funcHasFlag(cmd.action(), LyXAction::NoUpdate) || singleParUpdate); - + bool const last_misspelled = lyxrc.spellcheck_continuously + && cur.paragraph().isMisspelled(cur.pos(), true); + FuncCode const act = cmd.action(); switch (act) { @@ -624,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)) - cur.forceBufferUpdate();; + if (cur.bv().checkDepm(dummy, cur)) { + cur.forceBufferUpdate(); + // DEPM may have requested a screen update + cur.screenUpdateFlags( + cur.screenUpdate() | dummy.screenUpdate()); + } } } break; @@ -651,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; @@ -768,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; @@ -782,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; } @@ -819,6 +851,30 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) case LFUN_WORD_FORWARD_SELECT: needsUpdate |= cur.selHandle(cmd.action() == LFUN_WORD_FORWARD_SELECT); needsUpdate |= cursorForwardOneWord(cur); + + if (!needsUpdate && oldTopSlice == cur.top() + && cur.boundary() == oldBoundary) { + cur.undispatched(); + cmd = FuncRequest(LFUN_FINISHED_FORWARD); + + // we will probably be moving out the inset, so we should execute + // the depm-mechanism, but only when the cursor has a place to + // go outside this inset, i.e. in a slice above. + if (cur.depth() > 1 && cur.pos() == cur.lastpos() + && cur.pit() == cur.lastpit()) { + // The cursor hasn't changed yet. To give the + // DEPM the possibility of doing something we must + // provide it with two different cursors. + Cursor dummy = cur; + dummy.pos() = dummy.pit() = 0; + if (cur.bv().checkDepm(dummy, cur)) { + cur.forceBufferUpdate(); + // DEPM may have requested a screen update + cur.screenUpdateFlags( + cur.screenUpdate() | dummy.screenUpdate()); + } + } + } break; case LFUN_WORD_LEFT: @@ -848,6 +904,31 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) case LFUN_WORD_BACKWARD_SELECT: needsUpdate |= cur.selHandle(cmd.action() == LFUN_WORD_BACKWARD_SELECT); needsUpdate |= cursorBackwardOneWord(cur); + + if (!needsUpdate && oldTopSlice == cur.top() + && cur.boundary() == oldBoundary) { + cur.undispatched(); + cmd = FuncRequest(LFUN_FINISHED_BACKWARD); + + // we will probably be moving out the inset, so we should execute + // the depm-mechanism, but only when the cursor has a place to + // go outside this inset, i.e. in a slice above. + if (cur.depth() > 1 && cur.pos() == 0 + && cur.pit() == 0) { + // The cursor hasn't changed yet. To give the + // DEPM the possibility of doing something we must + // provide it with two different cursors. + Cursor dummy = cur; + dummy.pos() = cur.lastpos(); + dummy.pit() = cur.lastpit(); + if (cur.bv().checkDepm(dummy, cur)) { + cur.forceBufferUpdate(); + // DEPM may have requested a screen update + cur.screenUpdateFlags( + cur.screenUpdate() | dummy.screenUpdate()); + } + } + } break; case LFUN_WORD_SELECT: { @@ -859,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); @@ -911,24 +989,22 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) pit_type const pit_end = cur.selEnd().pit(); for (pit_type pit = cur.selBegin().pit(); pit <= pit_end; pit++) { Paragraph & par = paragraphs()[pit]; - if (par.getChar(0) == '\t') { - if (cur.pit() == pit) - cur.posBackward(); - if (cur.realAnchor().pit() == pit && cur.realAnchor().pos() > 0 ) - cur.realAnchor().backwardPos(); - - par.eraseChar(0, tc); - } else - // If no tab was present, try to remove up to four spaces. - for (int n_spaces = 0; - par.getChar(0) == ' ' && n_spaces < 4; ++n_spaces) { + if (par.empty()) + continue; + char_type const c = par.getChar(0); + if (c == '\t' || c == ' ') { + // remove either 1 tab or 4 spaces. + int const n = (c == ' ' ? 4 : 1); + for (int i = 0; i < n + && !par.empty() && par.getChar(0) == c; ++i) { if (cur.pit() == pit) cur.posBackward(); - if (cur.realAnchor().pit() == pit && cur.realAnchor().pos() > 0 ) + if (cur.realAnchor().pit() == pit + && cur.realAnchor().pos() > 0 ) cur.realAnchor().backwardPos(); - par.eraseChar(0, tc); } + } } cur.finishUndo(); } else { @@ -973,7 +1049,6 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) singleParUpdate = false; } moveCursor(cur, false); - cur.forceBufferUpdate(); break; case LFUN_CHAR_DELETE_BACKWARD: @@ -991,10 +1066,9 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) cutSelection(cur, true, false); singleParUpdate = false; } - cur.forceBufferUpdate(); break; - case LFUN_BREAK_PARAGRAPH: + case LFUN_PARAGRAPH_BREAK: cap::replaceSelection(cur); breakParagraph(cur, cmd.argument() == "inverse"); cur.resetAnchor(); @@ -1023,7 +1097,7 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) /* Paragraph & par = pars_[cur.pit()]; if (inset->lyxCode() == LABEL_CODE - && par.layout().labeltype == LABEL_COUNTER) { + && !par.layout().counter.empty() { // Go to the end of the paragraph // Warning: Because of Change-Tracking, the last // position is 'size()' and not 'size()-1': @@ -1036,7 +1110,10 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) if (cur.selection()) cutSelection(cur, true, false); cur.insert(inset); - cur.posForward(); + if (inset->editable() && inset->asInsetText()) + inset->edit(cur, true); + else + cur.posForward(); // trigger InstantPreview now if (inset->lyxCode() == EXTERNAL_CODE) { @@ -1112,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; @@ -1130,7 +1236,7 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) case LFUN_PASTE: { cur.message(_("Paste")); - LASSERT(cur.selBegin().idx() == cur.selEnd().idx(), /**/); + LASSERT(cur.selBegin().idx() == cur.selEnd().idx(), break); cap::replaceSelection(cur); // without argument? @@ -1142,11 +1248,15 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) && !theClipboard().hasTextContents()) pasteClipboardGraphics(cur, bv->buffer().errorList("Paste")); else - pasteClipboardText(cur, bv->buffer().errorList("Paste")); + pasteClipboardText(cur, bv->buffer().errorList("Paste"), true); } else if (isStrUnsignedInt(arg)) { // we have a numerical argument pasteFromStack(cur, bv->buffer().errorList("Paste"), convert(arg)); + } else if (arg == "html" || arg == "latex") { + Clipboard::TextType type = (arg == "html") ? + Clipboard::HtmlTextType : Clipboard::LaTeXTextType; + pasteClipboardText(cur, bv->buffer().errorList("Paste"), true, type); } else { Clipboard::GraphicsType type = Clipboard::AnyGraphicsType; if (arg == "pdf") @@ -1161,9 +1271,10 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) type = Clipboard::EmfGraphicsType; else if (arg == "wmf") type = Clipboard::WmfGraphicsType; - else - LASSERT(false, /**/); + // We used to assert, but couldn't the argument come from, say, the + // minibuffer and just be mistyped? + LYXERR0("Unrecognized graphics type: " << arg); pasteClipboardGraphics(cur, bv->buffer().errorList("Paste"), type); } @@ -1265,17 +1376,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: + 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; @@ -1284,6 +1427,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"); @@ -1305,33 +1449,34 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) } case LFUN_QUOTE_INSERT: { - Paragraph & par = cur.paragraph(); + 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(); - Layout const & style = par.layout(); - InsetLayout const & ilayout = cur.inset().getLayout(); - if (!style.pass_thru && !ilayout.isPassThru() - && par.getFontSettings(bufparams, pos).language()->lang() != "hebrew") { - // this avoids a double undo - // FIXME: should not be needed, ideally - if (!cur.selection()) - cur.recordUndo(); - cap::replaceSelection(cur); - pos = cur.pos(); - char_type c; - if (pos == 0) - c = ' '; - else if (cur.prevInset() && cur.prevInset()->isSpace()) - c = ' '; - else + bool const hebrew = + par.getFontSettings(bufparams, pos).language()->lang() == "hebrew"; + bool const allow_inset_quote = !(par.isPassThru() || hebrew); + + if (allow_inset_quote) { + char_type c = ' '; + if (pos > 0 && (!cur.prevInset() || !cur.prevInset()->isSpace())) c = par.getChar(pos - 1); - string arg = to_utf8(cmd.argument()); - cur.insert(new InsetQuotes(cur.buffer(), c, (arg == "single") - ? InsetQuotes::SingleQuotes : InsetQuotes::DoubleQuotes)); + string const arg = to_utf8(cmd.argument()); + InsetQuotes::QuoteTimes const quote_type = (arg == "single") + ? InsetQuotes::SingleQuotes : InsetQuotes::DoubleQuotes; + cur.insert(new InsetQuotes(cur.buffer(), c, quote_type)); cur.posForward(); - } - else + } else { + // The cursor might have been invalidated by the replaceSelection. + cur.buffer()->changed(true); lyx::dispatch(FuncRequest(LFUN_SELF_INSERT, "\"")); + } break; } @@ -1508,6 +1653,7 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) cur.resetAnchor(); moveCursor(cur, false); + cur.markNewWordPosition(); bv->bookmarkEditPosition(); break; } @@ -1521,7 +1667,7 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) } p["target"] = (cmd.argument().empty()) ? content : cmd.argument(); - string const data = InsetCommand::params2string("href", p); + string const data = InsetCommand::params2string(p); if (p["target"].empty()) { bv->showDialog("href", data); } else { @@ -1537,7 +1683,7 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) p["name"] = (cmd.argument().empty()) ? cur.getPossibleLabel() : cmd.argument(); - string const data = InsetCommand::params2string("label", p); + string const data = InsetCommand::params2string(p); if (cmd.argument().empty()) { bv->showDialog("label", data); @@ -1569,7 +1715,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: @@ -1579,6 +1724,8 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) case LFUN_ARGUMENT_INSERT: 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); @@ -1588,6 +1735,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)) @@ -1600,16 +1778,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(); @@ -1653,7 +1834,7 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) p["symbol"] = bv->cursor().innerText()->getStringToIndex(bv->cursor()); else p["symbol"] = cmd.argument(); - string const data = InsetCommand::params2string("nomenclature", p); + string const data = InsetCommand::params2string(p); bv->showDialog("nomenclature", data); break; } @@ -1664,7 +1845,7 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) p["type"] = from_ascii("idx"); else p["type"] = cmd.argument(); - string const data = InsetCommand::params2string("index_print", p); + string const data = InsetCommand::params2string(p); FuncRequest fr(LFUN_INSET_INSERT, data); dispatch(cur, fr); break; @@ -1696,6 +1877,7 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) case LFUN_MATH_MODE: if (cmd.argument() == "on") // 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); @@ -1705,6 +1887,7 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) if (cmd.argument().empty()) cur.errorMessage(from_utf8(N_("Missing argument"))); else { + cur.recordUndo(); string s = to_utf8(cmd.argument()); string const s1 = token(s, ' ', 1); int const nargs = s1.empty() ? 0 : convert(s1); @@ -1743,7 +1926,7 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) cap::replaceSelection(cur); cur.insert(new InsetMathHull(cur.buffer(), hullSimple)); checkAndActivateInset(cur, true); - LASSERT(cur.inMathed(), /**/); + LASSERT(cur.inMathed(), break); cur.dispatch(cmd); break; } @@ -1840,11 +2023,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); + toggleAndShow(cur, this, font, toggle); break; } @@ -1942,6 +2133,7 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) if (!cmd.argument().empty()) // FIXME: Are all these characters encoded in one byte in utf8? bv->translateAndInsert(cmd.argument()[0], this, cur); + cur.screenUpdateFlags(Update::FitCursor); break; case LFUN_FLOAT_LIST_INSERT: { @@ -2128,6 +2320,21 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) needsUpdate |= (cur.pos() != cur.lastpos()) && cur.selection(); + if (lyxrc.spellcheck_continuously && !needsUpdate) { + // Check for misspelled text + // The redraw is useful because of the painting of + // misspelled markers depends on the cursor position. + // Trigger a redraw for cursor moves inside misspelled text. + if (!cur.inTexted()) { + // move from regular text to math + needsUpdate = last_misspelled; + } else if (oldTopSlice != cur.top() || oldBoundary != cur.boundary()) { + // move inside regular text + needsUpdate = last_misspelled + || cur.paragraph().isMisspelled(cur.pos(), true); + } + } + // FIXME: The cursor flag is reset two lines below // so we need to check here if some of the LFUN did touch that. // for now only Text::erase() and Text::backspace() do that. @@ -2147,11 +2354,10 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) cur.screenUpdateFlags(Update::SinglePar | Update::FitCursor); return; } - if (!needsUpdate && &oldTopSlice.inset() == &cur.inset() && oldTopSlice.idx() == cur.idx() - && !sel // sel is a backup of cur.selection() at the beginning of the function. + && !oldSelection // oldSelection is a backup of cur.selection() at the beginning of the function. && !cur.selection()) // FIXME: it would be better if we could just do this // @@ -2171,10 +2377,11 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) bool Text::getStatus(Cursor & cur, FuncRequest const & cmd, FuncStatus & flag) const { - LASSERT(cur.text() == this, /**/); + LBUFERR(this == cur.text()); FontInfo const & fontinfo = cur.real_current_font.fontInfo(); bool enable = true; + bool allow_in_passthru = false; InsetCode code = NO_CODE; switch (cmd.action()) { @@ -2188,6 +2395,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; @@ -2312,7 +2522,7 @@ bool Text::getStatus(Cursor & cur, FuncRequest const & cmd, // make sure we know about such floats if (cit == floats.end() || // and that we know how to generate a list of them - (!cit->second.needsFloatPkg() && cit->second.listCommand().empty())) { + (!cit->second.usesFloatPkg() && cit->second.listCommand().empty())) { flag.setUnknown(true); // probably not necessary, but... enable = false; @@ -2320,11 +2530,20 @@ 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); + string arg = cmd.getArg(0); + bool varia = arg != "LongTableNoNumber"; + if (cur.depth() > 0) { + if (&cur[cur.depth() - 1].inset()) + varia = cur[cur.depth() - 1].inset().allowsCaptionVariation(arg); + } + // 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, @@ -2354,6 +2573,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; @@ -2365,9 +2587,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: @@ -2397,6 +2665,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. @@ -2412,6 +2688,9 @@ bool Text::getStatus(Cursor & cur, FuncRequest const & cmd, case LFUN_PREVIEW_INSERT: code = PREVIEW_CODE; break; + case LFUN_SCRIPT_INSERT: + code = SCRIPT_CODE; + break; case LFUN_MATH_INSERT: case LFUN_MATH_AMS_MATRIX: @@ -2441,38 +2720,38 @@ bool Text::getStatus(Cursor & cur, FuncRequest const & cmd, case LFUN_FONT_EMPH: flag.setOnOff(fontinfo.emph() == FONT_ON); - enable = !cur.inset().getLayout().isPassThru(); + enable = !cur.paragraph().isPassThru(); break; case LFUN_FONT_ITAL: flag.setOnOff(fontinfo.shape() == ITALIC_SHAPE); - enable = !cur.inset().getLayout().isPassThru(); + enable = !cur.paragraph().isPassThru(); break; case LFUN_FONT_NOUN: flag.setOnOff(fontinfo.noun() == FONT_ON); - enable = !cur.inset().getLayout().isPassThru(); + enable = !cur.paragraph().isPassThru(); break; case LFUN_FONT_BOLD: case LFUN_FONT_BOLDSYMBOL: flag.setOnOff(fontinfo.series() == BOLD_SERIES); - enable = !cur.inset().getLayout().isPassThru(); + enable = !cur.paragraph().isPassThru(); break; case LFUN_FONT_SANS: flag.setOnOff(fontinfo.family() == SANS_FAMILY); - enable = !cur.inset().getLayout().isPassThru(); + enable = !cur.paragraph().isPassThru(); break; case LFUN_FONT_ROMAN: flag.setOnOff(fontinfo.family() == ROMAN_FAMILY); - enable = !cur.inset().getLayout().isPassThru(); + enable = !cur.paragraph().isPassThru(); break; case LFUN_FONT_TYPEWRITER: flag.setOnOff(fontinfo.family() == TYPEWRITER_FAMILY); - enable = !cur.inset().getLayout().isPassThru(); + enable = !cur.paragraph().isPassThru(); break; case LFUN_CUT: @@ -2498,6 +2777,20 @@ bool Text::getStatus(Cursor & cur, FuncRequest const & cmd, break; } + // explicit text type? + if (arg == "html") { + // Do not enable for PlainTextType, since some tidying in the + // frontend is needed for HTML, which is too unsafe for plain text. + enable = theClipboard().hasTextContents(Clipboard::HtmlTextType); + break; + } else if (arg == "latex") { + // LaTeX is usually not available on the clipboard with + // the correct MIME type, but in plain text. + enable = theClipboard().hasTextContents(Clipboard::PlainTextType) || + theClipboard().hasTextContents(Clipboard::LaTeXTextType); + break; + } + // explicit graphics type? Clipboard::GraphicsType type = Clipboard::AnyGraphicsType; if ((arg == "pdf" && (type = Clipboard::PdfGraphicsType)) @@ -2516,6 +2809,7 @@ bool Text::getStatus(Cursor & cur, FuncRequest const & cmd, } case LFUN_CLIPBOARD_PASTE: + case LFUN_CLIPBOARD_PASTE_SIMPLE: enable = !theClipboard().empty(); break; @@ -2556,7 +2850,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: @@ -2566,7 +2860,7 @@ bool Text::getStatus(Cursor & cur, FuncRequest const & cmd, case LFUN_TAB_INSERT: case LFUN_TAB_DELETE: - enable = cur.inset().getLayout().isPassThru(); + enable = cur.paragraph().isPassThru(); break; case LFUN_SET_GRAPHICS_GROUP: { @@ -2592,11 +2886,11 @@ bool Text::getStatus(Cursor & cur, FuncRequest const & cmd, } case LFUN_LANGUAGE: - enable = !cur.inset().getLayout().isPassThru(); - flag.setOnOff(to_utf8(cmd.argument()) == cur.real_current_font.language()->lang()); + enable = !cur.paragraph().isPassThru(); + 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; @@ -2609,7 +2903,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: @@ -2644,7 +2948,7 @@ bool Text::getStatus(Cursor & cur, FuncRequest const & cmd, case LFUN_FONT_UWAVE: case LFUN_TEXTSTYLE_APPLY: case LFUN_TEXTSTYLE_UPDATE: - enable = !cur.inset().getLayout().isPassThru(); + enable = !cur.paragraph().isPassThru(); break; case LFUN_WORD_DELETE_FORWARD: @@ -2726,7 +3030,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); @@ -2737,7 +3041,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)