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