1 // -*- C++ -*-
2 /**
3  * \file Cursor.h
4  * This file is part of LyX, the document processor.
5  * Licence details can be found in the file COPYING.
6  *
7  * \author André Pönitz
8  *
9  * Full author contact details are available in file CREDITS.
10  */
11 
12 /*
13 First some explanation about what a Cursor really is, from local to
14 global.
15 
16 * a CursorSlice indicates the position of the cursor at local level.
17   It contains in particular:
18   * idx(): the cell that contains the cursor (for Tabular or math
19            arrays). Always 0 for 'plain' insets
20   * pit(): the index of the current paragraph (only for text)
21   * pos(): the position in the current paragraph (or in the math
22            equation in mathed).
23   * inset(): the inset in which the cursor is. For a InsetTabular,
24     this is the tabular itself, not the cell inset (which is an
25     InsetTableCell).
26 
27 * a DocIterator indicated the position of the cursor in the document.
28   It knows about the current buffer (buffer() method) and contains a
29   vector of CursorSlices that describes the nesting of insets up to the
30   point of interest. Note that operator<< has been implemented, so that
31   one can send a DocIterator to a stream to see its value. Try it, it is
32   very helpful to understand the cursor layout.
33   * when using idx/pit/pos on a DocIterator, one gets the information
34     from the inner slice (this slice can be accessed as top())
35   * inMathed() returns true when the cursor is in a math formula
36   * inTexted() returns true when the cursor is in text
37   * innerTextSlice() returns the deepest slice that is text (useful
38     when one is in a math equation and looks for the enclosing text)
39 
40 * A CursorData is a descendant of Dociterator that contains
41   * a second DocIterator object, the anchor, that is useful when
42     selecting.
43   * some other data that describes current selection, cursor font, etc.
44 
45   This class is mostly used only for undo and contains the Cursor
46   elements that are not GUI-related. In LyX 2.0, Cursor was directly
47   deriving from DocIterator
48 
49 * A Cursor is a descendant of CursorData that contains interesting
50   display-related information, in particular targetX(), the horizontal
51   position of the cursor in pixels.
52 */
53 
54 #ifndef LCURSOR_H
55 #define LCURSOR_H
56 
57 #include "DispatchResult.h"
58 #include "DocIterator.h"
59 #include "Font.h"
60 #include "Undo.h"
61 
62 #include "mathed/MathParser_flags.h"
63 
64 
65 namespace lyx {
66 
67 class Buffer;
68 class BufferView;
69 class FuncStatus;
70 class FuncRequest;
71 class Row;
72 
73 // these should go
74 class InsetMathUnknown;
75 
76 /**
77  * This class describes the position of a cursor within a document,
78  * but does not contain any detail about the view. It is currently
79  * only used to save cursor position in Undo, but culd be extended to
80  * handle the methods that only need this data.
81  **/
82 class CursorData : public DocIterator
83 {
84 public:
85 	///
86 	CursorData();
87 	///
88 	explicit CursorData(Buffer * buffer);
89 	///
90 	explicit CursorData(DocIterator const & dit);
91 	/// output
92 	friend std::ostream & operator<<(std::ostream & os, CursorData const & cur);
93 	friend LyXErr & operator<<(LyXErr & os, CursorData const & cur);
94 
95 protected:
96 	/// the anchor position
97 	DocIterator anchor_;
98 	/// do we have a selection?
99 	bool selection_;
100 	/// are we on the way to get one?
101 	bool mark_;
102 	/// are we in word-selection mode? This is set when double clicking.
103 	bool word_selection_;
104 
105 // FIXME: make them protected.
106 public:
107 	/// the current font settings
108 	Font current_font;
109 	/// the current font
110 	Font real_current_font;
111 
112 protected:
113 
114 	//
115 	// math specific stuff that could be promoted to "global" later
116 	//
117 	/// do we allow autocorrection
118 	bool autocorrect_;
119 	/// are we entering a macro name?
120 	bool macromode_;
121 };
122 
123 
124 /// The cursor class describes the position of a cursor within a document.
125 class Cursor : public CursorData
126 {
127 public:
128 	/// create the cursor of a BufferView
129 	explicit Cursor(BufferView & bv);
130 
131 	/// returns true if we made a decision
132 	bool getStatus(FuncRequest const & cmd, FuncStatus & flag) const;
133 	/// dispatch from innermost inset upwards
134 	void dispatch(FuncRequest const & cmd);
135 	/// get the resut of the last dispatch
136 	DispatchResult const & result() const;
137 	/// add a new cursor slice
138 	void push(Inset & inset);
139 	/// add a new cursor slice, place cursor at front (move backwards)
140 	void pushBackward(Inset & inset);
141 	/// pop one level off the cursor
142 	void pop();
143 	/// pop one slice off the cursor stack and go backwards
144 	bool popBackward();
145 	/// pop one slice off the cursor stack and go forward
146 	bool popForward();
147 	/// make sure we are outside of given inset
148 	void leaveInset(Inset const & inset);
149 	/// set the cursor data
150 	void setCursorData(CursorData const & data);
151 	/// sets cursor part
152 	/// this (intentionally) does neither touch anchor nor selection status
153 	void setCursor(DocIterator const & it);
154 	/// set the cursor to dit normalised against the anchor, and set selection.
155 	void setCursorSelectionTo(DocIterator dit);
156 	/// sets the cursor to the normalized selection anchor
157 	void setCursorToAnchor();
158 
159 	///
160 	void setCurrentFont();
161 
162 	//
163 	// selection
164 	//
165 	/// selection active?
selection()166 	bool selection() const { return selection_; }
167 	/// set selection; this is lower level than (set|clear)Selection
selection(bool sel)168 	void selection(bool sel) { selection_ = sel; }
169 	/// do we have a multicell selection?
selIsMultiCell()170 	bool selIsMultiCell() const
171 		{ return selection_ && selBegin().idx() != selEnd().idx(); }
172 	/// do we have a multiline selection?
selIsMultiLine()173 	bool selIsMultiLine() const
174 		{ return selection_ && selBegin().pit() != selEnd().pit(); }
175 	///
setWordSelection(bool set)176 	void setWordSelection(bool set) { word_selection_ = set; }
177 	///
wordSelection()178 	bool wordSelection() { return word_selection_; }
179 	/// did we place the anchor?
mark()180 	bool mark() const { return mark_; }
181 	/// did we place the anchor?
setMark(bool mark)182 	void setMark(bool mark) { mark_ = mark; }
183 	///
184 	void setSelection();
185 	/// set selection at given position
186 	void setSelection(DocIterator const & where, int n);
187 	///
188 	void clearSelection();
189 	/// check whether selection contains specific type of inset
190 	/// returns 0 if no selection was made
191 	bool insetInSelection(InsetCode const & inset);
192 	/// count occurences of specific inset type in the selection
193 	/// returns 0 if no selection was made
194 	int countInsetsInSelection(InsetCode const & inset);
195 	/// access start of selection
196 	CursorSlice selBegin() const;
197 	/// access end of selection
198 	CursorSlice selEnd() const;
199 	/// access start of selection
200 	DocIterator selectionBegin() const;
201 	/// access end of selection
202 	DocIterator selectionEnd() const;
203 	/**
204 	 * Update the selection status and save permanent
205 	 * selection if needed.
206 	 * @param selecting the new selection status
207 	 * @return whether the selection status has changed
208 	 */
209 	bool selHandle(bool selecting);
210 	///
211 	docstring selectionAsString(bool with_label) const;
212 	///
213 	docstring currentState(bool devel_mode) const;
214 
215 	/// auto-correct mode
autocorrect()216 	bool autocorrect() const { return autocorrect_; }
217 	/// auto-correct mode
autocorrect()218 	bool & autocorrect() { return autocorrect_; }
219 	/// are we entering a macro name?
macromode()220 	bool macromode() const { return macromode_; }
221 	/// are we entering a macro name?
macromode()222 	bool & macromode() { return macromode_; }
223 
224 	/// returns x,y position
225 	void getPos(int & x, int & y) const;
226 	/// return logical positions between which the cursor is situated
227 	/**
228 	 * If the cursor is at the edge of a row, the position which is "over the
229 	 * edge" will be returned as -1.
230 	 */
231 	void getSurroundingPos(pos_type & left_pos, pos_type & right_pos) const;
232 	/// the row in the paragraph we're in
233 	Row const & textRow() const;
234 
235 	//
236 	// common part
237 	//
238 	/// move visually one step to the right
239 	/**
240 	 * @note: This method may move into an inset unless skip_inset == true.
241 	 * @note: This method may move into a new paragraph.
242 	 * @note: This method may move out of the current slice.
243 	 * @return: true if moved, false if not moved
244 	 */
245 	bool posVisRight(bool skip_inset = false);
246 	/// move visually one step to the left
247 	/**
248 	 * @note: This method may move into an inset unless skip_inset == true.
249 	 * @note: This method may move into a new paragraph.
250 	 * @note: This method may move out of the current slice.
251 	 * @return: true if moved, false if not moved
252 	 */
253 	bool posVisLeft(bool skip_inset = false);
254 	/// move visually to next/previous row
255 	/**
256 	 * Assuming we were to keep moving left (right) from the current cursor
257 	 * position, place the cursor at the rightmost (leftmost) edge of the
258 	 * new row to which we would move according to visual-mode cursor movement.
259 	 * This could be either the next or the previous row, depending on the
260 	 * direction in which we're moving, and whether we're in an LTR or RTL
261 	 * paragraph.
262 	 * @note: The new position may even be in a new paragraph.
263 	 * @note: This method will not move out of the current slice.
264 	 * @return: false if not moved (no more rows to move to in given direction)
265 	 * @return: true if moved
266 	 */
267 	bool posVisToNewRow(bool movingLeft);
268 	/// move to right or left extremity of the current row
269 	void posVisToRowExtremity(bool left);
270 	/// Should interpretation of the arrow keys be reversed?
271 	bool reverseDirectionNeeded() const;
272 
273 	/// insert an inset
274 	void insert(Inset *);
275 	/// insert a single char
276 	void insert(char_type c);
277 	/// insert a string
278 	void insert(docstring const & str);
279 
280 	/// FIXME: rename to something sensible showing difference to x_target()
281 	/// in pixels from left of screen, set to current position if unset
282 	int targetX() const;
283 	/// set the targetX to x
284 	void setTargetX(int x);
285 	/// return targetX or -1 if unset
286 	int x_target() const;
287 	/// set targetX to current position
288 	void setTargetX();
289 	/// clear targetX, i.e. set it to -1
290 	void clearTargetX();
291 	/// set offset to actual position - targetX
292 	void updateTextTargetOffset();
293 	/// distance between actual and targeted position during last up/down in text
294 	int textTargetOffset() const;
295 
296 	/// access to normalized selection anchor
297 	CursorSlice normalAnchor() const;
298 	/// access to real selection anchor
realAnchor()299 	DocIterator const & realAnchor() const { return anchor_; }
realAnchor()300 	DocIterator & realAnchor() { return anchor_; }
301 	/// sets anchor to cursor position
302 	void resetAnchor();
303 	/// access to owning BufferView
304 	BufferView & bv() const;
305 	/// get some interesting description of top position
306 	void info(odocstream & os, bool devel_mode) const;
307 	/// are we in math mode (2), text mode (1) or unsure (0)?
308 	int currentMode();
309 	/// reset cursor bottom to the beginning of the top inset
310 	// (sort of 'chroot' environment...)
311 	void reset();
312 	/// for spellchecking
313 	void replaceWord(std::string const & replacestring);
314 	/**
315 	 * the event was not (yet) dispatched.
316 	 *
317 	 * Should only be called by an inset's doDispatch() method. It means:
318 	 * I, the doDispatch() method of InsetFoo, hereby declare that I am
319 	 * not able to handle that request and trust my parent will do the
320 	 * Right Thing (even if my getStatus partner said that I can do it).
321 	 * It is sort of a kludge that should be used only rarely...
322 	 */
323 	void undispatched() const;
324 	/// the event was already dispatched
325 	void dispatched() const;
326 	/// Set which screen update should be done
327 	void screenUpdateFlags(Update::flags f) const;
328 	/// Forces an updateBuffer() call
329 	void forceBufferUpdate() const;
330 	/// Removes any pending updateBuffer() call
331 	void clearBufferUpdate() const;
332 	/// Do we need to call updateBuffer()?
333 	bool needBufferUpdate() const;
334 	/**
335 	 * don't call update() when done
336 	 *
337 	 * Should only be called by an inset's doDispatch() method. It means:
338 	 * I handled that request and I can reassure you that the screen does
339 	 * not need to be re-drawn and all entries in the coord cache stay
340 	 * valid (and there are no other things to put in the coord cache).
341 	 * This is a fairly rare event as well and only some optimization.
342 	 * Not using noScreenUpdate() should never be wrong.
343 	 */
344 	void noScreenUpdate() const;
345 	/// fix cursor in circumstances that should never happen.
346 	/// \retval true if a fix occurred.
347 	bool fixIfBroken();
348 	/// Repopulate the slices insets from bottom to top. Useful
349 	/// for stable iterators or Undo data.
350 	void sanitize();
351 
352 	///
353 	bool textUndo();
354 	///
355 	bool textRedo();
356 
357 	/// makes sure the next operation will be stored
358 	void finishUndo() const;
359 
360 	/// open a new group of undo operations. Groups can be nested.
361 	void beginUndoGroup() const;
362 
363 	/// end the current undo group
364 	void endUndoGroup() const;
365 
366 	/// The general case: prepare undo for an arbitrary range.
367 	void recordUndo(pit_type from, pit_type to) const;
368 
369 	/// Convenience: prepare undo for the range between 'from' and cursor.
370 	void recordUndo(pit_type from) const;
371 
372 	/// Convenience: prepare undo for the single paragraph or cell
373 	/// containing the cursor
374 	void recordUndo(UndoKind kind = ATOMIC_UNDO) const;
375 
376 	/// Convenience: prepare undo for the inset containing the cursor
377 	void recordUndoInset(Inset const * inset = 0) const;
378 
379 	/// Convenience: prepare undo for the whole buffer
380 	void recordUndoFullBuffer() const;
381 
382 	/// Convenience: prepare undo for buffer parameters
383 	void recordUndoBufferParams() const;
384 
385 	/// Convenience: prepare undo for the selected paragraphs or cells
386 	void recordUndoSelection() const;
387 
388 	///
389 	void checkBufferStructure();
390 
391 	/// hook for text input to maintain the "new born word"
392 	void markNewWordPosition();
393 
394 	/// The position of the new born word
395 	/// As the user is entering a word without leaving it
396 	/// the result is not empty. When not in text mode
397 	/// and after leaving the word the result is empty.
newWord()398 	DocIterator newWord() const { return new_word_; }
399 
400 	/// Return true if the next or previous inset has confirmDeletion depending
401 	/// on the boolean before. If there is a selection, return true if at least
402 	/// one inset in the selection has confirmDeletion.
403 	bool confirmDeletion(bool before = false) const;
404 
405 	/// Determine if x falls to the left or to the side of the middle of the
406 	/// inset, and advance the cursor to match this position. If edit is true,
407 	/// keep the cursor in front of the inset if it matter for dialogs.
408 	/// Note: it does not handle RTL text yet, and is only used in math for now.
409 	void moveToClosestEdge(int x, bool edit = false);
410 
411 public:
412 //private:
413 
414 	///
beforeDispatchCursor()415 	DocIterator const & beforeDispatchCursor() const { return beforeDispatchCursor_; }
416 	///
417 	void saveBeforeDispatchPosXY();
418 
419 private:
420 	/// validate the "new born word" position
421 	void checkNewWordPosition();
422 	/// clear the "new born word" position
423 	void clearNewWordPosition();
424 
425 private:
426 	///
427 	BufferView * bv_;
428 	///
429 	mutable DispatchResult disp_;
430 	/**
431 	 * The target x position of the cursor. This is used for when
432 	 * we have text like :
433 	 *
434 	 * blah blah blah blah| blah blah blah
435 	 * blah blah blah
436 	 * blah blah blah blah blah blah
437 	 *
438 	 * When we move onto row 3, we would like to be vertically aligned
439 	 * with where we were in row 1, despite the fact that row 2 is
440 	 * shorter than x()
441 	 */
442 	int x_target_;
443 	/// if a x_target cannot be hit exactly in a text, put the difference here
444 	int textTargetOffset_;
445 	/// the start of the new born word
446 	DocIterator new_word_;
447 	/// position before dispatch started
448 	DocIterator beforeDispatchCursor_;
449 	/// cursor screen coordinates before dispatch started
450 	int beforeDispatchPosX_;
451 	int beforeDispatchPosY_;
452 
453 ///////////////////////////////////////////////////////////////////
454 //
455 // The part below is the non-integrated rest of the original math
456 // cursor. This should be either generalized for texted or moved
457 // back to the math insets.
458 //
459 ///////////////////////////////////////////////////////////////////
460 
461 public:
462 	///
463 	void insert(MathAtom const &);
464 	///
465 	void insert(MathData const &);
466 	/// return false for empty math insets
467 	/// Use force to skip the confirmDeletion check.
468 	bool erase(bool force = false);
469 	bool backspace(bool force = false);
470 
471 	/// move the cursor up by sending an internal LFUN_UP
472 	/// return true if fullscreen update is needed
473 	bool up();
474 	/// move the cursor up by sending an internal LFUN_DOWN,
475 	/// return true if fullscreen update is needed
476 	bool down();
477 	/// whether the cursor is either at the first or last row
478 	bool atFirstOrLastRow(bool up);
479 	/// move up/down in a text inset, called for LFUN_UP/DOWN,
480 	/// return true if successful, updateNeeded set to true if fullscreen
481 	/// update is needed, otherwise it's not touched
482 	bool upDownInText(bool up, bool & updateNeeded);
483 	/// move up/down in math or any non text inset, call for LFUN_UP/DOWN
484 	/// return true if successful
485 	bool upDownInMath(bool up);
486 	///
487 	InsetMath & nextMath();
488 	///
489 	InsetMath & prevMath();
490 	/// move forward in math. word: whether to skip a whole "word" (insets with
491 	/// the same mathclass)
492 	bool mathForward(bool word);
493 	///
494 	bool mathBackward(bool word);
495 	///
496 	void plainErase();
497 	///
498 	void plainInsert(MathAtom const & at);
499 	///
500 	void niceInsert(MathAtom const & at);
501 	/// return the number of inserted array items
502 	int niceInsert(docstring const & str, Parse::flags f = Parse::NORMAL,
503 			bool enter = true);
504 
505 	/// in pixels from top of screen
506 	void setScreenPos(int x, int y);
507 	/// current offset in the top cell
508 
509 	/// interpret name of a macro or ditch it if \c cancel is true.
510 	/// Returns true if something got inserted.
511 	bool macroModeClose(bool cancel = false);
512 	/// are we currently typing the name of a macro?
513 	bool inMacroMode() const;
514 	/// get access to the macro we are currently typing
515 	InsetMathUnknown * activeMacro();
516 	/// get access to the macro we are currently typing
517 	InsetMathUnknown const * activeMacro() const;
518 
519 	/// replace selected stuff with at, placing the former
520 	// selection in given cell of atom
521 	void handleNest(MathAtom const & at, int cell = 0);
522 	///
523 	bool isInside(Inset const *) const;
524 
525 	/// make sure cursor position is valid
526 	/// FIXME: It does a subset of fixIfBroken. Maybe merge them?
527 	void normalize();
528 	/// mark current cursor trace for redraw
529 	void touch();
530 
531 	/// hack for reveal codes
532 	void markInsert();
533 	void markErase();
534 	/// injects content of a cell into parent
535 	void pullArg();
536 	/// split font inset etc
537 	void handleFont(std::string const & font);
538 
539 	/// display a message
540 	void message(docstring const & msg) const;
541 	/// display an error message
542 	void errorMessage(docstring const & msg) const;
543 	///
544 	docstring getPossibleLabel() const;
545 
546 	/// the name of the macro we are currently inputting
547 	docstring macroName();
548 	/// where in the curent cell does the macro name start?
549 	int macroNamePos();
550 	/// can we enter the inset?
551 	bool openable(MathAtom const &) const;
552 	/// font at cursor position
553 	Font getFont() const;
554 };
555 
556 
557 /**
558  * Notifies all insets which appear in \c old, but not in \c cur. And then
559  * notify all insets which appear in \c cur, but not in \c old.
560  * \returns true if cursor is now invalid, e.g. if some insets in
561  *   higher cursor slices of \c old do not exist anymore. In this case
562  *   it may be necessary to use Use Cursor::fixIfBroken.
563  */
564 bool notifyCursorLeavesOrEnters(Cursor const & old, Cursor & cur);
565 
566 
567 } // namespace lyx
568 
569 #endif // LYXLCURSOR_H
570