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