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 : #include "cursor.hxx"
29 : #include "parse.hxx"
30 : #include "visitors.hxx"
31 : #include "document.hxx"
32 : #include "view.hxx"
33 : #include "accessibility.hxx"
34 : #include <comphelper/string.hxx>
35 :
36 30 : void SmCursor::Move(OutputDevice* pDev, SmMovementDirection direction, bool bMoveAnchor){
37 30 : SmCaretPosGraphEntry* NewPos = NULL;
38 30 : switch(direction){
39 : case MoveLeft:
40 : {
41 5 : NewPos = position->Left;
42 : OSL_ENSURE(NewPos, "NewPos shouldn't be NULL here!");
43 5 : }break;
44 : case MoveRight:
45 : {
46 23 : NewPos = position->Right;
47 : OSL_ENSURE(NewPos, "NewPos shouldn't be NULL here!");
48 23 : }break;
49 : case MoveUp:
50 : //Implementation is practically identical to MoveDown, except for a single if statement
51 : //so I've implemented them together and added a direction == MoveDown to the if statements.
52 : case MoveDown:
53 : {
54 2 : SmCaretLine from_line = SmCaretPos2LineVisitor(pDev, position->CaretPos).GetResult(),
55 2 : best_line, //Best approximated line found so far
56 2 : curr_line; //Current line
57 2 : long dbp_sq = 0; //Distance squared to best line
58 2 : SmCaretPosGraphIterator it = pGraph->GetIterator();
59 18 : while(it.Next()){
60 : //Reject it if it's the current position
61 14 : if(it->CaretPos == position->CaretPos) continue;
62 : //Compute caret line
63 12 : curr_line = SmCaretPos2LineVisitor(pDev, it->CaretPos).GetResult();
64 : //Reject anything above if we're moving down
65 12 : if(curr_line.GetTop() <= from_line.GetTop() && direction == MoveDown) continue;
66 : //Reject anything below if we're moving up
67 0 : if(curr_line.GetTop() + curr_line.GetHeight() >= from_line.GetTop() + from_line.GetHeight()
68 0 : && direction == MoveUp) continue;
69 : //Compare if it to what we have, if we have anything yet
70 0 : if(NewPos){
71 : //Compute distance to current line squared, multiplied with a horizontial factor
72 0 : long dp_sq = curr_line.SquaredDistanceX(from_line) * HORIZONTICAL_DISTANCE_FACTOR +
73 0 : curr_line.SquaredDistanceY(from_line);
74 : //Discard current line if best line is closer
75 0 : if(dbp_sq <= dp_sq) continue;
76 : }
77 : //Take current line as the best
78 0 : best_line = curr_line;
79 0 : NewPos = it.Current();
80 : //Update distance to best line
81 0 : dbp_sq = best_line.SquaredDistanceX(from_line) * HORIZONTICAL_DISTANCE_FACTOR +
82 0 : best_line.SquaredDistanceY(from_line);
83 : }
84 2 : }break;
85 : default:
86 : OSL_FAIL("Movement direction not supported!");
87 : }
88 30 : if(NewPos){
89 28 : position = NewPos;
90 28 : if(bMoveAnchor)
91 25 : anchor = NewPos;
92 28 : RequestRepaint();
93 : }
94 30 : }
95 :
96 0 : void SmCursor::MoveTo(OutputDevice* pDev, Point pos, bool bMoveAnchor){
97 0 : SmCaretLine best_line, //Best line found so far, when iterating
98 0 : curr_line; //Current line, when iterating
99 0 : SmCaretPosGraphEntry* NewPos = NULL;
100 0 : long dp_sq = 0, //Distance to current line squared
101 0 : dbp_sq = 1; //Distance to best line squared
102 0 : SmCaretPosGraphIterator it = pGraph->GetIterator();
103 0 : while(it.Next()){
104 : OSL_ENSURE(it->CaretPos.IsValid(), "The caret position graph may not have invalid positions!");
105 : //Compute current line
106 0 : curr_line = SmCaretPos2LineVisitor(pDev, it->CaretPos).GetResult();
107 : //If we have a position compare to it
108 0 : if(NewPos){
109 : //Compute squared distance to current line
110 0 : dp_sq = curr_line.SquaredDistanceX(pos) + curr_line.SquaredDistanceY(pos);
111 : //If best line is closer, reject current line
112 0 : if(dbp_sq <= dp_sq) continue;
113 : }
114 : //Accept current position as the best
115 0 : best_line = curr_line;
116 0 : NewPos = it.Current();
117 : //Update distance to best line
118 0 : dbp_sq = best_line.SquaredDistanceX(pos) + best_line.SquaredDistanceY(pos);
119 : }
120 0 : if(NewPos){
121 0 : position = NewPos;
122 0 : if(bMoveAnchor)
123 0 : anchor = NewPos;
124 0 : RequestRepaint();
125 : }
126 0 : }
127 :
128 20 : void SmCursor::BuildGraph(){
129 : //Save the current anchor and position
130 20 : SmCaretPos _anchor, _position;
131 : //Release pGraph if allocated
132 20 : if(pGraph){
133 16 : if(anchor)
134 0 : _anchor = anchor->CaretPos;
135 16 : if(position)
136 0 : _position = position->CaretPos;
137 16 : delete pGraph;
138 : //Reset anchor and position as they point into an old graph
139 16 : anchor = NULL;
140 16 : position = NULL;
141 : }
142 20 : pGraph = NULL;
143 :
144 : //Build the new graph
145 20 : pGraph = SmCaretPosGraphBuildingVisitor(pTree).Graph();
146 :
147 : //Restore anchor and position pointers
148 20 : if(_anchor.IsValid() || _position.IsValid()){
149 0 : SmCaretPosGraphIterator it = pGraph->GetIterator();
150 0 : while(it.Next()){
151 0 : if(_anchor == it->CaretPos)
152 0 : anchor = it.Current();
153 0 : if(_position == it->CaretPos)
154 0 : position = it.Current();
155 : }
156 : }
157 : //Set position and anchor to first caret position
158 20 : SmCaretPosGraphIterator it = pGraph->GetIterator();
159 20 : if(!position)
160 20 : position = it.Next();
161 20 : if(!anchor)
162 20 : anchor = position;
163 :
164 : OSL_ENSURE(position->CaretPos.IsValid(), "Position must be valid");
165 : OSL_ENSURE(anchor->CaretPos.IsValid(), "Anchor must be valid");
166 20 : }
167 :
168 17 : bool SmCursor::SetCaretPosition(SmCaretPos pos, bool moveAnchor){
169 17 : SmCaretPosGraphIterator it = pGraph->GetIterator();
170 17 : while(it.Next()){
171 120 : if(it->CaretPos == pos){
172 16 : position = it.Current();
173 16 : if(moveAnchor)
174 16 : anchor = it.Current();
175 16 : return true;
176 : }
177 : }
178 1 : return false;
179 : }
180 :
181 19 : void SmCursor::AnnotateSelection(){
182 : //TODO: Manage a state, reset it upon modification and optimize this call
183 19 : SmSetSelectionVisitor(anchor->CaretPos, position->CaretPos, pTree);
184 19 : }
185 :
186 0 : void SmCursor::Draw(OutputDevice& pDev, Point Offset, bool isCaretVisible){
187 0 : SmCaretDrawingVisitor(pDev, GetPosition(), Offset, isCaretVisible);
188 0 : }
189 :
190 0 : void SmCursor::DeletePrev(OutputDevice* pDev){
191 : //Delete only a selection if there's a selection
192 0 : if(HasSelection()){
193 0 : Delete();
194 0 : return;
195 : }
196 :
197 0 : SmNode* pLine = FindTopMostNodeInLine(position->CaretPos.pSelectedNode);
198 0 : SmStructureNode* pLineParent = pLine->GetParent();
199 0 : int nLineOffset = pLineParent->IndexOfSubNode(pLine);
200 :
201 : //If we're in front of a node who's parent is a TABLE
202 0 : if(pLineParent->GetType() == NTABLE && position->CaretPos.Index == 0 && nLineOffset > 0){
203 : //Now we can merge with nLineOffset - 1
204 0 : BeginEdit();
205 : //Line to merge things into, so we can delete pLine
206 0 : SmNode* pMergeLine = pLineParent->GetSubNode(nLineOffset-1);
207 : OSL_ENSURE(pMergeLine, "pMergeLine cannot be NULL!");
208 0 : SmCaretPos PosAfterDelete;
209 : //Convert first line to list
210 0 : SmNodeList *pLineList = NodeToList(pMergeLine);
211 0 : if(!pLineList->empty()){
212 : //Find iterator to patch
213 0 : SmNodeList::iterator patchPoint = pLineList->end();
214 0 : --patchPoint;
215 : //Convert second line to list
216 0 : NodeToList(pLine, pLineList);
217 : //Patch the line list
218 0 : ++patchPoint;
219 0 : PosAfterDelete = PatchLineList(pLineList, patchPoint);
220 : //Parse the line
221 0 : pLine = SmNodeListParser().Parse(pLineList);
222 : }
223 0 : delete pLineList;
224 0 : pLineParent->SetSubNode(nLineOffset-1, pLine);
225 : //Delete the removed line slot
226 0 : SmNodeArray lines(pLineParent->GetNumSubNodes()-1);
227 0 : for(int i = 0; i < pLineParent->GetNumSubNodes(); i++){
228 0 : if(i < nLineOffset)
229 0 : lines[i] = pLineParent->GetSubNode(i);
230 0 : else if(i > nLineOffset)
231 0 : lines[i-1] = pLineParent->GetSubNode(i);
232 : }
233 0 : pLineParent->SetSubNodes(lines);
234 : //Rebuild graph
235 0 : anchor = NULL;
236 0 : position = NULL;
237 0 : BuildGraph();
238 0 : AnnotateSelection();
239 : //Set caret position
240 0 : if(!SetCaretPosition(PosAfterDelete, true))
241 0 : SetCaretPosition(SmCaretPos(pLine, 0), true);
242 : //Finish editing
243 0 : EndEdit();
244 :
245 : //TODO: If we're in an empty (sub/super/*) script
246 : /*}else if(pLineParent->GetType() == NSUBSUP &&
247 : nLineOffset != 0 &&
248 : pLine->GetType() == NEXPRESSION &&
249 : pLine->GetNumSubNodes() == 0){
250 : //There's a (sub/super) script we can delete
251 : //Consider selecting the entire script if GetNumSubNodes() != 0 or pLine->GetType() != NEXPRESSION
252 : //TODO: Handle case where we delete a limit
253 : */
254 :
255 : //Else move select, and delete if not complex
256 : }else{
257 0 : this->Move(pDev, MoveLeft, false);
258 0 : if(!this->HasComplexSelection())
259 0 : Delete();
260 : }
261 : }
262 :
263 13 : void SmCursor::Delete(){
264 : //Return if we don't have a selection to delete
265 13 : if(!HasSelection())
266 13 : return;
267 :
268 : //Enter edit setion
269 0 : BeginEdit();
270 :
271 : //Set selected on nodes
272 0 : AnnotateSelection();
273 :
274 : //Find an arbitrary selected node
275 0 : SmNode* pSNode = FindSelectedNode(pTree);
276 : OSL_ENSURE(pSNode != NULL, "There must be a selection when HasSelection is true!");
277 :
278 : //Find the topmost node of the line that holds the selection
279 0 : SmNode* pLine = FindTopMostNodeInLine(pSNode, true);
280 : OSL_ENSURE(pLine != pTree, "Shouldn't be able to select the entire tree");
281 :
282 : //Get the parent of the line
283 0 : SmStructureNode* pLineParent = pLine->GetParent();
284 : //Find line offset in parent
285 0 : int nLineOffset = pLineParent->IndexOfSubNode(pLine);
286 : OSL_ENSURE(nLineOffset != -1, "pLine must be a child of it's parent!");
287 :
288 : //Position after delete
289 0 : SmCaretPos PosAfterDelete;
290 :
291 0 : SmNodeList* pLineList = NodeToList(pLine);
292 :
293 : //Take the selected nodes and delete them...
294 0 : SmNodeList::iterator patchIt = TakeSelectedNodesFromList(pLineList);
295 :
296 : //Get teh position to set after delete
297 0 : PosAfterDelete = PatchLineList(pLineList, patchIt);
298 :
299 : //Finish editing
300 0 : FinishEdit(pLineList, pLineParent, nLineOffset, PosAfterDelete);
301 : }
302 :
303 13 : void SmCursor::InsertNodes(SmNodeList* pNewNodes){
304 13 : if(pNewNodes->empty()){
305 0 : delete pNewNodes;
306 13 : return;
307 : }
308 :
309 : //Begin edit section
310 13 : BeginEdit();
311 :
312 : //Position after insert should be after pNewNode
313 13 : SmCaretPos PosAfterInsert = SmCaretPos(pNewNodes->back(), 1);
314 :
315 : //Get the current position
316 13 : const SmCaretPos pos = position->CaretPos;
317 :
318 : //Find top most of line that holds position
319 13 : SmNode* pLine = FindTopMostNodeInLine(pos.pSelectedNode, false);
320 :
321 : //Find line parent and line index in parent
322 13 : SmStructureNode* pLineParent = pLine->GetParent();
323 13 : int nParentIndex = pLineParent->IndexOfSubNode(pLine);
324 : OSL_ENSURE(nParentIndex != -1, "pLine must be a subnode of pLineParent!");
325 :
326 : //Convert line to list
327 13 : SmNodeList* pLineList = NodeToList(pLine);
328 :
329 : //Find iterator for place to insert nodes
330 13 : SmNodeList::iterator it = FindPositionInLineList(pLineList, pos);
331 :
332 : //Insert all new nodes
333 13 : SmNodeList::iterator newIt,
334 13 : patchIt = it, // (pointless default value, fixes compiler warnings)
335 13 : insIt;
336 26 : for(newIt = pNewNodes->begin(); newIt != pNewNodes->end(); ++newIt){
337 13 : insIt = pLineList->insert(it, *newIt);
338 13 : if(newIt == pNewNodes->begin())
339 13 : patchIt = insIt;
340 13 : if((*newIt)->GetType() == NTEXT)
341 9 : PosAfterInsert = SmCaretPos(*newIt, ((SmTextNode*)*newIt)->GetText().getLength());
342 : else
343 4 : PosAfterInsert = SmCaretPos(*newIt, 1);
344 : }
345 : //Patch the places we've changed stuff
346 13 : PatchLineList(pLineList, patchIt);
347 13 : PosAfterInsert = PatchLineList(pLineList, it);
348 : //Release list, we've taken the nodes
349 13 : delete pNewNodes;
350 13 : pNewNodes = NULL;
351 :
352 : //Finish editing
353 13 : FinishEdit(pLineList, pLineParent, nParentIndex, PosAfterInsert);
354 : }
355 :
356 14 : SmNodeList::iterator SmCursor::FindPositionInLineList(SmNodeList* pLineList, SmCaretPos aCaretPos) {
357 : //Find iterator for position
358 14 : SmNodeList::iterator it;
359 21 : for(it = pLineList->begin(); it != pLineList->end(); ++it){
360 18 : if(*it == aCaretPos.pSelectedNode){
361 11 : if((*it)->GetType() == NTEXT){
362 : //Split textnode if needed
363 2 : if(aCaretPos.Index > 0){
364 2 : SmTextNode* pText = (SmTextNode*)aCaretPos.pSelectedNode;
365 2 : OUString str1 = pText->GetText().copy(0, aCaretPos.Index);
366 2 : OUString str2 = pText->GetText().copy(aCaretPos.Index);
367 2 : pText->ChangeText(str1);
368 2 : ++it;
369 : //Insert str2 as new text node
370 2 : if(!str2.isEmpty()){
371 0 : SmTextNode* pNewText = new SmTextNode(pText->GetToken(), pText->GetFontDesc());
372 0 : pNewText->ChangeText(str2);
373 0 : it = pLineList->insert(it, pNewText);
374 2 : }
375 : }
376 : }else
377 9 : ++it;
378 : //it now pointer to the node following pos, so pLineList->insert(it, ...) will insert correctly
379 11 : return it;
380 :
381 : }
382 : }
383 : //If we didn't find pSelectedNode, it must be because the caret is in front of the line
384 3 : return pLineList->begin();
385 : }
386 :
387 30 : SmCaretPos SmCursor::PatchLineList(SmNodeList* pLineList, SmNodeList::iterator aIter) {
388 : //The nodes we should consider merging
389 30 : SmNode *prev = NULL,
390 30 : *next = NULL;
391 30 : if(aIter != pLineList->end())
392 16 : next = *aIter;
393 30 : if(aIter != pLineList->begin()) {
394 26 : --aIter;
395 26 : prev = *aIter;
396 26 : ++aIter;
397 : }
398 :
399 : //Check if there's textnodes to merge
400 44 : if( prev &&
401 : next &&
402 12 : prev->GetType() == NTEXT &&
403 2 : next->GetType() == NTEXT &&
404 0 : ( prev->GetToken().eType != TNUMBER ||
405 0 : next->GetToken().eType == TNUMBER) ){
406 0 : SmTextNode *pText = (SmTextNode*)prev,
407 0 : *pOldN = (SmTextNode*)next;
408 0 : SmCaretPos retval(pText, pText->GetText().getLength());
409 0 : OUString newText;
410 0 : newText += pText->GetText();
411 0 : newText += pOldN->GetText();
412 0 : pText->ChangeText(newText);
413 0 : delete pOldN;
414 0 : pLineList->erase(aIter);
415 0 : return retval;
416 : }
417 :
418 : //Check if there's a SmPlaceNode to remove:
419 30 : if(prev && next && prev->GetType() == NPLACE && !SmNodeListParser::IsOperator(next->GetToken())){
420 2 : --aIter;
421 2 : aIter = pLineList->erase(aIter);
422 2 : delete prev;
423 : //Return caret pos in front of aIter
424 2 : if(aIter != pLineList->begin())
425 0 : --aIter; //Thus find node before aIter
426 2 : if(aIter == pLineList->begin())
427 2 : return SmCaretPos();
428 0 : if((*aIter)->GetType() == NTEXT)
429 0 : return SmCaretPos(*aIter, ((SmTextNode*)*aIter)->GetText().getLength());
430 0 : return SmCaretPos(*aIter, 1);
431 : }
432 28 : if(prev && next && next->GetType() == NPLACE && !SmNodeListParser::IsOperator(prev->GetToken())){
433 0 : aIter = pLineList->erase(aIter);
434 0 : delete next;
435 0 : if(prev->GetType() == NTEXT)
436 0 : return SmCaretPos(prev, ((SmTextNode*)prev)->GetText().getLength());
437 0 : return SmCaretPos(prev, 1);
438 : }
439 :
440 : //If we didn't do anything return
441 28 : if(!prev) //return an invalid to indicate we're in front of line
442 4 : return SmCaretPos();
443 24 : if(prev->GetType() == NTEXT)
444 10 : return SmCaretPos(prev, ((SmTextNode*)prev)->GetText().getLength());
445 14 : return SmCaretPos(prev, 1);
446 : }
447 :
448 2 : SmNodeList::iterator SmCursor::TakeSelectedNodesFromList(SmNodeList *pLineList,
449 : SmNodeList *pSelectedNodes) {
450 2 : SmNodeList::iterator retval;
451 2 : SmNodeList::iterator it = pLineList->begin();
452 8 : while(it != pLineList->end()){
453 4 : if((*it)->IsSelected()){
454 : //Split text nodes
455 3 : if((*it)->GetType() == NTEXT) {
456 2 : SmTextNode* pText = (SmTextNode*)*it;
457 2 : OUString aText = pText->GetText();
458 : //Start and lengths of the segments, 2 is the selected segment
459 2 : int start2 = pText->GetSelectionStart(),
460 2 : start3 = pText->GetSelectionEnd(),
461 2 : len1 = start2 - 0,
462 2 : len2 = start3 - start2,
463 2 : len3 = aText.getLength() - start3;
464 2 : SmToken aToken = pText->GetToken();
465 2 : sal_uInt16 eFontDesc = pText->GetFontDesc();
466 : //If we need make segment 1
467 2 : if(len1 > 0) {
468 0 : int start1 = 0;
469 0 : OUString str = aText.copy(start1, len1);
470 0 : pText->ChangeText(str);
471 0 : ++it;
472 : } else {//Remove it if not needed
473 2 : it = pLineList->erase(it);
474 2 : delete pText;
475 : }
476 : //Set retval to be right after the selection
477 2 : retval = it;
478 : //if we need make segment 3
479 2 : if(len3 > 0) {
480 0 : OUString str = aText.copy(start3, len3);
481 0 : SmTextNode* pSeg3 = new SmTextNode(aToken, eFontDesc);
482 0 : pSeg3->ChangeText(str);
483 0 : retval = pLineList->insert(it, pSeg3);
484 : }
485 : //If we need to save the selected text
486 2 : if(pSelectedNodes && len2 > 0) {
487 2 : OUString str = aText.copy(start2, len2);
488 2 : SmTextNode* pSeg2 = new SmTextNode(aToken, eFontDesc);
489 2 : pSeg2->ChangeText(str);
490 2 : pSelectedNodes->push_back(pSeg2);
491 2 : }
492 : } else { //if it's not textnode
493 1 : SmNode* pNode = *it;
494 1 : retval = it = pLineList->erase(it);
495 1 : if(pSelectedNodes)
496 1 : pSelectedNodes->push_back(pNode);
497 : else
498 0 : delete pNode;
499 : }
500 : } else
501 1 : ++it;
502 : }
503 2 : return retval;
504 : }
505 :
506 1 : void SmCursor::InsertSubSup(SmSubSup eSubSup) {
507 1 : AnnotateSelection();
508 :
509 : //Find line
510 : SmNode *pLine;
511 1 : if(HasSelection()) {
512 0 : SmNode *pSNode = FindSelectedNode(pTree);
513 : OSL_ENSURE(pSNode != NULL, "There must be a selected node when HasSelection is true!");
514 0 : pLine = FindTopMostNodeInLine(pSNode, sal_True);
515 : } else
516 1 : pLine = FindTopMostNodeInLine(position->CaretPos.pSelectedNode, sal_False);
517 :
518 : //Find Parent and offset in parent
519 1 : SmStructureNode *pLineParent = pLine->GetParent();
520 1 : int nParentIndex = pLineParent->IndexOfSubNode(pLine);
521 : OSL_ENSURE(nParentIndex != -1, "pLine must be a subnode of pLineParent!");
522 :
523 : //TODO: Consider handling special cases where parent is an SmOperNode,
524 : // Maybe this method should be able to add limits to an SmOperNode...
525 :
526 : //We begin modifying the tree here
527 1 : BeginEdit();
528 :
529 : //Convert line to list
530 1 : SmNodeList* pLineList = NodeToList(pLine);
531 :
532 : //Take the selection, and/or find iterator for current position
533 1 : SmNodeList* pSelectedNodesList = new SmNodeList();
534 1 : SmNodeList::iterator it;
535 1 : if(HasSelection())
536 0 : it = TakeSelectedNodesFromList(pLineList, pSelectedNodesList);
537 : else
538 1 : it = FindPositionInLineList(pLineList, position->CaretPos);
539 :
540 : //Find node that this should be applied to
541 : SmNode* pSubject;
542 1 : bool bPatchLine = pSelectedNodesList->size() > 0; //If the line should be patched later
543 1 : if(it != pLineList->begin()) {
544 1 : --it;
545 1 : pSubject = *it;
546 1 : ++it;
547 : } else {
548 : //Create a new place node
549 0 : pSubject = new SmPlaceNode();
550 0 : pSubject->Prepare(pDocShell->GetFormat(), *pDocShell);
551 0 : it = pLineList->insert(it, pSubject);
552 0 : ++it;
553 0 : bPatchLine = true; //We've modified the line it should be patched later.
554 : }
555 :
556 : //Wrap the subject in a SmSubSupNode
557 : SmSubSupNode* pSubSup;
558 1 : if(pSubject->GetType() != NSUBSUP){
559 1 : SmToken token;
560 1 : token.nGroup = TGPOWER;
561 1 : pSubSup = new SmSubSupNode(token);
562 1 : pSubSup->SetBody(pSubject);
563 1 : *(--it) = pSubSup;
564 1 : ++it;
565 : }else
566 0 : pSubSup = (SmSubSupNode*)pSubject;
567 : //pSubject shouldn't be referenced anymore, pSubSup is the SmSubSupNode in pLineList we wish to edit.
568 : //and it pointer to the element following pSubSup in pLineList.
569 1 : pSubject = NULL;
570 :
571 : //Patch the line if we noted that was needed previously
572 1 : if(bPatchLine)
573 0 : PatchLineList(pLineList, it);
574 :
575 : //Convert existing, if any, sub-/superscript line to list
576 1 : SmNode *pScriptLine = pSubSup->GetSubSup(eSubSup);
577 1 : SmNodeList* pScriptLineList = NodeToList(pScriptLine);
578 :
579 : //Add selection to pScriptLineList
580 1 : unsigned int nOldSize = pScriptLineList->size();
581 1 : pScriptLineList->insert(pScriptLineList->end(), pSelectedNodesList->begin(), pSelectedNodesList->end());
582 1 : delete pSelectedNodesList;
583 1 : pSelectedNodesList = NULL;
584 :
585 : //Patch pScriptLineList if needed
586 1 : if(0 < nOldSize && nOldSize < pScriptLineList->size()) {
587 0 : SmNodeList::iterator iPatchPoint = pScriptLineList->begin();
588 0 : std::advance(iPatchPoint, nOldSize);
589 0 : PatchLineList(pScriptLineList, iPatchPoint);
590 : }
591 :
592 : //Find caret pos, that should be used after sub-/superscription.
593 1 : SmCaretPos PosAfterScript; //Leave invalid for first position
594 1 : if(pScriptLineList->size() > 0)
595 0 : PosAfterScript = SmCaretPos::GetPosAfter(pScriptLineList->back());
596 :
597 : //Parse pScriptLineList
598 1 : pScriptLine = SmNodeListParser().Parse(pScriptLineList);
599 1 : delete pScriptLineList;
600 1 : pScriptLineList = NULL;
601 :
602 : //Insert pScriptLine back into the tree
603 1 : pSubSup->SetSubSup(eSubSup, pScriptLine);
604 :
605 : //Finish editing
606 1 : FinishEdit(pLineList, pLineParent, nParentIndex, PosAfterScript, pScriptLine);
607 1 : }
608 :
609 0 : bool SmCursor::InsertLimit(SmSubSup eSubSup, bool bMoveCaret) {
610 : //Find a subject to set limits on
611 0 : SmOperNode *pSubject = NULL;
612 : //Check if pSelectedNode might be a subject
613 0 : if(position->CaretPos.pSelectedNode->GetType() == NOPER)
614 0 : pSubject = (SmOperNode*)position->CaretPos.pSelectedNode;
615 : else {
616 : //If not, check if parent of the current line is a SmOperNode
617 0 : SmNode *pLineNode = FindTopMostNodeInLine(position->CaretPos.pSelectedNode, sal_False);
618 0 : if(pLineNode->GetParent() && pLineNode->GetParent()->GetType() == NOPER)
619 0 : pSubject = (SmOperNode*)pLineNode->GetParent();
620 : }
621 :
622 : //Abort operation if we're not in the appropriate context
623 0 : if(!pSubject)
624 0 : return false;
625 :
626 0 : BeginEdit();
627 :
628 : //Find the sub sup node
629 0 : SmSubSupNode *pSubSup = NULL;
630 : //Check if there's already one there...
631 0 : if(pSubject->GetSubNode(0)->GetType() == NSUBSUP)
632 0 : pSubSup = (SmSubSupNode*)pSubject->GetSubNode(0);
633 : else { //if not create a new SmSubSupNode
634 0 : SmToken token;
635 0 : token.nGroup = TGLIMIT;
636 0 : pSubSup = new SmSubSupNode(token);
637 : //Set it's body
638 0 : pSubSup->SetBody(pSubject->GetSubNode(0));
639 : //Replace the operation of the SmOperNode
640 0 : pSubject->SetSubNode(0, pSubSup);
641 : }
642 :
643 : //Create the limit, if needed
644 0 : SmCaretPos PosAfterLimit;
645 0 : SmNode *pLine = NULL;
646 0 : if(!pSubSup->GetSubSup(eSubSup)){
647 0 : pLine = new SmPlaceNode();
648 0 : pSubSup->SetSubSup(eSubSup, pLine);
649 0 : PosAfterLimit = SmCaretPos(pLine, 1);
650 : //If it's already there... let's move the caret
651 0 : } else if(bMoveCaret){
652 0 : pLine = pSubSup->GetSubSup(eSubSup);
653 0 : SmNodeList* pLineList = NodeToList(pLine);
654 0 : if(pLineList->size() > 0)
655 0 : PosAfterLimit = SmCaretPos::GetPosAfter(pLineList->back());
656 0 : pLine = SmNodeListParser().Parse(pLineList);
657 0 : delete pLineList;
658 0 : pSubSup->SetSubSup(eSubSup, pLine);
659 : }
660 :
661 : //Rebuild graph of caret positions
662 0 : BuildGraph();
663 0 : AnnotateSelection();
664 :
665 : //Set caret position
666 0 : if(bMoveCaret)
667 0 : if(!SetCaretPosition(PosAfterLimit, true))
668 0 : SetCaretPosition(SmCaretPos(pLine, 0), true);
669 :
670 0 : EndEdit();
671 :
672 0 : return true;
673 : }
674 :
675 0 : void SmCursor::InsertBrackets(SmBracketType eBracketType) {
676 0 : BeginEdit();
677 :
678 0 : AnnotateSelection();
679 :
680 : //Find line
681 : SmNode *pLine;
682 0 : if(HasSelection()) {
683 0 : SmNode *pSNode = FindSelectedNode(pTree);
684 : OSL_ENSURE(pSNode != NULL, "There must be a selected node if HasSelection()");
685 0 : pLine = FindTopMostNodeInLine(pSNode, sal_True);
686 : } else
687 0 : pLine = FindTopMostNodeInLine(position->CaretPos.pSelectedNode, sal_False);
688 :
689 : //Find parent and offset in parent
690 0 : SmStructureNode *pLineParent = pLine->GetParent();
691 0 : int nParentIndex = pLineParent->IndexOfSubNode(pLine);
692 : OSL_ENSURE( nParentIndex != -1, "pLine must be a subnode of pLineParent!");
693 :
694 : //Convert line to list
695 0 : SmNodeList *pLineList = NodeToList(pLine);
696 :
697 : //Take the selection, and/or find iterator for current position
698 0 : SmNodeList *pSelectedNodesList = new SmNodeList();
699 0 : SmNodeList::iterator it;
700 0 : if(HasSelection())
701 0 : it = TakeSelectedNodesFromList(pLineList, pSelectedNodesList);
702 : else
703 0 : it = FindPositionInLineList(pLineList, position->CaretPos);
704 :
705 : //If there's no selected nodes, create a place node
706 : SmNode *pBodyNode;
707 0 : SmCaretPos PosAfterInsert;
708 0 : if(pSelectedNodesList->empty()) {
709 0 : pBodyNode = new SmPlaceNode();
710 0 : PosAfterInsert = SmCaretPos(pBodyNode, 1);
711 : } else
712 0 : pBodyNode = SmNodeListParser().Parse(pSelectedNodesList);
713 :
714 0 : delete pSelectedNodesList;
715 :
716 : //Create SmBraceNode
717 0 : SmToken aTok(TLEFT, '\0', "left", 0, 5);
718 0 : SmBraceNode *pBrace = new SmBraceNode(aTok);
719 0 : pBrace->SetScaleMode(SCALE_HEIGHT);
720 0 : SmNode *pLeft = CreateBracket(eBracketType, true),
721 0 : *pRight = CreateBracket(eBracketType, false);
722 0 : SmBracebodyNode *pBody = new SmBracebodyNode(SmToken());
723 0 : pBody->SetSubNodes(pBodyNode, NULL);
724 0 : pBrace->SetSubNodes(pLeft, pBody, pRight);
725 0 : pBrace->Prepare(pDocShell->GetFormat(), *pDocShell);
726 :
727 : //Insert into line
728 0 : pLineList->insert(it, pBrace);
729 : //Patch line (I think this is good enough)
730 0 : SmCaretPos pAfter = PatchLineList(pLineList, it);
731 0 : if( !PosAfterInsert.IsValid() )
732 0 : PosAfterInsert = pAfter;
733 :
734 : //Finish editing
735 0 : FinishEdit(pLineList, pLineParent, nParentIndex, PosAfterInsert);
736 0 : }
737 :
738 0 : SmNode *SmCursor::CreateBracket(SmBracketType eBracketType, bool bIsLeft) {
739 0 : SmToken aTok;
740 0 : if(bIsLeft){
741 0 : switch(eBracketType){
742 : case NoneBrackets:
743 0 : aTok = SmToken(TNONE, '\0', "none", TGLBRACES | TGRBRACES, 0);
744 0 : break;
745 : case RoundBrackets:
746 0 : aTok = SmToken(TLPARENT, MS_LPARENT, "(", TGLBRACES, 5);
747 0 : break;
748 : case SquareBrackets:
749 0 : aTok = SmToken(TLBRACKET, MS_LBRACKET, "[", TGLBRACES, 5);
750 0 : break;
751 : case DoubleSquareBrackets:
752 0 : aTok = SmToken(TLDBRACKET, MS_LDBRACKET, "ldbracket", TGLBRACES, 5);
753 0 : break;
754 : case LineBrackets:
755 0 : aTok = SmToken(TLLINE, MS_LINE, "lline", TGLBRACES, 5);
756 0 : break;
757 : case DoubleLineBrackets:
758 0 : aTok = SmToken(TLDLINE, MS_DLINE, "ldline", TGLBRACES, 5);
759 0 : break;
760 : case CurlyBrackets:
761 0 : aTok = SmToken(TLBRACE, MS_LBRACE, "lbrace", TGLBRACES, 5);
762 0 : break;
763 : case AngleBrackets:
764 0 : aTok = SmToken(TLANGLE, MS_LANGLE, "langle", TGLBRACES, 5);
765 0 : break;
766 : case CeilBrackets:
767 0 : aTok = SmToken(TLCEIL, MS_LCEIL, "lceil", TGLBRACES, 5);
768 0 : break;
769 : case FloorBrackets:
770 0 : aTok = SmToken(TLFLOOR, MS_LFLOOR, "lfloor", TGLBRACES, 5);
771 0 : break;
772 : }
773 : } else {
774 0 : switch(eBracketType) {
775 : case NoneBrackets:
776 0 : aTok = SmToken(TNONE, '\0', "none", TGLBRACES | TGRBRACES, 0);
777 0 : break;
778 : case RoundBrackets:
779 0 : aTok = SmToken(TRPARENT, MS_RPARENT, ")", TGRBRACES, 5);
780 0 : break;
781 : case SquareBrackets:
782 0 : aTok = SmToken(TRBRACKET, MS_RBRACKET, "]", TGRBRACES, 5);
783 0 : break;
784 : case DoubleSquareBrackets:
785 0 : aTok = SmToken(TRDBRACKET, MS_RDBRACKET, "rdbracket", TGRBRACES, 5);
786 0 : break;
787 : case LineBrackets:
788 0 : aTok = SmToken(TRLINE, MS_LINE, "rline", TGRBRACES, 5);
789 0 : break;
790 : case DoubleLineBrackets:
791 0 : aTok = SmToken(TRDLINE, MS_DLINE, "rdline", TGRBRACES, 5);
792 0 : break;
793 : case CurlyBrackets:
794 0 : aTok = SmToken(TRBRACE, MS_RBRACE, "rbrace", TGRBRACES, 5);
795 0 : break;
796 : case AngleBrackets:
797 0 : aTok = SmToken(TRANGLE, MS_RANGLE, "rangle", TGRBRACES, 5);
798 0 : break;
799 : case CeilBrackets:
800 0 : aTok = SmToken(TRCEIL, MS_RCEIL, "rceil", TGRBRACES, 5);
801 0 : break;
802 : case FloorBrackets:
803 0 : aTok = SmToken(TRFLOOR, MS_RFLOOR, "rfloor", TGRBRACES, 5);
804 0 : break;
805 : }
806 : }
807 0 : SmNode* pRetVal = new SmMathSymbolNode(aTok);
808 0 : pRetVal->SetScaleMode(SCALE_HEIGHT);
809 0 : return pRetVal;
810 : }
811 :
812 0 : bool SmCursor::InsertRow() {
813 0 : AnnotateSelection();
814 :
815 : //Find line
816 : SmNode *pLine;
817 0 : if(HasSelection()) {
818 0 : SmNode *pSNode = FindSelectedNode(pTree);
819 : OSL_ENSURE(pSNode != NULL, "There must be a selected node if HasSelection()");
820 0 : pLine = FindTopMostNodeInLine(pSNode, sal_True);
821 : } else
822 0 : pLine = FindTopMostNodeInLine(position->CaretPos.pSelectedNode, sal_False);
823 :
824 : //Find parent and offset in parent
825 0 : SmStructureNode *pLineParent = pLine->GetParent();
826 0 : int nParentIndex = pLineParent->IndexOfSubNode(pLine);
827 : OSL_ENSURE( nParentIndex != -1, "pLine must be a subnode of pLineParent!");
828 :
829 : //Discover the context of this command
830 0 : SmTableNode *pTable = NULL;
831 0 : SmMatrixNode *pMatrix = NULL;
832 0 : int nTableIndex = nParentIndex;
833 0 : if(pLineParent->GetType() == NTABLE)
834 0 : pTable = (SmTableNode*)pLineParent;
835 : //If it's warped in a SmLineNode, we can still insert a newline
836 0 : else if(pLineParent->GetType() == NLINE &&
837 0 : pLineParent->GetParent() &&
838 0 : pLineParent->GetParent()->GetType() == NTABLE) {
839 : //NOTE: This hack might give problems if we stop ignoring SmAlignNode
840 0 : pTable = (SmTableNode*)pLineParent->GetParent();
841 0 : nTableIndex = pTable->IndexOfSubNode(pLineParent);
842 : OSL_ENSURE(nTableIndex != -1, "pLineParent must be a child of its parent!");
843 : }
844 0 : if(pLineParent->GetType() == NMATRIX)
845 0 : pMatrix = (SmMatrixNode*)pLineParent;
846 :
847 : //If we're not in a context that supports InsertRow, return sal_False
848 0 : if(!pTable && !pMatrix)
849 0 : return false;
850 :
851 : //Now we start editing
852 0 : BeginEdit();
853 :
854 : //Convert line to list
855 0 : SmNodeList *pLineList = NodeToList(pLine);
856 :
857 : //Find position in line
858 0 : SmNodeList::iterator it;
859 0 : if(HasSelection()) {
860 : //Take the selected nodes and delete them...
861 0 : it = TakeSelectedNodesFromList(pLineList);
862 : } else
863 0 : it = FindPositionInLineList(pLineList, position->CaretPos);
864 :
865 : //New caret position after inserting the newline/row in whatever context
866 0 : SmCaretPos PosAfterInsert;
867 :
868 : //If we're in the context of a table
869 0 : if(pTable) {
870 0 : SmNodeList *pNewLineList = new SmNodeList();
871 : //Move elements from pLineList to pNewLineList
872 0 : pNewLineList->splice(pNewLineList->begin(), *pLineList, it, pLineList->end());
873 : //Make sure it is valid again
874 0 : it = pLineList->end();
875 0 : if(it != pLineList->begin())
876 0 : --it;
877 0 : if(pNewLineList->empty())
878 0 : pNewLineList->push_front(new SmPlaceNode());
879 : //Parse new line
880 0 : SmNode *pNewLine = SmNodeListParser().Parse(pNewLineList);
881 0 : delete pNewLineList;
882 : //Wrap pNewLine in SmLineNode if needed
883 0 : if(pLineParent->GetType() == NLINE) {
884 0 : SmLineNode *pNewLineNode = new SmLineNode(SmToken(TNEWLINE, '\0', "newline"));
885 0 : pNewLineNode->SetSubNodes(pNewLine, NULL);
886 0 : pNewLine = pNewLineNode;
887 : }
888 : //Get position
889 0 : PosAfterInsert = SmCaretPos(pNewLine, 0);
890 : //Move other nodes if needed
891 0 : for( int i = pTable->GetNumSubNodes(); i > nTableIndex + 1; i--)
892 0 : pTable->SetSubNode(i, pTable->GetSubNode(i-1));
893 :
894 : //Insert new line
895 0 : pTable->SetSubNode(nTableIndex + 1, pNewLine);
896 :
897 : //Check if we need to change token type:
898 0 : if(pTable->GetNumSubNodes() > 2 && pTable->GetToken().eType == TBINOM) {
899 0 : SmToken tok = pTable->GetToken();
900 0 : tok.eType = TSTACK;
901 0 : pTable->SetToken(tok);
902 : }
903 : }
904 : //If we're in the context of a matrix
905 0 : else if(pMatrix) {
906 : //Find position after insert and patch the list
907 0 : PosAfterInsert = PatchLineList(pLineList, it);
908 : //Move other children
909 0 : sal_uInt16 rows = pMatrix->GetNumRows();
910 0 : sal_uInt16 cols = pMatrix->GetNumCols();
911 0 : int nRowStart = (nParentIndex - nParentIndex % cols) + cols;
912 0 : for( int i = pMatrix->GetNumSubNodes() + cols - 1; i >= nRowStart + cols; i--)
913 0 : pMatrix->SetSubNode(i, pMatrix->GetSubNode(i - cols));
914 0 : for( int i = nRowStart; i < nRowStart + cols; i++) {
915 0 : SmPlaceNode *pNewLine = new SmPlaceNode();
916 0 : if(i == nParentIndex + cols)
917 0 : PosAfterInsert = SmCaretPos(pNewLine, 0);
918 0 : pMatrix->SetSubNode(i, pNewLine);
919 : }
920 0 : pMatrix->SetRowCol(rows + 1, cols);
921 : } else
922 : OSL_FAIL("We must be either the context of a table or matrix!");
923 :
924 : //Finish editing
925 0 : FinishEdit(pLineList, pLineParent, nParentIndex, PosAfterInsert);
926 : //FinishEdit is actually used to handle siturations where parent is an instance of
927 : //SmSubSupNode. In this case parent should always be a table or matrix, however, for
928 : //code reuse we just use FinishEdit() here too.
929 0 : return true;
930 : }
931 :
932 2 : void SmCursor::InsertFraction() {
933 2 : AnnotateSelection();
934 :
935 : //Find line
936 : SmNode *pLine;
937 2 : if(HasSelection()) {
938 2 : SmNode *pSNode = FindSelectedNode(pTree);
939 : OSL_ENSURE(pSNode != NULL, "There must be a selected node when HasSelection is true!");
940 2 : pLine = FindTopMostNodeInLine(pSNode, sal_True);
941 : } else
942 0 : pLine = FindTopMostNodeInLine(position->CaretPos.pSelectedNode, sal_False);
943 :
944 : //Find Parent and offset in parent
945 2 : SmStructureNode *pLineParent = pLine->GetParent();
946 2 : int nParentIndex = pLineParent->IndexOfSubNode(pLine);
947 : OSL_ENSURE(nParentIndex != -1, "pLine must be a subnode of pLineParent!");
948 :
949 : //We begin modifying the tree here
950 2 : BeginEdit();
951 :
952 : //Convert line to list
953 2 : SmNodeList* pLineList = NodeToList(pLine);
954 :
955 : //Take the selection, and/or find iterator for current position
956 2 : SmNodeList* pSelectedNodesList = new SmNodeList();
957 2 : SmNodeList::iterator it;
958 2 : if(HasSelection())
959 2 : it = TakeSelectedNodesFromList(pLineList, pSelectedNodesList);
960 : else
961 0 : it = FindPositionInLineList(pLineList, position->CaretPos);
962 :
963 : //Create pNum, and pDenom
964 2 : bool bEmptyFraction = pSelectedNodesList->empty();
965 : SmNode *pNum = bEmptyFraction
966 0 : ? new SmPlaceNode()
967 2 : : SmNodeListParser().Parse(pSelectedNodesList);
968 2 : SmNode *pDenom = new SmPlaceNode();
969 2 : delete pSelectedNodesList;
970 2 : pSelectedNodesList = NULL;
971 :
972 : //Create new fraction
973 2 : SmBinVerNode *pFrac = new SmBinVerNode(SmToken(TOVER, '\0', "over", TGPRODUCT, 0));
974 2 : SmNode *pRect = new SmRectangleNode(SmToken());
975 2 : pFrac->SetSubNodes(pNum, pRect, pDenom);
976 :
977 : //Insert in pLineList
978 2 : SmNodeList::iterator patchIt = pLineList->insert(it, pFrac);
979 2 : PatchLineList(pLineList, patchIt);
980 2 : PatchLineList(pLineList, it);
981 :
982 : //Finish editing
983 2 : SmNode *pSelectedNode = bEmptyFraction ? pNum : pDenom;
984 2 : FinishEdit(pLineList, pLineParent, nParentIndex, SmCaretPos(pSelectedNode, 1));
985 2 : }
986 :
987 9 : void SmCursor::InsertText(rtl::OUString aString)
988 : {
989 9 : BeginEdit();
990 :
991 9 : Delete();
992 :
993 9 : SmToken token;
994 9 : token.eType = TIDENT;
995 9 : token.cMathChar = '\0';
996 9 : token.nGroup = 0;
997 9 : token.nLevel = 5;
998 9 : token.aText = aString;
999 :
1000 9 : SmTextNode* pText = new SmTextNode(token, FNT_VARIABLE);
1001 :
1002 : //Prepare the new node
1003 9 : pText->Prepare(pDocShell->GetFormat(), *pDocShell);
1004 9 : pText->AdjustFontDesc();
1005 :
1006 9 : SmNodeList* pList = new SmNodeList();
1007 9 : pList->push_front(pText);
1008 9 : InsertNodes(pList);
1009 :
1010 9 : EndEdit();
1011 9 : }
1012 :
1013 4 : void SmCursor::InsertElement(SmFormulaElement element){
1014 4 : BeginEdit();
1015 :
1016 4 : Delete();
1017 :
1018 : //Create new node
1019 4 : SmNode* pNewNode = NULL;
1020 4 : switch(element){
1021 : case BlankElement:
1022 : {
1023 0 : SmToken token;
1024 0 : token.nGroup = TGBLANK;
1025 0 : token.aText = "~";
1026 0 : pNewNode = new SmBlankNode(token);
1027 0 : }break;
1028 : case FactorialElement:
1029 : {
1030 0 : SmToken token(TFACT, MS_FACT, "fact", TGUNOPER, 5);
1031 0 : pNewNode = new SmMathSymbolNode(token);
1032 0 : }break;
1033 : case PlusElement:
1034 : {
1035 4 : SmToken token;
1036 4 : token.eType = TPLUS;
1037 4 : token.cMathChar = MS_PLUS;
1038 4 : token.nGroup = TGUNOPER | TGSUM;
1039 4 : token.nLevel = 5;
1040 4 : token.aText = "+";
1041 4 : pNewNode = new SmMathSymbolNode(token);
1042 4 : }break;
1043 : case MinusElement:
1044 : {
1045 0 : SmToken token;
1046 0 : token.eType = TMINUS;
1047 0 : token.cMathChar = MS_MINUS;
1048 0 : token.nGroup = TGUNOPER | TGSUM;
1049 0 : token.nLevel = 5;
1050 0 : token.aText = "-";
1051 0 : pNewNode = new SmMathSymbolNode(token);
1052 0 : }break;
1053 : case CDotElement:
1054 : {
1055 0 : SmToken token;
1056 0 : token.eType = TCDOT;
1057 0 : token.cMathChar = MS_CDOT;
1058 0 : token.nGroup = TGPRODUCT;
1059 0 : token.aText = "cdot";
1060 0 : pNewNode = new SmMathSymbolNode(token);
1061 0 : }break;
1062 : case EqualElement:
1063 : {
1064 0 : SmToken token;
1065 0 : token.eType = TASSIGN;
1066 0 : token.cMathChar = MS_ASSIGN;
1067 0 : token.nGroup = TGRELATION;
1068 0 : token.aText = "=";
1069 0 : pNewNode = new SmMathSymbolNode(token);
1070 0 : }break;
1071 : case LessThanElement:
1072 : {
1073 0 : SmToken token;
1074 0 : token.eType = TLT;
1075 0 : token.cMathChar = MS_LT;
1076 0 : token.nGroup = TGRELATION;
1077 0 : token.aText = "<";
1078 0 : pNewNode = new SmMathSymbolNode(token);
1079 0 : }break;
1080 : case GreaterThanElement:
1081 : {
1082 0 : SmToken token;
1083 0 : token.eType = TGT;
1084 0 : token.cMathChar = MS_GT;
1085 0 : token.nGroup = TGRELATION;
1086 0 : token.aText = ">";
1087 0 : pNewNode = new SmMathSymbolNode(token);
1088 0 : }break;
1089 : case PercentElement:
1090 : {
1091 0 : SmToken token;
1092 0 : token.eType = TTEXT;
1093 0 : token.cMathChar = MS_PERCENT;
1094 0 : token.nGroup = 0;
1095 0 : token.aText = "\"%\"";
1096 0 : pNewNode = new SmMathSymbolNode(token);
1097 0 : }break;
1098 : default:
1099 : OSL_FAIL("Element unknown!");
1100 : }
1101 : OSL_ENSURE(pNewNode != NULL, "No new node was created!");
1102 4 : if(!pNewNode)
1103 4 : return;
1104 :
1105 : //Prepare the new node
1106 4 : pNewNode->Prepare(pDocShell->GetFormat(), *pDocShell);
1107 :
1108 : //Insert new node
1109 4 : SmNodeList* pList = new SmNodeList();
1110 4 : pList->push_front(pNewNode);
1111 4 : InsertNodes(pList);
1112 :
1113 4 : EndEdit();
1114 : }
1115 :
1116 0 : void SmCursor::InsertSpecial(rtl::OUString aString)
1117 : {
1118 0 : BeginEdit();
1119 0 : Delete();
1120 :
1121 0 : aString = comphelper::string::strip(aString, ' ');
1122 :
1123 : //Create instance of special node
1124 0 : SmToken token;
1125 0 : token.eType = TSPECIAL;
1126 0 : token.cMathChar = '\0';
1127 0 : token.nGroup = 0;
1128 0 : token.nLevel = 5;
1129 0 : token.aText = aString;
1130 0 : SmSpecialNode* pSpecial = new SmSpecialNode(token);
1131 :
1132 : //Prepare the special node
1133 0 : pSpecial->Prepare(pDocShell->GetFormat(), *pDocShell);
1134 :
1135 : //Insert the node
1136 0 : SmNodeList* pList = new SmNodeList();
1137 0 : pList->push_front(pSpecial);
1138 0 : InsertNodes(pList);
1139 :
1140 0 : EndEdit();
1141 0 : }
1142 :
1143 0 : void SmCursor::InsertCommand(sal_uInt16 nCommand) {
1144 0 : switch(nCommand){
1145 : case RID_NEWLINE:
1146 0 : InsertRow();
1147 0 : break;
1148 : case RID_FROMX:
1149 0 : InsertLimit(CSUB, true);
1150 0 : break;
1151 : case RID_TOX:
1152 0 : InsertLimit(CSUP, true);
1153 0 : break;
1154 : case RID_FROMXTOY:
1155 0 : if(InsertLimit(CSUB, true))
1156 0 : InsertLimit(CSUP, true);
1157 0 : break;
1158 : default:
1159 0 : InsertCommandText(SM_RESSTR(nCommand));
1160 0 : break;
1161 : }
1162 0 : }
1163 :
1164 0 : void SmCursor::InsertCommandText(OUString aCommandText) {
1165 : //Parse the the sub expression
1166 0 : SmNode* pSubExpr = SmParser().ParseExpression(aCommandText);
1167 :
1168 : //Prepare the subtree
1169 0 : pSubExpr->Prepare(pDocShell->GetFormat(), *pDocShell);
1170 :
1171 : //Convert subtree to list
1172 0 : SmNodeList* pLineList = NodeToList(pSubExpr);
1173 :
1174 0 : BeginEdit();
1175 :
1176 : //Delete any selection
1177 0 : Delete();
1178 :
1179 : //Insert it
1180 0 : InsertNodes(pLineList);
1181 :
1182 0 : EndEdit();
1183 0 : }
1184 :
1185 0 : void SmCursor::Copy(){
1186 0 : if(!HasSelection())
1187 0 : return;
1188 :
1189 : //Find selected node
1190 0 : SmNode* pSNode = FindSelectedNode(pTree);
1191 : //Find visual line
1192 0 : SmNode* pLine = FindTopMostNodeInLine(pSNode, true);
1193 :
1194 : //Clone selected nodes
1195 : SmNodeList* pList;
1196 0 : if(IsLineCompositionNode(pLine))
1197 0 : pList = CloneLineToList((SmStructureNode*)pLine, true);
1198 : else{
1199 0 : pList = new SmNodeList();
1200 : //Special care to only clone selected text
1201 0 : if(pLine->GetType() == NTEXT) {
1202 0 : SmTextNode *pText = (SmTextNode*)pLine;
1203 0 : SmTextNode *pClone = new SmTextNode( pText->GetToken(), pText->GetFontDesc() );
1204 0 : int start = pText->GetSelectionStart(),
1205 0 : length = pText->GetSelectionEnd() - pText->GetSelectionStart();
1206 0 : pClone->ChangeText(pText->GetText().copy(start, length));
1207 0 : pClone->SetScaleMode(pText->GetScaleMode());
1208 0 : pList->push_front(pClone);
1209 : } else {
1210 0 : SmCloningVisitor aCloneFactory;
1211 0 : pList->push_front(aCloneFactory.Clone(pLine));
1212 : }
1213 : }
1214 :
1215 : //Set clipboard
1216 0 : if(pList->size() > 0)
1217 0 : SetClipboard(pList);
1218 : }
1219 :
1220 0 : void SmCursor::Paste() {
1221 0 : BeginEdit();
1222 0 : Delete();
1223 :
1224 0 : if(pClipboard && pClipboard->size() > 0)
1225 0 : InsertNodes(CloneList(pClipboard));
1226 :
1227 0 : EndEdit();
1228 0 : }
1229 :
1230 0 : SmNodeList* SmCursor::CloneList(SmNodeList* pList){
1231 0 : SmCloningVisitor aCloneFactory;
1232 0 : SmNodeList* pClones = new SmNodeList();
1233 :
1234 0 : SmNodeList::iterator it;
1235 0 : for(it = pList->begin(); it != pList->end(); ++it){
1236 0 : SmNode *pClone = aCloneFactory.Clone(*it);
1237 0 : pClones->push_back(pClone);
1238 : }
1239 :
1240 0 : return pClones;
1241 : }
1242 :
1243 4 : void SmCursor::SetClipboard(SmNodeList* pList){
1244 4 : if(pClipboard){
1245 : //Delete all nodes on the clipboard
1246 0 : SmNodeList::iterator it;
1247 0 : for(it = pClipboard->begin(); it != pClipboard->end(); ++it)
1248 0 : delete (*it);
1249 0 : delete pClipboard;
1250 : }
1251 4 : pClipboard = pList;
1252 4 : }
1253 :
1254 16 : SmNode* SmCursor::FindTopMostNodeInLine(SmNode* pSNode, bool MoveUpIfSelected){
1255 : //If we haven't got a subnode
1256 16 : if(!pSNode)
1257 0 : return NULL;
1258 :
1259 : //Move up parent untill we find a node who's
1260 : //parent is NULL or isn't selected and not a type of:
1261 : // SmExpressionNode
1262 : // SmLineNode
1263 : // SmBinHorNode
1264 : // SmUnHorNode
1265 : // SmAlignNode
1266 : // SmFontNode
1267 96 : while(pSNode->GetParent() &&
1268 : ((MoveUpIfSelected &&
1269 8 : pSNode->GetParent()->IsSelected()) ||
1270 36 : IsLineCompositionNode(pSNode->GetParent())))
1271 20 : pSNode = pSNode->GetParent();
1272 : //Now we have the selection line node
1273 16 : return pSNode;
1274 : }
1275 :
1276 9 : SmNode* SmCursor::FindSelectedNode(SmNode* pNode){
1277 9 : SmNodeIterator it(pNode);
1278 19 : while(it.Next()){
1279 9 : if(it->IsSelected())
1280 2 : return it.Current();
1281 7 : SmNode* pRetVal = FindSelectedNode(it.Current());
1282 7 : if(pRetVal)
1283 6 : return pRetVal;
1284 : }
1285 1 : return NULL;
1286 : }
1287 :
1288 24 : SmNodeList* SmCursor::LineToList(SmStructureNode* pLine, SmNodeList* list){
1289 24 : SmNodeIterator it(pLine);
1290 82 : while(it.Next()){
1291 34 : switch(it->GetType()){
1292 : case NLINE:
1293 : case NUNHOR:
1294 : case NEXPRESSION:
1295 : case NBINHOR:
1296 : case NALIGN:
1297 : case NFONT:
1298 10 : LineToList((SmStructureNode*)it.Current(), list);
1299 10 : break;
1300 : case NERROR:
1301 4 : delete it.Current();
1302 4 : break;
1303 : default:
1304 20 : list->push_back(it.Current());
1305 : }
1306 : }
1307 24 : SmNodeArray emptyArray(0);
1308 24 : pLine->SetSubNodes(emptyArray);
1309 24 : delete pLine;
1310 24 : return list;
1311 : }
1312 :
1313 0 : SmNodeList* SmCursor::CloneLineToList(SmStructureNode* pLine, bool bOnlyIfSelected, SmNodeList* pList){
1314 0 : SmCloningVisitor aCloneFactory;
1315 0 : SmNodeIterator it(pLine);
1316 0 : while(it.Next()){
1317 0 : if( IsLineCompositionNode( it.Current() ) )
1318 0 : CloneLineToList( (SmStructureNode*)it.Current(), bOnlyIfSelected, pList );
1319 0 : else if( (!bOnlyIfSelected || it->IsSelected()) && it->GetType() != NERROR ) {
1320 : //Only clone selected text from SmTextNode
1321 0 : if(it->GetType() == NTEXT) {
1322 0 : SmTextNode *pText = (SmTextNode*)it.Current();
1323 0 : SmTextNode *pClone = new SmTextNode( it->GetToken(), pText->GetFontDesc() );
1324 0 : int start = pText->GetSelectionStart(),
1325 0 : length = pText->GetSelectionEnd() - pText->GetSelectionStart();
1326 0 : pClone->ChangeText(pText->GetText().copy(start, length));
1327 0 : pClone->SetScaleMode(pText->GetScaleMode());
1328 0 : pList->push_back(pClone);
1329 : } else
1330 0 : pList->push_back(aCloneFactory.Clone(it.Current()));
1331 : }
1332 : }
1333 0 : return pList;
1334 : }
1335 :
1336 52 : bool SmCursor::IsLineCompositionNode(SmNode* pNode){
1337 52 : switch(pNode->GetType()){
1338 : case NLINE:
1339 : case NUNHOR:
1340 : case NEXPRESSION:
1341 : case NBINHOR:
1342 : case NALIGN:
1343 : case NFONT:
1344 34 : return true;
1345 : default:
1346 18 : return false;
1347 : }
1348 : }
1349 :
1350 0 : int SmCursor::CountSelectedNodes(SmNode* pNode){
1351 0 : int nCount = 0;
1352 0 : SmNodeIterator it(pNode);
1353 0 : while(it.Next()){
1354 0 : if(it->IsSelected() && !IsLineCompositionNode(it.Current()))
1355 0 : nCount++;
1356 0 : nCount += CountSelectedNodes(it.Current());
1357 : }
1358 0 : return nCount;
1359 : }
1360 :
1361 0 : bool SmCursor::HasComplexSelection(){
1362 0 : if(!HasSelection())
1363 0 : return false;
1364 0 : AnnotateSelection();
1365 :
1366 0 : return CountSelectedNodes(pTree) > 1;
1367 : }
1368 :
1369 16 : void SmCursor::FinishEdit(SmNodeList* pLineList,
1370 : SmStructureNode* pParent,
1371 : int nParentIndex,
1372 : SmCaretPos PosAfterEdit,
1373 : SmNode* pStartLine) {
1374 : //Store number of nodes in line for later
1375 16 : int entries = pLineList->size();
1376 :
1377 : //Parse list of nodes to a tree
1378 16 : SmNodeListParser parser;
1379 16 : SmNode* pLine = parser.Parse(pLineList);
1380 16 : delete pLineList;
1381 :
1382 : //Check if we're making the body of a subsup node bigger than one
1383 16 : if(pParent->GetType() == NSUBSUP &&
1384 : nParentIndex == 0 &&
1385 : entries > 1) {
1386 : //Wrap pLine in scalable round brackets
1387 0 : SmToken aTok(TLEFT, '\0', "left", 0, 5);
1388 0 : SmBraceNode *pBrace = new SmBraceNode(aTok);
1389 0 : pBrace->SetScaleMode(SCALE_HEIGHT);
1390 0 : SmNode *pLeft = CreateBracket(RoundBrackets, true),
1391 0 : *pRight = CreateBracket(RoundBrackets, false);
1392 0 : SmBracebodyNode *pBody = new SmBracebodyNode(SmToken());
1393 0 : pBody->SetSubNodes(pLine, NULL);
1394 0 : pBrace->SetSubNodes(pLeft, pBody, pRight);
1395 0 : pBrace->Prepare(pDocShell->GetFormat(), *pDocShell);
1396 0 : pLine = pBrace;
1397 : //TODO: Consider the following alternative behavior:
1398 : //Consider the line: A + {B + C}^D lsub E
1399 : //Here pLineList is B, + and C and pParent is a subsup node with
1400 : //both RSUP and LSUB set. Imagine the user just inserted "B +" in
1401 : //the body of the subsup node...
1402 : //The most natural thing to do would be to make the line like this:
1403 : //A + B lsub E + C ^ D
1404 : //E.g. apply LSUB and LSUP to the first element in pLineList and RSUP
1405 : //and RSUB to the last eleent in pLineList. But how should this act
1406 : //for CSUP and CSUB ???
1407 : //For this reason and because brackets was faster to implement, this solution
1408 : //have been choosen. It might be worth working on the other solution later...
1409 : }
1410 :
1411 : //Set pStartLine if NULL
1412 16 : if(!pStartLine)
1413 15 : pStartLine = pLine;
1414 :
1415 : //Insert it back into the parent
1416 16 : pParent->SetSubNode(nParentIndex, pLine);
1417 :
1418 : //Rebuild graph of caret position
1419 16 : anchor = NULL;
1420 16 : position = NULL;
1421 16 : BuildGraph();
1422 16 : AnnotateSelection(); //Update selection annotation!
1423 :
1424 : //Set caret position
1425 16 : if(!SetCaretPosition(PosAfterEdit, true))
1426 1 : SetCaretPosition(SmCaretPos(pStartLine, 0), true);
1427 :
1428 : //End edit section
1429 16 : EndEdit();
1430 16 : }
1431 :
1432 29 : void SmCursor::BeginEdit(){
1433 58 : if(nEditSections++ > 0) return;
1434 :
1435 16 : bIsEnabledSetModifiedSmDocShell = pDocShell->IsEnableSetModified();
1436 16 : if( bIsEnabledSetModifiedSmDocShell )
1437 0 : pDocShell->EnableSetModified( sal_False );
1438 : }
1439 :
1440 29 : void SmCursor::EndEdit(){
1441 58 : if(--nEditSections > 0) return;
1442 :
1443 16 : pDocShell->SetFormulaArranged(sal_False);
1444 : //Okay, I don't know what this does... :)
1445 : //It's used in SmDocShell::SetText and with places where everything is modified.
1446 : //I think it does some magic, with sfx, but everything is totally undocumented so
1447 : //it's kinda hard to tell...
1448 16 : if ( bIsEnabledSetModifiedSmDocShell )
1449 0 : pDocShell->EnableSetModified( bIsEnabledSetModifiedSmDocShell );
1450 : //I think this notifies people around us that we've modified this document...
1451 16 : pDocShell->SetModified(sal_True);
1452 : //I think SmDocShell uses this value when it sends an update graphics event
1453 : //Anyway comments elsewhere suggests it need to be updated...
1454 16 : pDocShell->nModifyCount++;
1455 :
1456 : //TODO: Consider copying the update accessability code from SmDocShell::SetText in here...
1457 : //This somehow updates the size of SmGraphicView if it is running in embedded mode
1458 16 : if( pDocShell->GetCreateMode() == SFX_CREATE_MODE_EMBEDDED )
1459 16 : pDocShell->OnDocumentPrinterChanged(0);
1460 :
1461 : //Request a replaint...
1462 16 : RequestRepaint();
1463 :
1464 : //Update the edit engine and text of the document
1465 16 : OUString formula;
1466 16 : SmNodeToTextVisitor(pTree, formula);
1467 : //pTree->CreateTextFromNode(formula);
1468 16 : pDocShell->aText = formula;
1469 16 : pDocShell->GetEditEngine().QuickInsertText( formula, ESelection( 0, 0, EE_PARA_ALL, EE_PARA_ALL ) );
1470 16 : pDocShell->GetEditEngine().QuickFormatDoc();
1471 : }
1472 :
1473 44 : void SmCursor::RequestRepaint(){
1474 44 : SmViewShell *pViewSh = SmGetActiveView();
1475 44 : if( pViewSh ) {
1476 0 : if ( SFX_CREATE_MODE_EMBEDDED == pDocShell->GetCreateMode() )
1477 0 : pDocShell->Repaint();
1478 : else
1479 0 : pViewSh->GetGraphicWindow().Invalidate();
1480 : }
1481 44 : }
1482 :
1483 0 : bool SmCursor::IsAtTailOfBracket(SmBracketType eBracketType, SmBraceNode** ppBraceNode) const {
1484 0 : const SmCaretPos pos = GetPosition();
1485 0 : if (!pos.IsValid()) {
1486 0 : return false;
1487 : }
1488 :
1489 0 : SmNode* pNode = pos.pSelectedNode;
1490 :
1491 0 : if (pNode->GetType() == NTEXT) {
1492 0 : SmTextNode* pTextNode = static_cast<SmTextNode*>(pNode);
1493 0 : if (pos.Index < pTextNode->GetText().getLength()) {
1494 : // The cursor is on a text node and at the middle of it.
1495 0 : return false;
1496 : }
1497 : } else {
1498 0 : if (pos.Index < 1) {
1499 0 : return false;
1500 : }
1501 : }
1502 :
1503 0 : while (true) {
1504 0 : SmStructureNode* pParentNode = pNode->GetParent();
1505 0 : if (!pParentNode) {
1506 : // There's no brace body node in the ancestors.
1507 0 : return false;
1508 : }
1509 :
1510 0 : sal_uInt16 index = pNode->FindIndex();
1511 0 : if (index + 1 != pParentNode->GetNumSubNodes()) {
1512 : // The cursor is not at the tail at one of ancestor nodes.
1513 0 : return false;
1514 : }
1515 :
1516 0 : pNode = pParentNode;
1517 0 : if (pNode->GetType() == NBRACEBODY) {
1518 : // Found the brace body node.
1519 0 : break;
1520 : }
1521 : }
1522 :
1523 0 : SmStructureNode* pBraceNodeTmp = pNode->GetParent();
1524 0 : if (!pBraceNodeTmp || pBraceNodeTmp->GetType() != NBRACE) {
1525 : // Brace node is invalid.
1526 0 : return false;
1527 : }
1528 :
1529 0 : SmBraceNode* pBraceNode = static_cast<SmBraceNode*>(pBraceNodeTmp);
1530 0 : SmMathSymbolNode* pClosingNode = pBraceNode->ClosingBrace();
1531 0 : if (!pClosingNode) {
1532 : // Couldn't get closing symbol node.
1533 0 : return false;
1534 : }
1535 :
1536 : // Check if the closing brace matches eBracketType.
1537 0 : SmTokenType eClosingTokenType = pClosingNode->GetToken().eType;
1538 0 : switch (eBracketType) {
1539 0 : case NoneBrackets: if (eClosingTokenType != TNONE) { return false; } break;
1540 0 : case RoundBrackets: if (eClosingTokenType != TRPARENT) { return false; } break;
1541 0 : case SquareBrackets: if (eClosingTokenType != TRBRACKET) { return false; } break;
1542 0 : case DoubleSquareBrackets: if (eClosingTokenType != TRDBRACKET) { return false; } break;
1543 0 : case LineBrackets: if (eClosingTokenType != TRLINE) { return false; } break;
1544 0 : case DoubleLineBrackets: if (eClosingTokenType != TRDLINE) { return false; } break;
1545 0 : case CurlyBrackets: if (eClosingTokenType != TRBRACE) { return false; } break;
1546 0 : case AngleBrackets: if (eClosingTokenType != TRANGLE) { return false; } break;
1547 0 : case CeilBrackets: if (eClosingTokenType != TRCEIL) { return false; } break;
1548 0 : case FloorBrackets: if (eClosingTokenType != TRFLOOR) { return false; } break;
1549 : default:
1550 0 : return false;
1551 : }
1552 :
1553 0 : if (ppBraceNode) {
1554 0 : *ppBraceNode = static_cast<SmBraceNode*>(pBraceNode);
1555 : }
1556 :
1557 0 : return true;
1558 : }
1559 :
1560 0 : void SmCursor::MoveAfterBracket(SmBraceNode* pBraceNode, bool bMoveAnchor)
1561 : {
1562 0 : position->CaretPos.pSelectedNode = pBraceNode;
1563 0 : position->CaretPos.Index = 1;
1564 0 : if (bMoveAnchor) {
1565 0 : anchor->CaretPos.pSelectedNode = pBraceNode;
1566 0 : anchor->CaretPos.Index = 1;
1567 : }
1568 0 : RequestRepaint();
1569 0 : }
1570 :
1571 :
1572 : /////////////////////////////////////// SmNodeListParser ///////////////////////////////////////
1573 :
1574 19 : SmNode* SmNodeListParser::Parse(SmNodeList* list, bool bDeleteErrorNodes){
1575 19 : pList = list;
1576 19 : if(bDeleteErrorNodes){
1577 : //Delete error nodes
1578 19 : SmNodeList::iterator it = pList->begin();
1579 73 : while(it != pList->end()) {
1580 35 : if((*it)->GetType() == NERROR){
1581 : //Delete and erase
1582 0 : delete *it;
1583 0 : it = pList->erase(it);
1584 : }else
1585 35 : ++it;
1586 : }
1587 : }
1588 19 : SmNode* retval = Expression();
1589 19 : pList = NULL;
1590 19 : return retval;
1591 : }
1592 :
1593 19 : SmNode* SmNodeListParser::Expression(){
1594 19 : SmNodeArray NodeArray;
1595 : //Accept as many relations as there is
1596 59 : while(Terminal())
1597 21 : NodeArray.push_back(Relation());
1598 :
1599 : //Create SmExpressionNode, I hope SmToken() will do :)
1600 19 : SmStructureNode* pExpr = new SmExpressionNode(SmToken());
1601 19 : pExpr->SetSubNodes(NodeArray);
1602 19 : return pExpr;
1603 : }
1604 :
1605 21 : SmNode* SmNodeListParser::Relation(){
1606 : //Read a sum
1607 21 : SmNode* pLeft = Sum();
1608 : //While we have tokens and the next is a relation
1609 42 : while(Terminal() && IsRelationOperator(Terminal()->GetToken())){
1610 : //Take the operator
1611 0 : SmNode* pOper = Take();
1612 : //Find the right side of the relation
1613 0 : SmNode* pRight = Sum();
1614 : //Create new SmBinHorNode
1615 0 : SmStructureNode* pNewNode = new SmBinHorNode(SmToken());
1616 0 : pNewNode->SetSubNodes(pLeft, pOper, pRight);
1617 0 : pLeft = pNewNode;
1618 : }
1619 21 : return pLeft;
1620 : }
1621 :
1622 21 : SmNode* SmNodeListParser::Sum(){
1623 : //Read a product
1624 21 : SmNode* pLeft = Product();
1625 : //While we have tokens and the next is a sum
1626 50 : while(Terminal() && IsSumOperator(Terminal()->GetToken())){
1627 : //Take the operator
1628 8 : SmNode* pOper = Take();
1629 : //Find the right side of the sum
1630 8 : SmNode* pRight = Product();
1631 : //Create new SmBinHorNode
1632 8 : SmStructureNode* pNewNode = new SmBinHorNode(SmToken());
1633 8 : pNewNode->SetSubNodes(pLeft, pOper, pRight);
1634 8 : pLeft = pNewNode;
1635 : }
1636 21 : return pLeft;
1637 : }
1638 :
1639 29 : SmNode* SmNodeListParser::Product(){
1640 : //Read a Factor
1641 29 : SmNode* pLeft = Factor();
1642 : //While we have tokens and the next is a product
1643 58 : while(Terminal() && IsProductOperator(Terminal()->GetToken())){
1644 : //Take the operator
1645 0 : SmNode* pOper = Take();
1646 : //Find the right side of the operation
1647 0 : SmNode* pRight = Factor();
1648 : //Create new SmBinHorNode
1649 0 : SmStructureNode* pNewNode = new SmBinHorNode(SmToken());
1650 0 : pNewNode->SetSubNodes(pLeft, pOper, pRight);
1651 0 : pLeft = pNewNode;
1652 : }
1653 29 : return pLeft;
1654 : }
1655 :
1656 31 : SmNode* SmNodeListParser::Factor(){
1657 : //Read unary operations
1658 31 : if(!Terminal())
1659 4 : return Error();
1660 : //Take care of unary operators
1661 27 : else if(IsUnaryOperator(Terminal()->GetToken()))
1662 : {
1663 2 : SmStructureNode *pUnary = new SmUnHorNode(SmToken());
1664 2 : SmNode *pOper = Terminal(),
1665 : *pArg;
1666 :
1667 2 : if(Next())
1668 2 : pArg = Factor();
1669 : else
1670 0 : pArg = Error();
1671 :
1672 2 : pUnary->SetSubNodes(pOper, pArg);
1673 2 : return pUnary;
1674 : }
1675 25 : return Postfix();
1676 : }
1677 :
1678 25 : SmNode* SmNodeListParser::Postfix(){
1679 25 : if(!Terminal())
1680 0 : return Error();
1681 25 : SmNode *pArg = NULL;
1682 25 : if(IsPostfixOperator(Terminal()->GetToken()))
1683 0 : pArg = Error();
1684 25 : else if(IsOperator(Terminal()->GetToken()))
1685 0 : return Error();
1686 : else
1687 25 : pArg = Take();
1688 50 : while(Terminal() && IsPostfixOperator(Terminal()->GetToken())) {
1689 0 : SmStructureNode *pUnary = new SmUnHorNode(SmToken());
1690 0 : SmNode *pOper = Take();
1691 0 : pUnary->SetSubNodes(pArg, pOper);
1692 0 : pArg = pUnary;
1693 : }
1694 25 : return pArg;
1695 : }
1696 :
1697 4 : SmNode* SmNodeListParser::Error(){
1698 4 : return new SmErrorNode(PE_UNEXPECTED_TOKEN, SmToken());
1699 : }
1700 :
1701 27 : bool SmNodeListParser::IsOperator(const SmToken &token) {
1702 27 : return IsRelationOperator(token) ||
1703 27 : IsSumOperator(token) ||
1704 27 : IsProductOperator(token) ||
1705 27 : IsUnaryOperator(token) ||
1706 108 : IsPostfixOperator(token);
1707 : }
1708 :
1709 30 : bool SmNodeListParser::IsRelationOperator(const SmToken &token) {
1710 30 : return token.nGroup & TGRELATION;
1711 : }
1712 :
1713 38 : bool SmNodeListParser::IsSumOperator(const SmToken &token) {
1714 38 : return token.nGroup & TGSUM;
1715 : }
1716 :
1717 38 : bool SmNodeListParser::IsProductOperator(const SmToken &token) {
1718 : return token.nGroup & TGPRODUCT &&
1719 : token.eType != TWIDESLASH &&
1720 : token.eType != TWIDEBACKSLASH &&
1721 : token.eType != TUNDERBRACE &&
1722 : token.eType != TOVERBRACE &&
1723 38 : token.eType != TOVER;
1724 : }
1725 :
1726 54 : bool SmNodeListParser::IsUnaryOperator(const SmToken &token) {
1727 : return token.nGroup & TGUNOPER &&
1728 : (token.eType == TPLUS ||
1729 : token.eType == TMINUS ||
1730 : token.eType == TPLUSMINUS ||
1731 : token.eType == TMINUSPLUS ||
1732 : token.eType == TNEG ||
1733 54 : token.eType == TUOPER);
1734 : }
1735 :
1736 63 : bool SmNodeListParser::IsPostfixOperator(const SmToken &token) {
1737 63 : return token.eType == TFACT;
1738 12 : }
1739 :
1740 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|