]> git.lyx.org Git - lyx.git/blobdiff - src/Cursor.cpp
Allow using \binom without amsmath and add support for \brace and \brack
[lyx.git] / src / Cursor.cpp
index 405d72b4f0af6bc4ea460e155b1506da528329bb..b0997ecbba12047eef5dac3ce3ad5cd55d7c526b 100644 (file)
@@ -23,9 +23,9 @@
 #include "DispatchResult.h"
 #include "Encoding.h"
 #include "Font.h"
+#include "FuncCode.h"
 #include "FuncRequest.h"
 #include "Language.h"
-#include "lfuns.h"
 #include "LyXFunc.h" // only for setMessage()
 #include "LyXRC.h"
 #include "paragraph_funcs.h"
@@ -43,6 +43,7 @@
 #include "insets/InsetText.h"
 
 #include "mathed/InsetMath.h"
+#include "mathed/InsetMathBrace.h"
 #include "mathed/InsetMathScript.h"
 #include "mathed/MacroTable.h"
 #include "mathed/MathData.h"
@@ -300,7 +301,7 @@ void Cursor::dispatch(FuncRequest const & cmd0)
        
        // store some values to be used inside of the handlers
        beforeDispatchCursor_ = *this;
-       for (; depth(); pop()) {
+       for (; depth(); pop(), boundary(false)) {
                LYXERR(Debug::DEBUG, "Cursor::dispatch: cmd: "
                        << cmd0 << endl << *this);
                BOOST_ASSERT(pos() <= lastpos());
@@ -362,6 +363,7 @@ void Cursor::pop()
 void Cursor::push(Inset & p)
 {
        push_back(CursorSlice(p));
+       p.setBuffer(bv_->buffer());
 }
 
 
@@ -377,8 +379,6 @@ void Cursor::pushBackward(Inset & p)
 bool Cursor::popBackward()
 {
        BOOST_ASSERT(!empty());
-       //lyxerr << "Leaving inset from in front" << endl;
-       inset().notifyCursorLeaves(*this);
        if (depth() == 1)
                return false;
        pop();
@@ -391,7 +391,6 @@ bool Cursor::popForward()
        BOOST_ASSERT(!empty());
        //lyxerr << "Leaving inset from in back" << endl;
        const pos_type lp = (depth() > 1) ? (*this)[depth() - 2].lastpos() : 0;
-       inset().notifyCursorLeaves(*this);
        if (depth() == 1)
                return false;
        pop();
@@ -470,6 +469,21 @@ void Cursor::getSurroundingPos(pos_type & left_pos, pos_type & right_pos)
        // known position around the cursor:
        pos_type known_pos = boundary() ? pos() - 1 : pos();
        
+       // edge case: if we're at the end of the paragraph, things are a little 
+       // different (because lastpos is a position which does not really "exist" 
+       // --- there's no character there yet).
+       if (known_pos == lastpos()) {
+               if (par.isRTL(buf.params())) {
+                       left_pos = -1;
+                       right_pos = bidi.vis2log(row.pos());
+               }
+               else { // LTR paragraph
+                       right_pos = -1;
+                       left_pos = bidi.vis2log(row.endpos() - 1);
+               }
+               return;
+       }
+       
        // Whether 'known_pos' is to the left or to the right of the cursor depends
        // on whether it is an RTL or LTR character...
        bool const cur_is_RTL = 
@@ -484,14 +498,6 @@ void Cursor::getSurroundingPos(pos_type & left_pos, pos_type & right_pos)
        // determine the other one:
        
        if (known_pos_on_right) {
-               // edge-case: we're at the end of the paragraph, there isn't really any
-               // position any further to the right 
-               if (known_pos == lastpos()) {
-                       right_pos = -1;
-                       left_pos = row.endpos() - 1;
-                       return;
-               }
-               // the normal case
                right_pos = known_pos;
                // *visual* position of 'left_pos':
                pos_type v_left_pos = bidi.log2vis(right_pos) - 1;
@@ -524,14 +530,6 @@ void Cursor::getSurroundingPos(pos_type & left_pos, pos_type & right_pos)
                }
        } 
        else { // known_pos is on the left
-               // edge-case: we're at the end of the paragraph, there isn't really any
-               // position any further to the left
-               if (known_pos == lastpos()) {
-                       left_pos = -1;
-                       right_pos = row.endpos() - 1;
-                       return;
-               }
-               // the normal case
                left_pos = known_pos;
                // *visual* position of 'right_pos'
                pos_type v_right_pos = bidi.log2vis(left_pos) + 1;
@@ -989,6 +987,8 @@ void Cursor::plainInsert(MathAtom const & t)
 {
        cell().insert(pos(), t);
        ++pos();
+       inset().setBuffer(bv_->buffer());
+       inset().initView();
 }
 
 
@@ -1022,12 +1022,16 @@ void Cursor::insert(MathAtom const & t)
 }
 
 
-void Cursor::insert(Inset * inset)
+void Cursor::insert(Inset * inset0)
 {
+       BOOST_ASSERT(inset0);
        if (inMathed())
-               insert(MathAtom(inset));
-       else
-               text()->insertInset(*this, inset);
+               insert(MathAtom(inset0));
+       else {
+               text()->insertInset(*this, inset0);
+               inset0->setBuffer(bv_->buffer());
+               inset0->initView();
+       }
 }
 
 
@@ -1205,6 +1209,8 @@ bool Cursor::macroModeClose()
                return false;
        InsetMathUnknown * p = activeMacro();
        p->finalize();
+       MathData selection;
+       asArray(p->selection(), selection);
        docstring const s = p->name();
        --pos();
        cell().erase(pos());
@@ -1222,12 +1228,37 @@ bool Cursor::macroModeClose()
        if (in && in->interpretString(*this, s))
                return true;
        MathAtom atom = createInsetMath(name);
+
+       // try to put argument into macro, if we just inserted a macro
+       bool macroArg = false;
        MathMacro * atomAsMacro = atom.nucleus()->asMacro();
        if (atomAsMacro) {
-               // make non-greedy, i.e. don't eat parameters from the right
-               atomAsMacro->setDisplayMode(MathMacro::DISPLAY_INTERACTIVE_INIT);
+               // macros here are still unfolded (in init mode in fact). So
+               // we have to resolve the macro here manually and check its arity
+               // to put the selection behind it if arity > 0.
+               MacroData const * data = buffer().getMacro(atomAsMacro->name());
+               if (selection.size() > 0 && data && data->numargs() - data->optionals() > 0) {
+                       macroArg = true;
+                       atomAsMacro->setDisplayMode(MathMacro::DISPLAY_INTERACTIVE_INIT, 1);
+               } else
+                       // non-greedy case. Do not touch the arguments behind
+                       atomAsMacro->setDisplayMode(MathMacro::DISPLAY_INTERACTIVE_INIT, 0);
        }
+
+       // insert remembered selection into first argument of a non-macro
+       else if (atom.nucleus()->nargs() > 0)
+               atom.nucleus()->cell(0).append(selection);
+       
        plainInsert(atom);
+
+       // finally put the macro argument behind, if needed
+       if (macroArg) {
+               if (selection.size() > 1)
+                       plainInsert(MathAtom(new InsetMathBrace(selection)));
+               else
+                       insert(selection);
+       }
+       
        return true;
 }
 
@@ -1523,7 +1554,7 @@ bool Cursor::upDownInText(bool up, bool & updateNeeded)
                if (pit() + 1 >= int(text()->paragraphs().size()) &&
                                row + 1 >= int(pm.rows().size()))
                        return false;
-       }       
+       }
 
        // with and without selection are handled differently
        if (!selection()) {
@@ -1556,7 +1587,10 @@ bool Cursor::upDownInText(bool up, bool & updateNeeded)
                                top().pos() = min(tm.x2pos(pit(), row - 1, xo), top().lastpos());
                        } else if (pit() > 0) {
                                --pit();
-                               ParagraphMetrics const & pmcur = bv_->parMetrics(text(), pit());
+                               TextMetrics & tm = bv_->textMetrics(text());
+                               if (!tm.contains(pit()))
+                                       tm.newParMetricsUp();
+                               ParagraphMetrics const & pmcur = tm.parMetrics(pit());
                                top().pos() = min(tm.x2pos(pit(), pmcur.rows().size() - 1, xo), top().lastpos());
                        }
                } else {
@@ -1564,6 +1598,9 @@ bool Cursor::upDownInText(bool up, bool & updateNeeded)
                                top().pos() = min(tm.x2pos(pit(), row + 1, xo), top().lastpos());
                        } else if (pit() + 1 < int(text()->paragraphs().size())) {
                                ++pit();
+                               TextMetrics & tm = bv_->textMetrics(text());
+                               if (!tm.contains(pit()))
+                                       tm.newParMetricsDown();
                                top().pos() = min(tm.x2pos(pit(), 0, xo), top().lastpos());
                        }
                }
@@ -1629,7 +1666,6 @@ docstring Cursor::selectionAsString(bool label) const
                return docstring();
 
        if (inTexted()) {
-               Buffer const & buffer = bv().buffer();
                ParagraphList const & pars = text()->paragraphs();
 
                // should be const ...
@@ -1639,22 +1675,22 @@ docstring Cursor::selectionAsString(bool label) const
                size_t const endpos = selEnd().pos();
 
                if (startpit == endpit)
-                       return pars[startpit].asString(buffer, startpos, endpos, label);
+                       return pars[startpit].asString(startpos, endpos, label);
 
                // First paragraph in selection
                docstring result = pars[startpit].
-                       asString(buffer, startpos, pars[startpit].size(), label)
+                       asString(startpos, pars[startpit].size(), label)
                                 + parbreak(pars[startpit]);
 
                // The paragraphs in between (if any)
                for (pit_type pit = startpit + 1; pit != endpit; ++pit) {
                        Paragraph const & par = pars[pit];
-                       result += par.asString(buffer, 0, par.size(), label)
+                       result += par.asString(0, par.size(), label)
                                  + parbreak(pars[pit]);
                }
 
                // Last paragraph in selection
-               result += pars[endpit].asString(buffer, 0, endpos, label);
+               result += pars[endpit].asString(0, endpos, label);
 
                return result;
        }
@@ -1768,7 +1804,7 @@ bool Cursor::fixIfBroken()
 }
 
 
-bool notifyCursorLeaves(DocIterator const & old, Cursor & cur)
+bool notifyCursorLeaves(Cursor const & old, Cursor & cur)
 {
        // find inset in common
        size_type i;
@@ -1776,11 +1812,22 @@ bool notifyCursorLeaves(DocIterator const & old, Cursor & cur)
                if (&old[i].inset() != &cur[i].inset())
                        break;
        }
+
+       // update words if we just moved to another paragraph
+       if (i == old.depth() && i == cur.depth()
+           && !cur.buffer().isClean()
+           && cur.inTexted() && old.inTexted()
+           && cur.pit() != old.pit()) {
+               old.paragraph().updateWords(old.top());
+               return false;
+       }
        
        // notify everything on top of the common part in old cursor,
        // but stop if the inset claims the cursor to be invalid now
        for (; i < old.depth(); ++i) {
-               if (old[i].inset().notifyCursorLeaves(cur))
+               Cursor insetPos = old;
+               insetPos.cutOff(i);
+               if (old[i].inset().notifyCursorLeaves(insetPos, cur))
                        return true;
        }
        
@@ -1819,7 +1866,7 @@ void Cursor::setCurrentFont()
        // get font
        BufferParams const & bufparams = buffer().params();
        current_font = par.getFontSettings(bufparams, cpos);
-       real_current_font = tm.getDisplayFont(cpit, cpos);
+       real_current_font = tm.displayFont(cpit, cpos);
 
        // special case for paragraph end
        if (cs.pos() == lastpos()
@@ -1902,14 +1949,20 @@ void Cursor::recordUndoFullDocument()
 
 void Cursor::recordUndoSelection()
 {
-       bv_->buffer().undo().recordUndo(*this, ATOMIC_UNDO,
-               selBegin().pit(), selEnd().pit());
+       if (inMathed()) {
+               if (cap::multipleCellsSelected(*this))
+                       recordUndoInset();
+               else
+                       recordUndo();
+       } else
+               bv_->buffer().undo().recordUndo(*this, ATOMIC_UNDO,
+                       selBegin().pit(), selEnd().pit());
 }
 
 
 void Cursor::checkBufferStructure()
 {
-       if (paragraph().layout()->toclevel == Layout::NOT_IN_TOC)
+       if (paragraph().layout().toclevel == Layout::NOT_IN_TOC)
                return;
        Buffer const * master = buffer().masterBuffer();
        master->tocBackend().updateItem(ParConstIterator(*this));