]> git.lyx.org Git - lyx.git/blobdiff - src/Text3.cpp
#9130 Text in main work area isn't rendered with high resolution
[lyx.git] / src / Text3.cpp
index aed5068e150739caa3674826432012680399e8ba..79711f43732769b8555e611328e9b74d0588ef3a 100644 (file)
@@ -73,6 +73,7 @@
 #include "support/lstrings.h"
 #include "support/lyxtime.h"
 #include "support/os.h"
+#include "support/regex.h"
 
 #include "mathed/InsetMathHull.h"
 #include "mathed/MathMacroTemplate.h"
@@ -408,7 +409,8 @@ static void outline(OutlineOp mode, Cursor & cur)
                        cur.pit() = newpit - len;
                        break;
                }
-               case OutlineIn: {
+               case OutlineIn:
+               case OutlineOut: {
                        pit_type const len = distance(start, finish);
                        buf.undo().recordUndo(cur, ATOMIC_UNDO, pit, pit + len - 1);
                        for (; start != finish; ++start) {
@@ -418,7 +420,8 @@ static void outline(OutlineOp mode, Cursor & cur)
                                DocumentClass::const_iterator lit = tc.begin();
                                DocumentClass::const_iterator len = tc.end();
                                for (; lit != len; ++lit) {
-                                       if (lit->toclevel == toclevel + 1 &&
+                                       if (lit->toclevel == (mode == OutlineIn ?
+                                                             toclevel + 1 : toclevel - 1) &&
                                            start->layout().labeltype == lit->labeltype) {
                                                start->setLayout(*lit);
                                                break;
@@ -427,25 +430,6 @@ static void outline(OutlineOp mode, Cursor & cur)
                        }
                        break;
                }
-               case OutlineOut: {
-                       pit_type const len = distance(start, finish);
-                       buf.undo().recordUndo(cur, ATOMIC_UNDO, pit, pit + len - 1);
-                       for (; start != finish; ++start) {
-                               toclevel = buf.text().getTocLevel(distance(bgn, start));
-                               if (toclevel == Layout::NOT_IN_TOC)
-                                       continue;
-                               DocumentClass::const_iterator lit = tc.begin();
-                               DocumentClass::const_iterator len = tc.end();
-                               for (; lit != len; ++lit) {
-                                       if (lit->toclevel == toclevel - 1 &&
-                                               start->layout().labeltype == lit->labeltype) {
-                                                       start->setLayout(*lit);
-                                                       break;
-                                       }
-                               }
-                       }
-                       break;
-               }
        }
 }
 
@@ -488,7 +472,7 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
        // at the end?
        cur.noScreenUpdate();
 
-       LBUFERR(this == cur.text(), _("Invalid cursor."));
+       LBUFERR(this == cur.text());
        CursorSlice const oldTopSlice = cur.top();
        bool const oldBoundary = cur.boundary();
        bool const oldSelection = cur.selection();
@@ -501,7 +485,7 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
                LyXAction::NoUpdate) || singleParUpdate);
        bool const last_misspelled = lyxrc.spellcheck_continuously
                && cur.paragraph().isMisspelled(cur.pos(), true);
-       
+
        FuncCode const act = cmd.action();
        switch (act) {
 
@@ -567,7 +551,7 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
                finishChange(cur, false);
                break;
 
-       case LFUN_LINE_DELETE:
+       case LFUN_LINE_DELETE_FORWARD:
                if (cur.selection())
                        cutSelection(cur, true, false);
                else
@@ -622,7 +606,7 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
                        needsUpdate |= cursorTop(cur);
                        needsUpdate |= cur.selHandle(true);
                        needsUpdate |= cursorBottom(cur);
-               } else 
+               } else
                        cur.undispatched();
                cur.screenUpdateFlags(Update::FitCursor);
                break;
@@ -637,23 +621,19 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
                                && cur.boundary() == oldBoundary) {
                        cur.undispatched();
                        cmd = FuncRequest(LFUN_FINISHED_FORWARD);
-               
+
                        // we will probably be moving out the inset, so we should execute
-                       // the depm-mechanism, but only when the cursor has a place to 
+                       // the depm-mechanism, but only when the cursor has a place to
                        // go outside this inset, i.e. in a slice above.
-                       if (cur.depth() > 1 && cur.pos() == cur.lastpos() 
+                       if (cur.depth() > 1 && cur.pos() == cur.lastpos()
                                  && cur.pit() == cur.lastpit()) {
-                               // The cursor hasn't changed yet. To give the 
+                               // The cursor hasn't changed yet. To give the
                                // DEPM the possibility of doing something we must
                                // provide it with two different cursors.
                                Cursor dummy = cur;
                                dummy.pos() = dummy.pit() = 0;
-                               if (cur.bv().checkDepm(dummy, cur)) {
+                               if (cur.bv().checkDepm(dummy, cur))
                                        cur.forceBufferUpdate();
-                                       // DEPM may have requested a screen update
-                                       cur.screenUpdateFlags(
-                                               cur.screenUpdate() | dummy.screenUpdate());
-                               }
                        }
                }
                break;
@@ -670,21 +650,17 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
                        cmd = FuncRequest(LFUN_FINISHED_BACKWARD);
 
                        // we will probably be moving out the inset, so we should execute
-                       // the depm-mechanism, but only when the cursor has a place to 
+                       // the depm-mechanism, but only when the cursor has a place to
                        // go outside this inset, i.e. in a slice above.
                        if (cur.depth() > 1 && cur.pos() == 0 && cur.pit() == 0) {
-                               // The cursor hasn't changed yet. To give the 
+                               // The cursor hasn't changed yet. To give the
                                // DEPM the possibility of doing something we must
                                // provide it with two different cursors.
                                Cursor dummy = cur;
                                dummy.pos() = cur.lastpos();
                                dummy.pit() = cur.lastpit();
-                               if (cur.bv().checkDepm(dummy, cur)) {
+                               if (cur.bv().checkDepm(dummy, cur))
                                        cur.forceBufferUpdate();
-                                       // DEPM may have requested a screen update
-                                       cur.screenUpdateFlags(
-                                               cur.screenUpdate() | dummy.screenUpdate());
-                               }
                        }
                }
                break;
@@ -749,15 +725,14 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
                bool const atFirstOrLastRow = cur.atFirstOrLastRow(up);
 
                if (!atFirstOrLastRow) {
-                       needsUpdate |= cur.selHandle(select);   
-                       cur.selHandle(select);
+                       needsUpdate |= cur.selHandle(select);
                        cur.upDownInText(up, needsUpdate);
                        needsUpdate |= cur.beforeDispatchCursor().inMathed();
                } else {
                        // if the cursor cannot be moved up or down do not remove
                        // the selection right now, but wait for the next dispatch.
                        if (select)
-                               needsUpdate |= cur.selHandle(select);   
+                               needsUpdate |= cur.selHandle(select);
                        cur.upDownInText(up, needsUpdate);
                        cur.undispatched();
                }
@@ -819,7 +794,7 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
                                break;
                }
                cur.pos() = cur.lastpos();
-               
+
                needsUpdate |= cur != old_cur;
                break;
        }
@@ -856,23 +831,19 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
                                && cur.boundary() == oldBoundary) {
                        cur.undispatched();
                        cmd = FuncRequest(LFUN_FINISHED_FORWARD);
-               
+
                        // we will probably be moving out the inset, so we should execute
-                       // the depm-mechanism, but only when the cursor has a place to 
+                       // the depm-mechanism, but only when the cursor has a place to
                        // go outside this inset, i.e. in a slice above.
-                       if (cur.depth() > 1 && cur.pos() == cur.lastpos() 
+                       if (cur.depth() > 1 && cur.pos() == cur.lastpos()
                                  && cur.pit() == cur.lastpit()) {
-                               // The cursor hasn't changed yet. To give the 
+                               // The cursor hasn't changed yet. To give the
                                // DEPM the possibility of doing something we must
                                // provide it with two different cursors.
                                Cursor dummy = cur;
                                dummy.pos() = dummy.pit() = 0;
-                               if (cur.bv().checkDepm(dummy, cur)) {
+                               if (cur.bv().checkDepm(dummy, cur))
                                        cur.forceBufferUpdate();
-                                       // DEPM may have requested a screen update
-                                       cur.screenUpdateFlags(
-                                               cur.screenUpdate() | dummy.screenUpdate());
-                               }
                        }
                }
                break;
@@ -904,29 +875,25 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
        case LFUN_WORD_BACKWARD_SELECT:
                needsUpdate |= cur.selHandle(cmd.action() == LFUN_WORD_BACKWARD_SELECT);
                needsUpdate |= cursorBackwardOneWord(cur);
-       
+
                if (!needsUpdate && oldTopSlice == cur.top()
                                && cur.boundary() == oldBoundary) {
                        cur.undispatched();
                        cmd = FuncRequest(LFUN_FINISHED_BACKWARD);
-               
+
                        // we will probably be moving out the inset, so we should execute
-                       // the depm-mechanism, but only when the cursor has a place to 
+                       // the depm-mechanism, but only when the cursor has a place to
                        // go outside this inset, i.e. in a slice above.
-                       if (cur.depth() > 1 && cur.pos() == 0 
+                       if (cur.depth() > 1 && cur.pos() == 0
                                  && cur.pit() == 0) {
-                               // The cursor hasn't changed yet. To give the 
+                               // The cursor hasn't changed yet. To give the
                                // DEPM the possibility of doing something we must
                                // provide it with two different cursors.
                                Cursor dummy = cur;
                                dummy.pos() = cur.lastpos();
                                dummy.pit() = cur.lastpit();
-                               if (cur.bv().checkDepm(dummy, cur)) {
+                               if (cur.bv().checkDepm(dummy, cur))
                                        cur.forceBufferUpdate();
-                                       // DEPM may have requested a screen update
-                                       cur.screenUpdateFlags(
-                                               cur.screenUpdate() | dummy.screenUpdate());
-                               }
                        }
                }
                break;
@@ -961,8 +928,8 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
                        cur.recordUndoSelection();
                        pit_type const pit_end = cur.selEnd().pit();
                        for (pit_type pit = cur.selBegin().pit(); pit <= pit_end; pit++) {
-                               pars_[pit].insertChar(0, '\t', 
-                                                     bv->buffer().params().trackChanges);
+                               pars_[pit].insertChar(0, '\t',
+                                                     bv->buffer().params().track_changes);
                                // Update the selection pos to make sure the selection does not
                                // change as the inserted tab will increase the logical pos.
                                if (cur.realAnchor().pit() == pit)
@@ -975,13 +942,13 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
                        // Maybe we shouldn't allow tabs within a line, because they
                        // are not (yet) aligned as one might do expect.
                        FuncRequest cmd(LFUN_SELF_INSERT, from_ascii("\t"));
-                       dispatch(cur, cmd);     
+                       dispatch(cur, cmd);
                }
                break;
        }
 
        case LFUN_TAB_DELETE: {
-               bool const tc = bv->buffer().params().trackChanges;
+               bool const tc = bv->buffer().params().track_changes;
                if (cur.selection()) {
                        // If there is a selection, a tab (if present) is removed from
                        // the beginning of each paragraph.
@@ -995,11 +962,11 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
                                if (c == '\t' || c == ' ') {
                                        // remove either 1 tab or 4 spaces.
                                        int const n = (c == ' ' ? 4 : 1);
-                                       for (int i = 0; i < n 
+                                       for (int i = 0; i < n
                                                  && !par.empty() && par.getChar(0) == c; ++i) {
                                                if (cur.pit() == pit)
                                                        cur.posBackward();
-                                               if (cur.realAnchor().pit() == pit 
+                                               if (cur.realAnchor().pit() == pit
                                                          && cur.realAnchor().pos() > 0 )
                                                        cur.realAnchor().backwardPos();
                                                par.eraseChar(0, tc);
@@ -1008,23 +975,23 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
                        }
                        cur.finishUndo();
                } else {
-                       // If there is no selection, try to remove a tab or some spaces 
+                       // If there is no selection, try to remove a tab or some spaces
                        // before the position of the cursor.
                        Paragraph & par = paragraphs()[cur.pit()];
                        pos_type const pos = cur.pos();
-                       
+
                        if (pos == 0)
                                break;
-                       
+
                        char_type const c = par.getChar(pos - 1);
                        cur.recordUndo();
                        if (c == '\t') {
                                cur.posBackward();
                                par.eraseChar(cur.pos(), tc);
                        } else
-                               for (int n_spaces = 0; 
+                               for (int n_spaces = 0;
                                     cur.pos() > 0
-                                            && par.getChar(cur.pos() - 1) == ' ' 
+                                            && par.getChar(cur.pos() - 1) == ' '
                                             && n_spaces < 4;
                                     ++n_spaces) {
                                        cur.posBackward();
@@ -1037,11 +1004,20 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
 
        case LFUN_CHAR_DELETE_FORWARD:
                if (!cur.selection()) {
+                       bool was_separator = cur.paragraph().isEnvSeparator(cur.pos());
                        if (cur.pos() == cur.paragraph().size())
                                // Par boundary, force full-screen update
                                singleParUpdate = false;
                        needsUpdate |= erase(cur);
                        cur.resetAnchor();
+                       if (was_separator && cur.pos() == cur.paragraph().size()
+                           && (!cur.paragraph().layout().isEnvironment()
+                               || cur.paragraph().size() > 0)) {
+                               // Force full-screen update
+                               singleParUpdate = false;
+                               needsUpdate |= erase(cur);
+                               cur.resetAnchor();
+                       }
                        // It is possible to make it a lot faster still
                        // just comment out the line below...
                } else {
@@ -1054,11 +1030,17 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
        case LFUN_CHAR_DELETE_BACKWARD:
                if (!cur.selection()) {
                        if (bv->getIntl().getTransManager().backspace()) {
+                               bool par_boundary = cur.pos() == 0;
                                // Par boundary, full-screen update
-                               if (cur.pos() == 0)
+                               if (par_boundary)
                                        singleParUpdate = false;
                                needsUpdate |= backspace(cur);
                                cur.resetAnchor();
+                               if (par_boundary && cur.pos() > 0
+                                   && cur.paragraph().isEnvSeparator(cur.pos() - 1)) {
+                                       needsUpdate |= backspace(cur);
+                                       cur.resetAnchor();
+                               }
                                // It is possible to make it a lot faster still
                                // just comment out the line below...
                        }
@@ -1068,11 +1050,42 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
                }
                break;
 
-       case LFUN_PARAGRAPH_BREAK:
+       case LFUN_PARAGRAPH_BREAK: {
                cap::replaceSelection(cur);
-               breakParagraph(cur, cmd.argument() == "inverse");
+               pit_type pit = cur.pit();
+               Paragraph const & par = pars_[pit];
+               pit_type prev = pit;
+               if (pit > 0) {
+                       if (!pars_[pit - 1].layout().isEnvironment())
+                               prev = depthHook(pit, par.getDepth());
+                       else if (pars_[pit - 1].getDepth() >= par.getDepth())
+                               prev = pit - 1;
+               }
+               if (prev < pit && cur.pos() == par.beginOfBody()
+                   && !par.isEnvSeparator(cur.pos())
+                   && !par.layout().isCommand()
+                   && pars_[prev].layout() != par.layout()
+                   && pars_[prev].layout().isEnvironment()) {
+                       if (par.layout().isEnvironment()
+                           && pars_[prev].getDepth() == par.getDepth()) {
+                               docstring const layout = par.layout().name();
+                               DocumentClass const & tc = bv->buffer().params().documentClass();
+                               lyx::dispatch(FuncRequest(LFUN_LAYOUT, tc.plainLayout().name()));
+                               lyx::dispatch(FuncRequest(LFUN_SEPARATOR_INSERT, "parbreak"));
+                               lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_BREAK, "inverse"));
+                               lyx::dispatch(FuncRequest(LFUN_LAYOUT, layout));
+                       } else {
+                               lyx::dispatch(FuncRequest(LFUN_SEPARATOR_INSERT, "parbreak"));
+                               breakParagraph(cur);
+                       }
+                       Font const f(inherit_font, cur.current_font.language());
+                       pars_[cur.pit() - 1].resetFonts(f);
+               } else {
+                       breakParagraph(cur, cmd.argument() == "inverse");
+               }
                cur.resetAnchor();
                break;
+       }
 
        case LFUN_INSET_INSERT: {
                cur.recordUndo();
@@ -1242,13 +1255,16 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
                // without argument?
                string const arg = to_utf8(cmd.argument());
                if (arg.empty()) {
+                       bool tryGraphics = true;
                        if (theClipboard().isInternal())
                                pasteFromStack(cur, bv->buffer().errorList("Paste"), 0);
-                       else if (theClipboard().hasGraphicsContents() 
-                                    && !theClipboard().hasTextContents())
+                       else if (theClipboard().hasTextContents()) {
+                               if (pasteClipboardText(cur, bv->buffer().errorList("Paste"),
+                                                      true, Clipboard::AnyTextType))
+                                       tryGraphics = false;
+                       }
+                       if (tryGraphics && theClipboard().hasGraphicsContents())
                                pasteClipboardGraphics(cur, bv->buffer().errorList("Paste"));
-                       else
-                               pasteClipboardText(cur, bv->buffer().errorList("Paste"), true);
                } else if (isStrUnsignedInt(arg)) {
                        // we have a numerical argument
                        pasteFromStack(cur, bv->buffer().errorList("Paste"),
@@ -1391,15 +1407,36 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
        }
 
        case LFUN_ENVIRONMENT_SPLIT: {
+               bool const outer = cmd.argument() == "outer";
                Paragraph const & para = cur.paragraph();
-               docstring const layout = para.layout().name();
+               docstring layout = para.layout().name();
+               depth_type split_depth = cur.paragraph().params().depth();
+               if (outer) {
+                       // check if we have an environment in our nesting hierarchy
+                       pit_type pit = cur.pit();
+                       Paragraph cpar = pars_[pit];
+                       while (true) {
+                               if (pit == 0 || cpar.params().depth() == 0)
+                                       break;
+                               --pit;
+                               cpar = pars_[pit];
+                               if (cpar.params().depth() < split_depth
+                                   && cpar.layout().isEnvironment()) {
+                                               layout = cpar.layout().name();
+                                               split_depth = cpar.params().depth();
+                               }
+                       }
+               }
                if (cur.pos() > 0)
                        lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_BREAK));
-               bool const morecont = cur.lastpos() > cur.pos();
-               lyx::dispatch(FuncRequest(LFUN_LAYOUT, "Separator"));
+               if (outer) {
+                       while (cur.paragraph().params().depth() > split_depth)
+                               lyx::dispatch(FuncRequest(LFUN_DEPTH_DECREMENT));
+               }
+               DocumentClass const & tc = bv->buffer().params().documentClass();
+               lyx::dispatch(FuncRequest(LFUN_LAYOUT, tc.plainLayout().name()));
+               lyx::dispatch(FuncRequest(LFUN_SEPARATOR_INSERT, "plain"));
                lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_BREAK, "inverse"));
-               if (morecont) 
-                       lyx::dispatch(FuncRequest(LFUN_DOWN));
                lyx::dispatch(FuncRequest(LFUN_LAYOUT, layout));
 
                break;
@@ -1459,15 +1496,15 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
                        --pos;
 
                BufferParams const & bufparams = bv->buffer().params();
-               bool const hebrew = 
+               bool const hebrew =
                        par.getFontSettings(bufparams, pos).language()->lang() == "hebrew";
                bool const allow_inset_quote = !(par.isPassThru() || hebrew);
-               
+
+               string const arg = to_utf8(cmd.argument());
                if (allow_inset_quote) {
                        char_type c = ' ';
                        if (pos > 0 && (!cur.prevInset() || !cur.prevInset()->isSpace()))
                                c = par.getChar(pos - 1);
-                       string const arg = to_utf8(cmd.argument());
                        InsetQuotes::QuoteTimes const quote_type = (arg == "single")
                                ? InsetQuotes::SingleQuotes : InsetQuotes::DoubleQuotes;
                        cur.insert(new InsetQuotes(cur.buffer(), c, quote_type));
@@ -1475,8 +1512,9 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
                } else {
                        // The cursor might have been invalidated by the replaceSelection.
                        cur.buffer()->changed(true);
-                       lyx::dispatch(FuncRequest(LFUN_SELF_INSERT, "\""));
-               }                       
+                       string const quote_string = (arg == "single") ? "'" : "\"";
+                       lyx::dispatch(FuncRequest(LFUN_SELF_INSERT, quote_string));
+               }
                break;
        }
 
@@ -1500,7 +1538,7 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
 
        case LFUN_MOUSE_DOUBLE:
                if (cmd.button() == mouse_button::button1) {
-                       selectWord(cur, WHOLE_WORD_STRICT);
+                       selectWord(cur, WHOLE_WORD);
                        bv->cursor() = cur;
                }
                break;
@@ -1658,16 +1696,47 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
                break;
        }
 
-       case LFUN_HYPERLINK_INSERT: {
-               InsetCommandParams p(HYPERLINK_CODE);
-               docstring content;
+       case LFUN_HREF_INSERT: {
+               // FIXME If we're actually given an argument, shouldn't
+               // we use it, whether or not we have a selection?
+               docstring content = cmd.argument();
                if (cur.selection()) {
                        content = cur.selectionAsString(false);
                        cutSelection(cur, true, false);
                }
-               p["target"] = (cmd.argument().empty()) ?
-                       content : cmd.argument();
+
+               InsetCommandParams p(HYPERLINK_CODE);
+               if (!content.empty()){
+                       // if it looks like a link, we'll put it as target,
+                       // otherwise as name (bug #8792).
+
+                       // We can't do:
+                       //   regex_match(to_utf8(content), matches, link_re)
+                       // because smatch stores pointers to the substrings rather
+                       // than making copies of them. And those pointers become
+                       // invalid after regex_match returns, since it is then
+                       // being given a temporary object. (Thanks to Georg for
+                       // figuring that out.)
+                       regex const link_re("^([a-z]+):.*");
+                       smatch matches;
+                       string const c = to_utf8(lowercase(content));
+
+                       if (c.substr(0,7) == "mailto:") {
+                               p["target"] = content;
+                               p["type"] = from_ascii("mailto:");
+                       } else if (regex_match(c, matches, link_re)) {
+                               p["target"] = content;
+                               string protocol = matches.str(1);
+                               if (protocol == "file")
+                                       p["type"] = from_ascii("file:");
+                       } else
+                               p["name"] = content;
+               }
                string const data = InsetCommand::params2string(p);
+
+               // we need to have a target. if we already have one, then
+               // that gets used at the default for the name, too, which
+               // is probably what is wanted.
                if (p["target"].empty()) {
                        bv->showDialog("href", data);
                } else {
@@ -1850,7 +1919,7 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
                dispatch(cur, fr);
                break;
        }
-       
+
        case LFUN_NOMENCL_PRINT:
        case LFUN_NEWPAGE_INSERT:
                // do nothing fancy
@@ -1858,6 +1927,16 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
                cur.posForward();
                break;
 
+       case LFUN_SEPARATOR_INSERT: {
+               doInsertInset(cur, this, cmd, false, false);
+               cur.posForward();
+               // remove a following space
+               Paragraph & par = cur.paragraph();
+               if (cur.pos() != cur.lastpos() && par.isLineSeparator(cur.pos()))
+                   par.eraseChar(cur.pos(), cur.buffer()->params().track_changes);
+               break;
+       }
+
        case LFUN_DEPTH_DECREMENT:
                changeDepth(cur, DEC_DEPTH);
                break;
@@ -1994,14 +2073,14 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
                break;
        }
 
-       case LFUN_FONT_UULINE: {
+       case LFUN_FONT_UNDERUNDERLINE: {
                Font font(ignore_font, ignore_language);
                font.fontInfo().setUuline(FONT_TOGGLE);
                toggleAndShow(cur, this, font);
                break;
        }
 
-       case LFUN_FONT_UWAVE: {
+       case LFUN_FONT_UNDERWAVE: {
                Font font(ignore_font, ignore_language);
                font.fontInfo().setUwave(FONT_TOGGLE);
                toggleAndShow(cur, this, font);
@@ -2377,7 +2456,7 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
 bool Text::getStatus(Cursor & cur, FuncRequest const & cmd,
                        FuncStatus & flag) const
 {
-       LBUFERR(this == cur.text(), _("Invalid cursor."));
+       LBUFERR(this == cur.text());
 
        FontInfo const & fontinfo = cur.real_current_font.fontInfo();
        bool enable = true;
@@ -2396,7 +2475,7 @@ bool Text::getStatus(Cursor & cur, FuncRequest const & cmd,
 
        case LFUN_APPENDIX:
                // FIXME We really should not allow this to be put, e.g.,
-               // in a footnote, or in ERT. But it would make sense in a 
+               // in a footnote, or in ERT. But it would make sense in a
                // branch, so I'm not sure what to do.
                flag.setOnOff(cur.paragraph().params().startOfAppendix());
                break;
@@ -2431,6 +2510,10 @@ bool Text::getStatus(Cursor & cur, FuncRequest const & cmd,
                        code = INDEX_CODE;
                else if (cmd.argument() == "index_print")
                        code = INDEX_PRINT_CODE;
+               else if (cmd.argument() == "listings")
+                       code = LISTINGS_CODE;
+               else if (cmd.argument() == "mathspace")
+                       code = MATH_HULL_CODE;
                else if (cmd.argument() == "nomenclature")
                        code = NOMENCL_CODE;
                else if (cmd.argument() == "nomencl_print")
@@ -2453,8 +2536,6 @@ bool Text::getStatus(Cursor & cur, FuncRequest const & cmd,
                        code = VSPACE_CODE;
                else if (cmd.argument() == "wrap")
                        code = WRAP_CODE;
-               else if (cmd.argument() == "listings")
-                       code = LISTINGS_CODE;
                break;
 
        case LFUN_ERT_INSERT:
@@ -2658,7 +2739,7 @@ bool Text::getStatus(Cursor & cur, FuncRequest const & cmd,
                // not allowed in description items
                enable = !inDescriptionItem(cur);
                break;
-       case LFUN_HYPERLINK_INSERT:
+       case LFUN_HREF_INSERT:
                if (cur.selIsMultiCell() || cur.selIsMultiLine()) {
                        enable = false;
                        break;
@@ -2855,7 +2936,13 @@ bool Text::getStatus(Cursor & cur, FuncRequest const & cmd,
 
        case LFUN_NEWLINE_INSERT:
                // LaTeX restrictions (labels or empty par)
-               enable = (cur.pos() > cur.paragraph().beginOfBody());
+               enable = !cur.paragraph().isPassThru()
+                       && cur.pos() > cur.paragraph().beginOfBody();
+               break;
+
+       case LFUN_SEPARATOR_INSERT:
+               // Always enabled for now
+               enable = true;
                break;
 
        case LFUN_TAB_INSERT:
@@ -2893,7 +2980,7 @@ bool Text::getStatus(Cursor & cur, FuncRequest const & cmd,
        case LFUN_PARAGRAPH_BREAK:
                enable = cur.inset().getLayout().isMultiPar();
                break;
-       
+
        case LFUN_SPELLING_ADD:
        case LFUN_SPELLING_IGNORE:
        case LFUN_SPELLING_REMOVE:
@@ -2903,10 +2990,26 @@ bool Text::getStatus(Cursor & cur, FuncRequest const & cmd,
        case LFUN_LAYOUT:
                enable = !cur.inset().forcePlainLayout();
                break;
-       
+
        case LFUN_ENVIRONMENT_SPLIT: {
-               if (cur.paragraph().layout().isEnvironment()
-                   && cur.buffer()->params().documentClass().hasLayout(from_ascii("Separator"))) {
+               if (cmd.argument() == "outer") {
+                       // check if we have an environment in our nesting hierarchy
+                       bool res = false;
+                       depth_type const current_depth = cur.paragraph().params().depth();
+                       pit_type pit = cur.pit();
+                       Paragraph cpar = pars_[pit];
+                       while (true) {
+                               if (pit == 0 || cpar.params().depth() == 0)
+                                       break;
+                               --pit;
+                               cpar = pars_[pit];
+                               if (cpar.params().depth() < current_depth)
+                                       res = cpar.layout().isEnvironment();
+                       }
+                       enable = res;
+                       break;
+               }
+               else if (cur.paragraph().layout().isEnvironment()) {
                        enable = true;
                        break;
                }
@@ -2944,8 +3047,8 @@ bool Text::getStatus(Cursor & cur, FuncRequest const & cmd,
        case LFUN_FONT_STATE:
        case LFUN_FONT_UNDERLINE:
        case LFUN_FONT_STRIKEOUT:
-       case LFUN_FONT_UULINE:
-       case LFUN_FONT_UWAVE:
+       case LFUN_FONT_UNDERUNDERLINE:
+       case LFUN_FONT_UNDERWAVE:
        case LFUN_TEXTSTYLE_APPLY:
        case LFUN_TEXTSTYLE_UPDATE:
                enable = !cur.paragraph().isPassThru();
@@ -2953,7 +3056,7 @@ bool Text::getStatus(Cursor & cur, FuncRequest const & cmd,
 
        case LFUN_WORD_DELETE_FORWARD:
        case LFUN_WORD_DELETE_BACKWARD:
-       case LFUN_LINE_DELETE:
+       case LFUN_LINE_DELETE_FORWARD:
        case LFUN_WORD_FORWARD:
        case LFUN_WORD_BACKWARD:
        case LFUN_WORD_RIGHT:
@@ -3028,7 +3131,7 @@ bool Text::getStatus(Cursor & cur, FuncRequest const & cmd,
        }
 
        if (code != NO_CODE
-           && (cur.empty() 
+           && (cur.empty()
                || !cur.inset().insetAllowed(code)
                || (cur.paragraph().layout().pass_thru && !allow_in_passthru)))
                enable = false;