]> git.lyx.org Git - lyx.git/blob - src/cursor.C
955d3084242cac94ae577e926f897e931de49198
[lyx.git] / src / cursor.C
1 /**
2  * \file cursor.C
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Alejandro Aguilar Sierra
7  * \author Alfredo Braunstein
8  * \author André Pönitz
9  *
10  * Full author contact details are available in file CREDITS.
11  */
12
13 #include <config.h>
14
15 #include "buffer.h"
16 #include "BufferView.h"
17 #include "cursor.h"
18 #include "debug.h"
19 #include "dispatchresult.h"
20 #include "funcrequest.h"
21 #include "iterators.h"
22 #include "lfuns.h"
23 #include "lyxrc.h"
24 #include "lyxtext.h"
25 #include "lyxrow.h"
26 #include "paragraph.h"
27
28 #include "insets/updatableinset.h"
29 #include "insets/insettabular.h"
30 #include "insets/insettext.h"
31
32 #include "mathed/math_data.h"
33 #include "mathed/math_hullinset.h"
34 #include "mathed/math_support.h"
35
36 #include "support/limited_stack.h"
37
38 #include <boost/assert.hpp>
39
40 using std::string;
41 using std::vector;
42 using std::endl;
43 #ifndef CXX_GLOBAL_CSTD
44 using std::isalpha;
45 #endif
46 using std::min;
47 using std::swap;
48
49
50
51 // our own cut buffer
52 limited_stack<string> theCutBuffer;
53
54
55 LCursor::LCursor(BufferView & bv)
56         : cursor_(1), anchor_(1), bv_(&bv), current_(0),
57           cached_y_(0), x_target_(-1),
58           selection_(false), mark_(false)
59 {}
60
61
62 void LCursor::reset()
63 {
64         cursor_.clear();
65         anchor_.clear();
66         cursor_.push_back(CursorSlice());
67         anchor_.push_back(CursorSlice());
68         current_ = 0;
69         cached_y_ = 0;
70         clearTargetX();
71         selection_ = false;
72         mark_ = false;
73 }
74
75
76 DispatchResult LCursor::dispatch(FuncRequest const & cmd0)
77 {
78         //lyxerr << "\nLCursor::dispatch: cmd: " << cmd0 << endl << *this << endl;
79         FuncRequest cmd = cmd0;
80
81         for (int i = cursor_.size() - 1; i >= 1; --i) {
82                 current_ = i;
83                 CursorSlice const & citem = cursor_[i];
84                 lyxerr << "trying to dispatch to inset " << citem.inset_ << endl;
85                 DispatchResult res = inset()->dispatch(*this, cmd);
86                 if (res.dispatched()) {
87                         lyxerr << " successfully dispatched to inset " << citem.inset_ << endl;
88                         return DispatchResult(true, true);
89                 }
90                 // remove one level of cursor
91                 switch (res.val()) {
92                         case FINISHED:
93                                 pop(i);
94                                 cmd = FuncRequest(LFUN_FINISHED_LEFT);
95                                 break;
96                         case FINISHED_RIGHT:
97                                 pop(i);
98                                 cmd = FuncRequest(LFUN_FINISHED_RIGHT);
99                                 break;
100                         case FINISHED_UP:
101                                 pop(i);
102                                 cmd = FuncRequest(LFUN_FINISHED_UP);
103                                 break;
104                         case FINISHED_DOWN:
105                                 pop(i);
106                                 cmd = FuncRequest(LFUN_FINISHED_DOWN);
107                                 break;
108                         default:
109                                 lyxerr << "not handled on level " << i << " val: " << res.val() << endl;
110                                 break;
111                 }
112         }
113         lyxerr << "trying to dispatch to main text " << bv_->text() << endl;
114         DispatchResult res = bv_->text()->dispatch(*this, cmd);
115         lyxerr << "   result: " << res.val() << endl;
116         return res;
117 }
118
119
120 void LCursor::push(InsetBase * inset)
121 {
122         lyxerr << "LCursor::push()  inset: " << inset << endl;
123         cursor_.push_back(CursorSlice(inset));
124         anchor_.push_back(CursorSlice(inset));
125         ++current_;
126         updatePos();
127 }
128
129
130 void LCursor::pop(int depth)
131 {
132         //lyxerr << "LCursor::pop() to depth " << depth << endl;
133         while (int(cursor_.size()) > depth)
134                 pop();
135 }
136
137
138 void LCursor::pop()
139 {
140         BOOST_ASSERT(!cursor_.empty());
141         //lyxerr << "LCursor::pop() a level" << endl;
142         if (cursor_.size() <= 1)
143                 lyxerr << "### TRYING TO POP FROM EMPTY CURSOR" << endl;
144         else {
145                 cursor_.pop_back();
146                 anchor_.pop_back();
147                 current_ = cursor_.size() - 1;
148         }
149         //lyxerr << "LCursor::pop() current now: " << current_ << endl;
150 }
151
152
153 void LCursor::pushLeft(InsetBase * p)
154 {
155         BOOST_ASSERT(!cursor_.empty());
156         //lyxerr << "Entering inset " << t << " left" << endl;
157         push(p);
158         p->idxFirst(*this);
159 }
160
161
162 bool LCursor::popLeft()
163 {
164         BOOST_ASSERT(!cursor_.empty());
165         //lyxerr << "Leaving inset to the left" << endl;
166         if (depth() <= 1) {
167                 if (depth() == 1)
168                         inset()->notifyCursorLeaves(idx());
169                 return false;
170         }
171         inset()->notifyCursorLeaves(idx());
172         pop();
173         return true;
174 }
175
176
177 bool LCursor::popRight()
178 {
179         BOOST_ASSERT(!cursor_.empty());
180         //lyxerr << "Leaving inset to the right" << endl;
181         if (depth() <= 1) {
182                 if (depth() == 1)
183                         inset()->notifyCursorLeaves(idx());
184                 return false;
185         }
186         inset()->notifyCursorLeaves(idx());
187         pop();
188         posRight();
189         return true;
190 }
191
192
193 CursorSlice & LCursor::current()
194 {
195         BOOST_ASSERT(!cursor_.empty());
196         //lyxerr << "accessing cursor slice " << current_
197         //      << ": " << cursor_[current_] << endl;
198         return cursor_[current_];
199 }
200
201
202 CursorSlice const & LCursor::current() const
203 {
204         //lyxerr << "accessing cursor slice " << current_
205         //      << ": " << cursor_[current_] << endl;
206         return cursor_[current_];
207 }
208
209
210 int LCursor::currentMode()
211 {
212         BOOST_ASSERT(!cursor_.empty());
213         for (int i = cursor_.size() - 1; i >= 1; --i) {
214                 int res = cursor_[i].inset()->currentMode();
215                 if (res != MathInset::UNDECIDED_MODE)
216                         return res;
217         }
218         return MathInset::TEXT_MODE;
219 }
220
221
222 LyXText * LCursor::innerText() const
223 {
224         BOOST_ASSERT(!cursor_.empty());
225         //lyxerr << "LCursor::innerText()  depth: " << cursor_.size() << endl;
226         if (cursor_.size() > 1) {
227                 // go up until first non-0 text is hit
228                 // (innermost text is 0 in mathed)
229                 for (int i = cursor_.size() - 1; i >= 1; --i)
230                         if (cursor_[i].text())
231                                 return cursor_[i].text();
232         }
233         return bv_->text();
234 }
235
236
237 CursorSlice const & LCursor::innerTextSlice() const
238 {
239         BOOST_ASSERT(!cursor_.empty());
240         //lyxerr << "LCursor::innerTextSlice()  depth: " << cursor_.size() << endl;
241         if (cursor_.size() > 1) {
242                 // go up until first non-0 text is hit
243                 // (innermost text is 0 in mathed)
244                 for (int i = cursor_.size() - 1; i >= 1; --i)
245                         if (cursor_[i].text())
246                                 return cursor_[i];
247         }
248         return cursor_[0];
249 }
250
251
252 void LCursor::updatePos()
253 {
254         BOOST_ASSERT(!cursor_.empty());
255         if (cursor_.size() > 1)
256                 cached_y_ = bv_->top_y() + cursor_.back().inset()->yo();
257                 //cached_y_ = cursor_.back().inset()->yo();
258 }
259
260
261 void LCursor::getDim(int & asc, int & des) const
262 {
263         BOOST_ASSERT(!cursor_.empty());
264         LyXText * text = innerText();
265 #warning crashes with text-in-math
266         if (0 && text) {
267                 RowList::iterator const rit = text->cursorRow();
268                 if (rit != text->endRow()) {
269                         asc = rit->baseline();
270                         des = rit->height() - asc;
271                 } else {
272                         asc = 10;
273                         des = 10;
274                 }
275         } else {
276                 asc = 10;
277                 des = 10;
278                 //innerInset()->getCursorDim(asc, des);
279         }
280 }
281
282
283 void LCursor::getPos(int & x, int & y) const
284 {
285         BOOST_ASSERT(!cursor_.empty());
286         x = 0;
287         y = 0;
288         if (cursor_.size() <= 1) {
289                 x = bv_->text()->cursorX(cursor_.front());
290                 y = bv_->text()->cursorY(cursor_.front());
291         } else {
292                 inset()->getCursorPos(cursor_.back(), x, y);
293                 // getCursorPos gives _screen_ coordinates. We need to add
294                 // top_y to get document coordinates. This is hidden in cached_y_.
295                 //y += cached_y_ - inset()->yo();
296                 // The rest is non-obvious. The reason we have to have these
297                 // extra computation is that the getCursorPos() calls rely
298                 // on the inset's own knowledge of its screen position.
299                 // If we scroll up or down in a big enough increment,
300                 // inset->draw() is not called: this doesn't update
301                 // inset.yo_, so getCursor() returns an old value.
302                 // Ugly as you like.
303         }
304         lyxerr << "#### LCursor::getPos: x: " << x << " y: " << y << endl;
305 }
306
307
308 void LCursor::paste(string const & data)
309 {
310         dispatch(FuncRequest(LFUN_PASTE, data));
311 }
312
313
314 InsetBase * LCursor::innerInsetOfType(int code) const
315 {
316         for (int i = cursor_.size() - 1; i >= 1; --i)
317                 if (cursor_[i].inset_->lyxCode() == code)
318                         return cursor_[i].inset_;
319         return 0;
320 }
321
322
323 InsetTabular * LCursor::innerInsetTabular() const
324 {
325         return static_cast<InsetTabular *>(innerInsetOfType(InsetBase::TABULAR_CODE));
326 }
327
328
329 void LCursor::resetAnchor()
330 {
331         anchor_ = cursor_;
332 }
333
334
335 BufferView & LCursor::bv() const
336 {
337         return *bv_;
338 }
339
340
341 MathAtom const & LCursor::prevAtom() const
342 {
343         BOOST_ASSERT(pos() > 0);
344         return cell()[pos() - 1];
345 }
346
347
348 MathAtom & LCursor::prevAtom()
349 {
350         BOOST_ASSERT(pos() > 0);
351         return cell()[pos() - 1];
352 }
353
354
355 MathAtom const & LCursor::nextAtom() const
356 {
357         BOOST_ASSERT(pos() < lastpos());
358         return cell()[pos()];
359 }
360
361
362 MathAtom & LCursor::nextAtom()
363 {
364         BOOST_ASSERT(pos() < lastpos());
365         return cell()[pos()];
366 }
367
368
369 bool LCursor::posLeft()
370 {
371         if (pos() == 0)
372                 return false;
373         --pos();
374         return true;
375 }
376
377
378 bool LCursor::posRight()
379 {
380         if (pos() == lastpos())
381                 return false;
382         ++pos();
383         return true;
384 }
385
386
387 CursorSlice & LCursor::anchor()
388 {
389         return anchor_.back();
390 }
391
392
393 CursorSlice const & LCursor::anchor() const
394 {
395         return anchor_.back();
396 }
397
398
399 CursorSlice const & LCursor::selBegin() const
400 {
401         if (!selection())
402                 return cursor_.back();
403         // can't use std::min as this creates a new object
404         return anchor() < cursor_.back() ? anchor() : cursor_.back();
405 }
406
407
408 CursorSlice & LCursor::selBegin()
409 {
410         if (!selection())
411                 return cursor_.back();
412         return anchor() < cursor_.back() ? anchor() : cursor_.back();
413 }
414
415
416 CursorSlice const & LCursor::selEnd() const
417 {
418         if (!selection())
419                 return cursor_.back();
420         return anchor() > cursor_.back() ? anchor() : cursor_.back();
421 }
422
423
424 CursorSlice & LCursor::selEnd()
425 {
426         if (selection())
427                 return cursor_.back();
428         return anchor() > cursor_.back() ? anchor() : cursor_.back();
429 }
430
431
432 void LCursor::setSelection()
433 {
434         selection() = true;
435         // a selection with no contents is not a selection
436         if (par() == anchor().par() && pos() == anchor().pos())
437                 selection() = false;
438 }
439
440
441 void LCursor::setSelection(CursorBase const & where, size_t n)
442 {
443         selection() = true;
444         cursor_ = where;
445         anchor_ = where;
446         pos() += n;
447 }
448
449
450 void LCursor::clearSelection()
451 {
452         selection() = false;
453         mark() = false;
454         resetAnchor();
455         bv().unsetXSel();
456 }
457
458
459 int & LCursor::x_target()
460 {
461         return x_target_;
462 }
463
464
465 int LCursor::x_target() const
466 {
467         return x_target_;
468 }
469
470
471 void LCursor::clearTargetX()
472 {
473         x_target_ = -1;
474 }
475
476
477 LyXText * LCursor::text() const
478 {
479         return current_ ? current().text() : bv_->text();
480 }
481
482
483 Paragraph & LCursor::paragraph()
484 {
485         return current_ ? current().paragraph() : *bv_->text()->getPar(par());
486 }
487
488
489 Paragraph const & LCursor::paragraph() const
490 {
491         return current_ ? current().paragraph() : *bv_->text()->getPar(par());
492 }
493
494
495 LCursor::pos_type LCursor::lastpos() const
496 {
497         InsetBase * inset = current().inset();
498         return inset && inset->asMathInset() ? cell().size() : paragraph().size();
499 }
500
501
502 size_t LCursor::nargs() const
503 {
504         // assume 1x1 grid for 'plain text'
505         return current_ ? current().nargs() : 1;
506 }
507
508
509 size_t LCursor::ncols() const
510 {
511         // assume 1x1 grid for 'plain text'
512         return current_ ? current().ncols() : 1;
513 }
514
515
516 size_t LCursor::nrows() const
517 {
518         // assume 1x1 grid for 'plain text'
519         return current_ ? current().nrows() : 1;
520 }
521
522
523 LCursor::row_type LCursor::row() const
524 {
525         BOOST_ASSERT(current_ > 0);
526         return current().row();
527 }
528
529
530 LCursor::col_type LCursor::col() const
531 {
532         BOOST_ASSERT(current_ > 0);
533         return current().col();
534 }
535
536
537 MathArray const & LCursor::cell() const
538 {
539         BOOST_ASSERT(current_ > 0);
540         return current().cell();
541 }
542
543
544 MathArray & LCursor::cell()
545 {
546         BOOST_ASSERT(current_ > 0);
547         return current().cell();
548 }
549
550
551 void LCursor::info(std::ostream & os)
552 {
553         for (int i = 1, n = depth(); i < n; ++i) {
554                 cursor_[i].inset()->infoize(os);
555                 os << "  ";
556         }
557 #warning FIXME
558         //if (pos() != 0)
559         //      prevAtom()->infoize2(os);
560         // overwite old message
561         os << "                    ";
562 }
563
564
565 namespace {
566
567 void region(CursorSlice const & i1, CursorSlice const & i2,
568         LCursor::row_type & r1, LCursor::row_type & r2,
569         LCursor::col_type & c1, LCursor::col_type & c2)
570 {
571         InsetBase * p = i1.inset();
572         c1 = p->col(i1.idx_);
573         c2 = p->col(i2.idx_);
574         if (c1 > c2)
575                 swap(c1, c2);
576         r1 = p->row(i1.idx_);
577         r2 = p->row(i2.idx_);
578         if (r1 > r2)
579                 swap(r1, r2);
580 }
581
582 }
583
584
585 string LCursor::grabSelection()
586 {
587         if (!selection())
588                 return string();
589
590         CursorSlice i1 = selBegin();
591         CursorSlice i2 = selEnd();
592
593         if (i1.idx_ == i2.idx_) {
594                 if (i1.inset()->asMathInset()) {
595                         MathArray::const_iterator it = i1.cell().begin();
596                         return asString(MathArray(it + i1.pos_, it + i2.pos_));
597                 } else {
598                         return "unknown selection 1";
599                 }
600         }
601
602         row_type r1, r2;
603         col_type c1, c2;
604         region(i1, i2, r1, r2, c1, c2);
605
606         string data;
607         if (i1.inset()->asMathInset()) {
608                 for (row_type row = r1; row <= r2; ++row) {
609                         if (row > r1)
610                                 data += "\\\\";
611                         for (col_type col = c1; col <= c2; ++col) {
612                                 if (col > c1)
613                                         data += '&';
614                                 data += asString(i1.asMathInset()->cell(i1.asMathInset()->index(row, col)));
615                         }
616                 }
617         } else {
618                 data = "unknown selection 2";
619         }
620         return data;
621 }
622
623
624 void LCursor::eraseSelection()
625 {
626         CursorSlice i1 = selBegin();
627         CursorSlice i2 = selEnd();
628 #warning FIXME
629         if (i1.inset()->asMathInset()) {
630                 if (i1.idx_ == i2.idx_) {
631                         i1.cell().erase(i1.pos_, i2.pos_);
632                 } else {
633                         MathInset * p = i1.asMathInset();
634                         row_type r1, r2;
635                         col_type c1, c2;
636                         region(i1, i2, r1, r2, c1, c2);
637                         for (row_type row = r1; row <= r2; ++row)
638                                 for (col_type col = c1; col <= c2; ++col)
639                                         p->cell(p->index(row, col)).clear();
640                 }
641                 current() = i1;
642         } else {
643                 lyxerr << "can't erase this selection 1" << endl;
644         }
645 }
646
647
648 string LCursor::grabAndEraseSelection()
649 {
650         if (!selection())
651                 return string();
652         string res = grabSelection();
653         eraseSelection();
654         selection() = false;
655         return res;
656 }
657
658
659 void LCursor::selClear()
660 {
661         resetAnchor();
662         clearSelection();
663 }
664
665
666 void LCursor::selCopy()
667 {
668         if (selection()) {
669                 theCutBuffer.push(grabSelection());
670                 selection() = false;
671         } else {
672                 //theCutBuffer.erase();
673         }
674 }
675
676
677 void LCursor::selCut()
678 {
679         theCutBuffer.push(grabAndEraseSelection());
680 }
681
682
683 void LCursor::selDel()
684 {
685         if (selection()) {
686                 eraseSelection();
687                 selection() = false;
688         }
689 }
690
691
692 void LCursor::selPaste(size_t n)
693 {
694         selClearOrDel();
695         if (n < theCutBuffer.size())
696                 paste(theCutBuffer[n]);
697         //grabSelection();
698         selection() = false;
699 }
700
701
702 void LCursor::selHandle(bool sel)
703 {
704         if (sel == selection())
705                 return;
706         resetAnchor();
707         selection() = sel;
708 }
709
710
711 void LCursor::selStart()
712 {
713         resetAnchor();
714         selection() = true;
715 }
716
717
718 void LCursor::selClearOrDel()
719 {
720         if (lyxrc.auto_region_delete)
721                 selDel();
722         else
723                 selection() = false;
724 }
725
726
727 std::ostream & operator<<(std::ostream & os, LCursor const & cur)
728 {
729         os << "\n";
730         for (size_t i = 0, n = cur.cursor_.size(); i != n; ++i)
731                 os << "  (" << cur.cursor_[i] << " | " << cur.anchor_[i] << "\n";
732         return os << "current: " << cur.current_ << endl;
733 }
734
735
736
737
738 //
739 // CursorBase
740 //
741
742
743 void increment(CursorBase & it)
744 {
745         CursorSlice & top = it.back();
746         MathArray & ar = top.asMathInset()->cell(top.idx_);
747
748         // move into the current inset if possible
749         // it is impossible for pos() == size()!
750         MathInset * n = 0;
751         if (top.pos() != top.lastpos())
752                 n = (ar.begin() + top.pos_)->nucleus();
753         if (n && n->isActive()) {
754                 it.push_back(CursorSlice(n));
755                 return;
756         }
757
758         // otherwise move on one cell back if possible
759         if (top.pos() < top.lastpos()) {
760                 // pos() == lastpos() is valid!
761                 ++top.pos_;
762                 return;
763         }
764
765         // otherwise try to move on one cell if possible
766         while (top.idx() < top.lastidx()) {
767                 ++top.idx_;
768                 if (top.asMathInset()->validCell(top.idx_)) {
769                         top.pos_ = 0;
770                         return;
771                 }
772         }
773
774         // otherwise leave array, move on one back
775         // this might yield pos() == size(), but that's a ok.
776         it.pop_back();
777         // it certainly invalidates top
778         ++it.back().pos_;
779 }
780
781
782 CursorBase ibegin(InsetBase * p)
783 {
784         CursorBase it;
785         it.push_back(CursorSlice(p));
786         return it;
787 }
788
789
790 CursorBase iend(InsetBase * p)
791 {
792         CursorBase it;
793         it.push_back(CursorSlice(p));
794         CursorSlice & cur = it.back();
795         cur.idx() = cur.lastidx();
796         cur.pos() = cur.lastpos();
797         return it;
798 }
799
800
801
802
803 ///////////////////////////////////////////////////////////////////
804 //
805 // The part below is the non-integrated rest of the original math
806 // cursor. This should be either generalized for texted or moved
807 // back to the math insets.
808 //
809 ///////////////////////////////////////////////////////////////////
810
811 #include "mathed/math_braceinset.h"
812 #include "mathed/math_charinset.h"
813 #include "mathed/math_commentinset.h"
814 #include "mathed/math_factory.h"
815 #include "mathed/math_gridinset.h"
816 #include "mathed/math_macroarg.h"
817 #include "mathed/math_macrotemplate.h"
818 #include "mathed/math_mathmlstream.h"
819 #include "mathed/math_scriptinset.h"
820 #include "mathed/math_spaceinset.h"
821 #include "mathed/math_support.h"
822 #include "mathed/math_unknowninset.h"
823
824 //#define FILEDEBUG 1
825
826
827 bool LCursor::isInside(InsetBase const * p)
828 {
829         for (unsigned i = 0; i < depth(); ++i)
830                 if (cursor_[i].inset() == p)
831                         return true;
832         return false;
833 }
834
835
836 bool LCursor::openable(MathAtom const & t)
837 {
838         if (!t->isActive())
839                 return false;
840
841         if (t->lock())
842                 return false;
843
844         if (!selection())
845                 return true;
846
847         // we can't move into anything new during selection
848         if (depth() == anchor_.size())
849                 return false;
850         if (t.nucleus() != anchor_[depth()].inset())
851                 return false;
852
853         return true;
854 }
855
856
857 bool LCursor::inNucleus()
858 {
859         return inset()->asMathInset()->asScriptInset() && idx() == 2;
860 }
861
862
863 bool LCursor::left()
864 {
865         autocorrect() = false;
866         clearTargetX();
867         if (inMacroMode()) {
868                 macroModeClose();
869                 return true;
870         }
871
872         if (pos() != 0 && openable(prevAtom())) {
873                 posLeft();
874                 push(nextAtom().nucleus());
875                 inset()->idxLast(*this);
876                 return true;
877         }
878
879         return posLeft() || idxLeft() || popLeft() || selection();
880 }
881
882
883 bool LCursor::right()
884 {
885         autocorrect() = false;
886         clearTargetX();
887         if (inMacroMode()) {
888                 macroModeClose();
889                 return true;
890         }
891
892         if (pos() != lastpos() && openable(nextAtom())) {
893                 pushLeft(nextAtom().nucleus());
894                 inset()->idxFirst(*this);
895                 return true;
896         }
897
898         return posRight() || idxRight() || popRight() || selection();
899 }
900
901
902 bool positionable(CursorBase const & cursor, CursorBase const & anchor)
903 {
904         // avoid deeper nested insets when selecting
905         if (cursor.size() > anchor.size())
906                 return false;
907
908         // anchor might be deeper, should have same path then
909         for (size_t i = 0; i < cursor.size(); ++i)
910                 if (cursor[i].inset() != anchor[i].inset())
911                         return false;
912
913         // position should be ok.
914         return true;
915 }
916
917
918 void LCursor::setScreenPos(int x, int y)
919 {
920         bool res = bruteFind(x, y, formula()->xlow(), formula()->xhigh(),
921                 formula()->ylow(), formula()->yhigh());
922         if (!res) {
923                 // this can happen on creation of "math-display"
924                 idx() = 0;
925                 pos() = 0;
926         }
927         clearTargetX();
928 }
929
930
931
932 bool LCursor::home()
933 {
934         autocorrect() = false;
935         macroModeClose();
936         if (!inset()->idxHome(*this))
937                 return popLeft();
938         clearTargetX();
939         return true;
940 }
941
942
943 bool LCursor::end()
944 {
945         autocorrect() = false;
946         macroModeClose();
947         if (!inset()->idxEnd(*this))
948                 return popRight();
949         clearTargetX();
950         return true;
951 }
952
953
954 void LCursor::plainErase()
955 {
956         cell().erase(pos());
957 }
958
959
960 void LCursor::markInsert()
961 {
962         cell().insert(pos(), MathAtom(new MathCharInset(0)));
963 }
964
965
966 void LCursor::markErase()
967 {
968         cell().erase(pos());
969 }
970
971
972 void LCursor::plainInsert(MathAtom const & t)
973 {
974         cell().insert(pos(), t);
975         ++pos();
976 }
977
978
979 void LCursor::insert2(string const & str)
980 {
981         MathArray ar;
982         asArray(str, ar);
983         insert(ar);
984 }
985
986
987 void LCursor::insert(string const & str)
988 {
989         lyxerr << "inserting '" << str << "'" << endl;
990         selClearOrDel();
991         for (string::const_iterator it = str.begin(); it != str.end(); ++it)
992                 plainInsert(MathAtom(new MathCharInset(*it)));
993 }
994
995
996 void LCursor::insert(char c)
997 {
998         lyxerr << "inserting '" << c << "'" << endl;
999         selClearOrDel();
1000         plainInsert(MathAtom(new MathCharInset(c)));
1001 }
1002
1003
1004 void LCursor::insert(MathAtom const & t)
1005 {
1006         macroModeClose();
1007         selClearOrDel();
1008         plainInsert(t);
1009 }
1010
1011
1012 void LCursor::niceInsert(string const & t)
1013 {
1014         lyxerr << "*** LCursor::niceInsert 1: " << t << endl;
1015         MathArray ar;
1016         asArray(t, ar);
1017         if (ar.size() == 1)
1018                 niceInsert(ar[0]);
1019         else
1020                 insert(ar);
1021 }
1022
1023
1024 void LCursor::niceInsert(MathAtom const & t)
1025 {
1026         lyxerr << "*** LCursor::niceInsert 2: " << t << endl;
1027         macroModeClose();
1028         string safe = grabAndEraseSelection();
1029         plainInsert(t);
1030         // enter the new inset and move the contents of the selection if possible
1031         if (t->isActive()) {
1032                 posLeft();
1033                 pushLeft(nextAtom().nucleus());
1034                 paste(safe);
1035         }
1036         lyxerr << "*** LCursor::niceInsert 3: " << t << endl;
1037 }
1038
1039
1040 void LCursor::insert(MathArray const & ar)
1041 {
1042         macroModeClose();
1043         if (selection())
1044                 eraseSelection();
1045         cell().insert(pos(), ar);
1046         pos() += ar.size();
1047 }
1048
1049
1050 bool LCursor::backspace()
1051 {
1052         autocorrect() = false;
1053
1054         if (selection()) {
1055                 selDel();
1056                 return true;
1057         }
1058
1059         if (pos() == 0) {
1060                 if (inset()->nargs() == 1 && depth() == 1 && lastpos() == 0)
1061                         return false;
1062                 pullArg();
1063                 return true;
1064         }
1065
1066         if (inMacroMode()) {
1067                 MathUnknownInset * p = activeMacro();
1068                 if (p->name().size() > 1) {
1069                         p->setName(p->name().substr(0, p->name().size() - 1));
1070                         return true;
1071                 }
1072         }
1073
1074         if (pos() != 0 && prevAtom()->nargs() > 0) {
1075                 // let's require two backspaces for 'big stuff' and
1076                 // highlight on the first
1077                 selection() = true;
1078                 left();
1079         } else {
1080                 --pos();
1081                 plainErase();
1082         }
1083         return true;
1084 }
1085
1086
1087 bool LCursor::erase()
1088 {
1089         autocorrect() = false;
1090         if (inMacroMode())
1091                 return true;
1092
1093         if (selection()) {
1094                 selDel();
1095                 return true;
1096         }
1097
1098         // delete empty cells if possible
1099 #warning FIXME
1100         //if (cell().empty() && inset()->idxDelete(idx()))
1101         //              return true;
1102
1103         // special behaviour when in last position of cell
1104         if (pos() == lastpos()) {
1105                 bool one_cell = inset()->nargs() == 1;
1106                 if (one_cell && depth() == 1 && lastpos() == 0)
1107                         return false;
1108                 // remove markup
1109                 if (one_cell)
1110                         pullArg();
1111                 else
1112                         inset()->idxGlue(idx());
1113                 return true;
1114         }
1115
1116         if (pos() != lastpos() && inset()->nargs() > 0) {
1117                 selection() = true;
1118                 right();
1119         } else {
1120                 plainErase();
1121         }
1122
1123         return true;
1124 }
1125
1126
1127 bool LCursor::up()
1128 {
1129         macroModeClose();
1130         CursorBase save = cursor_;
1131         if (goUpDown(true))
1132                 return true;
1133         cursor_ = save;
1134         autocorrect() = false;
1135         return selection();
1136 }
1137
1138
1139 bool LCursor::down()
1140 {
1141         macroModeClose();
1142         CursorBase save = cursor_;
1143         if (goUpDown(false))
1144                 return true;
1145         cursor_ = save;
1146         autocorrect() = false;
1147         return selection();
1148 }
1149
1150
1151 void LCursor::macroModeClose()
1152 {
1153         if (!inMacroMode())
1154                 return;
1155         MathUnknownInset * p = activeMacro();
1156         p->finalize();
1157         string s = p->name();
1158         --pos();
1159         cell().erase(pos());
1160
1161         // do nothing if the macro name is empty
1162         if (s == "\\")
1163                 return;
1164
1165         string const name = s.substr(1);
1166
1167         // prevent entering of recursive macros
1168         if (formula()->lyxCode() == InsetOld::MATHMACRO_CODE
1169                         && formula()->getInsetName() == name)
1170                 lyxerr << "can't enter recursive macro" << endl;
1171
1172         niceInsert(createMathInset(name));
1173 }
1174
1175
1176 string LCursor::macroName()
1177 {
1178         return inMacroMode() ? activeMacro()->name() : string();
1179 }
1180
1181
1182 void LCursor::drawSelection(PainterInfo & pi)
1183 {
1184         if (!selection())
1185                 return;
1186         CursorSlice i1 = selBegin();
1187         CursorSlice i2 = selEnd();
1188         i1.asMathInset()->drawSelection(pi, i1.idx_, i1.pos_, i2.idx_, i2.pos_);
1189 }
1190
1191
1192 void LCursor::handleNest(MathAtom const & a, int c)
1193 {
1194         MathAtom t = a;
1195         asArray(grabAndEraseSelection(), t.nucleus()->cell(c));
1196         insert(t);
1197         posLeft();
1198         pushLeft(t.nucleus());
1199 }
1200
1201
1202 int LCursor::targetX() const
1203 {
1204         if (x_target() != -1)
1205                 return x_target();
1206         int x = 0;
1207         int y = 0;
1208         getPos(x, y);
1209         return x;
1210 }
1211
1212
1213 MathHullInset * LCursor::formula() const
1214 {
1215         for (int i = cursor_.size() - 1; i >= 1; --i)
1216                 if (cursor_[i].inset()->lyxCode() == InsetBase::MATH_CODE)
1217                         return static_cast<MathHullInset *>(cursor_[i].inset());
1218         return 0;
1219 }
1220
1221
1222 void LCursor::adjust(pos_type from, int diff)
1223 {
1224         if (pos() > from)
1225                 pos() += diff;
1226         if (anchor().pos_ > from)
1227                 anchor().pos_ += diff;
1228         // just to be on the safe side
1229         // theoretically unecessary
1230         normalize();
1231 }
1232
1233
1234 bool LCursor::inMacroMode() const
1235 {
1236         if (!pos() != 0)
1237                 return false;
1238         MathUnknownInset const * p = prevAtom()->asUnknownInset();
1239         return p && !p->final();
1240 }
1241
1242
1243 MathUnknownInset * LCursor::activeMacro()
1244 {
1245         return inMacroMode() ? prevAtom().nucleus()->asUnknownInset() : 0;
1246 }
1247
1248
1249 bool LCursor::inMacroArgMode() const
1250 {
1251         return pos() > 0 && prevAtom()->getChar() == '#';
1252 }
1253
1254
1255 MathGridInset * LCursor::enclosingGrid(idx_type & idx) const
1256 {
1257         for (MathInset::difference_type i = depth() - 1; i >= 0; --i) {
1258                 MathInset * m = cursor_[i].inset()->asMathInset();
1259                 if (!m)
1260                         return 0;
1261                 MathGridInset * p = m->asGridInset();
1262                 if (p) {
1263                         idx = cursor_[i].idx_;
1264                         return p;
1265                 }
1266         }
1267         return 0;
1268 }
1269
1270
1271 void LCursor::pullArg()
1272 {
1273 #warning look here
1274 #if 0
1275         MathArray ar = cell();
1276         if (popLeft()) {
1277                 plainErase();
1278                 cell().insert(pos(), ar);
1279                 resetAnchor();
1280         } else {
1281                 formula()->mutateToText();
1282         }
1283 #endif
1284 }
1285
1286
1287 void LCursor::touch()
1288 {
1289 #warning look here
1290 #if 0
1291         CursorBase::const_iterator it = cursor_.begin();
1292         CursorBase::const_iterator et = cursor_.end();
1293         for ( ; it != et; ++it)
1294                 it->cell().touch();
1295 #endif
1296 }
1297
1298
1299 void LCursor::normalize()
1300 {
1301         if (idx() >= nargs()) {
1302                 lyxerr << "this should not really happen - 1: "
1303                        << idx() << ' ' << nargs()
1304                        << " in: " << inset() << endl;
1305         }
1306         idx() = min(idx(), nargs() - 1);
1307
1308         if (pos() > lastpos()) {
1309                 lyxerr << "this should not really happen - 2: "
1310                         << pos() << ' ' << lastpos() <<  " in idx: " << idx()
1311                        << " in atom: '";
1312                 WriteStream wi(lyxerr, false, true);
1313                 inset()->asMathInset()->write(wi);
1314                 lyxerr << endl;
1315         }
1316         pos() = min(pos(), lastpos());
1317 }
1318
1319
1320 char LCursor::valign()
1321 {
1322         idx_type idx;
1323         MathGridInset * p = enclosingGrid(idx);
1324         return p ? p->valign() : '\0';
1325 }
1326
1327
1328 char LCursor::halign()
1329 {
1330         idx_type idx;
1331         MathGridInset * p = enclosingGrid(idx);
1332         return p ? p->halign(idx % p->ncols()) : '\0';
1333 }
1334
1335
1336 bool LCursor::goUpDown(bool up)
1337 {
1338         // Be warned: The 'logic' implemented in this function is highly fragile.
1339         // A distance of one pixel or a '<' vs '<=' _really_ matters.
1340         // So fiddle around with it only if you know what you are doing!
1341   int xo = 0;
1342         int yo = 0;
1343         getPos(xo, yo);
1344
1345         // check if we had something else in mind, if not, this is the future goal
1346         if (x_target() == -1)
1347                 x_target() = xo;
1348         else
1349                 xo = x_target();
1350
1351         // try neigbouring script insets
1352         if (!selection()) {
1353                 // try left
1354                 if (pos() != 0) {
1355                         MathScriptInset const * p = prevAtom()->asScriptInset();
1356                         if (p && p->has(up)) {
1357                                 --pos();
1358                                 push(inset());
1359                                 idx() = up; // the superscript has index 1
1360                                 pos() = lastpos();
1361                                 //lyxerr << "updown: handled by scriptinset to the left" << endl;
1362                                 return true;
1363                         }
1364                 }
1365
1366                 // try right
1367                 if (pos() != lastpos()) {
1368                         MathScriptInset const * p = nextAtom()->asScriptInset();
1369                         if (p && p->has(up)) {
1370                                 push(inset());
1371                                 idx() = up;
1372                                 pos() = 0;
1373                                 //lyxerr << "updown: handled by scriptinset to the right" << endl;
1374                                 return true;
1375                         }
1376                 }
1377         }
1378
1379         // try current cell for e.g. text insets
1380         if (inset()->idxUpDown2(*this, up))
1381                 return true;
1382
1383         //xarray().boundingBox(xlow, xhigh, ylow, yhigh);
1384         //if (up)
1385         //      yhigh = yo - 4;
1386         //else
1387         //      ylow = yo + 4;
1388         //if (bruteFind(xo, yo, xlow, xhigh, ylow, yhigh)) {
1389         //      lyxerr << "updown: handled by brute find in the same cell" << endl;
1390         //      return true;
1391         //}
1392
1393         // try to find an inset that knows better then we
1394         while (1) {
1395                 //lyxerr << "updown: We are in " << inset() << " idx: " << idx() << endl;
1396                 // ask inset first
1397                 if (inset()->idxUpDown(*this, up)) {
1398                         // try to find best position within this inset
1399                         if (!selection())
1400                                 bruteFind2(xo, yo);
1401                         return true;
1402                 }
1403
1404                 // no such inset found, just take something "above"
1405                 //lyxerr << "updown: handled by strange case" << endl;
1406                 if (!popLeft()) {
1407                         return
1408                                 bruteFind(xo, yo,
1409                                         formula()->xlow(),
1410                                         formula()->xhigh(),
1411                                         up ? formula()->ylow() : yo + 4,
1412                                         up ? yo - 4 : formula()->yhigh()
1413                                 );
1414                 }
1415
1416                 // any improvement so far?
1417                 int xnew, ynew;
1418                 getPos(xnew, ynew);
1419                 if (up ? ynew < yo : ynew > yo)
1420                         return true;
1421         }
1422 }
1423
1424
1425 bool LCursor::bruteFind(int x, int y, int xlow, int xhigh, int ylow, int yhigh)
1426 {
1427         CursorBase best_cursor;
1428         double best_dist = 1e10;
1429
1430         CursorBase it = ibegin(formula());
1431         CursorBase et = iend(formula());
1432         while (1) {
1433                 // avoid invalid nesting when selecting
1434                 if (!selection() || positionable(it, anchor_)) {
1435                         int xo, yo;
1436                         CursorSlice & cur = it.back();
1437                         cur.inset()->getCursorPos(cur, xo, yo);
1438                         if (xlow <= xo && xo <= xhigh && ylow <= yo && yo <= yhigh) {
1439                                 double d = (x - xo) * (x - xo) + (y - yo) * (y - yo);
1440                                 //lyxerr << "x: " << x << " y: " << y << " d: " << endl;
1441                                 // '<=' in order to take the last possible position
1442                                 // this is important for clicking behind \sum in e.g. '\sum_i a'
1443                                 if (d <= best_dist) {
1444                                         best_dist   = d;
1445                                         best_cursor = it;
1446                                 }
1447                         }
1448                 }
1449
1450                 if (it == et)
1451                         break;
1452                 increment(it);
1453         }
1454
1455         if (best_dist < 1e10)
1456                 cursor_ = best_cursor;
1457         return best_dist < 1e10;
1458 }
1459
1460
1461 void LCursor::bruteFind2(int x, int y)
1462 {
1463         double best_dist = 1e10;
1464
1465         CursorBase it = cursor_;
1466         it.back().pos(0);
1467         CursorBase et = cursor_;
1468         int n = et.back().asMathInset()->cell(et.back().idx_).size();
1469         et.back().pos(n);
1470         for (int i = 0; ; ++i) {
1471                 int xo, yo;
1472                 CursorSlice & cur = it.back();
1473                 cur.inset()->getCursorPos(cur, xo, yo);
1474                 double d = (x - xo) * (x - xo) + (y - yo) * (y - yo);
1475                 // '<=' in order to take the last possible position
1476                 // this is important for clicking behind \sum in e.g. '\sum_i a'
1477                 lyxerr << "i: " << i << " d: " << d << " best: " << best_dist << endl;
1478                 if (d <= best_dist) {
1479                         best_dist = d;
1480                         cursor_ = it;
1481                 }
1482                 if (it == et)
1483                         break;
1484                 increment(it);
1485         }
1486 }
1487
1488
1489 bool LCursor::idxLineLast()
1490 {
1491         idx() -= idx() % ncols();
1492         idx() += ncols() - 1;
1493         pos() = lastpos();
1494         return true;
1495 }
1496
1497
1498 bool LCursor::idxLeft()
1499 {
1500         return inset()->idxLeft(*this);
1501 }
1502
1503
1504 bool LCursor::idxRight()
1505 {
1506         return inset()->idxRight(*this);
1507 }
1508
1509
1510 bool LCursor::script(bool up)
1511 {
1512         // Hack to get \\^ and \\_ working
1513         if (inMacroMode() && macroName() == "\\") {
1514                 if (up)
1515                         niceInsert(createMathInset("mathcircumflex"));
1516                 else
1517                         interpret('_');
1518                 return true;
1519         }
1520
1521         macroModeClose();
1522         string safe = grabAndEraseSelection();
1523         if (inNucleus()) {
1524                 // we are in a nucleus of a script inset, move to _our_ script
1525                 inset()->asMathInset()->asScriptInset()->ensure(up);
1526                 idx() = up;
1527                 pos() = 0;
1528         } else if (pos() != 0 && prevAtom()->asScriptInset()) {
1529                 prevAtom().nucleus()->asScriptInset()->ensure(up);
1530                 posLeft();
1531                 push(inset());
1532                 idx() = up;
1533                 pos() = lastpos();
1534         } else if (pos() != 0) {
1535                 --pos();
1536                 cell()[pos()]
1537                         = MathAtom(new MathScriptInset(nextAtom(), up));
1538                 push(inset());
1539                 idx() = up;
1540                 pos() = 0;
1541         } else {
1542                 plainInsert(MathAtom(new MathScriptInset(up)));
1543                 posLeft();
1544                 nextAtom().nucleus()->asScriptInset()->ensure(up);
1545                 push(inset());
1546                 idx() = up;
1547                 pos() = 0;
1548         }
1549         paste(safe);
1550         return true;
1551 }
1552
1553
1554 bool LCursor::interpret(char c)
1555 {
1556         //lyxerr << "interpret 2: '" << c << "'" << endl;
1557         clearTargetX();
1558         if (inMacroArgMode()) {
1559                 posLeft();
1560                 plainErase();
1561 #warning FIXME
1562 #if 0
1563                 int n = c - '0';
1564                 MathMacroTemplate const * p = formula()->asMacroTemplate();
1565                 if (p && 1 <= n && n <= p->numargs())
1566                         insert(MathAtom(new MathMacroArgument(c - '0')));
1567                 else {
1568                         insert(createMathInset("#"));
1569                         interpret(c); // try again
1570                 }
1571 #endif
1572                 return true;
1573         }
1574
1575         // handle macroMode
1576         if (inMacroMode()) {
1577                 string name = macroName();
1578                 //lyxerr << "interpret name: '" << name << "'" << endl;
1579
1580                 if (isalpha(c)) {
1581                         activeMacro()->setName(activeMacro()->name() + c);
1582                         return true;
1583                 }
1584
1585                 // handle 'special char' macros
1586                 if (name == "\\") {
1587                         // remove the '\\'
1588                         backspace();
1589                         if (c == '\\') {
1590                                 if (currentMode() == MathInset::TEXT_MODE)
1591                                         niceInsert(createMathInset("textbackslash"));
1592                                 else
1593                                         niceInsert(createMathInset("backslash"));
1594                         } else if (c == '{') {
1595                                 niceInsert(MathAtom(new MathBraceInset));
1596                         } else {
1597                                 niceInsert(createMathInset(string(1, c)));
1598                         }
1599                         return true;
1600                 }
1601
1602                 // leave macro mode and try again if necessary
1603                 macroModeClose();
1604                 if (c == '{')
1605                         niceInsert(MathAtom(new MathBraceInset));
1606                 else if (c != ' ')
1607                         interpret(c);
1608                 return true;
1609         }
1610
1611         // This is annoying as one has to press <space> far too often.
1612         // Disable it.
1613
1614         if (0) {
1615                 // leave autocorrect mode if necessary
1616                 if (autocorrect() && c == ' ') {
1617                         autocorrect() = false;
1618                         return true;
1619                 }
1620         }
1621
1622         // just clear selection on pressing the space bar
1623         if (selection() && c == ' ') {
1624                 selection() = false;
1625                 return true;
1626         }
1627
1628         selClearOrDel();
1629
1630         if (c == '\\') {
1631                 //lyxerr << "starting with macro" << endl;
1632                 insert(MathAtom(new MathUnknownInset("\\", false)));
1633                 return true;
1634         }
1635
1636         if (c == '\n') {
1637                 if (currentMode() == MathInset::TEXT_MODE)
1638                         insert(c);
1639                 return true;
1640         }
1641
1642         if (c == ' ') {
1643                 if (currentMode() == MathInset::TEXT_MODE) {
1644                         // insert spaces in text mode,
1645                         // but suppress direct insertion of two spaces in a row
1646                         // the still allows typing  '<space>a<space>' and deleting the 'a', but
1647                         // it is better than nothing...
1648                         if (!pos() != 0 || prevAtom()->getChar() != ' ')
1649                                 insert(c);
1650                         return true;
1651                 }
1652                 if (pos() != 0 && prevAtom()->asSpaceInset()) {
1653                         prevAtom().nucleus()->asSpaceInset()->incSpace();
1654                         return true;
1655                 }
1656                 if (popRight())
1657                         return true;
1658                 // if are at the very end, leave the formula
1659                 return pos() != lastpos();
1660         }
1661
1662         if (c == '_') {
1663                 script(false);
1664                 return true;
1665         }
1666
1667         if (c == '^') {
1668                 script(true);
1669                 return true;
1670         }
1671
1672         if (c == '{' || c == '}' || c == '#' || c == '&' || c == '$') {
1673                 niceInsert(createMathInset(string(1, c)));
1674                 return true;
1675         }
1676
1677         if (c == '%') {
1678                 niceInsert(MathAtom(new MathCommentInset));
1679                 return true;
1680         }
1681
1682         // try auto-correction
1683         //if (autocorrect() && hasPrevAtom() && math_autocorrect(prevAtom(), c))
1684         //      return true;
1685
1686         // no special circumstances, so insert the character without any fuss
1687         insert(c);
1688         autocorrect() = true;
1689         return true;
1690 }
1691
1692
1693 void LCursor::lockToggle()
1694 {
1695         if (pos() != lastpos()) {
1696                 // toggle previous inset ...
1697                 nextAtom().nucleus()->lock(!nextAtom()->lock());
1698         } else if (popLeft() && pos() != lastpos()) {
1699                 // ... or enclosing inset if we are in the last inset position
1700                 nextAtom().nucleus()->lock(!nextAtom()->lock());
1701                 posRight();
1702         }
1703 }
1704
1705
1706 CursorSlice LCursor::normalAnchor()
1707 {
1708         if (anchor_.size() < depth()) {
1709                 resetAnchor();
1710                 lyxerr << "unusual Anchor size" << endl;
1711         }
1712         //lyx::BOOST_ASSERT(Anchor_.size() >= cursor.depth());
1713         // use Anchor on the same level as Cursor
1714         CursorSlice normal = anchor_[current_];
1715 #if 0
1716         if (depth() < anchor_.size() && !(normal < xx())) {
1717                 // anchor is behind cursor -> move anchor behind the inset
1718                 ++normal.pos_;
1719         }
1720 #endif
1721         return normal;
1722 }
1723
1724
1725 /*
1726 DispatchResult dispatch(LCursor & cur, FuncRequest const & cmd)
1727 {
1728         // mouse clicks are somewhat special
1729         // check
1730         switch (cmd.action) {
1731         case LFUN_MOUSE_PRESS:
1732         case LFUN_MOUSE_MOTION:
1733         case LFUN_MOUSE_RELEASE:
1734         case LFUN_MOUSE_DOUBLE: {
1735                 CursorSlice & pos = cursor_.back();
1736                 int x = 0;
1737                 int y = 0;
1738                 getPos(x, y);
1739                 if (x < cmd.x && pos() != 0) {
1740                         DispatchResult const res = prevAtom().nucleus()->dispatch(cmd);
1741                         if (res.dispatched())
1742                                 return res;
1743                 }
1744                 if (x > cmd.x && pos() != lastpos()) {
1745                         DispatchResult const res = inset()->dispatch(cmd);
1746                         if (res.dispatched())
1747                                 return res;
1748                 }
1749         }
1750         default:
1751         break;
1752         }
1753 }
1754 */
1755
1756
1757 void LCursor::handleFont(string const & font)
1758 {
1759         string safe;
1760         if (selection()) {
1761                 macroModeClose();
1762                 safe = grabAndEraseSelection();
1763         }
1764
1765         if (lastpos() != 0) {
1766                 // something left in the cell
1767                 if (pos() == 0) {
1768                         // cursor in first position
1769                         popLeft();
1770                 } else if (pos() == lastpos()) {
1771                         // cursor in last position
1772                         popRight();
1773                 } else {
1774                         // cursor in between. split cell
1775                         MathArray::iterator bt = cell().begin();
1776                         MathAtom at = createMathInset(font);
1777                         at.nucleus()->cell(0) = MathArray(bt, bt + pos());
1778                         cell().erase(bt, bt + pos());
1779                         popLeft();
1780                         plainInsert(at);
1781                 }
1782         } else {
1783                 // nothing left in the cell
1784                 pullArg();
1785                 plainErase();
1786         }
1787         insert(safe);
1788 }
1789
1790
1791 void LCursor::releaseMathCursor()
1792 {
1793         if (inMathed())
1794                 formula()->insetUnlock(bv());
1795 }
1796
1797
1798 bool LCursor::inMathed() const
1799 {
1800         return formula();
1801 }