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