]> git.lyx.org Git - lyx.git/blobdiff - src/mathed/InsetMathNest.cpp
Find: Renamend for_searchAdv to for_serach in src/OutputParams.h
[lyx.git] / src / mathed / InsetMathNest.cpp
index 3460cdca35a7be3b65c7b66664d1820497da6064..d4759a0c0f16fdd345cd19fbf9b313efe0c8af05 100644 (file)
 #include "InsetMathUnknown.h"
 #include "MathAutoCorrect.h"
 #include "MathCompletionList.h"
-#include "MathData.h"
 #include "MathFactory.h"
-#include "MathMacro.h"
-#include "MathMacroArgument.h"
+#include "InsetMathMacro.h"
+#include "InsetMathMacroArgument.h"
 #include "MathParser.h"
 #include "MathStream.h"
 #include "MathSupport.h"
@@ -52,7 +51,6 @@
 #include "LyX.h"
 #include "LyXRC.h"
 #include "MetricsInfo.h"
-#include "OutputParams.h"
 #include "TexRow.h"
 #include "Text.h"
 
@@ -86,7 +84,11 @@ using cap::selClearOrDel;
 InsetMathNest::InsetMathNest(Buffer * buf, idx_type nargs)
        : InsetMath(buf), cells_(nargs), lock_(false)
 {
-       setBuffer(*buf);
+       // FIXME This should not really be necessary, but when we are
+       // initializing the table of global macros, we create macros
+       // with no associated Buffer.
+       if (buf)
+               setBuffer(*buf);
 }
 
 
@@ -118,15 +120,12 @@ InsetMathNest & InsetMathNest::operator=(InsetMathNest const & inset)
 void InsetMathNest::setBuffer(Buffer & buffer)
 {
        InsetMath::setBuffer(buffer);
-       for (idx_type i = 0, n = nargs(); i != n; ++i) {
-               MathData & data = cell(i);
-               for (size_t j = 0; j != data.size(); ++j)
-                       data[j].nucleus()->setBuffer(buffer);
-       }
+       for (MathData & data : cells_)
+               data.setBuffer(buffer);
 }
 
 
-InsetMath::idx_type InsetMathNest::nargs() const
+idx_type InsetMathNest::nargs() const
 {
        return cells_.size();
 }
@@ -171,8 +170,10 @@ void InsetMathNest::cursorPos(BufferView const & bv,
 //             << " asc: " << ascent() << "  des: " << descent()
 //             << " ar.asc: " << ar.ascent() << " ar.des: " << ar.descent() << endl;
        // move cursor visually into empty cells ("blue rectangles");
-       if (ar.empty())
-               x += 2;
+       if (ar.empty()) {
+               Dimension const dim = coord_cache.getArrays().dim(&ar);
+               x += dim.wid / 3;
+       }
 }
 
 
@@ -186,10 +187,10 @@ void InsetMathNest::cellsMetrics(MetricsInfo const & mi) const
 }
 
 
-void InsetMathNest::updateBuffer(ParIterator const & it, UpdateType utype)
+void InsetMathNest::updateBuffer(ParIterator const & it, UpdateType utype, bool const deleted)
 {
        for (idx_type i = 0, n = nargs(); i != n; ++i)
-               cell(i).updateBuffer(it, utype);
+               cell(i).updateBuffer(it, utype, deleted);
 }
 
 
@@ -217,7 +218,7 @@ bool InsetMathNest::idxPrev(Cursor & cur) const
        if (cur.idx() == 0)
                return false;
        --cur.idx();
-       cur.pos() = cur.lastpos();
+       cur.pos() = lyxrc.mac_like_cursor_movement ? cur.lastpos() : 0;
        return true;
 }
 
@@ -233,7 +234,7 @@ bool InsetMathNest::idxFirst(Cursor & cur) const
        LASSERT(&cur.inset() == this, return false);
        if (nargs() == 0)
                return false;
-       cur.idx() = 0;
+       cur.idx() = firstIdx();
        cur.pos() = 0;
        return true;
 }
@@ -244,7 +245,7 @@ bool InsetMathNest::idxLast(Cursor & cur) const
        LASSERT(&cur.inset() == this, return false);
        if (nargs() == 0)
                return false;
-       cur.idx() = cur.lastidx();
+       cur.idx() = lastIdx();
        cur.pos() = cur.lastpos();
        return true;
 }
@@ -254,7 +255,7 @@ void InsetMathNest::dump() const
 {
        odocstringstream oss;
        otexrowstream ots(oss);
-       WriteStream os(ots);
+       TeXMathStream os(ots);
        os << "---------------------------------------------\n";
        write(os);
        os << "\n";
@@ -275,54 +276,6 @@ void InsetMathNest::draw(PainterInfo &, int, int) const
 }
 
 
-void InsetMathNest::drawSelection(PainterInfo & pi, int x, int y) const
-{
-       BufferView & bv = *pi.base.bv;
-       // this should use the x/y values given, not the cached values
-       Cursor & cur = bv.cursor();
-       if (!cur.selection())
-               return;
-       if (&cur.inset() != this)
-               return;
-
-       // FIXME: hack to get position cache warm
-       bool const original_drawing_state = pi.pain.isDrawingEnabled();
-       pi.pain.setDrawingEnabled(false);
-       draw(pi, x, y);
-       pi.pain.setDrawingEnabled(original_drawing_state);
-
-       CursorSlice s1 = cur.selBegin();
-       CursorSlice s2 = cur.selEnd();
-
-       //lyxerr << "InsetMathNest::drawing selection: "
-       //      << " s1: " << s1 << " s2: " << s2 << endl;
-       if (s1.idx() == s2.idx()) {
-               MathData const & c = cell(s1.idx());
-               Geometry const & g = bv.coordCache().getArrays().geometry(&c);
-               int x1 = g.pos.x_ + c.pos2x(pi.base.bv, s1.pos());
-               int y1 = g.pos.y_ - g.dim.ascent();
-               int x2 = g.pos.x_ + c.pos2x(pi.base.bv, s2.pos());
-               int y2 = g.pos.y_ + g.dim.descent();
-               pi.pain.fillRectangle(x1, y1, x2 - x1, y2 - y1, Color_selection);
-       //lyxerr << "InsetMathNest::drawing selection 3: "
-       //      << " x1: " << x1 << " x2: " << x2
-       //      << " y1: " << y1 << " y2: " << y2 << endl;
-       } else {
-               for (idx_type i = 0; i < nargs(); ++i) {
-                       if (idxBetween(i, s1.idx(), s2.idx())) {
-                               MathData const & c = cell(i);
-                               Geometry const & g = bv.coordCache().getArrays().geometry(&c);
-                               int x1 = g.pos.x_;
-                               int y1 = g.pos.y_ - g.dim.ascent();
-                               int x2 = g.pos.x_ + g.dim.width();
-                               int y2 = g.pos.y_ + g.dim.descent();
-                               pi.pain.fillRectangle(x1, y1, x2 - x1, y2 - y1, Color_selection);
-                       }
-               }
-       }
-}
-
-
 void InsetMathNest::validate(LaTeXFeatures & features) const
 {
        for (idx_type i = 0; i < nargs(); ++i)
@@ -373,12 +326,13 @@ MathData InsetMathNest::glue() const
 }
 
 
-void InsetMathNest::write(WriteStream & os) const
+void InsetMathNest::write(TeXMathStream & os) const
 {
        MathEnsurer ensurer(os, currentMode() == MATH_MODE);
        ModeSpecifier specifier(os, currentMode(), lockedMode());
        docstring const latex_name = name();
        os << '\\' << latex_name;
+       os.inMathClass(asClassInset());
        for (size_t i = 0; i < nargs(); ++i) {
                Changer dummy = os.changeRowEntry(TexRow::mathEntry(id(),i));
                os << '{' << cell(i) << '}';
@@ -389,6 +343,7 @@ void InsetMathNest::write(WriteStream & os) const
                os << "\\lyxlock";
                os.pendingSpace(true);
        }
+       os.inMathClass(false);
 }
 
 
@@ -403,28 +358,32 @@ void InsetMathNest::normalize(NormalStream & os) const
 
 void InsetMathNest::latex(otexstream & os, OutputParams const & runparams) const
 {
-       WriteStream wi(os, runparams.moving_arg, true,
-                       runparams.dryrun ? WriteStream::wsDryrun : WriteStream::wsDefault,
-                       runparams.encoding);
-       wi.strikeoutMath(runparams.inDeletedInset
-                        && (!LaTeXFeatures::isAvailable("dvipost")
-                               || (runparams.flavor != OutputParams::LATEX
-                                   && runparams.flavor != OutputParams::DVILUATEX)));
+       TeXMathStream::OutputType ot;
+       if (runparams.for_search != OutputParams::NoSearch)
+               ot = TeXMathStream::wsSearchAdv;
+       else if (runparams.dryrun)
+               ot = TeXMathStream::wsDryrun;
+       else
+               ot = TeXMathStream::wsDefault;
+       TeXMathStream wi(os, runparams.moving_arg, true, ot,
+                        runparams.encoding);
+       wi.strikeoutMath(runparams.inDeletedInset);
        if (runparams.inulemcmd) {
-               wi.ulemCmd(WriteStream::UNDERLINE);
+               wi.ulemCmd(TeXMathStream::UNDERLINE);
                if (runparams.local_font) {
                        FontInfo f = runparams.local_font->fontInfo();
                        if (f.strikeout() == FONT_ON)
-                               wi.ulemCmd(WriteStream::STRIKEOUT);
+                               wi.ulemCmd(TeXMathStream::STRIKEOUT);
                }
        }
        wi.canBreakLine(os.canBreakLine());
        Changer dummy = wi.changeRowEntry(TexRow::textEntry(runparams.lastid,
                                                            runparams.lastpos));
        write(wi);
-       // Reset parbreak status after a math inset.
+       // Reset parbreak and command termination status after a math inset.
        os.lastChar(0);
        os.canBreakLine(wi.canBreakLine());
+       os.terminateCommand(false);
 }
 
 
@@ -497,8 +456,8 @@ void InsetMathNest::handleNest(Cursor & cur, MathAtom const & nest)
 void InsetMathNest::handleNest(Cursor & cur, MathAtom const & nest,
        docstring const & arg)
 {
-       CursorSlice i1 = cur.selBegin();
-       CursorSlice i2 = cur.selEnd();
+       DocIterator const i1 = cur.selectionBegin();
+       DocIterator const i2 = cur.selectionEnd();
        if (!i1.inset().asInsetMath())
                return;
        if (i1.idx() == i2.idx()) {
@@ -509,8 +468,9 @@ void InsetMathNest::handleNest(Cursor & cur, MathAtom const & nest,
        }
 
        // multiple selected cells in a simple non-grid inset
-       if (i1.asInsetMath()->nrows() == 0 || i1.asInsetMath()->ncols() == 0) {
+       if (i1.inset().nrows() == 0 || i1.inset().ncols() == 0) {
                for (idx_type i = i1.idx(); i <= i2.idx(); ++i) {
+                       cur.setCursor(i1);
                        // select cell
                        cur.idx() = i;
                        cur.pos() = 0;
@@ -518,14 +478,9 @@ void InsetMathNest::handleNest(Cursor & cur, MathAtom const & nest,
                        cur.pos() = cur.lastpos();
                        cur.setSelection();
 
-                       // change font of cell
+                       // do the real job
                        cur.handleNest(nest);
                        cur.insert(arg);
-
-                       // cur is in the font inset now. If the loop continues,
-                       // we need to get outside again for the next cell
-                       if (i + 1 <= i2.idx())
-                               cur.pop_back();
                }
                return;
        }
@@ -533,24 +488,20 @@ void InsetMathNest::handleNest(Cursor & cur, MathAtom const & nest,
        // the complicated case with multiple selected cells in a grid
        row_type r1, r2;
        col_type c1, c2;
-       cap::region(i1, i2, r1, r2, c1, c2);
+       cap::region(i1.top(), i2.top(), r1, r2, c1, c2);
        for (row_type row = r1; row <= r2; ++row) {
                for (col_type col = c1; col <= c2; ++col) {
+                       cur.setCursor(i1);
                        // select cell
-                       cur.idx() = i1.asInsetMath()->index(row, col);
+                       cur.idx() = i1.inset().index(row, col);
                        cur.pos() = 0;
                        cur.resetAnchor();
                        cur.pos() = cur.lastpos();
                        cur.setSelection();
 
-                       //
+                       // do the real job
                        cur.handleNest(nest);
                        cur.insert(arg);
-
-                       // cur is in the font inset now. If the loop continues,
-                       // we need to get outside again for the next cell
-                       if (col + 1 <= c2 || row + 1 <= r2)
-                               cur.pop_back();
                }
        }
 }
@@ -569,7 +520,6 @@ void InsetMathNest::handleFont2(Cursor & cur, docstring const & arg)
        // FIXME: support other font changes here as well?
 }
 
-
 void InsetMathNest::doDispatch(Cursor & cur, FuncRequest & cmd)
 {
        //LYXERR0("InsetMathNest: request: " << cmd);
@@ -595,7 +545,8 @@ void InsetMathNest::doDispatch(Cursor & cur, FuncRequest & cmd)
                        size_t n = 0;
                        idocstringstream is(cmd.argument());
                        is >> n;
-                       topaste = cap::selection(n, buffer().params().documentClassPtr());
+                       topaste = cap::selection(n, make_pair(buffer().params().documentClassPtr(),
+                                                             buffer().params().authors()));
                }
                cur.niceInsert(topaste, parseflg, false);
                cur.clearSelection(); // bug 393
@@ -606,7 +557,7 @@ void InsetMathNest::doDispatch(Cursor & cur, FuncRequest & cmd)
 
        case LFUN_CUT:
                cur.recordUndo();
-               cutSelection(cur, true, true);
+               cutSelection(cur, true);
                cur.message(_("Cut"));
                // Prevent stale position >= size crash
                // Probably not necessary anymore, see eraseSelection (gb 2005-10-09)
@@ -614,11 +565,6 @@ void InsetMathNest::doDispatch(Cursor & cur, FuncRequest & cmd)
                cur.forceBufferUpdate();
                break;
 
-       case LFUN_COPY:
-               copySelection(cur);
-               cur.message(_("Copy"));
-               break;
-
        case LFUN_MOUSE_PRESS:
                lfunMousePress(cur, cmd);
                break;
@@ -755,12 +701,6 @@ void InsetMathNest::doDispatch(Cursor & cur, FuncRequest & cmd)
                        || act == LFUN_PARAGRAPH_UP_SELECT;
                cur.selHandle(select);
 
-               // handle autocorrect:
-               if (lyxrc.autocorrection_math && cur.autocorrect()) {
-                       cur.autocorrect() = false;
-                       cur.message(_("Autocorrect Off ('!' to enter)"));
-               }
-
                // go up/down
                bool up = act == LFUN_UP || act == LFUN_UP_SELECT
                        || act == LFUN_PARAGRAPH_UP || act == LFUN_PARAGRAPH_UP_SELECT;
@@ -781,20 +721,18 @@ void InsetMathNest::doDispatch(Cursor & cur, FuncRequest & cmd)
        case LFUN_MOUSE_DOUBLE:
        case LFUN_WORD_SELECT:
                cur.pos() = 0;
-               cur.resetAnchor();
-               cur.selection(true);
+               cur.bv().mouseSetCursor(cur);
                cur.pos() = cur.lastpos();
-               cur.bv().cursor() = cur;
+               cur.bv().mouseSetCursor(cur, true);
                break;
 
        case LFUN_MOUSE_TRIPLE:
                cur.idx() = 0;
                cur.pos() = 0;
-               cur.resetAnchor();
-               cur.selection(true);
+               cur.bv().mouseSetCursor(cur);
                cur.idx() = cur.lastidx();
                cur.pos() = cur.lastpos();
-               cur.bv().cursor() = cur;
+               cur.bv().mouseSetCursor(cur, true);
                break;
 
        case LFUN_LINE_BEGIN:
@@ -844,12 +782,32 @@ void InsetMathNest::doDispatch(Cursor & cur, FuncRequest & cmd)
 
        case LFUN_CELL_FORWARD:
                cur.screenUpdateFlags(Update::Decoration | Update::FitCursor);
-               cur.inset().idxNext(cur);
+               cur.selHandle(false);
+               cur.clearTargetX();
+               cur.macroModeClose();
+               if (!cur.inset().idxNext(cur)) {
+                       if (cur.lastidx() == 0)
+                               cur.popForward();
+                       else {
+                               cur.idx() = firstIdx();
+                               cur.pos() = 0;
+                       }
+               }
                break;
 
        case LFUN_CELL_BACKWARD:
                cur.screenUpdateFlags(Update::Decoration | Update::FitCursor);
-               cur.inset().idxPrev(cur);
+               cur.selHandle(false);
+               cur.clearTargetX();
+               cur.macroModeClose();
+               if (!cur.inset().idxPrev(cur)) {
+                       if (cur.lastidx() == 0)
+                               cur.popBackward();
+                       else {
+                               cur.idx() = cur.lastidx();
+                               cur.pos() = lyxrc.mac_like_cursor_movement ? cur.lastpos() : 0;
+                       }
+               }
                break;
 
        case LFUN_WORD_DELETE_BACKWARD:
@@ -860,9 +818,9 @@ void InsetMathNest::doDispatch(Cursor & cur, FuncRequest & cmd)
                else if (!cur.inMacroMode())
                        cur.recordUndoSelection();
                // if the inset can not be removed from within, delete it
-               if (!cur.backspace()) {
-                       FuncRequest cmd = FuncRequest(LFUN_CHAR_DELETE_FORWARD);
-                       cur.innerText()->dispatch(cur, cmd);
+               if (!cur.backspace(cmd.getArg(0) == "force")) {
+                       FuncRequest newcmd = FuncRequest(LFUN_CHAR_DELETE_FORWARD, "force");
+                       cur.innerText()->dispatch(cur, newcmd);
                }
                break;
 
@@ -874,9 +832,9 @@ void InsetMathNest::doDispatch(Cursor & cur, FuncRequest & cmd)
                else
                        cur.recordUndoSelection();
                // if the inset can not be removed from within, delete it
-               if (!cur.erase()) {
-                       FuncRequest cmd = FuncRequest(LFUN_CHAR_DELETE_FORWARD);
-                       cur.innerText()->dispatch(cur, cmd);
+               if (!cur.erase(cmd.getArg(0) == "force")) {
+                       FuncRequest newcmd = FuncRequest(LFUN_CHAR_DELETE_FORWARD, "force");
+                       cur.innerText()->dispatch(cur, newcmd);
                }
                break;
 
@@ -884,8 +842,12 @@ void InsetMathNest::doDispatch(Cursor & cur, FuncRequest & cmd)
                if (cur.selection())
                        cur.clearSelection();
                else  {
-                       cmd = FuncRequest(LFUN_FINISHED_FORWARD);
-                       cur.undispatched();
+                       if (cur.inMacroMode())
+                               cur.macroModeClose(true);
+                       else {
+                               cmd = FuncRequest(LFUN_FINISHED_FORWARD);
+                               cur.undispatched();
+                       }
                }
                break;
 
@@ -898,40 +860,37 @@ void InsetMathNest::doDispatch(Cursor & cur, FuncRequest & cmd)
                break;
 
        case LFUN_SELF_INSERT:
-               if (cmd.argument().size() != 1) {
-                       cur.recordUndoSelection();
-                       docstring const arg = cmd.argument();
-                       if (!interpretString(cur, arg))
-                               cur.insert(arg);
+               // special case first for big delimiters
+               if (cmd.argument().size() != 1 && interpretString(cur, cmd.argument()))
                        break;
-               }
-               // Don't record undo steps if we are in macro mode and thus
-               // cmd.argument is the next character of the macro name.
-               // Otherwise we'll get an invalid cursor if we undo after
-               // the macro was finished and the macro is a known command,
-               // e.g. sqrt. Cursor::macroModeClose replaces in this case
-               // the InsetMathUnknown with name "frac" by an empty
-               // InsetMathFrac -> a pos value > 0 is invalid.
-               // A side effect is that an undo before the macro is finished
-               // undoes the complete macro, not only the last character.
-               // At the time we hit '\' we are not in macro mode, still.
-               if (!cur.inMacroMode())
-                       cur.recordUndoSelection();
 
-               // spacial handling of space. If we insert an inset
-               // via macro mode, we want to put the cursor inside it
-               // if relevant. Think typing "\frac<space>".
-               if (cmd.argument()[0] == ' '
-                   && cur.inMacroMode() && cur.macroName() != "\\"
-                   && cur.macroModeClose() && cur.pos() > 0) {
-                       MathAtom const atom = cur.prevAtom();
-                       if (atom->asNestInset() && atom->isActive()) {
-                               cur.posBackward();
-                               cur.pushBackward(*cur.nextInset());
+               for (char_type c : cmd.argument()) {
+                       // Don't record undo steps if we are in macro mode and thus
+                       // cmd.argument is the next character of the macro name.
+                       // Otherwise we'll get an invalid cursor if we undo after
+                       // the macro was finished and the macro is a known command,
+                       // e.g. sqrt. Cursor::macroModeClose replaces in this case
+                       // the InsetMathUnknown with name "frac" by an empty
+                       // InsetMathFrac -> a pos value > 0 is invalid.
+                       // A side effect is that an undo before the macro is finished
+                       // undoes the complete macro, not only the last character.
+                       // At the time we hit '\' we are not in macro mode, still.
+                       if (!cur.inMacroMode())
+                               cur.recordUndoSelection();
+
+                       // special handling of space. If we insert an inset
+                       // via macro mode, we want to put the cursor inside it
+                       // if relevant. Think typing "\frac<space>".
+                       if (c == ' '
+                           && cur.inMacroMode() && cur.macroName() != "\\"
+                           && cur.macroModeClose() && cur.pos() > 0)
+                               cur.editInsertedInset();
+                       else if (!interpretChar(cur, c)) {
+                               cmd = FuncRequest(LFUN_FINISHED_FORWARD);
+                               cur.undispatched();
+                               // FIXME: can we avoid skipping the end of the string?
+                               break;
                        }
-               } else if (!interpretChar(cur, cmd.argument()[0])) {
-                       cmd = FuncRequest(LFUN_FINISHED_FORWARD);
-                       cur.undispatched();
                }
                break;
 
@@ -944,7 +903,7 @@ void InsetMathNest::doDispatch(Cursor & cur, FuncRequest & cmd)
                int y = 0;
                istringstream is(to_utf8(cmd.argument()));
                is >> x >> y;
-               cur.setScreenPos(x, y);
+               cur.setTargetX(x);
                break;
        }
 
@@ -954,7 +913,7 @@ void InsetMathNest::doDispatch(Cursor & cur, FuncRequest & cmd)
                if (cmd.argument().empty()) {
                        // do superscript if LyX handles
                        // deadkeys
-                       cur.recordUndoSelection();
+                       cur.recordUndoInset();
                        script(cur, true, grabAndEraseSelection(cur));
                }
                break;
@@ -1135,7 +1094,8 @@ void InsetMathNest::doDispatch(Cursor & cur, FuncRequest & cmd)
                // check if we have a valid decoration
                if (name != "pmatrix" && name != "bmatrix"
                        && name != "Bmatrix" && name != "vmatrix"
-                       && name != "Vmatrix" && name != "matrix")
+                       && name != "Vmatrix" && name != "matrix"
+                       && name != "smallmatrix")
                        name = from_ascii("matrix");
 
                cur.niceInsert(
@@ -1254,7 +1214,7 @@ void InsetMathNest::doDispatch(Cursor & cur, FuncRequest & cmd)
                bool fold = act == LFUN_MATH_MACRO_FOLD;
                bool found = findMacroToFoldUnfold(it, fold);
                if (found) {
-                       MathMacro * macro = it.nextInset()->asInsetMath()->asMacro();
+                       InsetMathMacro * macro = it.nextInset()->asInsetMath()->asMacro();
                        cur.recordUndoInset();
                        if (fold)
                                macro->fold(cur);
@@ -1277,10 +1237,12 @@ void InsetMathNest::doDispatch(Cursor & cur, FuncRequest & cmd)
                cur.recordUndoSelection();
                if (cmd.argument() == "^" || cmd.argument() == "_")
                        interpretChar(cur, cmd.argument()[0]);
+               else if (!cur.selection())
+                       cur.niceInsert(cmd.argument());
                else {
-                       MathData ar;
+                       MathData ar(cur.buffer());
                        asArray(cmd.argument(), ar);
-                       if (cur.selection() && ar.size() == 1
+                       if (ar.size() == 1
                            && ar[0]->asNestInset()
                            && ar[0]->asNestInset()->nargs() > 1)
                                handleNest(cur, ar[0]);
@@ -1290,22 +1252,6 @@ void InsetMathNest::doDispatch(Cursor & cur, FuncRequest & cmd)
                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) {
-                               docstring s = docstring(1, c);
-                               FuncCode code = currentMode() == MATH_MODE ?
-                                       LFUN_MATH_INSERT : LFUN_SELF_INSERT;
-                               lyx::dispatch(FuncRequest(code, s));
-                       }
-               }
-               break;
-       }
-
        case LFUN_DIALOG_SHOW_NEW_INSET: {
                docstring const & name = cmd.argument();
                string data;
@@ -1336,6 +1282,52 @@ void InsetMathNest::doDispatch(Cursor & cur, FuncRequest & cmd)
                }
                break;
 
+       case LFUN_MATH_LIMITS: {
+               InsetMath * in = 0;
+               if (cur.pos() < cur.lastpos() && cur.nextMath().allowsLimitsChange())
+                       in = &cur.nextMath();
+               else if (cur.pos() > 0 && cur.prevMath().allowsLimitsChange())
+                       in = &cur.prevMath();
+               else if (cur.lastpos() > 0 && cur.cell().back()->allowsLimitsChange())
+                       in = cur.cell().back().nucleus();
+               // only when nucleus allows this
+               if (!in)
+                       return;
+               cur.recordUndoInset();
+               if (!cmd.argument().empty()) {
+                       if (cmd.argument() == "limits")
+                               in->limits(LIMITS);
+                       else if (cmd.argument() == "nolimits")
+                               in->limits(NO_LIMITS);
+                       else
+                               in->limits(AUTO_LIMITS);
+               } else if (in->limits() != AUTO_LIMITS)
+                       in->limits(AUTO_LIMITS);
+               else if (in->defaultLimits(cur.cell().displayStyle()) == LIMITS)
+                       in->limits(NO_LIMITS);
+               else
+                       in->limits(LIMITS);
+               return;
+       }
+
+       case LFUN_PHANTOM_INSERT: {
+               docstring const & arg = cmd.argument();
+               docstring newarg;
+               if (arg == "Phantom")
+                       newarg = from_ascii("\\phantom");
+               else if (arg == "HPhantom")
+                       newarg = from_ascii("\\hphantom");
+               else if (arg == "VPhantom")
+                       newarg = from_ascii("\\vphantom");
+               if (newarg.empty())
+                       LYXERR0("Unknown phantom type " + newarg);
+               else {
+                       FuncRequest const newfunc(LFUN_MATH_INSERT, newarg);
+                       lyx::dispatch(newfunc);
+               }
+               break;
+       }
+
        default:
                InsetMath::doDispatch(cur, cmd);
                break;
@@ -1350,7 +1342,7 @@ bool InsetMathNest::findMacroToFoldUnfold(Cursor & it, bool fold) const {
                // go backward through the current cell
                Inset * inset = it.nextInset();
                while (inset && inset->asInsetMath()) {
-                       MathMacro * macro = inset->asInsetMath()->asMacro();
+                       InsetMathMacro * macro = inset->asInsetMath()->asMacro();
                        if (macro) {
                                // found the an macro to open/close?
                                if (macro->folded() != fold)
@@ -1385,9 +1377,6 @@ bool InsetMathNest::getStatus(Cursor & cur, FuncRequest const & cmd,
        bool ret = true;
        string const arg = to_utf8(cmd.argument());
        switch (cmd.action()) {
-       case LFUN_INSET_MODIFY:
-               flag.setEnabled(false);
-               break;
 #if 0
        case LFUN_INSET_MODIFY:
                // FIXME: check temporarily disabled
@@ -1463,7 +1452,7 @@ bool InsetMathNest::getStatus(Cursor & cur, FuncRequest const & cmd,
        case LFUN_INSET_INSERT: {
                // Don't test createMathInset_fromDialogStr(), since
                // getStatus is not called with a valid reference and the
-               // dialog would not be applyable.
+               // dialog would not be applicable.
                string const name = cmd.getArg(0);
                flag.setEnabled(name == "ref" || name == "mathspace");
                break;
@@ -1519,6 +1508,29 @@ bool InsetMathNest::getStatus(Cursor & cur, FuncRequest const & cmd,
                break;
        }
 
+       case LFUN_MATH_LIMITS: {
+               InsetMath * in = 0;
+               if (cur.pos() < cur.lastpos() && cur.nextMath().allowsLimitsChange())
+                       in = &cur.nextMath();
+               else if (cur.pos() > 0 && cur.prevMath().allowsLimitsChange())
+                       in = &cur.prevMath();
+               else if (cur.lastpos() > 0 && cur.cell().back()->allowsLimitsChange())
+                       in = cur.cell().back().nucleus();
+               if (in) {
+                       if (!cmd.argument().empty()) {
+                               if (cmd.argument() == "limits")
+                                       flag.setOnOff(in->limits() == LIMITS);
+                               else if (cmd.argument() == "nolimits")
+                                       flag.setOnOff(in->limits() == NO_LIMITS);
+                               else
+                                       flag.setOnOff(in->limits() == AUTO_LIMITS);
+                       }
+                       flag.setEnabled(true);
+               } else
+                       flag.setEnabled(false);
+               return true;
+       }
+
        default:
                ret = false;
                break;
@@ -1530,10 +1542,9 @@ bool InsetMathNest::getStatus(Cursor & cur, FuncRequest const & cmd,
 void InsetMathNest::edit(Cursor & cur, bool front, EntryDirection entry_from)
 {
        cur.push(*this);
-       bool enter_front = (entry_from == Inset::ENTRY_DIRECTION_RIGHT ||
+       bool enter_front = (entry_from == Inset::ENTRY_DIRECTION_LEFT ||
                (entry_from == Inset::ENTRY_DIRECTION_IGNORE && front));
-       cur.idx() = enter_front ? 0 : cur.lastidx();
-       cur.pos() = enter_front ? 0 : cur.lastpos();
+       enter_front ? idxFirst(cur) : idxLast(cur);
        cur.resetAnchor();
        //lyxerr << "InsetMathNest::edit, cur:\n" << cur << endl;
 }
@@ -1541,7 +1552,7 @@ void InsetMathNest::edit(Cursor & cur, bool front, EntryDirection entry_from)
 
 Inset * InsetMathNest::editXY(Cursor & cur, int x, int y)
 {
-       int idx_min = 0;
+       int idx_min = -1;
        int dist_min = 1000000;
        for (idx_type i = 0, n = nargs(); i != n; ++i) {
                int const d = cell(i).dist(cur.bv(), x, y);
@@ -1550,6 +1561,9 @@ Inset * InsetMathNest::editXY(Cursor & cur, int x, int y)
                        idx_min = i;
                }
        }
+       if (idx_min == -1)
+               return this;
+
        MathData & ar = cell(idx_min);
        cur.push(*this);
        cur.idx() = idx_min;
@@ -1579,6 +1593,10 @@ void InsetMathNest::lfunMousePress(Cursor & cur, FuncRequest & cmd)
                        return;
                }
        }
+
+       // set cursor after the inset if x is nearer to that position (bug 9748)
+       cur.moveToClosestEdge(cmd.x(), true);
+
        bool do_selection = cmd.button() == mouse_button::button1
                && cmd.modifier() == ShiftModifier;
        bv.mouseSetCursor(cur, do_selection);
@@ -1623,6 +1641,9 @@ void InsetMathNest::lfunMouseMotion(Cursor & cur, FuncRequest & cmd)
                return;
        }
 
+       // set cursor after the inset if x is nearer to that position (bug 9748)
+       cur.moveToClosestEdge(cmd.x());
+
        CursorSlice old = bvcur.top();
 
        // We continue with our existing selection or start a new one, so don't
@@ -1658,6 +1679,11 @@ void InsetMathNest::lfunMouseRelease(Cursor & cur, FuncRequest & cmd)
 
 bool InsetMathNest::interpretChar(Cursor & cur, char_type const c)
 {
+       // try auto-correction
+       if (lyxrc.autocorrection_math && cur.pos() != 0 && !cur.selection()
+            && math_autocorrect(cur, c))
+               return true;
+
        //lyxerr << "interpret 2: '" << c << "'" << endl;
        docstring save_selection;
        if (c == '^' || c == '_')
@@ -1675,7 +1701,7 @@ bool InsetMathNest::interpretChar(Cursor & cur, char_type const c)
                        cur.backspace();
                        int n = c - '0';
                        if (n >= 1 && n <= 9)
-                               cur.insert(new MathMacroArgument(n));
+                               cur.insert(new InsetMathMacroArgument(n));
                        return true;
                }
 
@@ -1763,7 +1789,7 @@ bool InsetMathNest::interpretChar(Cursor & cur, char_type const c)
                        MathAtom const atom = cur.prevAtom();
                        if (atom->asNestInset() && atom->isActive()) {
                                cur.posBackward();
-                               cur.pushBackward(*cur.nextInset());
+                               cur.nextInset()->edit(cur, true);
                        }
                }
                if (c == '{')
@@ -1774,18 +1800,6 @@ bool InsetMathNest::interpretChar(Cursor & cur, char_type const c)
        }
 
 
-       // leave autocorrect mode if necessary
-       if (lyxrc.autocorrection_math && c == ' ' && cur.autocorrect()) {
-               cur.autocorrect() = false;
-               cur.message(_("Autocorrect Off ('!' to enter)"));
-               return true;
-       }
-       if (lyxrc.autocorrection_math && c == '!' && !cur.autocorrect()) {
-               cur.autocorrect() = true;
-               cur.message(_("Autocorrect On (<space> to exit)"));
-               return true;
-       }
-
        // just clear selection on pressing the space bar
        if (cur.selection() && c == ' ') {
                cur.selection(false);
@@ -1820,7 +1834,13 @@ bool InsetMathNest::interpretChar(Cursor & cur, char_type const c)
                        // but suppress direct insertion of two spaces in a row
                        // the still allows typing  '<space>a<space>' and deleting the 'a', but
                        // it is better than nothing...
-                       if (cur.pos() == 0 || cur.prevAtom()->getChar() != ' ') {
+                       pos_type const pos = cur.pos();
+                       pos_type const lastpos = cur.lastpos();
+                       if ((pos == 0 && lastpos == 0)
+                           || (pos == 0 && cur.nextAtom()->getChar() != ' ')
+                           || (pos == lastpos && cur.prevAtom()->getChar() != ' ')
+                           || (pos > 0 && cur.prevAtom()->getChar() != ' '
+                                       && cur.nextAtom()->getChar() != ' ')) {
                                cur.insert(c);
                                // FIXME: we have to enable full redraw here because of the
                                // visual box corners that define the inset. If we know for
@@ -1875,10 +1895,12 @@ bool InsetMathNest::interpretChar(Cursor & cur, char_type const c)
                return true;
        } else if (currentMode() != InsetMath::TEXT_MODE) {
                if (c == '_') {
+                       cur.recordUndoInset();
                        script(cur, false, save_selection);
                        return true;
                }
                if (c == '^') {
+                       cur.recordUndoInset();
                        script(cur, true, save_selection);
                        return true;
                }
@@ -1887,10 +1909,16 @@ bool InsetMathNest::interpretChar(Cursor & cur, char_type const c)
                        return true;
                }
                if (currentMode() == InsetMath::MATH_MODE && Encodings::isUnicodeTextOnly(c)) {
-                       MathAtom at = createInsetMath("text", buf);
-                       at.nucleus()->cell(0).push_back(MathAtom(new InsetMathChar(c)));
-                       cur.niceInsert(at);
-                       cur.posForward();
+                       MathAtom const atom(new InsetMathChar(c));
+                       if (cur.prevInset() && cur.prevInset()->asInsetMath()->name() == "text") {
+                               // reuse existing \text inset
+                               cur.prevInset()->asInsetMath()->cell(0).push_back(atom);
+                       } else {
+                               MathAtom at = createInsetMath("text", buf);
+                               at.nucleus()->cell(0).push_back(atom);
+                               cur.insert(at);
+                               cur.posForward();
+                       }
                        return true;
                }
        } else {
@@ -1910,26 +1938,23 @@ bool InsetMathNest::interpretChar(Cursor & cur, char_type const c)
                return true;
        }
 
-
-       // try auto-correction
-       if (lyxrc.autocorrection_math && cur.autocorrect() && cur.pos() != 0
-                 && math_autocorrect(cur.prevAtom(), c))
-               return true;
-
        // no special circumstances, so insert the character without any fuss
        cur.insert(c);
-       if (lyxrc.autocorrection_math) {
-               if (!cur.autocorrect())
-                       cur.message(_("Autocorrect Off ('!' to enter)"));
-               else
-                       cur.message(_("Autocorrect On (<space> to exit)"));
-       }
        return true;
 }
 
 
 bool InsetMathNest::interpretString(Cursor & cur, docstring const & str)
 {
+       if (str == "\\limits" || str == "\\nolimits") {
+               if (cur.pos() > 0 && cur.prevMath().allowsLimitsChange()) {
+                       cur.prevMath().limits(str == "\\limits" ? LIMITS : NO_LIMITS);
+                       return true;
+               } else {
+                       cur.message(bformat(_("Cannot apply %1$s here."), str));
+                       return false;
+               }
+       }
        // Create a InsetMathBig from cur.cell()[cur.pos() - 1] and t if
        // possible
        if (!cur.empty() && cur.pos() > 0 &&
@@ -1940,6 +1965,7 @@ bool InsetMathNest::interpretString(Cursor & cur, docstring const & str)
                                prev = prev.substr(1);
                                latexkeys const * l = in_word_set(prev);
                                if (l && l->inset == "big") {
+                                       cur.recordUndoSelection();
                                        cur.cell()[cur.pos() - 1] =
                                                MathAtom(new InsetMathBig(prev, str));
                                        return true;
@@ -2039,7 +2065,7 @@ CompletionList const *
 InsetMathNest::createCompletionList(Cursor const & cur) const
 {
        if (!cur.inMacroMode())
-               return 0;
+               return nullptr;
 
        return new MathCompletionList(cur);
 }
@@ -2069,8 +2095,7 @@ bool InsetMathNest::insertCompletion(Cursor & cur, docstring const & s,
 #if 0
                // FIXME: this creates duplicates in the completion popup
                // which looks ugly. Moreover the changes the list lengths
-               // which seems to
-               confuse the popup as well.
+               // which seems to confuse the popup as well.
                MathCompletionList::addToFavorites(inset->name());
 #endif
                lyx::dispatch(FuncRequest(LFUN_SELF_INSERT, " "));
@@ -2249,11 +2274,11 @@ std::string MathCompletionList::icon(size_t idx) const
        else
                cmd = locals[idx];
 
-       // get the icon resource name by stripping the backslash
+       // get the icon name by stripping the backslash
        docstring icon_name = frontend::Application::mathIcon(cmd.substr(1));
        if (icon_name.empty())
                return std::string();
-       return "images/math/" + to_utf8(icon_name);
+       return "math/" + to_utf8(icon_name);
 }
 
 std::vector<docstring> MathCompletionList::globals;