Bump version to 21.06.18.1
[LibreOffice.git] / starmath / inc / cursor.hxx
bloba30beda63368ee578b7f9755a5b7b12e5fc49477
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
9 #ifndef INCLUDED_STARMATH_INC_CURSOR_HXX
10 #define INCLUDED_STARMATH_INC_CURSOR_HXX
12 #include "node.hxx"
13 #include "caret.hxx"
15 #include <cassert>
16 #include <list>
17 #include <memory>
19 /** Factor to multiple the squared horizontal distance with
20 * Used for Up and Down movement.
22 #define HORIZONTICAL_DISTANCE_FACTOR 10
24 /** Enum of direction for movement */
25 enum SmMovementDirection
27 MoveUp,
28 MoveDown,
29 MoveLeft,
30 MoveRight
33 /** Enum of elements that can inserted into a formula */
34 enum SmFormulaElement
36 BlankElement,
37 FactorialElement,
38 PlusElement,
39 MinusElement,
40 CDotElement,
41 EqualElement,
42 LessThanElement,
43 GreaterThanElement,
44 PercentElement
47 /** Bracket types that can be inserted */
48 enum class SmBracketType
50 /** Round brackets, left command "(" */
51 Round,
52 /**Square brackets, left command "[" */
53 Square,
54 /** Curly brackets, left command "lbrace" */
55 Curly,
58 /** A list of nodes */
59 typedef std::list<SmNode*> SmNodeList;
61 typedef std::list<std::unique_ptr<SmNode>> SmClipboard;
63 class SmDocShell;
65 /** Formula cursor
67 * This class is used to represent a cursor in a formula, which can be used to manipulate
68 * a formula programmatically.
69 * @remarks This class is a very intimate friend of SmDocShell.
71 class SmCursor
73 public:
74 SmCursor(SmNode* tree, SmDocShell* pShell)
75 : mpAnchor(nullptr)
76 , mpPosition(nullptr)
77 , mpTree(tree)
78 , mpDocShell(pShell)
79 , mnEditSections(0)
80 , mbIsEnabledSetModifiedSmDocShell(false)
82 //Build graph
83 BuildGraph();
86 /** Get position */
87 const SmCaretPos& GetPosition() const { return mpPosition->CaretPos; }
89 /** True, if the cursor has a selection */
90 bool HasSelection() const { return mpAnchor != mpPosition; }
92 /** Move the position of this cursor */
93 void Move(OutputDevice* pDev, SmMovementDirection direction, bool bMoveAnchor = true);
95 /** Move to the caret position closest to a given point */
96 void MoveTo(OutputDevice* pDev, const Point& pos, bool bMoveAnchor);
98 /** Delete the current selection or do nothing */
99 void Delete();
101 /** Delete selection, previous element or merge lines
103 * This method implements the behaviour of backspace.
105 void DeletePrev(OutputDevice* pDev);
107 /** Insert text at the current position */
108 void InsertText(const OUString& aString);
110 /** Insert an element into the formula */
111 void InsertElement(SmFormulaElement element);
113 /** Insert command text translated into line entries at position
115 * Note: This method uses the parser to translate a command text into a
116 * tree, then it copies line entries from this tree into the current tree.
117 * Will not work for commands such as newline or ##, if position is in a matrix.
118 * This will work for stuff like "A intersection B". But stuff spanning multiple lines
119 * or dependent on the context which position is placed in will not work!
121 void InsertCommandText(const OUString& aCommandText);
123 /** Insert a special node created from aString
125 * Used for handling insert request from the "catalog" dialog.
126 * The provided string should be formatted as the desired command: %phi
127 * Note: this method ONLY supports commands defined in Math.xcu
129 * For more complex expressions use InsertCommandText, this method doesn't
130 * use SmParser, this means that it's faster, but not as strong.
132 void InsertSpecial(const OUString& aString);
134 /** Create sub-/super script
136 * If there's a selection, it will be move into the appropriate sub-/super scription
137 * of the node in front of it. If there's no node in front of position (or the selection),
138 * a sub-/super scription of a new SmPlaceNode will be made.
140 * If there's is an existing subscription of the node, the caret will be moved into it,
141 * and any selection will replace it.
143 void InsertSubSup(SmSubSup eSubSup);
145 /** Insert a new row or newline
147 * Inserts a new row if position is in a matrix or stack command.
148 * Otherwise a newline is inserted if we're in a toplevel line.
150 * @returns True, if a new row/line could be inserted.
152 * @remarks If the caret is placed in a subline of a command that doesn't support
153 * this operator the method returns FALSE, and doesn't do anything.
155 bool InsertRow();
157 /** Insert a fraction, use selection as numerator */
158 void InsertFraction();
160 /** Create brackets around current selection, or new SmPlaceNode */
161 void InsertBrackets(SmBracketType eBracketType);
163 /** Copy the current selection */
164 void Copy();
165 /** Cut the current selection */
166 void Cut()
168 Copy();
169 Delete();
171 /** Paste the clipboard */
172 void Paste();
174 /** Returns true if more than one node is selected
176 * This method is used for implementing backspace and delete.
177 * If one of these causes a complex selection, e.g. a node with
178 * subnodes or similar, this should not be deleted immediately.
180 bool HasComplexSelection();
182 /** Finds the topmost node in a visual line
184 * If MoveUpIfSelected is true, this will move up to the parent line
185 * if the parent of the current line is selected.
187 static SmNode* FindTopMostNodeInLine(SmNode* pSNode, bool MoveUpIfSelected = false);
189 /** Draw the caret */
190 void Draw(OutputDevice& pDev, Point Offset, bool isCaretVisible);
192 bool IsAtTailOfBracket(SmBracketType eBracketType) const;
194 private:
195 friend class SmDocShell;
197 SmCaretPosGraphEntry *mpAnchor, *mpPosition;
198 /** Formula tree */
199 SmNode* mpTree;
200 /** Owner of the formula tree */
201 SmDocShell* mpDocShell;
202 /** Graph over caret position in the current tree */
203 std::unique_ptr<SmCaretPosGraph> mpGraph;
204 /** Clipboard holder */
205 SmClipboard maClipboard;
207 /** Returns a node that is selected, if any could be found */
208 SmNode* FindSelectedNode(SmNode* pNode);
210 /** Is this one of the nodes used to compose a line
212 * These are SmExpression, SmBinHorNode, SmUnHorNode etc.
214 static bool IsLineCompositionNode(SmNode const* pNode);
216 /** Count number of selected nodes, excluding line composition nodes
218 * Note this function doesn't count line composition nodes and it
219 * does count all subnodes as well as the owner nodes.
221 * Used by SmCursor::HasComplexSelection()
223 int CountSelectedNodes(SmNode* pNode);
225 /** Convert a visual line to a list
227 * Note this method will delete all the nodes that will no longer be needed.
228 * that includes pLine!
229 * This method also deletes SmErrorNode's as they're just meta info in the line.
231 static void LineToList(SmStructureNode* pLine, SmNodeList& rList);
233 /** Auxiliary function for calling LineToList on a node
235 * This method sets pNode = NULL and remove it from its parent.
236 * (Assuming it has a parent, and is a child of it).
238 static void NodeToList(SmNode*& rpNode, SmNodeList& rList)
240 //Remove from parent and NULL rpNode
241 SmNode* pNode = rpNode;
242 if (rpNode && rpNode->GetParent())
243 { //Don't remove this, correctness relies on it
244 int index = rpNode->GetParent()->IndexOfSubNode(rpNode);
245 assert(index >= 0);
246 rpNode->GetParent()->SetSubNode(index, nullptr);
248 rpNode = nullptr;
249 //Create line from node
250 if (pNode && IsLineCompositionNode(pNode))
252 LineToList(static_cast<SmStructureNode*>(pNode), rList);
253 return;
255 if (pNode)
256 rList.push_front(pNode);
259 /** Clone a visual line to a clipboard
261 * ... but the selected part only.
262 * Doesn't clone SmErrorNodes, which are ignored as they are context dependent metadata.
264 static void CloneLineToClipboard(SmStructureNode* pLine, SmClipboard* pClipboard);
266 /** Build pGraph over caret positions */
267 void BuildGraph();
269 /** Insert new nodes in the tree after position */
270 void InsertNodes(std::unique_ptr<SmNodeList> pNewNodes);
272 /** tries to set position to a specific SmCaretPos
274 * @returns false on failure to find the position in pGraph.
276 bool SetCaretPosition(SmCaretPos pos);
278 /** Set selected on nodes of the tree */
279 void AnnotateSelection();
281 /** Clone list of nodes in a clipboard (creates a deep clone) */
282 static std::unique_ptr<SmNodeList> CloneList(SmClipboard& rClipboard);
284 /** Find an iterator pointing to the node in pLineList following rCaretPos
286 * If rCaretPos.pSelectedNode cannot be found it is assumed that it's in front of pLineList,
287 * thus not an element in pLineList. In this case this method returns an iterator to the
288 * first element in pLineList.
290 * If the current position is inside an SmTextNode, this node will be split in two, for this
291 * reason you should beaware that iterators to elements in pLineList may be invalidated, and
292 * that you should call PatchLineList() with this iterator if no action is taken.
294 static SmNodeList::iterator FindPositionInLineList(SmNodeList* pLineList,
295 const SmCaretPos& rCaretPos);
297 /** Patch a line list after modification, merge SmTextNode, remove SmPlaceNode etc.
299 * @param pLineList The line list to patch
300 * @param aIter Iterator pointing to the element that needs to be patched with its previous.
302 * When the list is patched text nodes before and after aIter will be merged.
303 * If there's an, in the context, inappropriate SmPlaceNode before or after aIter it will also be
304 * removed.
306 * @returns A caret position equivalent to one selecting the node before aIter, the method returns
307 * an invalid SmCaretPos to indicate placement in front of the line.
309 static SmCaretPos PatchLineList(SmNodeList* pLineList, SmNodeList::iterator aIter);
311 /** Take selected nodes from a list
313 * Puts the selected nodes into pSelectedNodes, or if pSelectedNodes is NULL deletes
314 * the selected nodes.
315 * Note: If there's a selection inside an SmTextNode this node will be split, and it
316 * will not be merged when the selection have been taken. Use PatchLineList on the
317 * iterator returns to fix this.
319 * @returns An iterator pointing to the element following the selection taken.
321 static SmNodeList::iterator TakeSelectedNodesFromList(SmNodeList* pLineList,
322 SmNodeList* pSelectedNodes = nullptr);
324 /** Create an instance of SmMathSymbolNode usable for brackets */
325 static SmNode* CreateBracket(SmBracketType eBracketType, bool bIsLeft);
327 /** The number of times BeginEdit have been called
328 * Used to allow nesting of BeginEdit() and EndEdit() sections
330 int mnEditSections;
331 /** Holds data for BeginEdit() and EndEdit() */
332 bool mbIsEnabledSetModifiedSmDocShell;
333 /** Begin edit section where the tree will be modified */
334 void BeginEdit();
335 /** End edit section where the tree will be modified */
336 void EndEdit();
337 /** Finish editing
339 * Finishes editing by parsing pLineList and inserting back into pParent at nParentIndex.
340 * This method also rebuilds the graph, annotates the selection, sets caret position and
341 * Calls EndEdit.
343 * @remarks Please note that this method will delete pLineList, as the elements are taken.
345 * @param pLineList List the constitutes the edited line.
346 * @param pParent Parent to which the line should be inserted.
347 * @param nParentIndex Index in parent where the line should be inserted.
348 * @param PosAfterEdit Caret position to look for after rebuilding graph.
349 * @param pStartLine Line to take first position in, if PosAfterEdit cannot be found,
350 * leave it NULL for pLineList.
352 void FinishEdit(std::unique_ptr<SmNodeList> pLineList, SmStructureNode* pParent,
353 int nParentIndex, SmCaretPos PosAfterEdit, SmNode* pStartLine = nullptr);
354 /** Request the formula is repainted */
355 void RequestRepaint();
358 /** Minimalistic recursive decent SmNodeList parser
360 * This parser is used to take a list of nodes that constitutes a line
361 * and parse them to a tree of SmBinHorNode, SmUnHorNode and SmExpression.
363 * Please note, this will not handle all kinds of nodes, only nodes that
364 * constitutes and entry in a line.
366 * Below is an EBNF representation of the grammar used for this parser:
367 * \code
368 * Expression -> Relation*
369 * Relation -> Sum [(=|<|>|...) Sum]*
370 * Sum -> Product [(+|-) Product]*
371 * Product -> Factor [(*|/) Factor]*
372 * Factor -> [+|-|-+|...]* Factor | Postfix
373 * Postfix -> node [!]*
374 * \endcode
376 class SmNodeListParser
378 public:
379 /** Create an instance of SmNodeListParser */
380 SmNodeListParser() { pList = nullptr; }
381 /** Parse a list of nodes to an expression.
383 * Old error nodes will be deleted.
385 SmNode* Parse(SmNodeList* list);
386 /** True, if the token is an operator */
387 static bool IsOperator(const SmToken& token);
388 /** True, if the token is a relation operator */
389 static bool IsRelationOperator(const SmToken& token);
390 /** True, if the token is a sum operator */
391 static bool IsSumOperator(const SmToken& token);
392 /** True, if the token is a product operator */
393 static bool IsProductOperator(const SmToken& token);
394 /** True, if the token is a unary operator */
395 static bool IsUnaryOperator(const SmToken& token);
396 /** True, if the token is a postfix operator */
397 static bool IsPostfixOperator(const SmToken& token);
399 private:
400 SmNodeList* pList;
401 /** Get the current terminal */
402 SmNode* Terminal()
404 if (!pList->empty())
405 return pList->front();
406 return nullptr;
408 /** Move to next terminal */
409 SmNode* Next()
411 pList->pop_front();
412 return Terminal();
414 /** Take the current terminal */
415 SmNode* Take()
417 SmNode* pRetVal = Terminal();
418 Next();
419 return pRetVal;
421 SmNode* Expression();
422 SmNode* Relation();
423 SmNode* Sum();
424 SmNode* Product();
425 SmNode* Factor();
426 SmNode* Postfix();
427 static SmNode* Error();
430 #endif // INCLUDED_STARMATH_INC_CURSOR_HXX
432 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */