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