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