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