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 DispatchResult res = inset()->dispatch(*this, cmd);
85 return DispatchResult(true, true);
87 // "Mutate" the request for semi-handled requests that need
88 // additional handling in outer levels.
91 cmd = FuncRequest(LFUN_FINISHED_LEFT);
94 cmd = FuncRequest(LFUN_FINISHED_RIGHT);
97 cmd = FuncRequest(LFUN_FINISHED_UP);
100 cmd = FuncRequest(LFUN_FINISHED_DOWN);
103 //lyxerr << "not handled on level " << current_
104 // << " val: " << res.val() << endl;
108 BOOST_ASSERT(current_ == 0);
109 DispatchResult res = bv_->text()->dispatch(*this, cmd);
110 //lyxerr << " result: " << res.val() << endl;
115 void LCursor::push(InsetBase * inset)
117 lyxerr << "LCursor::push() inset: " << inset << endl;
118 cursor_.push_back(CursorSlice(inset));
119 anchor_.push_back(CursorSlice(inset));
125 void LCursor::pop(int depth)
127 while (int(cursor_.size()) > depth + 1)
129 lyxerr << "LCursor::pop() result: " << *this << endl;
135 BOOST_ASSERT(cursor_.size() >= 1);
138 current_ = cursor_.size() - 1;
142 void LCursor::pushLeft(InsetBase * p)
144 BOOST_ASSERT(!cursor_.empty());
145 //lyxerr << "Entering inset " << t << " left" << endl;
151 bool LCursor::popLeft()
153 BOOST_ASSERT(!cursor_.empty());
154 //lyxerr << "Leaving inset to the left" << endl;
157 inset()->notifyCursorLeaves(idx());
160 inset()->notifyCursorLeaves(idx());
166 bool LCursor::popRight()
168 BOOST_ASSERT(!cursor_.empty());
169 //lyxerr << "Leaving inset to the right" << endl;
172 inset()->notifyCursorLeaves(idx());
175 inset()->notifyCursorLeaves(idx());
182 CursorSlice & LCursor::current()
184 BOOST_ASSERT(!cursor_.empty());
185 //lyxerr << "accessing cursor slice " << current_
186 // << ": " << cursor_[current_] << endl;
187 return cursor_[current_];
191 CursorSlice const & LCursor::current() const
193 //lyxerr << "accessing cursor slice " << current_
194 // << ": " << cursor_[current_] << endl;
195 return cursor_[current_];
199 int LCursor::currentMode()
201 BOOST_ASSERT(!cursor_.empty());
202 for (int i = cursor_.size() - 1; i >= 1; --i) {
203 int res = cursor_[i].inset()->currentMode();
204 if (res != MathInset::UNDECIDED_MODE)
207 return MathInset::TEXT_MODE;
211 LyXText * LCursor::innerText() const
213 BOOST_ASSERT(!cursor_.empty());
214 if (cursor_.size() > 1) {
215 // go up until first non-0 text is hit
216 // (innermost text is 0 in mathed)
217 for (int i = cursor_.size() - 1; i >= 1; --i)
218 if (cursor_[i].text())
219 return cursor_[i].text();
225 CursorSlice const & LCursor::innerTextSlice() const
227 BOOST_ASSERT(!cursor_.empty());
228 if (cursor_.size() > 1) {
229 // go up until first non-0 text is hit
230 // (innermost text is 0 in mathed)
231 for (int i = cursor_.size() - 1; i >= 1; --i)
232 if (cursor_[i].text())
239 void LCursor::updatePos()
241 BOOST_ASSERT(!cursor_.empty());
242 if (cursor_.size() > 1)
243 cached_y_ = bv_->top_y() + cursor_.back().inset()->yo();
244 //cached_y_ = cursor_.back().inset()->yo();
248 void LCursor::getDim(int & asc, int & des) const
250 BOOST_ASSERT(!cursor_.empty());
252 BOOST_ASSERT(inset());
253 BOOST_ASSERT(inset()->asMathInset());
254 //inset()->asMathInset()->getCursorDim(asc, des);
258 Row const & row = *text()->cursorRow();
259 asc = row.baseline();
260 des = row.height() - asc;
265 void LCursor::getPos(int & x, int & y) const
267 BOOST_ASSERT(!cursor_.empty());
270 if (cursor_.size() == 1) {
271 x = bv_->text()->cursorX(cursor_.front());
272 y = bv_->text()->cursorY(cursor_.front());
275 lyxerr << "#### LCursor::getPos: " << *this << endl;
276 BOOST_ASSERT(inset());
278 inset()->getCursorPos(cursor_.back(), x, y);
279 // getCursorPos gives _screen_ coordinates. We need to add
280 // top_y to get document coordinates. This is hidden in cached_y_.
281 //y += cached_y_ - inset()->yo();
282 // The rest is non-obvious. The reason we have to have these
283 // extra computation is that the getCursorPos() calls rely
284 // on the inset's own knowledge of its screen position.
285 // If we scroll up or down in a big enough increment,
286 // inset->draw() is not called: this doesn't update
287 // inset.yo_, so getCursor() returns an old value.
290 //lyxerr << "#### LCursor::getPos: " << *this
291 // << " x: " << x << " y: " << y << endl;
295 void LCursor::paste(string const & data)
297 dispatch(FuncRequest(LFUN_PASTE, data));
301 InsetBase * LCursor::innerInsetOfType(int code) const
303 for (int i = cursor_.size() - 1; i >= 1; --i)
304 if (cursor_[i].inset_->lyxCode() == code)
305 return cursor_[i].inset_;
310 InsetTabular * LCursor::innerInsetTabular() const
312 return static_cast<InsetTabular *>(innerInsetOfType(InsetBase::TABULAR_CODE));
316 void LCursor::resetAnchor()
322 BufferView & LCursor::bv() const
328 MathAtom const & LCursor::prevAtom() const
330 BOOST_ASSERT(pos() > 0);
331 return cell()[pos() - 1];
335 MathAtom & LCursor::prevAtom()
337 BOOST_ASSERT(pos() > 0);
338 return cell()[pos() - 1];
342 MathAtom const & LCursor::nextAtom() const
344 BOOST_ASSERT(pos() < lastpos());
345 return cell()[pos()];
349 MathAtom & LCursor::nextAtom()
351 BOOST_ASSERT(pos() < lastpos());
352 return cell()[pos()];
356 bool LCursor::posLeft()
365 bool LCursor::posRight()
367 if (pos() == lastpos())
374 CursorSlice & LCursor::anchor()
376 return anchor_.back();
380 CursorSlice const & LCursor::anchor() const
382 return anchor_.back();
386 CursorSlice const & LCursor::selBegin() const
389 return cursor_.back();
390 return anchor() < cursor_.back() ? anchor() : cursor_.back();
394 CursorSlice & LCursor::selBegin()
397 return cursor_.back();
398 // can't use std::min as this returns a const ref
399 return anchor() < cursor_.back() ? anchor() : cursor_.back();
403 CursorSlice const & LCursor::selEnd() const
406 return cursor_.back();
407 return anchor() > cursor_.back() ? anchor() : cursor_.back();
411 CursorSlice & LCursor::selEnd()
414 return cursor_.back();
415 // can't use std::min as this returns a const ref
416 return anchor() > cursor_.back() ? anchor() : cursor_.back();
420 void LCursor::setSelection()
423 // a selection with no contents is not a selection
424 if (par() == anchor().par() && pos() == anchor().pos())
429 void LCursor::setSelection(CursorBase const & where, size_t n)
438 void LCursor::clearSelection()
447 int & LCursor::x_target()
453 int LCursor::x_target() const
459 void LCursor::clearTargetX()
465 LyXText * LCursor::text() const
467 return current_ ? current().text() : bv_->text();
471 Paragraph & LCursor::paragraph()
473 BOOST_ASSERT(inTexted());
474 return current_ ? current().paragraph() : *bv_->text()->getPar(par());
478 Paragraph const & LCursor::paragraph() const
480 BOOST_ASSERT(inTexted());
481 return current_ ? current().paragraph() : *bv_->text()->getPar(par());
485 Row & LCursor::textRow()
487 return *paragraph().getRow(pos());
491 Row const & LCursor::textRow() const
493 return *paragraph().getRow(pos());
497 LCursor::par_type LCursor::lastpar() const
499 return inMathed() ? 0 : text()->paragraphs().size() - 1;
503 LCursor::pos_type LCursor::lastpos() const
505 InsetBase * inset = current().inset();
506 return inset && inset->asMathInset() ? cell().size() : paragraph().size();
510 LCursor::row_type LCursor::crow() const
512 return paragraph().row(pos());
516 LCursor::row_type LCursor::lastcrow() const
518 return paragraph().rows.size();
522 size_t LCursor::nargs() const
524 // assume 1x1 grid for 'plain text'
525 return current_ ? current().nargs() : 1;
529 size_t LCursor::ncols() const
531 // assume 1x1 grid for 'plain text'
532 return current_ ? current().ncols() : 1;
536 size_t LCursor::nrows() const
538 // assume 1x1 grid for 'plain text'
539 return current_ ? current().nrows() : 1;
543 LCursor::row_type LCursor::row() const
545 BOOST_ASSERT(current_ > 0);
546 return current().row();
550 LCursor::col_type LCursor::col() const
552 BOOST_ASSERT(current_ > 0);
553 return current().col();
557 MathArray const & LCursor::cell() const
559 BOOST_ASSERT(current_ > 0);
560 return current().cell();
564 MathArray & LCursor::cell()
566 BOOST_ASSERT(current_ > 0);
567 return current().cell();
571 void LCursor::info(std::ostream & os)
573 for (int i = 1, n = depth(); i < n; ++i) {
574 cursor_[i].inset()->infoize(os);
578 prevInset()->infoize2(os);
579 // overwite old message
586 void region(CursorSlice const & i1, CursorSlice const & i2,
587 LCursor::row_type & r1, LCursor::row_type & r2,
588 LCursor::col_type & c1, LCursor::col_type & c2)
590 InsetBase * p = i1.inset();
591 c1 = p->col(i1.idx_);
592 c2 = p->col(i2.idx_);
595 r1 = p->row(i1.idx_);
596 r2 = p->row(i2.idx_);
604 string LCursor::grabSelection()
609 CursorSlice i1 = selBegin();
610 CursorSlice i2 = selEnd();
612 if (i1.idx_ == i2.idx_) {
613 if (i1.inset()->asMathInset()) {
614 MathArray::const_iterator it = i1.cell().begin();
615 return asString(MathArray(it + i1.pos_, it + i2.pos_));
617 return "unknown selection 1";
623 region(i1, i2, r1, r2, c1, c2);
626 if (i1.inset()->asMathInset()) {
627 for (row_type row = r1; row <= r2; ++row) {
630 for (col_type col = c1; col <= c2; ++col) {
633 data += asString(i1.asMathInset()->cell(i1.asMathInset()->index(row, col)));
637 data = "unknown selection 2";
643 void LCursor::eraseSelection()
645 //lyxerr << "LCursor::eraseSelection" << endl;
646 CursorSlice const & i1 = selBegin();
647 CursorSlice const & i2 = selEnd();
649 if (i1.inset()->asMathInset()) {
650 if (i1.idx_ == i2.idx_) {
651 i1.cell().erase(i1.pos_, i2.pos_);
653 MathInset * p = i1.asMathInset();
656 region(i1, i2, r1, r2, c1, c2);
657 for (row_type row = r1; row <= r2; ++row)
658 for (col_type col = c1; col <= c2; ++col)
659 p->cell(p->index(row, col)).clear();
663 lyxerr << "can't erase this selection 1" << endl;
665 //lyxerr << "LCursor::eraseSelection end" << endl;
669 string LCursor::grabAndEraseSelection()
673 string res = grabSelection();
680 void LCursor::selClear()
687 void LCursor::selCopy()
690 theCutBuffer.push(grabSelection());
693 //theCutBuffer.erase();
698 void LCursor::selCut()
700 theCutBuffer.push(grabAndEraseSelection());
704 void LCursor::selDel()
706 //lyxerr << "LCursor::selDel" << endl;
714 void LCursor::selPaste(size_t n)
717 if (n < theCutBuffer.size())
718 paste(theCutBuffer[n]);
724 void LCursor::selHandle(bool sel)
726 //lyxerr << "LCursor::selHandle" << endl;
727 if (sel == selection())
734 void LCursor::selClearOrDel()
736 //lyxerr << "LCursor::selClearOrDel" << endl;
737 if (lyxrc.auto_region_delete)
744 std::ostream & operator<<(std::ostream & os, LCursor const & cur)
746 for (size_t i = 0, n = cur.cursor_.size(); i != n; ++i)
747 os << " " << cur.cursor_[i] << " | " << cur.anchor_[i] << "\n";
748 os << " current: " << cur.current_ << endl;
749 os << " selection: " << cur.selection_ << endl;
761 void increment(CursorBase & it)
763 CursorSlice & top = it.back();
764 MathArray & ar = top.asMathInset()->cell(top.idx_);
766 // move into the current inset if possible
767 // it is impossible for pos() == size()!
769 if (top.pos() != top.lastpos())
770 n = (ar.begin() + top.pos_)->nucleus();
771 if (n && n->isActive()) {
772 it.push_back(CursorSlice(n));
776 // otherwise move on one cell back if possible
777 if (top.pos() < top.lastpos()) {
778 // pos() == lastpos() is valid!
783 // otherwise try to move on one cell if possible
784 while (top.idx() < top.lastidx()) {
786 if (top.asMathInset()->validCell(top.idx_)) {
792 // otherwise leave array, move on one back
793 // this might yield pos() == size(), but that's a ok.
795 // it certainly invalidates top
800 CursorBase ibegin(InsetBase * p)
803 it.push_back(CursorSlice(p));
808 CursorBase iend(InsetBase * p)
811 it.push_back(CursorSlice(p));
812 CursorSlice & cur = it.back();
813 cur.idx() = cur.lastidx();
814 cur.pos() = cur.lastpos();
821 ///////////////////////////////////////////////////////////////////
823 // The part below is the non-integrated rest of the original math
824 // cursor. This should be either generalized for texted or moved
825 // back to the math insets.
827 ///////////////////////////////////////////////////////////////////
829 #include "mathed/math_braceinset.h"
830 #include "mathed/math_charinset.h"
831 #include "mathed/math_commentinset.h"
832 #include "mathed/math_factory.h"
833 #include "mathed/math_gridinset.h"
834 #include "mathed/math_macroarg.h"
835 #include "mathed/math_macrotemplate.h"
836 #include "mathed/math_mathmlstream.h"
837 #include "mathed/math_scriptinset.h"
838 #include "mathed/math_spaceinset.h"
839 #include "mathed/math_support.h"
840 #include "mathed/math_unknowninset.h"
842 //#define FILEDEBUG 1
845 bool LCursor::isInside(InsetBase const * p)
847 for (unsigned i = 0; i < depth(); ++i)
848 if (cursor_[i].inset() == p)
854 bool LCursor::openable(MathAtom const & t)
865 // we can't move into anything new during selection
866 if (depth() == anchor_.size())
868 if (!ptr_cmp(t.nucleus(), anchor_[depth()].inset()))
875 bool LCursor::inNucleus()
877 return inset()->asMathInset()->asScriptInset() && idx() == 2;
883 autocorrect() = false;
890 if (pos() != 0 && openable(prevAtom())) {
892 push(nextAtom().nucleus());
893 inset()->idxLast(*this);
897 return posLeft() || idxLeft() || popLeft() || selection();
901 bool LCursor::right()
903 autocorrect() = false;
910 if (pos() != lastpos() && openable(nextAtom())) {
911 pushLeft(nextAtom().nucleus());
912 inset()->idxFirst(*this);
916 return posRight() || idxRight() || popRight() || selection();
920 bool positionable(CursorBase const & cursor, CursorBase const & anchor)
922 // avoid deeper nested insets when selecting
923 if (cursor.size() > anchor.size())
926 // anchor might be deeper, should have same path then
927 for (size_t i = 0; i < cursor.size(); ++i)
928 if (cursor[i].inset() != anchor[i].inset())
931 // position should be ok.
936 void LCursor::setScreenPos(int x, int y)
938 bool res = bruteFind(x, y, formula()->xlow(), formula()->xhigh(),
939 formula()->ylow(), formula()->yhigh());
941 // this can happen on creation of "math-display"
952 autocorrect() = false;
954 if (!inset()->idxHome(*this))
963 autocorrect() = false;
965 if (!inset()->idxEnd(*this))
972 void LCursor::plainErase()
978 void LCursor::markInsert()
980 cell().insert(pos(), MathAtom(new MathCharInset(0)));
984 void LCursor::markErase()
990 void LCursor::plainInsert(MathAtom const & t)
992 cell().insert(pos(), t);
997 void LCursor::insert(string const & str)
999 lyxerr << "LCursor::insert str '" << str << "'" << endl;
1002 for (string::const_iterator it = str.begin(); it != str.end(); ++it)
1003 plainInsert(MathAtom(new MathCharInset(*it)));
1012 void LCursor::insert(char c)
1014 //lyxerr << "LCursor::insert char '" << c << "'" << endl;
1016 plainInsert(MathAtom(new MathCharInset(c)));
1020 void LCursor::insert(MathAtom const & t)
1022 //lyxerr << "LCursor::insert MathAtom: " << endl;
1029 void LCursor::insert(InsetBase * inset)
1032 insert(MathAtom(inset));
1034 text()->insertInset(inset);
1038 void LCursor::niceInsert(string const & t)
1049 void LCursor::niceInsert(MathAtom const & t)
1052 string safe = grabAndEraseSelection();
1054 // enter the new inset and move the contents of the selection if possible
1055 if (t->isActive()) {
1057 // be careful here: don't use 'pushLeft(t)' as this we need to
1058 // push the clone, not the original
1059 pushLeft(nextAtom().nucleus());
1065 void LCursor::insert(MathArray const & ar)
1070 cell().insert(pos(), ar);
1075 bool LCursor::backspace()
1077 autocorrect() = false;
1085 if (inset()->nargs() == 1 && depth() == 1 && lastpos() == 0)
1091 if (inMacroMode()) {
1092 MathUnknownInset * p = activeMacro();
1093 if (p->name().size() > 1) {
1094 p->setName(p->name().substr(0, p->name().size() - 1));
1099 if (pos() != 0 && prevAtom()->nargs() > 0) {
1100 // let's require two backspaces for 'big stuff' and
1101 // highlight on the first
1112 bool LCursor::erase()
1114 autocorrect() = false;
1123 // delete empty cells if possible
1124 if (pos() == lastpos() && inset()->idxDelete(idx()))
1127 // special behaviour when in last position of cell
1128 if (pos() == lastpos()) {
1129 bool one_cell = inset()->nargs() == 1;
1130 if (one_cell && depth() == 1 && lastpos() == 0)
1136 inset()->idxGlue(idx());
1140 if (pos() != lastpos() && inset()->nargs() > 0) {
1154 CursorBase save = cursor_;
1158 autocorrect() = false;
1163 bool LCursor::down()
1166 CursorBase save = cursor_;
1167 if (goUpDown(false))
1170 autocorrect() = false;
1175 void LCursor::macroModeClose()
1179 MathUnknownInset * p = activeMacro();
1181 string s = p->name();
1183 cell().erase(pos());
1185 // do nothing if the macro name is empty
1189 string const name = s.substr(1);
1191 // prevent entering of recursive macros
1192 if (formula()->lyxCode() == InsetOld::MATHMACRO_CODE
1193 && formula()->getInsetName() == name)
1194 lyxerr << "can't enter recursive macro" << endl;
1196 niceInsert(createMathInset(name));
1200 string LCursor::macroName()
1202 return inMacroMode() ? activeMacro()->name() : string();
1206 void LCursor::handleNest(MathAtom const & a, int c)
1208 //lyxerr << "LCursor::handleNest: " << c << endl;
1210 asArray(grabAndEraseSelection(), t.nucleus()->cell(c));
1213 pushLeft(nextAtom().nucleus());
1217 int LCursor::targetX() const
1219 if (x_target() != -1)
1228 MathHullInset * LCursor::formula() const
1230 for (int i = cursor_.size() - 1; i >= 1; --i) {
1231 MathInset * inset = cursor_[i].inset()->asMathInset();
1232 if (inset && inset->asHullInset())
1233 return static_cast<MathHullInset *>(inset);
1239 void LCursor::adjust(pos_type from, int diff)
1243 if (anchor().pos_ > from)
1244 anchor().pos_ += diff;
1245 // just to be on the safe side
1246 // theoretically unecessary
1251 bool LCursor::inMacroMode() const
1255 MathUnknownInset const * p = prevAtom()->asUnknownInset();
1256 return p && !p->final();
1260 MathUnknownInset * LCursor::activeMacro()
1262 return inMacroMode() ? prevAtom().nucleus()->asUnknownInset() : 0;
1266 bool LCursor::inMacroArgMode() const
1268 return pos() > 0 && prevAtom()->getChar() == '#';
1272 MathGridInset * LCursor::enclosingGrid(idx_type & idx) const
1274 for (MathInset::difference_type i = depth() - 1; i >= 0; --i) {
1275 MathInset * m = cursor_[i].inset()->asMathInset();
1278 MathGridInset * p = m->asGridInset();
1280 idx = cursor_[i].idx_;
1288 void LCursor::pullArg()
1291 MathArray ar = cell();
1292 if (popLeft() && inMathed()) {
1294 cell().insert(pos(), ar);
1297 //formula()->mutateToText();
1302 void LCursor::touch()
1306 CursorBase::const_iterator it = cursor_.begin();
1307 CursorBase::const_iterator et = cursor_.end();
1308 for ( ; it != et; ++it)
1314 void LCursor::normalize()
1316 if (idx() >= nargs()) {
1317 lyxerr << "this should not really happen - 1: "
1318 << idx() << ' ' << nargs()
1319 << " in: " << inset() << endl;
1321 idx() = min(idx(), lastidx());
1323 if (pos() > lastpos()) {
1324 lyxerr << "this should not really happen - 2: "
1325 << pos() << ' ' << lastpos() << " in idx: " << idx()
1327 WriteStream wi(lyxerr, false, true);
1328 inset()->asMathInset()->write(wi);
1331 pos() = min(pos(), lastpos());
1335 char LCursor::valign()
1338 MathGridInset * p = enclosingGrid(idx);
1339 return p ? p->valign() : '\0';
1343 char LCursor::halign()
1346 MathGridInset * p = enclosingGrid(idx);
1347 return p ? p->halign(idx % p->ncols()) : '\0';
1351 bool LCursor::goUpDown(bool up)
1353 // Be warned: The 'logic' implemented in this function is highly fragile.
1354 // A distance of one pixel or a '<' vs '<=' _really_ matters.
1355 // So fiddle around with it only if you know what you are doing!
1360 // check if we had something else in mind, if not, this is the future goal
1361 if (x_target() == -1)
1366 // try neigbouring script insets
1370 MathScriptInset const * p = prevAtom()->asScriptInset();
1371 if (p && p->has(up)) {
1374 idx() = up; // the superscript has index 1
1376 //lyxerr << "updown: handled by scriptinset to the left" << endl;
1382 if (pos() != lastpos()) {
1383 MathScriptInset const * p = nextAtom()->asScriptInset();
1384 if (p && p->has(up)) {
1388 //lyxerr << "updown: handled by scriptinset to the right" << endl;
1394 // try current cell for e.g. text insets
1395 if (inset()->idxUpDown2(*this, up))
1398 //xarray().boundingBox(xlow, xhigh, ylow, yhigh);
1403 //if (bruteFind(xo, yo, xlow, xhigh, ylow, yhigh)) {
1404 // lyxerr << "updown: handled by brute find in the same cell" << endl;
1408 // try to find an inset that knows better then we
1410 //lyxerr << "updown: We are in " << inset() << " idx: " << idx() << endl;
1412 if (inset()->idxUpDown(*this, up)) {
1413 // try to find best position within this inset
1419 // no such inset found, just take something "above"
1420 //lyxerr << "updown: handled by strange case" << endl;
1426 up ? formula()->ylow() : yo + 4,
1427 up ? yo - 4 : formula()->yhigh()
1431 // any improvement so far?
1434 if (up ? ynew < yo : ynew > yo)
1440 bool LCursor::bruteFind(int x, int y, int xlow, int xhigh, int ylow, int yhigh)
1442 CursorBase best_cursor;
1443 double best_dist = 1e10;
1445 CursorBase it = ibegin(formula());
1446 CursorBase et = iend(formula());
1448 // avoid invalid nesting when selecting
1449 if (!selection() || positionable(it, anchor_)) {
1451 CursorSlice & cur = it.back();
1452 cur.inset()->getCursorPos(cur, xo, yo);
1453 if (xlow <= xo && xo <= xhigh && ylow <= yo && yo <= yhigh) {
1454 double d = (x - xo) * (x - xo) + (y - yo) * (y - yo);
1455 //lyxerr << "x: " << x << " y: " << y << " d: " << endl;
1456 // '<=' in order to take the last possible position
1457 // this is important for clicking behind \sum in e.g. '\sum_i a'
1458 if (d <= best_dist) {
1470 if (best_dist < 1e10)
1471 cursor_ = best_cursor;
1472 return best_dist < 1e10;
1476 void LCursor::bruteFind2(int x, int y)
1478 double best_dist = 1e10;
1480 CursorBase it = cursor_;
1482 CursorBase et = cursor_;
1483 int n = et.back().asMathInset()->cell(et.back().idx_).size();
1485 for (int i = 0; ; ++i) {
1487 CursorSlice & cur = it.back();
1488 cur.inset()->getCursorPos(cur, xo, yo);
1489 double d = (x - xo) * (x - xo) + (y - yo) * (y - yo);
1490 // '<=' in order to take the last possible position
1491 // this is important for clicking behind \sum in e.g. '\sum_i a'
1492 lyxerr << "i: " << i << " d: " << d << " best: " << best_dist << endl;
1493 if (d <= best_dist) {
1504 bool LCursor::idxLineLast()
1506 idx() -= idx() % ncols();
1507 idx() += ncols() - 1;
1513 bool LCursor::idxLeft()
1515 return inset()->idxLeft(*this);
1519 bool LCursor::idxRight()
1521 return inset()->idxRight(*this);
1525 bool LCursor::script(bool up)
1527 // Hack to get \\^ and \\_ working
1528 lyxerr << "handling script: up: " << up << endl;
1529 if (inMacroMode() && macroName() == "\\") {
1531 niceInsert(createMathInset("mathcircumflex"));
1538 string safe = grabAndEraseSelection();
1540 // we are in a nucleus of a script inset, move to _our_ script
1541 inset()->asMathInset()->asScriptInset()->ensure(up);
1544 } else if (pos() != 0 && prevAtom()->asScriptInset()) {
1546 nextAtom().nucleus()->asScriptInset()->ensure(up);
1550 } else if (pos() != 0) {
1552 cell()[pos()] = MathAtom(new MathScriptInset(nextAtom(), up));
1557 plainInsert(MathAtom(new MathScriptInset(up)));
1559 nextAtom().nucleus()->asScriptInset()->ensure(up);
1569 bool LCursor::interpret(char c)
1571 //lyxerr << "interpret 2: '" << c << "'" << endl;
1573 if (inMacroArgMode()) {
1579 MathMacroTemplate const * p = formula()->asMacroTemplate();
1580 if (p && 1 <= n && n <= p->numargs())
1581 insert(MathAtom(new MathMacroArgument(c - '0')));
1583 insert(createMathInset("#"));
1584 interpret(c); // try again
1591 if (inMacroMode()) {
1592 string name = macroName();
1593 //lyxerr << "interpret name: '" << name << "'" << endl;
1596 activeMacro()->setName(activeMacro()->name() + c);
1600 // handle 'special char' macros
1605 if (currentMode() == MathInset::TEXT_MODE)
1606 niceInsert(createMathInset("textbackslash"));
1608 niceInsert(createMathInset("backslash"));
1609 } else if (c == '{') {
1610 niceInsert(MathAtom(new MathBraceInset));
1612 niceInsert(createMathInset(string(1, c)));
1617 // leave macro mode and try again if necessary
1620 niceInsert(MathAtom(new MathBraceInset));
1626 // This is annoying as one has to press <space> far too often.
1630 // leave autocorrect mode if necessary
1631 if (autocorrect() && c == ' ') {
1632 autocorrect() = false;
1637 // just clear selection on pressing the space bar
1638 if (selection() && c == ' ') {
1639 selection() = false;
1646 //lyxerr << "starting with macro" << endl;
1647 insert(MathAtom(new MathUnknownInset("\\", false)));
1652 if (currentMode() == MathInset::TEXT_MODE)
1658 if (currentMode() == MathInset::TEXT_MODE) {
1659 // insert spaces in text mode,
1660 // but suppress direct insertion of two spaces in a row
1661 // the still allows typing '<space>a<space>' and deleting the 'a', but
1662 // it is better than nothing...
1663 if (!pos() != 0 || prevAtom()->getChar() != ' ')
1667 if (pos() != 0 && prevAtom()->asSpaceInset()) {
1668 prevAtom().nucleus()->asSpaceInset()->incSpace();
1673 // if are at the very end, leave the formula
1674 return pos() != lastpos();
1687 if (c == '{' || c == '}' || c == '#' || c == '&' || c == '$') {
1688 niceInsert(createMathInset(string(1, c)));
1693 niceInsert(MathAtom(new MathCommentInset));
1697 // try auto-correction
1698 //if (autocorrect() && hasPrevAtom() && math_autocorrect(prevAtom(), c))
1701 // no special circumstances, so insert the character without any fuss
1703 autocorrect() = true;
1708 void LCursor::lockToggle()
1710 if (pos() != lastpos()) {
1711 // toggle previous inset ...
1712 nextAtom().nucleus()->lock(!nextAtom()->lock());
1713 } else if (popLeft() && pos() != lastpos()) {
1714 // ... or enclosing inset if we are in the last inset position
1715 nextAtom().nucleus()->lock(!nextAtom()->lock());
1721 CursorSlice LCursor::normalAnchor()
1723 if (anchor_.size() < depth()) {
1725 lyxerr << "unusual Anchor size" << endl;
1727 //lyx::BOOST_ASSERT(Anchor_.size() >= cursor.depth());
1728 // use Anchor on the same level as Cursor
1729 CursorSlice normal = anchor_[current_];
1731 if (depth() < anchor_.size() && !(normal < xx())) {
1732 // anchor is behind cursor -> move anchor behind the inset
1741 DispatchResult dispatch(LCursor & cur, FuncRequest const & cmd)
1743 // mouse clicks are somewhat special
1745 switch (cmd.action) {
1746 case LFUN_MOUSE_PRESS:
1747 case LFUN_MOUSE_MOTION:
1748 case LFUN_MOUSE_RELEASE:
1749 case LFUN_MOUSE_DOUBLE: {
1750 CursorSlice & pos = cursor_.back();
1754 if (x < cmd.x && pos() != 0) {
1755 DispatchResult const res = prevAtom().nucleus()->dispatch(cmd);
1756 if (res.dispatched())
1759 if (x > cmd.x && pos() != lastpos()) {
1760 DispatchResult const res = inset()->dispatch(cmd);
1761 if (res.dispatched())
1772 void LCursor::handleFont(string const & font)
1774 lyxerr << "LCursor::handleFont: " << font << endl;
1778 safe = grabAndEraseSelection();
1781 if (lastpos() != 0) {
1782 // something left in the cell
1784 // cursor in first position
1786 } else if (pos() == lastpos()) {
1787 // cursor in last position
1790 // cursor in between. split cell
1791 MathArray::iterator bt = cell().begin();
1792 MathAtom at = createMathInset(font);
1793 at.nucleus()->cell(0) = MathArray(bt, bt + pos());
1794 cell().erase(bt, bt + pos());
1799 // nothing left in the cell
1807 void LCursor::releaseMathCursor()
1810 formula()->insetUnlock(bv());
1814 bool LCursor::inMathed() const
1820 bool LCursor::inTexted() const
1826 InsetBase * LCursor::nextInset()
1828 if (pos() == lastpos())
1831 return nextAtom().nucleus();
1832 Paragraph & par = paragraph();
1833 return par.isInset(pos()) ? par.getInset(pos()) : 0;
1837 InsetBase * LCursor::prevInset()
1842 return prevAtom().nucleus();
1843 Paragraph & par = paragraph();
1844 return par.isInset(pos() - 1) ? par.getInset(pos() - 1) : 0;
1848 void LCursor::message(string const & msg) const
1850 bv().owner()->getLyXFunc().setMessage(msg);
1854 void LCursor::errorMessage(string const & msg) const
1856 bv().owner()->getLyXFunc().setErrorMessage(msg);
1860 string LCursor::selectionAsString(bool label) const
1866 Buffer const & buffer = *bv().buffer();
1868 // should be const ...
1869 ParagraphList::iterator startpit = text()->getPar(selBegin());
1870 ParagraphList::iterator endpit = text()->getPar(selEnd());
1871 size_t const startpos = selBegin().pos();
1872 size_t const endpos = selEnd().pos();
1874 if (startpit == endpit)
1875 return startpit->asString(buffer, startpos, endpos, label);
1877 // First paragraph in selection
1879 startpit->asString(buffer, startpos, startpit->size(), label) + "\n\n";
1881 // The paragraphs in between (if any)
1882 ParagraphList::iterator pit = startpit;
1883 for (++pit; pit != endpit; ++pit)
1884 result += pit->asString(buffer, 0, pit->size(), label) + "\n\n";
1886 // Last paragraph in selection
1887 result += endpit->asString(buffer, 0, endpos, label);