1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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/.
9 #ifndef INCLUDED_STARMATH_INC_CURSOR_HXX
10 #define INCLUDED_STARMATH_INC_CURSOR_HXX
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
{
32 /** Enum of elements that can inserted into a formula */
33 enum SmFormulaElement
{
45 /** Bracket types that can be inserted */
46 enum class SmBracketType
{
47 /** Round brackets, left command "(" */
49 /**Square brackets, left command "[" */
51 /** Curly brackets, left command "lbrace" */
55 /** A list of nodes */
56 typedef std::list
<SmNode
*> SmNodeList
;
58 typedef std::list
<std::unique_ptr
<SmNode
>> SmClipboard
;
64 * This class is used to represent a cursor in a formula, which can be used to manipulate
65 * a formula programmatically.
66 * @remarks This class is a very intimate friend of SmDocShell.
70 SmCursor(SmNode
* tree
, SmDocShell
* pShell
)
76 , mbIsEnabledSetModifiedSmDocShell(false)
83 const SmCaretPos
& GetPosition() const { return mpPosition
->CaretPos
; }
85 /** True, if the cursor has a selection */
86 bool HasSelection() const { return mpAnchor
!= mpPosition
; }
88 /** Move the position of this cursor */
89 void Move(OutputDevice
* pDev
, SmMovementDirection direction
, bool bMoveAnchor
= true);
91 /** Move to the caret position closet to a given point */
92 void MoveTo(OutputDevice
* pDev
, const Point
& pos
, bool bMoveAnchor
);
94 /** Delete the current selection or do nothing */
97 /** Delete selection, previous element or merge lines
99 * This method implements the behaviour of backspace.
101 void DeletePrev(OutputDevice
* pDev
);
103 /** Insert text at the current position */
104 void InsertText(const OUString
& aString
);
106 /** Insert an element into the formula */
107 void InsertElement(SmFormulaElement element
);
109 /** Insert command text translated into line entries at position
111 * Note: This method uses the parser to translate a command text into a
112 * tree, then it copies line entries from this tree into the current tree.
113 * Will not work for commands such as newline or ##, if position is in a matrix.
114 * This will work for stuff like "A intersection B". But stuff spanning multiple lines
115 * or dependent on the context which position is placed in will not work!
117 void InsertCommandText(const OUString
& aCommandText
);
119 /** Insert a special node created from aString
121 * Used for handling insert request from the "catalog" dialog.
122 * The provided string should be formatted as the desired command: %phi
123 * Note: this method ONLY supports commands defined in Math.xcu
125 * For more complex expressions use InsertCommandText, this method doesn't
126 * use SmParser, this means that it's faster, but not as strong.
128 void InsertSpecial(const OUString
& aString
);
130 /** Create sub-/super script
132 * If there's a selection, it will be move into the appropriate sub-/super scription
133 * of the node in front of it. If there's no node in front of position (or the selection),
134 * a sub-/super scription of a new SmPlaceNode will be made.
136 * If there's is an existing subscription of the node, the caret will be moved into it,
137 * and any selection will replace it.
139 void InsertSubSup(SmSubSup eSubSup
);
141 /** Insert a new row or newline
143 * Inserts a new row if position is in a matrix or stack command.
144 * Otherwise a newline is inserted if we're in a toplevel line.
146 * @returns True, if a new row/line could be inserted.
148 * @remarks If the caret is placed in a subline of a command that doesn't support
149 * this operator the method returns FALSE, and doesn't do anything.
153 /** Insert a fraction, use selection as numerator */
154 void InsertFraction();
156 /** Create brackets around current selection, or new SmPlaceNode */
157 void InsertBrackets(SmBracketType eBracketType
);
159 /** Copy the current selection */
161 /** Cut the current selection */
166 /** Paste the clipboard */
169 /** Returns true if more than one node is selected
171 * This method is used for implementing backspace and delete.
172 * If one of these causes a complex selection, e.g. a node with
173 * subnodes or similar, this should not be deleted immediately.
175 bool HasComplexSelection();
177 /** Finds the topmost node in a visual line
179 * If MoveUpIfSelected is true, this will move up to the parent line
180 * if the parent of the current line is selected.
182 static SmNode
* FindTopMostNodeInLine(SmNode
* pSNode
, bool MoveUpIfSelected
= false);
184 /** Draw the caret */
185 void Draw(OutputDevice
& pDev
, Point Offset
, bool isCaretVisible
);
187 bool IsAtTailOfBracket(SmBracketType eBracketType
, SmBraceNode
** ppBraceNode
) const;
188 void MoveAfterBracket(SmBraceNode
* pBraceNode
);
191 friend class SmDocShell
;
193 SmCaretPosGraphEntry
*mpAnchor
,
197 /** Owner of the formula tree */
198 SmDocShell
* mpDocShell
;
199 /** Graph over caret position in the current tree */
200 std::unique_ptr
<SmCaretPosGraph
> mpGraph
;
201 /** Clipboard holder */
202 SmClipboard maClipboard
;
204 /** Returns a node that is selected, if any could be found */
205 SmNode
* FindSelectedNode(SmNode
* pNode
);
207 /** Is this one of the nodes used to compose a line
209 * These are SmExpression, SmBinHorNode, SmUnHorNode etc.
211 static bool IsLineCompositionNode(SmNode
const * pNode
);
213 /** Count number of selected nodes, excluding line composition nodes
215 * Note this function doesn't count line composition nodes and it
216 * does count all subnodes as well as the owner nodes.
218 * Used by SmCursor::HasComplexSelection()
220 int CountSelectedNodes(SmNode
* pNode
);
222 /** Convert a visual line to a list
224 * Note this method will delete all the nodes that will no longer be needed.
225 * that includes pLine!
226 * This method also deletes SmErrorNode's as they're just meta info in the line.
228 static void LineToList(SmStructureNode
* pLine
, SmNodeList
& rList
);
230 /** Auxiliary function for calling LineToList on a node
232 * This method sets pNode = NULL and remove it from its parent.
233 * (Assuming it has a parent, and is a child of it).
235 static void NodeToList(SmNode
*& rpNode
, SmNodeList
& rList
){
236 //Remove from parent and NULL rpNode
237 SmNode
* pNode
= rpNode
;
238 if(rpNode
&& rpNode
->GetParent()){ //Don't remove this, correctness relies on it
239 int index
= rpNode
->GetParent()->IndexOfSubNode(rpNode
);
241 rpNode
->GetParent()->SetSubNode(index
, nullptr);
244 //Create line from node
245 if(pNode
&& IsLineCompositionNode(pNode
)){
246 LineToList(static_cast<SmStructureNode
*>(pNode
), rList
);
250 rList
.push_front(pNode
);
253 /** Clone a visual line to a clipboard
255 * ... but the selected part only.
256 * Doesn't clone SmErrorNodes, which are ignored as they are context dependent metadata.
258 static void CloneLineToClipboard(SmStructureNode
* pLine
, SmClipboard
* pClipboard
);
260 /** Build pGraph over caret positions */
263 /** Insert new nodes in the tree after position */
264 void InsertNodes(std::unique_ptr
<SmNodeList
> pNewNodes
);
266 /** tries to set position to a specific SmCaretPos
268 * @returns false on failure to find the position in pGraph.
270 bool SetCaretPosition(SmCaretPos pos
);
272 /** Set selected on nodes of the tree */
273 void AnnotateSelection();
275 /** Clone list of nodes in a clipboard (creates a deep clone) */
276 static std::unique_ptr
<SmNodeList
> CloneList(SmClipboard
&rClipboard
);
278 /** Find an iterator pointing to the node in pLineList following rCaretPos
280 * If rCaretPos.pSelectedNode cannot be found it is assumed that it's in front of pLineList,
281 * thus not an element in pLineList. In this case this method returns an iterator to the
282 * first element in pLineList.
284 * If the current position is inside an SmTextNode, this node will be split in two, for this
285 * reason you should beaware that iterators to elements in pLineList may be invalidated, and
286 * that you should call PatchLineList() with this iterator if no action is taken.
288 static SmNodeList::iterator
FindPositionInLineList(SmNodeList
* pLineList
,
289 const SmCaretPos
& rCaretPos
);
291 /** Patch a line list after modification, merge SmTextNode, remove SmPlaceNode etc.
293 * @param pLineList The line list to patch
294 * @param aIter Iterator pointing to the element that needs to be patched with its previous.
296 * When the list is patched text nodes before and after aIter will be merged.
297 * If there's an, in the context, inappropriate SmPlaceNode before or after aIter it will also be
300 * @returns A caret position equivalent to one selecting the node before aIter, the method returns
301 * an invalid SmCaretPos to indicate placement in front of the line.
303 static SmCaretPos
PatchLineList(SmNodeList
* pLineList
, SmNodeList::iterator aIter
);
305 /** Take selected nodes from a list
307 * Puts the selected nodes into pSelectedNodes, or if pSelectedNodes is NULL deletes
308 * the selected nodes.
309 * Note: If there's a selection inside an SmTextNode this node will be split, and it
310 * will not be merged when the selection have been taken. Use PatchLineList on the
311 * iterator returns to fix this.
313 * @returns An iterator pointing to the element following the selection taken.
315 static SmNodeList::iterator
TakeSelectedNodesFromList(SmNodeList
*pLineList
,
316 SmNodeList
*pSelectedNodes
= nullptr);
318 /** Create an instance of SmMathSymbolNode usable for brackets */
319 static SmNode
*CreateBracket(SmBracketType eBracketType
, bool bIsLeft
);
321 /** The number of times BeginEdit have been called
322 * Used to allow nesting of BeginEdit() and EndEdit() sections
325 /** Holds data for BeginEdit() and EndEdit() */
326 bool mbIsEnabledSetModifiedSmDocShell
;
327 /** Begin edit section where the tree will be modified */
329 /** End edit section where the tree will be modified */
333 * Finishes editing by parsing pLineList and inserting back into pParent at nParentIndex.
334 * This method also rebuilds the graph, annotates the selection, sets caret position and
337 * @remarks Please note that this method will delete pLineList, as the elements are taken.
339 * @param pLineList List the constitutes the edited line.
340 * @param pParent Parent to which the line should be inserted.
341 * @param nParentIndex Index in parent where the line should be inserted.
342 * @param PosAfterEdit Caret position to look for after rebuilding graph.
343 * @param pStartLine Line to take first position in, if PosAfterEdit cannot be found,
344 * leave it NULL for pLineList.
346 void FinishEdit(std::unique_ptr
<SmNodeList
> pLineList
,
347 SmStructureNode
* pParent
,
349 SmCaretPos PosAfterEdit
,
350 SmNode
* pStartLine
= nullptr);
351 /** Request the formula is repainted */
352 void RequestRepaint();
355 /** Minimalistic recursive decent SmNodeList parser
357 * This parser is used to take a list of nodes that constitutes a line
358 * and parse them to a tree of SmBinHorNode, SmUnHorNode and SmExpression.
360 * Please note, this will not handle all kinds of nodes, only nodes that
361 * constitutes and entry in a line.
363 * Below is an EBNF representation of the grammar used for this parser:
365 * Expression -> Relation*
366 * Relation -> Sum [(=|<|>|...) Sum]*
367 * Sum -> Product [(+|-) Product]*
368 * Product -> Factor [(*|/) Factor]*
369 * Factor -> [+|-|-+|...]* Factor | Postfix
370 * Postfix -> node [!]*
373 class SmNodeListParser
{
375 /** Create an instance of SmNodeListParser */
379 /** Parse a list of nodes to an expression.
381 * Old error nodes will be deleted.
383 SmNode
* Parse(SmNodeList
* list
);
384 /** True, if the token is an operator */
385 static bool IsOperator(const SmToken
&token
);
386 /** True, if the token is a relation operator */
387 static bool IsRelationOperator(const SmToken
&token
);
388 /** True, if the token is a sum operator */
389 static bool IsSumOperator(const SmToken
&token
);
390 /** True, if the token is a product operator */
391 static bool IsProductOperator(const SmToken
&token
);
392 /** True, if the token is a unary operator */
393 static bool IsUnaryOperator(const SmToken
&token
);
394 /** True, if the token is a postfix operator */
395 static bool IsPostfixOperator(const SmToken
&token
);
398 /** Get the current terminal */
401 return pList
->front();
404 /** Move to next terminal */
409 /** Take the current terminal */
411 SmNode
* pRetVal
= Terminal();
415 SmNode
* Expression();
421 static SmNode
* Error();
425 #endif // INCLUDED_STARMATH_INC_CURSOR_HXX
427 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */