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