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