]> git.lyx.org Git - features.git/blob - src/cursor.C
the DocIterator stuff
[features.git] / src / cursor.C
1 /**
2  * \file cursor.C
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Alejandro Aguilar Sierra
7  * \author Alfredo Braunstein
8  * \author André Pönitz
9  *
10  * Full author contact details are available in file CREDITS.
11  */
12
13 #include <config.h>
14
15 #include "buffer.h"
16 #include "BufferView.h"
17 #include "cursor.h"
18 #include "debug.h"
19 #include "dispatchresult.h"
20 #include "funcrequest.h"
21 #include "iterators.h"
22 #include "lfuns.h"
23 #include "lyxfunc.h" // only for setMessage()
24 #include "lyxrc.h"
25 #include "lyxrow.h"
26 #include "lyxtext.h"
27 #include "paragraph.h"
28
29 #include "insets/updatableinset.h"
30 #include "insets/insettabular.h"
31 #include "insets/insettext.h"
32
33 #include "mathed/math_data.h"
34 #include "mathed/math_hullinset.h"
35 #include "mathed/math_support.h"
36
37 #include "support/limited_stack.h"
38 #include "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         : DocumentIterator(bv), anchor_(bv),
61           cached_y_(0), x_target_(-1), selection_(false), mark_(false)
62 {}
63
64
65 void LCursor::reset()
66 {
67         clear();
68         push_back(CursorSlice());
69         anchor_.clear();
70         anchor_.push_back(CursorSlice());
71         cached_y_ = 0;
72         clearTargetX();
73         selection_ = false;
74         mark_ = false;
75 }
76
77
78 void LCursor::setCursor(DocumentIterator const & cur, bool sel)
79 {
80         // this (intentionally) does not touch the anchor
81         DocumentIterator::operator=(cur);
82         selection() = sel;
83 }
84
85
86 DispatchResult LCursor::dispatch(FuncRequest const & cmd0)
87 {
88         lyxerr << "\nLCursor::dispatch: cmd: " << cmd0 << endl << *this << endl;
89         BOOST_ASSERT(pos() <= lastpos());
90         BOOST_ASSERT(idx() <= lastidx());
91         BOOST_ASSERT(par() <= lastpar());
92         FuncRequest cmd = cmd0;
93         nopop_ = false;
94         DocumentIterator orig = *this;
95         disp_.update(true);
96         disp_.val(NONE);
97         while (size() != 1) {
98                 // the inset's dispatch() is supposed to reset the update and
99                 // val flags if necessary 
100                 inset()->dispatch(*this, cmd);
101                 
102                 // "Mutate" the request for semi-handled requests that need
103                 // additional handling in outer levels.
104                 switch (disp_.val()) {
105                         case NONE:
106                                 // the inset handled the event fully
107                                 return DispatchResult(true, true);
108                         case FINISHED_LEFT:
109                                 // the inset handled the event partially
110                                 cmd = FuncRequest(LFUN_FINISHED_LEFT);
111                                 break;
112                         case FINISHED_RIGHT:
113                                 cmd = FuncRequest(LFUN_FINISHED_RIGHT);
114                                 break;
115                         case FINISHED_UP:
116                                 cmd = FuncRequest(LFUN_FINISHED_UP);
117                                 break;
118                         case FINISHED_DOWN:
119                                 cmd = FuncRequest(LFUN_FINISHED_DOWN);
120                                 break;
121                         default:
122                                 //lyxerr << "not handled on level " << depth()
123                                 //      << " val: " << disp_.val() << endl;
124                                 break;
125                 }
126         }
127         bv().text()->dispatch(*this, cmd);
128         if (nopop_)
129                 setCursor(orig, false);
130         //lyxerr << "   result: " << res.val() << endl;
131         return disp_;
132 }
133
134
135 bool LCursor::getStatus(FuncRequest const & cmd, FuncStatus & status)
136 {
137         lyxerr << "\nLCursor::getStatus: cmd: " << cmd << endl << *this << endl;
138         DocumentIterator orig = *this;
139         BOOST_ASSERT(pos() <= lastpos());
140         BOOST_ASSERT(idx() <= lastidx());
141         BOOST_ASSERT(par() <= lastpar());
142         for ( ; size() != 0; pop_back()) {
143                 // the inset's getStatus() will return 'true' if it made
144                 // a definitive decision on whether it want to handle the
145                 // request or not. The result of this decision is put into
146                 // the 'status' parameter.
147                 bool const res = inset()->getStatus(*this, cmd, status);
148                 if (res) {
149                         setCursor(orig, false);
150                         return true;
151                 }
152         }
153         bool const res = bv().text()->getStatus(*this, cmd, status);
154         setCursor(orig, false);
155         return res;
156 }
157
158
159 BufferView & LCursor::bv() const
160 {
161         return DocumentIterator::bv();
162 }
163
164
165 /*
166 void LCursor::pop(int depth)
167 {
168         while (int(size()) > depth + 1)
169                 pop();
170         lyxerr << "LCursor::pop() result: " << *this << endl;
171 }
172 */
173
174
175 void LCursor::pop()
176 {
177         BOOST_ASSERT(size() >= 1);
178         pop_back();
179         anchor_.pop_back();
180 }
181
182
183 void LCursor::push(InsetBase * p)
184 {
185         push_back(CursorSlice(p));
186 }
187
188
189 void LCursor::pushLeft(InsetBase * p)
190 {
191         BOOST_ASSERT(!empty());
192         //lyxerr << "Entering inset " << t << " left" << endl;
193         push(p);
194         p->idxFirst(*this);
195 }
196
197
198 bool LCursor::popLeft()
199 {
200         BOOST_ASSERT(!empty());
201         //lyxerr << "Leaving inset to the left" << endl;
202         if (depth() <= 1) {
203                 if (depth() == 1)
204                         inset()->notifyCursorLeaves(idx());
205                 return false;
206         }
207         inset()->notifyCursorLeaves(idx());
208         pop();
209         return true;
210 }
211
212
213 bool LCursor::popRight()
214 {
215         BOOST_ASSERT(!empty());
216         //lyxerr << "Leaving inset to the right" << endl;
217         if (depth() <= 1) {
218                 if (depth() == 1)
219                         inset()->notifyCursorLeaves(idx());
220                 return false;
221         }
222         inset()->notifyCursorLeaves(idx());
223         pop();
224         ++pos();
225         return true;
226 }
227
228
229 int LCursor::currentMode()
230 {
231         BOOST_ASSERT(!empty());
232         for (int i = size() - 1; i >= 1; --i) {
233                 int res = operator[](i).inset()->currentMode();
234                 if (res != MathInset::UNDECIDED_MODE)
235                         return res;
236         }
237         return MathInset::TEXT_MODE;
238 }
239
240
241 void LCursor::updatePos()
242 {
243         BOOST_ASSERT(!empty());
244         if (size() > 1)
245                 cached_y_ = bv().top_y() + back().inset()->yo();
246                 //cached_y_ = back().inset()->yo();
247 }
248
249
250 void LCursor::getDim(int & asc, int & des) const
251 {
252         BOOST_ASSERT(!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(!empty());
270         x = 0;
271         y = 0;
272         if (size() == 1) {
273                 x = bv().text()->cursorX(front());
274                 y = bv().text()->cursorY(front());
275         } else {
276                 if (!inset()) {
277                         lyxerr << "#### LCursor::getPos: " << *this << endl;
278                         BOOST_ASSERT(inset());
279                 }
280                 inset()->getCursorPos(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 void LCursor::resetAnchor()
304 {
305         anchor_ = *this;
306 }
307
308
309
310 bool LCursor::posLeft()
311 {
312         if (pos() == 0)
313                 return false;
314         --pos();
315         return true;
316 }
317
318
319 bool LCursor::posRight()
320 {
321         if (pos() == lastpos())
322                 return false;
323         ++pos();
324         return true;
325 }
326
327
328 CursorSlice & LCursor::anchor()
329 {
330         return anchor_.back();
331 }
332
333
334 CursorSlice const & LCursor::anchor() const
335 {
336         return anchor_.back();
337 }
338
339
340 CursorSlice const & LCursor::selBegin() const
341 {
342         if (!selection())
343                 return back();
344         return anchor() < back() ? anchor() : back();
345 }
346
347
348 CursorSlice & LCursor::selBegin()
349 {
350         if (!selection())
351                 return back();
352         // can't use std::min as this returns a const ref
353         return anchor() < back() ? anchor() : back();
354 }
355
356
357 CursorSlice const & LCursor::selEnd() const
358 {
359         if (!selection())
360                 return back();
361         return anchor() > back() ? anchor() : back();
362 }
363
364
365 CursorSlice & LCursor::selEnd()
366 {
367         if (!selection())
368                 return back();
369         // can't use std::min as this returns a const ref
370         return anchor() > back() ? anchor() : back();
371 }
372
373
374 void LCursor::setSelection()
375 {
376         selection() = true;
377         // a selection with no contents is not a selection
378         if (par() == anchor().par() && pos() == anchor().pos())
379                 selection() = false;
380 }
381
382
383 void LCursor::setSelection(DocumentIterator const & where, size_t n)
384 {
385         selection() = true;
386         setCursor(where, false);
387         anchor_ = where;
388         pos() += n;
389 }
390
391
392 void LCursor::clearSelection()
393 {
394         selection() = false;
395         mark() = false;
396         resetAnchor();
397         bv().unsetXSel();
398 }
399
400
401 int & LCursor::x_target()
402 {
403         return x_target_;
404 }
405
406
407 int LCursor::x_target() const
408 {
409         return x_target_;
410 }
411
412
413 void LCursor::clearTargetX()
414 {
415         x_target_ = -1;
416 }
417
418
419
420 void LCursor::info(std::ostream & os) const
421 {
422         for (int i = 1, n = depth(); i < n; ++i) {
423                 operator[](i).inset()->infoize(os);
424                 os << "  ";
425         }
426         if (pos() != 0)
427                 prevInset()->infoize2(os);
428         // overwite old message
429         os << "                    ";
430 }
431
432
433 namespace {
434
435 void region(CursorSlice const & i1, CursorSlice const & i2,
436         LCursor::row_type & r1, LCursor::row_type & r2,
437         LCursor::col_type & c1, LCursor::col_type & c2)
438 {
439         InsetBase * p = i1.inset();
440         c1 = p->col(i1.idx_);
441         c2 = p->col(i2.idx_);
442         if (c1 > c2)
443                 swap(c1, c2);
444         r1 = p->row(i1.idx_);
445         r2 = p->row(i2.idx_);
446         if (r1 > r2)
447                 swap(r1, r2);
448 }
449
450 }
451
452
453 string LCursor::grabSelection()
454 {
455         if (!selection())
456                 return string();
457
458         CursorSlice i1 = selBegin();
459         CursorSlice i2 = selEnd();
460
461         if (i1.idx_ == i2.idx_) {
462                 if (i1.inset()->asMathInset()) {
463                         MathArray::const_iterator it = i1.cell().begin();
464                         return asString(MathArray(it + i1.pos_, it + i2.pos_));
465                 } else {
466                         return "unknown selection 1";
467                 }
468         }
469
470         row_type r1, r2;
471         col_type c1, c2;
472         region(i1, i2, r1, r2, c1, c2);
473
474         string data;
475         if (i1.inset()->asMathInset()) {
476                 for (row_type row = r1; row <= r2; ++row) {
477                         if (row > r1)
478                                 data += "\\\\";
479                         for (col_type col = c1; col <= c2; ++col) {
480                                 if (col > c1)
481                                         data += '&';
482                                 data += asString(i1.asMathInset()->cell(i1.asMathInset()->index(row, col)));
483                         }
484                 }
485         } else {
486                 data = "unknown selection 2";
487         }
488         return data;
489 }
490
491
492 void LCursor::eraseSelection()
493 {
494         //lyxerr << "LCursor::eraseSelection" << endl;
495         CursorSlice const & i1 = selBegin();
496         CursorSlice const & i2 = selEnd();
497 #warning FIXME
498         if (i1.inset()->asMathInset()) {
499                 if (i1.idx_ == i2.idx_) {
500                         i1.cell().erase(i1.pos_, i2.pos_);
501                 } else {
502                         MathInset * p = i1.asMathInset();
503                         row_type r1, r2;
504                         col_type c1, c2;
505                         region(i1, i2, r1, r2, c1, c2);
506                         for (row_type row = r1; row <= r2; ++row)
507                                 for (col_type col = c1; col <= c2; ++col)
508                                         p->cell(p->index(row, col)).clear();
509                 }
510                 back() = i1;
511         } else {
512                 lyxerr << "can't erase this selection 1" << endl;
513         }
514         //lyxerr << "LCursor::eraseSelection end" << endl;
515 }
516
517
518 string LCursor::grabAndEraseSelection()
519 {
520         if (!selection())
521                 return string();
522         string res = grabSelection();
523         eraseSelection();
524         selection() = false;
525         return res;
526 }
527
528
529 void LCursor::selClear()
530 {
531         resetAnchor();
532         clearSelection();
533 }
534
535
536 void LCursor::selCopy()
537 {
538         if (selection()) {
539                 theCutBuffer.push(grabSelection());
540                 selection() = false;
541         } else {
542                 //theCutBuffer.erase();
543         }
544 }
545
546
547 void LCursor::selCut()
548 {
549         theCutBuffer.push(grabAndEraseSelection());
550 }
551
552
553 void LCursor::selDel()
554 {
555         //lyxerr << "LCursor::selDel" << endl;
556         if (selection()) {
557                 eraseSelection();
558                 selection() = false;
559         }
560 }
561
562
563 void LCursor::selPaste(size_t n)
564 {
565         selClearOrDel();
566         if (n < theCutBuffer.size())
567                 paste(theCutBuffer[n]);
568         //grabSelection();
569         selection() = false;
570 }
571
572
573 void LCursor::selHandle(bool sel)
574 {
575         //lyxerr << "LCursor::selHandle" << endl;
576         if (sel == selection())
577                 return;
578         resetAnchor();
579         selection() = sel;
580 }
581
582
583 void LCursor::selClearOrDel()
584 {
585         //lyxerr << "LCursor::selClearOrDel" << endl;
586         if (lyxrc.auto_region_delete)
587                 selDel();
588         else
589                 selection() = false;
590 }
591
592
593 std::ostream & operator<<(std::ostream & os, LCursor const & cur)
594 {
595         for (size_t i = 0, n = cur.size(); i != n; ++i)
596                 os << " " << cur.operator[](i) << " | " << cur.anchor_[i] << "\n";
597         os << " selection: " << cur.selection_ << endl;
598         return os;
599 }
600
601
602
603
604 ///////////////////////////////////////////////////////////////////
605 //
606 // The part below is the non-integrated rest of the original math
607 // cursor. This should be either generalized for texted or moved
608 // back to mathed (in most cases to MathNestInset).
609 //
610 ///////////////////////////////////////////////////////////////////
611
612 #include "mathed/math_charinset.h"
613 #include "mathed/math_factory.h"
614 #include "mathed/math_gridinset.h"
615 #include "mathed/math_macroarg.h"
616 #include "mathed/math_macrotemplate.h"
617 #include "mathed/math_mathmlstream.h"
618 #include "mathed/math_scriptinset.h"
619 #include "mathed/math_support.h"
620 #include "mathed/math_unknowninset.h"
621
622 //#define FILEDEBUG 1
623
624
625 bool LCursor::isInside(InsetBase const * p)
626 {
627         for (unsigned i = 0; i < depth(); ++i)
628                 if (operator[](i).inset() == p)
629                         return true;
630         return false;
631 }
632
633
634 bool LCursor::openable(MathAtom const & t) const
635 {
636         if (!t->isActive())
637                 return false;
638
639         if (t->lock())
640                 return false;
641
642         if (!selection())
643                 return true;
644
645         // we can't move into anything new during selection
646         if (depth() == anchor_.size())
647                 return false;
648         if (!ptr_cmp(t.nucleus(), anchor_[depth()].inset()))
649                 return false;
650
651         return true;
652 }
653
654
655 bool positionable(DocumentIterator const & cursor,
656         DocumentIterator const & anchor)
657 {
658         // avoid deeper nested insets when selecting
659         if (cursor.size() > anchor.size())
660                 return false;
661
662         // anchor might be deeper, should have same path then
663         for (size_t i = 0; i < cursor.size(); ++i)
664                 if (cursor[i].inset() != anchor[i].inset())
665                         return false;
666
667         // position should be ok.
668         return true;
669 }
670
671
672 void LCursor::setScreenPos(int x, int y)
673 {
674         bool res = bruteFind(x, y, formula()->xlow(), formula()->xhigh(),
675                 formula()->ylow(), formula()->yhigh());
676         if (!res) {
677                 // this can happen on creation of "math-display"
678                 idx() = 0;
679                 pos() = 0;
680         }
681         clearTargetX();
682 }
683
684
685
686 void LCursor::plainErase()
687 {
688         cell().erase(pos());
689 }
690
691
692 void LCursor::markInsert()
693 {
694         cell().insert(pos(), MathAtom(new MathCharInset(0)));
695 }
696
697
698 void LCursor::markErase()
699 {
700         cell().erase(pos());
701 }
702
703
704 void LCursor::plainInsert(MathAtom const & t)
705 {
706         cell().insert(pos(), t);
707         ++pos();
708 }
709
710
711 void LCursor::insert(string const & str)
712 {
713         lyxerr << "LCursor::insert str '" << str << "'" << endl;
714         selClearOrDel();
715 #if 0
716         for (string::const_iterator it = str.begin(); it != str.end(); ++it)
717                 plainInsert(MathAtom(new MathCharInset(*it)));
718 #else
719         MathArray ar;
720         asArray(str, ar);
721         insert(ar);
722 #endif
723 }
724
725
726 void LCursor::insert(char c)
727 {
728         //lyxerr << "LCursor::insert char '" << c << "'" << endl;
729         selClearOrDel();
730         plainInsert(MathAtom(new MathCharInset(c)));
731 }
732
733
734 void LCursor::insert(MathAtom const & t)
735 {
736         //lyxerr << "LCursor::insert MathAtom: " << endl;
737         macroModeClose();
738         selClearOrDel();
739         plainInsert(t);
740 }
741
742
743 void LCursor::insert(InsetBase * inset)
744 {
745         if (inMathed())
746                 insert(MathAtom(inset));
747         else
748                 text()->insertInset(*this, inset);
749 }
750
751
752 void LCursor::niceInsert(string const & t)
753 {
754         MathArray ar;
755         asArray(t, ar);
756         if (ar.size() == 1)
757                 niceInsert(ar[0]);
758         else
759                 insert(ar);
760 }
761
762
763 void LCursor::niceInsert(MathAtom const & t)
764 {
765         macroModeClose();
766         string safe = grabAndEraseSelection();
767         plainInsert(t);
768         // enter the new inset and move the contents of the selection if possible
769         if (t->isActive()) {
770                 posLeft();
771                 // be careful here: don't use 'pushLeft(t)' as this we need to
772                 // push the clone, not the original
773                 pushLeft(nextAtom().nucleus());
774                 paste(safe);
775         }
776 }
777
778
779 void LCursor::insert(MathArray const & ar)
780 {
781         macroModeClose();
782         if (selection())
783                 eraseSelection();
784         cell().insert(pos(), ar);
785         pos() += ar.size();
786 }
787
788
789 bool LCursor::backspace()
790 {
791         autocorrect() = false;
792
793         if (selection()) {
794                 selDel();
795                 return true;
796         }
797
798         if (pos() == 0) {
799                 if (inset()->nargs() == 1 && depth() == 1 && lastpos() == 0)
800                         return false;
801                 pullArg();
802                 return true;
803         }
804
805         if (inMacroMode()) {
806                 MathUnknownInset * p = activeMacro();
807                 if (p->name().size() > 1) {
808                         p->setName(p->name().substr(0, p->name().size() - 1));
809                         return true;
810                 }
811         }
812
813         if (pos() != 0 && prevAtom()->nargs() > 0) {
814                 // let's require two backspaces for 'big stuff' and
815                 // highlight on the first
816                 selection() = true;
817                 --pos();
818         } else {
819                 --pos();
820                 plainErase();
821         }
822         return true;
823 }
824
825
826 bool LCursor::erase()
827 {
828         autocorrect() = false;
829         if (inMacroMode())
830                 return true;
831
832         if (selection()) {
833                 selDel();
834                 return true;
835         }
836
837         // delete empty cells if possible
838         if (pos() == lastpos() && inset()->idxDelete(idx()))
839                 return true;
840
841         // special behaviour when in last position of cell
842         if (pos() == lastpos()) {
843                 bool one_cell = inset()->nargs() == 1;
844                 if (one_cell && depth() == 1 && lastpos() == 0)
845                         return false;
846                 // remove markup
847                 if (one_cell)
848                         pullArg();
849                 else
850                         inset()->idxGlue(idx());
851                 return true;
852         }
853
854         if (pos() != lastpos() && inset()->nargs() > 0) {
855                 selection() = true;
856                 ++pos();
857         } else {
858                 plainErase();
859         }
860
861         return true;
862 }
863
864
865 bool LCursor::up()
866 {
867         macroModeClose();
868         DocumentIterator save = *this;
869         if (goUpDown(true))
870                 return true;
871         setCursor(save, false);
872         autocorrect() = false;
873         return selection();
874 }
875
876
877 bool LCursor::down()
878 {
879         macroModeClose();
880         DocumentIterator save = *this;
881         if (goUpDown(false))
882                 return true;
883         setCursor(save, false);
884         autocorrect() = false;
885         return selection();
886 }
887
888
889 void LCursor::macroModeClose()
890 {
891         if (!inMacroMode())
892                 return;
893         MathUnknownInset * p = activeMacro();
894         p->finalize();
895         string s = p->name();
896         --pos();
897         cell().erase(pos());
898
899         // do nothing if the macro name is empty
900         if (s == "\\")
901                 return;
902
903         string const name = s.substr(1);
904
905         // prevent entering of recursive macros
906         InsetBase const * macro = innerInsetOfType(InsetBase::MATHMACRO_CODE);
907         if (macro && macro->getInsetName() == name)
908                 lyxerr << "can't enter recursive macro" << endl;
909
910         niceInsert(createMathInset(name));
911 }
912
913
914 string LCursor::macroName()
915 {
916         return inMacroMode() ? activeMacro()->name() : string();
917 }
918
919
920 void LCursor::handleNest(MathAtom const & a, int c)
921 {
922         //lyxerr << "LCursor::handleNest: " << c << endl;
923         MathAtom t = a;
924         asArray(grabAndEraseSelection(), t.nucleus()->cell(c));
925         insert(t);
926         posLeft();
927         pushLeft(nextAtom().nucleus());
928 }
929
930
931 int LCursor::targetX() const
932 {
933         if (x_target() != -1)
934                 return x_target();
935         int x = 0;
936         int y = 0;
937         getPos(x, y);
938         return x;
939 }
940
941
942 MathHullInset * LCursor::formula() const
943 {
944         for (int i = size() - 1; i >= 1; --i) {
945                 MathInset * inset = operator[](i).inset()->asMathInset();
946                 if (inset && inset->asHullInset())
947                         return static_cast<MathHullInset *>(inset);
948         }
949         return 0;
950 }
951
952
953 void LCursor::adjust(pos_type from, int diff)
954 {
955         if (pos() > from)
956                 pos() += diff;
957         if (anchor().pos_ > from)
958                 anchor().pos_ += diff;
959         // just to be on the safe side
960         // theoretically unecessary
961         normalize();
962 }
963
964
965 bool LCursor::inMacroMode() const
966 {
967         if (!pos() != 0)
968                 return false;
969         MathUnknownInset const * p = prevAtom()->asUnknownInset();
970         return p && !p->final();
971 }
972
973
974 MathUnknownInset * LCursor::activeMacro()
975 {
976         return inMacroMode() ? prevAtom().nucleus()->asUnknownInset() : 0;
977 }
978
979
980 bool LCursor::inMacroArgMode() const
981 {
982         return pos() > 0 && prevAtom()->getChar() == '#';
983 }
984
985
986 MathGridInset * LCursor::enclosingGrid(idx_type & idx) const
987 {
988         for (MathInset::difference_type i = depth() - 1; i >= 0; --i) {
989                 MathInset * m = operator[](i).inset()->asMathInset();
990                 if (!m)
991                         return 0;
992                 MathGridInset * p = m->asGridInset();
993                 if (p) {
994                         idx = operator[](i).idx_;
995                         return p;
996                 }
997         }
998         return 0;
999 }
1000
1001
1002 void LCursor::pullArg()
1003 {
1004 #warning Look here
1005         MathArray ar = cell();
1006         if (popLeft() && inMathed()) {
1007                 plainErase();
1008                 cell().insert(pos(), ar);
1009                 resetAnchor();
1010         } else {
1011                 //formula()->mutateToText();
1012         }
1013 }
1014
1015
1016 void LCursor::touch()
1017 {
1018 #warning look here
1019 #if 0
1020         DocumentIterator::const_iterator it = begin();
1021         DocumentIterator::const_iterator et = end();
1022         for ( ; it != et; ++it)
1023                 it->cell().touch();
1024 #endif
1025 }
1026
1027
1028 void LCursor::normalize()
1029 {
1030         if (idx() >= nargs()) {
1031                 lyxerr << "this should not really happen - 1: "
1032                        << idx() << ' ' << nargs()
1033                        << " in: " << inset() << endl;
1034         }
1035         idx() = min(idx(), lastidx());
1036
1037         if (pos() > lastpos()) {
1038                 lyxerr << "this should not really happen - 2: "
1039                         << pos() << ' ' << lastpos() <<  " in idx: " << idx()
1040                        << " in atom: '";
1041                 WriteStream wi(lyxerr, false, true);
1042                 inset()->asMathInset()->write(wi);
1043                 lyxerr << endl;
1044         }
1045         pos() = min(pos(), lastpos());
1046 }
1047
1048
1049 char LCursor::valign()
1050 {
1051         idx_type idx;
1052         MathGridInset * p = enclosingGrid(idx);
1053         return p ? p->valign() : '\0';
1054 }
1055
1056
1057 char LCursor::halign()
1058 {
1059         idx_type idx;
1060         MathGridInset * p = enclosingGrid(idx);
1061         return p ? p->halign(idx % p->ncols()) : '\0';
1062 }
1063
1064
1065 bool LCursor::goUpDown(bool up)
1066 {
1067         // Be warned: The 'logic' implemented in this function is highly
1068         // fragile. A distance of one pixel or a '<' vs '<=' _really
1069         // matters. So fiddle around with it only if you think you know
1070         // what you are doing!
1071   int xo = 0;
1072         int yo = 0;
1073         getPos(xo, yo);
1074
1075         // check if we had something else in mind, if not, this is the future goal
1076         if (x_target() == -1)
1077                 x_target() = xo;
1078         else
1079                 xo = x_target();
1080
1081         // try neigbouring script insets
1082         if (!selection()) {
1083                 // try left
1084                 if (pos() != 0) {
1085                         MathScriptInset const * p = prevAtom()->asScriptInset();
1086                         if (p && p->has(up)) {
1087                                 --pos();
1088                                 push(inset());
1089                                 idx() = up; // the superscript has index 1
1090                                 pos() = lastpos();
1091                                 //lyxerr << "updown: handled by scriptinset to the left" << endl;
1092                                 return true;
1093                         }
1094                 }
1095
1096                 // try right
1097                 if (pos() != lastpos()) {
1098                         MathScriptInset const * p = nextAtom()->asScriptInset();
1099                         if (p && p->has(up)) {
1100                                 push(inset());
1101                                 idx() = up;
1102                                 pos() = 0;
1103                                 //lyxerr << "updown: handled by scriptinset to the right" << endl;
1104                                 return true;
1105                         }
1106                 }
1107         }
1108
1109         // try current cell for e.g. text insets
1110         if (inset()->idxUpDown2(*this, up))
1111                 return true;
1112
1113         //xarray().boundingBox(xlow, xhigh, ylow, yhigh);
1114         //if (up)
1115         //      yhigh = yo - 4;
1116         //else
1117         //      ylow = yo + 4;
1118         //if (bruteFind(xo, yo, xlow, xhigh, ylow, yhigh)) {
1119         //      lyxerr << "updown: handled by brute find in the same cell" << endl;
1120         //      return true;
1121         //}
1122
1123         // try to find an inset that knows better then we
1124         while (1) {
1125                 //lyxerr << "updown: We are in " << inset() << " idx: " << idx() << endl;
1126                 // ask inset first
1127                 if (inset()->idxUpDown(*this, up)) {
1128                         // try to find best position within this inset
1129                         if (!selection())
1130                                 bruteFind2(xo, yo);
1131                         return true;
1132                 }
1133
1134                 // no such inset found, just take something "above"
1135                 //lyxerr << "updown: handled by strange case" << endl;
1136                 if (!popLeft()) {
1137                         return
1138                                 bruteFind(xo, yo,
1139                                         formula()->xlow(),
1140                                         formula()->xhigh(),
1141                                         up ? formula()->ylow() : yo + 4,
1142                                         up ? yo - 4 : formula()->yhigh()
1143                                 );
1144                 }
1145
1146                 // any improvement so far?
1147                 int xnew, ynew;
1148                 getPos(xnew, ynew);
1149                 if (up ? ynew < yo : ynew > yo)
1150                         return true;
1151         }
1152 }
1153
1154
1155 bool LCursor::bruteFind(int x, int y, int xlow, int xhigh, int ylow, int yhigh)
1156 {
1157         DocumentIterator best_cursor(bv());
1158         double best_dist = 1e10;
1159
1160         DocumentIterator it = bufferBegin(bv());
1161         DocumentIterator et = bufferEnd();
1162         while (1) {
1163                 // avoid invalid nesting when selecting
1164                 if (!selection() || positionable(it, anchor_)) {
1165                         int xo, yo;
1166                         CursorSlice & cur = it.back();
1167                         cur.inset()->getCursorPos(cur, xo, yo);
1168                         if (xlow <= xo && xo <= xhigh && ylow <= yo && yo <= yhigh) {
1169                                 double d = (x - xo) * (x - xo) + (y - yo) * (y - yo);
1170                                 //lyxerr << "x: " << x << " y: " << y << " d: " << endl;
1171                                 // '<=' in order to take the last possible position
1172                                 // this is important for clicking behind \sum in e.g. '\sum_i a'
1173                                 if (d <= best_dist) {
1174                                         best_dist   = d;
1175                                         best_cursor = it;
1176                                 }
1177                         }
1178                 }
1179
1180                 if (it == et)
1181                         break;
1182                 it.forwardPos();
1183         }
1184
1185         if (best_dist < 1e10)
1186                 setCursor(best_cursor, false);
1187         return best_dist < 1e10;
1188 }
1189
1190
1191 void LCursor::bruteFind2(int x, int y)
1192 {
1193         double best_dist = 1e10;
1194
1195         DocumentIterator it = *this;
1196         it.back().pos() = 0;
1197         DocumentIterator et = *this;
1198         et.back().pos() = et.back().asMathInset()->cell(et.back().idx_).size();
1199         for (int i = 0; ; ++i) {
1200                 int xo, yo;
1201                 CursorSlice & cur = it.back();
1202                 cur.inset()->getCursorPos(cur, xo, yo);
1203                 double d = (x - xo) * (x - xo) + (y - yo) * (y - yo);
1204                 // '<=' in order to take the last possible position
1205                 // this is important for clicking behind \sum in e.g. '\sum_i a'
1206                 lyxerr << "i: " << i << " d: " << d << " best: " << best_dist << endl;
1207                 if (d <= best_dist) {
1208                         best_dist = d;
1209                         setCursor(it, false);
1210                 }
1211                 if (it == et)
1212                         break;
1213                 it.forwardPos();
1214         }
1215 }
1216
1217
1218 CursorSlice LCursor::normalAnchor()
1219 {
1220         if (anchor_.size() < depth()) {
1221                 resetAnchor();
1222                 lyxerr << "unusual Anchor size" << endl;
1223         }
1224         //lyx::BOOST_ASSERT(Anchor_.size() >= cursor.depth());
1225         // use Anchor on the same level as Cursor
1226         CursorSlice normal = anchor_[size() - 1];
1227 #if 0
1228         if (depth() < anchor_.size() && !(normal < xx())) {
1229                 // anchor is behind cursor -> move anchor behind the inset
1230                 ++normal.pos_;
1231         }
1232 #endif
1233         return normal;
1234 }
1235
1236
1237 /*
1238 DispatchResult dispatch(LCursor & cur, FuncRequest const & cmd)
1239 {
1240         // mouse clicks are somewhat special
1241         // check
1242         switch (cmd.action) {
1243         case LFUN_MOUSE_PRESS:
1244         case LFUN_MOUSE_MOTION:
1245         case LFUN_MOUSE_RELEASE:
1246         case LFUN_MOUSE_DOUBLE: {
1247                 CursorSlice & pos = back();
1248                 int x = 0;
1249                 int y = 0;
1250                 getPos(x, y);
1251                 if (x < cmd.x && pos() != 0) {
1252                         DispatchResult const res = prevAtom().nucleus()->dispatch(cmd);
1253                         if (res.dispatched())
1254                                 return res;
1255                 }
1256                 if (x > cmd.x && pos() != lastpos()) {
1257                         DispatchResult const res = inset()->dispatch(cmd);
1258                         if (res.dispatched())
1259                                 return res;
1260                 }
1261         }
1262         default:
1263         break;
1264         }
1265 }
1266 */
1267
1268
1269 void LCursor::handleFont(string const & font)
1270 {
1271         lyxerr << "LCursor::handleFont: " << font << endl;
1272         string safe;
1273         if (selection()) {
1274                 macroModeClose();
1275                 safe = grabAndEraseSelection();
1276         }
1277
1278         if (lastpos() != 0) {
1279                 // something left in the cell
1280                 if (pos() == 0) {
1281                         // cursor in first position
1282                         popLeft();
1283                 } else if (pos() == lastpos()) {
1284                         // cursor in last position
1285                         popRight();
1286                 } else {
1287                         // cursor in between. split cell
1288                         MathArray::iterator bt = cell().begin();
1289                         MathAtom at = createMathInset(font);
1290                         at.nucleus()->cell(0) = MathArray(bt, bt + pos());
1291                         cell().erase(bt, bt + pos());
1292                         popLeft();
1293                         plainInsert(at);
1294                 }
1295         } else {
1296                 // nothing left in the cell
1297                 pullArg();
1298                 plainErase();
1299         }
1300         insert(safe);
1301 }
1302
1303
1304 void LCursor::message(string const & msg) const
1305 {
1306         bv().owner()->getLyXFunc().setMessage(msg);
1307 }
1308
1309
1310 void LCursor::errorMessage(string const & msg) const
1311 {
1312         bv().owner()->getLyXFunc().setErrorMessage(msg);
1313 }
1314
1315
1316 string LCursor::selectionAsString(bool label) const
1317 {
1318         if (!selection())
1319                 return string();
1320
1321         if (inTexted()) {
1322                 Buffer const & buffer = *bv().buffer();
1323
1324                 // should be const ...
1325                 ParagraphList::iterator startpit = text()->getPar(selBegin());
1326                 ParagraphList::iterator endpit = text()->getPar(selEnd());
1327                 size_t const startpos = selBegin().pos();
1328                 size_t const endpos = selEnd().pos();
1329
1330                 if (startpit == endpit)
1331                         return startpit->asString(buffer, startpos, endpos, label);
1332
1333                 // First paragraph in selection
1334                 string result =
1335                         startpit->asString(buffer, startpos, startpit->size(), label) + "\n\n";
1336
1337                 // The paragraphs in between (if any)
1338                 ParagraphList::iterator pit = startpit;
1339                 for (++pit; pit != endpit; ++pit)
1340                         result += pit->asString(buffer, 0, pit->size(), label) + "\n\n";
1341
1342                 // Last paragraph in selection
1343                 result += endpit->asString(buffer, 0, endpos, label);
1344
1345                 return result;
1346         }
1347
1348 #warning an mathed?
1349         return string();
1350 }
1351
1352
1353 string LCursor::currentState()
1354 {
1355         if (inMathed()) {
1356                 std::ostringstream os;
1357                 info(os);
1358                 return os.str();
1359         }
1360         return text() ? text()->currentState(*this) : string();
1361 }
1362
1363
1364 // only used by the spellchecker
1365 void LCursor::replaceWord(string const & replacestring)
1366 {
1367         LyXText * t = text();
1368         BOOST_ASSERT(t);
1369
1370         t->replaceSelectionWithString(*this, replacestring);
1371         t->setSelectionRange(*this, replacestring.length());
1372
1373         // Go back so that replacement string is also spellchecked
1374         for (string::size_type i = 0; i < replacestring.length() + 1; ++i)
1375                 t->cursorLeft(*this);
1376 }
1377
1378
1379 void LCursor::update()
1380 {
1381         bv().update();
1382 }
1383
1384
1385 string LCursor::getPossibleLabel()
1386 {
1387         return inMathed() ? "eq:" : text()->getPossibleLabel(*this);
1388 }
1389
1390
1391 void LCursor::undispatched()
1392 {
1393         disp_.dispatched(false);
1394 }
1395
1396
1397 void LCursor::dispatched(dispatch_result_t res)
1398 {
1399         disp_.val(res);
1400 }
1401
1402
1403 void LCursor::noUpdate()
1404 {
1405         disp_.update(false);
1406 }
1407
1408
1409 void LCursor::noPop()
1410 {
1411         nopop_ = true;
1412 }