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
7 * \author Alfredo Braunstein
10 * Full author contact details are available in file CREDITS.
16 #include "BufferView.h"
19 #include "dispatchresult.h"
20 #include "funcrequest.h"
21 #include "iterators.h"
23 #include "lyxfunc.h" // only for setMessage()
27 #include "paragraph.h"
29 #include "insets/updatableinset.h"
30 #include "insets/insettabular.h"
31 #include "insets/insettext.h"
33 #include "mathed/math_data.h"
34 #include "mathed/math_hullinset.h"
35 #include "mathed/math_support.h"
37 #include "support/limited_stack.h"
38 #include "frontends/LyXView.h"
40 #include <boost/assert.hpp>
45 #ifndef CXX_GLOBAL_CSTD
54 limited_stack<string> theCutBuffer;
57 LCursor::LCursor(BufferView & bv)
58 : cursor_(1), anchor_(1), bv_(&bv), current_(0),
59 cached_y_(0), x_target_(-1),
60 selection_(false), mark_(false)
68 cursor_.push_back(CursorSlice());
69 anchor_.push_back(CursorSlice());
78 DispatchResult LCursor::dispatch(FuncRequest const & cmd0)
80 //lyxerr << "\nLCursor::dispatch: cmd: " << cmd0 << endl << *this << endl;
81 FuncRequest cmd = cmd0;
83 for (int i = cursor_.size() - 1; i >= 1; --i) {
85 CursorSlice const & citem = cursor_[i];
86 lyxerr << "trying to dispatch to inset " << citem.inset_ << endl;
87 DispatchResult res = inset()->dispatch(*this, cmd);
88 if (res.dispatched()) {
89 lyxerr << " successfully dispatched to inset " << citem.inset_ << endl;
90 return DispatchResult(true, true);
92 // remove one level of cursor
95 cmd = FuncRequest(LFUN_FINISHED_LEFT);
98 cmd = FuncRequest(LFUN_FINISHED_RIGHT);
101 cmd = FuncRequest(LFUN_FINISHED_UP);
104 cmd = FuncRequest(LFUN_FINISHED_DOWN);
107 lyxerr << "not handled on level " << i << " val: " << res.val() << endl;
112 lyxerr << "trying to dispatch to main text " << bv_->text()
113 << " with cursor: " << *this << endl;
114 DispatchResult res = bv_->text()->dispatch(*this, cmd);
115 lyxerr << " result: " << res.val() << endl;
120 void LCursor::push(InsetBase * inset)
122 lyxerr << "LCursor::push() inset: " << inset << endl;
123 cursor_.push_back(CursorSlice(inset));
124 anchor_.push_back(CursorSlice(inset));
130 void LCursor::pop(int depth)
132 while (int(cursor_.size()) > depth + 1)
134 lyxerr << "LCursor::pop() result: " << *this << endl;
140 BOOST_ASSERT(cursor_.size() >= 1);
143 current_ = cursor_.size() - 1;
147 void LCursor::pushLeft(InsetBase * p)
149 BOOST_ASSERT(!cursor_.empty());
150 //lyxerr << "Entering inset " << t << " left" << endl;
156 bool LCursor::popLeft()
158 BOOST_ASSERT(!cursor_.empty());
159 //lyxerr << "Leaving inset to the left" << endl;
162 inset()->notifyCursorLeaves(idx());
165 inset()->notifyCursorLeaves(idx());
171 bool LCursor::popRight()
173 BOOST_ASSERT(!cursor_.empty());
174 //lyxerr << "Leaving inset to the right" << endl;
177 inset()->notifyCursorLeaves(idx());
180 inset()->notifyCursorLeaves(idx());
187 CursorSlice & LCursor::current()
189 BOOST_ASSERT(!cursor_.empty());
190 //lyxerr << "accessing cursor slice " << current_
191 // << ": " << cursor_[current_] << endl;
192 return cursor_[current_];
196 CursorSlice const & LCursor::current() const
198 //lyxerr << "accessing cursor slice " << current_
199 // << ": " << cursor_[current_] << endl;
200 return cursor_[current_];
204 int LCursor::currentMode()
206 BOOST_ASSERT(!cursor_.empty());
207 for (int i = cursor_.size() - 1; i >= 1; --i) {
208 int res = cursor_[i].inset()->currentMode();
209 if (res != MathInset::UNDECIDED_MODE)
212 return MathInset::TEXT_MODE;
216 LyXText * LCursor::innerText() const
218 BOOST_ASSERT(!cursor_.empty());
219 //lyxerr << "LCursor::innerText() depth: " << cursor_.size() << endl;
220 if (cursor_.size() > 1) {
221 // go up until first non-0 text is hit
222 // (innermost text is 0 in mathed)
223 for (int i = cursor_.size() - 1; i >= 1; --i)
224 if (cursor_[i].text())
225 return cursor_[i].text();
231 CursorSlice const & LCursor::innerTextSlice() const
233 BOOST_ASSERT(!cursor_.empty());
234 //lyxerr << "LCursor::innerTextSlice() depth: " << cursor_.size() << endl;
235 if (cursor_.size() > 1) {
236 // go up until first non-0 text is hit
237 // (innermost text is 0 in mathed)
238 for (int i = cursor_.size() - 1; i >= 1; --i)
239 if (cursor_[i].text())
246 void LCursor::updatePos()
248 BOOST_ASSERT(!cursor_.empty());
249 if (cursor_.size() > 1)
250 cached_y_ = bv_->top_y() + cursor_.back().inset()->yo();
251 //cached_y_ = cursor_.back().inset()->yo();
255 void LCursor::getDim(int & asc, int & des) const
257 BOOST_ASSERT(!cursor_.empty());
258 LyXText * text = innerText();
259 #warning crashes with text-in-math
261 RowList::iterator const rit = text->cursorRow();
262 if (rit != text->endRow()) {
263 asc = rit->baseline();
264 des = rit->height() - asc;
272 //innerInset()->getCursorDim(asc, des);
277 void LCursor::getPos(int & x, int & y) const
279 BOOST_ASSERT(!cursor_.empty());
282 if (cursor_.size() <= 1) {
283 x = bv_->text()->cursorX(cursor_.front());
284 y = bv_->text()->cursorY(cursor_.front());
286 inset()->getCursorPos(cursor_.back(), x, y);
287 // getCursorPos gives _screen_ coordinates. We need to add
288 // top_y to get document coordinates. This is hidden in cached_y_.
289 //y += cached_y_ - inset()->yo();
290 // The rest is non-obvious. The reason we have to have these
291 // extra computation is that the getCursorPos() calls rely
292 // on the inset's own knowledge of its screen position.
293 // If we scroll up or down in a big enough increment,
294 // inset->draw() is not called: this doesn't update
295 // inset.yo_, so getCursor() returns an old value.
298 //lyxerr << "#### LCursor::getPos: " << *this
299 // << " x: " << x << " y: " << y << endl;
303 void LCursor::paste(string const & data)
305 dispatch(FuncRequest(LFUN_PASTE, data));
309 InsetBase * LCursor::innerInsetOfType(int code) const
311 for (int i = cursor_.size() - 1; i >= 1; --i)
312 if (cursor_[i].inset_->lyxCode() == code)
313 return cursor_[i].inset_;
318 InsetTabular * LCursor::innerInsetTabular() const
320 return static_cast<InsetTabular *>(innerInsetOfType(InsetBase::TABULAR_CODE));
324 void LCursor::resetAnchor()
330 BufferView & LCursor::bv() const
336 MathAtom const & LCursor::prevAtom() const
338 BOOST_ASSERT(pos() > 0);
339 return cell()[pos() - 1];
343 MathAtom & LCursor::prevAtom()
345 BOOST_ASSERT(pos() > 0);
346 return cell()[pos() - 1];
350 MathAtom const & LCursor::nextAtom() const
352 BOOST_ASSERT(pos() < lastpos());
353 return cell()[pos()];
357 MathAtom & LCursor::nextAtom()
359 BOOST_ASSERT(pos() < lastpos());
360 return cell()[pos()];
364 bool LCursor::posLeft()
373 bool LCursor::posRight()
375 if (pos() == lastpos())
382 CursorSlice & LCursor::anchor()
384 return anchor_.back();
388 CursorSlice const & LCursor::anchor() const
390 return anchor_.back();
394 CursorSlice const & LCursor::selBegin() const
397 return cursor_.back();
398 // can't use std::min as this creates a new object
399 return anchor() < cursor_.back() ? anchor() : cursor_.back();
403 CursorSlice & LCursor::selBegin()
406 return cursor_.back();
407 return anchor() < cursor_.back() ? anchor() : cursor_.back();
411 CursorSlice const & LCursor::selEnd() const
414 return cursor_.back();
415 return anchor() > cursor_.back() ? anchor() : cursor_.back();
419 CursorSlice & LCursor::selEnd()
422 return cursor_.back();
423 return anchor() > cursor_.back() ? anchor() : cursor_.back();
427 void LCursor::setSelection()
430 // a selection with no contents is not a selection
431 if (par() == anchor().par() && pos() == anchor().pos())
436 void LCursor::setSelection(CursorBase const & where, size_t n)
445 void LCursor::clearSelection()
454 int & LCursor::x_target()
460 int LCursor::x_target() const
466 void LCursor::clearTargetX()
472 LyXText * LCursor::text() const
474 return current_ ? current().text() : bv_->text();
478 Paragraph & LCursor::paragraph()
480 BOOST_ASSERT(!inMathed());
481 return current_ ? current().paragraph() : *bv_->text()->getPar(par());
485 Paragraph const & LCursor::paragraph() const
487 BOOST_ASSERT(!inMathed());
488 return current_ ? current().paragraph() : *bv_->text()->getPar(par());
492 LCursor::par_type LCursor::lastpar() const
494 return inMathed() ? 0 : text()->paragraphs().size() - 1;
498 LCursor::pos_type LCursor::lastpos() const
500 InsetBase * inset = current().inset();
501 return inset && inset->asMathInset() ? cell().size() : paragraph().size();
505 LCursor::row_type LCursor::crow() const
507 return paragraph().row(pos());
511 LCursor::row_type LCursor::lastcrow() const
513 return paragraph().rows.size();
517 size_t LCursor::nargs() const
519 // assume 1x1 grid for 'plain text'
520 return current_ ? current().nargs() : 1;
524 size_t LCursor::ncols() const
526 // assume 1x1 grid for 'plain text'
527 return current_ ? current().ncols() : 1;
531 size_t LCursor::nrows() const
533 // assume 1x1 grid for 'plain text'
534 return current_ ? current().nrows() : 1;
538 LCursor::row_type LCursor::row() const
540 BOOST_ASSERT(current_ > 0);
541 return current().row();
545 LCursor::col_type LCursor::col() const
547 BOOST_ASSERT(current_ > 0);
548 return current().col();
552 MathArray const & LCursor::cell() const
554 BOOST_ASSERT(current_ > 0);
555 return current().cell();
559 MathArray & LCursor::cell()
561 BOOST_ASSERT(current_ > 0);
562 return current().cell();
566 void LCursor::info(std::ostream & os)
568 for (int i = 1, n = depth(); i < n; ++i) {
569 cursor_[i].inset()->infoize(os);
573 prevInset()->infoize2(os);
574 // overwite old message
581 void region(CursorSlice const & i1, CursorSlice const & i2,
582 LCursor::row_type & r1, LCursor::row_type & r2,
583 LCursor::col_type & c1, LCursor::col_type & c2)
585 InsetBase * p = i1.inset();
586 c1 = p->col(i1.idx_);
587 c2 = p->col(i2.idx_);
590 r1 = p->row(i1.idx_);
591 r2 = p->row(i2.idx_);
599 string LCursor::grabSelection()
604 CursorSlice i1 = selBegin();
605 CursorSlice i2 = selEnd();
607 if (i1.idx_ == i2.idx_) {
608 if (i1.inset()->asMathInset()) {
609 MathArray::const_iterator it = i1.cell().begin();
610 return asString(MathArray(it + i1.pos_, it + i2.pos_));
612 return "unknown selection 1";
618 region(i1, i2, r1, r2, c1, c2);
621 if (i1.inset()->asMathInset()) {
622 for (row_type row = r1; row <= r2; ++row) {
625 for (col_type col = c1; col <= c2; ++col) {
628 data += asString(i1.asMathInset()->cell(i1.asMathInset()->index(row, col)));
632 data = "unknown selection 2";
638 void LCursor::eraseSelection()
640 CursorSlice i1 = selBegin();
641 CursorSlice i2 = selEnd();
643 if (i1.inset()->asMathInset()) {
644 if (i1.idx_ == i2.idx_) {
645 i1.cell().erase(i1.pos_, i2.pos_);
647 MathInset * p = i1.asMathInset();
650 region(i1, i2, r1, r2, c1, c2);
651 for (row_type row = r1; row <= r2; ++row)
652 for (col_type col = c1; col <= c2; ++col)
653 p->cell(p->index(row, col)).clear();
657 lyxerr << "can't erase this selection 1" << endl;
662 string LCursor::grabAndEraseSelection()
666 string res = grabSelection();
673 void LCursor::selClear()
680 void LCursor::selCopy()
683 theCutBuffer.push(grabSelection());
686 //theCutBuffer.erase();
691 void LCursor::selCut()
693 theCutBuffer.push(grabAndEraseSelection());
697 void LCursor::selDel()
706 void LCursor::selPaste(size_t n)
709 if (n < theCutBuffer.size())
710 paste(theCutBuffer[n]);
716 void LCursor::selHandle(bool sel)
718 if (sel == selection())
725 void LCursor::selStart()
732 void LCursor::selClearOrDel()
734 if (lyxrc.auto_region_delete)
741 std::ostream & operator<<(std::ostream & os, LCursor const & cur)
744 for (size_t i = 0, n = cur.cursor_.size(); i != n; ++i)
745 os << " (" << cur.cursor_[i] << " | " << cur.anchor_[i] << "\n";
746 return os << "current: " << cur.current_ << endl;
757 void increment(CursorBase & it)
759 CursorSlice & top = it.back();
760 MathArray & ar = top.asMathInset()->cell(top.idx_);
762 // move into the current inset if possible
763 // it is impossible for pos() == size()!
765 if (top.pos() != top.lastpos())
766 n = (ar.begin() + top.pos_)->nucleus();
767 if (n && n->isActive()) {
768 it.push_back(CursorSlice(n));
772 // otherwise move on one cell back if possible
773 if (top.pos() < top.lastpos()) {
774 // pos() == lastpos() is valid!
779 // otherwise try to move on one cell if possible
780 while (top.idx() < top.lastidx()) {
782 if (top.asMathInset()->validCell(top.idx_)) {
788 // otherwise leave array, move on one back
789 // this might yield pos() == size(), but that's a ok.
791 // it certainly invalidates top
796 CursorBase ibegin(InsetBase * p)
799 it.push_back(CursorSlice(p));
804 CursorBase iend(InsetBase * p)
807 it.push_back(CursorSlice(p));
808 CursorSlice & cur = it.back();
809 cur.idx() = cur.lastidx();
810 cur.pos() = cur.lastpos();
817 ///////////////////////////////////////////////////////////////////
819 // The part below is the non-integrated rest of the original math
820 // cursor. This should be either generalized for texted or moved
821 // back to the math insets.
823 ///////////////////////////////////////////////////////////////////
825 #include "mathed/math_braceinset.h"
826 #include "mathed/math_charinset.h"
827 #include "mathed/math_commentinset.h"
828 #include "mathed/math_factory.h"
829 #include "mathed/math_gridinset.h"
830 #include "mathed/math_macroarg.h"
831 #include "mathed/math_macrotemplate.h"
832 #include "mathed/math_mathmlstream.h"
833 #include "mathed/math_scriptinset.h"
834 #include "mathed/math_spaceinset.h"
835 #include "mathed/math_support.h"
836 #include "mathed/math_unknowninset.h"
838 //#define FILEDEBUG 1
841 bool LCursor::isInside(InsetBase const * p)
843 for (unsigned i = 0; i < depth(); ++i)
844 if (cursor_[i].inset() == p)
850 bool LCursor::openable(MathAtom const & t)
861 // we can't move into anything new during selection
862 if (depth() == anchor_.size())
864 if (t.nucleus() != anchor_[depth()].inset())
871 bool LCursor::inNucleus()
873 return inset()->asMathInset()->asScriptInset() && idx() == 2;
879 autocorrect() = false;
886 if (pos() != 0 && openable(prevAtom())) {
888 push(nextAtom().nucleus());
889 inset()->idxLast(*this);
893 return posLeft() || idxLeft() || popLeft() || selection();
897 bool LCursor::right()
899 autocorrect() = false;
906 if (pos() != lastpos() && openable(nextAtom())) {
907 pushLeft(nextAtom().nucleus());
908 inset()->idxFirst(*this);
912 return posRight() || idxRight() || popRight() || selection();
916 bool positionable(CursorBase const & cursor, CursorBase const & anchor)
918 // avoid deeper nested insets when selecting
919 if (cursor.size() > anchor.size())
922 // anchor might be deeper, should have same path then
923 for (size_t i = 0; i < cursor.size(); ++i)
924 if (cursor[i].inset() != anchor[i].inset())
927 // position should be ok.
932 void LCursor::setScreenPos(int x, int y)
934 bool res = bruteFind(x, y, formula()->xlow(), formula()->xhigh(),
935 formula()->ylow(), formula()->yhigh());
937 // this can happen on creation of "math-display"
948 autocorrect() = false;
950 if (!inset()->idxHome(*this))
959 autocorrect() = false;
961 if (!inset()->idxEnd(*this))
968 void LCursor::plainErase()
974 void LCursor::markInsert()
976 cell().insert(pos(), MathAtom(new MathCharInset(0)));
980 void LCursor::markErase()
986 void LCursor::plainInsert(MathAtom const & t)
988 cell().insert(pos(), t);
993 void LCursor::insert2(string const & str)
1001 void LCursor::insert(string const & str)
1003 lyxerr << "inserting '" << str << "'" << endl;
1005 for (string::const_iterator it = str.begin(); it != str.end(); ++it)
1006 plainInsert(MathAtom(new MathCharInset(*it)));
1010 void LCursor::insert(char c)
1012 lyxerr << "inserting '" << c << "'" << endl;
1014 plainInsert(MathAtom(new MathCharInset(c)));
1018 void LCursor::insert(MathAtom const & t)
1026 void LCursor::niceInsert(string const & t)
1028 lyxerr << "*** LCursor::niceInsert 1: " << t << endl;
1038 void LCursor::niceInsert(MathAtom const & t)
1040 lyxerr << "*** LCursor::niceInsert 2: " << t << endl;
1042 string safe = grabAndEraseSelection();
1044 // enter the new inset and move the contents of the selection if possible
1045 if (t->isActive()) {
1047 pushLeft(nextAtom().nucleus());
1050 lyxerr << "*** LCursor::niceInsert 3: " << t << endl;
1054 void LCursor::insert(MathArray const & ar)
1059 cell().insert(pos(), ar);
1064 bool LCursor::backspace()
1066 autocorrect() = false;
1074 if (inset()->nargs() == 1 && depth() == 1 && lastpos() == 0)
1080 if (inMacroMode()) {
1081 MathUnknownInset * p = activeMacro();
1082 if (p->name().size() > 1) {
1083 p->setName(p->name().substr(0, p->name().size() - 1));
1088 if (pos() != 0 && prevAtom()->nargs() > 0) {
1089 // let's require two backspaces for 'big stuff' and
1090 // highlight on the first
1101 bool LCursor::erase()
1103 autocorrect() = false;
1112 // delete empty cells if possible
1113 if (pos() == lastpos() && inset()->idxDelete(idx()))
1116 // special behaviour when in last position of cell
1117 if (pos() == lastpos()) {
1118 bool one_cell = inset()->nargs() == 1;
1119 if (one_cell && depth() == 1 && lastpos() == 0)
1125 inset()->idxGlue(idx());
1129 if (pos() != lastpos() && inset()->nargs() > 0) {
1143 CursorBase save = cursor_;
1147 autocorrect() = false;
1152 bool LCursor::down()
1155 CursorBase save = cursor_;
1156 if (goUpDown(false))
1159 autocorrect() = false;
1164 void LCursor::macroModeClose()
1168 MathUnknownInset * p = activeMacro();
1170 string s = p->name();
1172 cell().erase(pos());
1174 // do nothing if the macro name is empty
1178 string const name = s.substr(1);
1180 // prevent entering of recursive macros
1181 if (formula()->lyxCode() == InsetOld::MATHMACRO_CODE
1182 && formula()->getInsetName() == name)
1183 lyxerr << "can't enter recursive macro" << endl;
1185 niceInsert(createMathInset(name));
1189 string LCursor::macroName()
1191 return inMacroMode() ? activeMacro()->name() : string();
1195 void LCursor::handleNest(MathAtom const & a, int c)
1198 asArray(grabAndEraseSelection(), t.nucleus()->cell(c));
1201 pushLeft(t.nucleus());
1205 int LCursor::targetX() const
1207 if (x_target() != -1)
1216 MathHullInset * LCursor::formula() const
1218 for (int i = cursor_.size() - 1; i >= 1; --i)
1219 if (cursor_[i].inset()->lyxCode() == InsetBase::MATH_CODE)
1220 return static_cast<MathHullInset *>(cursor_[i].inset());
1225 void LCursor::adjust(pos_type from, int diff)
1229 if (anchor().pos_ > from)
1230 anchor().pos_ += diff;
1231 // just to be on the safe side
1232 // theoretically unecessary
1237 bool LCursor::inMacroMode() const
1241 MathUnknownInset const * p = prevAtom()->asUnknownInset();
1242 return p && !p->final();
1246 MathUnknownInset * LCursor::activeMacro()
1248 return inMacroMode() ? prevAtom().nucleus()->asUnknownInset() : 0;
1252 bool LCursor::inMacroArgMode() const
1254 return pos() > 0 && prevAtom()->getChar() == '#';
1258 MathGridInset * LCursor::enclosingGrid(idx_type & idx) const
1260 for (MathInset::difference_type i = depth() - 1; i >= 0; --i) {
1261 MathInset * m = cursor_[i].inset()->asMathInset();
1264 MathGridInset * p = m->asGridInset();
1266 idx = cursor_[i].idx_;
1274 void LCursor::pullArg()
1278 MathArray ar = cell();
1281 cell().insert(pos(), ar);
1284 formula()->mutateToText();
1290 void LCursor::touch()
1294 CursorBase::const_iterator it = cursor_.begin();
1295 CursorBase::const_iterator et = cursor_.end();
1296 for ( ; it != et; ++it)
1302 void LCursor::normalize()
1304 if (idx() >= nargs()) {
1305 lyxerr << "this should not really happen - 1: "
1306 << idx() << ' ' << nargs()
1307 << " in: " << inset() << endl;
1309 idx() = min(idx(), lastidx());
1311 if (pos() > lastpos()) {
1312 lyxerr << "this should not really happen - 2: "
1313 << pos() << ' ' << lastpos() << " in idx: " << idx()
1315 WriteStream wi(lyxerr, false, true);
1316 inset()->asMathInset()->write(wi);
1319 pos() = min(pos(), lastpos());
1323 char LCursor::valign()
1326 MathGridInset * p = enclosingGrid(idx);
1327 return p ? p->valign() : '\0';
1331 char LCursor::halign()
1334 MathGridInset * p = enclosingGrid(idx);
1335 return p ? p->halign(idx % p->ncols()) : '\0';
1339 bool LCursor::goUpDown(bool up)
1341 // Be warned: The 'logic' implemented in this function is highly fragile.
1342 // A distance of one pixel or a '<' vs '<=' _really_ matters.
1343 // So fiddle around with it only if you know what you are doing!
1348 // check if we had something else in mind, if not, this is the future goal
1349 if (x_target() == -1)
1354 // try neigbouring script insets
1358 MathScriptInset const * p = prevAtom()->asScriptInset();
1359 if (p && p->has(up)) {
1362 idx() = up; // the superscript has index 1
1364 //lyxerr << "updown: handled by scriptinset to the left" << endl;
1370 if (pos() != lastpos()) {
1371 MathScriptInset const * p = nextAtom()->asScriptInset();
1372 if (p && p->has(up)) {
1376 //lyxerr << "updown: handled by scriptinset to the right" << endl;
1382 // try current cell for e.g. text insets
1383 if (inset()->idxUpDown2(*this, up))
1386 //xarray().boundingBox(xlow, xhigh, ylow, yhigh);
1391 //if (bruteFind(xo, yo, xlow, xhigh, ylow, yhigh)) {
1392 // lyxerr << "updown: handled by brute find in the same cell" << endl;
1396 // try to find an inset that knows better then we
1398 //lyxerr << "updown: We are in " << inset() << " idx: " << idx() << endl;
1400 if (inset()->idxUpDown(*this, up)) {
1401 // try to find best position within this inset
1407 // no such inset found, just take something "above"
1408 //lyxerr << "updown: handled by strange case" << endl;
1414 up ? formula()->ylow() : yo + 4,
1415 up ? yo - 4 : formula()->yhigh()
1419 // any improvement so far?
1422 if (up ? ynew < yo : ynew > yo)
1428 bool LCursor::bruteFind(int x, int y, int xlow, int xhigh, int ylow, int yhigh)
1430 CursorBase best_cursor;
1431 double best_dist = 1e10;
1433 CursorBase it = ibegin(formula());
1434 CursorBase et = iend(formula());
1436 // avoid invalid nesting when selecting
1437 if (!selection() || positionable(it, anchor_)) {
1439 CursorSlice & cur = it.back();
1440 cur.inset()->getCursorPos(cur, xo, yo);
1441 if (xlow <= xo && xo <= xhigh && ylow <= yo && yo <= yhigh) {
1442 double d = (x - xo) * (x - xo) + (y - yo) * (y - yo);
1443 //lyxerr << "x: " << x << " y: " << y << " d: " << endl;
1444 // '<=' in order to take the last possible position
1445 // this is important for clicking behind \sum in e.g. '\sum_i a'
1446 if (d <= best_dist) {
1458 if (best_dist < 1e10)
1459 cursor_ = best_cursor;
1460 return best_dist < 1e10;
1464 void LCursor::bruteFind2(int x, int y)
1466 double best_dist = 1e10;
1468 CursorBase it = cursor_;
1470 CursorBase et = cursor_;
1471 int n = et.back().asMathInset()->cell(et.back().idx_).size();
1473 for (int i = 0; ; ++i) {
1475 CursorSlice & cur = it.back();
1476 cur.inset()->getCursorPos(cur, xo, yo);
1477 double d = (x - xo) * (x - xo) + (y - yo) * (y - yo);
1478 // '<=' in order to take the last possible position
1479 // this is important for clicking behind \sum in e.g. '\sum_i a'
1480 lyxerr << "i: " << i << " d: " << d << " best: " << best_dist << endl;
1481 if (d <= best_dist) {
1492 bool LCursor::idxLineLast()
1494 idx() -= idx() % ncols();
1495 idx() += ncols() - 1;
1501 bool LCursor::idxLeft()
1503 return inset()->idxLeft(*this);
1507 bool LCursor::idxRight()
1509 return inset()->idxRight(*this);
1513 bool LCursor::script(bool up)
1515 // Hack to get \\^ and \\_ working
1516 lyxerr << "handling script: up: " << up << endl;
1517 if (inMacroMode() && macroName() == "\\") {
1519 niceInsert(createMathInset("mathcircumflex"));
1526 string safe = grabAndEraseSelection();
1528 // we are in a nucleus of a script inset, move to _our_ script
1529 inset()->asMathInset()->asScriptInset()->ensure(up);
1532 } else if (pos() != 0 && prevAtom()->asScriptInset()) {
1534 nextAtom().nucleus()->asScriptInset()->ensure(up);
1538 } else if (pos() != 0) {
1540 cell()[pos()] = MathAtom(new MathScriptInset(nextAtom(), up));
1545 plainInsert(MathAtom(new MathScriptInset(up)));
1547 nextAtom().nucleus()->asScriptInset()->ensure(up);
1557 bool LCursor::interpret(char c)
1559 //lyxerr << "interpret 2: '" << c << "'" << endl;
1561 if (inMacroArgMode()) {
1567 MathMacroTemplate const * p = formula()->asMacroTemplate();
1568 if (p && 1 <= n && n <= p->numargs())
1569 insert(MathAtom(new MathMacroArgument(c - '0')));
1571 insert(createMathInset("#"));
1572 interpret(c); // try again
1579 if (inMacroMode()) {
1580 string name = macroName();
1581 //lyxerr << "interpret name: '" << name << "'" << endl;
1584 activeMacro()->setName(activeMacro()->name() + c);
1588 // handle 'special char' macros
1593 if (currentMode() == MathInset::TEXT_MODE)
1594 niceInsert(createMathInset("textbackslash"));
1596 niceInsert(createMathInset("backslash"));
1597 } else if (c == '{') {
1598 niceInsert(MathAtom(new MathBraceInset));
1600 niceInsert(createMathInset(string(1, c)));
1605 // leave macro mode and try again if necessary
1608 niceInsert(MathAtom(new MathBraceInset));
1614 // This is annoying as one has to press <space> far too often.
1618 // leave autocorrect mode if necessary
1619 if (autocorrect() && c == ' ') {
1620 autocorrect() = false;
1625 // just clear selection on pressing the space bar
1626 if (selection() && c == ' ') {
1627 selection() = false;
1634 //lyxerr << "starting with macro" << endl;
1635 insert(MathAtom(new MathUnknownInset("\\", false)));
1640 if (currentMode() == MathInset::TEXT_MODE)
1646 if (currentMode() == MathInset::TEXT_MODE) {
1647 // insert spaces in text mode,
1648 // but suppress direct insertion of two spaces in a row
1649 // the still allows typing '<space>a<space>' and deleting the 'a', but
1650 // it is better than nothing...
1651 if (!pos() != 0 || prevAtom()->getChar() != ' ')
1655 if (pos() != 0 && prevAtom()->asSpaceInset()) {
1656 prevAtom().nucleus()->asSpaceInset()->incSpace();
1661 // if are at the very end, leave the formula
1662 return pos() != lastpos();
1675 if (c == '{' || c == '}' || c == '#' || c == '&' || c == '$') {
1676 niceInsert(createMathInset(string(1, c)));
1681 niceInsert(MathAtom(new MathCommentInset));
1685 // try auto-correction
1686 //if (autocorrect() && hasPrevAtom() && math_autocorrect(prevAtom(), c))
1689 // no special circumstances, so insert the character without any fuss
1691 autocorrect() = true;
1696 void LCursor::lockToggle()
1698 if (pos() != lastpos()) {
1699 // toggle previous inset ...
1700 nextAtom().nucleus()->lock(!nextAtom()->lock());
1701 } else if (popLeft() && pos() != lastpos()) {
1702 // ... or enclosing inset if we are in the last inset position
1703 nextAtom().nucleus()->lock(!nextAtom()->lock());
1709 CursorSlice LCursor::normalAnchor()
1711 if (anchor_.size() < depth()) {
1713 lyxerr << "unusual Anchor size" << endl;
1715 //lyx::BOOST_ASSERT(Anchor_.size() >= cursor.depth());
1716 // use Anchor on the same level as Cursor
1717 CursorSlice normal = anchor_[current_];
1719 if (depth() < anchor_.size() && !(normal < xx())) {
1720 // anchor is behind cursor -> move anchor behind the inset
1729 DispatchResult dispatch(LCursor & cur, FuncRequest const & cmd)
1731 // mouse clicks are somewhat special
1733 switch (cmd.action) {
1734 case LFUN_MOUSE_PRESS:
1735 case LFUN_MOUSE_MOTION:
1736 case LFUN_MOUSE_RELEASE:
1737 case LFUN_MOUSE_DOUBLE: {
1738 CursorSlice & pos = cursor_.back();
1742 if (x < cmd.x && pos() != 0) {
1743 DispatchResult const res = prevAtom().nucleus()->dispatch(cmd);
1744 if (res.dispatched())
1747 if (x > cmd.x && pos() != lastpos()) {
1748 DispatchResult const res = inset()->dispatch(cmd);
1749 if (res.dispatched())
1760 void LCursor::handleFont(string const & font)
1765 safe = grabAndEraseSelection();
1768 if (lastpos() != 0) {
1769 // something left in the cell
1771 // cursor in first position
1773 } else if (pos() == lastpos()) {
1774 // cursor in last position
1777 // cursor in between. split cell
1778 MathArray::iterator bt = cell().begin();
1779 MathAtom at = createMathInset(font);
1780 at.nucleus()->cell(0) = MathArray(bt, bt + pos());
1781 cell().erase(bt, bt + pos());
1786 // nothing left in the cell
1794 void LCursor::releaseMathCursor()
1797 formula()->insetUnlock(bv());
1801 bool LCursor::inMathed() const
1807 InsetBase * LCursor::nextInset()
1809 if (pos() == lastpos())
1812 return nextAtom().nucleus();
1813 Paragraph & par = paragraph();
1814 return par.isInset(pos()) ? par.getInset(pos()) : 0;
1818 InsetBase * LCursor::prevInset()
1823 return prevAtom().nucleus();
1824 Paragraph & par = paragraph();
1825 return par.isInset(pos() - 1) ? par.getInset(pos() - 1) : 0;
1829 void LCursor::message(string const & msg) const
1831 bv().owner()->getLyXFunc().setMessage(msg);
1835 void LCursor::errorMessage(string const & msg) const
1837 bv().owner()->getLyXFunc().setErrorMessage(msg);