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