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