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) : last(*bv);
63 MathCursor::~MathCursor()
65 // ensure that 'notifyCursorLeave' is called
71 void MathCursor::push(BufferView & bv, MathAtom & t)
73 bv.fullCursor().push(t.nucleus());
77 void MathCursor::pushLeft(BufferView & bv, MathAtom & t)
79 //lyxerr << "Entering atom " << t << " left" << endl;
85 void MathCursor::pushRight(BufferView & bv, MathAtom & t)
87 //lyxerr << "Entering atom " << t << " right" << endl;
94 bool MathCursor::popLeft(BufferView & bv)
96 CursorSlice & cur = cursorTip(bv);
97 //lyxerr << "Leaving atom to the left" << endl;
100 cur.inset()->asMathInset()->notifyCursorLeaves(cur.idx());
103 cur.inset()->asMathInset()->notifyCursorLeaves(cur.idx());
104 bv.fullCursor().pop();
109 bool MathCursor::popRight(BufferView & bv)
111 CursorSlice & cur = cursorTip(bv);
112 //lyxerr << "Leaving atom "; bv.inset->asMathInset()->write(cerr, false); cerr << " right" << endl;
113 if (depth(bv) <= 1) {
115 cur.inset()->asMathInset()->notifyCursorLeaves(cur.idx());
118 cur.inset()->asMathInset()->notifyCursorLeaves(cur.idx());
119 bv.fullCursor().pop();
127 void MathCursor::dump(char const * what) const
129 lyxerr << "MC: " << what << endl;
130 lyxerr << " Cursor: " << depth() << endl;
131 for (unsigned i = 0; i < depth(); ++i)
132 lyxerr << " i: " << i << ' ' << Cursor_[i] << endl;
133 lyxerr << " Anchor: " << Anchor_.size() << endl;
134 for (unsigned i = 0; i < Anchor_.size(); ++i)
135 lyxerr << " i: " << i << ' ' << Anchor_[i] << endl;
136 lyxerr << " sel: " << selection_ << endl;
139 void MathCursor::dump(char const *) const {}
143 bool MathCursor::isInside(MathInset const *) const
147 for (unsigned i = 0; i < depth(); ++i)
148 if (Cursor_[i].asMathInset() == p)
155 bool MathCursor::openable(MathAtom const & t, bool sel) const
166 // we can't move into anything new during selection
167 if (depth() == Anchor_.size())
169 if (t.operator->() != Anchor_[depth()].asMathInset())
181 bool MathCursor::inNucleus(BufferView & bv) const
183 CursorSlice & cur = cursorTip(bv);
184 return cur.inset()->asMathInset()->asScriptInset() && cur.idx() == 2;
188 bool MathCursor::posLeft(BufferView & bv)
190 CursorSlice & cur = cursorTip(bv);
198 bool MathCursor::posRight(BufferView & bv)
200 CursorSlice & cur = cursorTip(bv);
201 if (cur.pos() == cur.lastpos())
208 bool MathCursor::left(BufferView & bv, bool sel)
211 autocorrect_ = false;
212 bv.x_target(-1); // "no target"
213 if (inMacroMode(bv)) {
219 if (hasPrevAtom(bv) && openable(prevAtom(bv), sel)) {
220 pushRight(bv, prevAtom(bv));
224 return posLeft(bv) || idxLeft(bv) || popLeft(bv) || selection_;
228 bool MathCursor::right(BufferView & bv, bool sel)
231 autocorrect_ = false;
232 bv.x_target(-1); // "no target"
233 if (inMacroMode(bv)) {
239 if (hasNextAtom(bv) && openable(nextAtom(bv), sel)) {
240 pushLeft(bv, nextAtom(bv));
244 return posRight(bv) || idxRight(bv) || popRight(bv) || selection_;
248 void MathCursor::first(BufferView & bv)
252 push(bv, formula_->par());
253 bv.cursor().inset()->asMathInset()->idxFirst(bv);
258 void MathCursor::last(BufferView & bv)
262 push(bv, formula_->par());
263 bv.cursor().inset()->asMathInset()->idxLast(bv);
268 bool positionable(CursorBase const & cursor, CursorBase const & anchor)
270 // avoid deeper nested insets when selecting
271 if (cursor.size() > anchor.size())
274 // anchor might be deeper, should have same path then
275 for (CursorBase::size_type i = 0; i < cursor.size(); ++i)
276 if (cursor[i].asMathInset() != anchor[i].asMathInset())
279 // position should be ok.
284 void MathCursor::setScreenPos(BufferView & bv, int x, int y)
286 dump("setScreenPos 1");
287 bool res = bruteFind(bv, x, y,
288 formula()->xlow(), formula()->xhigh(),
289 formula()->ylow(), formula()->yhigh());
291 // this can happen on creation of "math-display"
292 dump("setScreenPos 1.5");
295 bv.x_target(-1); // "no target"
296 dump("setScreenPos 2");
301 bool MathCursor::home(BufferView & bv, bool sel)
304 autocorrect_ = false;
307 if (!bv.cursor().inset()->asMathInset()->idxHome(bv))
310 bv.x_target(-1); // "no target"
315 bool MathCursor::end(BufferView & bv, bool sel)
318 autocorrect_ = false;
321 if (!bv.cursor().inset()->asMathInset()->idxEnd(bv))
324 bv.x_target(-1); // "no target"
329 void MathCursor::plainErase(BufferView & bv)
331 CursorSlice & cur = cursorTip(bv);
332 cur.cell().erase(cur.pos());
336 void MathCursor::markInsert(BufferView & bv)
338 //lyxerr << "inserting mark" << endl;
339 CursorSlice & cur = cursorTip(bv);
340 cur.cell().insert(cur.pos(), MathAtom(new MathCharInset(0)));
344 void MathCursor::markErase(BufferView & bv)
346 //lyxerr << "deleting mark" << endl;
347 CursorSlice & cur = cursorTip(bv);
348 cur.cell().erase(cur.pos());
352 void MathCursor::plainInsert(BufferView & bv, MathAtom const & t)
355 CursorSlice & cur = cursorTip(bv);
356 cur.cell().insert(cur.pos(), t);
361 void MathCursor::insert2(BufferView & bv, string const & str)
369 void MathCursor::insert(BufferView & bv, string const & str)
371 //lyxerr << "inserting '" << str << "'" << endl;
373 for (string::const_iterator it = str.begin(); it != str.end(); ++it)
374 plainInsert(bv, MathAtom(new MathCharInset(*it)));
378 void MathCursor::insert(BufferView & bv, char c)
380 //lyxerr << "inserting '" << c << "'" << endl;
382 plainInsert(bv, MathAtom(new MathCharInset(c)));
386 void MathCursor::insert(BufferView & bv, MathAtom const & t)
394 void MathCursor::niceInsert(BufferView & bv, string const & t)
399 niceInsert(bv, ar[0]);
405 void MathCursor::niceInsert(BufferView & bv, MathAtom const & t)
408 string safe = grabAndEraseSelection(bv);
410 // enter the new inset and move the contents of the selection if possible
413 pushLeft(bv, nextAtom(bv));
419 void MathCursor::insert(BufferView & bv, MathArray const & ar)
421 CursorSlice & cur = cursorTip(bv);
425 cur.cell().insert(cur.pos(), ar);
426 cur.pos() += ar.size();
430 void MathCursor::paste(BufferView & bv, string const & data)
432 dispatch(bv, FuncRequest(LFUN_PASTE, data));
436 bool MathCursor::backspace(BufferView & bv)
438 CursorSlice & cur = cursorTip(bv);
439 autocorrect_ = false;
446 if (cur.pos() == 0) {
447 if (cur.inset()->asMathInset()->nargs() == 1 &&
455 if (inMacroMode(bv)) {
456 MathUnknownInset * p = activeMacro(bv);
457 if (p->name().size() > 1) {
458 p->setName(p->name().substr(0, p->name().size() - 1));
463 if (hasPrevAtom(bv) && prevAtom(bv)->nargs() > 0) {
464 // let's require two backspaces for 'big stuff' and
465 // highlight on the first
475 bool MathCursor::erase(BufferView & bv)
477 CursorSlice & cur = cursorTip(bv);
478 autocorrect_ = false;
487 // delete empty cells if possible
489 //if (cur.cell().empty() && cur.inset()->idxDelete(cur.idx()))
492 // special behaviour when in last position of cell
493 if (cur.pos() == cur.lastpos()) {
494 bool one_cell = cur.inset()->asMathInset()->nargs() == 1;
495 if (one_cell && depth(bv) == 1 && cur.lastpos() == 0)
501 cur.inset()->asMathInset()->idxGlue(cur.idx());
505 if (hasNextAtom(bv) && nextAtom(bv)->nargs() > 0)
514 bool MathCursor::up(BufferView & bv, bool sel)
521 CursorBase save = Cursor_;
526 autocorrect_ = false;
531 bool MathCursor::down(BufferView & bv, bool sel)
538 CursorBase save = Cursor_;
543 autocorrect_ = false;
548 void MathCursor::macroModeClose(BufferView & bv)
550 CursorSlice & cur = cursorTip(bv);
551 if (!inMacroMode(bv))
553 MathUnknownInset * p = activeMacro(bv);
555 string s = p->name();
557 cur.cell().erase(cur.pos());
559 // do nothing if the macro name is empty
563 string const name = s.substr(1);
565 // prevent entering of recursive macros
566 if (formula()->lyxCode() == InsetOld::MATHMACRO_CODE
567 && formula()->getInsetName() == name)
568 lyxerr << "can't enter recursive macro" << endl;
570 niceInsert(bv, createMathInset(name));
574 string MathCursor::macroName(BufferView & bv) const
576 return inMacroMode(bv) ? activeMacro(bv)->name() : string();
580 void MathCursor::selClear(BufferView & bv)
587 void MathCursor::selCopy(BufferView & bv)
591 theCutBuffer.push(grabSelection(bv));
594 //theCutBuffer.erase();
599 void MathCursor::selCut(BufferView & bv)
602 theCutBuffer.push(grabAndEraseSelection(bv));
606 void MathCursor::selDel(BufferView & bv)
616 void MathCursor::selPaste(BufferView & bv, size_t n)
620 if (n < theCutBuffer.size())
621 paste(bv, theCutBuffer[n]);
627 void MathCursor::selHandle(BufferView & bv, bool sel)
629 if (sel == selection_)
637 void MathCursor::selStart(BufferView & bv)
647 void MathCursor::selClearOrDel(BufferView & bv)
649 if (lyxrc.auto_region_delete)
656 void MathCursor::drawSelection(PainterInfo & pi) const
662 getSelection(*pi.base.bv, i1, i2);
663 i1.asMathInset()->drawSelection(pi, i1.idx_, i1.pos_, i2.idx_, i2.pos_);
667 void MathCursor::handleNest(BufferView & bv, MathAtom const & a, int c)
670 asArray(grabAndEraseSelection(bv), at.nucleus()->cell(c));
672 pushRight(bv, prevAtom(bv));
676 void MathCursor::getScreenPos(BufferView & bv, int & x, int & y) const
678 CursorSlice & cur = cursorTip(bv);
679 cur.inset()->asMathInset()->getScreenPos(cur.idx(), cur.pos(), x, y);
683 int MathCursor::targetX(BufferView & bv) const
685 if (bv.x_target() != -1)
686 return bv.x_target();
689 getScreenPos(bv, x, y);
694 InsetFormulaBase * MathCursor::formula() const
700 void MathCursor::adjust(BufferView & bv, pos_type from, difference_type diff)
702 CursorSlice & cur = cursorTip(bv);
703 if (cur.pos() > from)
707 if (Anchor_.back().pos_ > from)
708 Anchor_.back().pos_ += diff;
709 // just to be on the safe side
710 // theoretically unecessary
716 bool MathCursor::inMacroMode(BufferView & bv) const
718 if (!hasPrevAtom(bv))
720 MathUnknownInset const * p = prevAtom(bv)->asUnknownInset();
721 return p && !p->final();
725 MathUnknownInset * MathCursor::activeMacro(BufferView & bv)
727 return inMacroMode(bv) ? prevAtom(bv).nucleus()->asUnknownInset() : 0;
731 MathUnknownInset const * MathCursor::activeMacro(BufferView & bv) const
733 return inMacroMode(bv) ? prevAtom(bv)->asUnknownInset() : 0;
737 bool MathCursor::inMacroArgMode(BufferView & bv) const
739 return bv.cursor().pos() > 0 && prevAtom(bv)->getChar() == '#';
743 bool MathCursor::selection() const
749 MathGridInset * MathCursor::enclosingGrid
750 (BufferView &, MathCursor::idx_type &) const
754 for (MathInset::difference_type i = depth() - 1; i >= 0; --i) {
755 MathGridInset * p = Cursor_[i].asMathInset()->asGridInset();
757 idx = Cursor_[i].idx_;
766 void MathCursor::popToHere(BufferView & bv, MathInset const * p)
768 while (depth(bv) && bv.cursor().asMathInset() != p)
769 bv.fullCursor().cursor_.pop_back();
773 void MathCursor::popToEnclosingGrid(BufferView & bv)
775 while (depth(bv) && !bv.cursor().asMathInset()->asGridInset())
776 bv.fullCursor().cursor_.pop_back();
780 void MathCursor::popToEnclosingHull(BufferView & bv)
782 while (depth(bv) && !bv.cursor().asMathInset()->asGridInset())
783 bv.fullCursor().cursor_.pop_back();
787 void MathCursor::pullArg(BufferView & bv)
789 CursorSlice & cur = cursorTip(bv);
791 MathArray ar = cur.cell();
794 cur.cell().insert(cur.pos(), ar);
797 formula()->mutateToText();
802 void MathCursor::touch()
806 CursorBase::const_iterator it = Cursor_.begin();
807 CursorBase::const_iterator et = Cursor_.end();
808 for ( ; it != et; ++it)
814 void MathCursor::normalize(BufferView & bv)
816 CursorSlice & cur = cursorTip(bv);
817 if (cur.idx() >= cur.nargs()) {
818 lyxerr << "this should not really happen - 1: "
819 << cur.idx() << ' ' << cur.nargs()
820 << " in: " << cur.inset() << endl;
823 cur.idx() = min(cur.idx(), cur.nargs() - 1);
825 if (cur.pos() > cur.lastpos()) {
826 lyxerr << "this should not really happen - 2: "
827 << cur.pos() << ' ' << cur.lastpos() << " in idx: " << cur.idx()
829 WriteStream wi(lyxerr, false, true);
830 cur.inset()->asMathInset()->write(wi);
834 cur.pos() = min(cur.pos(), cur.lastpos());
838 bool MathCursor::hasPrevAtom(BufferView & bv) const
840 CursorSlice & cur = cursorTip(bv);
841 return cur.pos() > 0;
845 bool MathCursor::hasNextAtom(BufferView & bv) const
847 CursorSlice & cur = cursorTip(bv);
848 return cur.pos() < cur.lastpos();
852 MathAtom const & MathCursor::prevAtom(BufferView & bv) const
854 CursorSlice & cur = cursorTip(bv);
855 BOOST_ASSERT(cur.pos() > 0);
856 return cur.cell()[cur.pos() - 1];
860 MathAtom & MathCursor::prevAtom(BufferView & bv)
862 CursorSlice & cur = cursorTip(bv);
863 BOOST_ASSERT(cur.pos() > 0);
864 return cur.cell()[cur.pos() - 1];
868 MathAtom const & MathCursor::nextAtom(BufferView & bv) const
870 CursorSlice & cur = cursorTip(bv);
871 BOOST_ASSERT(cur.pos() < cur.lastpos());
872 return cur.cell()[cur.pos()];
876 MathAtom & MathCursor::nextAtom(BufferView & bv)
878 CursorSlice & cur = cursorTip(bv);
879 BOOST_ASSERT(cur.pos() < cur.lastpos());
880 return cur.cell()[cur.pos()];
884 void MathCursor::idxNext(BufferView & bv)
886 CursorSlice & cur = cursorTip(bv);
887 cur.inset()->asMathInset()->idxNext(bv);
891 void MathCursor::idxPrev(BufferView & bv)
893 CursorSlice & cur = cursorTip(bv);
894 cur.inset()->asMathInset()->idxPrev(bv);
898 char MathCursor::valign(BufferView & bv) const
901 MathGridInset * p = enclosingGrid(bv, idx);
902 return p ? p->valign() : '\0';
906 char MathCursor::halign(BufferView & bv) const
909 MathGridInset * p = enclosingGrid(bv, idx);
910 return p ? p->halign(idx % p->ncols()) : '\0';
914 void MathCursor::getSelection(BufferView & bv,
915 CursorSlice & i1, CursorSlice & i2) const
917 CursorSlice anc = normalAnchor(bv);
918 if (anc < bv.cursor()) {
928 bool MathCursor::goUpDown(BufferView & bv, bool up)
930 // Be warned: The 'logic' implemented in this function is highly fragile.
931 // A distance of one pixel or a '<' vs '<=' _really_ matters.
932 // So fiddle around with it only if you know what you are doing!
935 getScreenPos(bv, xo, yo);
937 // check if we had something else in mind, if not, this is the future goal
938 if (bv.x_target() == -1)
943 // try neigbouring script insets
946 if (hasPrevAtom(bv)) {
947 MathScriptInset const * p = prevAtom(bv)->asScriptInset();
948 if (p && p->has(up)) {
950 push(bv, nextAtom(bv));
951 bv.cursor().idx() = up; // the superscript has index 1
952 bv.cursor().pos() = bv.cursor().lastpos();
953 //lyxerr << "updown: handled by scriptinset to the left" << endl;
959 if (hasNextAtom(bv)) {
960 MathScriptInset const * p = nextAtom(bv)->asScriptInset();
961 if (p && p->has(up)) {
962 push(bv, nextAtom(bv));
963 bv.cursor().idx() = up;
964 bv.cursor().pos() = 0;
965 //lyxerr << "updown: handled by scriptinset to the right" << endl;
971 // try current cell for e.g. text insets
972 if (bv.cursor().inset()->asMathInset()->idxUpDown2(bv, up, bv.x_target()))
975 //xarray().boundingBox(xlow, xhigh, ylow, yhigh);
980 //if (bruteFind(xo, yo, xlow, xhigh, ylow, yhigh)) {
981 // lyxerr << "updown: handled by brute find in the same cell" << endl;
985 // try to find an inset that knows better then we
987 //lyxerr << "updown: We are in " << inset() << " idx: " << idx() << endl;
989 if (bv.cursor().inset()->asMathInset()->idxUpDown(bv, up, bv.x_target())) {
990 // try to find best position within this inset
992 bruteFind2(bv, xo, yo);
996 // no such inset found, just take something "above"
997 //lyxerr << "updown: handled by strange case" << endl;
1000 bruteFind(bv, xo, yo,
1003 up ? formula()->ylow() : yo + 4,
1004 up ? yo - 4 : formula()->yhigh()
1007 // any improvement so far?
1009 getScreenPos(bv, xnew, ynew);
1010 if (up ? ynew < yo : ynew > yo)
1016 bool MathCursor::bruteFind
1017 (BufferView & bv, int x, int y, int xlow, int xhigh, int ylow, int yhigh)
1019 CursorBase best_cursor;
1020 double best_dist = 1e10;
1022 CursorBase it = ibegin(formula()->par().nucleus());
1023 CursorBase et = iend(formula()->par().nucleus());
1025 // avoid invalid nesting when selecting
1026 if (!selection_ || positionable(it, bv.fullCursor().anchor_)) {
1028 it.back().getScreenPos(xo, yo);
1029 if (xlow <= xo && xo <= xhigh && ylow <= yo && yo <= yhigh) {
1030 double d = (x - xo) * (x - xo) + (y - yo) * (y - yo);
1031 //lyxerr << "x: " << x << " y: " << y << " d: " << endl;
1032 // '<=' in order to take the last possible position
1033 // this is important for clicking behind \sum in e.g. '\sum_i a'
1034 if (d <= best_dist) {
1046 if (best_dist < 1e10)
1047 bv.fullCursor().cursor_ = best_cursor;
1048 return best_dist < 1e10;
1052 void MathCursor::bruteFind2(BufferView & bv, int x, int y)
1054 double best_dist = 1e10;
1056 CursorBase it = bv.fullCursor().cursor_;
1058 CursorBase et = bv.fullCursor().cursor_;
1059 int n = et.back().asMathInset()->cell(et.back().idx_).size();
1061 for (int i = 0; ; ++i) {
1063 it.back().getScreenPos(xo, yo);
1064 double d = (x - xo) * (x - xo) + (y - yo) * (y - yo);
1065 // '<=' in order to take the last possible position
1066 // this is important for clicking behind \sum in e.g. '\sum_i a'
1067 lyxerr << "i: " << i << " d: " << d << " best: " << best_dist << endl;
1068 if (d <= best_dist) {
1070 bv.fullCursor().cursor_ = it;
1079 bool MathCursor::idxLineLast(BufferView & bv)
1081 CursorSlice & cur = bv.cursor();
1082 cur.idx() -= cur.idx() % cur.ncols();
1083 cur.idx() += cur.ncols() - 1;
1084 cur.pos() = cur.lastpos();
1089 bool MathCursor::idxLeft(BufferView & bv)
1091 return bv.cursor().inset()->asMathInset()->idxLeft(bv);
1095 bool MathCursor::idxRight(BufferView & bv)
1097 return bv.cursor().inset()->asMathInset()->idxRight(bv);
1101 bool MathCursor::script(BufferView & bv, bool up)
1103 // Hack to get \\^ and \\_ working
1104 if (inMacroMode(bv) && macroName(bv) == "\\") {
1106 niceInsert(bv, createMathInset("mathcircumflex"));
1113 string safe = grabAndEraseSelection(bv);
1114 if (inNucleus(bv)) {
1115 // we are in a nucleus of a script inset, move to _our_ script
1116 bv.cursor().inset()->asMathInset()->asScriptInset()->ensure(up);
1117 bv.cursor().idx() = up;
1118 bv.cursor().pos() = 0;
1119 } else if (hasPrevAtom(bv) && prevAtom(bv)->asScriptInset()) {
1120 prevAtom(bv).nucleus()->asScriptInset()->ensure(up);
1121 pushRight(bv, prevAtom(bv));
1122 bv.cursor().idx() = up;
1123 bv.cursor().pos() = bv.cursor().lastpos();
1124 } else if (hasPrevAtom(bv)) {
1125 --bv.cursor().pos();
1126 bv.cursor().cell()[bv.cursor().pos()]
1127 = MathAtom(new MathScriptInset(nextAtom(bv), up));
1128 pushLeft(bv, nextAtom(bv));
1129 bv.cursor().idx() = up;
1130 bv.cursor().pos() = 0;
1132 plainInsert(bv, MathAtom(new MathScriptInset(up)));
1133 prevAtom(bv).nucleus()->asScriptInset()->ensure(up);
1134 pushRight(bv, prevAtom(bv));
1135 bv.cursor().idx() = up;
1136 bv.cursor().pos() = 0;
1144 bool MathCursor::interpret(BufferView & bv, char c)
1146 //lyxerr << "interpret 2: '" << c << "'" << endl;
1147 CursorSlice & cur = bv.cursor();
1148 bv.x_target(-1); // "no target"
1149 if (inMacroArgMode(bv)) {
1153 MathMacroTemplate const * p = formula()->par()->asMacroTemplate();
1154 if (p && 1 <= n && n <= p->numargs())
1155 insert(bv, MathAtom(new MathMacroArgument(c - '0')));
1157 insert(bv, createMathInset("#"));
1158 interpret(bv, c); // try again
1164 if (inMacroMode(bv)) {
1165 string name = macroName(bv);
1166 //lyxerr << "interpret name: '" << name << "'" << endl;
1169 activeMacro(bv)->setName(activeMacro(bv)->name() + c);
1173 // handle 'special char' macros
1178 if (currentMode(bv) == MathInset::TEXT_MODE)
1179 niceInsert(bv, createMathInset("textbackslash"));
1181 niceInsert(bv, createMathInset("backslash"));
1182 } else if (c == '{') {
1183 niceInsert(bv, MathAtom(new MathBraceInset));
1185 niceInsert(bv, createMathInset(string(1, c)));
1190 // leave macro mode and try again if necessary
1193 niceInsert(bv, MathAtom(new MathBraceInset));
1199 // This is annoying as one has to press <space> far too often.
1203 // leave autocorrect mode if necessary
1204 if (autocorrect_ && c == ' ') {
1205 autocorrect_ = false;
1210 // just clear selection on pressing the space bar
1211 if (selection_ && c == ' ') {
1219 //lyxerr << "starting with macro" << endl;
1220 insert(bv, MathAtom(new MathUnknownInset("\\", false)));
1225 if (currentMode(bv) == MathInset::TEXT_MODE)
1231 if (currentMode(bv) == MathInset::TEXT_MODE) {
1232 // insert spaces in text mode,
1233 // but suppress direct insertion of two spaces in a row
1234 // the still allows typing '<space>a<space>' and deleting the 'a', but
1235 // it is better than nothing...
1236 if (!hasPrevAtom(bv) || prevAtom(bv)->getChar() != ' ')
1240 if (hasPrevAtom(bv) && prevAtom(bv)->asSpaceInset()) {
1241 prevAtom(bv).nucleus()->asSpaceInset()->incSpace();
1246 // if are at the very end, leave the formula
1247 return cur.pos() != cur.lastpos();
1260 if (c == '{' || c == '}' || c == '#' || c == '&' || c == '$') {
1261 niceInsert(bv, createMathInset(string(1, c)));
1266 niceInsert(bv, MathAtom(new MathCommentInset));
1270 // try auto-correction
1271 //if (autocorrect_ && hasPrevAtom() && math_autocorrect(prevAtom(), c))
1274 // no special circumstances, so insert the character without any fuss
1276 autocorrect_ = true;
1281 void MathCursor::setSelection
1282 (BufferView & bv, CursorBase const & where, size_t n)
1285 bv.fullCursor().cursor_ = where;
1286 bv.fullCursor().anchor_ = where;
1287 bv.cursor().pos_ += n;
1291 void MathCursor::insetToggle(BufferView & bv)
1293 if (hasNextAtom(bv)) {
1294 // toggle previous inset ...
1295 nextAtom(bv).nucleus()->lock(!nextAtom(bv)->lock());
1296 } else if (popLeft(bv) && hasNextAtom(bv)) {
1297 // ... or enclosing inset if we are in the last inset position
1298 nextAtom(bv).nucleus()->lock(!nextAtom(bv)->lock());
1304 string MathCursor::info(BufferView & bv) const
1307 os << "Math editor mode. ";
1308 for (int i = 0, n = depth(bv); i < n; ++i) {
1309 bv.fullCursor().cursor_[i].asMathInset()->infoize(os);
1312 if (hasPrevAtom(bv))
1313 prevAtom(bv)->infoize2(os);
1319 unsigned MathCursor::depth(BufferView & bv) const
1321 return bv.fullCursor().cursor_.size();
1329 void region(CursorSlice const & i1, CursorSlice const & i2,
1330 MathInset::row_type & r1, MathInset::row_type & r2,
1331 MathInset::col_type & c1, MathInset::col_type & c2)
1333 MathInset * p = i1.asMathInset();
1334 c1 = p->col(i1.idx_);
1335 c2 = p->col(i2.idx_);
1338 r1 = p->row(i1.idx_);
1339 r2 = p->row(i2.idx_);
1347 string MathCursor::grabSelection(BufferView & bv) const
1354 getSelection(bv, i1, i2);
1356 if (i1.idx_ == i2.idx_) {
1357 MathArray::const_iterator it = i1.cell().begin();
1358 return asString(MathArray(it + i1.pos_, it + i2.pos_));
1363 region(i1, i2, r1, r2, c1, c2);
1366 for (row_type row = r1; row <= r2; ++row) {
1369 for (col_type col = c1; col <= c2; ++col) {
1372 data += asString(i1.asMathInset()->cell(i1.asMathInset()->index(row, col)));
1379 void MathCursor::eraseSelection(BufferView & bv)
1383 getSelection(bv, i1, i2);
1384 if (i1.idx_ == i2.idx_)
1385 i1.cell().erase(i1.pos_, i2.pos_);
1387 MathInset * p = i1.asMathInset();
1390 region(i1, i2, r1, r2, c1, c2);
1391 for (row_type row = r1; row <= r2; ++row)
1392 for (col_type col = c1; col <= c2; ++col)
1393 p->cell(p->index(row, col)).clear();
1399 string MathCursor::grabAndEraseSelection(BufferView & bv)
1403 string res = grabSelection(bv);
1410 CursorSlice MathCursor::normalAnchor(BufferView & bv) const
1414 if (Anchor_.size() < depth()) {
1416 lyxerr << "unusual Anchor size" << endl;
1418 //lyx::BOOST_ASSERT(Anchor_.size() >= cursor.depth());
1419 // use Anchor on the same level as Cursor
1420 CursorSlice normal = Anchor_[depth() - 1];
1421 if (depth() < Anchor_.size() && !(normal < cursor())) {
1422 // anchor is behind cursor -> move anchor behind the inset
1432 DispatchResult MathCursor::dispatch(BufferView &, FuncRequest const & cmd)
1434 // mouse clicks are somewhat special
1436 switch (cmd.action) {
1437 case LFUN_MOUSE_PRESS:
1438 case LFUN_MOUSE_MOTION:
1439 case LFUN_MOUSE_RELEASE:
1440 case LFUN_MOUSE_DOUBLE: {
1442 CursorSlice & pos = Cursor_.back();
1446 if (x < cmd.x && hasPrevAtom()) {
1447 DispatchResult const res =
1448 prevAtom().nucleus()->dispatch(bv, cmd);
1449 if (res.dispatched())
1452 if (x > cmd.x && hasNextAtom()) {
1453 DispatchResult const res =
1454 nextAtom().nucleus()->dispatch(bv, cmd);
1455 if (res.dispatched())
1465 for (int i = Cursor_.size() - 1; i >= 0; --i) {
1466 CursorBase tmp = bv->Cursor_;
1467 CursorSlice & pos = tmp.back()
1468 DispatchResult const res = pos.asMathInset()->dispatch(bv, cmd);
1469 if (res.dispatched()) {
1470 if (res.val() == FINISHED) {
1471 if (i + 1 < Cursor_.size())
1472 Cursor_.erase(Cursor_.begin() + i + 1, Cursor_.end());
1479 return DispatchResult(false);
1483 MathInset::mode_type MathCursor::currentMode(BufferView &) const
1486 for (int i = Cursor_.size() - 1; i >= 0; --i) {
1487 MathInset::mode_type res = Cursor_[i].asMathInset()->currentMode();
1488 if (res != MathInset::UNDECIDED_MODE)
1492 return MathInset::UNDECIDED_MODE;
1496 void MathCursor::handleFont(BufferView & bv, string const & font)
1498 CursorSlice cur = cursorTip(bv);
1502 safe = grabAndEraseSelection(bv);
1505 if (cur.lastpos() != 0) {
1506 // something left in the cell
1507 if (cur.pos() == 0) {
1508 // cursor in first position
1510 } else if (cur.pos() == cur.lastpos()) {
1511 // cursor in last position
1514 // cursor in between. split cell
1515 MathArray::iterator bt = cur.cell().begin();
1516 MathAtom at = createMathInset(font);
1517 at.nucleus()->cell(0) = MathArray(bt, bt + cur.pos());
1518 cur.cell().erase(bt, bt + cur.pos());
1520 plainInsert(bv, at);
1523 // nothing left in the cell
1531 void releaseMathCursor(BufferView & bv)
1534 InsetFormulaBase * f = mathcursor->formula();