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