]> git.lyx.org Git - lyx.git/blob - src/cursor.C
try to fix broken cursor (from Andr�)
[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 "coordcache.h"
19 #include "CutAndPaste.h"
20 #include "debug.h"
21 #include "dispatchresult.h"
22 #include "encoding.h"
23 #include "funcrequest.h"
24 #include "language.h"
25 #include "lfuns.h"
26 #include "lyxfont.h"
27 #include "lyxfunc.h" // only for setMessage()
28 #include "lyxrc.h"
29 #include "lyxrow.h"
30 #include "lyxtext.h"
31 #include "paragraph.h"
32 #include "paragraph_funcs.h"
33 #include "pariterator.h"
34
35 #include "insets/updatableinset.h"
36 #include "insets/insettabular.h"
37 #include "insets/insettext.h"
38
39 #include "mathed/math_data.h"
40 #include "mathed/math_inset.h"
41 #include "mathed/math_macrotable.h"
42
43 #include "support/limited_stack.h"
44
45 #include "frontends/LyXView.h"
46 #include "frontends/font_metrics.h"
47
48 #include <boost/assert.hpp>
49 #include <boost/bind.hpp>
50 #include <boost/current_function.hpp>
51
52 #include <sstream>
53 #include <limits>
54
55 using lyx::pit_type;
56
57 using std::string;
58 using std::vector;
59 using std::endl;
60 #ifndef CXX_GLOBAL_CSTD
61 using std::isalpha;
62 #endif
63 using std::min;
64 using std::swap;
65
66 namespace {
67
68         bool
69         positionable(DocIterator const & cursor, DocIterator const & anchor)
70         {
71                 // avoid deeper nested insets when selecting
72                 if (cursor.depth() > anchor.depth())
73                         return false;
74
75                 // anchor might be deeper, should have same path then
76                 for (size_t i = 0; i < cursor.depth(); ++i)
77                         if (&cursor[i].inset() != &anchor[i].inset())
78                                 return false;
79
80                 // position should be ok.
81                 return true;
82         }
83
84
85         // Find position closest to (x, y) in cell given by iter.
86         // Used only in mathed
87         DocIterator bruteFind2(LCursor const & c, int x, int y)
88         {
89                 double best_dist = std::numeric_limits<double>::max();
90
91                 DocIterator result;
92
93                 DocIterator it = c;
94                 it.top().pos() = 0;
95                 DocIterator et = c;
96                 et.top().pos() = et.top().asMathInset()->cell(et.top().idx()).size();
97                 for (int i = 0; ; ++i) {
98                         int xo;
99                         int yo;
100                         LCursor cur = c;
101                         cur.setCursor(it);
102                         cur.inset().getCursorPos(cur.top(), xo, yo);
103                         double d = (x - xo) * (x - xo) + (y - yo) * (y - yo);
104                         // '<=' in order to take the last possible position
105                         // this is important for clicking behind \sum in e.g. '\sum_i a'
106                         lyxerr[Debug::DEBUG] << "i: " << i << " d: " << d
107                                 << " best: " << best_dist << endl;
108                         if (d <= best_dist) {
109                                 best_dist = d;
110                                 result = it;
111                         }
112                         if (it == et)
113                                 break;
114                         it.forwardPos();
115                 }
116                 return result;
117         }
118
119
120         /// moves position closest to (x, y) in given box
121         bool bruteFind(LCursor & cursor,
122                 int x, int y, int xlow, int xhigh, int ylow, int yhigh)
123         {
124                 BOOST_ASSERT(!cursor.empty());
125                 CursorSlice bottom = cursor[0];
126                 LyXText * text = bottom.text();
127                 BOOST_ASSERT(text);
128
129                 DocIterator it = doc_iterator_begin(bottom.inset());
130                 DocIterator const et = doc_iterator_end(bottom.inset());
131
132                 double best_dist = std::numeric_limits<double>::max();;
133                 DocIterator best_cursor = et;
134
135                 for ( ; it != et; it.forwardPos()) {
136                         // avoid invalid nesting when selecting
137                         if (bv_funcs::status(&cursor.bv(), it) == bv_funcs::CUR_INSIDE
138                             && (!cursor.selection() || positionable(it, cursor.anchor_))) {
139                                 Point p = bv_funcs::getPos(it);
140                                 int xo = p.x_;
141                                 int yo = p.y_;
142                                 if (xlow <= xo && xo <= xhigh && ylow <= yo && yo <= yhigh) {
143                                         double d = (x - xo) * (x - xo) + (y - yo) * (y - yo);
144                                         //lyxerr << "xo: " << xo << " yo: " << yo << " d: " << d << endl;
145                                         // '<=' in order to take the last possible position
146                                         // this is important for clicking behind \sum in e.g. '\sum_i a'
147                                         if (d <= best_dist) {
148                                                 //lyxerr << "*" << endl;
149                                                 best_dist   = d;
150                                                 best_cursor = it;
151                                         }
152                                 }
153                         }
154                 }
155
156                 //lyxerr << "best_dist: " << best_dist << " cur:\n" << best_cursor << endl;
157                 if (best_cursor != et) {
158                         cursor.setCursor(best_cursor);
159                         return true;
160                 }
161
162                 return false;
163         }
164
165 } // namespace anon
166
167
168 // be careful: this is called from the bv's constructor, too, so
169 // bv functions are not yet available!
170 LCursor::LCursor(BufferView & bv)
171         : DocIterator(), bv_(&bv), anchor_(), x_target_(-1),
172           selection_(false), mark_(false)
173 {}
174
175
176 void LCursor::reset(InsetBase & inset)
177 {
178         clear();
179         push_back(CursorSlice(inset));
180         anchor_ = DocIterator(inset);
181         clearTargetX();
182         selection_ = false;
183         mark_ = false;
184 }
185
186
187 // this (intentionally) does neither touch anchor nor selection status
188 void LCursor::setCursor(DocIterator const & cur)
189 {
190         DocIterator::operator=(cur);
191 }
192
193
194 void LCursor::dispatch(FuncRequest const & cmd0)
195 {
196         lyxerr[Debug::DEBUG] << BOOST_CURRENT_FUNCTION
197                              << " cmd: " << cmd0 << '\n'
198                              << *this << endl;
199         if (empty())
200                 return;
201
202         fixIfBroken();
203         FuncRequest cmd = cmd0;
204         LCursor safe = *this;
205
206         for (; depth(); pop()) {
207                 lyxerr[Debug::DEBUG] << "LCursor::dispatch: cmd: "
208                         << cmd0 << endl << *this << endl;
209                 BOOST_ASSERT(pos() <= lastpos());
210                 BOOST_ASSERT(idx() <= lastidx());
211                 BOOST_ASSERT(pit() <= lastpit());
212
213                 // The common case is 'LFUN handled, need update', so make the
214                 // LFUN handler's life easier by assuming this as default value.
215                 // The handler can reset the update and val flags if necessary.
216                 disp_.update(true);
217                 disp_.dispatched(true);
218                 inset().dispatch(*this, cmd);
219                 if (disp_.dispatched())
220                         break;
221         }
222         // it completely to get a 'bomb early' behaviour in case this
223         // object will be used again.
224         if (!disp_.dispatched()) {
225                 lyxerr[Debug::DEBUG] << "RESTORING OLD CURSOR!" << endl;
226                 operator=(safe);
227                 disp_.dispatched(false);
228         }
229 }
230
231
232 DispatchResult LCursor::result() const
233 {
234         return disp_;
235 }
236
237
238 BufferView & LCursor::bv() const
239 {
240         BOOST_ASSERT(bv_);
241         return *bv_;
242 }
243
244
245 Buffer & LCursor::buffer() const
246 {
247         BOOST_ASSERT(bv_);
248         BOOST_ASSERT(bv_->buffer());
249         return *bv_->buffer();
250 }
251
252
253 void LCursor::pop()
254 {
255         BOOST_ASSERT(depth() >= 1);
256         pop_back();
257 }
258
259
260 void LCursor::push(InsetBase & p)
261 {
262         push_back(CursorSlice(p));
263 }
264
265
266 void LCursor::pushLeft(InsetBase & p)
267 {
268         BOOST_ASSERT(!empty());
269         //lyxerr << "Entering inset " << t << " left" << endl;
270         push(p);
271         p.idxFirst(*this);
272 }
273
274
275 bool LCursor::popLeft()
276 {
277         BOOST_ASSERT(!empty());
278         //lyxerr << "Leaving inset to the left" << endl;
279         inset().notifyCursorLeaves(*this);
280         if (depth() == 1)
281                 return false;
282         pop();
283         return true;
284 }
285
286
287 bool LCursor::popRight()
288 {
289         BOOST_ASSERT(!empty());
290         //lyxerr << "Leaving inset to the right" << endl;
291         inset().notifyCursorLeaves(*this);
292         if (depth() == 1)
293                 return false;
294         pop();
295         ++pos();
296         return true;
297 }
298
299
300 int LCursor::currentMode()
301 {
302         BOOST_ASSERT(!empty());
303         for (int i = depth() - 1; i >= 0; --i) {
304                 int res = operator[](i).inset().currentMode();
305                 if (res != InsetBase::UNDECIDED_MODE)
306                         return res;
307         }
308         return InsetBase::TEXT_MODE;
309 }
310
311
312 void LCursor::getPos(int & x, int & y) const
313 {
314         Point p = bv_funcs::getPos(*this);
315         x = p.x_;
316         y = p.y_;
317 }
318
319
320 void LCursor::paste(string const & data)
321 {
322         dispatch(FuncRequest(LFUN_PASTE, data));
323 }
324
325
326 void LCursor::resetAnchor()
327 {
328         anchor_ = *this;
329 }
330
331
332
333 bool LCursor::posLeft()
334 {
335         if (pos() == 0)
336                 return false;
337         --pos();
338         return true;
339 }
340
341
342 bool LCursor::posRight()
343 {
344         if (pos() == lastpos())
345                 return false;
346         ++pos();
347         return true;
348 }
349
350
351 CursorSlice LCursor::anchor() const
352 {
353         BOOST_ASSERT(anchor_.depth() >= depth());
354         CursorSlice normal = anchor_[depth() - 1];
355         if (depth() < anchor_.depth() && top() <= normal) {
356                 // anchor is behind cursor -> move anchor behind the inset
357                 ++normal.pos();
358         }
359         return normal;
360 }
361
362
363 CursorSlice LCursor::selBegin() const
364 {
365         if (!selection())
366                 return top();
367         return anchor() < top() ? anchor() : top();
368 }
369
370
371 CursorSlice LCursor::selEnd() const
372 {
373         if (!selection())
374                 return top();
375         return anchor() > top() ? anchor() : top();
376 }
377
378
379 DocIterator LCursor::selectionBegin() const
380 {
381         if (!selection())
382                 return *this;
383         return anchor() < top() ? anchor_ : *this;
384 }
385
386
387 DocIterator LCursor::selectionEnd() const
388 {
389         if (!selection())
390                 return *this;
391         return anchor() > top() ? anchor_ : *this;
392 }
393
394
395 void LCursor::setSelection()
396 {
397         selection() = true;
398         // a selection with no contents is not a selection
399 #ifdef WITH_WARNINGS
400 #warning doesnt look ok
401 #endif
402         if (pit() == anchor().pit() && pos() == anchor().pos())
403                 selection() = false;
404 }
405
406
407 void LCursor::setSelection(DocIterator const & where, size_t n)
408 {
409         setCursor(where);
410         selection() = true;
411         anchor_ = where;
412         pos() += n;
413 }
414
415
416 void LCursor::clearSelection()
417 {
418         selection() = false;
419         mark() = false;
420         resetAnchor();
421         bv().unsetXSel();
422 }
423
424
425 int & LCursor::x_target()
426 {
427         return x_target_;
428 }
429
430
431 int LCursor::x_target() const
432 {
433         return x_target_;
434 }
435
436
437 void LCursor::clearTargetX()
438 {
439         x_target_ = -1;
440 }
441
442
443
444 void LCursor::info(std::ostream & os) const
445 {
446         for (int i = 1, n = depth(); i < n; ++i) {
447                 operator[](i).inset().infoize(os);
448                 os << "  ";
449         }
450         if (pos() != 0)
451                 prevInset()->infoize2(os);
452         // overwite old message
453         os << "                    ";
454 }
455
456
457 void LCursor::selHandle(bool sel)
458 {
459         //lyxerr << "LCursor::selHandle" << endl;
460         if (sel == selection())
461                 return;
462
463         resetAnchor();
464         selection() = sel;
465 }
466
467
468 std::ostream & operator<<(std::ostream & os, LCursor const & cur)
469 {
470         os << "\n cursor:                                | anchor:\n";
471         for (size_t i = 0, n = cur.depth(); i != n; ++i) {
472                 os << " " << cur[i] << " | ";
473                 if (i < cur.anchor_.depth())
474                         os << cur.anchor_[i];
475                 else
476                         os << "-------------------------------";
477                 os << "\n";
478         }
479         for (size_t i = cur.depth(), n = cur.anchor_.depth(); i < n; ++i) {
480                 os << "------------------------------- | " << cur.anchor_[i] << "\n";
481         }
482         os << " selection: " << cur.selection_
483            << " x_target: " << cur.x_target_ << endl;
484         return os;
485 }
486
487
488
489
490 ///////////////////////////////////////////////////////////////////
491 //
492 // The part below is the non-integrated rest of the original math
493 // cursor. This should be either generalized for texted or moved
494 // back to mathed (in most cases to MathNestInset).
495 //
496 ///////////////////////////////////////////////////////////////////
497
498 #include "mathed/math_charinset.h"
499 #include "mathed/math_factory.h"
500 #include "mathed/math_gridinset.h"
501 #include "mathed/math_macroarg.h"
502 #include "mathed/math_mathmlstream.h"
503 #include "mathed/math_scriptinset.h"
504 #include "mathed/math_support.h"
505 #include "mathed/math_unknowninset.h"
506
507 //#define FILEDEBUG 1
508
509
510 bool LCursor::isInside(InsetBase const * p)
511 {
512         for (unsigned i = 0; i < depth(); ++i)
513                 if (&operator[](i).inset() == p)
514                         return true;
515         return false;
516 }
517
518
519 bool LCursor::openable(MathAtom const & t) const
520 {
521         if (!t->isActive())
522                 return false;
523
524         if (t->lock())
525                 return false;
526
527         if (!selection())
528                 return true;
529
530         // we can't move into anything new during selection
531         if (depth() >= anchor_.depth())
532                 return false;
533         if (!ptr_cmp(t.nucleus(), &anchor_[depth()].inset()))
534                 return false;
535
536         return true;
537 }
538
539
540 void LCursor::setScreenPos(int x, int y)
541 {
542         x_target() = x;
543         bruteFind(*this, x, y, 0, bv().workWidth(), 0, bv().workHeight());
544 }
545
546
547
548 void LCursor::plainErase()
549 {
550         cell().erase(pos());
551 }
552
553
554 void LCursor::markInsert()
555 {
556         insert(char(0));
557 }
558
559
560 void LCursor::markErase()
561 {
562         cell().erase(pos());
563 }
564
565
566 void LCursor::plainInsert(MathAtom const & t)
567 {
568         cell().insert(pos(), t);
569         ++pos();
570 }
571
572
573 void LCursor::insert(string const & str)
574 {
575         for_each(str.begin(), str.end(),
576                  boost::bind(static_cast<void(LCursor::*)(char)>
577                              (&LCursor::insert), this, _1));
578 }
579
580
581 void LCursor::insert(char c)
582 {
583         //lyxerr << "LCursor::insert char '" << c << "'" << endl;
584         BOOST_ASSERT(!empty());
585         if (inMathed()) {
586                 lyx::cap::selClearOrDel(*this);
587                 insert(new MathCharInset(c));
588         } else {
589                 text()->insertChar(*this, c);
590         }
591 }
592
593
594 void LCursor::insert(MathAtom const & t)
595 {
596         //lyxerr << "LCursor::insert MathAtom '" << t << "'" << endl;
597         macroModeClose();
598         lyx::cap::selClearOrDel(*this);
599         plainInsert(t);
600 }
601
602
603 void LCursor::insert(InsetBase * inset)
604 {
605         if (inMathed())
606                 insert(MathAtom(inset));
607         else
608                 text()->insertInset(*this, inset);
609 }
610
611
612 void LCursor::niceInsert(string const & t)
613 {
614         MathArray ar;
615         asArray(t, ar);
616         if (ar.size() == 1)
617                 niceInsert(ar[0]);
618         else
619                 insert(ar);
620 }
621
622
623 void LCursor::niceInsert(MathAtom const & t)
624 {
625         macroModeClose();
626         string const safe = lyx::cap::grabAndEraseSelection(*this);
627         plainInsert(t);
628         // enter the new inset and move the contents of the selection if possible
629         if (t->isActive()) {
630                 posLeft();
631                 // be careful here: don't use 'pushLeft(t)' as this we need to
632                 // push the clone, not the original
633                 pushLeft(*nextInset());
634                 paste(safe);
635         }
636 }
637
638
639 void LCursor::insert(MathArray const & ar)
640 {
641         macroModeClose();
642         if (selection())
643                 lyx::cap::eraseSelection(*this);
644         cell().insert(pos(), ar);
645         pos() += ar.size();
646 }
647
648
649 bool LCursor::backspace()
650 {
651         autocorrect() = false;
652
653         if (selection()) {
654                 lyx::cap::selDel(*this);
655                 return true;
656         }
657
658         if (pos() == 0) {
659                 if (inset().nargs() == 1 && depth() == 1 && lastpos() == 0)
660                         return false;
661                 pullArg();
662                 return true;
663         }
664
665         if (inMacroMode()) {
666                 MathUnknownInset * p = activeMacro();
667                 if (p->name().size() > 1) {
668                         p->setName(p->name().substr(0, p->name().size() - 1));
669                         return true;
670                 }
671         }
672
673         if (pos() != 0 && prevAtom()->nargs() > 0) {
674                 // let's require two backspaces for 'big stuff' and
675                 // highlight on the first
676                 resetAnchor();
677                 selection() = true;
678                 --pos();
679         } else {
680                 --pos();
681                 plainErase();
682         }
683         return true;
684 }
685
686
687 bool LCursor::erase()
688 {
689         autocorrect() = false;
690         if (inMacroMode())
691                 return true;
692
693         if (selection()) {
694                 lyx::cap::selDel(*this);
695                 return true;
696         }
697
698         // delete empty cells if possible
699         if (pos() == lastpos() && inset().idxDelete(idx()))
700                 return true;
701
702         // special behaviour when in last position of cell
703         if (pos() == lastpos()) {
704                 bool one_cell = inset().nargs() == 1;
705                 if (one_cell && depth() == 1 && lastpos() == 0)
706                         return false;
707                 // remove markup
708                 if (one_cell)
709                         pullArg();
710                 else
711                         inset().idxGlue(idx());
712                 return true;
713         }
714
715         // 'clever' UI hack: only erase large items if previously slected
716         if (pos() != lastpos() && inset().nargs() > 0) {
717                 resetAnchor();
718                 selection() = true;
719                 ++pos();
720         } else {
721                 plainErase();
722         }
723
724         return true;
725 }
726
727
728 bool LCursor::up()
729 {
730         macroModeClose();
731         DocIterator save = *this;
732         if (goUpDown(true))
733                 return true;
734         setCursor(save);
735         autocorrect() = false;
736         return selection();
737 }
738
739
740 bool LCursor::down()
741 {
742         macroModeClose();
743         DocIterator save = *this;
744         if (goUpDown(false))
745                 return true;
746         setCursor(save);
747         autocorrect() = false;
748         return selection();
749 }
750
751
752 void LCursor::macroModeClose()
753 {
754         if (!inMacroMode())
755                 return;
756         MathUnknownInset * p = activeMacro();
757         p->finalize();
758         string const s = p->name();
759         --pos();
760         cell().erase(pos());
761
762         // do nothing if the macro name is empty
763         if (s == "\\")
764                 return;
765
766         string const name = s.substr(1);
767
768         // prevent entering of recursive macros
769         // FIXME: this is only a weak attempt... only prevents immediate
770         // recursion
771         InsetBase const * macro = innerInsetOfType(InsetBase::MATHMACRO_CODE);
772         if (macro && macro->getInsetName() == name)
773                 lyxerr << "can't enter recursive macro" << endl;
774
775         plainInsert(createMathInset(name));
776 }
777
778
779 string LCursor::macroName()
780 {
781         return inMacroMode() ? activeMacro()->name() : string();
782 }
783
784
785 void LCursor::handleNest(MathAtom const & a, int c)
786 {
787         //lyxerr << "LCursor::handleNest: " << c << endl;
788         MathAtom t = a;
789         asArray(lyx::cap::grabAndEraseSelection(*this), t.nucleus()->cell(c));
790         insert(t);
791         posLeft();
792         pushLeft(*nextInset());
793 }
794
795
796 int LCursor::targetX() const
797 {
798         if (x_target() != -1)
799                 return x_target();
800         int x = 0;
801         int y = 0;
802         getPos(x, y);
803         return x;
804 }
805
806
807 void LCursor::setTargetX()
808 {
809         // For now this is good enough. A better solution would be to
810         // avoid this rebreak by setting cursorX only after drawing
811         bottom().text()->redoParagraph(bottom().pit());
812         int x;
813         int y;
814         getPos(x, y);
815         x_target_ = x;
816 }
817
818
819 bool LCursor::inMacroMode() const
820 {
821         if (pos() == 0)
822                 return false;
823         MathUnknownInset const * p = prevAtom()->asUnknownInset();
824         return p && !p->final();
825 }
826
827
828 MathUnknownInset * LCursor::activeMacro()
829 {
830         return inMacroMode() ? prevAtom().nucleus()->asUnknownInset() : 0;
831 }
832
833
834 void LCursor::pullArg()
835 {
836 #ifdef WITH_WARNINGS
837 #warning Look here
838 #endif
839         MathArray ar = cell();
840         if (popLeft() && inMathed()) {
841                 plainErase();
842                 cell().insert(pos(), ar);
843                 resetAnchor();
844         } else {
845                 //formula()->mutateToText();
846         }
847 }
848
849
850 void LCursor::touch()
851 {
852 #ifdef WITH_WARNINGS
853 #warning look here
854 #endif
855 #if 0
856         DocIterator::const_iterator it = begin();
857         DocIterator::const_iterator et = end();
858         for ( ; it != et; ++it)
859                 it->cell().touch();
860 #endif
861 }
862
863
864 void LCursor::normalize()
865 {
866         if (idx() >= nargs()) {
867                 lyxerr << "this should not really happen - 1: "
868                        << idx() << ' ' << nargs()
869                        << " in: " << &inset() << endl;
870         }
871         idx() = min(idx(), lastidx());
872
873         if (pos() > lastpos()) {
874                 lyxerr << "this should not really happen - 2: "
875                         << pos() << ' ' << lastpos() <<  " in idx: " << idx()
876                        << " in atom: '";
877                 WriteStream wi(lyxerr, false, true);
878                 inset().asMathInset()->write(wi);
879                 lyxerr << endl;
880         }
881         pos() = min(pos(), lastpos());
882 }
883
884
885 bool LCursor::goUpDown(bool up)
886 {
887         // Be warned: The 'logic' implemented in this function is highly
888         // fragile. A distance of one pixel or a '<' vs '<=' _really
889         // matters. So fiddle around with it only if you think you know
890         // what you are doing!
891
892         int xo = 0;
893         int yo = 0;
894         getPos(xo, yo);
895
896         // check if we had something else in mind, if not, this is the future goal
897         if (x_target() == -1)
898                 x_target() = xo;
899         else
900                 xo = x_target();
901
902         // try neigbouring script insets
903         if (!selection()) {
904                 // try left
905                 if (pos() != 0) {
906                         MathScriptInset const * p = prevAtom()->asScriptInset();
907                         if (p && p->has(up)) {
908                                 --pos();
909                                 push(inset());
910                                 idx() = up; // the superscript has index 1
911                                 pos() = lastpos();
912                                 //lyxerr << "updown: handled by scriptinset to the left" << endl;
913                                 return true;
914                         }
915                 }
916
917                 // try right
918                 if (pos() != lastpos()) {
919                         MathScriptInset const * p = nextAtom()->asScriptInset();
920                         if (p && p->has(up)) {
921                                 push(inset());
922                                 idx() = up;
923                                 pos() = 0;
924                                 //lyxerr << "updown: handled by scriptinset to the right" << endl;
925                                 return true;
926                         }
927                 }
928         }
929
930         //xarray().boundingBox(xlow, xhigh, ylow, yhigh);
931         //if (up)
932         //      yhigh = yo - 4;
933         //else
934         //      ylow = yo + 4;
935         //if (bruteFind(*this, xo, yo, xlow, xhigh, ylow, yhigh)) {
936         //      lyxerr << "updown: handled by brute find in the same cell" << endl;
937         //      return true;
938         //}
939
940         // try to find an inset that knows better then we
941         while (true) {
942                 //lyxerr << "updown: We are in " << &inset() << " idx: " << idx() << endl;
943                 // ask inset first
944                 if (inset().idxUpDown(*this, up)) {
945                         // try to find best position within this inset
946                         if (!selection())
947                                 setCursor(bruteFind2(*this, xo, yo));
948                         return true;
949                 }
950
951                 // no such inset found, just take something "above"
952                 //lyxerr << "updown: handled by strange case" << endl;
953                 if (!popLeft()) {
954                         int ylow  = up ? 0 : yo + 1;
955                         int yhigh = up ? yo - 1 : bv().workHeight();
956                         return bruteFind(*this, xo, yo, 0, bv().workWidth(), ylow, yhigh);
957                 }
958
959                 // any improvement so far?
960                 int xnew;
961                 int ynew;
962                 getPos(xnew, ynew);
963                 if (up ? ynew < yo : ynew > yo)
964                         return true;
965         }
966
967         // we should not come here.
968         BOOST_ASSERT(false);
969 }
970
971
972 void LCursor::handleFont(string const & font)
973 {
974         lyxerr[Debug::DEBUG] << BOOST_CURRENT_FUNCTION << ": " << font << endl;
975         string safe;
976         if (selection()) {
977                 macroModeClose();
978                 safe = lyx::cap::grabAndEraseSelection(*this);
979         }
980
981         if (lastpos() != 0) {
982                 // something left in the cell
983                 if (pos() == 0) {
984                         // cursor in first position
985                         popLeft();
986                 } else if (pos() == lastpos()) {
987                         // cursor in last position
988                         popRight();
989                 } else {
990                         // cursor in between. split cell
991                         MathArray::iterator bt = cell().begin();
992                         MathAtom at = createMathInset(font);
993                         at.nucleus()->cell(0) = MathArray(bt, bt + pos());
994                         cell().erase(bt, bt + pos());
995                         popLeft();
996                         plainInsert(at);
997                 }
998         } else {
999                 // nothing left in the cell
1000                 pullArg();
1001                 plainErase();
1002         }
1003         insert(safe);
1004 }
1005
1006
1007 void LCursor::message(string const & msg) const
1008 {
1009         bv().owner()->getLyXFunc().setMessage(msg);
1010 }
1011
1012
1013 void LCursor::errorMessage(string const & msg) const
1014 {
1015         bv().owner()->getLyXFunc().setErrorMessage(msg);
1016 }
1017
1018
1019 string LCursor::selectionAsString(bool label) const
1020 {
1021         if (!selection())
1022                 return string();
1023
1024         if (inTexted()) {
1025                 Buffer const & buffer = *bv().buffer();
1026                 ParagraphList & pars = text()->paragraphs();
1027
1028                 // should be const ...
1029                 pit_type startpit = selBegin().pit();
1030                 pit_type endpit = selEnd().pit();
1031                 size_t const startpos = selBegin().pos();
1032                 size_t const endpos = selEnd().pos();
1033
1034                 if (startpit == endpit)
1035                         return pars[startpit].asString(buffer, startpos, endpos, label);
1036
1037                 // First paragraph in selection
1038                 string result = pars[startpit].
1039                         asString(buffer, startpos, pars[startpit].size(), label) + "\n\n";
1040
1041                 // The paragraphs in between (if any)
1042                 for (pit_type pit = startpit + 1; pit != endpit; ++pit) {
1043                         Paragraph & par = pars[pit];
1044                         result += par.asString(buffer, 0, par.size(), label) + "\n\n";
1045                 }
1046
1047                 // Last paragraph in selection
1048                 result += pars[endpit].asString(buffer, 0, endpos, label);
1049
1050                 return result;
1051         }
1052
1053 #ifdef WITH_WARNINGS
1054 #warning and mathed?
1055 #endif
1056         return string();
1057 }
1058
1059
1060 string LCursor::currentState()
1061 {
1062         if (inMathed()) {
1063                 std::ostringstream os;
1064                 info(os);
1065                 return os.str();
1066         }
1067
1068         if (inTexted())
1069                 return text()->currentState(*this);
1070
1071         return string();
1072 }
1073
1074
1075 string LCursor::getPossibleLabel()
1076 {
1077         return inMathed() ? "eq:" : text()->getPossibleLabel(*this);
1078 }
1079
1080
1081 Encoding const * LCursor::getEncoding() const
1082 {
1083         if (empty())
1084                 return 0;
1085         if (!bv().buffer())
1086                 return 0;
1087         int s = 0;
1088         // go up until first non-0 text is hit
1089         // (innermost text is 0 in mathed)
1090         for (s = depth() - 1; s >= 0; --s)
1091                 if (operator[](s).text())
1092                         break;
1093         CursorSlice const & sl = operator[](s);
1094         LyXText const & text = *sl.text();
1095         LyXFont font = text.getPar(sl.pit()).getFont(
1096                 bv().buffer()->params(), sl.pos(), outerFont(sl.pit(), text.paragraphs()));
1097         return font.language()->encoding();
1098 }
1099
1100
1101 void LCursor::undispatched()
1102 {
1103         disp_.dispatched(false);
1104 }
1105
1106
1107 void LCursor::dispatched()
1108 {
1109         disp_.dispatched(true);
1110 }
1111
1112
1113 void LCursor::needsUpdate()
1114 {
1115         disp_.update(true);
1116 }
1117
1118
1119 void LCursor::noUpdate()
1120 {
1121         disp_.update(false);
1122 }
1123
1124
1125 LyXFont LCursor::getFont() const
1126 {
1127         // HACK. far from being perfect...
1128         int s = 0;
1129         // go up until first non-0 text is hit
1130         // (innermost text is 0 in mathed)
1131         for (s = depth() - 1; s >= 0; --s)
1132                 if (operator[](s).text())
1133                         break;
1134         CursorSlice const & sl = operator[](s);
1135         LyXText const & text = *sl.text();
1136         LyXFont font = text.getPar(sl.pit()).getFont(
1137                 bv().buffer()->params(),
1138                 sl.pos(),
1139                 outerFont(sl.pit(), text.paragraphs()));
1140
1141         return font;
1142 }
1143
1144
1145 void LCursor::fixIfBroken()
1146 {
1147         // find out last good level
1148         LCursor copy = *this;
1149         size_t newdepth = depth();
1150         while (!copy.empty()) {
1151                 if (copy.idx() > copy.lastidx()) {
1152                         lyxerr << "wrong idx " << copy.idx()
1153                                << ", max is " << copy.lastidx()
1154                                << " at level " << copy.depth()
1155                                << ". Trying to correct this."  << endl;
1156                         newdepth = copy.depth() - 1;
1157                 }
1158                 else if (copy.pit() > copy.lastpit()) {
1159                         lyxerr << "wrong pit " << copy.pit()
1160                                << ", max is " << copy.lastpit()
1161                                << " at level " << copy.depth()
1162                                << ". Trying to correct this."  << endl;
1163                         newdepth = copy.depth() - 1;
1164                 }
1165                 else if (copy.pos() > copy.lastpos()) {
1166                         lyxerr << "wrong pos " << copy.pos()
1167                                << ", max is " << copy.lastpos()
1168                                << " at level " << copy.depth()
1169                                << ". Trying to correct this."  << endl;
1170                         newdepth = copy.depth() - 1;
1171                 }
1172                 copy.pop();     
1173         }
1174         // shrink cursor to a size where everything is valid, possibly
1175         // leaving insets
1176         while (depth() > newdepth) {
1177                 pop();
1178                 lyxerr << "correcting cursor to level " << depth() << endl;
1179         }
1180 }