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