]> git.lyx.org Git - lyx.git/blobdiff - src/Cursor.cpp
Add Qt-based fallback-converter for Mac to compensate missing ImageMagick convert...
[lyx.git] / src / Cursor.cpp
index 822dc6e53cbb1519357479fe8224a393502e93c1..a139a2b310fd2a1de6674e62b762a6abf78039f9 100644 (file)
@@ -53,7 +53,7 @@
 #include "mathed/MacroTable.h"
 #include "mathed/MathData.h"
 #include "mathed/MathFactory.h"
-#include "mathed/MathMacro.h"
+#include "mathed/InsetMathMacro.h"
 
 #include <sstream>
 #include <limits>
@@ -115,7 +115,7 @@ DocIterator bruteFind(Cursor const & c, int x, int y)
 }
 
 
-} // namespace anon
+} // namespace
 
 
 CursorData::CursorData()
@@ -551,24 +551,6 @@ void Cursor::checkNewWordPosition()
 }
 
 
-bool Cursor::posBackward()
-{
-       if (pos() == 0)
-               return false;
-       --pos();
-       return true;
-}
-
-
-bool Cursor::posForward()
-{
-       if (pos() == lastpos())
-               return false;
-       ++pos();
-       return true;
-}
-
-
 bool Cursor::posVisRight(bool skip_inset)
 {
        Cursor new_cur = *this; // where we will move to
@@ -776,7 +758,7 @@ bool findNonVirtual(Row const & row, Row::const_iterator & cit, bool onleft)
        return cit != row.end() && !cit->isVirtual();
 }
 
-}
+} // namespace
 
 void Cursor::getSurroundingPos(pos_type & left_pos, pos_type & right_pos) const
 {
@@ -785,12 +767,11 @@ void Cursor::getSurroundingPos(pos_type & left_pos, pos_type & right_pos) const
        right_pos = -1;
 
        Row const & row = textRow();
-       TextMetrics const & tm = bv_->textMetrics(text());
        double dummy = 0;
-       Row::const_iterator cit = tm.findRowElement(row, pos(), boundary(), dummy);
+       Row::const_iterator cit = row.findElement(pos(), boundary(), dummy);
        // Handle the case of empty row
        if (cit == row.end()) {
-               if (paragraph().isRTL(buffer()->params()))
+               if (row.isRTL())
                        right_pos = row.pos();
                else
                        left_pos = row.pos() - 1;
@@ -864,10 +845,8 @@ void Cursor::getSurroundingPos(pos_type & left_pos, pos_type & right_pos) const
 
 bool Cursor::posVisToNewRow(bool movingLeft)
 {
-       Paragraph const & par = paragraph();
-       Buffer const & buf = *buffer();
        Row const & row = textRow();
-       bool par_is_LTR = !par.isRTL(buf.params());
+       bool par_is_LTR = !row.isRTL();
 
        // Inside a table, determining whether to move to the next or
        // previous row should be done based on the table's direction.
@@ -1078,7 +1057,7 @@ void Cursor::updateTextTargetOffset()
 }
 
 
-void Cursor::info(odocstream & os) const
+void Cursor::info(odocstream & os, bool devel_mode) const
 {
        for (int i = 1, n = depth(); i < n; ++i) {
                operator[](i).inset().infoize(os);
@@ -1090,6 +1069,14 @@ void Cursor::info(odocstream & os) const
                if (inset)
                        prevInset()->infoize2(os);
        }
+       if (devel_mode) {
+               InsetMath * math = inset().asInsetMath();
+               if (math)
+                       os << _(", Inset: ") << math->id();
+               os << _(", Cell: ") << idx();
+               os << _(", Position: ") << pos();
+       }
+
 }
 
 
@@ -1304,7 +1291,7 @@ void Cursor::insert(MathData const & ar)
 }
 
 
-bool Cursor::backspace()
+bool Cursor::backspace(bool const force)
 {
        if (selection()) {
                cap::eraseSelection(*this);
@@ -1340,7 +1327,7 @@ bool Cursor::backspace()
                }
        }
 
-       if (pos() != 0 && prevAtom()->nargs() > 0) {
+       if (pos() != 0 && !force && prevAtom()->confirmDeletion()) {
                // let's require two backspaces for 'big stuff' and
                // highlight on the first
                resetAnchor();
@@ -1354,7 +1341,7 @@ bool Cursor::backspace()
 }
 
 
-bool Cursor::erase()
+bool Cursor::erase(bool const force)
 {
        if (inMacroMode())
                return true;
@@ -1389,7 +1376,7 @@ bool Cursor::erase()
        }
 
        // 'clever' UI hack: only erase large items if previously slected
-       if (pos() != lastpos() && nextAtom()->nargs() > 0) {
+       if (pos() != lastpos() && !force && nextAtom()->confirmDeletion()) {
                resetAnchor();
                selection(true);
                ++pos();
@@ -1452,12 +1439,12 @@ bool Cursor::macroModeClose()
        if (in && in->interpretString(*this, s))
                return true;
        bool const user_macro = buffer()->getMacro(name, *this, false);
-       MathAtom atom = user_macro ? MathAtom(new MathMacro(buffer(), name))
+       MathAtom atom = user_macro ? MathAtom(new InsetMathMacro(buffer(), name))
                                   : createInsetMath(name, buffer());
 
        // try to put argument into macro, if we just inserted a macro
        bool macroArg = false;
-       MathMacro * atomAsMacro = atom.nucleus()->asMacro();
+       InsetMathMacro * atomAsMacro = atom.nucleus()->asMacro();
        if (atomAsMacro) {
                // macros here are still unfolded (in init mode in fact). So
                // we have to resolve the macro here manually and check its arity
@@ -1465,10 +1452,10 @@ bool Cursor::macroModeClose()
                MacroData const * data = buffer()->getMacro(atomAsMacro->name());
                if (!selection.empty() && data && data->numargs() - data->optionals() > 0) {
                        macroArg = true;
-                       atomAsMacro->setDisplayMode(MathMacro::DISPLAY_INTERACTIVE_INIT, 1);
+                       atomAsMacro->setDisplayMode(InsetMathMacro::DISPLAY_INTERACTIVE_INIT, 1);
                } else
                        // non-greedy case. Do not touch the arguments behind
-                       atomAsMacro->setDisplayMode(MathMacro::DISPLAY_INTERACTIVE_INIT, 0);
+                       atomAsMacro->setDisplayMode(InsetMathMacro::DISPLAY_INTERACTIVE_INIT, 0);
        }
 
        // insert remembered selection into first argument of a non-macro
@@ -1759,11 +1746,23 @@ bool Cursor::mathForward(bool word)
        LASSERT(inMathed(), return false);
        if (pos() < lastpos()) {
                if (word) {
-                       // word: skip a group of insets with same math class
+                       // word: skip a group of insets of the form X*(B*|R*|P*) (greedy
+                       // match) where X is any math class, B is mathbin, R is mathrel, and
+                       // P is mathpunct. Make sure that the following remains true:
+                       //   mathForward(true); mathBackward(true); mathForward(true)
+                       // is the same as mathForward(true) and
+                       //   mathBackward(true); mathForward(true); mathBackward(true)
+                       // is the same as mathBackward(true).
                        MathClass mc = nextMath().mathClass();
                        do
                                posForward();
                        while (pos() < lastpos() && mc == nextMath().mathClass());
+                       if (pos() < lastpos() &&
+                           ((mc = nextMath().mathClass()) == MC_BIN ||
+                            mc == MC_REL || mc == MC_PUNCT))
+                               do
+                                       posForward();
+                               while (pos() < lastpos() && mc == nextMath().mathClass());
                } else if (openable(nextAtom())) {
                        // single step: try to enter the next inset
                        pushBackward(nextMath());
@@ -1788,11 +1787,17 @@ bool Cursor::mathBackward(bool word)
        LASSERT(inMathed(), return false);
        if (pos() > 0) {
                if (word) {
-                       // word: skip a group of insets with same math class
+                       // word: skip a group of insets. See the comment in mathForward.
                        MathClass mc = prevMath().mathClass();
                        do
                                posBackward();
                        while (pos() > 0 && mc == prevMath().mathClass());
+                       if (pos() > 0 && (mc == MC_BIN || mc == MC_REL || mc == MC_PUNCT)) {
+                               mc = prevMath().mathClass();
+                               do
+                                       posBackward();
+                               while (pos() > 0 && mc == prevMath().mathClass());
+                       }
                } else if (openable(prevAtom())) {
                        // single step: try to enter the preceding inset
                        posBackward();
@@ -1927,10 +1932,13 @@ bool Cursor::upDownInText(bool up, bool & updateNeeded)
                int yo = bv().getPos(*this).y_;
                Cursor old = *this;
                // To next/previous row
+               // FIXME: the y position is often guessed wrongly across styles and
+               // insets, which leads to weird behaviour.
                if (up)
                        tm.editXY(*this, xo, yo - textRow().ascent() - 1);
                else
                        tm.editXY(*this, xo, yo + textRow().descent() + 1);
+               x_target_ = old.x_target_;
                clearSelection();
 
                // This happens when you move out of an inset.
@@ -2054,7 +2062,7 @@ docstring parbreak(Cursor const * cur)
        return os.str();
 }
 
-}
+} // namespace
 
 
 docstring Cursor::selectionAsString(bool with_label) const
@@ -2106,23 +2114,16 @@ docstring Cursor::selectionAsString(bool with_label) const
 }
 
 
-docstring Cursor::currentState() const
+docstring Cursor::currentState(bool devel_mode) const
 {
        if (inMathed()) {
                odocstringstream os;
-               info(os);
-#ifdef DEVEL_VERSION
-               InsetMath * math = inset().asInsetMath();
-               if (math)
-                       os << _(", Inset: ") << math->id();
-               os << _(", Cell: ") << idx();
-               os << _(", Position: ") << pos();
-#endif
+               info(os, devel_mode);
                return os.str();
        }
 
        if (inTexted())
-               return text()->currentState(*this);
+               return text()->currentState(*this, devel_mode);
 
        return docstring();
 }
@@ -2245,6 +2246,7 @@ void Cursor::sanitize()
 {
        setBuffer(&bv_->buffer());
        DocIterator::sanitize();
+       new_word_.sanitize();
        if (selection())
                anchor_.sanitize();
        else
@@ -2438,4 +2440,39 @@ void Cursor::checkBufferStructure()
 }
 
 
+bool Cursor::confirmDeletion(bool const before) const
+{
+       if (!selection()) {
+               if (Inset const * inset = before ? prevInset() : nextInset())
+                       return inset->confirmDeletion();
+       } else {
+               DocIterator dit = selectionBegin();
+               CursorSlice const end = selectionEnd().top();
+               for (; dit.top() < end; dit.top().forwardPos())
+                       if (Inset const * inset = dit.nextInset())
+                               if (inset->confirmDeletion())
+                                       return true;
+       }
+       return false;
+}
+
+
+void Cursor::moveToClosestEdge(int const x, bool const edit)
+{
+       if (Inset const * inset = nextInset()) {
+               // stay in front of insets for which we want to open the dialog
+               // (e.g. InsetMathSpace).
+               if (edit && (inset->hasSettings() || !inset->contextMenuName().empty()))
+                       return;
+               CoordCache::Insets const & insetCache = bv().coordCache().getInsets();
+               if (!insetCache.has(inset))
+                       return;
+               int const wid = insetCache.dim(inset).wid;
+               Point p = insetCache.xy(inset);
+               if (x > p.x_ + (wid + 1) / 2)
+                       posForward();
+       }
+}
+
+
 } // namespace lyx