3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Alejandro Aguilar Sierra
9 * Full author contact details are available in file CREDITS.
14 #include "math_cursor.h"
15 #include "BufferView.h"
18 #include "dispatchresult.h"
19 #include "formulabase.h"
20 #include "funcrequest.h"
22 #include "math_braceinset.h"
23 #include "math_commentinset.h"
24 #include "math_charinset.h"
25 #include "math_factory.h"
26 #include "math_gridinset.h"
27 #include "math_macroarg.h"
28 #include "math_macrotemplate.h"
29 #include "math_mathmlstream.h"
30 #include "math_scriptinset.h"
31 #include "math_spaceinset.h"
32 #include "math_support.h"
33 #include "math_unknowninset.h"
35 #include "support/limited_stack.h"
36 #include "support/std_sstream.h"
38 #include <boost/assert.hpp>
44 #ifndef CXX_GLOBAL_CSTD
49 using std::ostringstream;
52 // matheds own cut buffer
53 limited_stack<string> theCutBuffer;
56 MathCursor::MathCursor(BufferView * bv, InsetFormulaBase * formula, bool front)
57 : formula_(formula), autocorrect_(false), selection_(false)
59 front ? first(bv->fullCursor()) : last(bv->fullCursor());
63 MathCursor::~MathCursor()
65 // ensure that 'notifyCursorLeave' is called
71 void MathCursor::push(LCursor & cur, MathAtom & t)
73 cur.push(t.nucleus());
77 void MathCursor::pushLeft(LCursor & cur, MathAtom & t)
79 //lyxerr << "Entering atom " << t << " left" << endl;
85 void MathCursor::pushRight(LCursor & cur, MathAtom & t)
87 //lyxerr << "Entering atom " << t << " right" << endl;
94 bool MathCursor::popLeft(LCursor & cur)
96 //lyxerr << "Leaving atom to the left" << endl;
97 if (cur.depth() <= 1) {
99 cur.inset()->asMathInset()->notifyCursorLeaves(cur.idx());
102 cur.inset()->asMathInset()->notifyCursorLeaves(cur.idx());
108 bool MathCursor::popRight(LCursor & cur)
110 //lyxerr << "Leaving atom "; bv.inset->asMathInset()->write(cerr, false); cerr << " right" << endl;
111 if (cur.depth() <= 1) {
112 if (cur.depth() == 1)
113 cur.inset()->asMathInset()->notifyCursorLeaves(cur.idx());
116 cur.inset()->asMathInset()->notifyCursorLeaves(cur.idx());
125 void MathCursor::dump(char const * what) const
127 lyxerr << "MC: " << what << endl;
128 lyxerr << " Cursor: " << cur.depth() << endl;
129 for (unsigned i = 0; i < cur.depth(); ++i)
130 lyxerr << " i: " << i << ' ' << Cursor_[i] << endl;
131 lyxerr << " Anchor: " << Anchor_.size() << endl;
132 for (unsigned i = 0; i < Anchor_.size(); ++i)
133 lyxerr << " i: " << i << ' ' << Anchor_[i] << endl;
134 lyxerr << " sel: " << selection_ << endl;
137 void MathCursor::dump(char const *) const {}
141 bool MathCursor::isInside(MathInset const *) const
145 for (unsigned i = 0; i < cur.depth(); ++i)
146 if (Cursor_[i].asMathInset() == p)
153 bool MathCursor::openable(MathAtom const & t, bool sel) const
164 // we can't move into anything new during selection
165 if (cur.depth() == Anchor_.size())
167 if (t.operator->() != Anchor_[cur.depth()].asMathInset())
179 bool MathCursor::inNucleus(LCursor & cur) const
181 return cur.inset()->asMathInset()->asScriptInset() && cur.idx() == 2;
185 bool MathCursor::left(LCursor & cur, bool sel)
188 autocorrect_ = false;
189 cur.bv().x_target(-1); // "no target"
190 if (inMacroMode(cur)) {
196 if (cur.pos() != 0 && openable(cur.prevAtom(), sel)) {
197 pushRight(cur, cur.prevAtom());
201 return cur.posLeft() || idxLeft(cur) || popLeft(cur) || selection_;
205 bool MathCursor::right(LCursor & cur, bool sel)
208 autocorrect_ = false;
209 cur.bv().x_target(-1); // "no target"
210 if (inMacroMode(cur)) {
216 if (cur.pos() != cur.lastpos() && openable(cur.nextAtom(), sel)) {
217 pushLeft(cur, cur.nextAtom());
221 return cur.posRight() || idxRight(cur) || popRight(cur) || selection_;
225 void MathCursor::first(LCursor & cur)
229 push(cur, formula_->par());
230 cur.inset()->asMathInset()->idxFirst(cur);
235 void MathCursor::last(LCursor & cur)
239 push(cur, formula_->par());
240 cur.inset()->asMathInset()->idxLast(cur);
245 bool positionable(CursorBase const & cursor, CursorBase const & anchor)
247 // avoid deeper nested insets when selecting
248 if (cursor.size() > anchor.size())
251 // anchor might be deeper, should have same path then
252 for (CursorBase::size_type i = 0; i < cursor.size(); ++i)
253 if (cursor[i].asMathInset() != anchor[i].asMathInset())
256 // position should be ok.
261 void MathCursor::setScreenPos(LCursor & cur, int x, int y)
263 dump("setScreenPos 1");
264 bool res = bruteFind(cur, x, y,
265 formula()->xlow(), formula()->xhigh(),
266 formula()->ylow(), formula()->yhigh());
268 // this can happen on creation of "math-display"
269 dump("setScreenPos 1.5");
272 cur.bv().x_target(-1); // "no target"
273 dump("setScreenPos 2");
278 bool MathCursor::home(LCursor & cur, bool sel)
281 autocorrect_ = false;
284 if (!cur.inset()->asMathInset()->idxHome(cur))
287 cur.bv().x_target(-1); // "no target"
292 bool MathCursor::end(LCursor & cur, bool sel)
295 autocorrect_ = false;
298 if (!cur.inset()->asMathInset()->idxEnd(cur))
299 return popRight(cur);
301 cur.bv().x_target(-1); // "no target"
306 void MathCursor::plainErase(LCursor & cur)
308 cur.cell().erase(cur.pos());
312 void MathCursor::markInsert(LCursor & cur)
314 //lyxerr << "inserting mark" << endl;
315 cur.cell().insert(cur.pos(), MathAtom(new MathCharInset(0)));
319 void MathCursor::markErase(LCursor & cur)
321 //lyxerr << "deleting mark" << endl;
322 cur.cell().erase(cur.pos());
326 void MathCursor::plainInsert(LCursor & cur, MathAtom const & t)
329 cur.cell().insert(cur.pos(), t);
334 void MathCursor::insert2(LCursor & cur, string const & str)
342 void MathCursor::insert(LCursor & cur, string const & str)
344 //lyxerr << "inserting '" << str << "'" << endl;
346 for (string::const_iterator it = str.begin(); it != str.end(); ++it)
347 plainInsert(cur, MathAtom(new MathCharInset(*it)));
351 void MathCursor::insert(LCursor & cur, char c)
353 //lyxerr << "inserting '" << c << "'" << endl;
355 plainInsert(cur, MathAtom(new MathCharInset(c)));
359 void MathCursor::insert(LCursor & cur, MathAtom const & t)
367 void MathCursor::niceInsert(LCursor & cur, string const & t)
372 niceInsert(cur, ar[0]);
378 void MathCursor::niceInsert(LCursor & cur, MathAtom const & t)
381 string safe = grabAndEraseSelection(cur);
383 // enter the new inset and move the contents of the selection if possible
386 pushLeft(cur, cur.nextAtom());
392 void MathCursor::insert(LCursor & cur, MathArray const & ar)
397 cur.cell().insert(cur.pos(), ar);
398 cur.pos() += ar.size();
402 void MathCursor::paste(LCursor & cur, string const & data)
404 dispatch(cur, FuncRequest(LFUN_PASTE, data));
408 bool MathCursor::backspace(LCursor & cur)
410 autocorrect_ = false;
417 if (cur.pos() == 0) {
418 if (cur.inset()->asMathInset()->nargs() == 1 &&
426 if (inMacroMode(cur)) {
427 MathUnknownInset * p = activeMacro(cur);
428 if (p->name().size() > 1) {
429 p->setName(p->name().substr(0, p->name().size() - 1));
434 if (cur.pos() != 0 && cur.prevAtom()->nargs() > 0) {
435 // let's require two backspaces for 'big stuff' and
436 // highlight on the first
446 bool MathCursor::erase(LCursor & cur)
448 autocorrect_ = false;
449 if (inMacroMode(cur))
457 // delete empty cells if possible
459 //if (cur.cell().empty() && cur.inset()->idxDelete(cur.idx()))
462 // special behaviour when in last position of cell
463 if (cur.pos() == cur.lastpos()) {
464 bool one_cell = cur.inset()->asMathInset()->nargs() == 1;
465 if (one_cell && cur.depth() == 1 && cur.lastpos() == 0)
471 cur.inset()->asMathInset()->idxGlue(cur.idx());
475 if (cur.pos() != cur.lastpos() && cur.nextAtom()->nargs() > 0)
484 bool MathCursor::up(LCursor & cur, bool sel)
491 CursorBase save = Cursor_;
496 autocorrect_ = false;
501 bool MathCursor::down(LCursor & cur, bool sel)
508 CursorBase save = Cursor_;
513 autocorrect_ = false;
518 void MathCursor::macroModeClose(LCursor & cur)
520 if (!inMacroMode(cur))
522 MathUnknownInset * p = activeMacro(cur);
524 string s = p->name();
526 cur.cell().erase(cur.pos());
528 // do nothing if the macro name is empty
532 string const name = s.substr(1);
534 // prevent entering of recursive macros
535 if (formula()->lyxCode() == InsetOld::MATHMACRO_CODE
536 && formula()->getInsetName() == name)
537 lyxerr << "can't enter recursive macro" << endl;
539 niceInsert(cur, createMathInset(name));
543 string MathCursor::macroName(LCursor & cur) const
545 return inMacroMode(cur) ? activeMacro(cur)->name() : string();
549 void MathCursor::selClear(LCursor & cur)
552 cur.bv().clearSelection();
556 void MathCursor::selCopy(LCursor & cur)
560 theCutBuffer.push(grabSelection(cur));
563 //theCutBuffer.erase();
568 void MathCursor::selCut(LCursor & cur)
571 theCutBuffer.push(grabAndEraseSelection(cur));
575 void MathCursor::selDel(LCursor & cur)
585 void MathCursor::selPaste(LCursor & cur, size_t n)
589 if (n < theCutBuffer.size())
590 paste(cur, theCutBuffer[n]);
591 //grabSelection(cur);
596 void MathCursor::selHandle(LCursor & cur, bool sel)
598 if (sel == selection_)
606 void MathCursor::selStart(LCursor & cur)
616 void MathCursor::selClearOrDel(LCursor & cur)
618 if (lyxrc.auto_region_delete)
625 void MathCursor::drawSelection(PainterInfo & pi) const
631 getSelection(pi.base.bv->fullCursor(), i1, i2);
632 i1.asMathInset()->drawSelection(pi, i1.idx_, i1.pos_, i2.idx_, i2.pos_);
636 void MathCursor::handleNest(LCursor & cur, MathAtom const & a, int c)
639 asArray(grabAndEraseSelection(cur), at.nucleus()->cell(c));
641 pushRight(cur, cur.prevAtom());
645 void MathCursor::getScreenPos(LCursor & cur, int & x, int & y) const
647 cur.inset()->asMathInset()->getScreenPos(cur.idx(), cur.pos(), x, y);
651 int MathCursor::targetX(LCursor & cur) const
653 if (cur.bv().x_target() != -1)
654 return cur.bv().x_target();
657 getScreenPos(cur, x, y);
662 InsetFormulaBase * MathCursor::formula() const
668 void MathCursor::adjust(LCursor & cur, pos_type from, difference_type diff)
670 if (cur.pos() > from)
674 if (Anchor_.back().pos_ > from)
675 Anchor_.back().pos_ += diff;
676 // just to be on the safe side
677 // theoretically unecessary
683 bool MathCursor::inMacroMode(LCursor & cur) const
687 MathUnknownInset const * p = cur.prevAtom()->asUnknownInset();
688 return p && !p->final();
692 MathUnknownInset * MathCursor::activeMacro(LCursor & cur)
694 return inMacroMode(cur) ? cur.prevAtom().nucleus()->asUnknownInset() : 0;
698 MathUnknownInset const * MathCursor::activeMacro(LCursor & cur) const
700 return inMacroMode(cur) ? cur.prevAtom()->asUnknownInset() : 0;
704 bool MathCursor::inMacroArgMode(LCursor & cur) const
706 return cur.pos() > 0 && cur.prevAtom()->getChar() == '#';
710 bool MathCursor::selection() const
717 MathCursor::enclosingGrid(LCursor & cur, MathCursor::idx_type & idx) const
719 for (MathInset::difference_type i = cur.depth() - 1; i >= 0; --i) {
720 MathInset * m = cur.cursor_[i].inset()->asMathInset();
723 MathGridInset * p = m->asGridInset();
725 idx = cur.cursor_[i].idx_;
733 void MathCursor::popToHere(LCursor & cur, MathInset const * p)
735 while (cur.depth() && cur.inset()->asMathInset() != p)
740 void MathCursor::popToEnclosingGrid(LCursor & cur)
742 while (cur.depth() && !cur.inset()->asMathInset()->asGridInset())
747 void MathCursor::popToEnclosingHull(LCursor & cur)
749 while (cur.depth() && !cur.inset()->asMathInset()->asGridInset())
754 void MathCursor::pullArg(LCursor & cur)
757 MathArray ar = cur.cell();
760 cur.cell().insert(cur.pos(), ar);
763 formula()->mutateToText();
768 void MathCursor::touch()
772 CursorBase::const_iterator it = Cursor_.begin();
773 CursorBase::const_iterator et = Cursor_.end();
774 for ( ; it != et; ++it)
780 void MathCursor::normalize(LCursor & cur)
782 if (cur.idx() >= cur.nargs()) {
783 lyxerr << "this should not really happen - 1: "
784 << cur.idx() << ' ' << cur.nargs()
785 << " in: " << cur.inset() << endl;
788 cur.idx() = min(cur.idx(), cur.nargs() - 1);
790 if (cur.pos() > cur.lastpos()) {
791 lyxerr << "this should not really happen - 2: "
792 << cur.pos() << ' ' << cur.lastpos() << " in idx: " << cur.idx()
794 WriteStream wi(lyxerr, false, true);
795 cur.inset()->asMathInset()->write(wi);
799 cur.pos() = min(cur.pos(), cur.lastpos());
803 void MathCursor::idxNext(LCursor & cur)
805 cur.inset()->asMathInset()->idxNext(cur);
809 void MathCursor::idxPrev(LCursor & cur)
811 cur.inset()->asMathInset()->idxPrev(cur);
815 char MathCursor::valign(LCursor & cur) const
818 MathGridInset * p = enclosingGrid(cur, idx);
819 return p ? p->valign() : '\0';
823 char MathCursor::halign(LCursor & cur) const
826 MathGridInset * p = enclosingGrid(cur, idx);
827 return p ? p->halign(idx % p->ncols()) : '\0';
831 void MathCursor::getSelection(LCursor & cur,
832 CursorSlice & i1, CursorSlice & i2) const
834 CursorSlice anc = normalAnchor(cur);
835 if (anc < cur.top()) {
845 bool MathCursor::goUpDown(LCursor & cur, bool up)
847 // Be warned: The 'logic' implemented in this function is highly fragile.
848 // A distance of one pixel or a '<' vs '<=' _really_ matters.
849 // So fiddle around with it only if you know what you are doing!
852 getScreenPos(cur, xo, yo);
854 // check if we had something else in mind, if not, this is the future goal
855 if (cur.bv().x_target() == -1)
856 cur.bv().x_target(xo);
858 xo = cur.bv().x_target();
860 // try neigbouring script insets
863 if (cur.pos() != 0) {
864 MathScriptInset const * p = cur.prevAtom()->asScriptInset();
865 if (p && p->has(up)) {
867 push(cur, cur.nextAtom());
868 cur.idx() = up; // the superscript has index 1
869 cur.pos() = cur.lastpos();
870 //lyxerr << "updown: handled by scriptinset to the left" << endl;
876 if (cur.pos() != cur.lastpos()) {
877 MathScriptInset const * p = cur.nextAtom()->asScriptInset();
878 if (p && p->has(up)) {
879 push(cur, cur.nextAtom());
882 //lyxerr << "updown: handled by scriptinset to the right" << endl;
888 // try current cell for e.g. text insets
889 if (cur.inset()->asMathInset()->idxUpDown2(cur, up, cur.bv().x_target()))
892 //xarray().boundingBox(xlow, xhigh, ylow, yhigh);
897 //if (bruteFind(xo, yo, xlow, xhigh, ylow, yhigh)) {
898 // lyxerr << "updown: handled by brute find in the same cell" << endl;
902 // try to find an inset that knows better then we
904 //lyxerr << "updown: We are in " << inset() << " idx: " << idx() << endl;
906 if (cur.inset()->asMathInset()->idxUpDown(cur, up, cur.bv().x_target())) {
907 // try to find best position within this inset
909 bruteFind2(cur, xo, yo);
913 // no such inset found, just take something "above"
914 //lyxerr << "updown: handled by strange case" << endl;
917 bruteFind(cur, xo, yo,
920 up ? formula()->ylow() : yo + 4,
921 up ? yo - 4 : formula()->yhigh()
924 // any improvement so far?
926 getScreenPos(cur, xnew, ynew);
927 if (up ? ynew < yo : ynew > yo)
933 bool MathCursor::bruteFind
934 (LCursor & cur, int x, int y, int xlow, int xhigh, int ylow, int yhigh)
936 CursorBase best_cursor;
937 double best_dist = 1e10;
939 CursorBase it = ibegin(formula()->par().nucleus());
940 CursorBase et = iend(formula()->par().nucleus());
942 // avoid invalid nesting when selecting
943 if (!selection_ || positionable(it, cur.anchor_)) {
945 it.back().getScreenPos(xo, yo);
946 if (xlow <= xo && xo <= xhigh && ylow <= yo && yo <= yhigh) {
947 double d = (x - xo) * (x - xo) + (y - yo) * (y - yo);
948 //lyxerr << "x: " << x << " y: " << y << " d: " << endl;
949 // '<=' in order to take the last possible position
950 // this is important for clicking behind \sum in e.g. '\sum_i a'
951 if (d <= best_dist) {
963 if (best_dist < 1e10)
964 cur.cursor_ = best_cursor;
965 return best_dist < 1e10;
969 void MathCursor::bruteFind2(LCursor & cur, int x, int y)
971 double best_dist = 1e10;
973 CursorBase it = cur.cursor_;
975 CursorBase et = cur.cursor_;
976 int n = et.back().asMathInset()->cell(et.back().idx_).size();
978 for (int i = 0; ; ++i) {
980 it.back().getScreenPos(xo, yo);
981 double d = (x - xo) * (x - xo) + (y - yo) * (y - yo);
982 // '<=' in order to take the last possible position
983 // this is important for clicking behind \sum in e.g. '\sum_i a'
984 lyxerr << "i: " << i << " d: " << d << " best: " << best_dist << endl;
985 if (d <= best_dist) {
996 bool MathCursor::idxLineLast(LCursor & cur)
998 cur.idx() -= cur.idx() % cur.ncols();
999 cur.idx() += cur.ncols() - 1;
1000 cur.pos() = cur.lastpos();
1005 bool MathCursor::idxLeft(LCursor & cur)
1007 return cur.inset()->asMathInset()->idxLeft(cur);
1011 bool MathCursor::idxRight(LCursor & cur)
1013 return cur.inset()->asMathInset()->idxRight(cur);
1017 bool MathCursor::script(LCursor & cur, bool up)
1019 // Hack to get \\^ and \\_ working
1020 if (inMacroMode(cur) && macroName(cur) == "\\") {
1022 niceInsert(cur, createMathInset("mathcircumflex"));
1024 interpret(cur, '_');
1028 macroModeClose(cur);
1029 string safe = grabAndEraseSelection(cur);
1030 if (inNucleus(cur)) {
1031 // we are in a nucleus of a script inset, move to _our_ script
1032 cur.inset()->asMathInset()->asScriptInset()->ensure(up);
1035 } else if (cur.pos() != 0 && cur.prevAtom()->asScriptInset()) {
1036 cur.prevAtom().nucleus()->asScriptInset()->ensure(up);
1037 pushRight(cur, cur.prevAtom());
1039 cur.pos() = cur.lastpos();
1040 } else if (cur.pos() != 0) {
1042 cur.cell()[cur.pos()]
1043 = MathAtom(new MathScriptInset(cur.nextAtom(), up));
1044 pushLeft(cur, cur.nextAtom());
1048 plainInsert(cur, MathAtom(new MathScriptInset(up)));
1049 cur.prevAtom().nucleus()->asScriptInset()->ensure(up);
1050 pushRight(cur, cur.prevAtom());
1060 bool MathCursor::interpret(LCursor & cur, char c)
1062 //lyxerr << "interpret 2: '" << c << "'" << endl;
1063 cur.bv().x_target(-1); // "no target"
1064 if (inMacroArgMode(cur)) {
1068 MathMacroTemplate const * p = formula()->par()->asMacroTemplate();
1069 if (p && 1 <= n && n <= p->numargs())
1070 insert(cur, MathAtom(new MathMacroArgument(c - '0')));
1072 insert(cur, createMathInset("#"));
1073 interpret(cur, c); // try again
1079 if (inMacroMode(cur)) {
1080 string name = macroName(cur);
1081 //lyxerr << "interpret name: '" << name << "'" << endl;
1084 activeMacro(cur)->setName(activeMacro(cur)->name() + c);
1088 // handle 'special char' macros
1093 if (currentMode(cur) == MathInset::TEXT_MODE)
1094 niceInsert(cur, createMathInset("textbackslash"));
1096 niceInsert(cur, createMathInset("backslash"));
1097 } else if (c == '{') {
1098 niceInsert(cur, MathAtom(new MathBraceInset));
1100 niceInsert(cur, createMathInset(string(1, c)));
1105 // leave macro mode and try again if necessary
1106 macroModeClose(cur);
1108 niceInsert(cur, MathAtom(new MathBraceInset));
1114 // This is annoying as one has to press <space> far too often.
1118 // leave autocorrect mode if necessary
1119 if (autocorrect_ && c == ' ') {
1120 autocorrect_ = false;
1125 // just clear selection on pressing the space bar
1126 if (selection_ && c == ' ') {
1134 //lyxerr << "starting with macro" << endl;
1135 insert(cur, MathAtom(new MathUnknownInset("\\", false)));
1140 if (currentMode(cur) == MathInset::TEXT_MODE)
1146 if (currentMode(cur) == MathInset::TEXT_MODE) {
1147 // insert spaces in text mode,
1148 // but suppress direct insertion of two spaces in a row
1149 // the still allows typing '<space>a<space>' and deleting the 'a', but
1150 // it is better than nothing...
1151 if (!cur.pos() != 0 || cur.prevAtom()->getChar() != ' ')
1155 if (cur.pos() != 0 && cur.prevAtom()->asSpaceInset()) {
1156 cur.prevAtom().nucleus()->asSpaceInset()->incSpace();
1161 // if are at the very end, leave the formula
1162 return cur.pos() != cur.lastpos();
1175 if (c == '{' || c == '}' || c == '#' || c == '&' || c == '$') {
1176 niceInsert(cur, createMathInset(string(1, c)));
1181 niceInsert(cur, MathAtom(new MathCommentInset));
1185 // try auto-correction
1186 //if (autocorrect_ && hasPrevAtom() && math_autocorrect(prevAtom(), c))
1189 // no special circumstances, so insert the character without any fuss
1191 autocorrect_ = true;
1196 void MathCursor::setSelection
1197 (LCursor & cur, CursorBase const & where, size_t n)
1200 cur.cursor_ = where;
1201 cur.anchor_ = where;
1206 void MathCursor::insetToggle(LCursor & cur)
1208 if (cur.pos() != cur.lastpos()) {
1209 // toggle previous inset ...
1210 cur.nextAtom().nucleus()->lock(!cur.nextAtom()->lock());
1211 } else if (popLeft(cur) && cur.pos() != cur.lastpos()) {
1212 // ... or enclosing inset if we are in the last inset position
1213 cur.nextAtom().nucleus()->lock(!cur.nextAtom()->lock());
1219 string MathCursor::info(LCursor & cur) const
1222 os << "Math editor mode. ";
1223 for (int i = 0, n = cur.depth(); i < n; ++i) {
1224 cur.cursor_[i].asMathInset()->infoize(os);
1228 cur.prevAtom()->infoize2(os);
1236 void region(CursorSlice const & i1, CursorSlice const & i2,
1237 MathInset::row_type & r1, MathInset::row_type & r2,
1238 MathInset::col_type & c1, MathInset::col_type & c2)
1240 MathInset * p = i1.asMathInset();
1241 c1 = p->col(i1.idx_);
1242 c2 = p->col(i2.idx_);
1245 r1 = p->row(i1.idx_);
1246 r2 = p->row(i2.idx_);
1254 string MathCursor::grabSelection(LCursor & cur) const
1261 getSelection(cur, i1, i2);
1263 if (i1.idx_ == i2.idx_) {
1264 MathArray::const_iterator it = i1.cell().begin();
1265 return asString(MathArray(it + i1.pos_, it + i2.pos_));
1270 region(i1, i2, r1, r2, c1, c2);
1273 for (row_type row = r1; row <= r2; ++row) {
1276 for (col_type col = c1; col <= c2; ++col) {
1279 data += asString(i1.asMathInset()->cell(i1.asMathInset()->index(row, col)));
1286 void MathCursor::eraseSelection(LCursor & cur)
1290 getSelection(cur, i1, i2);
1291 if (i1.idx_ == i2.idx_)
1292 i1.cell().erase(i1.pos_, i2.pos_);
1294 MathInset * p = i1.asMathInset();
1297 region(i1, i2, r1, r2, c1, c2);
1298 for (row_type row = r1; row <= r2; ++row)
1299 for (col_type col = c1; col <= c2; ++col)
1300 p->cell(p->index(row, col)).clear();
1306 string MathCursor::grabAndEraseSelection(LCursor & cur)
1310 string res = grabSelection(cur);
1311 eraseSelection(cur);
1317 CursorSlice MathCursor::normalAnchor(LCursor & cur) const
1321 if (Anchor_.size() < cur.depth()) {
1323 lyxerr << "unusual Anchor size" << endl;
1325 //lyx::BOOST_ASSERT(Anchor_.size() >= cursor.cur.depth());
1326 // use Anchor on the same level as Cursor
1327 CursorSlice normal = Anchor_[cur.depth() - 1];
1328 if (cur.depth() < Anchor_.size() && !(normal < cursor())) {
1329 // anchor is behind cursor -> move anchor behind the inset
1339 DispatchResult MathCursor::dispatch(LCursor &, FuncRequest const & cmd)
1341 // mouse clicks are somewhat special
1343 switch (cmd.action) {
1344 case LFUN_MOUSE_PRESS:
1345 case LFUN_MOUSE_MOTION:
1346 case LFUN_MOUSE_RELEASE:
1347 case LFUN_MOUSE_DOUBLE: {
1349 CursorSlice & pos = Cursor_.back();
1353 if (x < cmd.x && hasPrevAtom()) {
1354 DispatchResult const res =
1355 prevAtom().nucleus()->dispatch(cur, cmd);
1356 if (res.dispatched())
1359 if (x > cmd.x && hasNextAtom()) {
1360 DispatchResult const res =
1361 nextAtom().nucleus()->dispatch(cur, cmd);
1362 if (res.dispatched())
1372 for (int i = cur.depth() - 1; i >= 0; --i) {
1373 CursorBase tmp = cur->cursor_;
1374 CursorSlice & pos = tmp.back()
1375 DispatchResult const res = pos.asMathInset()->dispatch(cur, cmd);
1376 if (res.dispatched()) {
1377 if (res.val() == FINISHED) {
1378 if (i + 1 < Cursor_.size())
1379 Cursor_.erase(Cursor_.begin() + i + 1, Cursor_.end());
1386 return DispatchResult(false);
1390 MathInset::mode_type MathCursor::currentMode(LCursor &) const
1393 for (int i = Cursor_.size() - 1; i >= 0; --i) {
1394 MathInset::mode_type res = Cursor_[i].asMathInset()->currentMode();
1395 if (res != MathInset::UNDECIDED_MODE)
1399 return MathInset::UNDECIDED_MODE;
1403 void MathCursor::handleFont(LCursor & cur, string const & font)
1407 macroModeClose(cur);
1408 safe = grabAndEraseSelection(cur);
1411 if (cur.lastpos() != 0) {
1412 // something left in the cell
1413 if (cur.pos() == 0) {
1414 // cursor in first position
1416 } else if (cur.pos() == cur.lastpos()) {
1417 // cursor in last position
1420 // cursor in between. split cell
1421 MathArray::iterator bt = cur.cell().begin();
1422 MathAtom at = createMathInset(font);
1423 at.nucleus()->cell(0) = MathArray(bt, bt + cur.pos());
1424 cur.cell().erase(bt, bt + cur.pos());
1426 plainInsert(cur, at);
1429 // nothing left in the cell
1437 void releaseMathCursor(BufferView & bv)
1440 InsetFormulaBase * f = mathcursor->formula();