]> git.lyx.org Git - lyx.git/blobdiff - src/Text3.cpp
Avoid infinite loop
[lyx.git] / src / Text3.cpp
index 833a7ed645340aa110571e32e1c4c1c689bf3e56..9cef6692b44812c60fc82a6f1f7493785e8a82d7 100644 (file)
@@ -60,6 +60,7 @@
 #include "insets/InsetFloatList.h"
 #include "insets/InsetGraphics.h"
 #include "insets/InsetGraphicsParams.h"
+#include "insets/InsetInfo.h"
 #include "insets/InsetIPAMacro.h"
 #include "insets/InsetNewline.h"
 #include "insets/InsetQuotes.h"
@@ -89,6 +90,7 @@ using namespace lyx::support;
 namespace lyx {
 
 using cap::copySelection;
+using cap::copySelectionToTemp;
 using cap::cutSelection;
 using cap::cutSelectionToTemp;
 using cap::pasteFromStack;
@@ -249,20 +251,7 @@ static bool doInsertInset(Cursor & cur, Text * text,
                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<InsetArgument const *>(inset);
                Layout const & lay = cur.paragraph().layout();
@@ -301,9 +290,16 @@ static bool doInsertInset(Cursor & cur, Text * text,
 
        bool gotsel = false;
        if (cur.selection()) {
-               cutSelectionToTemp(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();
        }
        text->insertInset(cur, inset);
 
@@ -351,12 +347,6 @@ static bool doInsertInset(Cursor & cur, Text * text,
 }
 
 
-string const freefont2string()
-{
-       return freefont.toString(toggleall);
-}
-
-
 /// the type of outline operation
 enum OutlineOp {
        OutlineUp, // Move this header with text down
@@ -577,9 +567,59 @@ bool Text::isRTL(Paragraph const & par) const
 
 namespace {
 
-       Language const * getLanguage(Cursor const & cur, string const & lang) {
-               return lang.empty() ? cur.getFont().language() : languages.getLanguage(lang);
+Language const * getLanguage(Cursor const & cur, string const & lang)
+{
+       return lang.empty() ? cur.getFont().language() : languages.getLanguage(lang);
+}
+
+
+docstring resolveLayout(docstring layout, DocIterator const & dit)
+{
+       Paragraph const & par = dit.paragraph();
+       docstring const old_layout = par.layout().name();
+       DocumentClass const & tclass = dit.buffer()->params().documentClass();
+
+       if (layout.empty())
+               layout = tclass.defaultLayoutName();
+
+       if (dit.inset().forcePlainLayout(dit.idx()))
+               // in this case only the empty layout is allowed
+               layout = tclass.plainLayoutName();
+       else if (par.usePlainLayout()) {
+               // in this case, default layout maps to empty layout
+               if (layout == tclass.defaultLayoutName())
+                       layout = tclass.plainLayoutName();
+       } else {
+               // otherwise, the empty layout maps to the default
+               if (layout == tclass.plainLayoutName())
+                       layout = tclass.defaultLayoutName();
+       }
+
+       // If the entry is obsolete, use the new one instead.
+       if (tclass.hasLayout(layout)) {
+               docstring const & obs = tclass[layout].obsoleted_by();
+               if (!obs.empty())
+                       layout = obs;
        }
+       if (!tclass.hasLayout(layout))
+               layout.clear();
+       return layout;
+}
+
+
+bool isAlreadyLayout(docstring const & layout, CursorData const & cur)
+{
+       ParagraphList const & pars = cur.text()->paragraphs();
+
+       pit_type pit = cur.selBegin().pit();
+       pit_type const epit = cur.selEnd().pit() + 1;
+       for ( ; pit != epit; ++pit)
+               if (pars[pit].layout().name() != layout)
+                       return false;
+
+       return true;
+}
+
 
 } // namespace
 
@@ -674,7 +714,7 @@ 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, cmd.getArg(0) == "force");
                finishChange(cur, false);
@@ -682,7 +722,7 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
 
        case LFUN_WORD_DELETE_BACKWARD:
                if (cur.selection())
-                       cutSelection(cur, true, false);
+                       cutSelection(cur, false);
                else
                        deleteWordBackward(cur, cmd.getArg(0) == "force");
                finishChange(cur, false);
@@ -690,7 +730,7 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
 
        case LFUN_LINE_DELETE_FORWARD:
                if (cur.selection())
-                       cutSelection(cur, true, false);
+                       cutSelection(cur, false);
                else
                        tm->deleteLineForward(cur);
                finishChange(cur, false);
@@ -1168,7 +1208,7 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
                        needsUpdate |= erase(cur);
                        cur.resetAnchor();
                } else {
-                       cutSelection(cur, true, false);
+                       cutSelection(cur, false);
                        singleParUpdate = false;
                }
                moveCursor(cur, false);
@@ -1198,7 +1238,7 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
                                }
                        }
                } else {
-                       cutSelection(cur, true, false);
+                       cutSelection(cur, false);
                        singleParUpdate = false;
                }
                break;
@@ -1283,7 +1323,7 @@ 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())
@@ -1475,7 +1515,7 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
        }
 
        case LFUN_CUT:
-               cutSelection(cur, true, true);
+               cutSelection(cur, true);
                cur.message(_("Cut"));
                break;
 
@@ -1507,60 +1547,25 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
                cur.message(cur.paragraph().layout().name());
                break;
 
-       case LFUN_LAYOUT: {
+       case LFUN_LAYOUT:
+       case LFUN_LAYOUT_TOGGLE: {
                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();
-               docstring const old_layout = para.layout().name();
-               DocumentClass const & tclass = bv->buffer().params().documentClass();
-
-               if (layout.empty())
-                       layout = tclass.defaultLayoutName();
-
-               if (owner_->forcePlainLayout())
-                       // in this case only the empty layout is allowed
-                       layout = tclass.plainLayoutName();
-               else if (para.usePlainLayout()) {
-                       // in this case, default layout maps to empty layout
-                       if (layout == tclass.defaultLayoutName())
-                               layout = tclass.plainLayoutName();
-               } else {
-                       // otherwise, the empty layout maps to the default
-                       if (layout == tclass.plainLayoutName())
-                               layout = tclass.defaultLayoutName();
-               }
-
-               bool hasLayout = tclass.hasLayout(layout);
-
-               // If the entry is obsolete, use the new one instead.
-               if (hasLayout) {
-                       docstring const & obs = tclass[layout].obsoleted_by();
-                       if (!obs.empty())
-                               layout = obs;
-               }
+               docstring req_layout = ignoreautonests ? from_utf8(cmd.getArg(0)) : cmd.argument();
+               LYXERR(Debug::INFO, "LFUN_LAYOUT: (arg) " << to_utf8(req_layout));
 
-               if (!hasLayout) {
-                       cur.errorMessage(from_utf8(N_("Layout ")) + cmd.argument() +
+               docstring layout = resolveLayout(req_layout, cur);
+               if (layout.empty()) {
+                       cur.errorMessage(from_utf8(N_("Layout ")) + req_layout +
                                from_utf8(N_(" not known")));
                        break;
                }
 
-               bool change_layout = (old_layout != layout);
+               docstring const old_layout = cur.paragraph().layout().name();
+               bool change_layout = !isAlreadyLayout(layout, cur);
 
-               if (!change_layout && cur.selection() &&
-                       cur.selBegin().pit() != cur.selEnd().pit())
-               {
-                       pit_type spit = cur.selBegin().pit();
-                       pit_type epit = cur.selEnd().pit() + 1;
-                       while (spit != epit) {
-                               if (pars_[spit].layout().name() != old_layout) {
-                                       change_layout = true;
-                                       break;
-                               }
-                               ++spit;
-                       }
+               if (cmd.action() == LFUN_LAYOUT_TOGGLE && !change_layout) {
+                       change_layout = true;
+                       layout = resolveLayout(docstring(), cur);
                }
 
                if (change_layout) {
@@ -1576,13 +1581,11 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
                        }
                }
 
-               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;
+               DocumentClass const & tclass = bv->buffer().params().documentClass();
+               for (auto const & la_pair : tclass[layout].args()) {
+                       Layout::latexarg const & arg = la_pair.second;
                        if (arg.autoinsert) {
-                               FuncRequest cmd2(LFUN_ARGUMENT_INSERT, (*lait).first);
+                               FuncRequest const cmd2(LFUN_ARGUMENT_INSERT, la_pair.first);
                                lyx::dispatch(cmd2);
                        }
                }
@@ -1660,8 +1663,13 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
                        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))
+                       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);
                }
 
@@ -1696,29 +1704,6 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
                bv->buffer().errors("Paste");
                break;
 
-       case LFUN_UNICODE_INSERT: {
-               if (cmd.argument().empty())
-                       break;
-               int i = 0;
-               while (true) {
-                       docstring const arg = from_utf8(cmd.getArg(i));
-                       if (arg.empty())
-                               break;
-                       if (!isHex(arg)) {
-                               LYXERR0("Not a hexstring: " << arg);
-                               ++i;
-                               continue;
-                       }
-                       char_type c = hexToInt(arg);
-                       if (c >= 32 && c < 0x10ffff) {
-                               LYXERR(Debug::KEY, "Inserting c: " << c);
-                               lyx::dispatch(FuncRequest(LFUN_SELF_INSERT, docstring(1, c)));
-                       }
-                       ++i;
-               }
-               break;
-       }
-
        case LFUN_QUOTE_INSERT: {
                cap::replaceSelection(cur);
                cur.recordUndo();
@@ -1770,14 +1755,6 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
                break;
        }
 
-       case LFUN_DATE_INSERT: {
-               string const format = cmd.argument().empty()
-                       ? lyxrc.date_insert_format : to_utf8(cmd.argument());
-               string const time = formatted_time(current_time(), format);
-               lyx::dispatch(FuncRequest(LFUN_SELF_INSERT, time));
-               break;
-       }
-
        case LFUN_MOUSE_TRIPLE:
                if (cmd.button() == mouse_button::button1) {
                        tm->cursorHome(cur);
@@ -1940,14 +1917,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);
@@ -2020,22 +1994,18 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
        }
 
        case LFUN_INFO_INSERT: {
-               Inset * inset;
-               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);
-                       FuncRequest cmd0(cmd, ds);
-                       inset = createInset(cur.buffer(), cmd0);
+               if (cmd.argument().empty()) {
+                       bv->showDialog("info", cur.current_font.language()->lang());
                } else {
+                       Inset * inset;
                        inset = createInset(cur.buffer(), cmd);
+                       if (!inset)
+                               break;
+                       cur.recordUndo();
+                       insertInset(cur, inset);
+                       cur.forceBufferUpdate();
+                       cur.posForward();
                }
-               if (!inset)
-                       break;
-               cur.recordUndo();
-               insertInset(cur, inset);
-               cur.forceBufferUpdate();
-               cur.posForward();
                break;
        }
        case LFUN_CAPTION_INSERT:
@@ -2156,9 +2126,11 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
 
        case LFUN_NOMENCL_INSERT: {
                InsetCommandParams p(NOMENCL_CODE);
-               if (cmd.argument().empty())
-                       p["symbol"] = bv->cursor().innerText()->getStringToIndex(bv->cursor());
-               else
+               if (cmd.argument().empty()) {
+                       p["symbol"] = 
+                               bv->cursor().innerText()->getStringForDialog(bv->cursor());
+                       cur.clearSelection();
+               } else
                        p["symbol"] = cmd.argument();
                string const data = InsetCommand::params2string(p);
                bv->showDialog("nomenclature", data);
@@ -2384,6 +2356,10 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
                selectWordWhenUnderCursor(cur, WHOLE_WORD_STRICT);
                Font font(ignore_font, lang);
                toggleAndShow(cur, this, font, toggle);
+               // We need a buffer update if we change the language
+               // of an info inset
+               if (cur.insetInSelection(INFO_CODE))
+                       cur.forceBufferUpdate();
                break;
        }
 
@@ -2401,6 +2377,10 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
                        freefont = font;
                        toggleall = toggle;
                        toggleAndShow(cur, this, freefont, toggleall);
+                       // We need a buffer update if we change the language
+                       // of an info inset
+                       if (cur.insetInSelection(INFO_CODE))
+                               cur.forceBufferUpdate();
                        cur.message(_("Character set"));
                } else {
                        lyxerr << "Argument not ok";
@@ -2491,7 +2471,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) {
@@ -2934,11 +2914,6 @@ bool Text::getStatus(Cursor & cur, FuncRequest const & cmd,
        }
        case LFUN_NOTE_INSERT:
                code = NOTE_CODE;
-               // in commands (sections etc.) and description items,
-               // only Notes are allowed
-               enable = (cmd.argument().empty() || cmd.getArg(0) == "Note" ||
-                         (!cur.paragraph().layout().isCommand()
-                          && !inDescriptionItem(cur)));
                break;
        case LFUN_FLEX_INSERT: {
                code = FLEX_CODE;
@@ -2972,6 +2947,7 @@ bool Text::getStatus(Cursor & cur, FuncRequest const & cmd,
                break;
        case LFUN_INFO_INSERT:
                code = INFO_CODE;
+               enable = infoparams.validateArgument(cur.buffer(), cmd.argument(), true);
                break;
        case LFUN_ARGUMENT_INSERT: {
                code = ARG_CODE;
@@ -3222,17 +3198,34 @@ bool Text::getStatus(Cursor & cur, FuncRequest const & cmd,
 
        case LFUN_CHANGE_ACCEPT:
        case LFUN_CHANGE_REJECT:
-               // In principle, these LFUNs should only be enabled if there
-               // is a change at the current position/in the current selection.
-               // However, without proper optimizations, this will inevitably
-               // result in unacceptable performance - just imagine a user who
-               // wants to select the complete content of a long document.
                if (!cur.selection())
                        enable = cur.paragraph().isChanged(cur.pos());
-               else
-                       // TODO: context-sensitive enabling of LFUN_CHANGE_ACCEPT/REJECT
-                       // for selections.
-                       enable = true;
+               else {
+                       // will enable if there is a change in the selection
+                       enable = false;
+
+                       // cheap improvement for efficiency: using cached
+                       // buffer variable, if there is no change in the
+                       // document, no need to check further.
+                       if (!cur.buffer()->areChangesPresent())
+                               break;
+
+                       for (DocIterator it = cur.selectionBegin(); ; it.forwardPar()) {
+                               pos_type const beg = it.pos();
+                               pos_type end;
+                               bool const in_last_par = (it.pit() == cur.selectionEnd().pit());
+                               if (in_last_par)
+                                       end = cur.selectionEnd().pos();
+                               else
+                                       end = it.lastpos();
+                               if (beg != end && it.paragraph().isChanged(beg, end)) {
+                                       enable = true;
+                                       break;
+                               }
+                               if (in_last_par)
+                                       break;
+                       }
+               }
                break;
 
        case LFUN_OUTLINE_UP:
@@ -3275,13 +3268,6 @@ bool Text::getStatus(Cursor & cur, FuncRequest const & cmd,
                enable = !inDescriptionItem(cur);
                break;
 
-       case LFUN_DATE_INSERT: {
-               string const format = cmd.argument().empty()
-                       ? lyxrc.date_insert_format : to_utf8(cmd.argument());
-               enable = support::os::is_valid_strftime(format);
-               break;
-       }
-
        case LFUN_LANGUAGE:
                enable = !cur.paragraph().isPassThru();
                flag.setOnOff(cmd.getArg(0) == cur.real_current_font.language()->lang());
@@ -3302,15 +3288,14 @@ bool Text::getStatus(Cursor & cur, FuncRequest const & cmd,
                }
                break;
 
-       case LFUN_LAYOUT: {
-               DocumentClass const & tclass = cur.buffer()->params().documentClass();
+       case LFUN_LAYOUT:
+       case LFUN_LAYOUT_TOGGLE: {
                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);
+               docstring const req_layout = ignoreautonests ? from_utf8(cmd.getArg(0)) : cmd.argument();
+               docstring const layout = resolveLayout(req_layout, cur);
 
-               flag.setOnOff(layout == cur.paragraph().layout().name());
+               enable = !owner_->forcePlainLayout() && !layout.empty();
+               flag.setOnOff(isAlreadyLayout(layout, cur));
                break;
        }