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