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::posLeft(LCursor & cur)
194 bool MathCursor::posRight(LCursor & cur)
196 if (cur.pos() == cur.lastpos())
203 bool MathCursor::left(LCursor & cur, bool sel)
206 autocorrect_ = false;
207 cur.bv().x_target(-1); // "no target"
208 if (inMacroMode(cur)) {
214 if (hasPrevAtom(cur) && openable(prevAtom(cur), sel)) {
215 pushRight(cur, prevAtom(cur));
219 return posLeft(cur) || idxLeft(cur) || popLeft(cur) || selection_;
223 bool MathCursor::right(LCursor & cur, bool sel)
226 autocorrect_ = false;
227 cur.bv().x_target(-1); // "no target"
228 if (inMacroMode(cur)) {
234 if (hasNextAtom(cur) && openable(nextAtom(cur), sel)) {
235 pushLeft(cur, nextAtom(cur));
239 return posRight(cur) || idxRight(cur) || popRight(cur) || selection_;
243 void MathCursor::first(LCursor & cur)
247 push(cur, formula_->par());
248 cur.inset()->asMathInset()->idxFirst(cur);
253 void MathCursor::last(LCursor & cur)
257 push(cur, formula_->par());
258 cur.inset()->asMathInset()->idxLast(cur);
263 bool positionable(CursorBase const & cursor, CursorBase const & anchor)
265 // avoid deeper nested insets when selecting
266 if (cursor.size() > anchor.size())
269 // anchor might be deeper, should have same path then
270 for (CursorBase::size_type i = 0; i < cursor.size(); ++i)
271 if (cursor[i].asMathInset() != anchor[i].asMathInset())
274 // position should be ok.
279 void MathCursor::setScreenPos(LCursor & cur, int x, int y)
281 dump("setScreenPos 1");
282 bool res = bruteFind(cur, x, y,
283 formula()->xlow(), formula()->xhigh(),
284 formula()->ylow(), formula()->yhigh());
286 // this can happen on creation of "math-display"
287 dump("setScreenPos 1.5");
290 cur.bv().x_target(-1); // "no target"
291 dump("setScreenPos 2");
296 bool MathCursor::home(LCursor & cur, bool sel)
299 autocorrect_ = false;
302 if (!cur.inset()->asMathInset()->idxHome(cur))
305 cur.bv().x_target(-1); // "no target"
310 bool MathCursor::end(LCursor & cur, bool sel)
313 autocorrect_ = false;
316 if (!cur.inset()->asMathInset()->idxEnd(cur))
317 return popRight(cur);
319 cur.bv().x_target(-1); // "no target"
324 void MathCursor::plainErase(LCursor & cur)
326 cur.cell().erase(cur.pos());
330 void MathCursor::markInsert(LCursor & cur)
332 //lyxerr << "inserting mark" << endl;
333 cur.cell().insert(cur.pos(), MathAtom(new MathCharInset(0)));
337 void MathCursor::markErase(LCursor & cur)
339 //lyxerr << "deleting mark" << endl;
340 cur.cell().erase(cur.pos());
344 void MathCursor::plainInsert(LCursor & cur, MathAtom const & t)
347 cur.cell().insert(cur.pos(), t);
352 void MathCursor::insert2(LCursor & cur, string const & str)
360 void MathCursor::insert(LCursor & cur, string const & str)
362 //lyxerr << "inserting '" << str << "'" << endl;
364 for (string::const_iterator it = str.begin(); it != str.end(); ++it)
365 plainInsert(cur, MathAtom(new MathCharInset(*it)));
369 void MathCursor::insert(LCursor & cur, char c)
371 //lyxerr << "inserting '" << c << "'" << endl;
373 plainInsert(cur, MathAtom(new MathCharInset(c)));
377 void MathCursor::insert(LCursor & cur, MathAtom const & t)
385 void MathCursor::niceInsert(LCursor & cur, string const & t)
390 niceInsert(cur, ar[0]);
396 void MathCursor::niceInsert(LCursor & cur, MathAtom const & t)
399 string safe = grabAndEraseSelection(cur);
401 // enter the new inset and move the contents of the selection if possible
404 pushLeft(cur, nextAtom(cur));
410 void MathCursor::insert(LCursor & cur, MathArray const & ar)
415 cur.cell().insert(cur.pos(), ar);
416 cur.pos() += ar.size();
420 void MathCursor::paste(LCursor & cur, string const & data)
422 dispatch(cur, FuncRequest(LFUN_PASTE, data));
426 bool MathCursor::backspace(LCursor & cur)
428 autocorrect_ = false;
435 if (cur.pos() == 0) {
436 if (cur.inset()->asMathInset()->nargs() == 1 &&
444 if (inMacroMode(cur)) {
445 MathUnknownInset * p = activeMacro(cur);
446 if (p->name().size() > 1) {
447 p->setName(p->name().substr(0, p->name().size() - 1));
452 if (hasPrevAtom(cur) && prevAtom(cur)->nargs() > 0) {
453 // let's require two backspaces for 'big stuff' and
454 // highlight on the first
464 bool MathCursor::erase(LCursor & cur)
466 autocorrect_ = false;
467 if (inMacroMode(cur))
475 // delete empty cells if possible
477 //if (cur.cell().empty() && cur.inset()->idxDelete(cur.idx()))
480 // special behaviour when in last position of cell
481 if (cur.pos() == cur.lastpos()) {
482 bool one_cell = cur.inset()->asMathInset()->nargs() == 1;
483 if (one_cell && cur.depth() == 1 && cur.lastpos() == 0)
489 cur.inset()->asMathInset()->idxGlue(cur.idx());
493 if (hasNextAtom(cur) && nextAtom(cur)->nargs() > 0)
502 bool MathCursor::up(LCursor & cur, bool sel)
509 CursorBase save = Cursor_;
514 autocorrect_ = false;
519 bool MathCursor::down(LCursor & cur, bool sel)
526 CursorBase save = Cursor_;
531 autocorrect_ = false;
536 void MathCursor::macroModeClose(LCursor & cur)
538 if (!inMacroMode(cur))
540 MathUnknownInset * p = activeMacro(cur);
542 string s = p->name();
544 cur.cell().erase(cur.pos());
546 // do nothing if the macro name is empty
550 string const name = s.substr(1);
552 // prevent entering of recursive macros
553 if (formula()->lyxCode() == InsetOld::MATHMACRO_CODE
554 && formula()->getInsetName() == name)
555 lyxerr << "can't enter recursive macro" << endl;
557 niceInsert(cur, createMathInset(name));
561 string MathCursor::macroName(LCursor & cur) const
563 return inMacroMode(cur) ? activeMacro(cur)->name() : string();
567 void MathCursor::selClear(LCursor & cur)
570 cur.bv().clearSelection();
574 void MathCursor::selCopy(LCursor & cur)
578 theCutBuffer.push(grabSelection(cur));
581 //theCutBuffer.erase();
586 void MathCursor::selCut(LCursor & cur)
589 theCutBuffer.push(grabAndEraseSelection(cur));
593 void MathCursor::selDel(LCursor & cur)
603 void MathCursor::selPaste(LCursor & cur, size_t n)
607 if (n < theCutBuffer.size())
608 paste(cur, theCutBuffer[n]);
609 //grabSelection(cur);
614 void MathCursor::selHandle(LCursor & cur, bool sel)
616 if (sel == selection_)
624 void MathCursor::selStart(LCursor & cur)
634 void MathCursor::selClearOrDel(LCursor & cur)
636 if (lyxrc.auto_region_delete)
643 void MathCursor::drawSelection(PainterInfo & pi) const
649 getSelection(pi.base.bv->fullCursor(), i1, i2);
650 i1.asMathInset()->drawSelection(pi, i1.idx_, i1.pos_, i2.idx_, i2.pos_);
654 void MathCursor::handleNest(LCursor & cur, MathAtom const & a, int c)
657 asArray(grabAndEraseSelection(cur), at.nucleus()->cell(c));
659 pushRight(cur, prevAtom(cur));
663 void MathCursor::getScreenPos(LCursor & cur, int & x, int & y) const
665 cur.inset()->asMathInset()->getScreenPos(cur.idx(), cur.pos(), x, y);
669 int MathCursor::targetX(LCursor & cur) const
671 if (cur.bv().x_target() != -1)
672 return cur.bv().x_target();
675 getScreenPos(cur, x, y);
680 InsetFormulaBase * MathCursor::formula() const
686 void MathCursor::adjust(LCursor & cur, pos_type from, difference_type diff)
688 if (cur.pos() > from)
692 if (Anchor_.back().pos_ > from)
693 Anchor_.back().pos_ += diff;
694 // just to be on the safe side
695 // theoretically unecessary
701 bool MathCursor::inMacroMode(LCursor & cur) const
703 if (!hasPrevAtom(cur))
705 MathUnknownInset const * p = prevAtom(cur)->asUnknownInset();
706 return p && !p->final();
710 MathUnknownInset * MathCursor::activeMacro(LCursor & cur)
712 return inMacroMode(cur) ? prevAtom(cur).nucleus()->asUnknownInset() : 0;
716 MathUnknownInset const * MathCursor::activeMacro(LCursor & cur) const
718 return inMacroMode(cur) ? prevAtom(cur)->asUnknownInset() : 0;
722 bool MathCursor::inMacroArgMode(LCursor & cur) const
724 return cur.pos() > 0 && prevAtom(cur)->getChar() == '#';
728 bool MathCursor::selection() const
735 MathCursor::enclosingGrid(LCursor & cur, MathCursor::idx_type & idx) const
737 for (MathInset::difference_type i = cur.depth() - 1; i >= 0; --i) {
738 MathInset * m = cur.cursor_[i].inset()->asMathInset();
741 MathGridInset * p = m->asGridInset();
743 idx = cur.cursor_[i].idx_;
751 void MathCursor::popToHere(LCursor & cur, MathInset const * p)
753 while (cur.depth() && cur.inset()->asMathInset() != p)
758 void MathCursor::popToEnclosingGrid(LCursor & cur)
760 while (cur.depth() && !cur.inset()->asMathInset()->asGridInset())
765 void MathCursor::popToEnclosingHull(LCursor & cur)
767 while (cur.depth() && !cur.inset()->asMathInset()->asGridInset())
772 void MathCursor::pullArg(LCursor & cur)
775 MathArray ar = cur.cell();
778 cur.cell().insert(cur.pos(), ar);
781 formula()->mutateToText();
786 void MathCursor::touch()
790 CursorBase::const_iterator it = Cursor_.begin();
791 CursorBase::const_iterator et = Cursor_.end();
792 for ( ; it != et; ++it)
798 void MathCursor::normalize(LCursor & cur)
800 if (cur.idx() >= cur.nargs()) {
801 lyxerr << "this should not really happen - 1: "
802 << cur.idx() << ' ' << cur.nargs()
803 << " in: " << cur.inset() << endl;
806 cur.idx() = min(cur.idx(), cur.nargs() - 1);
808 if (cur.pos() > cur.lastpos()) {
809 lyxerr << "this should not really happen - 2: "
810 << cur.pos() << ' ' << cur.lastpos() << " in idx: " << cur.idx()
812 WriteStream wi(lyxerr, false, true);
813 cur.inset()->asMathInset()->write(wi);
817 cur.pos() = min(cur.pos(), cur.lastpos());
821 bool MathCursor::hasPrevAtom(LCursor & cur) const
823 return cur.pos() > 0;
827 bool MathCursor::hasNextAtom(LCursor & cur) const
829 return cur.pos() < cur.lastpos();
833 MathAtom const & MathCursor::prevAtom(LCursor & cur) const
835 BOOST_ASSERT(cur.pos() > 0);
836 return cur.cell()[cur.pos() - 1];
840 MathAtom & MathCursor::prevAtom(LCursor & cur)
842 BOOST_ASSERT(cur.pos() > 0);
843 return cur.cell()[cur.pos() - 1];
847 MathAtom const & MathCursor::nextAtom(LCursor & cur) const
849 BOOST_ASSERT(cur.pos() < cur.lastpos());
850 return cur.cell()[cur.pos()];
854 MathAtom & MathCursor::nextAtom(LCursor & cur)
856 BOOST_ASSERT(cur.pos() < cur.lastpos());
857 return cur.cell()[cur.pos()];
861 void MathCursor::idxNext(LCursor & cur)
863 cur.inset()->asMathInset()->idxNext(cur);
867 void MathCursor::idxPrev(LCursor & cur)
869 cur.inset()->asMathInset()->idxPrev(cur);
873 char MathCursor::valign(LCursor & cur) const
876 MathGridInset * p = enclosingGrid(cur, idx);
877 return p ? p->valign() : '\0';
881 char MathCursor::halign(LCursor & cur) const
884 MathGridInset * p = enclosingGrid(cur, idx);
885 return p ? p->halign(idx % p->ncols()) : '\0';
889 void MathCursor::getSelection(LCursor & cur,
890 CursorSlice & i1, CursorSlice & i2) const
892 CursorSlice anc = normalAnchor(cur);
893 if (anc < cur.top()) {
903 bool MathCursor::goUpDown(LCursor & cur, bool up)
905 // Be warned: The 'logic' implemented in this function is highly fragile.
906 // A distance of one pixel or a '<' vs '<=' _really_ matters.
907 // So fiddle around with it only if you know what you are doing!
910 getScreenPos(cur, xo, yo);
912 // check if we had something else in mind, if not, this is the future goal
913 if (cur.bv().x_target() == -1)
914 cur.bv().x_target(xo);
916 xo = cur.bv().x_target();
918 // try neigbouring script insets
921 if (hasPrevAtom(cur)) {
922 MathScriptInset const * p = prevAtom(cur)->asScriptInset();
923 if (p && p->has(up)) {
925 push(cur, nextAtom(cur));
926 cur.idx() = up; // the superscript has index 1
927 cur.pos() = cur.lastpos();
928 //lyxerr << "updown: handled by scriptinset to the left" << endl;
934 if (hasNextAtom(cur)) {
935 MathScriptInset const * p = nextAtom(cur)->asScriptInset();
936 if (p && p->has(up)) {
937 push(cur, nextAtom(cur));
940 //lyxerr << "updown: handled by scriptinset to the right" << endl;
946 // try current cell for e.g. text insets
947 if (cur.inset()->asMathInset()->idxUpDown2(cur, up, cur.bv().x_target()))
950 //xarray().boundingBox(xlow, xhigh, ylow, yhigh);
955 //if (bruteFind(xo, yo, xlow, xhigh, ylow, yhigh)) {
956 // lyxerr << "updown: handled by brute find in the same cell" << endl;
960 // try to find an inset that knows better then we
962 //lyxerr << "updown: We are in " << inset() << " idx: " << idx() << endl;
964 if (cur.inset()->asMathInset()->idxUpDown(cur, up, cur.bv().x_target())) {
965 // try to find best position within this inset
967 bruteFind2(cur, xo, yo);
971 // no such inset found, just take something "above"
972 //lyxerr << "updown: handled by strange case" << endl;
975 bruteFind(cur, xo, yo,
978 up ? formula()->ylow() : yo + 4,
979 up ? yo - 4 : formula()->yhigh()
982 // any improvement so far?
984 getScreenPos(cur, xnew, ynew);
985 if (up ? ynew < yo : ynew > yo)
991 bool MathCursor::bruteFind
992 (LCursor & cur, int x, int y, int xlow, int xhigh, int ylow, int yhigh)
994 CursorBase best_cursor;
995 double best_dist = 1e10;
997 CursorBase it = ibegin(formula()->par().nucleus());
998 CursorBase et = iend(formula()->par().nucleus());
1000 // avoid invalid nesting when selecting
1001 if (!selection_ || positionable(it, cur.anchor_)) {
1003 it.back().getScreenPos(xo, yo);
1004 if (xlow <= xo && xo <= xhigh && ylow <= yo && yo <= yhigh) {
1005 double d = (x - xo) * (x - xo) + (y - yo) * (y - yo);
1006 //lyxerr << "x: " << x << " y: " << y << " d: " << endl;
1007 // '<=' in order to take the last possible position
1008 // this is important for clicking behind \sum in e.g. '\sum_i a'
1009 if (d <= best_dist) {
1021 if (best_dist < 1e10)
1022 cur.cursor_ = best_cursor;
1023 return best_dist < 1e10;
1027 void MathCursor::bruteFind2(LCursor & cur, int x, int y)
1029 double best_dist = 1e10;
1031 CursorBase it = cur.cursor_;
1033 CursorBase et = cur.cursor_;
1034 int n = et.back().asMathInset()->cell(et.back().idx_).size();
1036 for (int i = 0; ; ++i) {
1038 it.back().getScreenPos(xo, yo);
1039 double d = (x - xo) * (x - xo) + (y - yo) * (y - yo);
1040 // '<=' in order to take the last possible position
1041 // this is important for clicking behind \sum in e.g. '\sum_i a'
1042 lyxerr << "i: " << i << " d: " << d << " best: " << best_dist << endl;
1043 if (d <= best_dist) {
1054 bool MathCursor::idxLineLast(LCursor & cur)
1056 cur.idx() -= cur.idx() % cur.ncols();
1057 cur.idx() += cur.ncols() - 1;
1058 cur.pos() = cur.lastpos();
1063 bool MathCursor::idxLeft(LCursor & cur)
1065 return cur.inset()->asMathInset()->idxLeft(cur);
1069 bool MathCursor::idxRight(LCursor & cur)
1071 return cur.inset()->asMathInset()->idxRight(cur);
1075 bool MathCursor::script(LCursor & cur, bool up)
1077 // Hack to get \\^ and \\_ working
1078 if (inMacroMode(cur) && macroName(cur) == "\\") {
1080 niceInsert(cur, createMathInset("mathcircumflex"));
1082 interpret(cur, '_');
1086 macroModeClose(cur);
1087 string safe = grabAndEraseSelection(cur);
1088 if (inNucleus(cur)) {
1089 // we are in a nucleus of a script inset, move to _our_ script
1090 cur.inset()->asMathInset()->asScriptInset()->ensure(up);
1093 } else if (hasPrevAtom(cur) && prevAtom(cur)->asScriptInset()) {
1094 prevAtom(cur).nucleus()->asScriptInset()->ensure(up);
1095 pushRight(cur, prevAtom(cur));
1097 cur.pos() = cur.lastpos();
1098 } else if (hasPrevAtom(cur)) {
1100 cur.cell()[cur.pos()]
1101 = MathAtom(new MathScriptInset(nextAtom(cur), up));
1102 pushLeft(cur, nextAtom(cur));
1106 plainInsert(cur, MathAtom(new MathScriptInset(up)));
1107 prevAtom(cur).nucleus()->asScriptInset()->ensure(up);
1108 pushRight(cur, prevAtom(cur));
1118 bool MathCursor::interpret(LCursor & cur, char c)
1120 //lyxerr << "interpret 2: '" << c << "'" << endl;
1121 cur.bv().x_target(-1); // "no target"
1122 if (inMacroArgMode(cur)) {
1126 MathMacroTemplate const * p = formula()->par()->asMacroTemplate();
1127 if (p && 1 <= n && n <= p->numargs())
1128 insert(cur, MathAtom(new MathMacroArgument(c - '0')));
1130 insert(cur, createMathInset("#"));
1131 interpret(cur, c); // try again
1137 if (inMacroMode(cur)) {
1138 string name = macroName(cur);
1139 //lyxerr << "interpret name: '" << name << "'" << endl;
1142 activeMacro(cur)->setName(activeMacro(cur)->name() + c);
1146 // handle 'special char' macros
1151 if (currentMode(cur) == MathInset::TEXT_MODE)
1152 niceInsert(cur, createMathInset("textbackslash"));
1154 niceInsert(cur, createMathInset("backslash"));
1155 } else if (c == '{') {
1156 niceInsert(cur, MathAtom(new MathBraceInset));
1158 niceInsert(cur, createMathInset(string(1, c)));
1163 // leave macro mode and try again if necessary
1164 macroModeClose(cur);
1166 niceInsert(cur, MathAtom(new MathBraceInset));
1172 // This is annoying as one has to press <space> far too often.
1176 // leave autocorrect mode if necessary
1177 if (autocorrect_ && c == ' ') {
1178 autocorrect_ = false;
1183 // just clear selection on pressing the space bar
1184 if (selection_ && c == ' ') {
1192 //lyxerr << "starting with macro" << endl;
1193 insert(cur, MathAtom(new MathUnknownInset("\\", false)));
1198 if (currentMode(cur) == MathInset::TEXT_MODE)
1204 if (currentMode(cur) == MathInset::TEXT_MODE) {
1205 // insert spaces in text mode,
1206 // but suppress direct insertion of two spaces in a row
1207 // the still allows typing '<space>a<space>' and deleting the 'a', but
1208 // it is better than nothing...
1209 if (!hasPrevAtom(cur) || prevAtom(cur)->getChar() != ' ')
1213 if (hasPrevAtom(cur) && prevAtom(cur)->asSpaceInset()) {
1214 prevAtom(cur).nucleus()->asSpaceInset()->incSpace();
1219 // if are at the very end, leave the formula
1220 return cur.pos() != cur.lastpos();
1233 if (c == '{' || c == '}' || c == '#' || c == '&' || c == '$') {
1234 niceInsert(cur, createMathInset(string(1, c)));
1239 niceInsert(cur, MathAtom(new MathCommentInset));
1243 // try auto-correction
1244 //if (autocorrect_ && hasPrevAtom() && math_autocorrect(prevAtom(), c))
1247 // no special circumstances, so insert the character without any fuss
1249 autocorrect_ = true;
1254 void MathCursor::setSelection
1255 (LCursor & cur, CursorBase const & where, size_t n)
1258 cur.cursor_ = where;
1259 cur.anchor_ = where;
1264 void MathCursor::insetToggle(LCursor & cur)
1266 if (hasNextAtom(cur)) {
1267 // toggle previous inset ...
1268 nextAtom(cur).nucleus()->lock(!nextAtom(cur)->lock());
1269 } else if (popLeft(cur) && hasNextAtom(cur)) {
1270 // ... or enclosing inset if we are in the last inset position
1271 nextAtom(cur).nucleus()->lock(!nextAtom(cur)->lock());
1277 string MathCursor::info(LCursor & cur) const
1280 os << "Math editor mode. ";
1281 for (int i = 0, n = cur.depth(); i < n; ++i) {
1282 cur.cursor_[i].asMathInset()->infoize(os);
1285 if (hasPrevAtom(cur))
1286 prevAtom(cur)->infoize2(os);
1294 void region(CursorSlice const & i1, CursorSlice const & i2,
1295 MathInset::row_type & r1, MathInset::row_type & r2,
1296 MathInset::col_type & c1, MathInset::col_type & c2)
1298 MathInset * p = i1.asMathInset();
1299 c1 = p->col(i1.idx_);
1300 c2 = p->col(i2.idx_);
1303 r1 = p->row(i1.idx_);
1304 r2 = p->row(i2.idx_);
1312 string MathCursor::grabSelection(LCursor & cur) const
1319 getSelection(cur, i1, i2);
1321 if (i1.idx_ == i2.idx_) {
1322 MathArray::const_iterator it = i1.cell().begin();
1323 return asString(MathArray(it + i1.pos_, it + i2.pos_));
1328 region(i1, i2, r1, r2, c1, c2);
1331 for (row_type row = r1; row <= r2; ++row) {
1334 for (col_type col = c1; col <= c2; ++col) {
1337 data += asString(i1.asMathInset()->cell(i1.asMathInset()->index(row, col)));
1344 void MathCursor::eraseSelection(LCursor & cur)
1348 getSelection(cur, i1, i2);
1349 if (i1.idx_ == i2.idx_)
1350 i1.cell().erase(i1.pos_, i2.pos_);
1352 MathInset * p = i1.asMathInset();
1355 region(i1, i2, r1, r2, c1, c2);
1356 for (row_type row = r1; row <= r2; ++row)
1357 for (col_type col = c1; col <= c2; ++col)
1358 p->cell(p->index(row, col)).clear();
1364 string MathCursor::grabAndEraseSelection(LCursor & cur)
1368 string res = grabSelection(cur);
1369 eraseSelection(cur);
1375 CursorSlice MathCursor::normalAnchor(LCursor & cur) const
1379 if (Anchor_.size() < cur.depth()) {
1381 lyxerr << "unusual Anchor size" << endl;
1383 //lyx::BOOST_ASSERT(Anchor_.size() >= cursor.cur.depth());
1384 // use Anchor on the same level as Cursor
1385 CursorSlice normal = Anchor_[cur.depth() - 1];
1386 if (cur.depth() < Anchor_.size() && !(normal < cursor())) {
1387 // anchor is behind cursor -> move anchor behind the inset
1397 DispatchResult MathCursor::dispatch(LCursor &, FuncRequest const & cmd)
1399 // mouse clicks are somewhat special
1401 switch (cmd.action) {
1402 case LFUN_MOUSE_PRESS:
1403 case LFUN_MOUSE_MOTION:
1404 case LFUN_MOUSE_RELEASE:
1405 case LFUN_MOUSE_DOUBLE: {
1407 CursorSlice & pos = Cursor_.back();
1411 if (x < cmd.x && hasPrevAtom()) {
1412 DispatchResult const res =
1413 prevAtom().nucleus()->dispatch(cur, cmd);
1414 if (res.dispatched())
1417 if (x > cmd.x && hasNextAtom()) {
1418 DispatchResult const res =
1419 nextAtom().nucleus()->dispatch(cur, cmd);
1420 if (res.dispatched())
1430 for (int i = cur.depth() - 1; i >= 0; --i) {
1431 CursorBase tmp = cur->cursor_;
1432 CursorSlice & pos = tmp.back()
1433 DispatchResult const res = pos.asMathInset()->dispatch(cur, cmd);
1434 if (res.dispatched()) {
1435 if (res.val() == FINISHED) {
1436 if (i + 1 < Cursor_.size())
1437 Cursor_.erase(Cursor_.begin() + i + 1, Cursor_.end());
1444 return DispatchResult(false);
1448 MathInset::mode_type MathCursor::currentMode(LCursor &) const
1451 for (int i = Cursor_.size() - 1; i >= 0; --i) {
1452 MathInset::mode_type res = Cursor_[i].asMathInset()->currentMode();
1453 if (res != MathInset::UNDECIDED_MODE)
1457 return MathInset::UNDECIDED_MODE;
1461 void MathCursor::handleFont(LCursor & cur, string const & font)
1465 macroModeClose(cur);
1466 safe = grabAndEraseSelection(cur);
1469 if (cur.lastpos() != 0) {
1470 // something left in the cell
1471 if (cur.pos() == 0) {
1472 // cursor in first position
1474 } else if (cur.pos() == cur.lastpos()) {
1475 // cursor in last position
1478 // cursor in between. split cell
1479 MathArray::iterator bt = cur.cell().begin();
1480 MathAtom at = createMathInset(font);
1481 at.nucleus()->cell(0) = MathArray(bt, bt + cur.pos());
1482 cur.cell().erase(bt, bt + cur.pos());
1484 plainInsert(cur, at);
1487 // nothing left in the cell
1495 void releaseMathCursor(BufferView & bv)
1498 InsetFormulaBase * f = mathcursor->formula();