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