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;
82 for (current_ = cursor_.size() - 1; current_ >= 1; --current_) {
83 lyxerr << "trying to dispatch to inset " << inset() << endl;
84 DispatchResult res = inset()->dispatch(*this, cmd);
85 if (res.dispatched()) {
86 lyxerr << " successfully dispatched to inset " << inset() << endl;
87 return DispatchResult(true, true);
89 // "Mutate" the request for semi-handled requests that need
90 // additional handling in outer levels.
93 cmd = FuncRequest(LFUN_FINISHED_LEFT);
96 cmd = FuncRequest(LFUN_FINISHED_RIGHT);
99 cmd = FuncRequest(LFUN_FINISHED_UP);
102 cmd = FuncRequest(LFUN_FINISHED_DOWN);
105 //lyxerr << "not handled on level " << current_
106 // << " val: " << res.val() << endl;
110 BOOST_ASSERT(current_ == 0);
111 lyxerr << "trying to dispatch to main text " << bv_->text()
112 << " with cursor: " << *this << endl;
113 DispatchResult res = bv_->text()->dispatch(*this, cmd);
114 //lyxerr << " result: " << res.val() << endl;
119 void LCursor::push(InsetBase * inset)
121 lyxerr << "LCursor::push() inset: " << inset << endl;
122 cursor_.push_back(CursorSlice(inset));
123 anchor_.push_back(CursorSlice(inset));
129 void LCursor::pop(int depth)
131 while (int(cursor_.size()) > depth + 1)
133 lyxerr << "LCursor::pop() result: " << *this << endl;
139 BOOST_ASSERT(cursor_.size() >= 1);
142 current_ = cursor_.size() - 1;
146 void LCursor::pushLeft(InsetBase * p)
148 BOOST_ASSERT(!cursor_.empty());
149 //lyxerr << "Entering inset " << t << " left" << endl;
155 bool LCursor::popLeft()
157 BOOST_ASSERT(!cursor_.empty());
158 //lyxerr << "Leaving inset to the left" << endl;
161 inset()->notifyCursorLeaves(idx());
164 inset()->notifyCursorLeaves(idx());
170 bool LCursor::popRight()
172 BOOST_ASSERT(!cursor_.empty());
173 //lyxerr << "Leaving inset to the right" << endl;
176 inset()->notifyCursorLeaves(idx());
179 inset()->notifyCursorLeaves(idx());
186 CursorSlice & LCursor::current()
188 BOOST_ASSERT(!cursor_.empty());
189 //lyxerr << "accessing cursor slice " << current_
190 // << ": " << cursor_[current_] << endl;
191 return cursor_[current_];
195 CursorSlice const & LCursor::current() const
197 //lyxerr << "accessing cursor slice " << current_
198 // << ": " << cursor_[current_] << endl;
199 return cursor_[current_];
203 int LCursor::currentMode()
205 BOOST_ASSERT(!cursor_.empty());
206 for (int i = cursor_.size() - 1; i >= 1; --i) {
207 int res = cursor_[i].inset()->currentMode();
208 if (res != MathInset::UNDECIDED_MODE)
211 return MathInset::TEXT_MODE;
215 LyXText * LCursor::innerText() const
217 BOOST_ASSERT(!cursor_.empty());
218 //lyxerr << "LCursor::innerText() depth: " << cursor_.size() << endl;
219 if (cursor_.size() > 1) {
220 // go up until first non-0 text is hit
221 // (innermost text is 0 in mathed)
222 for (int i = cursor_.size() - 1; i >= 1; --i)
223 if (cursor_[i].text())
224 return cursor_[i].text();
230 CursorSlice const & LCursor::innerTextSlice() const
232 BOOST_ASSERT(!cursor_.empty());
233 //lyxerr << "LCursor::innerTextSlice() depth: " << cursor_.size() << endl;
234 if (cursor_.size() > 1) {
235 // go up until first non-0 text is hit
236 // (innermost text is 0 in mathed)
237 for (int i = cursor_.size() - 1; i >= 1; --i)
238 if (cursor_[i].text())
245 void LCursor::updatePos()
247 BOOST_ASSERT(!cursor_.empty());
248 if (cursor_.size() > 1)
249 cached_y_ = bv_->top_y() + cursor_.back().inset()->yo();
250 //cached_y_ = cursor_.back().inset()->yo();
254 void LCursor::getDim(int & asc, int & des) const
256 BOOST_ASSERT(!cursor_.empty());
257 LyXText * text = innerText();
258 #warning crashes with text-in-math
260 RowList::iterator const rit = text->cursorRow();
261 if (rit != text->endRow()) {
262 asc = rit->baseline();
263 des = rit->height() - asc;
271 //innerInset()->getCursorDim(asc, des);
276 void LCursor::getPos(int & x, int & y) const
278 BOOST_ASSERT(!cursor_.empty());
281 if (cursor_.size() <= 1) {
282 x = bv_->text()->cursorX(cursor_.front());
283 y = bv_->text()->cursorY(cursor_.front());
285 inset()->getCursorPos(cursor_.back(), x, y);
286 // getCursorPos gives _screen_ coordinates. We need to add
287 // top_y to get document coordinates. This is hidden in cached_y_.
288 //y += cached_y_ - inset()->yo();
289 // The rest is non-obvious. The reason we have to have these
290 // extra computation is that the getCursorPos() calls rely
291 // on the inset's own knowledge of its screen position.
292 // If we scroll up or down in a big enough increment,
293 // inset->draw() is not called: this doesn't update
294 // inset.yo_, so getCursor() returns an old value.
297 //lyxerr << "#### LCursor::getPos: " << *this
298 // << " x: " << x << " y: " << y << endl;
302 void LCursor::paste(string const & data)
304 dispatch(FuncRequest(LFUN_PASTE, data));
308 InsetBase * LCursor::innerInsetOfType(int code) const
310 for (int i = cursor_.size() - 1; i >= 1; --i)
311 if (cursor_[i].inset_->lyxCode() == code)
312 return cursor_[i].inset_;
317 InsetTabular * LCursor::innerInsetTabular() const
319 return static_cast<InsetTabular *>(innerInsetOfType(InsetBase::TABULAR_CODE));
323 void LCursor::resetAnchor()
329 BufferView & LCursor::bv() const
335 MathAtom const & LCursor::prevAtom() const
337 BOOST_ASSERT(pos() > 0);
338 return cell()[pos() - 1];
342 MathAtom & LCursor::prevAtom()
344 BOOST_ASSERT(pos() > 0);
345 return cell()[pos() - 1];
349 MathAtom const & LCursor::nextAtom() const
351 BOOST_ASSERT(pos() < lastpos());
352 return cell()[pos()];
356 MathAtom & LCursor::nextAtom()
358 BOOST_ASSERT(pos() < lastpos());
359 return cell()[pos()];
363 bool LCursor::posLeft()
372 bool LCursor::posRight()
374 if (pos() == lastpos())
381 CursorSlice & LCursor::anchor()
383 return anchor_.back();
387 CursorSlice const & LCursor::anchor() const
389 return anchor_.back();
393 CursorSlice const & LCursor::selBegin() const
396 return cursor_.back();
397 // can't use std::min as this creates a new object
398 return anchor() < cursor_.back() ? anchor() : cursor_.back();
402 CursorSlice & LCursor::selBegin()
405 return cursor_.back();
406 return anchor() < cursor_.back() ? anchor() : cursor_.back();
410 CursorSlice const & LCursor::selEnd() const
413 return cursor_.back();
414 return anchor() > cursor_.back() ? anchor() : cursor_.back();
418 CursorSlice & LCursor::selEnd()
421 return cursor_.back();
422 return anchor() > cursor_.back() ? anchor() : cursor_.back();
426 void LCursor::setSelection()
429 // a selection with no contents is not a selection
430 if (par() == anchor().par() && pos() == anchor().pos())
435 void LCursor::setSelection(CursorBase const & where, size_t n)
444 void LCursor::clearSelection()
453 int & LCursor::x_target()
459 int LCursor::x_target() const
465 void LCursor::clearTargetX()
471 LyXText * LCursor::text() const
473 return current_ ? current().text() : bv_->text();
477 Paragraph & LCursor::paragraph()
479 BOOST_ASSERT(!inMathed());
480 return current_ ? current().paragraph() : *bv_->text()->getPar(par());
484 Paragraph const & LCursor::paragraph() const
486 BOOST_ASSERT(!inMathed());
487 return current_ ? current().paragraph() : *bv_->text()->getPar(par());
491 LCursor::par_type LCursor::lastpar() const
493 return inMathed() ? 0 : text()->paragraphs().size() - 1;
497 LCursor::pos_type LCursor::lastpos() const
499 InsetBase * inset = current().inset();
500 return inset && inset->asMathInset() ? cell().size() : paragraph().size();
504 LCursor::row_type LCursor::crow() const
506 return paragraph().row(pos());
510 LCursor::row_type LCursor::lastcrow() const
512 return paragraph().rows.size();
516 size_t LCursor::nargs() const
518 // assume 1x1 grid for 'plain text'
519 return current_ ? current().nargs() : 1;
523 size_t LCursor::ncols() const
525 // assume 1x1 grid for 'plain text'
526 return current_ ? current().ncols() : 1;
530 size_t LCursor::nrows() const
532 // assume 1x1 grid for 'plain text'
533 return current_ ? current().nrows() : 1;
537 LCursor::row_type LCursor::row() const
539 BOOST_ASSERT(current_ > 0);
540 return current().row();
544 LCursor::col_type LCursor::col() const
546 BOOST_ASSERT(current_ > 0);
547 return current().col();
551 MathArray const & LCursor::cell() const
553 BOOST_ASSERT(current_ > 0);
554 return current().cell();
558 MathArray & LCursor::cell()
560 BOOST_ASSERT(current_ > 0);
561 return current().cell();
565 void LCursor::info(std::ostream & os)
567 for (int i = 1, n = depth(); i < n; ++i) {
568 cursor_[i].inset()->infoize(os);
572 prevInset()->infoize2(os);
573 // overwite old message
580 void region(CursorSlice const & i1, CursorSlice const & i2,
581 LCursor::row_type & r1, LCursor::row_type & r2,
582 LCursor::col_type & c1, LCursor::col_type & c2)
584 InsetBase * p = i1.inset();
585 c1 = p->col(i1.idx_);
586 c2 = p->col(i2.idx_);
589 r1 = p->row(i1.idx_);
590 r2 = p->row(i2.idx_);
598 string LCursor::grabSelection()
603 CursorSlice i1 = selBegin();
604 CursorSlice i2 = selEnd();
606 if (i1.idx_ == i2.idx_) {
607 if (i1.inset()->asMathInset()) {
608 MathArray::const_iterator it = i1.cell().begin();
609 return asString(MathArray(it + i1.pos_, it + i2.pos_));
611 return "unknown selection 1";
617 region(i1, i2, r1, r2, c1, c2);
620 if (i1.inset()->asMathInset()) {
621 for (row_type row = r1; row <= r2; ++row) {
624 for (col_type col = c1; col <= c2; ++col) {
627 data += asString(i1.asMathInset()->cell(i1.asMathInset()->index(row, col)));
631 data = "unknown selection 2";
637 void LCursor::eraseSelection()
639 CursorSlice i1 = selBegin();
640 CursorSlice i2 = selEnd();
642 if (i1.inset()->asMathInset()) {
643 if (i1.idx_ == i2.idx_) {
644 i1.cell().erase(i1.pos_, i2.pos_);
646 MathInset * p = i1.asMathInset();
649 region(i1, i2, r1, r2, c1, c2);
650 for (row_type row = r1; row <= r2; ++row)
651 for (col_type col = c1; col <= c2; ++col)
652 p->cell(p->index(row, col)).clear();
656 lyxerr << "can't erase this selection 1" << endl;
661 string LCursor::grabAndEraseSelection()
665 string res = grabSelection();
672 void LCursor::selClear()
679 void LCursor::selCopy()
682 theCutBuffer.push(grabSelection());
685 //theCutBuffer.erase();
690 void LCursor::selCut()
692 theCutBuffer.push(grabAndEraseSelection());
696 void LCursor::selDel()
705 void LCursor::selPaste(size_t n)
708 if (n < theCutBuffer.size())
709 paste(theCutBuffer[n]);
715 void LCursor::selHandle(bool sel)
717 if (sel == selection())
724 void LCursor::selStart()
731 void LCursor::selClearOrDel()
733 if (lyxrc.auto_region_delete)
740 std::ostream & operator<<(std::ostream & os, LCursor const & cur)
743 for (size_t i = 0, n = cur.cursor_.size(); i != n; ++i)
744 os << " (" << cur.cursor_[i] << " | " << cur.anchor_[i] << "\n";
745 return os << "current: " << cur.current_ << endl;
756 void increment(CursorBase & it)
758 CursorSlice & top = it.back();
759 MathArray & ar = top.asMathInset()->cell(top.idx_);
761 // move into the current inset if possible
762 // it is impossible for pos() == size()!
764 if (top.pos() != top.lastpos())
765 n = (ar.begin() + top.pos_)->nucleus();
766 if (n && n->isActive()) {
767 it.push_back(CursorSlice(n));
771 // otherwise move on one cell back if possible
772 if (top.pos() < top.lastpos()) {
773 // pos() == lastpos() is valid!
778 // otherwise try to move on one cell if possible
779 while (top.idx() < top.lastidx()) {
781 if (top.asMathInset()->validCell(top.idx_)) {
787 // otherwise leave array, move on one back
788 // this might yield pos() == size(), but that's a ok.
790 // it certainly invalidates top
795 CursorBase ibegin(InsetBase * p)
798 it.push_back(CursorSlice(p));
803 CursorBase iend(InsetBase * p)
806 it.push_back(CursorSlice(p));
807 CursorSlice & cur = it.back();
808 cur.idx() = cur.lastidx();
809 cur.pos() = cur.lastpos();
816 ///////////////////////////////////////////////////////////////////
818 // The part below is the non-integrated rest of the original math
819 // cursor. This should be either generalized for texted or moved
820 // back to the math insets.
822 ///////////////////////////////////////////////////////////////////
824 #include "mathed/math_braceinset.h"
825 #include "mathed/math_charinset.h"
826 #include "mathed/math_commentinset.h"
827 #include "mathed/math_factory.h"
828 #include "mathed/math_gridinset.h"
829 #include "mathed/math_macroarg.h"
830 #include "mathed/math_macrotemplate.h"
831 #include "mathed/math_mathmlstream.h"
832 #include "mathed/math_scriptinset.h"
833 #include "mathed/math_spaceinset.h"
834 #include "mathed/math_support.h"
835 #include "mathed/math_unknowninset.h"
837 //#define FILEDEBUG 1
840 bool LCursor::isInside(InsetBase const * p)
842 for (unsigned i = 0; i < depth(); ++i)
843 if (cursor_[i].inset() == p)
849 bool LCursor::openable(MathAtom const & t)
860 // we can't move into anything new during selection
861 if (depth() == anchor_.size())
863 if (t.nucleus() != anchor_[depth()].inset())
870 bool LCursor::inNucleus()
872 return inset()->asMathInset()->asScriptInset() && idx() == 2;
878 autocorrect() = false;
885 if (pos() != 0 && openable(prevAtom())) {
887 push(nextAtom().nucleus());
888 inset()->idxLast(*this);
892 return posLeft() || idxLeft() || popLeft() || selection();
896 bool LCursor::right()
898 autocorrect() = false;
905 if (pos() != lastpos() && openable(nextAtom())) {
906 pushLeft(nextAtom().nucleus());
907 inset()->idxFirst(*this);
911 return posRight() || idxRight() || popRight() || selection();
915 bool positionable(CursorBase const & cursor, CursorBase const & anchor)
917 // avoid deeper nested insets when selecting
918 if (cursor.size() > anchor.size())
921 // anchor might be deeper, should have same path then
922 for (size_t i = 0; i < cursor.size(); ++i)
923 if (cursor[i].inset() != anchor[i].inset())
926 // position should be ok.
931 void LCursor::setScreenPos(int x, int y)
933 bool res = bruteFind(x, y, formula()->xlow(), formula()->xhigh(),
934 formula()->ylow(), formula()->yhigh());
936 // this can happen on creation of "math-display"
947 autocorrect() = false;
949 if (!inset()->idxHome(*this))
958 autocorrect() = false;
960 if (!inset()->idxEnd(*this))
967 void LCursor::plainErase()
973 void LCursor::markInsert()
975 cell().insert(pos(), MathAtom(new MathCharInset(0)));
979 void LCursor::markErase()
985 void LCursor::plainInsert(MathAtom const & t)
987 cell().insert(pos(), t);
992 void LCursor::insert2(string const & str)
1000 void LCursor::insert(string const & str)
1002 lyxerr << "inserting '" << str << "'" << endl;
1004 for (string::const_iterator it = str.begin(); it != str.end(); ++it)
1005 plainInsert(MathAtom(new MathCharInset(*it)));
1009 void LCursor::insert(char c)
1011 lyxerr << "inserting '" << c << "'" << endl;
1013 plainInsert(MathAtom(new MathCharInset(c)));
1017 void LCursor::insert(MathAtom const & t)
1025 void LCursor::niceInsert(string const & t)
1027 lyxerr << "*** LCursor::niceInsert 1: " << t << endl;
1037 void LCursor::niceInsert(MathAtom const & t)
1039 lyxerr << "*** LCursor::niceInsert 2: " << t << endl;
1041 string safe = grabAndEraseSelection();
1043 // enter the new inset and move the contents of the selection if possible
1044 if (t->isActive()) {
1046 pushLeft(nextAtom().nucleus());
1049 lyxerr << "*** LCursor::niceInsert 3: " << t << endl;
1053 void LCursor::insert(MathArray const & ar)
1058 cell().insert(pos(), ar);
1063 bool LCursor::backspace()
1065 autocorrect() = false;
1073 if (inset()->nargs() == 1 && depth() == 1 && lastpos() == 0)
1079 if (inMacroMode()) {
1080 MathUnknownInset * p = activeMacro();
1081 if (p->name().size() > 1) {
1082 p->setName(p->name().substr(0, p->name().size() - 1));
1087 if (pos() != 0 && prevAtom()->nargs() > 0) {
1088 // let's require two backspaces for 'big stuff' and
1089 // highlight on the first
1100 bool LCursor::erase()
1102 autocorrect() = false;
1111 // delete empty cells if possible
1112 if (pos() == lastpos() && inset()->idxDelete(idx()))
1115 // special behaviour when in last position of cell
1116 if (pos() == lastpos()) {
1117 bool one_cell = inset()->nargs() == 1;
1118 if (one_cell && depth() == 1 && lastpos() == 0)
1124 inset()->idxGlue(idx());
1128 if (pos() != lastpos() && inset()->nargs() > 0) {
1142 CursorBase save = cursor_;
1146 autocorrect() = false;
1151 bool LCursor::down()
1154 CursorBase save = cursor_;
1155 if (goUpDown(false))
1158 autocorrect() = false;
1163 void LCursor::macroModeClose()
1167 MathUnknownInset * p = activeMacro();
1169 string s = p->name();
1171 cell().erase(pos());
1173 // do nothing if the macro name is empty
1177 string const name = s.substr(1);
1179 // prevent entering of recursive macros
1180 if (formula()->lyxCode() == InsetOld::MATHMACRO_CODE
1181 && formula()->getInsetName() == name)
1182 lyxerr << "can't enter recursive macro" << endl;
1184 niceInsert(createMathInset(name));
1188 string LCursor::macroName()
1190 return inMacroMode() ? activeMacro()->name() : string();
1194 void LCursor::handleNest(MathAtom const & a, int c)
1197 asArray(grabAndEraseSelection(), t.nucleus()->cell(c));
1200 pushLeft(t.nucleus());
1204 int LCursor::targetX() const
1206 if (x_target() != -1)
1215 MathHullInset * LCursor::formula() const
1217 for (int i = cursor_.size() - 1; i >= 1; --i)
1218 if (cursor_[i].inset()->lyxCode() == InsetBase::MATH_CODE)
1219 return static_cast<MathHullInset *>(cursor_[i].inset());
1224 void LCursor::adjust(pos_type from, int diff)
1228 if (anchor().pos_ > from)
1229 anchor().pos_ += diff;
1230 // just to be on the safe side
1231 // theoretically unecessary
1236 bool LCursor::inMacroMode() const
1240 MathUnknownInset const * p = prevAtom()->asUnknownInset();
1241 return p && !p->final();
1245 MathUnknownInset * LCursor::activeMacro()
1247 return inMacroMode() ? prevAtom().nucleus()->asUnknownInset() : 0;
1251 bool LCursor::inMacroArgMode() const
1253 return pos() > 0 && prevAtom()->getChar() == '#';
1257 MathGridInset * LCursor::enclosingGrid(idx_type & idx) const
1259 for (MathInset::difference_type i = depth() - 1; i >= 0; --i) {
1260 MathInset * m = cursor_[i].inset()->asMathInset();
1263 MathGridInset * p = m->asGridInset();
1265 idx = cursor_[i].idx_;
1273 void LCursor::pullArg()
1277 MathArray ar = cell();
1280 cell().insert(pos(), ar);
1283 formula()->mutateToText();
1289 void LCursor::touch()
1293 CursorBase::const_iterator it = cursor_.begin();
1294 CursorBase::const_iterator et = cursor_.end();
1295 for ( ; it != et; ++it)
1301 void LCursor::normalize()
1303 if (idx() >= nargs()) {
1304 lyxerr << "this should not really happen - 1: "
1305 << idx() << ' ' << nargs()
1306 << " in: " << inset() << endl;
1308 idx() = min(idx(), lastidx());
1310 if (pos() > lastpos()) {
1311 lyxerr << "this should not really happen - 2: "
1312 << pos() << ' ' << lastpos() << " in idx: " << idx()
1314 WriteStream wi(lyxerr, false, true);
1315 inset()->asMathInset()->write(wi);
1318 pos() = min(pos(), lastpos());
1322 char LCursor::valign()
1325 MathGridInset * p = enclosingGrid(idx);
1326 return p ? p->valign() : '\0';
1330 char LCursor::halign()
1333 MathGridInset * p = enclosingGrid(idx);
1334 return p ? p->halign(idx % p->ncols()) : '\0';
1338 bool LCursor::goUpDown(bool up)
1340 // Be warned: The 'logic' implemented in this function is highly fragile.
1341 // A distance of one pixel or a '<' vs '<=' _really_ matters.
1342 // So fiddle around with it only if you know what you are doing!
1347 // check if we had something else in mind, if not, this is the future goal
1348 if (x_target() == -1)
1353 // try neigbouring script insets
1357 MathScriptInset const * p = prevAtom()->asScriptInset();
1358 if (p && p->has(up)) {
1361 idx() = up; // the superscript has index 1
1363 //lyxerr << "updown: handled by scriptinset to the left" << endl;
1369 if (pos() != lastpos()) {
1370 MathScriptInset const * p = nextAtom()->asScriptInset();
1371 if (p && p->has(up)) {
1375 //lyxerr << "updown: handled by scriptinset to the right" << endl;
1381 // try current cell for e.g. text insets
1382 if (inset()->idxUpDown2(*this, up))
1385 //xarray().boundingBox(xlow, xhigh, ylow, yhigh);
1390 //if (bruteFind(xo, yo, xlow, xhigh, ylow, yhigh)) {
1391 // lyxerr << "updown: handled by brute find in the same cell" << endl;
1395 // try to find an inset that knows better then we
1397 //lyxerr << "updown: We are in " << inset() << " idx: " << idx() << endl;
1399 if (inset()->idxUpDown(*this, up)) {
1400 // try to find best position within this inset
1406 // no such inset found, just take something "above"
1407 //lyxerr << "updown: handled by strange case" << endl;
1413 up ? formula()->ylow() : yo + 4,
1414 up ? yo - 4 : formula()->yhigh()
1418 // any improvement so far?
1421 if (up ? ynew < yo : ynew > yo)
1427 bool LCursor::bruteFind(int x, int y, int xlow, int xhigh, int ylow, int yhigh)
1429 CursorBase best_cursor;
1430 double best_dist = 1e10;
1432 CursorBase it = ibegin(formula());
1433 CursorBase et = iend(formula());
1435 // avoid invalid nesting when selecting
1436 if (!selection() || positionable(it, anchor_)) {
1438 CursorSlice & cur = it.back();
1439 cur.inset()->getCursorPos(cur, xo, yo);
1440 if (xlow <= xo && xo <= xhigh && ylow <= yo && yo <= yhigh) {
1441 double d = (x - xo) * (x - xo) + (y - yo) * (y - yo);
1442 //lyxerr << "x: " << x << " y: " << y << " d: " << endl;
1443 // '<=' in order to take the last possible position
1444 // this is important for clicking behind \sum in e.g. '\sum_i a'
1445 if (d <= best_dist) {
1457 if (best_dist < 1e10)
1458 cursor_ = best_cursor;
1459 return best_dist < 1e10;
1463 void LCursor::bruteFind2(int x, int y)
1465 double best_dist = 1e10;
1467 CursorBase it = cursor_;
1469 CursorBase et = cursor_;
1470 int n = et.back().asMathInset()->cell(et.back().idx_).size();
1472 for (int i = 0; ; ++i) {
1474 CursorSlice & cur = it.back();
1475 cur.inset()->getCursorPos(cur, xo, yo);
1476 double d = (x - xo) * (x - xo) + (y - yo) * (y - yo);
1477 // '<=' in order to take the last possible position
1478 // this is important for clicking behind \sum in e.g. '\sum_i a'
1479 lyxerr << "i: " << i << " d: " << d << " best: " << best_dist << endl;
1480 if (d <= best_dist) {
1491 bool LCursor::idxLineLast()
1493 idx() -= idx() % ncols();
1494 idx() += ncols() - 1;
1500 bool LCursor::idxLeft()
1502 return inset()->idxLeft(*this);
1506 bool LCursor::idxRight()
1508 return inset()->idxRight(*this);
1512 bool LCursor::script(bool up)
1514 // Hack to get \\^ and \\_ working
1515 lyxerr << "handling script: up: " << up << endl;
1516 if (inMacroMode() && macroName() == "\\") {
1518 niceInsert(createMathInset("mathcircumflex"));
1525 string safe = grabAndEraseSelection();
1527 // we are in a nucleus of a script inset, move to _our_ script
1528 inset()->asMathInset()->asScriptInset()->ensure(up);
1531 } else if (pos() != 0 && prevAtom()->asScriptInset()) {
1533 nextAtom().nucleus()->asScriptInset()->ensure(up);
1537 } else if (pos() != 0) {
1539 cell()[pos()] = MathAtom(new MathScriptInset(nextAtom(), up));
1544 plainInsert(MathAtom(new MathScriptInset(up)));
1546 nextAtom().nucleus()->asScriptInset()->ensure(up);
1556 bool LCursor::interpret(char c)
1558 //lyxerr << "interpret 2: '" << c << "'" << endl;
1560 if (inMacroArgMode()) {
1566 MathMacroTemplate const * p = formula()->asMacroTemplate();
1567 if (p && 1 <= n && n <= p->numargs())
1568 insert(MathAtom(new MathMacroArgument(c - '0')));
1570 insert(createMathInset("#"));
1571 interpret(c); // try again
1578 if (inMacroMode()) {
1579 string name = macroName();
1580 //lyxerr << "interpret name: '" << name << "'" << endl;
1583 activeMacro()->setName(activeMacro()->name() + c);
1587 // handle 'special char' macros
1592 if (currentMode() == MathInset::TEXT_MODE)
1593 niceInsert(createMathInset("textbackslash"));
1595 niceInsert(createMathInset("backslash"));
1596 } else if (c == '{') {
1597 niceInsert(MathAtom(new MathBraceInset));
1599 niceInsert(createMathInset(string(1, c)));
1604 // leave macro mode and try again if necessary
1607 niceInsert(MathAtom(new MathBraceInset));
1613 // This is annoying as one has to press <space> far too often.
1617 // leave autocorrect mode if necessary
1618 if (autocorrect() && c == ' ') {
1619 autocorrect() = false;
1624 // just clear selection on pressing the space bar
1625 if (selection() && c == ' ') {
1626 selection() = false;
1633 //lyxerr << "starting with macro" << endl;
1634 insert(MathAtom(new MathUnknownInset("\\", false)));
1639 if (currentMode() == MathInset::TEXT_MODE)
1645 if (currentMode() == MathInset::TEXT_MODE) {
1646 // insert spaces in text mode,
1647 // but suppress direct insertion of two spaces in a row
1648 // the still allows typing '<space>a<space>' and deleting the 'a', but
1649 // it is better than nothing...
1650 if (!pos() != 0 || prevAtom()->getChar() != ' ')
1654 if (pos() != 0 && prevAtom()->asSpaceInset()) {
1655 prevAtom().nucleus()->asSpaceInset()->incSpace();
1660 // if are at the very end, leave the formula
1661 return pos() != lastpos();
1674 if (c == '{' || c == '}' || c == '#' || c == '&' || c == '$') {
1675 niceInsert(createMathInset(string(1, c)));
1680 niceInsert(MathAtom(new MathCommentInset));
1684 // try auto-correction
1685 //if (autocorrect() && hasPrevAtom() && math_autocorrect(prevAtom(), c))
1688 // no special circumstances, so insert the character without any fuss
1690 autocorrect() = true;
1695 void LCursor::lockToggle()
1697 if (pos() != lastpos()) {
1698 // toggle previous inset ...
1699 nextAtom().nucleus()->lock(!nextAtom()->lock());
1700 } else if (popLeft() && pos() != lastpos()) {
1701 // ... or enclosing inset if we are in the last inset position
1702 nextAtom().nucleus()->lock(!nextAtom()->lock());
1708 CursorSlice LCursor::normalAnchor()
1710 if (anchor_.size() < depth()) {
1712 lyxerr << "unusual Anchor size" << endl;
1714 //lyx::BOOST_ASSERT(Anchor_.size() >= cursor.depth());
1715 // use Anchor on the same level as Cursor
1716 CursorSlice normal = anchor_[current_];
1718 if (depth() < anchor_.size() && !(normal < xx())) {
1719 // anchor is behind cursor -> move anchor behind the inset
1728 DispatchResult dispatch(LCursor & cur, FuncRequest const & cmd)
1730 // mouse clicks are somewhat special
1732 switch (cmd.action) {
1733 case LFUN_MOUSE_PRESS:
1734 case LFUN_MOUSE_MOTION:
1735 case LFUN_MOUSE_RELEASE:
1736 case LFUN_MOUSE_DOUBLE: {
1737 CursorSlice & pos = cursor_.back();
1741 if (x < cmd.x && pos() != 0) {
1742 DispatchResult const res = prevAtom().nucleus()->dispatch(cmd);
1743 if (res.dispatched())
1746 if (x > cmd.x && pos() != lastpos()) {
1747 DispatchResult const res = inset()->dispatch(cmd);
1748 if (res.dispatched())
1759 void LCursor::handleFont(string const & font)
1764 safe = grabAndEraseSelection();
1767 if (lastpos() != 0) {
1768 // something left in the cell
1770 // cursor in first position
1772 } else if (pos() == lastpos()) {
1773 // cursor in last position
1776 // cursor in between. split cell
1777 MathArray::iterator bt = cell().begin();
1778 MathAtom at = createMathInset(font);
1779 at.nucleus()->cell(0) = MathArray(bt, bt + pos());
1780 cell().erase(bt, bt + pos());
1785 // nothing left in the cell
1793 void LCursor::releaseMathCursor()
1796 formula()->insetUnlock(bv());
1800 bool LCursor::inMathed() const
1806 InsetBase * LCursor::nextInset()
1808 if (pos() == lastpos())
1811 return nextAtom().nucleus();
1812 Paragraph & par = paragraph();
1813 return par.isInset(pos()) ? par.getInset(pos()) : 0;
1817 InsetBase * LCursor::prevInset()
1822 return prevAtom().nucleus();
1823 Paragraph & par = paragraph();
1824 return par.isInset(pos() - 1) ? par.getInset(pos() - 1) : 0;
1828 void LCursor::message(string const & msg) const
1830 bv().owner()->getLyXFunc().setMessage(msg);
1834 void LCursor::errorMessage(string const & msg) const
1836 bv().owner()->getLyXFunc().setErrorMessage(msg);