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