build fix
[LibreOffice.git] / starmath / inc / cursor.hxx
blob8eac8068977329566599f84e06bb7df264f9ed73
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{
26 MoveUp,
27 MoveDown,
28 MoveLeft,
29 MoveRight
32 /** Enum of elements that can inserted into a formula */
33 enum SmFormulaElement{
34 BlankElement,
35 FactorialElement,
36 PlusElement,
37 MinusElement,
38 CDotElement,
39 EqualElement,
40 LessThanElement,
41 GreaterThanElement,
42 PercentElement
45 /** Bracket types that can be inserted */
46 enum SmBracketType {
47 /** None brackets, left command "none" */
48 NoneBrackets,
49 /** Round brackets, left command "(" */
50 RoundBrackets,
51 /**Square brackets, left command "[" */
52 SquareBrackets,
53 /** Double square brackets, left command "ldbracket" */
54 DoubleSquareBrackets,
55 /** Line brackets, left command "lline" */
56 LineBrackets,
57 /** Double line brackets, left command "ldline" */
58 DoubleLineBrackets,
59 /** Curly brackets, left command "lbrace" */
60 CurlyBrackets,
61 /** Angle brackets, left command "langle" */
62 AngleBrackets,
63 /** Ceiling brackets, left command "lceil" */
64 CeilBrackets,
65 /** Floor brackets, left command "lfloor" */
66 FloorBrackets
69 /** A list of nodes */
70 typedef std::list<SmNode*> SmNodeList;
72 typedef std::list<std::unique_ptr<SmNode>> SmClipboard;
74 class SmDocShell;
76 /** Formula cursor
78 * This class is used to represent a cursor in a formula, which can be used to manipulate
79 * an formula programmatically.
80 * @remarks This class is a very intimate friend of SmDocShell.
82 class SmCursor{
83 public:
84 SmCursor(SmNode* tree, SmDocShell* pShell)
85 : mpAnchor(nullptr)
86 , mpPosition(nullptr)
87 , mpTree(tree)
88 , mpDocShell(pShell)
89 , mnEditSections(0)
90 , mbIsEnabledSetModifiedSmDocShell(false)
92 //Build graph
93 BuildGraph();
96 ~SmCursor()
100 /** Get position */
101 const SmCaretPos& GetPosition() const { return mpPosition->CaretPos; }
103 /** True, if the cursor has a selection */
104 bool HasSelection() { return mpAnchor != mpPosition; }
106 /** Move the position of this cursor */
107 void Move(OutputDevice* pDev, SmMovementDirection direction, bool bMoveAnchor = true);
109 /** Move to the caret position closet to a given point */
110 void MoveTo(OutputDevice* pDev, const Point& pos, bool bMoveAnchor);
112 /** Delete the current selection or do nothing */
113 void Delete();
115 /** Delete selection, previous element or merge lines
117 * This method implements the behaviour of backspace.
119 void DeletePrev(OutputDevice* pDev);
121 /** Insert text at the current position */
122 void InsertText(const OUString& aString);
124 /** Insert an element into the formula */
125 void InsertElement(SmFormulaElement element);
127 /** Insert a command specified in commands.src*/
128 void InsertCommand(sal_uInt16 nCommand);
130 /** Insert command text translated into line entries at position
132 * Note: This method uses the parser to translate a command text into a
133 * tree, then it copies line entries from this tree into the current tree.
134 * Will not work for commands such as newline or ##, if position is in a matrix.
135 * This will work for stuff like "A intersection B". But stuff spanning multiple lines
136 * or dependent on the context which position is placed in will not work!
138 void InsertCommandText(const OUString& aCommandText);
140 /** Insert a special node created from aString
142 * Used for handling insert request from the "catalog" dialog.
143 * The provided string should be formatted as the desired command: %phi
144 * Note: this method ONLY supports commands defined in Math.xcu
146 * For more complex expressions use InsertCommandText, this method doesn't
147 * use SmParser, this means that it's faster, but not as strong.
149 void InsertSpecial(const OUString& aString);
151 /** Create sub-/super script
153 * If there's a selection, it will be move into the appropriate sub-/super scription
154 * of the node in front of it. If there's no node in front of position (or the selection),
155 * a sub-/super scription of a new SmPlaceNode will be made.
157 * If there's is an existing subscription of the node, the caret will be moved into it,
158 * and any selection will replace it.
160 void InsertSubSup(SmSubSup eSubSup);
162 /** Create a limit on an SmOperNode
164 * This method only work if the caret is inside an SmOperNode, or to the right of one.
165 * Notice also that this method ignores any selection made.
167 * The caret will be moved into the limit.
169 * @returns True, if the caret was in a context where this operation was possible.
171 bool InsertLimit(SmSubSup eSubSup);
173 /** Insert a new row or newline
175 * Inserts a new row if position is in an matrix or stack command.
176 * Otherwise a newline is inserted if we're in a toplevel line.
178 * @returns True, if a new row/line could be inserted.
180 * @remarks If the caret is placed in a subline of a command that doesn't support
181 * this operator the method returns FALSE, and doesn't do anything.
183 bool InsertRow();
185 /** Insert a fraction, use selection as numerator */
186 void InsertFraction();
188 /** Create brackets around current selection, or new SmPlaceNode */
189 void InsertBrackets(SmBracketType eBracketType);
191 /** Copy the current selection */
192 void Copy();
193 /** Cut the current selection */
194 void Cut(){
195 Copy();
196 Delete();
198 /** Paste the clipboard */
199 void Paste();
201 /** Returns true if more than one node is selected
203 * This method is used for implementing backspace and delete.
204 * If one of these causes a complex selection, e.g. a node with
205 * subnodes or similar, this should not be deleted imidiately.
207 bool HasComplexSelection();
209 /** Finds the topmost node in a visual line
211 * If MoveUpIfSelected is true, this will move up to the parent line
212 * if the parent of the current line is selected.
214 static SmNode* FindTopMostNodeInLine(SmNode* pSNode, bool MoveUpIfSelected = false);
216 /** Draw the caret */
217 void Draw(OutputDevice& pDev, Point Offset, bool isCaretVisible);
219 bool IsAtTailOfBracket(SmBracketType eBracketType, SmBraceNode** ppBraceNode) const;
220 void MoveAfterBracket(SmBraceNode* pBraceNode);
222 private:
223 friend class SmDocShell;
225 SmCaretPosGraphEntry *mpAnchor,
226 *mpPosition;
227 /** Formula tree */
228 SmNode* mpTree;
229 /** Owner of the formula tree */
230 SmDocShell* mpDocShell;
231 /** Graph over caret position in the current tree */
232 std::unique_ptr<SmCaretPosGraph> mpGraph;
233 /** Clipboard holder */
234 SmClipboard maClipboard;
236 /** Returns a node that is selected, if any could be found */
237 SmNode* FindSelectedNode(SmNode* pNode);
239 /** Is this one of the nodes used to compose a line
241 * These are SmExpression, SmBinHorNode, SmUnHorNode etc.
243 static bool IsLineCompositionNode(SmNode* pNode);
245 /** Count number of selected nodes, excluding line composition nodes
247 * Note this function doesn't count line composition nodes and it
248 * does count all subnodes as well as the owner nodes.
250 * Used by SmCursor::HasComplexSelection()
252 int CountSelectedNodes(SmNode* pNode);
254 /** Convert a visual line to a list
256 * Note this method will delete all the nodes that will no longer be needed.
257 * that includes pLine!
258 * This method also deletes SmErrorNode's as they're just meta info in the line.
260 static SmNodeList* LineToList(SmStructureNode* pLine, SmNodeList* pList);
262 /** Auxiliary function for calling LineToList on a node
264 * This method sets pNode = NULL and remove it from its parent.
265 * (Assuming it has a parent, and is a child of it).
267 static SmNodeList* NodeToList(SmNode*& rpNode, SmNodeList* pList = new SmNodeList()){
268 //Remove from parent and NULL rpNode
269 SmNode* pNode = rpNode;
270 if(rpNode && rpNode->GetParent()){ //Don't remove this, correctness relies on it
271 int index = rpNode->GetParent()->IndexOfSubNode(rpNode);
272 assert(index >= 0);
273 rpNode->GetParent()->SetSubNode(index, nullptr);
275 rpNode = nullptr;
276 //Create line from node
277 if(pNode && IsLineCompositionNode(pNode))
278 return LineToList(static_cast<SmStructureNode*>(pNode), pList);
279 if(pNode)
280 pList->push_front(pNode);
281 return pList;
284 /** Clone a visual line to a clipboard
286 * ... but the selected part only.
287 * Doesn't clone SmErrorNodes, which are ignored as they are context dependent metadata.
289 static void CloneLineToClipboard(SmStructureNode* pLine, SmClipboard* pClipboard);
291 /** Build pGraph over caret positions */
292 void BuildGraph();
294 /** Insert new nodes in the tree after position */
295 void InsertNodes(SmNodeList* pNewNodes);
297 /** tries to set position to a specific SmCaretPos
299 * @returns false on failure to find the position in pGraph.
301 bool SetCaretPosition(SmCaretPos pos);
303 /** Set selected on nodes of the tree */
304 void AnnotateSelection();
306 /** Clone list of nodes in a clipboard (creates a deep clone) */
307 static SmNodeList* CloneList(SmClipboard &rClipboard);
309 /** Find an iterator pointing to the node in pLineList following rCaretPos
311 * If rCaretPos.pSelectedNode cannot be found it is assumed that it's in front of pLineList,
312 * thus not an element in pLineList. In this case this method returns an iterator to the
313 * first element in pLineList.
315 * If the current position is inside an SmTextNode, this node will be split in two, for this
316 * reason you should beaware that iterators to elements in pLineList may be invalidated, and
317 * that you should call PatchLineList() with this iterator if no action is taken.
319 static SmNodeList::iterator FindPositionInLineList(SmNodeList* pLineList,
320 const SmCaretPos& rCaretPos);
322 /** Patch a line list after modification, merge SmTextNode, remove SmPlaceNode etc.
324 * @param pLineList The line list to patch
325 * @param aIter Iterator pointing to the element that needs to be patched with its previous.
327 * When the list is patched text nodes before and after aIter will be merged.
328 * If there's an, in the context, inappropriate SmPlaceNode before or after aIter it will also be
329 * removed.
331 * @returns A caret position equivalent to one selecting the node before aIter, the method returns
332 * an invalid SmCaretPos to indicate placement in front of the line.
334 static SmCaretPos PatchLineList(SmNodeList* pLineList, SmNodeList::iterator aIter);
336 /** Take selected nodes from a list
338 * Puts the selected nodes into pSelectedNodes, or if pSelectedNodes is NULL deletes
339 * the selected nodes.
340 * Note: If there's a selection inside an SmTextNode this node will be split, and it
341 * will not be merged when the selection have been taken. Use PatchLineList on the
342 * iterator returns to fix this.
344 * @returns An iterator pointing to the element following the selection taken.
346 static SmNodeList::iterator TakeSelectedNodesFromList(SmNodeList *pLineList,
347 SmNodeList *pSelectedNodes = nullptr);
349 /** Create an instance of SmMathSymbolNode usable for brackets */
350 static SmNode *CreateBracket(SmBracketType eBracketType, bool bIsLeft);
352 /** The number of times BeginEdit have been called
353 * Used to allow nesting of BeginEdit() and EndEdit() sections
355 int mnEditSections;
356 /** Holds data for BeginEdit() and EndEdit() */
357 bool mbIsEnabledSetModifiedSmDocShell;
358 /** Begin edit section where the tree will be modified */
359 void BeginEdit();
360 /** End edit section where the tree will be modified */
361 void EndEdit();
362 /** Finish editing
364 * Finishes editing by parsing pLineList and inserting back into pParent at nParentIndex.
365 * This method also rebuilds the graph, annotates the selection, sets caret position and
366 * Calls EndEdit.
368 * @remarks Please note that this method will delete pLineList, as the elements are taken.
370 * @param pLineList List the constitutes the edited line.
371 * @param pParent Parent to which the line should be inserted.
372 * @param nParentIndex Index in parent where the line should be inserted.
373 * @param PosAfterEdit Caret position to look for after rebuilding graph.
374 * @param pStartLine Line to take first position in, if PosAfterEdit cannot be found,
375 * leave it NULL for pLineList.
377 void FinishEdit(SmNodeList* pLineList,
378 SmStructureNode* pParent,
379 int nParentIndex,
380 SmCaretPos PosAfterEdit,
381 SmNode* pStartLine = nullptr);
382 /** Request the formula is repainted */
383 void RequestRepaint();
386 /** Minimalistic recursive decent SmNodeList parser
388 * This parser is used to take a list of nodes that constitutes a line
389 * and parse them to a tree of SmBinHorNode, SmUnHorNode and SmExpression.
391 * Please note, this will not handle all kinds of nodes, only nodes that
392 * constitutes and entry in a line.
394 * Below is an EBNF representation of the grammar used for this parser:
395 * \code
396 * Expression -> Relation*
397 * Relation -> Sum [(=|<|>|...) Sum]*
398 * Sum -> Product [(+|-) Product]*
399 * Product -> Factor [(*|/) Factor]*
400 * Factor -> [+|-|-+|...]* Factor | Postfix
401 * Postfix -> node [!]*
402 * \endcode
404 class SmNodeListParser{
405 public:
406 /** Create an instance of SmNodeListParser */
407 SmNodeListParser(){
408 pList = nullptr;
410 /** Parse a list of nodes to an expression.
412 * Old error nodes will be deleted.
414 SmNode* Parse(SmNodeList* list);
415 /** True, if the token is an operator */
416 static bool IsOperator(const SmToken &token);
417 /** True, if the token is a relation operator */
418 static bool IsRelationOperator(const SmToken &token);
419 /** True, if the token is a sum operator */
420 static bool IsSumOperator(const SmToken &token);
421 /** True, if the token is a product operator */
422 static bool IsProductOperator(const SmToken &token);
423 /** True, if the token is a unary operator */
424 static bool IsUnaryOperator(const SmToken &token);
425 /** True, if the token is a postfix operator */
426 static bool IsPostfixOperator(const SmToken &token);
427 private:
428 SmNodeList* pList;
429 /** Get the current terminal */
430 SmNode* Terminal(){
431 if(pList->size() > 0)
432 return pList->front();
433 return nullptr;
435 /** Move to next terminal */
436 SmNode* Next(){
437 pList->pop_front();
438 return Terminal();
440 /** Take the current terminal */
441 SmNode* Take(){
442 SmNode* pRetVal = Terminal();
443 Next();
444 return pRetVal;
446 SmNode* Expression();
447 SmNode* Relation();
448 SmNode* Sum();
449 SmNode* Product();
450 SmNode* Factor();
451 SmNode* Postfix();
452 static SmNode* Error();
456 #endif // INCLUDED_STARMATH_INC_CURSOR_HXX
458 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */