]> git.lyx.org Git - lyx.git/blobdiff - src/mathed/InsetMathNest.cpp
nullptr
[lyx.git] / src / mathed / InsetMathNest.cpp
index 61fc4d0b2ade25c17bd247239c0960f421e27a18..abaeec903c6c882661ee21f1f9b6a0a2d65c62a5 100644 (file)
@@ -3,7 +3,7 @@
  * This file is part of LyX, the document processor.
  * Licence details can be found in the file COPYING.
  *
- * \author André Pönitz
+ * \author André Pönitz
  *
  * Full author contact details are available in file CREDITS.
  */
 #include "InsetMathNest.h"
 
 #include "InsetMathArray.h"
+#include "InsetMathAMSArray.h"
 #include "InsetMathBig.h"
 #include "InsetMathBox.h"
 #include "InsetMathBrace.h"
+#include "InsetMathChar.h"
 #include "InsetMathColor.h"
 #include "InsetMathComment.h"
 #include "InsetMathDelim.h"
+#include "InsetMathEnsureMath.h"
 #include "InsetMathHull.h"
 #include "InsetMathRef.h"
 #include "InsetMathScript.h"
 #include "InsetMathSpace.h"
 #include "InsetMathSymbol.h"
 #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"
 
-#include "Bidi.h"
 #include "Buffer.h"
+#include "BufferParams.h"
 #include "BufferView.h"
 #include "CoordCache.h"
 #include "Cursor.h"
 #include "CutAndPaste.h"
 #include "DispatchResult.h"
+#include "Encoding.h"
 #include "FuncRequest.h"
 #include "FuncStatus.h"
-#include "LyXFunc.h"
+#include "LaTeXFeatures.h"
+#include "LyX.h"
 #include "LyXRC.h"
-#include "OutputParams.h"
+#include "MetricsInfo.h"
+#include "TexRow.h"
 #include "Text.h"
 
+#include "frontends/Application.h"
 #include "frontends/Clipboard.h"
 #include "frontends/Painter.h"
 #include "frontends/Selection.h"
 
-#include "support/lassert.h"
 #include "support/debug.h"
+#include "support/docstream.h"
 #include "support/gettext.h"
+#include "support/lassert.h"
 #include "support/lstrings.h"
 #include "support/textutils.h"
-#include "support/docstream.h"
 
 #include <algorithm>
 #include <sstream>
@@ -74,28 +81,51 @@ using cap::replaceSelection;
 using cap::selClearOrDel;
 
 
-InsetMathNest::InsetMathNest(idx_type nargs)
-       : cells_(nargs), lock_(false), mouse_hover_(false)
-{}
+InsetMathNest::InsetMathNest(Buffer * buf, idx_type nargs)
+       : InsetMath(buf), cells_(nargs), lock_(false)
+{
+       // 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);
+}
 
 
 InsetMathNest::InsetMathNest(InsetMathNest const & inset)
-       : InsetMath(inset), cells_(inset.cells_), lock_(inset.lock_),
-         mouse_hover_(false)
+       : InsetMath(inset), cells_(inset.cells_), lock_(inset.lock_)
 {}
 
 
+InsetMathNest::~InsetMathNest()
+{
+       map<BufferView const *, bool>::iterator it = mouse_hover_.begin();
+       map<BufferView const *, bool>::iterator end = mouse_hover_.end();
+       for (; it != end; ++it)
+               if (it->second)
+                       it->first->clearLastInset(this);
+}
+
+
 InsetMathNest & InsetMathNest::operator=(InsetMathNest const & inset)
 {
        cells_ = inset.cells_;
        lock_ = inset.lock_;
-       mouse_hover_ = false;
+       mouse_hover_.clear();
        InsetMath::operator=(inset);
        return *this;
 }
 
 
-InsetMath::idx_type InsetMathNest::nargs() const
+void InsetMathNest::setBuffer(Buffer & buffer)
+{
+       InsetMath::setBuffer(buffer);
+       for (MathData & data : cells_)
+               data.setBuffer(buffer);
+}
+
+
+idx_type InsetMathNest::nargs() const
 {
        return cells_.size();
 }
@@ -112,7 +142,7 @@ void InsetMathNest::cursorPos(BufferView const & bv,
 // to touch all (math)inset's draw() methods. Right now, we'll store
 // absolute value, and make them here relative, only to make them
 // absolute again when actually drawing the cursor. What a mess.
-       LASSERT(&sl.inset() == this, /**/);
+       LASSERT(&sl.inset() == this, return);
        MathData const & ar = sl.cell();
        CoordCache const & coord_cache = bv.coordCache();
        if (!coord_cache.getArrays().has(&ar)) {
@@ -140,24 +170,34 @@ 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;
+       }
 }
 
 
-void InsetMathNest::metrics(MetricsInfo const & mi) const
+void InsetMathNest::cellsMetrics(MetricsInfo const & mi) const
 {
        MetricsInfo m = mi;
-       for (idx_type i = 0, n = nargs(); i != n; ++i) {
+       for (auto const & cell : cells_) {
                Dimension dim;
-               cell(i).metrics(m, dim);
+               cell.metrics(m, dim);
        }
 }
 
 
+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, deleted);
+}
+
+
+
 bool InsetMathNest::idxNext(Cursor & cur) const
 {
-       LASSERT(&cur.inset() == this, /**/);
+       LASSERT(&cur.inset() == this, return false);
        if (cur.idx() == cur.lastidx())
                return false;
        ++cur.idx();
@@ -174,11 +214,11 @@ bool InsetMathNest::idxForward(Cursor & cur) const
 
 bool InsetMathNest::idxPrev(Cursor & cur) const
 {
-       LASSERT(&cur.inset() == this, /**/);
+       LASSERT(&cur.inset() == this, return false);
        if (cur.idx() == 0)
                return false;
        --cur.idx();
-       cur.pos() = cur.lastpos();
+       cur.pos() = lyxrc.mac_like_cursor_movement ? cur.lastpos() : 0;
        return true;
 }
 
@@ -191,10 +231,10 @@ bool InsetMathNest::idxBackward(Cursor & cur) const
 
 bool InsetMathNest::idxFirst(Cursor & cur) const
 {
-       LASSERT(&cur.inset() == this, /**/);
+       LASSERT(&cur.inset() == this, return false);
        if (nargs() == 0)
                return false;
-       cur.idx() = 0;
+       cur.idx() = firstIdx();
        cur.pos() = 0;
        return true;
 }
@@ -202,10 +242,10 @@ bool InsetMathNest::idxFirst(Cursor & cur) const
 
 bool InsetMathNest::idxLast(Cursor & cur) const
 {
-       LASSERT(&cur.inset() == this, /**/);
+       LASSERT(&cur.inset() == this, return false);
        if (nargs() == 0)
                return false;
-       cur.idx() = cur.lastidx();
+       cur.idx() = lastIdx();
        cur.pos() = cur.lastpos();
        return true;
 }
@@ -214,7 +254,8 @@ bool InsetMathNest::idxLast(Cursor & cur) const
 void InsetMathNest::dump() const
 {
        odocstringstream oss;
-       WriteStream os(oss);
+       otexrowstream ots(oss);
+       TeXMathStream os(ots);
        os << "---------------------------------------------\n";
        write(os);
        os << "\n";
@@ -225,62 +266,13 @@ void InsetMathNest::dump() const
 }
 
 
-void InsetMathNest::draw(PainterInfo & pi, int x, int y) const
+void InsetMathNest::draw(PainterInfo &, int, int) const
 {
 #if 0
        if (lock_)
                pi.pain.fillRectangle(x, y - ascent(), width(), height(),
                                        Color_mathlockbg);
 #endif
-       setPosCache(pi, x, y);
-}
-
-
-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);
-                       }
-               }
-       }
 }
 
 
@@ -334,19 +326,24 @@ MathData InsetMathNest::glue() const
 }
 
 
-void InsetMathNest::write(WriteStream & os) const
+void InsetMathNest::write(TeXMathStream & os) const
 {
-       ModeSpecifier specifier(os, currentMode());
+       MathEnsurer ensurer(os, currentMode() == MATH_MODE);
+       ModeSpecifier specifier(os, currentMode(), lockedMode());
        docstring const latex_name = name();
        os << '\\' << latex_name;
-       for (size_t i = 0; i < nargs(); ++i)
+       os.inMathClass(asClassInset());
+       for (size_t i = 0; i < nargs(); ++i) {
+               Changer dummy = os.changeRowEntry(TexRow::mathEntry(id(),i));
                os << '{' << cell(i) << '}';
+       }
        if (nargs() == 0)
                os.pendingSpace(true);
        if (lock_ && !os.latex()) {
                os << "\\lyxlock";
                os.pendingSpace(true);
        }
+       os.inMathClass(false);
 }
 
 
@@ -359,18 +356,35 @@ void InsetMathNest::normalize(NormalStream & os) const
 }
 
 
-int InsetMathNest::latex(odocstream & os, OutputParams const & runparams) const
+void InsetMathNest::latex(otexstream & os, OutputParams const & runparams) const
 {
-       WriteStream wi(os, runparams.moving_arg, true, runparams.dryrun,
-                       runparams.encoding);
+       TeXMathStream wi(os, runparams.moving_arg, true,
+                        runparams.dryrun ? TeXMathStream::wsDryrun : TeXMathStream::wsDefault,
+                        runparams.encoding);
+       wi.strikeoutMath(runparams.inDeletedInset);
+       if (runparams.inulemcmd) {
+               wi.ulemCmd(TeXMathStream::UNDERLINE);
+               if (runparams.local_font) {
+                       FontInfo f = runparams.local_font->fontInfo();
+                       if (f.strikeout() == FONT_ON)
+                               wi.ulemCmd(TeXMathStream::STRIKEOUT);
+               }
+       }
+       wi.canBreakLine(os.canBreakLine());
+       Changer dummy = wi.changeRowEntry(TexRow::textEntry(runparams.lastid,
+                                                           runparams.lastpos));
        write(wi);
-       return wi.line();
+       // Reset parbreak and command termination status after a math inset.
+       os.lastChar(0);
+       os.canBreakLine(wi.canBreakLine());
+       os.terminateCommand(false);
 }
 
 
-bool InsetMathNest::setMouseHover(bool mouse_hover)
+bool InsetMathNest::setMouseHover(BufferView const * bv, bool mouse_hover)
+       const
 {
-       mouse_hover_ = mouse_hover;
+       mouse_hover_[bv] = mouse_hover;
        return true;
 }
 
@@ -423,7 +437,7 @@ void InsetMathNest::handleFont(Cursor & cur, docstring const & arg,
        if (cur.inset().asInsetMath()->name() == font)
                cur.handleFont(to_utf8(font));
        else
-               handleNest(cur, createInsetMath(font), arg);
+               handleNest(cur, createInsetMath(font, cur.buffer()), arg);
 }
 
 
@@ -436,8 +450,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()) {
@@ -446,50 +460,42 @@ void InsetMathNest::handleNest(Cursor & cur, MathAtom const & nest,
                cur.insert(arg);
                return;
        }
-       
+
        // 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;
                        cur.resetAnchor();
                        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;
        }
-       
+
        // 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();
                }
        }
 }
@@ -503,49 +509,53 @@ void InsetMathNest::handleFont2(Cursor & cur, docstring const & arg)
        font.fromString(to_utf8(arg), b);
        if (font.fontInfo().color() != Color_inherit &&
            font.fontInfo().color() != Color_ignore)
-               handleNest(cur, MathAtom(new InsetMathColor(true, font.fontInfo().color())));
-       
+               handleNest(cur, MathAtom(new InsetMathColor(buffer_, true, font.fontInfo().color())));
+
        // FIXME: support other font changes here as well?
 }
 
-
 void InsetMathNest::doDispatch(Cursor & cur, FuncRequest & cmd)
 {
-       //lyxerr << "InsetMathNest: request: " << cmd << endl;
+       //LYXERR0("InsetMathNest: request: " << cmd);
 
-       switch (cmd.action) {
+       Parse::flags parseflg = Parse::QUIET | Parse::USETEXT;
 
+       FuncCode const act = cmd.action();
+       switch (act) {
+
+       case LFUN_CLIPBOARD_PASTE:
+               parseflg |= Parse::VERBATIM;
+               // fall through
        case LFUN_PASTE: {
+               if (cur.currentMode() != MATH_MODE)
+                       parseflg |= Parse::TEXTMODE;
                cur.recordUndoSelection();
                cur.message(_("Paste"));
                replaceSelection(cur);
                docstring topaste;
                if (cmd.argument().empty() && !theClipboard().isInternal())
-                       topaste = theClipboard().getAsText();
+                       topaste = theClipboard().getAsText(frontend::Clipboard::PlainTextType);
                else {
                        size_t n = 0;
                        idocstringstream is(cmd.argument());
                        is >> n;
-                       topaste = cap::selection(n);
+                       topaste = cap::selection(n, buffer().params().documentClassPtr());
                }
-               cur.niceInsert(topaste);
+               cur.niceInsert(topaste, parseflg, false);
                cur.clearSelection(); // bug 393
+               cur.forceBufferUpdate();
                cur.finishUndo();
                break;
        }
 
        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)
                cur.normalize();
-               break;
-
-       case LFUN_COPY:
-               copySelection(cur);
-               cur.message(_("Copy"));
+               cur.forceBufferUpdate();
                break;
 
        case LFUN_MOUSE_PRESS:
@@ -571,47 +581,75 @@ void InsetMathNest::doDispatch(Cursor & cur, FuncRequest & cmd)
                cur.bv().cursor() = cur;
                break;
 
+       case LFUN_WORD_RIGHT:
+       case LFUN_WORD_LEFT:
+       case LFUN_WORD_BACKWARD:
+       case LFUN_WORD_FORWARD:
        case LFUN_CHAR_RIGHT:
        case LFUN_CHAR_LEFT:
        case LFUN_CHAR_BACKWARD:
        case LFUN_CHAR_FORWARD:
-               cur.updateFlags(Update::Decoration | Update::FitCursor);
+               cur.screenUpdateFlags(Update::Decoration | Update::FitCursor);
+               // fall through
+       case LFUN_WORD_RIGHT_SELECT:
+       case LFUN_WORD_LEFT_SELECT:
+       case LFUN_WORD_BACKWARD_SELECT:
+       case LFUN_WORD_FORWARD_SELECT:
        case LFUN_CHAR_RIGHT_SELECT:
        case LFUN_CHAR_LEFT_SELECT:
        case LFUN_CHAR_BACKWARD_SELECT:
        case LFUN_CHAR_FORWARD_SELECT: {
                // are we in a selection?
-               bool select = (cmd.action == LFUN_CHAR_RIGHT_SELECT 
-                                          || cmd.action == LFUN_CHAR_LEFT_SELECT
-                                          || cmd.action == LFUN_CHAR_BACKWARD_SELECT
-                                          || cmd.action == LFUN_CHAR_FORWARD_SELECT);
-               // are we moving forward or backwards? 
+               bool select = (act == LFUN_WORD_RIGHT_SELECT
+                                          || act == LFUN_WORD_LEFT_SELECT
+                                          || act == LFUN_WORD_BACKWARD_SELECT
+                                          || act == LFUN_WORD_FORWARD_SELECT
+                              || act == LFUN_CHAR_RIGHT_SELECT
+                                          || act == LFUN_CHAR_LEFT_SELECT
+                                          || act == LFUN_CHAR_BACKWARD_SELECT
+                                          || act == LFUN_CHAR_FORWARD_SELECT);
+               // select words
+               bool word = (act == LFUN_WORD_RIGHT_SELECT
+                            || act == LFUN_WORD_LEFT_SELECT
+                            || act == LFUN_WORD_BACKWARD_SELECT
+                            || act == LFUN_WORD_FORWARD_SELECT
+                            || act == LFUN_WORD_RIGHT
+                            || act == LFUN_WORD_LEFT
+                            || act == LFUN_WORD_BACKWARD
+                            || act == LFUN_WORD_FORWARD);
+               // are we moving forward or backwards?
                // If the command was RIGHT or LEFT, then whether we're moving forward
                // or backwards depends on the cursor movement mode (logical or visual):
-               //  * in visual mode, since math is always LTR, right -> forward, 
+               //  * in visual mode, since math is always LTR, right -> forward,
                //    left -> backwards
                //  * in logical mode, the mapping is determined by the
                //    reverseDirectionNeeded() function
-               
+
                bool forward;
                FuncCode finish_lfun;
 
-               if (cmd.action == LFUN_CHAR_FORWARD 
-                               || cmd.action == LFUN_CHAR_FORWARD_SELECT) {
+               if (act == LFUN_CHAR_FORWARD
+                   || act == LFUN_CHAR_FORWARD_SELECT
+                   || act == LFUN_WORD_FORWARD
+                   || act == LFUN_WORD_FORWARD_SELECT) {
                        forward = true;
                        finish_lfun = LFUN_FINISHED_FORWARD;
                }
-               else if (cmd.action == LFUN_CHAR_BACKWARD
-                               || cmd.action == LFUN_CHAR_BACKWARD_SELECT) {
+               else if (act == LFUN_CHAR_BACKWARD
+                        || act == LFUN_CHAR_BACKWARD_SELECT
+                        || act == LFUN_WORD_BACKWARD
+                        || act == LFUN_WORD_BACKWARD_SELECT) {
                        forward = false;
                        finish_lfun = LFUN_FINISHED_BACKWARD;
                }
                else {
-                       bool right = (cmd.action == LFUN_CHAR_RIGHT_SELECT
-                                                 || cmd.action == LFUN_CHAR_RIGHT);
-                       if (lyxrc.visual_cursor || !reverseDirectionNeeded(cur))
+                       bool right = (act == LFUN_CHAR_RIGHT_SELECT
+                                                 || act == LFUN_CHAR_RIGHT
+                                     || act == LFUN_WORD_RIGHT_SELECT
+                                     || act == LFUN_WORD_RIGHT);
+                       if (lyxrc.visual_cursor || !cur.reverseDirectionNeeded())
                                forward = right;
-                       else 
+                       else
                                forward = !right;
 
                        if (right)
@@ -621,11 +659,10 @@ void InsetMathNest::doDispatch(Cursor & cur, FuncRequest & cmd)
                }
                // Now that we know exactly what we want to do, let's do it!
                cur.selHandle(select);
-               cur.autocorrect() = false;
                cur.clearTargetX();
                cur.macroModeClose();
                // try moving forward or backwards as necessary...
-               if (!(forward ? cursorMathForward(cur) : cursorMathBackward(cur))) {
+               if (!(forward ? cur.mathForward(word) : cur.mathBackward(word))) {
                        // ... and if movement failed, then finish forward or backwards
                        // as necessary
                        cmd = FuncRequest(finish_lfun);
@@ -636,26 +673,34 @@ void InsetMathNest::doDispatch(Cursor & cur, FuncRequest & cmd)
 
        case LFUN_DOWN:
        case LFUN_UP:
-               cur.updateFlags(Update::Decoration | Update::FitCursor);
+       case LFUN_PARAGRAPH_UP:
+       case LFUN_PARAGRAPH_DOWN:
+               cur.screenUpdateFlags(Update::Decoration | Update::FitCursor);
+               // fall through
        case LFUN_DOWN_SELECT:
-       case LFUN_UP_SELECT: {
+       case LFUN_UP_SELECT:
+       case LFUN_PARAGRAPH_UP_SELECT:
+       case LFUN_PARAGRAPH_DOWN_SELECT: {
                // close active macro
                if (cur.inMacroMode()) {
                        cur.macroModeClose();
                        break;
                }
-               
+
                // stop/start the selection
-               bool select = cmd.action == LFUN_DOWN_SELECT ||
-                       cmd.action == LFUN_UP_SELECT;
+               bool select = act == LFUN_DOWN_SELECT
+                       || act == LFUN_UP_SELECT
+                       || act == LFUN_PARAGRAPH_DOWN_SELECT
+                       || act == LFUN_PARAGRAPH_UP_SELECT;
                cur.selHandle(select);
-               
+
                // go up/down
-               bool up = cmd.action == LFUN_UP || cmd.action == LFUN_UP_SELECT;
+               bool up = act == LFUN_UP || act == LFUN_UP_SELECT
+                       || act == LFUN_PARAGRAPH_UP || act == LFUN_PARAGRAPH_UP_SELECT;
                bool successful = cur.upDownInMath(up);
                if (successful)
                        break;
-               
+
                if (cur.fixIfBroken())
                        // FIXME: Something bad happened. We pass the corrected Cursor
                        // instead of letting things go worse.
@@ -667,33 +712,29 @@ void InsetMathNest::doDispatch(Cursor & cur, FuncRequest & cmd)
        }
 
        case LFUN_MOUSE_DOUBLE:
-       case LFUN_MOUSE_TRIPLE:
        case LFUN_WORD_SELECT:
                cur.pos() = 0;
-               cur.idx() = 0;
-               cur.resetAnchor();
-               cur.setSelection(true);
+               cur.bv().mouseSetCursor(cur);
                cur.pos() = cur.lastpos();
-               cur.idx() = cur.lastidx();
+               cur.bv().mouseSetCursor(cur, true);
                break;
 
-       case LFUN_PARAGRAPH_UP:
-       case LFUN_PARAGRAPH_DOWN:
-               cur.updateFlags(Update::Decoration | Update::FitCursor);
-       case LFUN_PARAGRAPH_UP_SELECT:
-       case LFUN_PARAGRAPH_DOWN_SELECT:
+       case LFUN_MOUSE_TRIPLE:
+               cur.idx() = 0;
+               cur.pos() = 0;
+               cur.bv().mouseSetCursor(cur);
+               cur.idx() = cur.lastidx();
+               cur.pos() = cur.lastpos();
+               cur.bv().mouseSetCursor(cur, true);
                break;
 
        case LFUN_LINE_BEGIN:
-       case LFUN_WORD_BACKWARD:
-       case LFUN_WORD_LEFT:
-               cur.updateFlags(Update::Decoration | Update::FitCursor);
+               cur.screenUpdateFlags(Update::Decoration | Update::FitCursor);
+               // fall through
        case LFUN_LINE_BEGIN_SELECT:
-       case LFUN_WORD_BACKWARD_SELECT:
-       case LFUN_WORD_LEFT_SELECT:
-               cur.selHandle(cmd.action == LFUN_WORD_BACKWARD_SELECT ||
-                               cmd.action == LFUN_WORD_LEFT_SELECT || 
-                               cmd.action == LFUN_LINE_BEGIN_SELECT);
+               cur.selHandle(act == LFUN_WORD_BACKWARD_SELECT ||
+                               act == LFUN_WORD_LEFT_SELECT ||
+                               act == LFUN_LINE_BEGIN_SELECT);
                cur.macroModeClose();
                if (cur.pos() != 0) {
                        cur.pos() = 0;
@@ -709,16 +750,13 @@ void InsetMathNest::doDispatch(Cursor & cur, FuncRequest & cmd)
                }
                break;
 
-       case LFUN_WORD_FORWARD:
-       case LFUN_WORD_RIGHT:
        case LFUN_LINE_END:
-               cur.updateFlags(Update::Decoration | Update::FitCursor);
-       case LFUN_WORD_FORWARD_SELECT:
-       case LFUN_WORD_RIGHT_SELECT:
+               cur.screenUpdateFlags(Update::Decoration | Update::FitCursor);
+               // fall through
        case LFUN_LINE_END_SELECT:
-               cur.selHandle(cmd.action == LFUN_WORD_FORWARD_SELECT ||
-                               cmd.action == LFUN_WORD_RIGHT_SELECT ||
-                               cmd.action == LFUN_LINE_END_SELECT);
+               cur.selHandle(act == LFUN_WORD_FORWARD_SELECT ||
+                               act == LFUN_WORD_RIGHT_SELECT ||
+                               act == LFUN_LINE_END_SELECT);
                cur.macroModeClose();
                cur.clearTargetX();
                if (cur.pos() != cur.lastpos()) {
@@ -736,13 +774,25 @@ void InsetMathNest::doDispatch(Cursor & cur, FuncRequest & cmd)
                break;
 
        case LFUN_CELL_FORWARD:
-               cur.updateFlags(Update::Decoration | Update::FitCursor);
-               cur.inset().idxNext(cur);
+               cur.screenUpdateFlags(Update::Decoration | Update::FitCursor);
+               cur.selHandle(false);
+               cur.clearTargetX();
+               cur.macroModeClose();
+               if (!cur.inset().idxNext(cur)) {
+                       cur.idx() = firstIdx();
+                       cur.pos() = 0;
+               }
                break;
 
        case LFUN_CELL_BACKWARD:
-               cur.updateFlags(Update::Decoration | Update::FitCursor);
-               cur.inset().idxPrev(cur);
+               cur.screenUpdateFlags(Update::Decoration | Update::FitCursor);
+               cur.selHandle(false);
+               cur.clearTargetX();
+               cur.macroModeClose();
+               if (!cur.inset().idxPrev(cur)) {
+                       cur.idx() = lastIdx();
+                       cur.pos() = lyxrc.mac_like_cursor_movement ? cur.lastpos() : 0;
+               }
                break;
 
        case LFUN_WORD_DELETE_BACKWARD:
@@ -750,12 +800,12 @@ void InsetMathNest::doDispatch(Cursor & cur, FuncRequest & cmd)
                if (cur.pos() == 0)
                        // May affect external cell:
                        cur.recordUndoInset();
-               else
+               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;
 
@@ -767,9 +817,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;
 
@@ -777,8 +827,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;
 
@@ -791,44 +845,41 @@ 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
-               // 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.
-               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()) {
-                       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;
 
        //case LFUN_SERVER_GET_XY:
-       //      sprintf(dispatch_buffer, "%d %d",);
        //      break;
 
        case LFUN_SERVER_SET_XY: {
@@ -837,7 +888,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;
        }
 
@@ -875,37 +926,37 @@ void InsetMathNest::doDispatch(Cursor & cur, FuncRequest & cmd)
                break;
 
        case LFUN_FONT_BOLD:
-               if (currentMode() == TEXT_MODE)
+               if (currentMode() != MATH_MODE)
                        handleFont(cur, cmd.argument(), "textbf");
                else
                        handleFont(cur, cmd.argument(), "mathbf");
                break;
        case LFUN_FONT_BOLDSYMBOL:
-               if (currentMode() == TEXT_MODE)
+               if (currentMode() != MATH_MODE)
                        handleFont(cur, cmd.argument(), "textbf");
                else
                        handleFont(cur, cmd.argument(), "boldsymbol");
                break;
        case LFUN_FONT_SANS:
-               if (currentMode() == TEXT_MODE)
+               if (currentMode() != MATH_MODE)
                        handleFont(cur, cmd.argument(), "textsf");
                else
                        handleFont(cur, cmd.argument(), "mathsf");
                break;
        case LFUN_FONT_EMPH:
-               if (currentMode() == TEXT_MODE)
+               if (currentMode() != MATH_MODE)
                        handleFont(cur, cmd.argument(), "emph");
                else
                        handleFont(cur, cmd.argument(), "mathcal");
                break;
        case LFUN_FONT_ROMAN:
-               if (currentMode() == TEXT_MODE)
+               if (currentMode() != MATH_MODE)
                        handleFont(cur, cmd.argument(), "textrm");
                else
                        handleFont(cur, cmd.argument(), "mathrm");
                break;
        case LFUN_FONT_TYPEWRITER:
-               if (currentMode() == TEXT_MODE)
+               if (currentMode() != MATH_MODE)
                        handleFont(cur, cmd.argument(), "texttt");
                else
                        handleFont(cur, cmd.argument(), "mathtt");
@@ -914,13 +965,13 @@ void InsetMathNest::doDispatch(Cursor & cur, FuncRequest & cmd)
                handleFont(cur, cmd.argument(), "mathfrak");
                break;
        case LFUN_FONT_ITAL:
-               if (currentMode() == TEXT_MODE)
+               if (currentMode() != MATH_MODE)
                        handleFont(cur, cmd.argument(), "textit");
                else
                        handleFont(cur, cmd.argument(), "mathit");
                break;
        case LFUN_FONT_NOUN:
-               if (currentMode() == TEXT_MODE)
+               if (currentMode() != MATH_MODE)
                        // FIXME: should be "noun"
                        handleFont(cur, cmd.argument(), "textsc");
                else
@@ -929,6 +980,10 @@ void InsetMathNest::doDispatch(Cursor & cur, FuncRequest & cmd)
        case LFUN_FONT_DEFAULT:
                handleFont(cur, cmd.argument(), "textnormal");
                break;
+       case LFUN_FONT_UNDERLINE:
+               cur.recordUndo();
+               cur.handleNest(createInsetMath("underline", cur.buffer()));
+               break;
 
        case LFUN_MATH_MODE: {
 #if 1
@@ -939,15 +994,18 @@ void InsetMathNest::doDispatch(Cursor & cur, FuncRequest & cmd)
                cur.macroModeClose();
                docstring const save_selection = grabAndEraseSelection(cur);
                selClearOrDel(cur);
-               //cur.plainInsert(MathAtom(new InsetMathMBox(cur.bv())));
-               cur.plainInsert(MathAtom(new InsetMathBox(from_ascii("mbox"))));
+               if (currentMode() != Inset::MATH_MODE)
+                       cur.plainInsert(MathAtom(new InsetMathEnsureMath(buffer_)));
+               else
+                       cur.plainInsert(createInsetMath("text", buffer_));
                cur.posBackward();
                cur.pushBackward(*cur.nextInset());
                cur.niceInsert(save_selection);
+               cur.forceBufferUpdate();
 #else
                if (currentMode() == Inset::TEXT_MODE) {
                        cur.recordUndoSelection();
-                       cur.niceInsert(MathAtom(new InsetMathHull("simple")));
+                       cur.niceInsert(MathAtom(new InsetMathHull("simple", cur.buffer())));
                        cur.message(_("create new math text environment ($...$)"));
                } else {
                        handleFont(cur, cmd.argument(), "textrm");
@@ -957,6 +1015,32 @@ void InsetMathNest::doDispatch(Cursor & cur, FuncRequest & cmd)
                break;
        }
 
+       case LFUN_REGEXP_MODE: {
+               InsetMath * im = cur.inset().asInsetMath();
+               if (im) {
+                       InsetMathHull * i = im->asHullInset();
+                       if (i && i->getType() == hullRegexp) {
+                               cur.message(_("Already in regular expression mode"));
+                               break;
+                       }
+               }
+               cur.macroModeClose();
+               docstring const save_selection = grabAndEraseSelection(cur);
+               selClearOrDel(cur);
+               cur.plainInsert(MathAtom(new InsetMathHull(buffer_, hullRegexp)));
+               cur.posBackward();
+               cur.pushBackward(*cur.nextInset());
+               cur.niceInsert(save_selection);
+               cur.message(_("Regular expression editor mode"));
+               break;
+       }
+
+       case LFUN_MATH_FONT_STYLE: {
+               FuncRequest fr = FuncRequest(LFUN_MATH_INSERT, '\\' + cmd.argument());
+               doDispatch(cur, fr);
+               break;
+       }
+
        case LFUN_MATH_SIZE: {
                FuncRequest fr = FuncRequest(LFUN_MATH_INSERT, cmd.argument());
                doDispatch(cur, fr);
@@ -976,8 +1060,31 @@ void InsetMathNest::doDispatch(Cursor & cur, FuncRequest & cmd)
                if (n < 1)
                        n = 1;
                v_align += 'c';
+               cur.niceInsert(MathAtom(new InsetMathArray(buffer_,
+                       from_ascii("array"), m, n, (char)v_align[0], h_align)));
+               break;
+       }
+
+       case LFUN_MATH_AMS_MATRIX: {
+               cur.recordUndo();
+               unsigned int m = 1;
+               unsigned int n = 1;
+               docstring name = from_ascii("matrix");
+               idocstringstream is(cmd.argument());
+               is >> m >> n >> name;
+               if (m < 1)
+                       m = 1;
+               if (n < 1)
+                       n = 1;
+               // check if we have a valid decoration
+               if (name != "pmatrix" && name != "bmatrix"
+                       && name != "Bmatrix" && name != "vmatrix"
+                       && name != "Vmatrix" && name != "matrix"
+                       && name != "smallmatrix")
+                       name = from_ascii("matrix");
+
                cur.niceInsert(
-                       MathAtom(new InsetMathArray(from_ascii("array"), m, n, (char)v_align[0], h_align)));
+                       MathAtom(new InsetMathAMSArray(buffer_, name, m, n)));
                break;
        }
 
@@ -990,7 +1097,7 @@ void InsetMathNest::doDispatch(Cursor & cur, FuncRequest & cmd)
                if (rs.empty())
                        rs = ')';
                cur.recordUndo();
-               cur.handleNest(MathAtom(new InsetMathDelim(ls, rs)));
+               cur.handleNest(MathAtom(new InsetMathDelim(buffer_, ls, rs)));
                break;
        }
 
@@ -1014,27 +1121,58 @@ void InsetMathNest::doDispatch(Cursor & cur, FuncRequest & cmd)
                        if (have_l)
                                cur.insert(MathAtom(new InsetMathBig(lname,
                                                                ldelim)));
-                       cur.niceInsert(selection);
-                       if (have_r)
+                       // first insert the right delimiter and then go back
+                       // and re-insert the selection (bug 7088)
+                       if (have_r) {
                                cur.insert(MathAtom(new InsetMathBig(rname,
                                                                rdelim)));
+                               cur.posBackward();
+                       }
+                       cur.niceInsert(selection);
                }
                // Don't call cur.undispatched() if we did nothing, this would
                // lead to infinite recursion via Text::dispatch().
                break;
        }
 
-       case LFUN_SPACE_INSERT:
+       case LFUN_SPACE_INSERT: {
                cur.recordUndoSelection();
-               cur.insert(MathAtom(new InsetMathSpace(from_ascii(","))));
+               string const name = cmd.getArg(0);
+               if (name == "normal")
+                       cur.insert(MathAtom(new InsetMathSpace(" ", "")));
+               else if (name == "protected")
+                       cur.insert(MathAtom(new InsetMathSpace("~", "")));
+               else if (name == "thin" || name == "med" || name == "thick")
+                       cur.insert(MathAtom(new InsetMathSpace(name + "space", "")));
+               else if (name == "hfill*")
+                       cur.insert(MathAtom(new InsetMathSpace("hspace*{\\fill}", "")));
+               else if (name == "quad" || name == "qquad" ||
+                        name == "enspace" || name == "enskip" ||
+                        name == "negthinspace" || name == "negmedspace" ||
+                        name == "negthickspace" || name == "hfill")
+                       cur.insert(MathAtom(new InsetMathSpace(name, "")));
+               else if (name == "hspace" || name == "hspace*") {
+                       string const len = cmd.getArg(1);
+                       if (len.empty() || !isValidLength(len)) {
+                               lyxerr << "LyX function 'space-insert " << name << "' "
+                                         "needs a valid length argument." << endl;
+                               break;
+                       }
+                       cur.insert(MathAtom(new InsetMathSpace(name, len)));
+               } else
+                       cur.insert(MathAtom(new InsetMathSpace));
                break;
+       }
 
        case LFUN_MATH_SPACE:
                cur.recordUndoSelection();
                if (cmd.argument().empty())
-                       cur.insert(MathAtom(new InsetMathSpace(from_ascii(","))));
-               else
-                       cur.insert(MathAtom(new InsetMathSpace(cmd.argument())));
+                       cur.insert(MathAtom(new InsetMathSpace));
+               else {
+                       string const name = cmd.getArg(0);
+                       string const len = cmd.getArg(1);
+                       cur.insert(MathAtom(new InsetMathSpace(name, len)));
+               }
                break;
 
        case LFUN_ERT_INSERT:
@@ -1054,14 +1192,14 @@ void InsetMathNest::doDispatch(Cursor & cur, FuncRequest & cmd)
                cur.recordUndoSelection();
                interpretChar(cur, '^');
                break;
-               
+
        case LFUN_MATH_MACRO_FOLD:
        case LFUN_MATH_MACRO_UNFOLD: {
                Cursor it = cur;
-               bool fold = cmd.action == LFUN_MATH_MACRO_FOLD;
+               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);
@@ -1084,26 +1222,31 @@ 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 (ar.size() == 1 && ar[0]->asNestInset()
-                                       && ar[0]->asNestInset()->nargs() > 1)
+                       if (ar.size() == 1
+                           && ar[0]->asNestInset()
+                           && ar[0]->asNestInset()->nargs() > 1)
                                handleNest(cur, ar[0]);
                        else
                                cur.niceInsert(cmd.argument());
                }
                break;
-               }
+       }
 
        case LFUN_DIALOG_SHOW_NEW_INSET: {
                docstring const & name = cmd.argument();
                string data;
                if (name == "ref") {
-                       InsetMathRef tmp(name);
-                       data = tmp.createDialogStr(to_utf8(name));
+                       InsetMathRef tmp(buffer_, name);
+                       data = tmp.createDialogStr();
+                       cur.bv().showDialog(to_utf8(name), data);
+               } else if (name == "mathspace") {
+                       cur.bv().showDialog(to_utf8(name));
                }
-               cur.bv().showDialog(to_utf8(name), data);
                break;
        }
 
@@ -1112,6 +1255,7 @@ void InsetMathNest::doDispatch(Cursor & cur, FuncRequest & cmd)
                if (createInsetMath_fromDialogStr(cmd.argument(), ar)) {
                        cur.recordUndoSelection();
                        cur.insert(ar);
+                       cur.forceBufferUpdate();
                } else
                        cur.undispatched();
                break;
@@ -1123,6 +1267,34 @@ 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;
+       }
+
        default:
                InsetMath::doDispatch(cur, cmd);
                break;
@@ -1133,33 +1305,33 @@ void InsetMathNest::doDispatch(Cursor & cur, FuncRequest & cmd)
 bool InsetMathNest::findMacroToFoldUnfold(Cursor & it, bool fold) const {
        // look for macro to open/close, but stay in mathed
        for (; !it.empty(); it.pop_back()) {
-                       
+
                // 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)
                                        return true;
-                               
+
                                // Wrong folding state.
                                // If this was the first we see in this slice, look further left,
                                // otherwise go up.
                                if (inset != it.nextInset())
                                        break;
                        }
-                       
+
                        // go up if this was the left most position
                        if (it.pos() == 0)
                                break;
-                       
+
                        // go left
                        it.pos()--;
                        inset = it.nextInset();
                }
        }
-       
+
        return false;
 }
 
@@ -1171,12 +1343,9 @@ bool InsetMathNest::getStatus(Cursor & cur, FuncRequest const & cmd,
        //string tc = "mathnormal";
        bool ret = true;
        string const arg = to_utf8(cmd.argument());
-       switch (cmd.action) {
-       case LFUN_TABULAR_FEATURE:
-               flag.setEnabled(false);
-               break;
+       switch (cmd.action()) {
 #if 0
-       case LFUN_TABULAR_FEATURE:
+       case LFUN_INSET_MODIFY:
                // FIXME: check temporarily disabled
                // valign code
                char align = mathcursor::valign();
@@ -1207,35 +1376,42 @@ bool InsetMathNest::getStatus(Cursor & cur, FuncRequest const & cmd,
        case LFUN_FONT_DEFAULT:
                flag.setEnabled(true);
                break;
-       case LFUN_MATH_MUTATE:
-               //flag.setOnOff(mathcursor::formula()->hullType() == to_utf8(cmd.argument()));
-               flag.setOnOff(false);
-               break;
 
        // we just need to be in math mode to enable that
        case LFUN_MATH_SIZE:
        case LFUN_MATH_SPACE:
-       case LFUN_MATH_LIMITS:
        case LFUN_MATH_EXTERN:
                flag.setEnabled(true);
                break;
 
+       case LFUN_FONT_UNDERLINE:
        case LFUN_FONT_FRAK:
                flag.setEnabled(currentMode() != TEXT_MODE);
                break;
 
-       case LFUN_MATH_INSERT: {
+       case LFUN_MATH_FONT_STYLE: {
                bool const textarg =
-                       arg == "\\textbf"   || arg == "\\textsf" ||
-                       arg == "\\textrm"   || arg == "\\textmd" ||
-                       arg == "\\textit"   || arg == "\\textsc" ||
-                       arg == "\\textsl"   || arg == "\\textup" ||
-                       arg == "\\texttt"   || arg == "\\textbb" ||
-                       arg == "\\textnormal";
+                       arg == "textbf"   || arg == "textsf" ||
+                       arg == "textrm"   || arg == "textmd" ||
+                       arg == "textit"   || arg == "textsc" ||
+                       arg == "textsl"   || arg == "textup" ||
+                       arg == "texttt"   || arg == "textbb" ||
+                       arg == "textnormal";
                flag.setEnabled(currentMode() != TEXT_MODE || textarg);
                break;
        }
 
+       case LFUN_MATH_MODE:
+               // forbid "math-mode on" in math mode to prevent irritating
+               // behaviour of menu entries (bug 6709)
+               flag.setEnabled(currentMode() == TEXT_MODE || arg != "on");
+               break;
+
+       case LFUN_MATH_INSERT:
+               flag.setEnabled(currentMode() != TEXT_MODE);
+               break;
+
+       case LFUN_MATH_AMS_MATRIX:
        case LFUN_MATH_MATRIX:
                flag.setEnabled(currentMode() == MATH_MODE);
                break;
@@ -1243,35 +1419,85 @@ 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");
+               flag.setEnabled(name == "ref" || name == "mathspace");
+               break;
+       }
+
+       case LFUN_DIALOG_SHOW_NEW_INSET: {
+               docstring const & name = cmd.argument();
+               if (name == "space")
+                       flag.setEnabled(false);
                break;
        }
 
+
        case LFUN_MATH_DELIM:
        case LFUN_MATH_BIGDELIM:
                // Don't do this with multi-cell selections
                flag.setEnabled(cur.selBegin().idx() == cur.selEnd().idx());
                break;
-               
+
        case LFUN_MATH_MACRO_FOLD:
        case LFUN_MATH_MACRO_UNFOLD: {
                Cursor it = cur;
-               bool found = findMacroToFoldUnfold(it, cmd.action == LFUN_MATH_MACRO_FOLD);
+               bool found = findMacroToFoldUnfold(it, cmd.action() == LFUN_MATH_MACRO_FOLD);
                flag.setEnabled(found);
                break;
        }
-               
+
        case LFUN_SPECIALCHAR_INSERT:
+       case LFUN_SCRIPT_INSERT:
                // FIXME: These would probably make sense in math-text mode
                flag.setEnabled(false);
                break;
 
+       case LFUN_CAPTION_INSERT:
+               flag.setEnabled(false);
+               break;
+
+       case LFUN_SPACE_INSERT: {
+               docstring const & name = cmd.argument();
+               if (name == "visible")
+                       flag.setEnabled(false);
+               break;
+       }
+
        case LFUN_INSET_DISSOLVE:
                flag.setEnabled(!asHullInset());
                break;
 
+       case LFUN_PASTE: {
+               docstring const & name = cmd.argument();
+               if (name == "html" || name == "latex")
+                       flag.setEnabled(false);
+               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;
@@ -1283,10 +1509,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;
 }
@@ -1294,7 +1519,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);
@@ -1303,6 +1528,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;
@@ -1323,8 +1551,21 @@ void InsetMathNest::lfunMousePress(Cursor & cur, FuncRequest & cmd)
 {
        //lyxerr << "## lfunMousePress: buttons: " << cmd.button() << endl;
        BufferView & bv = cur.bv();
+       if (cmd.button() == mouse_button::button3) {
+               // Don't do anything if we right-click a
+               // selection, a context menu will popup.
+               if (bv.cursor().selection() && cur >= bv.cursor().selectionBegin()
+                     && cur < bv.cursor().selectionEnd()) {
+                       cur.noScreenUpdate();
+                       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.argument() == "region-select";
+               && cmd.modifier() == ShiftModifier;
        bv.mouseSetCursor(cur, do_selection);
        if (cmd.button() == mouse_button::button1) {
                //lyxerr << "## lfunMousePress: setting cursor to: " << cur << endl;
@@ -1336,9 +1577,9 @@ void InsetMathNest::lfunMousePress(Cursor & cur, FuncRequest & cmd)
                // Update::FitCursor: adjust the screen to the cursor
                //                    position if needed
                // cur.result().update(): don't overwrite previously set flags.
-               cur.updateFlags(Update::Decoration | Update::FitCursor 
-                               | cur.result().update());
-       } else if (cmd.button() == mouse_button::button2) {
+               cur.screenUpdateFlags(Update::Decoration | Update::FitCursor
+                               | cur.result().screenUpdate());
+       } else if (cmd.button() == mouse_button::button2 && lyxrc.mouse_middlebutton_paste) {
                if (cap::selection()) {
                        // See comment in Text::dispatch why we do this
                        cap::copySelectionToStack();
@@ -1356,16 +1597,32 @@ void InsetMathNest::lfunMousePress(Cursor & cur, FuncRequest & cmd)
 void InsetMathNest::lfunMouseMotion(Cursor & cur, FuncRequest & cmd)
 {
        // only select with button 1
-       if (cmd.button() == mouse_button::button1) {
-               Cursor & bvcur = cur.bv().cursor();
-               if (bvcur.anchor_.hasPart(cur)) {
-                       //lyxerr << "## lfunMouseMotion: cursor: " << cur << endl;
-                       bvcur.setCursor(cur);
-                       bvcur.setSelection(true);
-                       //lyxerr << "MOTION " << bvcur << endl;
-               } else
-                       cur.undispatched();
+       if (cmd.button() != mouse_button::button1)
+               return;
+
+       Cursor & bvcur = cur.bv().cursor();
+
+       // ignore motions deeper nested than the real anchor
+       if (!bvcur.realAnchor().hasPart(cur)) {
+               cur.undispatched();
+               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
+       // reset the anchor.
+       bvcur.setCursor(cur);
+       // Did we actually move?
+       if (cur.top() == old)
+               // We didn't move one iota, so no need to change selection status
+               // or update the screen.
+               cur.screenUpdateFlags(Update::SinglePar | Update::FitCursor);
+       else
+               bvcur.setSelection();
 }
 
 
@@ -1375,10 +1632,10 @@ void InsetMathNest::lfunMouseRelease(Cursor & cur, FuncRequest & cmd)
 
        if (cmd.button() == mouse_button::button1) {
                if (!cur.selection())
-                       cur.noUpdate();
+                       cur.noScreenUpdate();
                else {
                        Cursor & bvcur = cur.bv().cursor();
-                       bvcur.setSelection(true);
+                       bvcur.selection(true);
                }
                return;
        }
@@ -1387,14 +1644,20 @@ void InsetMathNest::lfunMouseRelease(Cursor & cur, FuncRequest & cmd)
 }
 
 
-bool InsetMathNest::interpretChar(Cursor & cur, char_type c)
+bool InsetMathNest::interpretChar(Cursor & cur, char_type const c)
 {
+        // try auto-correction
+        if (lyxrc.autocorrection_math && cur.pos() != 0
+                  && math_autocorrect(cur, c))
+                return true;
+
        //lyxerr << "interpret 2: '" << c << "'" << endl;
        docstring save_selection;
        if (c == '^' || c == '_')
                save_selection = grabAndEraseSelection(cur);
 
        cur.clearTargetX();
+       Buffer * buf = cur.buffer();
 
        // handle macroMode
        if (cur.inMacroMode()) {
@@ -1405,15 +1668,14 @@ bool InsetMathNest::interpretChar(Cursor & cur, char_type c)
                        cur.backspace();
                        int n = c - '0';
                        if (n >= 1 && n <= 9)
-                               cur.insert(new MathMacroArgument(n));
+                               cur.insert(new InsetMathMacroArgument(n));
                        return true;
                }
 
                // do not finish macro for known * commands
-               MathWordList const & mwl = mathedWordList();
                bool star_macro = c == '*'
-                       && (mwl.find(name.substr(1) + "*") != mwl.end()
-                           || cur.buffer().getMacro(name.substr(1) + "*", cur, true));
+                       && (in_word_set(name.substr(1) + '*')
+                           || cur.buffer()->getMacro(name.substr(1) + "*", cur, true));
                if (isAlphaASCII(c) || star_macro) {
                        cur.activeMacro()->setName(name + docstring(1, c));
                        return true;
@@ -1424,22 +1686,30 @@ bool InsetMathNest::interpretChar(Cursor & cur, char_type c)
                        // remove the '\\'
                        if (c == '\\') {
                                cur.backspace();
-                               if (currentMode() == InsetMath::TEXT_MODE)
-                                       cur.niceInsert(createInsetMath("textbackslash"));
+                               if (currentMode() != InsetMath::MATH_MODE)
+                                       cur.niceInsert(createInsetMath("textbackslash", buf));
                                else
-                                       cur.niceInsert(createInsetMath("backslash"));
-                       } else if (c == '{') {
+                                       cur.niceInsert(createInsetMath("backslash", buf));
+                       } else if (c == '^' && currentMode() == InsetMath::MATH_MODE) {
                                cur.backspace();
-                               cur.niceInsert(MathAtom(new InsetMathBrace));
-                       } else if (c == '%') {
+                               cur.niceInsert(createInsetMath("mathcircumflex", buf));
+                       } else if (c == '{' || c == '%') {
+                               //using the saved selection as argument
+                               InsetMathUnknown * p = cur.activeMacro();
+                               p->finalize();
+                               MathData sel(cur.buffer());
+                               asArray(p->selection(), sel);
                                cur.backspace();
-                               cur.niceInsert(MathAtom(new InsetMathComment));
+                               if (c == '{')
+                                       cur.niceInsert(MathAtom(new InsetMathBrace(sel)));
+                               else
+                                       cur.niceInsert(MathAtom(new InsetMathComment(sel)));
                        } else if (c == '#') {
-                               LASSERT(cur.activeMacro(), /**/);
+                               LASSERT(cur.activeMacro(), return false);
                                cur.activeMacro()->setName(name + docstring(1, c));
                        } else {
                                cur.backspace();
-                               cur.niceInsert(createInsetMath(docstring(1, c)));
+                               cur.niceInsert(createInsetMath(docstring(1, c), buf));
                        }
                        return true;
                }
@@ -1472,31 +1742,34 @@ bool InsetMathNest::interpretChar(Cursor & cur, char_type c)
                                        new InsetMathBig(name.substr(1), delim)));
                                return true;
                        }
+               } else if (name == "\\smash" && c == '[') {
+                       // We can't use cur.macroModeClose() because
+                       // it would create an InsetMathPhantom
+                       InsetMathUnknown * p = cur.activeMacro();
+                       p->finalize();
+                       interpretChar(cur, c);
+                       return true;
                }
 
                // leave macro mode and try again if necessary
-               cur.macroModeClose();
+               if (cur.macroModeClose()) {
+                       MathAtom const atom = cur.prevAtom();
+                       if (atom->asNestInset() && atom->isActive()) {
+                               cur.posBackward();
+                               cur.nextInset()->edit(cur, true);
+                       }
+               }
                if (c == '{')
-                       cur.niceInsert(MathAtom(new InsetMathBrace));
+                       cur.niceInsert(MathAtom(new InsetMathBrace(buf)));
                else if (c != ' ')
                        interpretChar(cur, c);
                return true;
        }
 
-       // This is annoying as one has to press <space> far too often.
-       // Disable it.
-
-#if 0
-               // leave autocorrect mode if necessary
-               if (autocorrect() && c == ' ') {
-                       autocorrect() = false;
-                       return true;
-               }
-#endif
 
        // just clear selection on pressing the space bar
        if (cur.selection() && c == ' ') {
-               cur.setSelection(false);
+               cur.selection(false);
                return true;
        }
 
@@ -1504,8 +1777,12 @@ bool InsetMathNest::interpretChar(Cursor & cur, char_type c)
                //lyxerr << "starting with macro" << endl;
                bool reduced = cap::reduceSelectionToOneCell(cur);
                if (reduced || !cur.selection()) {
+                       cur.recordUndoInset();
                        docstring const safe = cap::grabAndEraseSelection(cur);
-                       cur.insert(MathAtom(new InsetMathUnknown(from_ascii("\\"), safe, false)));
+                       if (!cur.inRegexped())
+                               cur.insert(MathAtom(new InsetMathUnknown(from_ascii("\\"), safe, false)));
+                       else
+                               cur.niceInsert(createInsetMath("backslash", buf));
                }
                return true;
        }
@@ -1513,24 +1790,24 @@ bool InsetMathNest::interpretChar(Cursor & cur, char_type c)
        selClearOrDel(cur);
 
        if (c == '\n') {
-               if (currentMode() == InsetMath::TEXT_MODE)
+               if (currentMode() != InsetMath::MATH_MODE)
                        cur.insert(c);
                return true;
        }
 
        if (c == ' ') {
-               if (currentMode() == InsetMath::TEXT_MODE) {
-                       // insert spaces in text mode,
+               if (currentMode() != InsetMath::MATH_MODE) {
+                       // insert spaces in text or undecided mode,
                        // 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() != ' ') {
+                       if (cur.pos() == 0 || cur.prevAtom()->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
                                // sure that we stay within the same cell we can optimize for
                                // that using:
-                               //cur.updateFlags(Update::SinglePar | Update::FitCursor);
+                               //cur.screenUpdateFlags(Update::SinglePar | Update::FitCursor);
                        }
                        return true;
                }
@@ -1540,7 +1817,7 @@ bool InsetMathNest::interpretChar(Cursor & cur, char_type c)
                        // visual box corners that define the inset. If we know for
                        // sure that we stay within the same cell we can optimize for
                        // that using:
-                       //cur.updateFlags(Update::SinglePar | Update::FitCursor);
+                       //cur.screenUpdateFlags(Update::SinglePar | Update::FitCursor);
                        return true;
                }
 
@@ -1549,7 +1826,7 @@ bool InsetMathNest::interpretChar(Cursor & cur, char_type c)
                        // visual box corners that define the inset. If we know for
                        // sure that we stay within the same cell we can optimize for
                        // that using:
-                       //cur.updateFlags(Update::FitCursor);
+                       //cur.screenUpdateFlags(Update::FitCursor);
                        return true;
                }
 
@@ -1557,8 +1834,27 @@ bool InsetMathNest::interpretChar(Cursor & cur, char_type c)
                return cur.pos() != cur.lastpos();
        }
 
-       // These shouldn't work in text mode:
-       if (currentMode() != InsetMath::TEXT_MODE) {
+       // These should be treated differently when not in text mode:
+       if (cur.inRegexped()) {
+               switch (c) {
+               case '^':
+                       cur.niceInsert(createInsetMath("mathcircumflex", buf));
+                       break;
+               case '{':
+               case '}':
+               case '#':
+               case '%':
+               case '_':
+                       cur.niceInsert(createInsetMath(docstring(1, c), buf));
+                       break;
+               case '~':
+                       cur.niceInsert(createInsetMath("sim", buf));
+                       break;
+               default:
+                       cur.insert(c);
+               }
+               return true;
+       } else if (currentMode() != InsetMath::TEXT_MODE) {
                if (c == '_') {
                        script(cur, false, save_selection);
                        return true;
@@ -1568,31 +1864,50 @@ bool InsetMathNest::interpretChar(Cursor & cur, char_type c)
                        return true;
                }
                if (c == '~') {
-                       cur.niceInsert(createInsetMath("sim"));
+                       cur.niceInsert(createInsetMath("sim", buf));
+                       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.insert(at);
+                       cur.posForward();
+                       return true;
+               }
+       } else {
+               if (c == '^') {
+                       cur.niceInsert(createInsetMath("textasciicircum", buf));
+                       return true;
+               }
+               if (c == '~') {
+                       cur.niceInsert(createInsetMath("textasciitilde", buf));
                        return true;
                }
        }
 
        if (c == '{' || c == '}' || c == '&' || c == '$' || c == '#' ||
-           c == '%' || c == '_' || c == '^') {
-               cur.niceInsert(createInsetMath(docstring(1, c)));
+           c == '%' || c == '_') {
+               cur.niceInsert(createInsetMath(docstring(1, c), buf));
                return true;
        }
 
-
-       // try auto-correction
-       //if (autocorrect() && hasPrevAtom() && math_autocorrect(prevAtom(), c))
-       //      return true;
-
        // no special circumstances, so insert the character without any fuss
        cur.insert(c);
-       cur.autocorrect() = true;
        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 &&
@@ -1603,6 +1918,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;
@@ -1627,7 +1943,7 @@ bool InsetMathNest::script(Cursor & cur, bool up,
        //lyxerr << "handling script: up: " << up << endl;
        if (cur.inMacroMode() && cur.macroName() == "\\") {
                if (up)
-                       cur.niceInsert(createInsetMath("mathcircumflex"));
+                       cur.niceInsert(createInsetMath("mathcircumflex", cur.buffer()));
                else
                        interpretChar(cur, '_');
                return true;
@@ -1653,10 +1969,10 @@ bool InsetMathNest::script(Cursor & cur, bool up,
                // one if in the very first position of the array
                if (cur.pos() == 0) {
                        //lyxerr << "new scriptinset" << endl;
-                       cur.insert(new InsetMathScript(up));
+                       cur.insert(new InsetMathScript(buffer_, up));
                } else {
                        //lyxerr << "converting prev atom " << endl;
-                       cur.prevAtom() = MathAtom(new InsetMathScript(cur.prevAtom(), up));
+                       cur.prevAtom() = MathAtom(new InsetMathScript(buffer_, cur.prevAtom(), up));
                }
                --cur.pos();
                InsetMathScript * inset = cur.nextAtom().nucleus()->asScriptInset();
@@ -1702,8 +2018,8 @@ CompletionList const *
 InsetMathNest::createCompletionList(Cursor const & cur) const
 {
        if (!cur.inMacroMode())
-               return 0;
-       
+               return nullptr;
+
        return new MathCompletionList(cur);
 }
 
@@ -1712,7 +2028,7 @@ docstring InsetMathNest::completionPrefix(Cursor const & cur) const
 {
        if (!cur.inMacroMode())
                return docstring();
-       
+
        return cur.activeMacro()->name();
 }
 
@@ -1732,8 +2048,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, " "));
@@ -1743,7 +2058,7 @@ bool InsetMathNest::insertCompletion(Cursor & cur, docstring const & s,
 }
 
 
-void InsetMathNest::completionPosAndDim(Cursor const & cur, int & x, int & y, 
+void InsetMathNest::completionPosAndDim(Cursor const & cur, int & x, int & y,
                                        Dimension & dim) const
 {
        Inset const * inset = cur.activeMacro();
@@ -1758,70 +2073,32 @@ void InsetMathNest::completionPosAndDim(Cursor const & cur, int & x, int & y,
        dim.asc += 3;
 
        // and position
-       Point xy
-       = cur.bv().coordCache().insets().xy(inset);
+       Point xy = cur.bv().coordCache().insets().xy(inset);
        x = xy.x_;
        y = xy.y_;
 }
 
 
-bool InsetMathNest::cursorMathForward(Cursor & cur)
-{
-       if (cur.pos() != cur.lastpos() && cur.openable(cur.nextAtom())) {
-               cur.pushBackward(*cur.nextAtom().nucleus());
-               cur.inset().idxFirst(cur);
-               return true;
-       } 
-       if (cur.posForward() || idxForward(cur))
-               return true;
-       // try to pop forwards --- but don't pop out of math! leave that to
-       // the FINISH lfuns
-       int s = cur.depth() - 2;
-       if (s >= 0 && cur[s].inset().asInsetMath())
-               return cur.popForward();
-       return false;
-}
-
-
-bool InsetMathNest::cursorMathBackward(Cursor & cur)
-{
-       if (cur.pos() != 0 && cur.openable(cur.prevAtom())) {
-               cur.posBackward();
-               cur.push(*cur.nextAtom().nucleus());
-               cur.inset().idxLast(cur);
-               return true;
-       } 
-       if (cur.posBackward() || idxBackward(cur))
-               return true;
-       // try to pop backwards --- but don't pop out of math! leave that to 
-       // the FINISH lfuns
-       int s = cur.depth() - 2;
-       if (s >= 0 && cur[s].inset().asInsetMath())
-               return cur.popBackward();
-       return false;
-}
-
-
 ////////////////////////////////////////////////////////////////////
 
 MathCompletionList::MathCompletionList(Cursor const & cur)
 {
        // fill it with macros from the buffer
        MacroNameSet macros;
-       cur.buffer().listMacroNames(macros);
+       cur.buffer()->listMacroNames(macros);
        MacroNameSet::const_iterator it;
        for (it = macros.begin(); it != macros.end(); ++it) {
-               if (cur.buffer().getMacro(*it, cur, false))
+               if (cur.buffer()->getMacro(*it, cur, false))
                        locals.push_back("\\" + *it);
        }
        sort(locals.begin(), locals.end());
 
-       if (globals.size() > 0)
+       if (!globals.empty())
                return;
 
        // fill in global macros
        macros.clear();
-       MacroTable::globalMacros().getMacroNames(macros);
+       MacroTable::globalMacros().getMacroNames(macros, false);
        //lyxerr << "Globals completion macros: ";
        for (it = macros.begin(); it != macros.end(); ++it) {
                //lyxerr << "\\" + *it << " ";
@@ -1835,8 +2112,21 @@ MathCompletionList::MathCompletionList(Cursor const & cur)
        globals.push_back(from_ascii("\\framebox"));
        globals.push_back(from_ascii("\\makebox"));
        globals.push_back(from_ascii("\\kern"));
+       globals.push_back(from_ascii("\\xhookrightarrow"));
+       globals.push_back(from_ascii("\\xhookleftarrow"));
        globals.push_back(from_ascii("\\xrightarrow"));
+       globals.push_back(from_ascii("\\xRightarrow"));
+       globals.push_back(from_ascii("\\xrightharpoondown"));
+       globals.push_back(from_ascii("\\xrightharpoonup"));
+       globals.push_back(from_ascii("\\xrightleftharpoons"));
        globals.push_back(from_ascii("\\xleftarrow"));
+       globals.push_back(from_ascii("\\xLeftarrow"));
+       globals.push_back(from_ascii("\\xleftharpoondown"));
+       globals.push_back(from_ascii("\\xleftharpoonup"));
+       globals.push_back(from_ascii("\\xleftrightarrow"));
+       globals.push_back(from_ascii("\\xLeftrightarrow"));
+       globals.push_back(from_ascii("\\xleftrightharpoons"));
+       globals.push_back(from_ascii("\\xmapsto"));
        globals.push_back(from_ascii("\\split"));
        globals.push_back(from_ascii("\\gathered"));
        globals.push_back(from_ascii("\\aligned"));
@@ -1844,12 +2134,15 @@ MathCompletionList::MathCompletionList(Cursor const & cur)
        globals.push_back(from_ascii("\\cases"));
        globals.push_back(from_ascii("\\substack"));
        globals.push_back(from_ascii("\\xymatrix"));
+       globals.push_back(from_ascii("\\Diagram"));
        globals.push_back(from_ascii("\\subarray"));
        globals.push_back(from_ascii("\\array"));
        globals.push_back(from_ascii("\\sqrt"));
        globals.push_back(from_ascii("\\root"));
        globals.push_back(from_ascii("\\tabular"));
+       globals.push_back(from_ascii("\\sideset"));
        globals.push_back(from_ascii("\\stackrel"));
+       globals.push_back(from_ascii("\\stackrelthree"));
        globals.push_back(from_ascii("\\binom"));
        globals.push_back(from_ascii("\\choose"));
        globals.push_back(from_ascii("\\brace"));
@@ -1869,6 +2162,9 @@ MathCompletionList::MathCompletionList(Cursor const & cur)
        globals.push_back(from_ascii("\\color"));
        globals.push_back(from_ascii("\\normalcolor"));
        globals.push_back(from_ascii("\\textcolor"));
+       globals.push_back(from_ascii("\\cfrac"));
+       globals.push_back(from_ascii("\\cfracleft"));
+       globals.push_back(from_ascii("\\cfracright"));
        globals.push_back(from_ascii("\\dfrac"));
        globals.push_back(from_ascii("\\tfrac"));
        globals.push_back(from_ascii("\\dbinom"));
@@ -1876,12 +2172,24 @@ MathCompletionList::MathCompletionList(Cursor const & cur)
        globals.push_back(from_ascii("\\hphantom"));
        globals.push_back(from_ascii("\\phantom"));
        globals.push_back(from_ascii("\\vphantom"));
+       globals.push_back(from_ascii("\\cancel"));
+       globals.push_back(from_ascii("\\bcancel"));
+       globals.push_back(from_ascii("\\xcancel"));
+       globals.push_back(from_ascii("\\cancelto"));
+       globals.push_back(from_ascii("\\smash"));
+       globals.push_back(from_ascii("\\mathclap"));
+       globals.push_back(from_ascii("\\mathllap"));
+       globals.push_back(from_ascii("\\mathrlap"));
+       globals.push_back(from_ascii("\\ensuremath"));
        MathWordList const & words = mathedWordList();
        MathWordList::const_iterator it2;
        //lyxerr << "Globals completion commands: ";
        for (it2 = words.begin(); it2 != words.end(); ++it2) {
-               globals.push_back("\\" + (*it2).first);
-               //lyxerr << "\\" + (*it2).first << " ";
+               if (it2->second.inset != "macro" && !it2->second.hidden) {
+                       // macros are already read from MacroTable::globalMacros()
+                       globals.push_back('\\' + it2->first);
+                       //lyxerr << '\\' + it2->first << ' ';
+               }
        }
        //lyxerr << std::endl;
        sort(globals.begin(), globals.end());
@@ -1909,7 +2217,7 @@ docstring const & MathCompletionList::data(size_t idx) const
 }
 
 
-std::string MathCompletionList::icon(size_t idx) const 
+std::string MathCompletionList::icon(size_t idx) const
 {
        // get the latex command
        docstring cmd;
@@ -1918,9 +2226,12 @@ std::string MathCompletionList::icon(size_t idx) const
                cmd = globals[idx - lsize];
        else
                cmd = locals[idx];
-       
-       // get the icon resource name by stripping the backslash
-       return "images/math/" + to_utf8(cmd.substr(1)) + ".png";
+
+       // 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 "math/" + to_utf8(icon_name);
 }
 
 std::vector<docstring> MathCompletionList::globals;