Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
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
11 :
12 : #include "node.hxx"
13 : #include "caret.hxx"
14 :
15 : /** Factor to multiple the squared horizontal distance with
16 : * Used for Up and Down movement.
17 : */
18 : #define HORIZONTICAL_DISTANCE_FACTOR 10
19 :
20 : /** Enum of direction for movement */
21 : enum SmMovementDirection{
22 : MoveUp,
23 : MoveDown,
24 : MoveLeft,
25 : MoveRight
26 : };
27 :
28 : /** Enum of elements that can inserted into a formula */
29 : enum SmFormulaElement{
30 : BlankElement,
31 : FactorialElement,
32 : PlusElement,
33 : MinusElement,
34 : CDotElement,
35 : EqualElement,
36 : LessThanElement,
37 : GreaterThanElement,
38 : PercentElement
39 : };
40 :
41 : /** Bracket types that can be inserted */
42 : enum SmBracketType {
43 : /** None brackets, left command "none" */
44 : NoneBrackets,
45 : /** Round brackets, left command "(" */
46 : RoundBrackets,
47 : /**Square brackets, left command "[" */
48 : SquareBrackets,
49 : /** Double square brackets, left command "ldbracket" */
50 : DoubleSquareBrackets,
51 : /** Line brackets, left command "lline" */
52 : LineBrackets,
53 : /** Double line brackets, left command "ldline" */
54 : DoubleLineBrackets,
55 : /** Curly brackets, left command "lbrace" */
56 : CurlyBrackets,
57 : /** Angle brackets, left command "langle" */
58 : AngleBrackets,
59 : /** Ceiling brackets, left command "lceil" */
60 : CeilBrackets,
61 : /** Floor brackets, left command "lfloor" */
62 : FloorBrackets
63 : };
64 :
65 : /** A list of nodes */
66 : typedef std::list<SmNode*> SmNodeList;
67 :
68 : class SmDocShell;
69 :
70 : /** Formula cursor
71 : *
72 : * This class is used to represent a cursor in a formula, which can be used to manipulate
73 : * an formula programmatically.
74 : * @remarks This class is a very intimite friend of SmDocShell.
75 : */
76 : class SmCursor{
77 : public:
78 0 : SmCursor(SmNode* tree, SmDocShell* pShell)
79 : : anchor(NULL)
80 : , position(NULL)
81 : , pTree(tree)
82 : , pDocShell(pShell)
83 : , pGraph(NULL)
84 : , pClipboard(NULL)
85 : , nEditSections(0)
86 0 : , bIsEnabledSetModifiedSmDocShell(false)
87 : {
88 : //Build graph
89 0 : BuildGraph();
90 0 : }
91 :
92 0 : ~SmCursor()
93 : {
94 0 : SetClipboard();
95 0 : delete pGraph;
96 0 : pGraph = NULL;
97 0 : }
98 :
99 : /** Gets the anchor */
100 : SmCaretPos GetAnchor(){ return anchor->CaretPos; }
101 :
102 : /** Get position */
103 0 : SmCaretPos GetPosition() const { return position->CaretPos; }
104 :
105 : /** True, if the cursor has a selection */
106 0 : bool HasSelection() { return anchor != position; }
107 :
108 : /** Move the position of this cursor */
109 : void Move(OutputDevice* pDev, SmMovementDirection direction, bool bMoveAnchor = true);
110 :
111 : /** Move to the caret position closet to a given point */
112 : void MoveTo(OutputDevice* pDev, Point pos, bool bMoveAnchor = true);
113 :
114 : /** Delete the current selection or do nothing */
115 : void Delete();
116 :
117 : /** Delete selection, previous element or merge lines
118 : *
119 : * This method implements the behaviour of backspace.
120 : */
121 : void DeletePrev(OutputDevice* pDev);
122 :
123 : /** Insert text at the current position */
124 : void InsertText(const OUString& aString);
125 :
126 : /** Insert an element into the formula */
127 : void InsertElement(SmFormulaElement element);
128 :
129 : /** Insert a command specified in commands.src*/
130 : void InsertCommand(sal_uInt16 nCommand);
131 :
132 : /** Insert command text translated into line entries at position
133 : *
134 : * Note: This method uses the parser to translate a command text into a
135 : * tree, then it copies line entries from this tree into the current tree.
136 : * Will not work for commands such as newline or ##, if position is in a matrix.
137 : * This will work for stuff like "A intersection B". But stuff spaning multiple lines
138 : * or dependent on the context which position is placed in will not work!
139 : */
140 : void InsertCommandText(const OUString& aCommandText);
141 :
142 : /** Insert a special node created from aString
143 : *
144 : * Used for handling insert request from the "catalog" dialog.
145 : * The provided string should be formated as the desired command: %phi
146 : * Note: this method ONLY supports commands defined in Math.xcu
147 : *
148 : * For more complex expressions use InsertCommandText, this method doesn't
149 : * use SmParser, this means that it's faster, but not as strong.
150 : */
151 : void InsertSpecial(const OUString& aString);
152 :
153 : /** Create sub-/super script
154 : *
155 : * If there's a selection, it will be move into the appropriate sub-/super scription
156 : * of the node in front of it. If there's no node in front of position (or the selection),
157 : * a sub-/super scription of a new SmPlaceNode will be made.
158 : *
159 : * If there's is an existing subscription of the node, the caret will be moved into it,
160 : * and any selection will replace it.
161 : */
162 : void InsertSubSup(SmSubSup eSubSup);
163 :
164 : /** Create a limit on an SmOperNode
165 : *
166 : * This this method only work if the caret is inside an SmOperNode, or to the right of one.
167 : * Notice also that this method ignores any selection made.
168 : *
169 : * @param bMoveCaret If true that caret will be moved into the limit.
170 : *
171 : * @returns True, if the caret was in a context where this operation was possible.
172 : */
173 : bool InsertLimit(SmSubSup eSubSup, bool bMoveCaret = true);
174 :
175 : /** Insert a new row or newline
176 : *
177 : * Inserts a new row if position is in an matrix or stack command.
178 : * Otherwise a newline is inserted if we're in a toplevel line.
179 : *
180 : * @returns True, if a new row/line could be inserted.
181 : *
182 : * @remarks If the caret is placed in a subline of a command that doesn't support
183 : * this operator the method returns FALSE, and doesn't do anything.
184 : */
185 : bool InsertRow();
186 :
187 : /** Insert a fraction, use selection as numerator */
188 : void InsertFraction();
189 :
190 : /** Create brackets around current selection, or new SmPlaceNode */
191 : void InsertBrackets(SmBracketType eBracketType);
192 :
193 : /** Copy the current selection */
194 : void Copy();
195 : /** Cut the current selection */
196 0 : void Cut(){
197 0 : Copy();
198 0 : Delete();
199 0 : }
200 : /** Paste the clipboard */
201 : void Paste();
202 :
203 : /** Returns true if more than one node is selected
204 : *
205 : * This method is used for implementing backspace and delete.
206 : * If one of these causes a complex selection, e.g. a node with
207 : * subnodes or similar, this should not be deleted imidiately.
208 : */
209 : bool HasComplexSelection();
210 :
211 : /** Finds the topmost node in a visual line
212 : *
213 : * If MoveUpIfSelected is true, this will move up to the parent line
214 : * if the parent of the current line is selected.
215 : */
216 : static SmNode* FindTopMostNodeInLine(SmNode* pSNode, bool MoveUpIfSelected = false);
217 :
218 : /** Draw the caret */
219 : void Draw(OutputDevice& pDev, Point Offset, bool isCaretVisible);
220 :
221 : bool IsAtTailOfBracket(SmBracketType eBracketType, SmBraceNode** ppBraceNode = NULL) const;
222 : void MoveAfterBracket(SmBraceNode* pBraceNode, bool bMoveAnchor = true);
223 :
224 : private:
225 : friend class SmDocShell;
226 :
227 : SmCaretPosGraphEntry *anchor,
228 : *position;
229 : /** Formula tree */
230 : SmNode* pTree;
231 : /** Owner of the formula tree */
232 : SmDocShell* pDocShell;
233 : /** Graph over caret position in the current tree */
234 : SmCaretPosGraph* pGraph;
235 : /** Clipboard holder */
236 : SmNodeList* pClipboard;
237 :
238 : /** Returns a node that is selected, if any could be found */
239 : SmNode* FindSelectedNode(SmNode* pNode);
240 :
241 : /** Is this one of the nodes used to compose a line
242 : *
243 : * These are SmExpression, SmBinHorNode, SmUnHorNode etc.
244 : */
245 : static bool IsLineCompositionNode(SmNode* pNode);
246 :
247 : /** Count number of selected nodes, excluding line composition nodes
248 : *
249 : * Note this function doesn't count line composition nodes and it
250 : * does count all subnodes as well as the owner nodes.
251 : *
252 : * Used by SmCursor::HasComplexSelection()
253 : */
254 : int CountSelectedNodes(SmNode* pNode);
255 :
256 : /** Convert a visual line to a list
257 : *
258 : * Note this method will delete all the nodes that will no longer be needed.
259 : * that includes pLine!
260 : * This method also deletes SmErrorNode's as they're just meta info in the line.
261 : */
262 : static SmNodeList* LineToList(SmStructureNode* pLine, SmNodeList* pList = new SmNodeList());
263 :
264 : /** Auxiliary function for calling LineToList on a node
265 : *
266 : * This method sets pNode = NULL and remove it from its parent.
267 : * (Assuming it has a parent, and is a child of it).
268 : */
269 0 : static SmNodeList* NodeToList(SmNode*& rpNode, SmNodeList* pList = new SmNodeList()){
270 : //Remove from parent and NULL rpNode
271 0 : SmNode* pNode = rpNode;
272 0 : if(rpNode && rpNode->GetParent()){ //Don't remove this, correctness relies on it
273 0 : int index = rpNode->GetParent()->IndexOfSubNode(rpNode);
274 0 : if(index != -1)
275 0 : rpNode->GetParent()->SetSubNode(index, NULL);
276 : }
277 0 : rpNode = NULL;
278 : //Create line from node
279 0 : if(pNode && IsLineCompositionNode(pNode))
280 0 : return LineToList((SmStructureNode*)pNode, pList);
281 0 : if(pNode)
282 0 : pList->push_front(pNode);
283 0 : return pList;
284 : }
285 :
286 : /** Clone a visual line to a list
287 : *
288 : * Doesn't clone SmErrorNode's these are ignored, as they are context dependent metadata.
289 : */
290 : static SmNodeList* CloneLineToList(SmStructureNode* pLine,
291 : bool bOnlyIfSelected = false,
292 0 : SmNodeList* pList = new SmNodeList());
293 :
294 : /** Build pGraph over caret positions */
295 : void BuildGraph();
296 :
297 : /** Insert new nodes in the tree after position */
298 : void InsertNodes(SmNodeList* pNewNodes);
299 :
300 : /** tries to set position to a specific SmCaretPos
301 : *
302 : * @returns false on failure to find the position in pGraph.
303 : */
304 : bool SetCaretPosition(SmCaretPos pos, bool moveAnchor = false);
305 :
306 : /** Set selected on nodes of the tree */
307 : void AnnotateSelection();
308 :
309 : /** Set the clipboard, and release current clipboard
310 : *
311 : * Call this method with NULL to reset the clipboard
312 : * @remarks: This method takes ownership of pList.
313 : */
314 : void SetClipboard(SmNodeList* pList = NULL);
315 :
316 : /** Clone list of nodes (creates a deep clone) */
317 : static SmNodeList* CloneList(SmNodeList* pList);
318 :
319 : /** Find an iterator pointing to the node in pLineList following aCaretPos
320 : *
321 : * If aCaretPos::pSelectedNode cannot be found it is assumed that it's in front of pLineList,
322 : * thus not an element in pLineList. In this case this method returns an iterator to the
323 : * first element in pLineList.
324 : *
325 : * If the current position is inside an SmTextNode, this node will be split in two, for this
326 : * reason you should beaware that iterators to elements in pLineList may be invalidated, and
327 : * that you should call PatchLineList() with this iterator if no action is taken.
328 : */
329 : static SmNodeList::iterator FindPositionInLineList(SmNodeList* pLineList, SmCaretPos aCaretPos);
330 :
331 : /** Patch a line list after modification, merge SmTextNode, remove SmPlaceNode etc.
332 : *
333 : * @param pLineList The line list to patch
334 : * @param aIter Iterator pointing to the element that needs to be patched with it's previous.
335 : *
336 : * When the list is patched text nodes before and after aIter will be merged.
337 : * If there's an, in the context, inappropriate SmPlaceNode before or after aIter it will also be
338 : * removed.
339 : *
340 : * @returns A caret position equivalent to one selecting the node before aIter, the method returns
341 : * an invalid SmCaretPos to indicate placement in front of the line.
342 : */
343 : static SmCaretPos PatchLineList(SmNodeList* pLineList, SmNodeList::iterator aIter);
344 :
345 : /** Take selected nodes from a list
346 : *
347 : * Puts the selected nodes into pSelectedNodes, or if pSelectedNodes is NULL deletes
348 : * the selected nodes.
349 : * Note: If there's a selection inside an SmTextNode this node will be split, and it
350 : * will not be merged when the selection have been taken. Use PatchLineList on the
351 : * iterator returns to fix this.
352 : *
353 : * @returns An iterator pointing to the element following the selection taken.
354 : */
355 : static SmNodeList::iterator TakeSelectedNodesFromList(SmNodeList *pLineList,
356 : SmNodeList *pSelectedNodes = NULL);
357 :
358 : /** Create an instance of SmMathSymbolNode usable for brackets */
359 : static SmNode *CreateBracket(SmBracketType eBracketType, bool bIsLeft);
360 :
361 : /** The number of times BeginEdit have been called
362 : * Used to allow nesting of BeginEdit() and EndEdit() sections
363 : */
364 : int nEditSections;
365 : /** Holds data for BeginEdit() and EndEdit() */
366 : bool bIsEnabledSetModifiedSmDocShell;
367 : /** Begin edit section where the tree will be modified */
368 : void BeginEdit();
369 : /** End edit section where the tree will be modified */
370 : void EndEdit();
371 : /** Finish editing
372 : *
373 : * Finishes editing by parsing pLineList and inserting back into pParent at nParentIndex.
374 : * This method also rebuilts the graph, annotates the selection, sets caret position and
375 : * Calls EndEdit.
376 : *
377 : * @remarks Please note that this method will delete pLineList, as the elements are taken.
378 : *
379 : * @param pLineList List the constitutes the edited line.
380 : * @param pParent Parent to which the line should be inserted.
381 : * @param nParentIndex Index in parent where the line should be inserted.
382 : * @param PosAfterEdit Caret position to look for after rebuilding graph.
383 : * @param pStartLine Line to take first position in, if PosAfterEdit cannot be found,
384 : * leave it NULL for pLineList.
385 : */
386 : void FinishEdit(SmNodeList* pLineList,
387 : SmStructureNode* pParent,
388 : int nParentIndex,
389 : SmCaretPos PosAfterEdit,
390 : SmNode* pStartLine = NULL);
391 : /** Request the formula is repainted */
392 : void RequestRepaint();
393 : };
394 :
395 : /** Minimalistic recursive decent SmNodeList parser
396 : *
397 : * This parser is used to take a list of nodes that constitutes a line
398 : * and parse them to a tree of SmBinHorNode, SmUnHorNode and SmExpression.
399 : *
400 : * Please note, this will not handle all kinds of nodes, only nodes that
401 : * constitutes and entry in a line.
402 : *
403 : * Below is an EBNF representation of the grammar used for this parser:
404 : * \code
405 : * Expression -> Relation*
406 : * Relation -> Sum [(=|<|>|...) Sum]*
407 : * Sum -> Product [(+|-) Product]*
408 : * Product -> Factor [(*|/) Factor]*
409 : * Factor -> [+|-|-+|...]* Factor | Postfix
410 : * Postfix -> node [!]*
411 : * \endcode
412 : */
413 : class SmNodeListParser{
414 : public:
415 : /** Create an instance of SmNodeListParser */
416 0 : SmNodeListParser(){
417 0 : pList = NULL;
418 0 : }
419 : /** Parse a list of nodes to an expression
420 : *
421 : * If bDeleteErrorNodes is true, old error nodes will be deleted.
422 : */
423 : SmNode* Parse(SmNodeList* list, bool bDeleteErrorNodes = true);
424 : /** True, if the token is an operator */
425 : static bool IsOperator(const SmToken &token);
426 : /** True, if the token is a relation operator */
427 : static bool IsRelationOperator(const SmToken &token);
428 : /** True, if the token is a sum operator */
429 : static bool IsSumOperator(const SmToken &token);
430 : /** True, if the token is a product operator */
431 : static bool IsProductOperator(const SmToken &token);
432 : /** True, if the token is a unary operator */
433 : static bool IsUnaryOperator(const SmToken &token);
434 : /** True, if the token is a postfix operator */
435 : static bool IsPostfixOperator(const SmToken &token);
436 : private:
437 : SmNodeList* pList;
438 : /** Get the current terminal */
439 0 : SmNode* Terminal(){
440 0 : if(pList->size() > 0)
441 0 : return pList->front();
442 0 : return NULL;
443 : }
444 : /** Move to next terminal */
445 0 : SmNode* Next(){
446 0 : pList->pop_front();
447 0 : return Terminal();
448 : }
449 : /** Take the current terminal */
450 0 : SmNode* Take(){
451 0 : SmNode* pRetVal = Terminal();
452 0 : Next();
453 0 : return pRetVal;
454 : }
455 : SmNode* Expression();
456 : SmNode* Relation();
457 : SmNode* Sum();
458 : SmNode* Product();
459 : SmNode* Factor();
460 : SmNode* Postfix();
461 : SmNode* Error();
462 : };
463 :
464 :
465 : #endif // INCLUDED_STARMATH_INC_CURSOR_HXX
466 :
467 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|