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