]> git.lyx.org Git - lyx.git/blob - src/Cursor.cpp
4785ce32643df208e1bdaa22dc8cd9113faf7e68
[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 Dov Feldstern
9  * \author André Pönitz
10  * \author Stefan Schimanski
11  *
12  * Full author contact details are available in file CREDITS.
13  */
14
15 #include <config.h>
16
17 #include "Bidi.h"
18 #include "Buffer.h"
19 #include "BufferView.h"
20 #include "CoordCache.h"
21 #include "Cursor.h"
22 #include "CutAndPaste.h"
23 #include "DispatchResult.h"
24 #include "Encoding.h"
25 #include "Font.h"
26 #include "FuncCode.h"
27 #include "FuncRequest.h"
28 #include "Language.h"
29 #include "LyXFunc.h" // only for setMessage()
30 #include "LyXRC.h"
31 #include "paragraph_funcs.h"
32 #include "Paragraph.h"
33 #include "ParIterator.h"
34 #include "Row.h"
35 #include "Text.h"
36 #include "TextMetrics.h"
37 #include "TocBackend.h"
38
39 #include "support/lassert.h"
40 #include "support/debug.h"
41 #include "support/docstream.h"
42
43 #include "insets/InsetTabular.h"
44 #include "insets/InsetText.h"
45
46 #include "mathed/InsetMath.h"
47 #include "mathed/InsetMathBrace.h"
48 #include "mathed/InsetMathScript.h"
49 #include "mathed/MacroTable.h"
50 #include "mathed/MathData.h"
51 #include "mathed/MathMacro.h"
52
53 #include <boost/bind.hpp>
54
55 #include <sstream>
56 #include <limits>
57 #include <map>
58
59 using namespace std;
60
61 namespace lyx {
62
63 namespace {
64
65 bool positionable(DocIterator const & cursor, DocIterator const & anchor)
66 {
67         // avoid deeper nested insets when selecting
68         if (cursor.depth() > anchor.depth())
69                 return false;
70
71         // anchor might be deeper, should have same path then
72         for (size_t i = 0; i < cursor.depth(); ++i)
73                 if (&cursor[i].inset() != &anchor[i].inset())
74                         return false;
75
76         // position should be ok.
77         return true;
78 }
79
80
81 // Find position closest to (x, y) in cell given by iter.
82 // Used only in mathed
83 DocIterator bruteFind2(Cursor const & c, int x, int y)
84 {
85         double best_dist = numeric_limits<double>::max();
86
87         DocIterator result;
88
89         DocIterator it = c;
90         it.top().pos() = 0;
91         DocIterator et = c;
92         et.top().pos() = et.top().asInsetMath()->cell(et.top().idx()).size();
93         for (size_t i = 0;; ++i) {
94                 int xo;
95                 int yo;
96                 Inset const * inset = &it.inset();
97                 map<Inset const *, Geometry> const & data =
98                         c.bv().coordCache().getInsets().getData();
99                 map<Inset const *, Geometry>::const_iterator I = data.find(inset);
100
101                 // FIXME: in the case where the inset is not in the cache, this
102                 // means that no part of it is visible on screen. In this case
103                 // we don't do elaborate search and we just return the forwarded
104                 // DocIterator at its beginning.
105                 if (I == data.end()) {
106                         it.top().pos() = 0;
107                         return it;
108                 }
109
110                 Point o = I->second.pos;
111                 inset->cursorPos(c.bv(), it.top(), c.boundary(), xo, yo);
112                 // Convert to absolute
113                 xo += o.x_;
114                 yo += o.y_;
115                 double d = (x - xo) * (x - xo) + (y - yo) * (y - yo);
116                 // '<=' in order to take the last possible position
117                 // this is important for clicking behind \sum in e.g. '\sum_i a'
118                 LYXERR(Debug::DEBUG, "i: " << i << " d: " << d
119                         << " best: " << best_dist);
120                 if (d <= best_dist) {
121                         best_dist = d;
122                         result = it;
123                 }
124                 if (it == et)
125                         break;
126                 it.forwardPos();
127         }
128         return result;
129 }
130
131
132 /*
133 /// moves position closest to (x, y) in given box
134 bool bruteFind(Cursor & cursor,
135         int x, int y, int xlow, int xhigh, int ylow, int yhigh)
136 {
137         LASSERT(!cursor.empty(), return false);
138         Inset & inset = cursor[0].inset();
139         BufferView & bv = cursor.bv();
140
141         CoordCache::InnerParPosCache const & cache =
142                 bv.coordCache().getParPos().find(cursor.bottom().text())->second;
143         // Get an iterator on the first paragraph in the cache
144         DocIterator it(inset);
145         it.push_back(CursorSlice(inset));
146         it.pit() = cache.begin()->first;
147         // Get an iterator after the last paragraph in the cache
148         DocIterator et(inset);
149         et.push_back(CursorSlice(inset));
150         et.pit() = boost::prior(cache.end())->first;
151         if (et.pit() >= et.lastpit())
152                 et = doc_iterator_end(inset);
153         else
154                 ++et.pit();
155
156         double best_dist = numeric_limits<double>::max();;
157         DocIterator best_cursor = et;
158
159         for ( ; it != et; it.forwardPos(true)) {
160                 // avoid invalid nesting when selecting
161                 if (!cursor.selection() || positionable(it, cursor.anchor_)) {
162                         Point p = bv.getPos(it, false);
163                         int xo = p.x_;
164                         int yo = p.y_;
165                         if (xlow <= xo && xo <= xhigh && ylow <= yo && yo <= yhigh) {
166                                 double const dx = xo - x;
167                                 double const dy = yo - y;
168                                 double const d = dx * dx + dy * dy;
169                                 // '<=' in order to take the last possible position
170                                 // this is important for clicking behind \sum in e.g. '\sum_i a'
171                                 if (d <= best_dist) {
172                                         //      lyxerr << "*" << endl;
173                                         best_dist   = d;
174                                         best_cursor = it;
175                                 }
176                         }
177                 }
178         }
179
180         if (best_cursor != et) {
181                 cursor.setCursor(best_cursor);
182                 return true;
183         }
184
185         return false;
186 }
187 */
188
189
190 /// moves position closest to (x, y) in given box
191 bool bruteFind3(Cursor & cur, int x, int y, bool up)
192 {
193         BufferView & bv = cur.bv();
194         int ylow  = up ? 0 : y + 1;
195         int yhigh = up ? y - 1 : bv.workHeight();
196         int xlow = 0;
197         int xhigh = bv.workWidth();
198
199 // FIXME: bit more work needed to get 'from' and 'to' right.
200         pit_type from = cur.bottom().pit();
201         //pit_type to = cur.bottom().pit();
202         //lyxerr << "Pit start: " << from << endl;
203
204         //lyxerr << "bruteFind3: x: " << x << " y: " << y
205         //      << " xlow: " << xlow << " xhigh: " << xhigh
206         //      << " ylow: " << ylow << " yhigh: " << yhigh
207         //      << endl;
208         DocIterator it = doc_iterator_begin(cur.buffer());
209         it.pit() = from;
210         DocIterator et = doc_iterator_end(cur.buffer());
211
212         double best_dist = numeric_limits<double>::max();
213         DocIterator best_cursor = et;
214
215         for ( ; it != et; it.forwardPos()) {
216                 // avoid invalid nesting when selecting
217                 if (bv.cursorStatus(it) == CUR_INSIDE
218                                 && (!cur.selection() || positionable(it, cur.anchor_))) {
219                         Point p = bv.getPos(it, false);
220                         int xo = p.x_;
221                         int yo = p.y_;
222                         if (xlow <= xo && xo <= xhigh && ylow <= yo && yo <= yhigh) {
223                                 double const dx = xo - x;
224                                 double const dy = yo - y;
225                                 double const d = dx * dx + dy * dy;
226                                 //lyxerr << "itx: " << xo << " ity: " << yo << " d: " << d
227                                 //      << " dx: " << dx << " dy: " << dy
228                                 //      << " idx: " << it.idx() << " pos: " << it.pos()
229                                 //      << " it:\n" << it
230                                 //      << endl;
231                                 // '<=' in order to take the last possible position
232                                 // this is important for clicking behind \sum in e.g. '\sum_i a'
233                                 if (d <= best_dist) {
234                                         //lyxerr << "*" << endl;
235                                         best_dist   = d;
236                                         best_cursor = it;
237                                 }
238                         }
239                 }
240         }
241
242         //lyxerr << "best_dist: " << best_dist << " cur:\n" << best_cursor << endl;
243         if (best_cursor == et)
244                 return false;
245         cur.setCursor(best_cursor);
246         return true;
247 }
248
249 docstring parbreak(Paragraph const & par)
250 {
251         odocstringstream os;
252         os << '\n';
253         // only add blank line if we're not in an ERT or Listings inset
254         if (par.ownerCode() != ERT_CODE && par.ownerCode() != LISTINGS_CODE)
255                 os << '\n';
256         return os.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.buffer()), bv_(&bv), anchor_(),
266           x_target_(-1), textTargetOffset_(0),
267           selection_(false), mark_(false), logicalpos_(false),
268           current_font(inherit_font)
269 {}
270
271
272 void Cursor::reset(Inset & inset)
273 {
274         clear();
275         push_back(CursorSlice(inset));
276         anchor_ = doc_iterator_begin(&inset.buffer(), &inset);
277         anchor_.clear();
278         clearTargetX();
279         selection_ = false;
280         mark_ = false;
281 }
282
283
284 // this (intentionally) does neither touch anchor nor selection status
285 void Cursor::setCursor(DocIterator const & cur)
286 {
287         DocIterator::operator=(cur);
288 }
289
290
291 void Cursor::dispatch(FuncRequest const & cmd0)
292 {
293         LYXERR(Debug::DEBUG, "cmd: " << cmd0 << '\n' << *this);
294         if (empty())
295                 return;
296
297         fixIfBroken();
298         FuncRequest cmd = cmd0;
299         Cursor safe = *this;
300         
301         // store some values to be used inside of the handlers
302         beforeDispatchCursor_ = *this;
303         for (; depth(); pop(), boundary(false)) {
304                 LYXERR(Debug::DEBUG, "Cursor::dispatch: cmd: "
305                         << cmd0 << endl << *this);
306                 LASSERT(pos() <= lastpos(), /**/);
307                 LASSERT(idx() <= lastidx(), /**/);
308                 LASSERT(pit() <= lastpit(), /**/);
309
310                 // The common case is 'LFUN handled, need update', so make the
311                 // LFUN handler's life easier by assuming this as default value.
312                 // The handler can reset the update and val flags if necessary.
313                 disp_.update(Update::FitCursor | Update::Force);
314                 disp_.dispatched(true);
315                 inset().dispatch(*this, cmd);
316                 if (disp_.dispatched())
317                         break;
318         }
319         
320         // it completely to get a 'bomb early' behaviour in case this
321         // object will be used again.
322         if (!disp_.dispatched()) {
323                 LYXERR(Debug::DEBUG, "RESTORING OLD CURSOR!");
324                 // We might have invalidated the cursor when removing an empty
325                 // paragraph while the cursor could not be moved out the inset
326                 // while we initially thought we could. This might happen when
327                 // a multiline inset becomes an inline inset when the second 
328                 // paragraph is removed.
329                 if (safe.pit() > safe.lastpit()) {
330                         safe.pit() = safe.lastpit();
331                         safe.pos() = safe.lastpos();
332                 }
333                 operator=(safe);
334                 disp_.update(Update::None);
335                 disp_.dispatched(false);
336         } else {
337                 // restore the previous one because nested Cursor::dispatch calls
338                 // are possible which would change it
339                 beforeDispatchCursor_ = safe.beforeDispatchCursor_;
340         }
341 }
342
343
344 DispatchResult Cursor::result() const
345 {
346         return disp_;
347 }
348
349
350 BufferView & Cursor::bv() const
351 {
352         LASSERT(bv_, /**/);
353         return *bv_;
354 }
355
356
357 void Cursor::pop()
358 {
359         LASSERT(depth() >= 1, /**/);
360         pop_back();
361 }
362
363
364 void Cursor::push(Inset & p)
365 {
366         push_back(CursorSlice(p));
367         p.setBuffer(*buffer());
368 }
369
370
371 void Cursor::pushBackward(Inset & p)
372 {
373         LASSERT(!empty(), return);
374         //lyxerr << "Entering inset " << t << " front" << endl;
375         push(p);
376         p.idxFirst(*this);
377 }
378
379
380 bool Cursor::popBackward()
381 {
382         LASSERT(!empty(), return false);
383         if (depth() == 1)
384                 return false;
385         pop();
386         return true;
387 }
388
389
390 bool Cursor::popForward()
391 {
392         LASSERT(!empty(), return false);
393         //lyxerr << "Leaving inset from in back" << endl;
394         const pos_type lp = (depth() > 1) ? (*this)[depth() - 2].lastpos() : 0;
395         if (depth() == 1)
396                 return false;
397         pop();
398         pos() += lastpos() - lp + 1;
399         return true;
400 }
401
402
403 int Cursor::currentMode()
404 {
405         LASSERT(!empty(), /**/);
406         for (int i = depth() - 1; i >= 0; --i) {
407                 int res = operator[](i).inset().currentMode();
408                 bool locked_mode = operator[](i).inset().lockedMode();
409                 // Also return UNDECIDED_MODE when the mode is locked,
410                 // as in this case it is treated the same as TEXT_MODE
411                 if (res != Inset::UNDECIDED_MODE || locked_mode)
412                         return res;
413         }
414         return Inset::TEXT_MODE;
415 }
416
417
418 void Cursor::getPos(int & x, int & y) const
419 {
420         Point p = bv().getPos(*this, boundary());
421         x = p.x_;
422         y = p.y_;
423 }
424
425
426 Row const & Cursor::textRow() const
427 {
428         CursorSlice const & cs = innerTextSlice();
429         ParagraphMetrics const & pm = bv().parMetrics(cs.text(), cs.pit());
430         LASSERT(!pm.rows().empty(), /**/);
431         return pm.getRow(pos(), boundary());
432 }
433
434
435 void Cursor::resetAnchor()
436 {
437         anchor_ = *this;
438 }
439
440
441 void Cursor::setCursorToAnchor()
442 {
443         if (selection())
444                 setCursor(anchor_);
445 }
446
447
448 bool Cursor::posBackward()
449 {
450         if (pos() == 0)
451                 return false;
452         --pos();
453         return true;
454 }
455
456
457 bool Cursor::posForward()
458 {
459         if (pos() == lastpos())
460                 return false;
461         ++pos();
462         return true;
463 }
464
465
466 bool Cursor::posVisRight(bool skip_inset)
467 {
468         Cursor new_cur = *this; // where we will move to
469         pos_type left_pos; // position visually left of current cursor
470         pos_type right_pos; // position visually right of current cursor
471         bool new_pos_is_RTL; // is new position we're moving to RTL?
472
473         getSurroundingPos(left_pos, right_pos);
474
475         LYXERR(Debug::RTL, left_pos <<"|"<< right_pos << " (pos: "<< pos() <<")");
476
477         // Are we at an inset?
478         new_cur.pos() = right_pos;
479         new_cur.boundary(false);
480         if (!skip_inset &&
481                 text()->checkAndActivateInsetVisual(new_cur, right_pos >= pos(), false)) {
482                 // we actually move the cursor at the end of this function, for now
483                 // we just keep track of the new position in new_cur...
484                 LYXERR(Debug::RTL, "entering inset at: " << new_cur.pos());
485         }
486
487         // Are we already at rightmost pos in row?
488         else if (text()->empty() || right_pos == -1) {
489                 
490                 new_cur = *this;
491                 if (!new_cur.posVisToNewRow(false)) {
492                         LYXERR(Debug::RTL, "not moving!");
493                         return false;
494                 }
495                 
496                 // we actually move the cursor at the end of this function, for now 
497                 // just keep track of the new position in new_cur...
498                 LYXERR(Debug::RTL, "right edge, moving: " << int(new_cur.pit()) << "," 
499                         << int(new_cur.pos()) << "," << (new_cur.boundary() ? 1 : 0));
500
501         }
502         // normal movement to the right
503         else {
504                 new_cur = *this;
505                 // Recall, if the cursor is at position 'x', that means *before* 
506                 // the character at position 'x'. In RTL, "before" means "to the 
507                 // right of", in LTR, "to the left of". So currently our situation
508                 // is this: the position to our right is 'right_pos' (i.e., we're 
509                 // currently to the left of 'right_pos'). In order to move to the 
510                 // right, it depends whether or not the character at 'right_pos' is RTL.
511                 new_pos_is_RTL = paragraph().getFontSettings(
512                         buffer()->params(), right_pos).isVisibleRightToLeft();
513                 // If the character at 'right_pos' *is* LTR, then in order to move to
514                 // the right of it, we need to be *after* 'right_pos', i.e., move to
515                 // position 'right_pos' + 1.
516                 if (!new_pos_is_RTL) {
517                         new_cur.pos() = right_pos + 1;
518                         // set the boundary to true in two situations:
519                         if (
520                         // 1. if new_pos is now lastpos, and we're in an RTL paragraph
521                         // (this means that we're moving right to the end of an LTR chunk
522                         // which is at the end of an RTL paragraph);
523                                 (new_cur.pos() == lastpos()
524                                  && paragraph().isRTL(buffer()->params()))
525                         // 2. if the position *after* right_pos is RTL (we want to be 
526                         // *after* right_pos, not before right_pos + 1!)
527                                 || paragraph().getFontSettings(buffer()->params(),
528                                                 new_cur.pos()).isVisibleRightToLeft()
529                         )
530                                 new_cur.boundary(true);
531                         else // set the boundary to false
532                                 new_cur.boundary(false);
533                 }
534                 // Otherwise (if the character at position 'right_pos' is RTL), then
535                 // moving to the right of it is as easy as setting the new position
536                 // to 'right_pos'.
537                 else {
538                         new_cur.pos() = right_pos;
539                         new_cur.boundary(false);
540                 }
541         
542         }
543
544         bool moved = (new_cur.pos() != pos()
545                                   || new_cur.pit() != pit()
546                                   || new_cur.boundary() != boundary()
547                                   || &new_cur.inset() != &inset());
548         
549         if (moved) {
550                 LYXERR(Debug::RTL, "moving to: " << new_cur.pos() 
551                         << (new_cur.boundary() ? " (boundary)" : ""));
552                 *this = new_cur;
553         }
554
555         return moved;
556 }
557
558
559 bool Cursor::posVisLeft(bool skip_inset)
560 {
561         Cursor new_cur = *this; // where we will move to
562         pos_type left_pos; // position visually left of current cursor
563         pos_type right_pos; // position visually right of current cursor
564         bool new_pos_is_RTL; // is new position we're moving to RTL?
565
566         getSurroundingPos(left_pos, right_pos);
567
568         LYXERR(Debug::RTL, left_pos <<"|"<< right_pos << " (pos: "<< pos() <<")");
569
570         // Are we at an inset?
571         new_cur.pos() = left_pos;
572         new_cur.boundary(false);
573         if (!skip_inset && 
574                 text()->checkAndActivateInsetVisual(new_cur, left_pos >= pos(), true)) {
575                 // we actually move the cursor at the end of this function, for now 
576                 // we just keep track of the new position in new_cur...
577                 LYXERR(Debug::RTL, "entering inset at: " << new_cur.pos());
578         }
579
580         // Are we already at leftmost pos in row?
581         else if (text()->empty() || left_pos == -1) {
582                 
583                 new_cur = *this;
584                 if (!new_cur.posVisToNewRow(true)) {
585                         LYXERR(Debug::RTL, "not moving!");
586                         return false;
587                 }
588                 
589                 // we actually move the cursor at the end of this function, for now 
590                 // just keep track of the new position in new_cur...
591                 LYXERR(Debug::RTL, "left edge, moving: " << int(new_cur.pit()) << "," 
592                         << int(new_cur.pos()) << "," << (new_cur.boundary() ? 1 : 0));
593
594         }
595         // normal movement to the left
596         else {
597                 new_cur = *this;
598                 // Recall, if the cursor is at position 'x', that means *before* 
599                 // the character at position 'x'. In RTL, "before" means "to the 
600                 // right of", in LTR, "to the left of". So currently our situation
601                 // is this: the position to our left is 'left_pos' (i.e., we're 
602                 // currently to the right of 'left_pos'). In order to move to the 
603                 // left, it depends whether or not the character at 'left_pos' is RTL.
604                 new_pos_is_RTL = paragraph().getFontSettings(
605                         buffer()->params(), left_pos).isVisibleRightToLeft();
606                 // If the character at 'left_pos' *is* RTL, then in order to move to
607                 // the left of it, we need to be *after* 'left_pos', i.e., move to
608                 // position 'left_pos' + 1.
609                 if (new_pos_is_RTL) {
610                         new_cur.pos() = left_pos + 1;
611                         // set the boundary to true in two situations:
612                         if (
613                         // 1. if new_pos is now lastpos and we're in an LTR paragraph
614                         // (this means that we're moving left to the end of an RTL chunk
615                         // which is at the end of an LTR paragraph);
616                                 (new_cur.pos() == lastpos()
617                                  && !paragraph().isRTL(buffer()->params()))
618                         // 2. if the position *after* left_pos is not RTL (we want to be 
619                         // *after* left_pos, not before left_pos + 1!)
620                                 || !paragraph().getFontSettings(buffer()->params(),
621                                                 new_cur.pos()).isVisibleRightToLeft()
622                         )
623                                 new_cur.boundary(true);
624                         else // set the boundary to false
625                                 new_cur.boundary(false);
626                 }
627                 // Otherwise (if the character at position 'left_pos' is LTR), then
628                 // moving to the left of it is as easy as setting the new position
629                 // to 'left_pos'.
630                 else {
631                         new_cur.pos() = left_pos;
632                         new_cur.boundary(false);
633                 }
634         
635         }
636
637         bool moved = (new_cur.pos() != pos() 
638                                   || new_cur.pit() != pit()
639                                   || new_cur.boundary() != boundary());
640
641         if (moved) {
642                 LYXERR(Debug::RTL, "moving to: " << new_cur.pos() 
643                         << (new_cur.boundary() ? " (boundary)" : ""));
644                 *this = new_cur;
645         }
646                 
647         return moved;
648 }
649
650
651 void Cursor::getSurroundingPos(pos_type & left_pos, pos_type & right_pos)
652 {
653         // preparing bidi tables
654         Paragraph const & par = paragraph();
655         Buffer const & buf = *buffer();
656         Row const & row = textRow();
657         Bidi bidi;
658         bidi.computeTables(par, buf, row);
659
660         LYXERR(Debug::RTL, "bidi: " << row.pos() << "--" << row.endpos());
661
662         // The cursor is painted *before* the character at pos(), or, if 'boundary'
663         // is true, *after* the character at (pos() - 1). So we already have one
664         // known position around the cursor:
665         pos_type const known_pos = boundary() && pos() > 0 ? pos() - 1 : pos();
666         
667         // edge case: if we're at the end of the paragraph, things are a little 
668         // different (because lastpos is a position which does not really "exist" 
669         // --- there's no character there yet).
670         if (known_pos == lastpos()) {
671                 if (par.isRTL(buf.params())) {
672                         left_pos = -1;
673                         right_pos = bidi.vis2log(row.pos());
674                 } else { 
675                         // LTR paragraph
676                         right_pos = -1;
677                         left_pos = bidi.vis2log(row.endpos() - 1);
678                 }
679                 return;
680         }
681         
682         // Whether 'known_pos' is to the left or to the right of the cursor depends
683         // on whether it is an RTL or LTR character...
684         bool const cur_is_RTL = 
685                 par.getFontSettings(buf.params(), known_pos).isVisibleRightToLeft();
686         // ... in the following manner:
687         // For an RTL character, "before" means "to the right" and "after" means
688         // "to the left"; and for LTR, it's the reverse. So, 'known_pos' is to the
689         // right of the cursor if (RTL && boundary) or (!RTL && !boundary):
690         bool const known_pos_on_right = cur_is_RTL == boundary();
691
692         // So we now know one of the positions surrounding the cursor. Let's 
693         // determine the other one:     
694         if (known_pos_on_right) {
695                 right_pos = known_pos;
696                 // *visual* position of 'left_pos':
697                 pos_type v_left_pos = bidi.log2vis(right_pos) - 1;
698                 // If the position we just identified as 'left_pos' is a "skipped 
699                 // separator" (a separator which is at the logical end of a row,
700                 // except for the last row in a paragraph; such separators are not
701                 // painted, so they "are not really there"; note that in bidi text,
702                 // such a separator could appear visually in the middle of a row),
703                 // set 'left_pos' to the *next* position to the left.
704                 if (bidi.inRange(v_left_pos) 
705                                 && bidi.vis2log(v_left_pos) + 1 == row.endpos() 
706                                 && row.endpos() < lastpos()
707                                 && par.isSeparator(bidi.vis2log(v_left_pos)))
708                         --v_left_pos;
709
710                 // calculate the logical position of 'left_pos', if in row
711                 if (!bidi.inRange(v_left_pos))
712                         left_pos = -1;
713                 else
714                         left_pos = bidi.vis2log(v_left_pos);
715                 // If the position we identified as 'right_pos' is a "skipped 
716                 // separator", set 'right_pos' to the *next* position to the right.
717                 if (right_pos + 1 == row.endpos() && row.endpos() < lastpos() 
718                                 && par.isSeparator(right_pos)) {
719                         pos_type const v_right_pos = bidi.log2vis(right_pos) + 1;
720                         if (!bidi.inRange(v_right_pos))
721                                 right_pos = -1;
722                         else
723                                 right_pos = bidi.vis2log(v_right_pos);
724                 }
725         } else { 
726                 // known_pos is on the left
727                 left_pos = known_pos;
728                 // *visual* position of 'right_pos'
729                 pos_type v_right_pos = bidi.log2vis(left_pos) + 1;
730                 // If the position we just identified as 'right_pos' is a "skipped 
731                 // separator", set 'right_pos' to the *next* position to the right.
732                 if (bidi.inRange(v_right_pos) 
733                                 && bidi.vis2log(v_right_pos) + 1 == row.endpos() 
734                                 && row.endpos() < lastpos()
735                                 && par.isSeparator(bidi.vis2log(v_right_pos)))
736                         ++v_right_pos;
737
738                 // calculate the logical position of 'right_pos', if in row
739                 if (!bidi.inRange(v_right_pos)) 
740                         right_pos = -1;
741                 else
742                         right_pos = bidi.vis2log(v_right_pos);
743                 // If the position we identified as 'left_pos' is a "skipped 
744                 // separator", set 'left_pos' to the *next* position to the left.
745                 if (left_pos + 1 == row.endpos() && row.endpos() < lastpos() 
746                                 && par.isSeparator(left_pos)) {
747                         pos_type const v_left_pos = bidi.log2vis(left_pos) - 1;
748                         if (!bidi.inRange(v_left_pos))
749                                 left_pos = -1;
750                         else
751                                 left_pos = bidi.vis2log(v_left_pos);
752                 }
753         }
754         return;
755 }
756
757
758 bool Cursor::posVisToNewRow(bool movingLeft)
759 {
760         Paragraph const & par = paragraph();
761         Buffer const & buf = *buffer();
762         Row const & row = textRow();
763         bool par_is_LTR = !par.isRTL(buf.params());
764
765         // Inside a table, determining whether to move to the next or previous row
766         // should be done based on the table's direction. 
767         int s = depth() - 1;
768         if (s >= 1 && (*this)[s].inset().asInsetTabular()) {
769                 par_is_LTR = !(*this)[s].inset().asInsetTabular()->isRightToLeft(*this);
770                 LYXERR(Debug::RTL, "Inside table! par_is_LTR=" << (par_is_LTR ? 1 : 0));
771         }
772         
773         // if moving left in an LTR paragraph or moving right in an RTL one, 
774         // move to previous row
775         if (par_is_LTR == movingLeft) {
776                 if (row.pos() == 0) { // we're at first row in paragraph
777                         if (pit() == 0) // no previous paragraph! don't move
778                                 return false;
779                         // move to last pos in previous par
780                         --pit();
781                         pos() = lastpos();
782                         boundary(false);
783                 } else { // move to previous row in this par
784                         pos() = row.pos() - 1; // this is guaranteed to be in previous row
785                         boundary(false);
786                 }
787         }
788         // if moving left in an RTL paragraph or moving right in an LTR one, 
789         // move to next row
790         else {
791                 if (row.endpos() == lastpos()) { // we're at last row in paragraph
792                         if (pit() == lastpit()) // last paragraph! don't move
793                                 return false;
794                         // move to first row in next par
795                         ++pit();
796                         pos() = 0;
797                         boundary(false);
798                 } else { // move to next row in this par
799                         pos() = row.endpos();
800                         boundary(false);
801                 }
802         }
803         
804         // make sure we're at left-/right-most pos in new row
805         posVisToRowExtremity(!movingLeft);
806
807         return true;
808 }
809
810
811 void Cursor::posVisToRowExtremity(bool left)  
812 {
813         // prepare bidi tables
814         Paragraph const & par = paragraph();
815         Buffer const & buf = *buffer();
816         Row const & row = textRow();
817         Bidi bidi;
818         bidi.computeTables(par, buf, row);
819
820         LYXERR(Debug::RTL, "entering extremity: " << pit() << "," << pos() << ","
821                 << (boundary() ? 1 : 0));
822
823         if (left) { // move to leftmost position
824                 // if this is an RTL paragraph, and we're at the last row in the
825                 // paragraph, move to lastpos
826                 if (par.isRTL(buf.params()) && row.endpos() == lastpos())
827                         pos() = lastpos();
828                 else {
829                         pos() = bidi.vis2log(row.pos());
830
831                         // Moving to the leftmost position in the row, the cursor should
832                         // normally be placed to the *left* of the leftmost position.
833                         // A very common exception, though, is if the leftmost character 
834                         // also happens to be the separator at the (logical) end of the row
835                         // --- in this case, the separator is positioned beyond the left 
836                         // margin, and we don't want to move the cursor there (moving to 
837                         // the left of the separator is equivalent to moving to the next
838                         // line). So, in this case we actually want to place the cursor 
839                         // to the *right* of the leftmost position (the separator). 
840                         // Another exception is if we're moving to the logically last 
841                         // position in the row, which is *not* a separator: this means
842                         // that the entire row has no separators (if there were any, the 
843                         // row would have been broken there); and therefore in this case
844                         // we also move to the *right* of the last position (this indicates
845                         // to the user that there is no space after this position, and is 
846                         // consistent with the behavior in the middle of a row --- moving
847                         // right or left moves to the next/previous character; if we were
848                         // to move to the *left* of this position, that would simulate 
849                         // a separator which is not really there!). 
850                         // Finally, there is an exception to the previous exception: if 
851                         // this non-separator-but-last-position-in-row is an inset, then
852                         // we *do* want to stay to the left of it anyway: this is the 
853                         // "boundary" which we simulate at insets.
854                         // Another exception is when row.endpos() is 0.
855                         
856                         // do we want to be to the right of pos?
857                         // as explained above, if at last pos in row, stay to the right
858                         bool const right_of_pos = row.endpos() > 0
859                                 && pos() == row.endpos() - 1 && !par.isInset(pos());
860
861                         // Now we know if we want to be to the left or to the right of pos,
862                         // let's make sure we are where we want to be.
863                         bool const new_pos_is_RTL = 
864                                 par.getFontSettings(buf.params(), pos()).isVisibleRightToLeft();
865
866                         if (new_pos_is_RTL != right_of_pos) {
867                                 ++pos();
868                                 boundary(true);
869                         }
870                 }
871         } else { 
872                 // move to rightmost position
873                 // if this is an LTR paragraph, and we're at the last row in the
874                 // paragraph, move to lastpos
875                 if (!par.isRTL(buf.params()) && row.endpos() == lastpos())
876                         pos() = lastpos();
877                 else {
878                         pos() = row.endpos() > 0 ? bidi.vis2log(row.endpos() - 1) : 0;
879
880                         // Moving to the rightmost position in the row, the cursor should
881                         // normally be placed to the *right* of the rightmost position.
882                         // A very common exception, though, is if the rightmost character 
883                         // also happens to be the separator at the (logical) end of the row
884                         // --- in this case, the separator is positioned beyond the right 
885                         // margin, and we don't want to move the cursor there (moving to 
886                         // the right of the separator is equivalent to moving to the next
887                         // line). So, in this case we actually want to place the cursor 
888                         // to the *left* of the rightmost position (the separator). 
889                         // Another exception is if we're moving to the logically last 
890                         // position in the row, which is *not* a separator: this means
891                         // that the entire row has no separators (if there were any, the 
892                         // row would have been broken there); and therefore in this case
893                         // we also move to the *left* of the last position (this indicates
894                         // to the user that there is no space after this position, and is 
895                         // consistent with the behavior in the middle of a row --- moving
896                         // right or left moves to the next/previous character; if we were
897                         // to move to the *right* of this position, that would simulate 
898                         // a separator which is not really there!). 
899                         // Finally, there is an exception to the previous exception: if 
900                         // this non-separator-but-last-position-in-row is an inset, then
901                         // we *do* want to stay to the right of it anyway: this is the 
902                         // "boundary" which we simulate at insets.
903                         // Another exception is when row.endpos() is 0.
904                         
905                         // do we want to be to the left of pos?
906                         // as explained above, if at last pos in row, stay to the left,
907                         // unless the last position is the same as the first.
908                         bool const left_of_pos = row.endpos() > 0
909                                 && pos() == row.endpos() - 1 && !par.isInset(pos());
910
911                         // Now we know if we want to be to the left or to the right of pos,
912                         // let's make sure we are where we want to be.
913                         bool const new_pos_is_RTL = 
914                                 par.getFontSettings(buf.params(), pos()).isVisibleRightToLeft();
915
916                         if (new_pos_is_RTL == left_of_pos) {
917                                 ++pos();
918                                 boundary(true);
919                         }
920                 }
921         }
922         LYXERR(Debug::RTL, "leaving extremity: " << pit() << "," << pos() << ","
923                 << (boundary() ? 1 : 0));
924 }
925
926
927 CursorSlice Cursor::anchor() const
928 {
929         if (!selection())
930                 return top();
931         LASSERT(anchor_.depth() >= depth(), /**/);
932         CursorSlice normal = anchor_[depth() - 1];
933         if (depth() < anchor_.depth() && top() <= normal) {
934                 // anchor is behind cursor -> move anchor behind the inset
935                 ++normal.pos();
936         }
937         return normal;
938 }
939
940
941 CursorSlice Cursor::selBegin() const
942 {
943         if (!selection())
944                 return top();
945         return anchor() < top() ? anchor() : top();
946 }
947
948
949 CursorSlice Cursor::selEnd() const
950 {
951         if (!selection())
952                 return top();
953         return anchor() > top() ? anchor() : top();
954 }
955
956
957 DocIterator Cursor::selectionBegin() const
958 {
959         if (!selection())
960                 return *this;
961
962         DocIterator di;
963         // FIXME: This is a work-around for the problem that
964         // CursorSlice doesn't keep track of the boundary.
965         if (anchor() == top())
966                 di = anchor_.boundary() > boundary() ? anchor_ : *this;
967         else
968                 di = anchor() < top() ? anchor_ : *this;
969         di.resize(depth());
970         return di;
971 }
972
973
974 DocIterator Cursor::selectionEnd() const
975 {
976         if (!selection())
977                 return *this;
978
979         DocIterator di;
980         // FIXME: This is a work-around for the problem that
981         // CursorSlice doesn't keep track of the boundary.
982         if (anchor() == top())
983                 di = anchor_.boundary() < boundary() ? anchor_ : *this;
984         else
985                 di = anchor() > top() ? anchor_ : *this;
986
987         if (di.depth() > depth()) {
988                 di.resize(depth());
989                 ++di.pos();
990         }
991         return di;
992 }
993
994
995 void Cursor::setSelection()
996 {
997         setSelection(true);
998         // A selection with no contents is not a selection
999         // FIXME: doesnt look ok
1000         if (idx() == anchor().idx() && 
1001             pit() == anchor().pit() && 
1002             pos() == anchor().pos())
1003                 setSelection(false);
1004 }
1005
1006
1007 void Cursor::setSelection(DocIterator const & where, int n)
1008 {
1009         setCursor(where);
1010         setSelection(true);
1011         anchor_ = where;
1012         pos() += n;
1013 }
1014
1015
1016 void Cursor::clearSelection()
1017 {
1018         setSelection(false);
1019         setMark(false);
1020         resetAnchor();
1021 }
1022
1023
1024 void Cursor::setTargetX(int x)
1025 {
1026         x_target_ = x;
1027         textTargetOffset_ = 0;
1028 }
1029
1030
1031 int Cursor::x_target() const
1032 {
1033         return x_target_;
1034 }
1035
1036
1037 void Cursor::clearTargetX()
1038 {
1039         x_target_ = -1;
1040         textTargetOffset_ = 0;
1041 }
1042
1043
1044 void Cursor::updateTextTargetOffset()
1045 {
1046         int x;
1047         int y;
1048         getPos(x, y);
1049         textTargetOffset_ = x - x_target_;
1050 }
1051
1052
1053 void Cursor::info(odocstream & os) const
1054 {
1055         for (int i = 1, n = depth(); i < n; ++i) {
1056                 operator[](i).inset().infoize(os);
1057                 os << "  ";
1058         }
1059         if (pos() != 0) {
1060                 Inset const * inset = prevInset();
1061                 // prevInset() can return 0 in certain case.
1062                 if (inset)
1063                         prevInset()->infoize2(os);
1064         }
1065         // overwite old message
1066         os << "                    ";
1067 }
1068
1069
1070 bool Cursor::selHandle(bool sel)
1071 {
1072         //lyxerr << "Cursor::selHandle" << endl;
1073         if (mark())
1074                 sel = true;
1075         if (sel == selection())
1076                 return false;
1077
1078         if (!sel)
1079                 cap::saveSelection(*this);
1080
1081         resetAnchor();
1082         setSelection(sel);
1083         return true;
1084 }
1085
1086
1087 ostream & operator<<(ostream & os, Cursor const & cur)
1088 {
1089         os << "\n cursor:                                | anchor:\n";
1090         for (size_t i = 0, n = cur.depth(); i != n; ++i) {
1091                 os << " " << cur[i] << " | ";
1092                 if (i < cur.anchor_.depth())
1093                         os << cur.anchor_[i];
1094                 else
1095                         os << "-------------------------------";
1096                 os << "\n";
1097         }
1098         for (size_t i = cur.depth(), n = cur.anchor_.depth(); i < n; ++i) {
1099                 os << "------------------------------- | " << cur.anchor_[i] << "\n";
1100         }
1101         os << " selection: " << cur.selection_
1102            << " x_target: " << cur.x_target_ << endl;
1103         return os;
1104 }
1105
1106
1107 LyXErr & operator<<(LyXErr & os, Cursor const & cur)
1108 {
1109         os.stream() << cur;
1110         return os;
1111 }
1112
1113
1114 } // namespace lyx
1115
1116
1117 ///////////////////////////////////////////////////////////////////
1118 //
1119 // FIXME: Look here
1120 // The part below is the non-integrated rest of the original math
1121 // cursor. This should be either generalized for texted or moved
1122 // back to mathed (in most cases to InsetMathNest).
1123 //
1124 ///////////////////////////////////////////////////////////////////
1125
1126 #include "mathed/InsetMathChar.h"
1127 #include "mathed/InsetMathGrid.h"
1128 #include "mathed/InsetMathScript.h"
1129 #include "mathed/InsetMathUnknown.h"
1130 #include "mathed/MathFactory.h"
1131 #include "mathed/MathStream.h"
1132 #include "mathed/MathSupport.h"
1133
1134
1135 namespace lyx {
1136
1137 //#define FILEDEBUG 1
1138
1139
1140 bool Cursor::isInside(Inset const * p) const
1141 {
1142         for (size_t i = 0; i != depth(); ++i)
1143                 if (&operator[](i).inset() == p)
1144                         return true;
1145         return false;
1146 }
1147
1148
1149 void Cursor::leaveInset(Inset const & inset)
1150 {
1151         for (size_t i = 0; i != depth(); ++i) {
1152                 if (&operator[](i).inset() == &inset) {
1153                         resize(i);
1154                         return;
1155                 }
1156         }
1157 }
1158
1159
1160 bool Cursor::openable(MathAtom const & t) const
1161 {
1162         if (!t->isActive())
1163                 return false;
1164
1165         if (t->lock())
1166                 return false;
1167
1168         if (!selection())
1169                 return true;
1170
1171         // we can't move into anything new during selection
1172         if (depth() >= anchor_.depth())
1173                 return false;
1174         if (t.nucleus() != &anchor_[depth()].inset())
1175                 return false;
1176
1177         return true;
1178 }
1179
1180
1181 void Cursor::setScreenPos(int x, int /*y*/)
1182 {
1183         setTargetX(x);
1184         //bruteFind(*this, x, y, 0, bv().workWidth(), 0, bv().workHeight());
1185 }
1186
1187
1188
1189 void Cursor::plainErase()
1190 {
1191         cell().erase(pos());
1192 }
1193
1194
1195 void Cursor::markInsert()
1196 {
1197         insert(char_type(0));
1198 }
1199
1200
1201 void Cursor::markErase()
1202 {
1203         cell().erase(pos());
1204 }
1205
1206
1207 void Cursor::plainInsert(MathAtom const & t)
1208 {
1209         cell().insert(pos(), t);
1210         ++pos();
1211         inset().setBuffer(bv_->buffer());
1212         inset().initView();
1213 }
1214
1215
1216 void Cursor::insert(docstring const & str)
1217 {
1218         for_each(str.begin(), str.end(),
1219                  boost::bind(static_cast<void(Cursor::*)(char_type)>
1220                              (&Cursor::insert), this, _1));
1221 }
1222
1223
1224 void Cursor::insert(char_type c)
1225 {
1226         //lyxerr << "Cursor::insert char '" << c << "'" << endl;
1227         LASSERT(!empty(), /**/);
1228         if (inMathed()) {
1229                 cap::selClearOrDel(*this);
1230                 insert(new InsetMathChar(c));
1231         } else {
1232                 text()->insertChar(*this, c);
1233         }
1234 }
1235
1236
1237 void Cursor::insert(MathAtom const & t)
1238 {
1239         //lyxerr << "Cursor::insert MathAtom '" << t << "'" << endl;
1240         macroModeClose();
1241         cap::selClearOrDel(*this);
1242         plainInsert(t);
1243 }
1244
1245
1246 void Cursor::insert(Inset * inset0)
1247 {
1248         LASSERT(inset0, /**/);
1249         if (inMathed())
1250                 insert(MathAtom(inset0));
1251         else {
1252                 text()->insertInset(*this, inset0);
1253                 inset0->setBuffer(bv_->buffer());
1254                 inset0->initView();
1255         }
1256 }
1257
1258
1259 void Cursor::niceInsert(docstring const & t, Parse::flags f)
1260 {
1261         MathData ar;
1262         asArray(t, ar, f);
1263         if (ar.size() == 1)
1264                 niceInsert(ar[0]);
1265         else
1266                 insert(ar);
1267 }
1268
1269
1270 void Cursor::niceInsert(MathAtom const & t)
1271 {
1272         macroModeClose();
1273         docstring const safe = cap::grabAndEraseSelection(*this);
1274         plainInsert(t);
1275         // enter the new inset and move the contents of the selection if possible
1276         if (t->isActive()) {
1277                 posBackward();
1278                 // be careful here: don't use 'pushBackward(t)' as this we need to
1279                 // push the clone, not the original
1280                 pushBackward(*nextInset());
1281                 // We may not use niceInsert here (recursion)
1282                 MathData ar;
1283                 asArray(safe, ar);
1284                 insert(ar);
1285         }
1286 }
1287
1288
1289 void Cursor::insert(MathData const & ar)
1290 {
1291         macroModeClose();
1292         if (selection())
1293                 cap::eraseSelection(*this);
1294         cell().insert(pos(), ar);
1295         pos() += ar.size();
1296 }
1297
1298
1299 bool Cursor::backspace()
1300 {
1301         autocorrect() = false;
1302
1303         if (selection()) {
1304                 cap::eraseSelection(*this);
1305                 return true;
1306         }
1307
1308         if (pos() == 0) {
1309                 // If empty cell, and not part of a big cell
1310                 if (lastpos() == 0 && inset().nargs() == 1) {
1311                         popBackward();
1312                         // Directly delete empty cell: [|[]] => [|]
1313                         if (inMathed()) {
1314                                 plainErase();
1315                                 resetAnchor();
1316                                 return true;
1317                         }
1318                         // [|], can not delete from inside
1319                         return false;
1320                 } else {
1321                         if (inMathed())
1322                                 pullArg();
1323                         else
1324                                 popBackward();
1325                         return true;
1326                 }
1327         }
1328
1329         if (inMacroMode()) {
1330                 InsetMathUnknown * p = activeMacro();
1331                 if (p->name().size() > 1) {
1332                         p->setName(p->name().substr(0, p->name().size() - 1));
1333                         return true;
1334                 }
1335         }
1336
1337         if (pos() != 0 && prevAtom()->nargs() > 0) {
1338                 // let's require two backspaces for 'big stuff' and
1339                 // highlight on the first
1340                 resetAnchor();
1341                 setSelection(true);
1342                 --pos();
1343         } else {
1344                 --pos();
1345                 plainErase();
1346         }
1347         return true;
1348 }
1349
1350
1351 bool Cursor::erase()
1352 {
1353         autocorrect() = false;
1354         if (inMacroMode())
1355                 return true;
1356
1357         if (selection()) {
1358                 cap::eraseSelection(*this);
1359                 return true;
1360         }
1361
1362         // delete empty cells if possible
1363         if (pos() == lastpos() && inset().idxDelete(idx()))
1364                 return true;
1365
1366         // special behaviour when in last position of cell
1367         if (pos() == lastpos()) {
1368                 bool one_cell = inset().nargs() == 1;
1369                 if (one_cell && lastpos() == 0) {
1370                         popBackward();
1371                         // Directly delete empty cell: [|[]] => [|]
1372                         if (inMathed()) {
1373                                 plainErase();
1374                                 resetAnchor();
1375                                 return true;
1376                         }
1377                         // [|], can not delete from inside
1378                         return false;
1379                 }
1380                 // remove markup
1381                 if (!one_cell)
1382                         inset().idxGlue(idx());
1383                 return true;
1384         }
1385
1386         // 'clever' UI hack: only erase large items if previously slected
1387         if (pos() != lastpos() && nextAtom()->nargs() > 0) {
1388                 resetAnchor();
1389                 setSelection(true);
1390                 ++pos();
1391         } else {
1392                 plainErase();
1393         }
1394
1395         return true;
1396 }
1397
1398
1399 bool Cursor::up()
1400 {
1401         macroModeClose();
1402         DocIterator save = *this;
1403         FuncRequest cmd(selection() ? LFUN_UP_SELECT : LFUN_UP, docstring());
1404         this->dispatch(cmd);
1405         if (disp_.dispatched())
1406                 return true;
1407         setCursor(save);
1408         autocorrect() = false;
1409         return false;
1410 }
1411
1412
1413 bool Cursor::down()
1414 {
1415         macroModeClose();
1416         DocIterator save = *this;
1417         FuncRequest cmd(selection() ? LFUN_DOWN_SELECT : LFUN_DOWN, docstring());
1418         this->dispatch(cmd);
1419         if (disp_.dispatched())
1420                 return true;
1421         setCursor(save);
1422         autocorrect() = false;
1423         return false;
1424 }
1425
1426
1427 bool Cursor::macroModeClose()
1428 {
1429         if (!inMacroMode())
1430                 return false;
1431         InsetMathUnknown * p = activeMacro();
1432         p->finalize();
1433         MathData selection;
1434         asArray(p->selection(), selection);
1435         docstring const s = p->name();
1436         --pos();
1437         cell().erase(pos());
1438
1439         // do nothing if the macro name is empty
1440         if (s == "\\")
1441                 return false;
1442
1443         // trigger updates of macros, at least, if no full
1444         // updates take place anyway
1445         updateFlags(Update::Force);
1446
1447         docstring const name = s.substr(1);
1448         InsetMathNest * const in = inset().asInsetMath()->asNestInset();
1449         if (in && in->interpretString(*this, s))
1450                 return true;
1451         MathAtom atom = createInsetMath(name);
1452
1453         // try to put argument into macro, if we just inserted a macro
1454         bool macroArg = false;
1455         MathMacro * atomAsMacro = atom.nucleus()->asMacro();
1456         if (atomAsMacro) {
1457                 // macros here are still unfolded (in init mode in fact). So
1458                 // we have to resolve the macro here manually and check its arity
1459                 // to put the selection behind it if arity > 0.
1460                 MacroData const * data = buffer()->getMacro(atomAsMacro->name());
1461                 if (selection.size() > 0 && data && data->numargs() - data->optionals() > 0) {
1462                         macroArg = true;
1463                         atomAsMacro->setDisplayMode(MathMacro::DISPLAY_INTERACTIVE_INIT, 1);
1464                 } else
1465                         // non-greedy case. Do not touch the arguments behind
1466                         atomAsMacro->setDisplayMode(MathMacro::DISPLAY_INTERACTIVE_INIT, 0);
1467         }
1468
1469         // insert remembered selection into first argument of a non-macro
1470         else if (atom.nucleus()->nargs() > 0)
1471                 atom.nucleus()->cell(0).append(selection);
1472         
1473         plainInsert(atom);
1474
1475         // finally put the macro argument behind, if needed
1476         if (macroArg) {
1477                 if (selection.size() > 1)
1478                         plainInsert(MathAtom(new InsetMathBrace(selection)));
1479                 else
1480                         insert(selection);
1481         }
1482         
1483         return true;
1484 }
1485
1486
1487 docstring Cursor::macroName()
1488 {
1489         return inMacroMode() ? activeMacro()->name() : docstring();
1490 }
1491
1492
1493 void Cursor::handleNest(MathAtom const & a, int c)
1494 {
1495         //lyxerr << "Cursor::handleNest: " << c << endl;
1496         MathAtom t = a;
1497         asArray(cap::grabAndEraseSelection(*this), t.nucleus()->cell(c));
1498         insert(t);
1499         posBackward();
1500         pushBackward(*nextInset());
1501 }
1502
1503
1504 int Cursor::targetX() const
1505 {
1506         if (x_target() != -1)
1507                 return x_target();
1508         int x = 0;
1509         int y = 0;
1510         getPos(x, y);
1511         return x;
1512 }
1513
1514
1515 int Cursor::textTargetOffset() const
1516 {
1517         return textTargetOffset_;
1518 }
1519
1520
1521 void Cursor::setTargetX()
1522 {
1523         int x;
1524         int y;
1525         getPos(x, y);
1526         setTargetX(x);
1527 }
1528
1529
1530 bool Cursor::inMacroMode() const
1531 {
1532         if (!inMathed())
1533                 return false;
1534         if (pos() == 0)
1535                 return false;
1536         InsetMathUnknown const * p = prevAtom()->asUnknownInset();
1537         return p && !p->final();
1538 }
1539
1540
1541 InsetMathUnknown * Cursor::activeMacro()
1542 {
1543         return inMacroMode() ? prevAtom().nucleus()->asUnknownInset() : 0;
1544 }
1545
1546
1547 InsetMathUnknown const * Cursor::activeMacro() const
1548 {
1549         return inMacroMode() ? prevAtom().nucleus()->asUnknownInset() : 0;
1550 }
1551
1552
1553 void Cursor::pullArg()
1554 {
1555         // FIXME: Look here
1556         MathData ar = cell();
1557         if (popBackward() && inMathed()) {
1558                 plainErase();
1559                 cell().insert(pos(), ar);
1560                 resetAnchor();
1561         } else {
1562                 //formula()->mutateToText();
1563         }
1564 }
1565
1566
1567 void Cursor::touch()
1568 {
1569         // FIXME: look here
1570 #if 0
1571         DocIterator::const_iterator it = begin();
1572         DocIterator::const_iterator et = end();
1573         for ( ; it != et; ++it)
1574                 it->cell().touch();
1575 #endif
1576 }
1577
1578
1579 void Cursor::normalize()
1580 {
1581         if (idx() > lastidx()) {
1582                 lyxerr << "this should not really happen - 1: "
1583                        << idx() << ' ' << nargs()
1584                        << " in: " << &inset() << endl;
1585                 idx() = lastidx();
1586         }
1587
1588         if (pos() > lastpos()) {
1589                 lyxerr << "this should not really happen - 2: "
1590                         << pos() << ' ' << lastpos() <<  " in idx: " << idx()
1591                        << " in atom: '";
1592                 odocstringstream os;
1593                 WriteStream wi(os, false, true, false);
1594                 inset().asInsetMath()->write(wi);
1595                 lyxerr << to_utf8(os.str()) << endl;
1596                 pos() = lastpos();
1597         }
1598 }
1599
1600
1601 bool Cursor::upDownInMath(bool up)
1602 {
1603         // Be warned: The 'logic' implemented in this function is highly
1604         // fragile. A distance of one pixel or a '<' vs '<=' _really
1605         // matters. So fiddle around with it only if you think you know
1606         // what you are doing!
1607         int xo = 0;
1608         int yo = 0;
1609         getPos(xo, yo);
1610         xo = theLyXFunc().cursorBeforeDispatchX();
1611         
1612         // check if we had something else in mind, if not, this is the future
1613         // target
1614         if (x_target_ == -1)
1615                 setTargetX(xo);
1616         else if (inset().asInsetText() && xo - textTargetOffset() != x_target()) {
1617                 // In text mode inside the line (not left or right) possibly set a new target_x,
1618                 // but only if we are somewhere else than the previous target-offset.
1619                 
1620                 // We want to keep the x-target on subsequent up/down movements
1621                 // that cross beyond the end of short lines. Thus a special
1622                 // handling when the cursor is at the end of line: Use the new
1623                 // x-target only if the old one was before the end of line
1624                 // or the old one was after the beginning of the line
1625                 bool inRTL = isWithinRtlParagraph(*this);
1626                 bool left;
1627                 bool right;
1628                 if (inRTL) {
1629                         left = pos() == textRow().endpos();
1630                         right = pos() == textRow().pos();
1631                 } else {
1632                         left = pos() == textRow().pos();
1633                         right = pos() == textRow().endpos();
1634                 }
1635                 if ((!left && !right) ||
1636                                 (left && !right && xo < x_target_) ||
1637                                 (!left && right && x_target_ < xo))
1638                         setTargetX(xo);
1639                 else
1640                         xo = targetX();
1641         } else
1642                 xo = targetX();
1643
1644         // try neigbouring script insets
1645         Cursor old = *this;
1646         if (inMathed() && !selection()) {
1647                 // try left
1648                 if (pos() != 0) {
1649                         InsetMathScript const * p = prevAtom()->asScriptInset();
1650                         if (p && p->has(up)) {
1651                                 --pos();
1652                                 push(*const_cast<InsetMathScript*>(p));
1653                                 idx() = p->idxOfScript(up);
1654                                 pos() = lastpos();
1655                                 
1656                                 // we went in the right direction? Otherwise don't jump into the script
1657                                 int x;
1658                                 int y;
1659                                 getPos(x, y);
1660                                 int oy = theLyXFunc().cursorBeforeDispatchY();
1661                                 if ((!up && y <= oy) ||
1662                                                 (up && y >= oy))
1663                                         operator=(old);
1664                                 else
1665                                         return true;
1666                         }
1667                 }
1668                 
1669                 // try right
1670                 if (pos() != lastpos()) {
1671                         InsetMathScript const * p = nextAtom()->asScriptInset();
1672                         if (p && p->has(up)) {
1673                                 push(*const_cast<InsetMathScript*>(p));
1674                                 idx() = p->idxOfScript(up);
1675                                 pos() = 0;
1676                                 
1677                                 // we went in the right direction? Otherwise don't jump into the script
1678                                 int x;
1679                                 int y;
1680                                 getPos(x, y);
1681                                 int oy = theLyXFunc().cursorBeforeDispatchY();
1682                                 if ((!up && y <= oy) ||
1683                                                 (up && y >= oy))
1684                                         operator=(old);
1685                                 else
1686                                         return true;
1687                         }
1688                 }
1689         }
1690                 
1691         // try to find an inset that knows better then we,
1692         if (inset().idxUpDown(*this, up)) {
1693                 //lyxerr << "idxUpDown triggered" << endl;
1694                 // try to find best position within this inset
1695                 if (!selection())
1696                         setCursor(bruteFind2(*this, xo, yo));
1697                 return true;
1698         }
1699         
1700         // any improvement going just out of inset?
1701         if (popBackward() && inMathed()) {
1702                 //lyxerr << "updown: popBackward succeeded" << endl;
1703                 int xnew;
1704                 int ynew;
1705                 int yold = theLyXFunc().cursorBeforeDispatchY();
1706                 getPos(xnew, ynew);
1707                 if (up ? ynew < yold : ynew > yold)
1708                         return true;
1709         }
1710         
1711         // no success, we are probably at the document top or bottom
1712         operator=(old);
1713         return false;
1714 }
1715
1716
1717 bool Cursor::atFirstOrLastRow(bool up)
1718 {
1719         TextMetrics const & tm = bv_->textMetrics(text());
1720         ParagraphMetrics const & pm = tm.parMetrics(pit());
1721         
1722         int row;
1723         if (pos() && boundary())
1724                 row = pm.pos2row(pos() - 1);
1725         else
1726                 row = pm.pos2row(pos());
1727         
1728         if (up) {
1729                 if (pit() == 0 && row == 0)
1730                         return true;
1731         } else {
1732                 if (pit() + 1 >= int(text()->paragraphs().size()) &&
1733                                 row + 1 >= int(pm.rows().size()))
1734                         return true;
1735         }
1736         return false;
1737 }
1738
1739 bool Cursor::upDownInText(bool up, bool & updateNeeded)
1740 {
1741         LASSERT(text(), /**/);
1742
1743         // where are we?
1744         int xo = 0;
1745         int yo = 0;
1746         getPos(xo, yo);
1747         xo = theLyXFunc().cursorBeforeDispatchX();
1748
1749         // update the targetX - this is here before the "return false"
1750         // to set a new target which can be used by InsetTexts above
1751         // if we cannot move up/down inside this inset anymore
1752         if (x_target_ == -1)
1753                 setTargetX(xo);
1754         else if (xo - textTargetOffset() != x_target() &&
1755                                          depth() == beforeDispatchCursor_.depth()) {
1756                 // In text mode inside the line (not left or right) possibly set a new target_x,
1757                 // but only if we are somewhere else than the previous target-offset.
1758                 
1759                 // We want to keep the x-target on subsequent up/down movements
1760                 // that cross beyond the end of short lines. Thus a special
1761                 // handling when the cursor is at the end of line: Use the new
1762                 // x-target only if the old one was before the end of line
1763                 // or the old one was after the beginning of the line
1764                 bool inRTL = isWithinRtlParagraph(*this);
1765                 bool left;
1766                 bool right;
1767                 if (inRTL) {
1768                         left = pos() == textRow().endpos();
1769                         right = pos() == textRow().pos();
1770                 } else {
1771                         left = pos() == textRow().pos();
1772                         right = pos() == textRow().endpos();
1773                 }
1774                 if ((!left && !right) ||
1775                                 (left && !right && xo < x_target_) ||
1776                                 (!left && right && x_target_ < xo))
1777                         setTargetX(xo);
1778                 else
1779                         xo = targetX();
1780         } else
1781                 xo = targetX();
1782                 
1783         // first get the current line
1784         TextMetrics & tm = bv_->textMetrics(text());
1785         ParagraphMetrics const & pm = tm.parMetrics(pit());
1786         int row;
1787         if (pos() && boundary())
1788                 row = pm.pos2row(pos() - 1);
1789         else
1790                 row = pm.pos2row(pos());
1791                 
1792         if (atFirstOrLastRow(up)) {
1793                 // Is there a place for the cursor to go ? If yes, we
1794                 // can execute the DEPM, otherwise we should keep the
1795                 // paragraph to host the cursor.
1796                 Cursor dummy = *this;
1797                 bool valid_destination = false;
1798                 for(; dummy.depth(); dummy.pop())
1799                         if (!dummy.atFirstOrLastRow(up)) {
1800                                 valid_destination = true;
1801                                 break;
1802                         }
1803
1804                 // will a next dispatch follow and if there is a new 
1805                 // dispatch will it move the cursor out ?
1806                 if (depth() > 1 && valid_destination) {
1807                         // The cursor hasn't changed yet. This happens when
1808                         // you e.g. move out of an inset. And to give the 
1809                         // DEPM the possibility of doing something we must
1810                         // provide it with two different cursors. (Lgb, vfr)
1811                         dummy = *this;
1812                         dummy.pos() = dummy.pos() == 0 ? dummy.lastpos() : 0;
1813                         dummy.pit() = dummy.pit() == 0 ? dummy.lastpit() : 0;
1814
1815                         updateNeeded |= bv().checkDepm(dummy, *this);
1816                         updateTextTargetOffset();
1817                 }
1818                 return false;
1819         }
1820
1821         // with and without selection are handled differently
1822         if (!selection()) {
1823                 int yo = bv().getPos(*this, boundary()).y_;
1824                 Cursor old = *this;
1825                 // To next/previous row
1826                 if (up)
1827                         tm.editXY(*this, xo, yo - textRow().ascent() - 1);
1828                 else
1829                         tm.editXY(*this, xo, yo + textRow().descent() + 1);
1830                 clearSelection();
1831                 
1832                 // This happens when you move out of an inset.  
1833                 // And to give the DEPM the possibility of doing  
1834                 // something we must provide it with two different  
1835                 // cursors. (Lgb)  
1836                 Cursor dummy = *this;
1837                 if (dummy == old)
1838                         ++dummy.pos();
1839                 if (bv().checkDepm(dummy, old)) {
1840                         updateNeeded = true;
1841                         // Make sure that cur gets back whatever happened to dummy(Lgb) 
1842                         operator=(dummy);
1843                 }
1844         } else {
1845                 // if there is a selection, we stay out of any inset, and just jump to the right position:
1846                 Cursor old = *this;
1847                 if (up) {
1848                         if (row > 0) {
1849                                 top().pos() = min(tm.x2pos(pit(), row - 1, xo), top().lastpos());
1850                         } else if (pit() > 0) {
1851                                 --pit();
1852                                 TextMetrics & tm = bv_->textMetrics(text());
1853                                 if (!tm.contains(pit()))
1854                                         tm.newParMetricsUp();
1855                                 ParagraphMetrics const & pmcur = tm.parMetrics(pit());
1856                                 top().pos() = min(tm.x2pos(pit(), pmcur.rows().size() - 1, xo), top().lastpos());
1857                         }
1858                 } else {
1859                         if (row + 1 < int(pm.rows().size())) {
1860                                 top().pos() = min(tm.x2pos(pit(), row + 1, xo), top().lastpos());
1861                         } else if (pit() + 1 < int(text()->paragraphs().size())) {
1862                                 ++pit();
1863                                 TextMetrics & tm = bv_->textMetrics(text());
1864                                 if (!tm.contains(pit()))
1865                                         tm.newParMetricsDown();
1866                                 top().pos() = min(tm.x2pos(pit(), 0, xo), top().lastpos());
1867                         }
1868                 }
1869
1870                 updateNeeded |= bv().checkDepm(*this, old);
1871         }
1872
1873         updateTextTargetOffset();
1874         return true;
1875 }       
1876
1877
1878 void Cursor::handleFont(string const & font)
1879 {
1880         LYXERR(Debug::DEBUG, font);
1881         docstring safe;
1882         if (selection()) {
1883                 macroModeClose();
1884                 safe = cap::grabAndEraseSelection(*this);
1885         }
1886
1887         recordUndoInset();
1888
1889         if (lastpos() != 0) {
1890                 // something left in the cell
1891                 if (pos() == 0) {
1892                         // cursor in first position
1893                         popBackward();
1894                 } else if (pos() == lastpos()) {
1895                         // cursor in last position
1896                         popForward();
1897                 } else {
1898                         // cursor in between. split cell
1899                         MathData::iterator bt = cell().begin();
1900                         MathAtom at = createInsetMath(from_utf8(font));
1901                         at.nucleus()->cell(0) = MathData(bt, bt + pos());
1902                         cell().erase(bt, bt + pos());
1903                         popBackward();
1904                         plainInsert(at);
1905                 }
1906         } else {
1907                 // nothing left in the cell
1908                 popBackward();
1909                 plainErase();
1910                 resetAnchor();
1911         }
1912         insert(safe);
1913 }
1914
1915
1916 void Cursor::message(docstring const & msg) const
1917 {
1918         theLyXFunc().setMessage(msg);
1919 }
1920
1921
1922 void Cursor::errorMessage(docstring const & msg) const
1923 {
1924         theLyXFunc().setErrorMessage(msg);
1925 }
1926
1927
1928 docstring Cursor::selectionAsString(bool with_label) const
1929 {
1930         if (!selection())
1931                 return docstring();
1932
1933         int const label = with_label
1934                 ? AS_STR_LABEL | AS_STR_INSETS : AS_STR_INSETS;
1935
1936         if (inTexted()) {
1937                 idx_type const startidx = selBegin().idx();
1938                 idx_type const endidx = selEnd().idx();
1939                 if (startidx != endidx) {
1940                         // multicell selection
1941                         InsetTabular * table = inset().asInsetTabular();
1942                         LASSERT(table, return docstring());
1943                         return table->asString(startidx, endidx);
1944                 }
1945                 
1946                 ParagraphList const & pars = text()->paragraphs();
1947
1948                 pit_type const startpit = selBegin().pit();
1949                 pit_type const endpit = selEnd().pit();
1950                 size_t const startpos = selBegin().pos();
1951                 size_t const endpos = selEnd().pos();
1952
1953                 if (startpit == endpit)
1954                         return pars[startpit].asString(startpos, endpos, label);
1955
1956                 // First paragraph in selection
1957                 docstring result = pars[startpit].
1958                         asString(startpos, pars[startpit].size(), label)
1959                                  + parbreak(pars[startpit]);
1960
1961                 // The paragraphs in between (if any)
1962                 for (pit_type pit = startpit + 1; pit != endpit; ++pit) {
1963                         Paragraph const & par = pars[pit];
1964                         result += par.asString(0, par.size(), label)
1965                                   + parbreak(pars[pit]);
1966                 }
1967
1968                 // Last paragraph in selection
1969                 result += pars[endpit].asString(0, endpos, label);
1970
1971                 return result;
1972         }
1973
1974         if (inMathed())
1975                 return cap::grabSelection(*this);
1976
1977         return docstring();
1978 }
1979
1980
1981 docstring Cursor::currentState() const
1982 {
1983         if (inMathed()) {
1984                 odocstringstream os;
1985                 info(os);
1986                 return os.str();
1987         }
1988
1989         if (inTexted())
1990                 return text()->currentState(*this);
1991
1992         return docstring();
1993 }
1994
1995
1996 docstring Cursor::getPossibleLabel() const
1997 {
1998         return inMathed() ? from_ascii("eq:") : text()->getPossibleLabel(*this);
1999 }
2000
2001
2002 Encoding const * Cursor::getEncoding() const
2003 {
2004         if (empty())
2005                 return 0;
2006         CursorSlice const & sl = innerTextSlice();
2007         Text const & text = *sl.text();
2008         Font font = text.getPar(sl.pit()).getFont(
2009                 bv().buffer().params(), sl.pos(), outerFont(sl.pit(), text.paragraphs()));
2010         return font.language()->encoding();
2011 }
2012
2013
2014 void Cursor::undispatched()
2015 {
2016         disp_.dispatched(false);
2017 }
2018
2019
2020 void Cursor::dispatched()
2021 {
2022         disp_.dispatched(true);
2023 }
2024
2025
2026 void Cursor::updateFlags(Update::flags f)
2027 {
2028         disp_.update(f);
2029 }
2030
2031
2032 void Cursor::noUpdate()
2033 {
2034         disp_.update(Update::None);
2035 }
2036
2037
2038 Font Cursor::getFont() const
2039 {
2040         // The logic here should more or less match to the Cursor::setCurrentFont
2041         // logic, i.e. the cursor height should give a hint what will happen
2042         // if a character is entered.
2043         
2044         // HACK. far from being perfect...
2045
2046         CursorSlice const & sl = innerTextSlice();
2047         Text const & text = *sl.text();
2048         Paragraph const & par = text.getPar(sl.pit());
2049         
2050         // on boundary, so we are really at the character before
2051         pos_type pos = sl.pos();
2052         if (pos > 0 && boundary())
2053                 --pos;
2054         
2055         // on space? Take the font before (only for RTL boundary stay)
2056         if (pos > 0) {
2057                 TextMetrics const & tm = bv().textMetrics(&text);
2058                 if (pos == sl.lastpos()
2059                         || (par.isSeparator(pos) 
2060                         && !tm.isRTLBoundary(sl.pit(), pos)))
2061                         --pos;
2062         }
2063         
2064         // get font at the position
2065         Font font = par.getFont(buffer()->params(), pos,
2066                 outerFont(sl.pit(), text.paragraphs()));
2067
2068         return font;
2069 }
2070
2071
2072 bool Cursor::fixIfBroken()
2073 {
2074         if (DocIterator::fixIfBroken()) {
2075                         clearSelection();
2076                         return true;
2077         }
2078         return false;
2079 }
2080
2081
2082 bool notifyCursorLeavesOrEnters(Cursor const & old, Cursor & cur)
2083 {
2084         // find inset in common
2085         size_type i;
2086         for (i = 0; i < old.depth() && i < cur.depth(); ++i) {
2087                 if (&old[i].inset() != &cur[i].inset())
2088                         break;
2089         }
2090
2091         // update words if we just moved to another paragraph
2092         if (i == old.depth() && i == cur.depth()
2093             && !cur.buffer()->isClean()
2094             && cur.inTexted() && old.inTexted()
2095             && cur.pit() != old.pit()) {
2096                 old.paragraph().updateWords();
2097         }
2098
2099         // notify everything on top of the common part in old cursor,
2100         // but stop if the inset claims the cursor to be invalid now
2101         for (size_type j = i; j < old.depth(); ++j) {
2102                 Cursor insetPos = old;
2103                 insetPos.cutOff(j);
2104                 if (old[j].inset().notifyCursorLeaves(insetPos, cur))
2105                         return true;
2106         }
2107
2108         // notify everything on top of the common part in new cursor,
2109         // but stop if the inset claims the cursor to be invalid now
2110         for (; i < cur.depth(); ++i) {
2111                 if (cur[i].inset().notifyCursorEnters(cur))
2112                         return true;
2113         }
2114         
2115         return false;
2116 }
2117
2118
2119 void Cursor::setCurrentFont()
2120 {
2121         CursorSlice const & cs = innerTextSlice();
2122         Paragraph const & par = cs.paragraph();
2123         pos_type cpit = cs.pit();
2124         pos_type cpos = cs.pos();
2125         Text const & ctext = *cs.text();
2126         TextMetrics const & tm = bv().textMetrics(&ctext);
2127
2128         // are we behind previous char in fact? -> go to that char
2129         if (cpos > 0 && boundary())
2130                 --cpos;
2131
2132         // find position to take the font from
2133         if (cpos != 0) {
2134                 // paragraph end? -> font of last char
2135                 if (cpos == lastpos())
2136                         --cpos;
2137                 // on space? -> look at the words in front of space
2138                 else if (cpos > 0 && par.isSeparator(cpos))     {
2139                         // abc| def -> font of c
2140                         // abc |[WERBEH], i.e. boundary==true -> font of c
2141                         // abc [WERBEH]| def, font of the space
2142                         if (!tm.isRTLBoundary(cpit, cpos))
2143                                 --cpos;
2144                 }
2145         }
2146
2147         // get font
2148         BufferParams const & bufparams = buffer()->params();
2149         current_font = par.getFontSettings(bufparams, cpos);
2150         real_current_font = tm.displayFont(cpit, cpos);
2151
2152         // special case for paragraph end
2153         if (cs.pos() == lastpos()
2154             && tm.isRTLBoundary(cpit, cs.pos())
2155             && !boundary()) {
2156                 Language const * lang = par.getParLanguage(bufparams);
2157                 current_font.setLanguage(lang);
2158                 current_font.fontInfo().setNumber(FONT_OFF);
2159                 real_current_font.setLanguage(lang);
2160                 real_current_font.fontInfo().setNumber(FONT_OFF);
2161         }
2162 }
2163
2164
2165 bool Cursor::textUndo()
2166 {
2167         DocIterator dit = *this;
2168         // Undo::textUndo() will modify dit.
2169         if (!buffer()->undo().textUndo(dit))
2170                 return false;
2171         // Set cursor
2172         setCursor(dit);
2173         clearSelection();
2174         fixIfBroken();
2175         return true;
2176 }
2177
2178
2179 bool Cursor::textRedo()
2180 {
2181         DocIterator dit = *this;
2182         // Undo::textRedo() will modify dit.
2183         if (!buffer()->undo().textRedo(dit))
2184                 return false;
2185         // Set cursor
2186         setCursor(dit);
2187         clearSelection();
2188         fixIfBroken();
2189         return true;
2190 }
2191
2192
2193 void Cursor::finishUndo() const
2194 {
2195         buffer()->undo().finishUndo();
2196 }
2197
2198
2199 void Cursor::beginUndoGroup() const
2200 {
2201         buffer()->undo().beginUndoGroup();
2202 }
2203
2204
2205 void Cursor::endUndoGroup() const
2206 {
2207         buffer()->undo().endUndoGroup();
2208 }
2209
2210
2211 void Cursor::recordUndo(UndoKind kind, pit_type from, pit_type to) const
2212 {
2213         buffer()->undo().recordUndo(*this, kind, from, to);
2214 }
2215
2216
2217 void Cursor::recordUndo(UndoKind kind, pit_type from) const
2218 {
2219         buffer()->undo().recordUndo(*this, kind, from);
2220 }
2221
2222
2223 void Cursor::recordUndo(UndoKind kind) const
2224 {
2225         buffer()->undo().recordUndo(*this, kind);
2226 }
2227
2228
2229 void Cursor::recordUndoInset(UndoKind kind) const
2230 {
2231         buffer()->undo().recordUndoInset(*this, kind);
2232 }
2233
2234
2235 void Cursor::recordUndoFullDocument() const
2236 {
2237         buffer()->undo().recordUndoFullDocument(*this);
2238 }
2239
2240
2241 void Cursor::recordUndoSelection() const
2242 {
2243         if (inMathed()) {
2244                 if (cap::multipleCellsSelected(*this))
2245                         recordUndoInset();
2246                 else
2247                         recordUndo();
2248         } else {
2249                 buffer()->undo().recordUndo(*this, ATOMIC_UNDO,
2250                         selBegin().pit(), selEnd().pit());
2251         }
2252 }
2253
2254
2255 void Cursor::checkBufferStructure()
2256 {
2257         Buffer const * master = buffer()->masterBuffer();
2258         master->tocBackend().updateItem(*this);
2259         if (master != buffer() && !master->hasGuiDelegate())
2260                 // In case the master has no gui associated with it, 
2261                 // the TocItem is not updated (part of bug 5699).
2262                 buffer()->tocBackend().updateItem(*this);
2263 }
2264
2265
2266 } // namespace lyx