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