]> git.lyx.org Git - features.git/blob - src/cursor.C
bug + spped fixes + small stuff
[features.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 LCursor::par_type LCursor::lastpar() const
486 {
487         return inMathed() ? 0 : text()->paragraphs().size() - 1;
488 }
489
490
491 LCursor::pos_type LCursor::lastpos() const
492 {
493         InsetBase * inset = current().inset();
494         return inset && inset->asMathInset() ? cell().size() : paragraph().size();
495 }
496
497
498 LCursor::row_type LCursor::crow() const
499 {
500         return paragraph().row(pos());
501 }
502
503
504 LCursor::row_type LCursor::lastcrow() const
505 {
506         return paragraph().rows.size();
507 }
508
509
510 size_t LCursor::nargs() const
511 {
512         // assume 1x1 grid for 'plain text'
513         return current_ ? current().nargs() : 1;
514 }
515
516
517 size_t LCursor::ncols() const
518 {
519         // assume 1x1 grid for 'plain text'
520         return current_ ? current().ncols() : 1;
521 }
522
523
524 size_t LCursor::nrows() const
525 {
526         // assume 1x1 grid for 'plain text'
527         return current_ ? current().nrows() : 1;
528 }
529
530
531 LCursor::row_type LCursor::row() const
532 {
533         BOOST_ASSERT(current_ > 0);
534         return current().row();
535 }
536
537
538 LCursor::col_type LCursor::col() const
539 {
540         BOOST_ASSERT(current_ > 0);
541         return current().col();
542 }
543
544
545 MathArray const & LCursor::cell() const
546 {
547         BOOST_ASSERT(current_ > 0);
548         return current().cell();
549 }
550
551
552 MathArray & LCursor::cell()
553 {
554         BOOST_ASSERT(current_ > 0);
555         return current().cell();
556 }
557
558
559 void LCursor::info(std::ostream & os)
560 {
561         for (int i = 1, n = depth(); i < n; ++i) {
562                 cursor_[i].inset()->infoize(os);
563                 os << "  ";
564         }
565         if (pos() != 0)
566                 prevInset()->infoize2(os);
567         // overwite old message
568         os << "                    ";
569 }
570
571
572 namespace {
573
574 void region(CursorSlice const & i1, CursorSlice const & i2,
575         LCursor::row_type & r1, LCursor::row_type & r2,
576         LCursor::col_type & c1, LCursor::col_type & c2)
577 {
578         InsetBase * p = i1.inset();
579         c1 = p->col(i1.idx_);
580         c2 = p->col(i2.idx_);
581         if (c1 > c2)
582                 swap(c1, c2);
583         r1 = p->row(i1.idx_);
584         r2 = p->row(i2.idx_);
585         if (r1 > r2)
586                 swap(r1, r2);
587 }
588
589 }
590
591
592 string LCursor::grabSelection()
593 {
594         if (!selection())
595                 return string();
596
597         CursorSlice i1 = selBegin();
598         CursorSlice i2 = selEnd();
599
600         if (i1.idx_ == i2.idx_) {
601                 if (i1.inset()->asMathInset()) {
602                         MathArray::const_iterator it = i1.cell().begin();
603                         return asString(MathArray(it + i1.pos_, it + i2.pos_));
604                 } else {
605                         return "unknown selection 1";
606                 }
607         }
608
609         row_type r1, r2;
610         col_type c1, c2;
611         region(i1, i2, r1, r2, c1, c2);
612
613         string data;
614         if (i1.inset()->asMathInset()) {
615                 for (row_type row = r1; row <= r2; ++row) {
616                         if (row > r1)
617                                 data += "\\\\";
618                         for (col_type col = c1; col <= c2; ++col) {
619                                 if (col > c1)
620                                         data += '&';
621                                 data += asString(i1.asMathInset()->cell(i1.asMathInset()->index(row, col)));
622                         }
623                 }
624         } else {
625                 data = "unknown selection 2";
626         }
627         return data;
628 }
629
630
631 void LCursor::eraseSelection()
632 {
633         //lyxerr << "LCursor::eraseSelection" << endl;
634         CursorSlice const & i1 = selBegin();
635         CursorSlice const & i2 = selEnd();
636 #warning FIXME
637         if (i1.inset()->asMathInset()) {
638                 if (i1.idx_ == i2.idx_) {
639                         i1.cell().erase(i1.pos_, i2.pos_);
640                 } else {
641                         MathInset * p = i1.asMathInset();
642                         row_type r1, r2;
643                         col_type c1, c2;
644                         region(i1, i2, r1, r2, c1, c2);
645                         for (row_type row = r1; row <= r2; ++row)
646                                 for (col_type col = c1; col <= c2; ++col)
647                                         p->cell(p->index(row, col)).clear();
648                 }
649                 current() = i1;
650         } else {
651                 lyxerr << "can't erase this selection 1" << endl;
652         }
653         //lyxerr << "LCursor::eraseSelection end" << endl;
654 }
655
656
657 string LCursor::grabAndEraseSelection()
658 {
659         if (!selection())
660                 return string();
661         string res = grabSelection();
662         eraseSelection();
663         selection() = false;
664         return res;
665 }
666
667
668 void LCursor::selClear()
669 {
670         resetAnchor();
671         clearSelection();
672 }
673
674
675 void LCursor::selCopy()
676 {
677         if (selection()) {
678                 theCutBuffer.push(grabSelection());
679                 selection() = false;
680         } else {
681                 //theCutBuffer.erase();
682         }
683 }
684
685
686 void LCursor::selCut()
687 {
688         theCutBuffer.push(grabAndEraseSelection());
689 }
690
691
692 void LCursor::selDel()
693 {
694         //lyxerr << "LCursor::selDel" << endl;
695         if (selection()) {
696                 eraseSelection();
697                 selection() = false;
698         }
699 }
700
701
702 void LCursor::selPaste(size_t n)
703 {
704         selClearOrDel();
705         if (n < theCutBuffer.size())
706                 paste(theCutBuffer[n]);
707         //grabSelection();
708         selection() = false;
709 }
710
711
712 void LCursor::selHandle(bool sel)
713 {
714         //lyxerr << "LCursor::selHandle" << endl;
715         if (sel == selection())
716                 return;
717         resetAnchor();
718         selection() = sel;
719 }
720
721
722 void LCursor::selClearOrDel()
723 {
724         //lyxerr << "LCursor::selClearOrDel" << endl;
725         if (lyxrc.auto_region_delete)
726                 selDel();
727         else
728                 selection() = false;
729 }
730
731
732 std::ostream & operator<<(std::ostream & os, LCursor const & cur)
733 {
734         for (size_t i = 0, n = cur.cursor_.size(); i != n; ++i)
735                 os << " " << cur.cursor_[i] << " | " << cur.anchor_[i] << "\n";
736         os << " current: " << cur.current_ << endl;
737         os << " selection: " << cur.selection_ << endl;
738         return os;
739 }
740
741
742
743
744 //
745 // CursorBase
746 //
747
748
749 void increment(CursorBase & it)
750 {
751         CursorSlice & top = it.back();
752         MathArray & ar = top.asMathInset()->cell(top.idx_);
753
754         // move into the current inset if possible
755         // it is impossible for pos() == size()!
756         MathInset * n = 0;
757         if (top.pos() != top.lastpos())
758                 n = (ar.begin() + top.pos_)->nucleus();
759         if (n && n->isActive()) {
760                 it.push_back(CursorSlice(n));
761                 return;
762         }
763
764         // otherwise move on one cell back if possible
765         if (top.pos() < top.lastpos()) {
766                 // pos() == lastpos() is valid!
767                 ++top.pos_;
768                 return;
769         }
770
771         // otherwise try to move on one cell if possible
772         while (top.idx() < top.lastidx()) {
773                 ++top.idx_;
774                 if (top.asMathInset()->validCell(top.idx_)) {
775                         top.pos_ = 0;
776                         return;
777                 }
778         }
779
780         // otherwise leave array, move on one back
781         // this might yield pos() == size(), but that's a ok.
782         it.pop_back();
783         // it certainly invalidates top
784         ++it.back().pos_;
785 }
786
787
788 CursorBase ibegin(InsetBase * p)
789 {
790         CursorBase it;
791         it.push_back(CursorSlice(p));
792         return it;
793 }
794
795
796 CursorBase iend(InsetBase * p)
797 {
798         CursorBase it;
799         it.push_back(CursorSlice(p));
800         CursorSlice & cur = it.back();
801         cur.idx() = cur.lastidx();
802         cur.pos() = cur.lastpos();
803         return it;
804 }
805
806
807
808
809 ///////////////////////////////////////////////////////////////////
810 //
811 // The part below is the non-integrated rest of the original math
812 // cursor. This should be either generalized for texted or moved
813 // back to the math insets.
814 //
815 ///////////////////////////////////////////////////////////////////
816
817 #include "mathed/math_braceinset.h"
818 #include "mathed/math_charinset.h"
819 #include "mathed/math_commentinset.h"
820 #include "mathed/math_factory.h"
821 #include "mathed/math_gridinset.h"
822 #include "mathed/math_macroarg.h"
823 #include "mathed/math_macrotemplate.h"
824 #include "mathed/math_mathmlstream.h"
825 #include "mathed/math_scriptinset.h"
826 #include "mathed/math_spaceinset.h"
827 #include "mathed/math_support.h"
828 #include "mathed/math_unknowninset.h"
829
830 //#define FILEDEBUG 1
831
832
833 bool LCursor::isInside(InsetBase const * p)
834 {
835         for (unsigned i = 0; i < depth(); ++i)
836                 if (cursor_[i].inset() == p)
837                         return true;
838         return false;
839 }
840
841
842 bool LCursor::openable(MathAtom const & t)
843 {
844         if (!t->isActive())
845                 return false;
846
847         if (t->lock())
848                 return false;
849
850         if (!selection())
851                 return true;
852
853         // we can't move into anything new during selection
854         if (depth() == anchor_.size())
855                 return false;
856         if (!ptr_cmp(t.nucleus(), anchor_[depth()].inset()))
857                 return false;
858
859         return true;
860 }
861
862
863 bool LCursor::inNucleus()
864 {
865         return inset()->asMathInset()->asScriptInset() && idx() == 2;
866 }
867
868
869 bool LCursor::left()
870 {
871         autocorrect() = false;
872         clearTargetX();
873         if (inMacroMode()) {
874                 macroModeClose();
875                 return true;
876         }
877
878         if (pos() != 0 && openable(prevAtom())) {
879                 posLeft();
880                 push(nextAtom().nucleus());
881                 inset()->idxLast(*this);
882                 return true;
883         }
884
885         return posLeft() || idxLeft() || popLeft() || selection();
886 }
887
888
889 bool LCursor::right()
890 {
891         autocorrect() = false;
892         clearTargetX();
893         if (inMacroMode()) {
894                 macroModeClose();
895                 return true;
896         }
897
898         if (pos() != lastpos() && openable(nextAtom())) {
899                 pushLeft(nextAtom().nucleus());
900                 inset()->idxFirst(*this);
901                 return true;
902         }
903
904         return posRight() || idxRight() || popRight() || selection();
905 }
906
907
908 bool positionable(CursorBase const & cursor, CursorBase const & anchor)
909 {
910         // avoid deeper nested insets when selecting
911         if (cursor.size() > anchor.size())
912                 return false;
913
914         // anchor might be deeper, should have same path then
915         for (size_t i = 0; i < cursor.size(); ++i)
916                 if (cursor[i].inset() != anchor[i].inset())
917                         return false;
918
919         // position should be ok.
920         return true;
921 }
922
923
924 void LCursor::setScreenPos(int x, int y)
925 {
926         bool res = bruteFind(x, y, formula()->xlow(), formula()->xhigh(),
927                 formula()->ylow(), formula()->yhigh());
928         if (!res) {
929                 // this can happen on creation of "math-display"
930                 idx() = 0;
931                 pos() = 0;
932         }
933         clearTargetX();
934 }
935
936
937
938 bool LCursor::home()
939 {
940         autocorrect() = false;
941         macroModeClose();
942         if (!inset()->idxHome(*this))
943                 return popLeft();
944         clearTargetX();
945         return true;
946 }
947
948
949 bool LCursor::end()
950 {
951         autocorrect() = false;
952         macroModeClose();
953         if (!inset()->idxEnd(*this))
954                 return popRight();
955         clearTargetX();
956         return true;
957 }
958
959
960 void LCursor::plainErase()
961 {
962         cell().erase(pos());
963 }
964
965
966 void LCursor::markInsert()
967 {
968         cell().insert(pos(), MathAtom(new MathCharInset(0)));
969 }
970
971
972 void LCursor::markErase()
973 {
974         cell().erase(pos());
975 }
976
977
978 void LCursor::plainInsert(MathAtom const & t)
979 {
980         cell().insert(pos(), t);
981         ++pos();
982 }
983
984
985 void LCursor::insert(string const & str)
986 {
987         lyxerr << "LCursor::insert str '" << str << "'" << endl;
988         selClearOrDel();
989 #if 0
990         for (string::const_iterator it = str.begin(); it != str.end(); ++it)
991                 plainInsert(MathAtom(new MathCharInset(*it)));
992 #else
993         MathArray ar;
994         asArray(str, ar);
995         insert(ar);
996 #endif
997 }
998
999
1000 void LCursor::insert(char c)
1001 {
1002         //lyxerr << "LCursor::insert char '" << c << "'" << endl;
1003         selClearOrDel();
1004         plainInsert(MathAtom(new MathCharInset(c)));
1005 }
1006
1007
1008 void LCursor::insert(MathAtom const & t)
1009 {
1010         //lyxerr << "LCursor::insert MathAtom: " << endl;
1011         macroModeClose();
1012         selClearOrDel();
1013         plainInsert(t);
1014 }
1015
1016
1017 void LCursor::insert(InsetBase * inset)
1018 {
1019         if (inMathed())
1020                 insert(MathAtom(inset));
1021         else
1022                 text()->insertInset(inset);
1023 }
1024
1025
1026 void LCursor::niceInsert(string const & t)
1027 {
1028         MathArray ar;
1029         asArray(t, ar);
1030         if (ar.size() == 1)
1031                 niceInsert(ar[0]);
1032         else
1033                 insert(ar);
1034 }
1035
1036
1037 void LCursor::niceInsert(MathAtom const & t)
1038 {
1039         macroModeClose();
1040         string safe = grabAndEraseSelection();
1041         plainInsert(t);
1042         // enter the new inset and move the contents of the selection if possible
1043         if (t->isActive()) {
1044                 posLeft();
1045                 // be careful here: don't use 'pushLeft(t)' as this we need to
1046                 // push the clone, not the original
1047                 pushLeft(nextAtom().nucleus());
1048                 paste(safe);
1049         }
1050 }
1051
1052
1053 void LCursor::insert(MathArray const & ar)
1054 {
1055         macroModeClose();
1056         if (selection())
1057                 eraseSelection();
1058         cell().insert(pos(), ar);
1059         pos() += ar.size();
1060 }
1061
1062
1063 bool LCursor::backspace()
1064 {
1065         autocorrect() = false;
1066
1067         if (selection()) {
1068                 selDel();
1069                 return true;
1070         }
1071
1072         if (pos() == 0) {
1073                 if (inset()->nargs() == 1 && depth() == 1 && lastpos() == 0)
1074                         return false;
1075                 pullArg();
1076                 return true;
1077         }
1078
1079         if (inMacroMode()) {
1080                 MathUnknownInset * p = activeMacro();
1081                 if (p->name().size() > 1) {
1082                         p->setName(p->name().substr(0, p->name().size() - 1));
1083                         return true;
1084                 }
1085         }
1086
1087         if (pos() != 0 && prevAtom()->nargs() > 0) {
1088                 // let's require two backspaces for 'big stuff' and
1089                 // highlight on the first
1090                 selection() = true;
1091                 left();
1092         } else {
1093                 --pos();
1094                 plainErase();
1095         }
1096         return true;
1097 }
1098
1099
1100 bool LCursor::erase()
1101 {
1102         autocorrect() = false;
1103         if (inMacroMode())
1104                 return true;
1105
1106         if (selection()) {
1107                 selDel();
1108                 return true;
1109         }
1110
1111         // delete empty cells if possible
1112         if (pos() == lastpos() && inset()->idxDelete(idx()))
1113                 return true;
1114
1115         // special behaviour when in last position of cell
1116         if (pos() == lastpos()) {
1117                 bool one_cell = inset()->nargs() == 1;
1118                 if (one_cell && depth() == 1 && lastpos() == 0)
1119                         return false;
1120                 // remove markup
1121                 if (one_cell)
1122                         pullArg();
1123                 else
1124                         inset()->idxGlue(idx());
1125                 return true;
1126         }
1127
1128         if (pos() != lastpos() && inset()->nargs() > 0) {
1129                 selection() = true;
1130                 right();
1131         } else {
1132                 plainErase();
1133         }
1134
1135         return true;
1136 }
1137
1138
1139 bool LCursor::up()
1140 {
1141         macroModeClose();
1142         CursorBase save = cursor_;
1143         if (goUpDown(true))
1144                 return true;
1145         cursor_ = save;
1146         autocorrect() = false;
1147         return selection();
1148 }
1149
1150
1151 bool LCursor::down()
1152 {
1153         macroModeClose();
1154         CursorBase save = cursor_;
1155         if (goUpDown(false))
1156                 return true;
1157         cursor_ = save;
1158         autocorrect() = false;
1159         return selection();
1160 }
1161
1162
1163 void LCursor::macroModeClose()
1164 {
1165         if (!inMacroMode())
1166                 return;
1167         MathUnknownInset * p = activeMacro();
1168         p->finalize();
1169         string s = p->name();
1170         --pos();
1171         cell().erase(pos());
1172
1173         // do nothing if the macro name is empty
1174         if (s == "\\")
1175                 return;
1176
1177         string const name = s.substr(1);
1178
1179         // prevent entering of recursive macros
1180         if (formula()->lyxCode() == InsetOld::MATHMACRO_CODE
1181                         && formula()->getInsetName() == name)
1182                 lyxerr << "can't enter recursive macro" << endl;
1183
1184         niceInsert(createMathInset(name));
1185 }
1186
1187
1188 string LCursor::macroName()
1189 {
1190         return inMacroMode() ? activeMacro()->name() : string();
1191 }
1192
1193
1194 void LCursor::handleNest(MathAtom const & a, int c)
1195 {
1196         //lyxerr << "LCursor::handleNest: " << c << endl;
1197         MathAtom t = a;
1198         asArray(grabAndEraseSelection(), t.nucleus()->cell(c));
1199         insert(t);
1200         posLeft();
1201         pushLeft(nextAtom().nucleus());
1202 }
1203
1204
1205 int LCursor::targetX() const
1206 {
1207         if (x_target() != -1)
1208                 return x_target();
1209         int x = 0;
1210         int y = 0;
1211         getPos(x, y);
1212         return x;
1213 }
1214
1215
1216 MathHullInset * LCursor::formula() const
1217 {
1218         for (int i = cursor_.size() - 1; i >= 1; --i) {
1219                 MathInset * inset = cursor_[i].inset()->asMathInset();
1220                 if (inset && inset->asHullInset())
1221                         return static_cast<MathHullInset *>(inset);
1222         }
1223         return 0;
1224 }
1225
1226
1227 void LCursor::adjust(pos_type from, int diff)
1228 {
1229         if (pos() > from)
1230                 pos() += diff;
1231         if (anchor().pos_ > from)
1232                 anchor().pos_ += diff;
1233         // just to be on the safe side
1234         // theoretically unecessary
1235         normalize();
1236 }
1237
1238
1239 bool LCursor::inMacroMode() const
1240 {
1241         if (!pos() != 0)
1242                 return false;
1243         MathUnknownInset const * p = prevAtom()->asUnknownInset();
1244         return p && !p->final();
1245 }
1246
1247
1248 MathUnknownInset * LCursor::activeMacro()
1249 {
1250         return inMacroMode() ? prevAtom().nucleus()->asUnknownInset() : 0;
1251 }
1252
1253
1254 bool LCursor::inMacroArgMode() const
1255 {
1256         return pos() > 0 && prevAtom()->getChar() == '#';
1257 }
1258
1259
1260 MathGridInset * LCursor::enclosingGrid(idx_type & idx) const
1261 {
1262         for (MathInset::difference_type i = depth() - 1; i >= 0; --i) {
1263                 MathInset * m = cursor_[i].inset()->asMathInset();
1264                 if (!m)
1265                         return 0;
1266                 MathGridInset * p = m->asGridInset();
1267                 if (p) {
1268                         idx = cursor_[i].idx_;
1269                         return p;
1270                 }
1271         }
1272         return 0;
1273 }
1274
1275
1276 void LCursor::pullArg()
1277 {
1278 #warning Look here
1279         MathArray ar = cell();
1280         if (popLeft() && inMathed()) {
1281                 plainErase();
1282                 cell().insert(pos(), ar);
1283                 resetAnchor();
1284         } else {
1285                 //formula()->mutateToText();
1286         }
1287 }
1288
1289
1290 void LCursor::touch()
1291 {
1292 #warning look here
1293 #if 0
1294         CursorBase::const_iterator it = cursor_.begin();
1295         CursorBase::const_iterator et = cursor_.end();
1296         for ( ; it != et; ++it)
1297                 it->cell().touch();
1298 #endif
1299 }
1300
1301
1302 void LCursor::normalize()
1303 {
1304         if (idx() >= nargs()) {
1305                 lyxerr << "this should not really happen - 1: "
1306                        << idx() << ' ' << nargs()
1307                        << " in: " << inset() << endl;
1308         }
1309         idx() = min(idx(), lastidx());
1310
1311         if (pos() > lastpos()) {
1312                 lyxerr << "this should not really happen - 2: "
1313                         << pos() << ' ' << lastpos() <<  " in idx: " << idx()
1314                        << " in atom: '";
1315                 WriteStream wi(lyxerr, false, true);
1316                 inset()->asMathInset()->write(wi);
1317                 lyxerr << endl;
1318         }
1319         pos() = min(pos(), lastpos());
1320 }
1321
1322
1323 char LCursor::valign()
1324 {
1325         idx_type idx;
1326         MathGridInset * p = enclosingGrid(idx);
1327         return p ? p->valign() : '\0';
1328 }
1329
1330
1331 char LCursor::halign()
1332 {
1333         idx_type idx;
1334         MathGridInset * p = enclosingGrid(idx);
1335         return p ? p->halign(idx % p->ncols()) : '\0';
1336 }
1337
1338
1339 bool LCursor::goUpDown(bool up)
1340 {
1341         // Be warned: The 'logic' implemented in this function is highly fragile.
1342         // A distance of one pixel or a '<' vs '<=' _really_ matters.
1343         // So fiddle around with it only if you know what you are doing!
1344   int xo = 0;
1345         int yo = 0;
1346         getPos(xo, yo);
1347
1348         // check if we had something else in mind, if not, this is the future goal
1349         if (x_target() == -1)
1350                 x_target() = xo;
1351         else
1352                 xo = x_target();
1353
1354         // try neigbouring script insets
1355         if (!selection()) {
1356                 // try left
1357                 if (pos() != 0) {
1358                         MathScriptInset const * p = prevAtom()->asScriptInset();
1359                         if (p && p->has(up)) {
1360                                 --pos();
1361                                 push(inset());
1362                                 idx() = up; // the superscript has index 1
1363                                 pos() = lastpos();
1364                                 //lyxerr << "updown: handled by scriptinset to the left" << endl;
1365                                 return true;
1366                         }
1367                 }
1368
1369                 // try right
1370                 if (pos() != lastpos()) {
1371                         MathScriptInset const * p = nextAtom()->asScriptInset();
1372                         if (p && p->has(up)) {
1373                                 push(inset());
1374                                 idx() = up;
1375                                 pos() = 0;
1376                                 //lyxerr << "updown: handled by scriptinset to the right" << endl;
1377                                 return true;
1378                         }
1379                 }
1380         }
1381
1382         // try current cell for e.g. text insets
1383         if (inset()->idxUpDown2(*this, up))
1384                 return true;
1385
1386         //xarray().boundingBox(xlow, xhigh, ylow, yhigh);
1387         //if (up)
1388         //      yhigh = yo - 4;
1389         //else
1390         //      ylow = yo + 4;
1391         //if (bruteFind(xo, yo, xlow, xhigh, ylow, yhigh)) {
1392         //      lyxerr << "updown: handled by brute find in the same cell" << endl;
1393         //      return true;
1394         //}
1395
1396         // try to find an inset that knows better then we
1397         while (1) {
1398                 //lyxerr << "updown: We are in " << inset() << " idx: " << idx() << endl;
1399                 // ask inset first
1400                 if (inset()->idxUpDown(*this, up)) {
1401                         // try to find best position within this inset
1402                         if (!selection())
1403                                 bruteFind2(xo, yo);
1404                         return true;
1405                 }
1406
1407                 // no such inset found, just take something "above"
1408                 //lyxerr << "updown: handled by strange case" << endl;
1409                 if (!popLeft()) {
1410                         return
1411                                 bruteFind(xo, yo,
1412                                         formula()->xlow(),
1413                                         formula()->xhigh(),
1414                                         up ? formula()->ylow() : yo + 4,
1415                                         up ? yo - 4 : formula()->yhigh()
1416                                 );
1417                 }
1418
1419                 // any improvement so far?
1420                 int xnew, ynew;
1421                 getPos(xnew, ynew);
1422                 if (up ? ynew < yo : ynew > yo)
1423                         return true;
1424         }
1425 }
1426
1427
1428 bool LCursor::bruteFind(int x, int y, int xlow, int xhigh, int ylow, int yhigh)
1429 {
1430         CursorBase best_cursor;
1431         double best_dist = 1e10;
1432
1433         CursorBase it = ibegin(formula());
1434         CursorBase et = iend(formula());
1435         while (1) {
1436                 // avoid invalid nesting when selecting
1437                 if (!selection() || positionable(it, anchor_)) {
1438                         int xo, yo;
1439                         CursorSlice & cur = it.back();
1440                         cur.inset()->getCursorPos(cur, xo, yo);
1441                         if (xlow <= xo && xo <= xhigh && ylow <= yo && yo <= yhigh) {
1442                                 double d = (x - xo) * (x - xo) + (y - yo) * (y - yo);
1443                                 //lyxerr << "x: " << x << " y: " << y << " d: " << endl;
1444                                 // '<=' in order to take the last possible position
1445                                 // this is important for clicking behind \sum in e.g. '\sum_i a'
1446                                 if (d <= best_dist) {
1447                                         best_dist   = d;
1448                                         best_cursor = it;
1449                                 }
1450                         }
1451                 }
1452
1453                 if (it == et)
1454                         break;
1455                 increment(it);
1456         }
1457
1458         if (best_dist < 1e10)
1459                 cursor_ = best_cursor;
1460         return best_dist < 1e10;
1461 }
1462
1463
1464 void LCursor::bruteFind2(int x, int y)
1465 {
1466         double best_dist = 1e10;
1467
1468         CursorBase it = cursor_;
1469         it.back().pos(0);
1470         CursorBase et = cursor_;
1471         int n = et.back().asMathInset()->cell(et.back().idx_).size();
1472         et.back().pos(n);
1473         for (int i = 0; ; ++i) {
1474                 int xo, yo;
1475                 CursorSlice & cur = it.back();
1476                 cur.inset()->getCursorPos(cur, xo, yo);
1477                 double d = (x - xo) * (x - xo) + (y - yo) * (y - yo);
1478                 // '<=' in order to take the last possible position
1479                 // this is important for clicking behind \sum in e.g. '\sum_i a'
1480                 lyxerr << "i: " << i << " d: " << d << " best: " << best_dist << endl;
1481                 if (d <= best_dist) {
1482                         best_dist = d;
1483                         cursor_ = it;
1484                 }
1485                 if (it == et)
1486                         break;
1487                 increment(it);
1488         }
1489 }
1490
1491
1492 bool LCursor::idxLineLast()
1493 {
1494         idx() -= idx() % ncols();
1495         idx() += ncols() - 1;
1496         pos() = lastpos();
1497         return true;
1498 }
1499
1500
1501 bool LCursor::idxLeft()
1502 {
1503         return inset()->idxLeft(*this);
1504 }
1505
1506
1507 bool LCursor::idxRight()
1508 {
1509         return inset()->idxRight(*this);
1510 }
1511
1512
1513 bool LCursor::script(bool up)
1514 {
1515         // Hack to get \\^ and \\_ working
1516         lyxerr << "handling script: up: " << up << endl;
1517         if (inMacroMode() && macroName() == "\\") {
1518                 if (up)
1519                         niceInsert(createMathInset("mathcircumflex"));
1520                 else
1521                         interpret('_');
1522                 return true;
1523         }
1524
1525         macroModeClose();
1526         string safe = grabAndEraseSelection();
1527         if (inNucleus()) {
1528                 // we are in a nucleus of a script inset, move to _our_ script
1529                 inset()->asMathInset()->asScriptInset()->ensure(up);
1530                 idx() = up;
1531                 pos() = 0;
1532         } else if (pos() != 0 && prevAtom()->asScriptInset()) {
1533                 --pos();
1534                 nextAtom().nucleus()->asScriptInset()->ensure(up);
1535                 push(nextInset());
1536                 idx() = up;
1537                 pos() = lastpos();
1538         } else if (pos() != 0) {
1539                 --pos();
1540                 cell()[pos()] = MathAtom(new MathScriptInset(nextAtom(), up));
1541                 push(nextInset());
1542                 idx() = up;
1543                 pos() = 0;
1544         } else {
1545                 plainInsert(MathAtom(new MathScriptInset(up)));
1546                 --pos();
1547                 nextAtom().nucleus()->asScriptInset()->ensure(up);
1548                 push(nextInset());
1549                 idx() = up;
1550                 pos() = 0;
1551         }
1552         paste(safe);
1553         return true;
1554 }
1555
1556
1557 bool LCursor::interpret(char c)
1558 {
1559         //lyxerr << "interpret 2: '" << c << "'" << endl;
1560         clearTargetX();
1561         if (inMacroArgMode()) {
1562                 posLeft();
1563                 plainErase();
1564 #warning FIXME
1565 #if 0
1566                 int n = c - '0';
1567                 MathMacroTemplate const * p = formula()->asMacroTemplate();
1568                 if (p && 1 <= n && n <= p->numargs())
1569                         insert(MathAtom(new MathMacroArgument(c - '0')));
1570                 else {
1571                         insert(createMathInset("#"));
1572                         interpret(c); // try again
1573                 }
1574 #endif
1575                 return true;
1576         }
1577
1578         // handle macroMode
1579         if (inMacroMode()) {
1580                 string name = macroName();
1581                 //lyxerr << "interpret name: '" << name << "'" << endl;
1582
1583                 if (isalpha(c)) {
1584                         activeMacro()->setName(activeMacro()->name() + c);
1585                         return true;
1586                 }
1587
1588                 // handle 'special char' macros
1589                 if (name == "\\") {
1590                         // remove the '\\'
1591                         backspace();
1592                         if (c == '\\') {
1593                                 if (currentMode() == MathInset::TEXT_MODE)
1594                                         niceInsert(createMathInset("textbackslash"));
1595                                 else
1596                                         niceInsert(createMathInset("backslash"));
1597                         } else if (c == '{') {
1598                                 niceInsert(MathAtom(new MathBraceInset));
1599                         } else {
1600                                 niceInsert(createMathInset(string(1, c)));
1601                         }
1602                         return true;
1603                 }
1604
1605                 // leave macro mode and try again if necessary
1606                 macroModeClose();
1607                 if (c == '{')
1608                         niceInsert(MathAtom(new MathBraceInset));
1609                 else if (c != ' ')
1610                         interpret(c);
1611                 return true;
1612         }
1613
1614         // This is annoying as one has to press <space> far too often.
1615         // Disable it.
1616
1617         if (0) {
1618                 // leave autocorrect mode if necessary
1619                 if (autocorrect() && c == ' ') {
1620                         autocorrect() = false;
1621                         return true;
1622                 }
1623         }
1624
1625         // just clear selection on pressing the space bar
1626         if (selection() && c == ' ') {
1627                 selection() = false;
1628                 return true;
1629         }
1630
1631         selClearOrDel();
1632
1633         if (c == '\\') {
1634                 //lyxerr << "starting with macro" << endl;
1635                 insert(MathAtom(new MathUnknownInset("\\", false)));
1636                 return true;
1637         }
1638
1639         if (c == '\n') {
1640                 if (currentMode() == MathInset::TEXT_MODE)
1641                         insert(c);
1642                 return true;
1643         }
1644
1645         if (c == ' ') {
1646                 if (currentMode() == MathInset::TEXT_MODE) {
1647                         // insert spaces in text mode,
1648                         // but suppress direct insertion of two spaces in a row
1649                         // the still allows typing  '<space>a<space>' and deleting the 'a', but
1650                         // it is better than nothing...
1651                         if (!pos() != 0 || prevAtom()->getChar() != ' ')
1652                                 insert(c);
1653                         return true;
1654                 }
1655                 if (pos() != 0 && prevAtom()->asSpaceInset()) {
1656                         prevAtom().nucleus()->asSpaceInset()->incSpace();
1657                         return true;
1658                 }
1659                 if (popRight())
1660                         return true;
1661                 // if are at the very end, leave the formula
1662                 return pos() != lastpos();
1663         }
1664
1665         if (c == '_') {
1666                 script(false);
1667                 return true;
1668         }
1669
1670         if (c == '^') {
1671                 script(true);
1672                 return true;
1673         }
1674
1675         if (c == '{' || c == '}' || c == '#' || c == '&' || c == '$') {
1676                 niceInsert(createMathInset(string(1, c)));
1677                 return true;
1678         }
1679
1680         if (c == '%') {
1681                 niceInsert(MathAtom(new MathCommentInset));
1682                 return true;
1683         }
1684
1685         // try auto-correction
1686         //if (autocorrect() && hasPrevAtom() && math_autocorrect(prevAtom(), c))
1687         //      return true;
1688
1689         // no special circumstances, so insert the character without any fuss
1690         insert(c);
1691         autocorrect() = true;
1692         return true;
1693 }
1694
1695
1696 void LCursor::lockToggle()
1697 {
1698         if (pos() != lastpos()) {
1699                 // toggle previous inset ...
1700                 nextAtom().nucleus()->lock(!nextAtom()->lock());
1701         } else if (popLeft() && pos() != lastpos()) {
1702                 // ... or enclosing inset if we are in the last inset position
1703                 nextAtom().nucleus()->lock(!nextAtom()->lock());
1704                 ++pos();
1705         }
1706 }
1707
1708
1709 CursorSlice LCursor::normalAnchor()
1710 {
1711         if (anchor_.size() < depth()) {
1712                 resetAnchor();
1713                 lyxerr << "unusual Anchor size" << endl;
1714         }
1715         //lyx::BOOST_ASSERT(Anchor_.size() >= cursor.depth());
1716         // use Anchor on the same level as Cursor
1717         CursorSlice normal = anchor_[current_];
1718 #if 0
1719         if (depth() < anchor_.size() && !(normal < xx())) {
1720                 // anchor is behind cursor -> move anchor behind the inset
1721                 ++normal.pos_;
1722         }
1723 #endif
1724         return normal;
1725 }
1726
1727
1728 /*
1729 DispatchResult dispatch(LCursor & cur, FuncRequest const & cmd)
1730 {
1731         // mouse clicks are somewhat special
1732         // check
1733         switch (cmd.action) {
1734         case LFUN_MOUSE_PRESS:
1735         case LFUN_MOUSE_MOTION:
1736         case LFUN_MOUSE_RELEASE:
1737         case LFUN_MOUSE_DOUBLE: {
1738                 CursorSlice & pos = cursor_.back();
1739                 int x = 0;
1740                 int y = 0;
1741                 getPos(x, y);
1742                 if (x < cmd.x && pos() != 0) {
1743                         DispatchResult const res = prevAtom().nucleus()->dispatch(cmd);
1744                         if (res.dispatched())
1745                                 return res;
1746                 }
1747                 if (x > cmd.x && pos() != lastpos()) {
1748                         DispatchResult const res = inset()->dispatch(cmd);
1749                         if (res.dispatched())
1750                                 return res;
1751                 }
1752         }
1753         default:
1754         break;
1755         }
1756 }
1757 */
1758
1759
1760 void LCursor::handleFont(string const & font)
1761 {
1762         lyxerr << "LCursor::handleFont: " << font << endl;
1763         string safe;
1764         if (selection()) {
1765                 macroModeClose();
1766                 safe = grabAndEraseSelection();
1767         }
1768
1769         if (lastpos() != 0) {
1770                 // something left in the cell
1771                 if (pos() == 0) {
1772                         // cursor in first position
1773                         popLeft();
1774                 } else if (pos() == lastpos()) {
1775                         // cursor in last position
1776                         popRight();
1777                 } else {
1778                         // cursor in between. split cell
1779                         MathArray::iterator bt = cell().begin();
1780                         MathAtom at = createMathInset(font);
1781                         at.nucleus()->cell(0) = MathArray(bt, bt + pos());
1782                         cell().erase(bt, bt + pos());
1783                         popLeft();
1784                         plainInsert(at);
1785                 }
1786         } else {
1787                 // nothing left in the cell
1788                 pullArg();
1789                 plainErase();
1790         }
1791         insert(safe);
1792 }
1793
1794
1795 void LCursor::releaseMathCursor()
1796 {
1797         if (inMathed())
1798                 formula()->insetUnlock(bv());
1799 }
1800
1801
1802 bool LCursor::inMathed() const
1803 {
1804         return formula();
1805 }
1806
1807
1808 bool LCursor::inTexted() const
1809 {
1810         return !formula();
1811 }
1812
1813
1814 InsetBase * LCursor::nextInset()
1815 {
1816         if (pos() == lastpos())
1817                 return 0;
1818         if (inMathed()) 
1819                 return nextAtom().nucleus();
1820         Paragraph & par = paragraph();
1821         return par.isInset(pos()) ? par.getInset(pos()) : 0;
1822 }
1823
1824
1825 InsetBase * LCursor::prevInset()
1826 {
1827         if (pos() == 0)
1828                 return 0;
1829         if (inMathed()) 
1830                 return prevAtom().nucleus();
1831         Paragraph & par = paragraph();
1832         return par.isInset(pos() - 1) ? par.getInset(pos() - 1) : 0;
1833 }
1834
1835
1836 void LCursor::message(string const & msg) const
1837 {
1838         bv().owner()->getLyXFunc().setMessage(msg);
1839 }
1840
1841
1842 void LCursor::errorMessage(string const & msg) const
1843 {
1844         bv().owner()->getLyXFunc().setErrorMessage(msg);
1845 }
1846
1847
1848 string LCursor::selectionAsString(bool label) const
1849 {
1850         if (!selection())
1851                 return string();
1852
1853         if (inTexted()) {
1854                 Buffer const & buffer = *bv().buffer();
1855
1856                 // should be const ...
1857                 ParagraphList::iterator startpit = text()->getPar(selBegin());
1858                 ParagraphList::iterator endpit = text()->getPar(selEnd());
1859                 size_t const startpos = selBegin().pos();
1860                 size_t const endpos = selEnd().pos();
1861
1862                 if (startpit == endpit)
1863                         return startpit->asString(buffer, startpos, endpos, label);
1864
1865                 // First paragraph in selection
1866                 string result =
1867                         startpit->asString(buffer, startpos, startpit->size(), label) + "\n\n";
1868
1869                 // The paragraphs in between (if any)
1870                 ParagraphList::iterator pit = startpit;
1871                 for (++pit; pit != endpit; ++pit)
1872                         result += pit->asString(buffer, 0, pit->size(), label) + "\n\n";
1873
1874                 // Last paragraph in selection
1875                 result += endpit->asString(buffer, 0, endpos, label);
1876
1877                 return result;
1878         }
1879
1880 #warning an mathed?
1881         return string();
1882 }