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