]> git.lyx.org Git - lyx.git/blob - src/Cursor.cpp
Add lfuns doxy.
[lyx.git] / src / Cursor.cpp
1 /**
2  * \file Cursor.cpp
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  * \author Stefan Schimanski
10  *
11  * Full author contact details are available in file CREDITS.
12  */
13
14 #include <config.h>
15
16 #include "Bidi.h"
17 #include "Buffer.h"
18 #include "BufferView.h"
19 #include "CoordCache.h"
20 #include "Cursor.h"
21 #include "CutAndPaste.h"
22 #include "DispatchResult.h"
23 #include "Encoding.h"
24 #include "Font.h"
25 #include "FuncRequest.h"
26 #include "Language.h"
27 #include "lfuns.h"
28 #include "LyXFunc.h" // only for setMessage()
29 #include "LyXRC.h"
30 #include "paragraph_funcs.h"
31 #include "Paragraph.h"
32 #include "ParIterator.h"
33 #include "Row.h"
34 #include "Text.h"
35 #include "TextMetrics.h"
36 #include "TocBackend.h"
37
38 #include "support/debug.h"
39 #include "support/docstream.h"
40
41 #include "insets/InsetTabular.h"
42 #include "insets/InsetText.h"
43
44 #include "mathed/InsetMath.h"
45 #include "mathed/InsetMathScript.h"
46 #include "mathed/MacroTable.h"
47 #include "mathed/MathData.h"
48 #include "mathed/MathMacro.h"
49
50 #include <boost/assert.hpp>
51 #include <boost/bind.hpp>
52
53 #include <sstream>
54 #include <limits>
55 #include <map>
56
57 using namespace std;
58
59 namespace lyx {
60
61 namespace {
62
63 bool positionable(DocIterator const & cursor, DocIterator const & anchor)
64 {
65         // avoid deeper nested insets when selecting
66         if (cursor.depth() > anchor.depth())
67                 return false;
68
69         // anchor might be deeper, should have same path then
70         for (size_t i = 0; i < cursor.depth(); ++i)
71                 if (&cursor[i].inset() != &anchor[i].inset())
72                         return false;
73
74         // position should be ok.
75         return true;
76 }
77
78
79 // Find position closest to (x, y) in cell given by iter.
80 // Used only in mathed
81 DocIterator bruteFind2(Cursor const & c, int x, int y)
82 {
83         double best_dist = numeric_limits<double>::max();
84
85         DocIterator result;
86
87         DocIterator it = c;
88         it.top().pos() = 0;
89         DocIterator et = c;
90         et.top().pos() = et.top().asInsetMath()->cell(et.top().idx()).size();
91         for (size_t i = 0;; ++i) {
92                 int xo;
93                 int yo;
94                 Inset const * inset = &it.inset();
95                 map<Inset const *, Geometry> const & data =
96                         c.bv().coordCache().getInsets().getData();
97                 map<Inset const *, Geometry>::const_iterator I = data.find(inset);
98
99                 // FIXME: in the case where the inset is not in the cache, this
100                 // means that no part of it is visible on screen. In this case
101                 // we don't do elaborate search and we just return the forwarded
102                 // DocIterator at its beginning.
103                 if (I == data.end()) {
104                         it.top().pos() = 0;
105                         return it;
106                 }
107
108                 Point o = I->second.pos;
109                 inset->cursorPos(c.bv(), it.top(), c.boundary(), xo, yo);
110                 // Convert to absolute
111                 xo += o.x_;
112                 yo += o.y_;
113                 double d = (x - xo) * (x - xo) + (y - yo) * (y - yo);
114                 // '<=' in order to take the last possible position
115                 // this is important for clicking behind \sum in e.g. '\sum_i a'
116                 LYXERR(Debug::DEBUG, "i: " << i << " d: " << d
117                         << " best: " << best_dist);
118                 if (d <= best_dist) {
119                         best_dist = d;
120                         result = it;
121                 }
122                 if (it == et)
123                         break;
124                 it.forwardPos();
125         }
126         return result;
127 }
128
129
130 /*
131 /// moves position closest to (x, y) in given box
132 bool bruteFind(Cursor & cursor,
133         int x, int y, int xlow, int xhigh, int ylow, int yhigh)
134 {
135         BOOST_ASSERT(!cursor.empty());
136         Inset & inset = cursor[0].inset();
137         BufferView & bv = cursor.bv();
138
139         CoordCache::InnerParPosCache const & cache =
140                 bv.coordCache().getParPos().find(cursor.bottom().text())->second;
141         // Get an iterator on the first paragraph in the cache
142         DocIterator it(inset);
143         it.push_back(CursorSlice(inset));
144         it.pit() = cache.begin()->first;
145         // Get an iterator after the last paragraph in the cache
146         DocIterator et(inset);
147         et.push_back(CursorSlice(inset));
148         et.pit() = boost::prior(cache.end())->first;
149         if (et.pit() >= et.lastpit())
150                 et = doc_iterator_end(inset);
151         else
152                 ++et.pit();
153
154         double best_dist = numeric_limits<double>::max();;
155         DocIterator best_cursor = et;
156
157         for ( ; it != et; it.forwardPos(true)) {
158                 // avoid invalid nesting when selecting
159                 if (!cursor.selection() || positionable(it, cursor.anchor_)) {
160                         Point p = bv.getPos(it, false);
161                         int xo = p.x_;
162                         int yo = p.y_;
163                         if (xlow <= xo && xo <= xhigh && ylow <= yo && yo <= yhigh) {
164                                 double const dx = xo - x;
165                                 double const dy = yo - y;
166                                 double const d = dx * dx + dy * dy;
167                                 // '<=' in order to take the last possible position
168                                 // this is important for clicking behind \sum in e.g. '\sum_i a'
169                                 if (d <= best_dist) {
170                                         //      lyxerr << "*" << endl;
171                                         best_dist   = d;
172                                         best_cursor = it;
173                                 }
174                         }
175                 }
176         }
177
178         if (best_cursor != et) {
179                 cursor.setCursor(best_cursor);
180                 return true;
181         }
182
183         return false;
184 }
185 */
186
187
188 /// moves position closest to (x, y) in given box
189 bool bruteFind3(Cursor & cur, int x, int y, bool up)
190 {
191         BufferView & bv = cur.bv();
192         int ylow  = up ? 0 : y + 1;
193         int yhigh = up ? y - 1 : bv.workHeight();
194         int xlow = 0;
195         int xhigh = bv.workWidth();
196
197 // FIXME: bit more work needed to get 'from' and 'to' right.
198         pit_type from = cur.bottom().pit();
199         //pit_type to = cur.bottom().pit();
200         //lyxerr << "Pit start: " << from << endl;
201
202         //lyxerr << "bruteFind3: x: " << x << " y: " << y
203         //      << " xlow: " << xlow << " xhigh: " << xhigh
204         //      << " ylow: " << ylow << " yhigh: " << yhigh
205         //      << endl;
206         Inset & inset = bv.buffer().inset();
207         DocIterator it = doc_iterator_begin(inset);
208         it.pit() = from;
209         DocIterator et = doc_iterator_end(inset);
210
211         double best_dist = numeric_limits<double>::max();
212         DocIterator best_cursor = et;
213
214         for ( ; it != et; it.forwardPos()) {
215                 // avoid invalid nesting when selecting
216                 if (bv.cursorStatus(it) == CUR_INSIDE
217                                 && (!cur.selection() || positionable(it, cur.anchor_))) {
218                         Point p = bv.getPos(it, false);
219                         int xo = p.x_;
220                         int yo = p.y_;
221                         if (xlow <= xo && xo <= xhigh && ylow <= yo && yo <= yhigh) {
222                                 double const dx = xo - x;
223                                 double const dy = yo - y;
224                                 double const d = dx * dx + dy * dy;
225                                 //lyxerr << "itx: " << xo << " ity: " << yo << " d: " << d
226                                 //      << " dx: " << dx << " dy: " << dy
227                                 //      << " idx: " << it.idx() << " pos: " << it.pos()
228                                 //      << " it:\n" << it
229                                 //      << endl;
230                                 // '<=' in order to take the last possible position
231                                 // this is important for clicking behind \sum in e.g. '\sum_i a'
232                                 if (d <= best_dist) {
233                                         //lyxerr << "*" << endl;
234                                         best_dist   = d;
235                                         best_cursor = it;
236                                 }
237                         }
238                 }
239         }
240
241         //lyxerr << "best_dist: " << best_dist << " cur:\n" << best_cursor << endl;
242         if (best_cursor == et)
243                 return false;
244         cur.setCursor(best_cursor);
245         return true;
246 }
247
248 docstring parbreak(Paragraph const & par)
249 {
250         odocstringstream ods;
251         ods << '\n';
252         // only add blank line if we're not in an ERT or Listings inset
253         if (par.ownerCode() != ERT_CODE
254                         && par.ownerCode() != LISTINGS_CODE)
255                 ods << '\n';
256         return ods.str();
257 }
258
259 } // namespace anon
260
261
262 // be careful: this is called from the bv's constructor, too, so
263 // bv functions are not yet available!
264 Cursor::Cursor(BufferView & bv)
265         : DocIterator(), bv_(&bv), anchor_(), x_target_(-1), textTargetOffset_(0),
266           selection_(false), mark_(false), logicalpos_(false),
267           current_font(inherit_font)
268 {}
269
270
271 void Cursor::reset(Inset & inset)
272 {
273         clear();
274         push_back(CursorSlice(inset));
275         anchor_ = DocIterator(inset);
276         clearTargetX();
277         selection_ = false;
278         mark_ = false;
279 }
280
281
282 // this (intentionally) does neither touch anchor nor selection status
283 void Cursor::setCursor(DocIterator const & cur)
284 {
285         DocIterator::operator=(cur);
286 }
287
288
289 void Cursor::dispatch(FuncRequest const & cmd0)
290 {
291         LYXERR(Debug::DEBUG, "cmd: " << cmd0 << '\n' << *this);
292         if (empty())
293                 return;
294
295         fixIfBroken();
296         FuncRequest cmd = cmd0;
297         Cursor safe = *this;
298         
299         // store some values to be used inside of the handlers
300         beforeDispatchCursor_ = *this;
301         for (; depth(); pop()) {
302                 LYXERR(Debug::DEBUG, "Cursor::dispatch: cmd: "
303                         << cmd0 << endl << *this);
304                 BOOST_ASSERT(pos() <= lastpos());
305                 BOOST_ASSERT(idx() <= lastidx());
306                 BOOST_ASSERT(pit() <= lastpit());
307
308                 // The common case is 'LFUN handled, need update', so make the
309                 // LFUN handler's life easier by assuming this as default value.
310                 // The handler can reset the update and val flags if necessary.
311                 disp_.update(Update::FitCursor | Update::Force);
312                 disp_.dispatched(true);
313                 inset().dispatch(*this, cmd);
314                 if (disp_.dispatched())
315                         break;
316         }
317         
318         // it completely to get a 'bomb early' behaviour in case this
319         // object will be used again.
320         if (!disp_.dispatched()) {
321                 LYXERR(Debug::DEBUG, "RESTORING OLD CURSOR!");
322                 operator=(safe);
323                 disp_.update(Update::None);
324                 disp_.dispatched(false);
325         } else {
326                 // restore the previous one because nested Cursor::dispatch calls
327                 // are possible which would change it
328                 beforeDispatchCursor_ = safe.beforeDispatchCursor_;
329         }
330 }
331
332
333 DispatchResult Cursor::result() const
334 {
335         return disp_;
336 }
337
338
339 BufferView & Cursor::bv() const
340 {
341         BOOST_ASSERT(bv_);
342         return *bv_;
343 }
344
345
346 Buffer & Cursor::buffer() const
347 {
348         BOOST_ASSERT(bv_);
349         return bv_->buffer();
350 }
351
352
353 void Cursor::pop()
354 {
355         BOOST_ASSERT(depth() >= 1);
356         pop_back();
357 }
358
359
360 void Cursor::push(Inset & p)
361 {
362         push_back(CursorSlice(p));
363 }
364
365
366 void Cursor::pushBackward(Inset & p)
367 {
368         BOOST_ASSERT(!empty());
369         //lyxerr << "Entering inset " << t << " front" << endl;
370         push(p);
371         p.idxFirst(*this);
372 }
373
374
375 bool Cursor::popBackward()
376 {
377         BOOST_ASSERT(!empty());
378         //lyxerr << "Leaving inset from in front" << endl;
379         inset().notifyCursorLeaves(*this);
380         if (depth() == 1)
381                 return false;
382         pop();
383         return true;
384 }
385
386
387 bool Cursor::popForward()
388 {
389         BOOST_ASSERT(!empty());
390         //lyxerr << "Leaving inset from in back" << endl;
391         const pos_type lp = (depth() > 1) ? (*this)[depth() - 2].lastpos() : 0;
392         inset().notifyCursorLeaves(*this);
393         if (depth() == 1)
394                 return false;
395         pop();
396         pos() += lastpos() - lp + 1;
397         return true;
398 }
399
400
401 int Cursor::currentMode()
402 {
403         BOOST_ASSERT(!empty());
404         for (int i = depth() - 1; i >= 0; --i) {
405                 int res = operator[](i).inset().currentMode();
406                 if (res != Inset::UNDECIDED_MODE)
407                         return res;
408         }
409         return Inset::TEXT_MODE;
410 }
411
412
413 void Cursor::getPos(int & x, int & y) const
414 {
415         Point p = bv().getPos(*this, boundary());
416         x = p.x_;
417         y = p.y_;
418 }
419
420
421 Row const & Cursor::textRow() const
422 {
423         CursorSlice const & cs = innerTextSlice();
424         ParagraphMetrics const & pm = bv().parMetrics(cs.text(), cs.pit());
425         BOOST_ASSERT(!pm.rows().empty());
426         return pm.getRow(pos(), boundary());
427 }
428
429
430 void Cursor::resetAnchor()
431 {
432         anchor_ = *this;
433 }
434
435
436
437 bool Cursor::posBackward()
438 {
439         if (pos() == 0)
440                 return false;
441         --pos();
442         return true;
443 }
444
445
446 bool Cursor::posForward()
447 {
448         if (pos() == lastpos())
449                 return false;
450         ++pos();
451         return true;
452 }
453
454
455 CursorSlice Cursor::anchor() const
456 {
457         BOOST_ASSERT(anchor_.depth() >= depth());
458         CursorSlice normal = anchor_[depth() - 1];
459         if (depth() < anchor_.depth() && top() <= normal) {
460                 // anchor is behind cursor -> move anchor behind the inset
461                 ++normal.pos();
462         }
463         return normal;
464 }
465
466
467 CursorSlice Cursor::selBegin() const
468 {
469         if (!selection())
470                 return top();
471         return anchor() < top() ? anchor() : top();
472 }
473
474
475 CursorSlice Cursor::selEnd() const
476 {
477         if (!selection())
478                 return top();
479         return anchor() > top() ? anchor() : top();
480 }
481
482
483 DocIterator Cursor::selectionBegin() const
484 {
485         if (!selection())
486                 return *this;
487         DocIterator di = (anchor() < top() ? anchor_ : *this);
488         di.resize(depth());
489         return di;
490 }
491
492
493 DocIterator Cursor::selectionEnd() const
494 {
495         if (!selection())
496                 return *this;
497         DocIterator di = (anchor() > top() ? anchor_ : *this);
498         if (di.depth() > depth()) {
499                 di.resize(depth());
500                 ++di.pos();
501         }
502         return di;
503 }
504
505
506 void Cursor::setSelection()
507 {
508         selection() = true;
509         // A selection with no contents is not a selection
510         // FIXME: doesnt look ok
511         if (pit() == anchor().pit() && pos() == anchor().pos())
512                 selection() = false;
513 }
514
515
516 void Cursor::setSelection(DocIterator const & where, int n)
517 {
518         setCursor(where);
519         selection() = true;
520         anchor_ = where;
521         pos() += n;
522 }
523
524
525 void Cursor::clearSelection()
526 {
527         selection() = false;
528         mark() = false;
529         resetAnchor();
530 }
531
532
533 void Cursor::setTargetX(int x)
534 {
535         x_target_ = x;
536         textTargetOffset_ = 0;
537 }
538
539
540 int Cursor::x_target() const
541 {
542         return x_target_;
543 }
544
545
546 void Cursor::clearTargetX()
547 {
548         x_target_ = -1;
549         textTargetOffset_ = 0;
550 }
551
552
553 void Cursor::updateTextTargetOffset()
554 {
555         int x;
556         int y;
557         getPos(x, y);
558         textTargetOffset_ = x - x_target_;
559 }
560
561
562 void Cursor::info(odocstream & os) const
563 {
564         for (int i = 1, n = depth(); i < n; ++i) {
565                 operator[](i).inset().infoize(os);
566                 os << "  ";
567         }
568         if (pos() != 0) {
569                 Inset const * inset = prevInset();
570                 // prevInset() can return 0 in certain case.
571                 if (inset)
572                         prevInset()->infoize2(os);
573         }
574         // overwite old message
575         os << "                    ";
576 }
577
578
579 bool Cursor::selHandle(bool sel)
580 {
581         //lyxerr << "Cursor::selHandle" << endl;
582         if (mark())
583                 sel = true;
584         if (sel == selection())
585                 return false;
586
587         if (!sel)
588                 cap::saveSelection(*this);
589
590         resetAnchor();
591         selection() = sel;
592         return true;
593 }
594
595
596 ostream & operator<<(ostream & os, Cursor const & cur)
597 {
598         os << "\n cursor:                                | anchor:\n";
599         for (size_t i = 0, n = cur.depth(); i != n; ++i) {
600                 os << " " << cur[i] << " | ";
601                 if (i < cur.anchor_.depth())
602                         os << cur.anchor_[i];
603                 else
604                         os << "-------------------------------";
605                 os << "\n";
606         }
607         for (size_t i = cur.depth(), n = cur.anchor_.depth(); i < n; ++i) {
608                 os << "------------------------------- | " << cur.anchor_[i] << "\n";
609         }
610         os << " selection: " << cur.selection_
611            << " x_target: " << cur.x_target_ << endl;
612         return os;
613 }
614
615
616 LyXErr & operator<<(LyXErr & os, Cursor const & cur)
617 {
618         os.stream() << cur;
619         return os;
620 }
621
622
623 } // namespace lyx
624
625
626 ///////////////////////////////////////////////////////////////////
627 //
628 // FIXME: Look here
629 // The part below is the non-integrated rest of the original math
630 // cursor. This should be either generalized for texted or moved
631 // back to mathed (in most cases to InsetMathNest).
632 //
633 ///////////////////////////////////////////////////////////////////
634
635 #include "mathed/InsetMathChar.h"
636 #include "mathed/InsetMathGrid.h"
637 #include "mathed/InsetMathScript.h"
638 #include "mathed/InsetMathUnknown.h"
639 #include "mathed/MathFactory.h"
640 #include "mathed/MathStream.h"
641 #include "mathed/MathSupport.h"
642
643
644 namespace lyx {
645
646 //#define FILEDEBUG 1
647
648
649 bool Cursor::isInside(Inset const * p) const
650 {
651         for (size_t i = 0; i != depth(); ++i)
652                 if (&operator[](i).inset() == p)
653                         return true;
654         return false;
655 }
656
657
658 void Cursor::leaveInset(Inset const & inset)
659 {
660         for (size_t i = 0; i != depth(); ++i) {
661                 if (&operator[](i).inset() == &inset) {
662                         resize(i);
663                         return;
664                 }
665         }
666 }
667
668
669 bool Cursor::openable(MathAtom const & t) const
670 {
671         if (!t->isActive())
672                 return false;
673
674         if (t->lock())
675                 return false;
676
677         if (!selection())
678                 return true;
679
680         // we can't move into anything new during selection
681         if (depth() >= anchor_.depth())
682                 return false;
683         if (t.nucleus() != &anchor_[depth()].inset())
684                 return false;
685
686         return true;
687 }
688
689
690 void Cursor::setScreenPos(int x, int /*y*/)
691 {
692         setTargetX(x);
693         //bruteFind(*this, x, y, 0, bv().workWidth(), 0, bv().workHeight());
694 }
695
696
697
698 void Cursor::plainErase()
699 {
700         cell().erase(pos());
701 }
702
703
704 void Cursor::markInsert()
705 {
706         insert(char_type(0));
707 }
708
709
710 void Cursor::markErase()
711 {
712         cell().erase(pos());
713 }
714
715
716 void Cursor::plainInsert(MathAtom const & t)
717 {
718         cell().insert(pos(), t);
719         ++pos();
720 }
721
722
723 void Cursor::insert(docstring const & str)
724 {
725         for_each(str.begin(), str.end(),
726                  boost::bind(static_cast<void(Cursor::*)(char_type)>
727                              (&Cursor::insert), this, _1));
728 }
729
730
731 void Cursor::insert(char_type c)
732 {
733         //lyxerr << "Cursor::insert char '" << c << "'" << endl;
734         BOOST_ASSERT(!empty());
735         if (inMathed()) {
736                 cap::selClearOrDel(*this);
737                 insert(new InsetMathChar(c));
738         } else {
739                 text()->insertChar(*this, c);
740         }
741 }
742
743
744 void Cursor::insert(MathAtom const & t)
745 {
746         //lyxerr << "Cursor::insert MathAtom '" << t << "'" << endl;
747         macroModeClose();
748         cap::selClearOrDel(*this);
749         plainInsert(t);
750 }
751
752
753 void Cursor::insert(Inset * inset)
754 {
755         if (inMathed())
756                 insert(MathAtom(inset));
757         else
758                 text()->insertInset(*this, inset);
759 }
760
761
762 void Cursor::niceInsert(docstring const & t)
763 {
764         MathData ar;
765         asArray(t, ar);
766         if (ar.size() == 1)
767                 niceInsert(ar[0]);
768         else
769                 insert(ar);
770 }
771
772
773 void Cursor::niceInsert(MathAtom const & t)
774 {
775         macroModeClose();
776         docstring const safe = cap::grabAndEraseSelection(*this);
777         plainInsert(t);
778         // enter the new inset and move the contents of the selection if possible
779         if (t->isActive()) {
780                 posBackward();
781                 // be careful here: don't use 'pushBackward(t)' as this we need to
782                 // push the clone, not the original
783                 pushBackward(*nextInset());
784                 // We may not use niceInsert here (recursion)
785                 MathData ar;
786                 asArray(safe, ar);
787                 insert(ar);
788         }
789 }
790
791
792 void Cursor::insert(MathData const & ar)
793 {
794         macroModeClose();
795         if (selection())
796                 cap::eraseSelection(*this);
797         cell().insert(pos(), ar);
798         pos() += ar.size();
799 }
800
801
802 bool Cursor::backspace()
803 {
804         autocorrect() = false;
805
806         if (selection()) {
807                 cap::eraseSelection(*this);
808                 return true;
809         }
810
811         if (pos() == 0) {
812                 // If empty cell, and not part of a big cell
813                 if (lastpos() == 0 && inset().nargs() == 1) {
814                         popBackward();
815                         // Directly delete empty cell: [|[]] => [|]
816                         if (inMathed()) {
817                                 plainErase();
818                                 resetAnchor();
819                                 return true;
820                         }
821                         // [|], can not delete from inside
822                         return false;
823                 } else {
824                         if (inMathed())
825                                 pullArg();
826                         else
827                                 popBackward();
828                         return true;
829                 }
830         }
831
832         if (inMacroMode()) {
833                 InsetMathUnknown * p = activeMacro();
834                 if (p->name().size() > 1) {
835                         p->setName(p->name().substr(0, p->name().size() - 1));
836                         return true;
837                 }
838         }
839
840         if (pos() != 0 && prevAtom()->nargs() > 0) {
841                 // let's require two backspaces for 'big stuff' and
842                 // highlight on the first
843                 resetAnchor();
844                 selection() = true;
845                 --pos();
846         } else {
847                 --pos();
848                 plainErase();
849         }
850         return true;
851 }
852
853
854 bool Cursor::erase()
855 {
856         autocorrect() = false;
857         if (inMacroMode())
858                 return true;
859
860         if (selection()) {
861                 cap::eraseSelection(*this);
862                 return true;
863         }
864
865         // delete empty cells if possible
866         if (pos() == lastpos() && inset().idxDelete(idx()))
867                 return true;
868
869         // special behaviour when in last position of cell
870         if (pos() == lastpos()) {
871                 bool one_cell = inset().nargs() == 1;
872                 if (one_cell && lastpos() == 0) {
873                         popBackward();
874                         // Directly delete empty cell: [|[]] => [|]
875                         if (inMathed()) {
876                                 plainErase();
877                                 resetAnchor();
878                                 return true;
879                         }
880                         // [|], can not delete from inside
881                         return false;
882                 }
883                 // remove markup
884                 if (!one_cell)
885                         inset().idxGlue(idx());
886                 return true;
887         }
888
889         // 'clever' UI hack: only erase large items if previously slected
890         if (pos() != lastpos() && nextAtom()->nargs() > 0) {
891                 resetAnchor();
892                 selection() = true;
893                 ++pos();
894         } else {
895                 plainErase();
896         }
897
898         return true;
899 }
900
901
902 bool Cursor::up()
903 {
904         macroModeClose();
905         DocIterator save = *this;
906         FuncRequest cmd(selection() ? LFUN_UP_SELECT : LFUN_UP, docstring());
907         this->dispatch(cmd);
908         if (disp_.dispatched())
909                 return true;
910         setCursor(save);
911         autocorrect() = false;
912         return false;
913 }
914
915
916 bool Cursor::down()
917 {
918         macroModeClose();
919         DocIterator save = *this;
920         FuncRequest cmd(selection() ? LFUN_DOWN_SELECT : LFUN_DOWN, docstring());
921         this->dispatch(cmd);
922         if (disp_.dispatched())
923                 return true;
924         setCursor(save);
925         autocorrect() = false;
926         return false;
927 }
928
929
930 bool Cursor::macroModeClose()
931 {
932         if (!inMacroMode())
933                 return false;
934         InsetMathUnknown * p = activeMacro();
935         p->finalize();
936         docstring const s = p->name();
937         --pos();
938         cell().erase(pos());
939
940         // do nothing if the macro name is empty
941         if (s == "\\")
942                 return false;
943
944         // trigger updates of macros, at least, if no full
945         // updates take place anyway
946         updateFlags(Update::Force);
947
948         docstring const name = s.substr(1);
949         InsetMathNest * const in = inset().asInsetMath()->asNestInset();
950         if (in && in->interpretString(*this, s))
951                 return true;
952         MathAtom atom = createInsetMath(name);
953         MathMacro * atomAsMacro = atom.nucleus()->asMacro();
954         if (atomAsMacro) {
955                 // make non-greedy, i.e. don't eat parameters from the right
956                 atomAsMacro->setDisplayMode(MathMacro::DISPLAY_INTERACTIVE_INIT);
957         }
958         plainInsert(atom);
959         return true;
960 }
961
962
963 docstring Cursor::macroName()
964 {
965         return inMacroMode() ? activeMacro()->name() : docstring();
966 }
967
968
969 void Cursor::handleNest(MathAtom const & a, int c)
970 {
971         //lyxerr << "Cursor::handleNest: " << c << endl;
972         MathAtom t = a;
973         asArray(cap::grabAndEraseSelection(*this), t.nucleus()->cell(c));
974         insert(t);
975         posBackward();
976         pushBackward(*nextInset());
977 }
978
979
980 int Cursor::targetX() const
981 {
982         if (x_target() != -1)
983                 return x_target();
984         int x = 0;
985         int y = 0;
986         getPos(x, y);
987         return x;
988 }
989
990
991 int Cursor::textTargetOffset() const
992 {
993         return textTargetOffset_;
994 }
995
996
997 void Cursor::setTargetX()
998 {
999         int x;
1000         int y;
1001         getPos(x, y);
1002         setTargetX(x);
1003 }
1004
1005
1006 bool Cursor::inMacroMode() const
1007 {
1008         if (!inMathed())
1009                 return false;
1010         if (pos() == 0)
1011                 return false;
1012         InsetMathUnknown const * p = prevAtom()->asUnknownInset();
1013         return p && !p->final();
1014 }
1015
1016
1017 InsetMathUnknown * Cursor::activeMacro()
1018 {
1019         return inMacroMode() ? prevAtom().nucleus()->asUnknownInset() : 0;
1020 }
1021
1022
1023 void Cursor::pullArg()
1024 {
1025         // FIXME: Look here
1026         MathData ar = cell();
1027         if (popBackward() && inMathed()) {
1028                 plainErase();
1029                 cell().insert(pos(), ar);
1030                 resetAnchor();
1031         } else {
1032                 //formula()->mutateToText();
1033         }
1034 }
1035
1036
1037 void Cursor::touch()
1038 {
1039         // FIXME: look here
1040 #if 0
1041         DocIterator::const_iterator it = begin();
1042         DocIterator::const_iterator et = end();
1043         for ( ; it != et; ++it)
1044                 it->cell().touch();
1045 #endif
1046 }
1047
1048
1049 void Cursor::normalize()
1050 {
1051         if (idx() > lastidx()) {
1052                 lyxerr << "this should not really happen - 1: "
1053                        << idx() << ' ' << nargs()
1054                        << " in: " << &inset() << endl;
1055                 idx() = lastidx();
1056         }
1057
1058         if (pos() > lastpos()) {
1059                 lyxerr << "this should not really happen - 2: "
1060                         << pos() << ' ' << lastpos() <<  " in idx: " << idx()
1061                        << " in atom: '";
1062                 odocstringstream os;
1063                 WriteStream wi(os, false, true);
1064                 inset().asInsetMath()->write(wi);
1065                 lyxerr << to_utf8(os.str()) << endl;
1066                 pos() = lastpos();
1067         }
1068 }
1069
1070
1071 bool Cursor::upDownInMath(bool up)
1072 {
1073         // Be warned: The 'logic' implemented in this function is highly
1074         // fragile. A distance of one pixel or a '<' vs '<=' _really
1075         // matters. So fiddle around with it only if you think you know
1076         // what you are doing!
1077         int xo = 0;
1078         int yo = 0;
1079         getPos(xo, yo);
1080         xo = theLyXFunc().cursorBeforeDispatchX();
1081         
1082         // check if we had something else in mind, if not, this is the future
1083         // target
1084         if (x_target_ == -1)
1085                 setTargetX(xo);
1086         else if (inset().asInsetText() && xo - textTargetOffset() != x_target()) {
1087                 // In text mode inside the line (not left or right) possibly set a new target_x,
1088                 // but only if we are somewhere else than the previous target-offset.
1089                 
1090                 // We want to keep the x-target on subsequent up/down movements
1091                 // that cross beyond the end of short lines. Thus a special
1092                 // handling when the cursor is at the end of line: Use the new
1093                 // x-target only if the old one was before the end of line
1094                 // or the old one was after the beginning of the line
1095                 bool inRTL = isWithinRtlParagraph(*this);
1096                 bool left;
1097                 bool right;
1098                 if (inRTL) {
1099                         left = pos() == textRow().endpos();
1100                         right = pos() == textRow().pos();
1101                 } else {
1102                         left = pos() == textRow().pos();
1103                         right = pos() == textRow().endpos();
1104                 }
1105                 if ((!left && !right) ||
1106                                 (left && !right && xo < x_target_) ||
1107                                 (!left && right && x_target_ < xo))
1108                         setTargetX(xo);
1109                 else
1110                         xo = targetX();
1111         } else
1112                 xo = targetX();
1113
1114         // try neigbouring script insets
1115         Cursor old = *this;
1116         if (inMathed() && !selection()) {
1117                 // try left
1118                 if (pos() != 0) {
1119                         InsetMathScript const * p = prevAtom()->asScriptInset();
1120                         if (p && p->has(up)) {
1121                                 --pos();
1122                                 push(*const_cast<InsetMathScript*>(p));
1123                                 idx() = p->idxOfScript(up);
1124                                 pos() = lastpos();
1125                                 
1126                                 // we went in the right direction? Otherwise don't jump into the script
1127                                 int x;
1128                                 int y;
1129                                 getPos(x, y);
1130                                 int oy = theLyXFunc().cursorBeforeDispatchY();
1131                                 if ((!up && y <= oy) ||
1132                                                 (up && y >= oy))
1133                                         operator=(old);
1134                                 else
1135                                         return true;
1136                         }
1137                 }
1138                 
1139                 // try right
1140                 if (pos() != lastpos()) {
1141                         InsetMathScript const * p = nextAtom()->asScriptInset();
1142                         if (p && p->has(up)) {
1143                                 push(*const_cast<InsetMathScript*>(p));
1144                                 idx() = p->idxOfScript(up);
1145                                 pos() = 0;
1146                                 
1147                                 // we went in the right direction? Otherwise don't jump into the script
1148                                 int x;
1149                                 int y;
1150                                 getPos(x, y);
1151                                 int oy = theLyXFunc().cursorBeforeDispatchY();
1152                                 if ((!up && y <= oy) ||
1153                                                 (up && y >= oy))
1154                                         operator=(old);
1155                                 else
1156                                         return true;
1157                         }
1158                 }
1159         }
1160                 
1161         // try to find an inset that knows better then we,
1162         if (inset().idxUpDown(*this, up)) {
1163                 //lyxerr << "idxUpDown triggered" << endl;
1164                 // try to find best position within this inset
1165                 if (!selection())
1166                         setCursor(bruteFind2(*this, xo, yo));
1167                 return true;
1168         }
1169         
1170         // any improvement going just out of inset?
1171         if (popBackward() && inMathed()) {
1172                 //lyxerr << "updown: popBackward succeeded" << endl;
1173                 int xnew;
1174                 int ynew;
1175                 int yold = theLyXFunc().cursorBeforeDispatchY();
1176                 getPos(xnew, ynew);
1177                 if (up ? ynew < yold : ynew > yold)
1178                         return true;
1179         }
1180         
1181         // no success, we are probably at the document top or bottom
1182         operator=(old);
1183         return false;
1184 }
1185
1186
1187 bool Cursor::upDownInText(bool up, bool & updateNeeded)
1188 {
1189         BOOST_ASSERT(text());
1190
1191         // where are we?
1192         int xo = 0;
1193         int yo = 0;
1194         getPos(xo, yo);
1195         xo = theLyXFunc().cursorBeforeDispatchX();
1196
1197         // update the targetX - this is here before the "return false"
1198         // to set a new target which can be used by InsetTexts above
1199         // if we cannot move up/down inside this inset anymore
1200         if (x_target_ == -1)
1201                 setTargetX(xo);
1202         else if (xo - textTargetOffset() != x_target() &&
1203                                          depth() == beforeDispatchCursor_.depth()) {
1204                 // In text mode inside the line (not left or right) possibly set a new target_x,
1205                 // but only if we are somewhere else than the previous target-offset.
1206                 
1207                 // We want to keep the x-target on subsequent up/down movements
1208                 // that cross beyond the end of short lines. Thus a special
1209                 // handling when the cursor is at the end of line: Use the new
1210                 // x-target only if the old one was before the end of line
1211                 // or the old one was after the beginning of the line
1212                 bool inRTL = isWithinRtlParagraph(*this);
1213                 bool left;
1214                 bool right;
1215                 if (inRTL) {
1216                         left = pos() == textRow().endpos();
1217                         right = pos() == textRow().pos();
1218                 } else {
1219                         left = pos() == textRow().pos();
1220                         right = pos() == textRow().endpos();
1221                 }
1222                 if ((!left && !right) ||
1223                                 (left && !right && xo < x_target_) ||
1224                                 (!left && right && x_target_ < xo))
1225                         setTargetX(xo);
1226                 else
1227                         xo = targetX();
1228         } else
1229                 xo = targetX();
1230                 
1231         // first get the current line
1232         TextMetrics & tm = bv_->textMetrics(text());
1233         ParagraphMetrics const & pm = tm.parMetrics(pit());
1234         int row;
1235         if (pos() && boundary())
1236                 row = pm.pos2row(pos() - 1);
1237         else
1238                 row = pm.pos2row(pos());
1239                 
1240         // are we not at the start or end?
1241         if (up) {
1242                 if (pit() == 0 && row == 0)
1243                         return false;
1244         } else {
1245                 if (pit() + 1 >= int(text()->paragraphs().size()) &&
1246                                 row + 1 >= int(pm.rows().size()))
1247                         return false;
1248         }       
1249
1250         // with and without selection are handled differently
1251         if (!selection()) {
1252                 int yo = bv().getPos(*this, boundary()).y_;
1253                 Cursor old = *this;
1254                 // To next/previous row
1255                 if (up)
1256                         tm.editXY(*this, xo, yo - textRow().ascent() - 1);
1257                 else
1258                         tm.editXY(*this, xo, yo + textRow().descent() + 1);
1259                 clearSelection();
1260                 
1261                 // This happens when you move out of an inset.
1262                 // And to give the DEPM the possibility of doing
1263                 // something we must provide it with two different
1264                 // cursors. (Lgb)
1265                 Cursor dummy = *this;
1266                 if (dummy == old)
1267                         ++dummy.pos();
1268                 if (bv().checkDepm(dummy, old)) {
1269                         updateNeeded = true;
1270                         // Make sure that cur gets back whatever happened to dummy(Lgb)
1271                         operator=(dummy);
1272                 }
1273         } else {
1274                 // if there is a selection, we stay out of any inset, and just jump to the right position:
1275                 Cursor old = *this;
1276                 if (up) {
1277                         if (row > 0) {
1278                                 top().pos() = min(tm.x2pos(pit(), row - 1, xo), top().lastpos());
1279                         } else if (pit() > 0) {
1280                                 --pit();
1281                                 ParagraphMetrics const & pmcur = bv_->parMetrics(text(), pit());
1282                                 top().pos() = min(tm.x2pos(pit(), pmcur.rows().size() - 1, xo), top().lastpos());
1283                         }
1284                 } else {
1285                         if (row + 1 < int(pm.rows().size())) {
1286                                 top().pos() = min(tm.x2pos(pit(), row + 1, xo), top().lastpos());
1287                         } else if (pit() + 1 < int(text()->paragraphs().size())) {
1288                                 ++pit();
1289                                 top().pos() = min(tm.x2pos(pit(), 0, xo), top().lastpos());
1290                         }
1291                 }
1292
1293                 updateNeeded |= bv().checkDepm(*this, old);
1294         }
1295
1296         updateTextTargetOffset();
1297         return true;
1298 }       
1299
1300
1301 void Cursor::handleFont(string const & font)
1302 {
1303         LYXERR(Debug::DEBUG, font);
1304         docstring safe;
1305         if (selection()) {
1306                 macroModeClose();
1307                 safe = cap::grabAndEraseSelection(*this);
1308         }
1309
1310         if (lastpos() != 0) {
1311                 // something left in the cell
1312                 if (pos() == 0) {
1313                         // cursor in first position
1314                         popBackward();
1315                 } else if (pos() == lastpos()) {
1316                         // cursor in last position
1317                         popForward();
1318                 } else {
1319                         // cursor in between. split cell
1320                         MathData::iterator bt = cell().begin();
1321                         MathAtom at = createInsetMath(from_utf8(font));
1322                         at.nucleus()->cell(0) = MathData(bt, bt + pos());
1323                         cell().erase(bt, bt + pos());
1324                         popBackward();
1325                         plainInsert(at);
1326                 }
1327         } else {
1328                 // nothing left in the cell
1329                 pullArg();
1330                 plainErase();
1331         }
1332         insert(safe);
1333 }
1334
1335
1336 void Cursor::message(docstring const & msg) const
1337 {
1338         theLyXFunc().setMessage(msg);
1339 }
1340
1341
1342 void Cursor::errorMessage(docstring const & msg) const
1343 {
1344         theLyXFunc().setErrorMessage(msg);
1345 }
1346
1347
1348 docstring Cursor::selectionAsString(bool label) const
1349 {
1350         if (!selection())
1351                 return docstring();
1352
1353         if (inTexted()) {
1354                 Buffer const & buffer = bv().buffer();
1355                 ParagraphList const & pars = text()->paragraphs();
1356
1357                 // should be const ...
1358                 pit_type startpit = selBegin().pit();
1359                 pit_type endpit = selEnd().pit();
1360                 size_t const startpos = selBegin().pos();
1361                 size_t const endpos = selEnd().pos();
1362
1363                 if (startpit == endpit)
1364                         return pars[startpit].asString(buffer, startpos, endpos, label);
1365
1366                 // First paragraph in selection
1367                 docstring result = pars[startpit].
1368                         asString(buffer, startpos, pars[startpit].size(), label)
1369                                  + parbreak(pars[startpit]);
1370
1371                 // The paragraphs in between (if any)
1372                 for (pit_type pit = startpit + 1; pit != endpit; ++pit) {
1373                         Paragraph const & par = pars[pit];
1374                         result += par.asString(buffer, 0, par.size(), label)
1375                                   + parbreak(pars[pit]);
1376                 }
1377
1378                 // Last paragraph in selection
1379                 result += pars[endpit].asString(buffer, 0, endpos, label);
1380
1381                 return result;
1382         }
1383
1384         if (inMathed())
1385                 return cap::grabSelection(*this);
1386
1387         return docstring();
1388 }
1389
1390
1391 docstring Cursor::currentState()
1392 {
1393         if (inMathed()) {
1394                 odocstringstream os;
1395                 info(os);
1396                 return os.str();
1397         }
1398
1399         if (inTexted())
1400                 return text()->currentState(*this);
1401
1402         return docstring();
1403 }
1404
1405
1406 docstring Cursor::getPossibleLabel()
1407 {
1408         return inMathed() ? from_ascii("eq:") : text()->getPossibleLabel(*this);
1409 }
1410
1411
1412 Encoding const * Cursor::getEncoding() const
1413 {
1414         if (empty())
1415                 return 0;
1416         int s = 0;
1417         // go up until first non-0 text is hit
1418         // (innermost text is 0 in mathed)
1419         for (s = depth() - 1; s >= 0; --s)
1420                 if (operator[](s).text())
1421                         break;
1422         CursorSlice const & sl = operator[](s);
1423         Text const & text = *sl.text();
1424         Font font = text.getPar(sl.pit()).getFont(
1425                 bv().buffer().params(), sl.pos(), outerFont(sl.pit(), text.paragraphs()));
1426         return font.language()->encoding();
1427 }
1428
1429
1430 void Cursor::undispatched()
1431 {
1432         disp_.dispatched(false);
1433 }
1434
1435
1436 void Cursor::dispatched()
1437 {
1438         disp_.dispatched(true);
1439 }
1440
1441
1442 void Cursor::updateFlags(Update::flags f)
1443 {
1444         disp_.update(f);
1445 }
1446
1447
1448 void Cursor::noUpdate()
1449 {
1450         disp_.update(Update::None);
1451 }
1452
1453
1454 Font Cursor::getFont() const
1455 {
1456         // The logic here should more or less match to the Cursor::setCurrentFont
1457         // logic, i.e. the cursor height should give a hint what will happen
1458         // if a character is entered.
1459         
1460         // HACK. far from being perfect...
1461         // go up until first non-0 text is hit
1462         // (innermost text is 0 in mathed)
1463         int s = 0;
1464         for (s = depth() - 1; s >= 0; --s)
1465                 if (operator[](s).text())
1466                         break;
1467         CursorSlice const & sl = operator[](s);
1468         Text const & text = *sl.text();
1469         Paragraph const & par = text.getPar(sl.pit());
1470         
1471         // on boundary, so we are really at the character before
1472         pos_type pos = sl.pos();
1473         if (pos > 0 && boundary())
1474                 --pos;
1475         
1476         // on space? Take the font before (only for RTL boundary stay)
1477         if (pos > 0) {
1478                 TextMetrics const & tm = bv().textMetrics(&text);
1479                 if (pos == sl.lastpos()
1480                         || (par.isSeparator(pos) 
1481                         && !tm.isRTLBoundary(sl.pit(), pos)))
1482                         --pos;
1483         }
1484         
1485         // get font at the position
1486         Font font = par.getFont(bv().buffer().params(), pos,
1487                 outerFont(sl.pit(), text.paragraphs()));
1488
1489         return font;
1490 }
1491
1492
1493 bool Cursor::fixIfBroken()
1494 {
1495         if (DocIterator::fixIfBroken()) {
1496                         clearSelection();
1497                         resetAnchor();
1498                         return true;
1499         }
1500         return false;
1501 }
1502
1503
1504 bool notifyCursorLeaves(DocIterator const & old, Cursor & cur)
1505 {
1506         // find inset in common
1507         size_type i;
1508         for (i = 0; i < old.depth() && i < cur.depth(); ++i) {
1509                 if (&old.inset() != &cur.inset())
1510                         break;
1511         }
1512         
1513         // notify everything on top of the common part in old cursor,
1514         // but stop if the inset claims the cursor to be invalid now
1515         for (;  i < old.depth(); ++i) {
1516                 if (old[i].inset().notifyCursorLeaves(cur))
1517                         return true;
1518         }
1519         
1520         return false;
1521 }
1522
1523
1524 void Cursor::setCurrentFont()
1525 {
1526         CursorSlice const & cs = innerTextSlice();
1527         Paragraph const & par = cs.paragraph();
1528         pos_type cpit = cs.pit();
1529         pos_type cpos = cs.pos();
1530         Text const & ctext = *cs.text();
1531         TextMetrics const & tm = bv().textMetrics(&ctext);
1532
1533         // are we behind previous char in fact? -> go to that char
1534         if (cpos > 0 && boundary())
1535                 --cpos;
1536
1537         // find position to take the font from
1538         if (cpos != 0) {
1539                 // paragraph end? -> font of last char
1540                 if (cpos == lastpos())
1541                         --cpos;
1542                 // on space? -> look at the words in front of space
1543                 else if (cpos > 0 && par.isSeparator(cpos))     {
1544                         // abc| def -> font of c
1545                         // abc |[WERBEH], i.e. boundary==true -> font of c
1546                         // abc [WERBEH]| def, font of the space
1547                         if (!tm.isRTLBoundary(cpit, cpos))
1548                                 --cpos;
1549                 }
1550         }
1551
1552         // get font
1553         BufferParams const & bufparams = buffer().params();
1554         current_font = par.getFontSettings(bufparams, cpos);
1555         real_current_font = tm.getDisplayFont(cpit, cpos);
1556
1557         // special case for paragraph end
1558         if (cs.pos() == lastpos()
1559             && tm.isRTLBoundary(cpit, cs.pos())
1560             && !boundary()) {
1561                 Language const * lang = par.getParLanguage(bufparams);
1562                 current_font.setLanguage(lang);
1563                 current_font.fontInfo().setNumber(FONT_OFF);
1564                 real_current_font.setLanguage(lang);
1565                 real_current_font.fontInfo().setNumber(FONT_OFF);
1566         }
1567 }
1568
1569
1570 bool Cursor::textUndo()
1571 {
1572         DocIterator dit = *this;
1573         // Undo::textUndo() will modify dit.
1574         if (!bv_->buffer().undo().textUndo(dit))
1575                 return false;
1576         // Set cursor
1577         setCursor(dit);
1578         selection() = false;
1579         resetAnchor();
1580         fixIfBroken();
1581         return true;
1582 }
1583
1584
1585 bool Cursor::textRedo()
1586 {
1587         DocIterator dit = *this;
1588         // Undo::textRedo() will modify dit.
1589         if (!bv_->buffer().undo().textRedo(dit))
1590                 return false;
1591         // Set cursor
1592         setCursor(dit);
1593         selection() = false;
1594         resetAnchor();
1595         fixIfBroken();
1596         return true;
1597 }
1598
1599
1600 void Cursor::finishUndo()
1601 {
1602         bv_->buffer().undo().finishUndo();
1603 }
1604
1605
1606 void Cursor::recordUndo(UndoKind kind, pit_type from, pit_type to)
1607 {
1608         bv_->buffer().undo().recordUndo(*this, kind, from, to);
1609 }
1610
1611
1612 void Cursor::recordUndo(UndoKind kind, pit_type from)
1613 {
1614         bv_->buffer().undo().recordUndo(*this, kind, from);
1615 }
1616
1617
1618 void Cursor::recordUndo(UndoKind kind)
1619 {
1620         bv_->buffer().undo().recordUndo(*this, kind);
1621 }
1622
1623
1624 void Cursor::recordUndoInset(UndoKind kind)
1625 {
1626         bv_->buffer().undo().recordUndoInset(*this, kind);
1627 }
1628
1629
1630 void Cursor::recordUndoFullDocument()
1631 {
1632         bv_->buffer().undo().recordUndoFullDocument(*this);
1633 }
1634
1635
1636 void Cursor::recordUndoSelection()
1637 {
1638         bv_->buffer().undo().recordUndo(*this, ATOMIC_UNDO,
1639                 selBegin().pit(), selEnd().pit());
1640 }
1641
1642
1643 void Cursor::checkBufferStructure()
1644 {
1645         if (paragraph().layout()->toclevel == Layout::NOT_IN_TOC)
1646                 return;
1647         Buffer const * master = buffer().masterBuffer();
1648         master->tocBackend().updateItem(ParConstIterator(*this));
1649         master->structureChanged();
1650 }
1651
1652
1653 } // namespace lyx