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