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 : #include "cursor.hxx"
10 : #include "visitors.hxx"
11 : #include "document.hxx"
12 : #include "view.hxx"
13 : #include "accessibility.hxx"
14 : #include <comphelper/string.hxx>
15 :
16 30 : void SmCursor::Move(OutputDevice* pDev, SmMovementDirection direction, bool bMoveAnchor){
17 30 : SmCaretPosGraphEntry* NewPos = NULL;
18 30 : switch(direction){
19 : case MoveLeft:
20 : {
21 5 : NewPos = position->Left;
22 : OSL_ENSURE(NewPos, "NewPos shouldn't be NULL here!");
23 5 : }break;
24 : case MoveRight:
25 : {
26 23 : NewPos = position->Right;
27 : OSL_ENSURE(NewPos, "NewPos shouldn't be NULL here!");
28 23 : }break;
29 : case MoveUp:
30 : //Implementation is practically identical to MoveDown, except for a single if statement
31 : //so I've implemented them together and added a direction == MoveDown to the if statements.
32 : case MoveDown:
33 : {
34 2 : SmCaretLine from_line = SmCaretPos2LineVisitor(pDev, position->CaretPos).GetResult(),
35 2 : best_line, //Best approximated line found so far
36 2 : curr_line; //Current line
37 2 : long dbp_sq = 0; //Distance squared to best line
38 2 : SmCaretPosGraphIterator it = pGraph->GetIterator();
39 18 : while(it.Next()){
40 : //Reject it if it's the current position
41 14 : if(it->CaretPos == position->CaretPos) continue;
42 : //Compute caret line
43 12 : curr_line = SmCaretPos2LineVisitor(pDev, it->CaretPos).GetResult();
44 : //Reject anything above if we're moving down
45 12 : if(curr_line.GetTop() <= from_line.GetTop() && direction == MoveDown) continue;
46 : //Reject anything below if we're moving up
47 0 : if(curr_line.GetTop() + curr_line.GetHeight() >= from_line.GetTop() + from_line.GetHeight()
48 0 : && direction == MoveUp) continue;
49 : //Compare if it to what we have, if we have anything yet
50 0 : if(NewPos){
51 : //Compute distance to current line squared, multiplied with a horizontal factor
52 0 : long dp_sq = curr_line.SquaredDistanceX(from_line) * HORIZONTICAL_DISTANCE_FACTOR +
53 0 : curr_line.SquaredDistanceY(from_line);
54 : //Discard current line if best line is closer
55 0 : if(dbp_sq <= dp_sq) continue;
56 : }
57 : //Take current line as the best
58 0 : best_line = curr_line;
59 0 : NewPos = it.Current();
60 : //Update distance to best line
61 0 : dbp_sq = best_line.SquaredDistanceX(from_line) * HORIZONTICAL_DISTANCE_FACTOR +
62 0 : best_line.SquaredDistanceY(from_line);
63 : }
64 2 : }break;
65 : default:
66 : SAL_WARN("starmath", "Movement direction not supported!");
67 : }
68 30 : if(NewPos){
69 28 : position = NewPos;
70 28 : if(bMoveAnchor)
71 25 : anchor = NewPos;
72 28 : RequestRepaint();
73 : }
74 30 : }
75 :
76 0 : void SmCursor::MoveTo(OutputDevice* pDev, Point pos, bool bMoveAnchor){
77 0 : SmCaretLine best_line, //Best line found so far, when iterating
78 0 : curr_line; //Current line, when iterating
79 0 : SmCaretPosGraphEntry* NewPos = NULL;
80 0 : long dp_sq = 0, //Distance to current line squared
81 0 : dbp_sq = 1; //Distance to best line squared
82 0 : SmCaretPosGraphIterator it = pGraph->GetIterator();
83 0 : while(it.Next()){
84 : OSL_ENSURE(it->CaretPos.IsValid(), "The caret position graph may not have invalid positions!");
85 : //Compute current line
86 0 : curr_line = SmCaretPos2LineVisitor(pDev, it->CaretPos).GetResult();
87 : //If we have a position compare to it
88 0 : if(NewPos){
89 : //Compute squared distance to current line
90 0 : dp_sq = curr_line.SquaredDistanceX(pos) + curr_line.SquaredDistanceY(pos);
91 : //If best line is closer, reject current line
92 0 : if(dbp_sq <= dp_sq) continue;
93 : }
94 : //Accept current position as the best
95 0 : best_line = curr_line;
96 0 : NewPos = it.Current();
97 : //Update distance to best line
98 0 : dbp_sq = best_line.SquaredDistanceX(pos) + best_line.SquaredDistanceY(pos);
99 : }
100 0 : if(NewPos){
101 0 : position = NewPos;
102 0 : if(bMoveAnchor)
103 0 : anchor = NewPos;
104 0 : RequestRepaint();
105 : }
106 0 : }
107 :
108 20 : void SmCursor::BuildGraph(){
109 : //Save the current anchor and position
110 20 : SmCaretPos _anchor, _position;
111 : //Release pGraph if allocated
112 20 : if(pGraph){
113 16 : if(anchor)
114 0 : _anchor = anchor->CaretPos;
115 16 : if(position)
116 0 : _position = position->CaretPos;
117 16 : delete pGraph;
118 : //Reset anchor and position as they point into an old graph
119 16 : anchor = NULL;
120 16 : position = NULL;
121 : }
122 :
123 : //Build the new graph
124 20 : pGraph = SmCaretPosGraphBuildingVisitor(pTree).takeGraph();
125 :
126 : //Restore anchor and position pointers
127 20 : if(_anchor.IsValid() || _position.IsValid()){
128 0 : SmCaretPosGraphIterator it = pGraph->GetIterator();
129 0 : while(it.Next()){
130 0 : if(_anchor == it->CaretPos)
131 0 : anchor = it.Current();
132 0 : if(_position == it->CaretPos)
133 0 : position = it.Current();
134 : }
135 : }
136 : //Set position and anchor to first caret position
137 20 : SmCaretPosGraphIterator it = pGraph->GetIterator();
138 20 : if(!position)
139 20 : position = it.Next();
140 20 : if(!anchor)
141 20 : anchor = position;
142 :
143 : OSL_ENSURE(position->CaretPos.IsValid(), "Position must be valid");
144 : OSL_ENSURE(anchor->CaretPos.IsValid(), "Anchor must be valid");
145 20 : }
146 :
147 17 : bool SmCursor::SetCaretPosition(SmCaretPos pos, bool moveAnchor){
148 17 : SmCaretPosGraphIterator it = pGraph->GetIterator();
149 17 : while(it.Next()){
150 120 : if(it->CaretPos == pos){
151 16 : position = it.Current();
152 16 : if(moveAnchor)
153 16 : anchor = it.Current();
154 16 : return true;
155 : }
156 : }
157 1 : return false;
158 : }
159 :
160 19 : void SmCursor::AnnotateSelection(){
161 : //TODO: Manage a state, reset it upon modification and optimize this call
162 19 : SmSetSelectionVisitor(anchor->CaretPos, position->CaretPos, pTree);
163 19 : }
164 :
165 0 : void SmCursor::Draw(OutputDevice& pDev, Point Offset, bool isCaretVisible){
166 0 : SmCaretDrawingVisitor(pDev, GetPosition(), Offset, isCaretVisible);
167 0 : }
168 :
169 0 : void SmCursor::DeletePrev(OutputDevice* pDev){
170 : //Delete only a selection if there's a selection
171 0 : if(HasSelection()){
172 0 : Delete();
173 0 : return;
174 : }
175 :
176 0 : SmNode* pLine = FindTopMostNodeInLine(position->CaretPos.pSelectedNode);
177 0 : SmStructureNode* pLineParent = pLine->GetParent();
178 0 : int nLineOffset = pLineParent->IndexOfSubNode(pLine);
179 :
180 : //If we're in front of a node who's parent is a TABLE
181 0 : if(pLineParent->GetType() == NTABLE && position->CaretPos.Index == 0 && nLineOffset > 0){
182 : //Now we can merge with nLineOffset - 1
183 0 : BeginEdit();
184 : //Line to merge things into, so we can delete pLine
185 0 : SmNode* pMergeLine = pLineParent->GetSubNode(nLineOffset-1);
186 : OSL_ENSURE(pMergeLine, "pMergeLine cannot be NULL!");
187 0 : SmCaretPos PosAfterDelete;
188 : //Convert first line to list
189 0 : SmNodeList *pLineList = NodeToList(pMergeLine);
190 0 : if(!pLineList->empty()){
191 : //Find iterator to patch
192 0 : SmNodeList::iterator patchPoint = pLineList->end();
193 0 : --patchPoint;
194 : //Convert second line to list
195 0 : NodeToList(pLine, pLineList);
196 : //Patch the line list
197 0 : ++patchPoint;
198 0 : PosAfterDelete = PatchLineList(pLineList, patchPoint);
199 : //Parse the line
200 0 : pLine = SmNodeListParser().Parse(pLineList);
201 : }
202 0 : delete pLineList;
203 0 : pLineParent->SetSubNode(nLineOffset-1, pLine);
204 : //Delete the removed line slot
205 0 : SmNodeArray lines(pLineParent->GetNumSubNodes()-1);
206 0 : for(int i = 0; i < pLineParent->GetNumSubNodes(); i++){
207 0 : if(i < nLineOffset)
208 0 : lines[i] = pLineParent->GetSubNode(i);
209 0 : else if(i > nLineOffset)
210 0 : lines[i-1] = pLineParent->GetSubNode(i);
211 : }
212 0 : pLineParent->SetSubNodes(lines);
213 : //Rebuild graph
214 0 : anchor = NULL;
215 0 : position = NULL;
216 0 : BuildGraph();
217 0 : AnnotateSelection();
218 : //Set caret position
219 0 : if(!SetCaretPosition(PosAfterDelete, true))
220 0 : SetCaretPosition(SmCaretPos(pLine, 0), true);
221 : //Finish editing
222 0 : EndEdit();
223 :
224 : //TODO: If we're in an empty (sub/super/*) script
225 : /*}else if(pLineParent->GetType() == NSUBSUP &&
226 : nLineOffset != 0 &&
227 : pLine->GetType() == NEXPRESSION &&
228 : pLine->GetNumSubNodes() == 0){
229 : //There's a (sub/super) script we can delete
230 : //Consider selecting the entire script if GetNumSubNodes() != 0 or pLine->GetType() != NEXPRESSION
231 : //TODO: Handle case where we delete a limit
232 : */
233 :
234 : //Else move select, and delete if not complex
235 : }else{
236 0 : this->Move(pDev, MoveLeft, false);
237 0 : if(!this->HasComplexSelection())
238 0 : Delete();
239 : }
240 : }
241 :
242 13 : void SmCursor::Delete(){
243 : //Return if we don't have a selection to delete
244 13 : if(!HasSelection())
245 26 : return;
246 :
247 : //Enter edit section
248 0 : BeginEdit();
249 :
250 : //Set selected on nodes
251 0 : AnnotateSelection();
252 :
253 : //Find an arbitrary selected node
254 0 : SmNode* pSNode = FindSelectedNode(pTree);
255 : OSL_ENSURE(pSNode != NULL, "There must be a selection when HasSelection is true!");
256 :
257 : //Find the topmost node of the line that holds the selection
258 0 : SmNode* pLine = FindTopMostNodeInLine(pSNode, true);
259 : OSL_ENSURE(pLine != pTree, "Shouldn't be able to select the entire tree");
260 :
261 : //Get the parent of the line
262 0 : SmStructureNode* pLineParent = pLine->GetParent();
263 : //Find line offset in parent
264 0 : int nLineOffset = pLineParent->IndexOfSubNode(pLine);
265 0 : if (nLineOffset == -1)
266 : {
267 : SAL_WARN("starmath", "pLine must be a child of its parent!");
268 0 : return;
269 : }
270 :
271 : //Position after delete
272 0 : SmCaretPos PosAfterDelete;
273 :
274 0 : SmNodeList* pLineList = NodeToList(pLine);
275 :
276 : //Take the selected nodes and delete them...
277 0 : SmNodeList::iterator patchIt = TakeSelectedNodesFromList(pLineList);
278 :
279 : //Get the position to set after delete
280 0 : PosAfterDelete = PatchLineList(pLineList, patchIt);
281 :
282 : //Finish editing
283 0 : FinishEdit(pLineList, pLineParent, nLineOffset, PosAfterDelete);
284 : }
285 :
286 13 : void SmCursor::InsertNodes(SmNodeList* pNewNodes){
287 13 : if(pNewNodes->empty()){
288 0 : delete pNewNodes;
289 0 : return;
290 : }
291 :
292 : //Begin edit section
293 13 : BeginEdit();
294 :
295 : //Position after insert should be after pNewNode
296 13 : SmCaretPos PosAfterInsert = SmCaretPos(pNewNodes->back(), 1);
297 :
298 : //Get the current position
299 13 : const SmCaretPos pos = position->CaretPos;
300 :
301 : //Find top most of line that holds position
302 13 : SmNode* pLine = FindTopMostNodeInLine(pos.pSelectedNode, false);
303 :
304 : //Find line parent and line index in parent
305 13 : SmStructureNode* pLineParent = pLine->GetParent();
306 13 : int nParentIndex = pLineParent->IndexOfSubNode(pLine);
307 : OSL_ENSURE(nParentIndex != -1, "pLine must be a subnode of pLineParent!");
308 13 : if (nParentIndex == -1)
309 : {
310 0 : delete pNewNodes;
311 0 : return;
312 : }
313 :
314 : //Convert line to list
315 13 : SmNodeList* pLineList = NodeToList(pLine);
316 :
317 : //Find iterator for place to insert nodes
318 13 : SmNodeList::iterator it = FindPositionInLineList(pLineList, pos);
319 :
320 : //Insert all new nodes
321 13 : SmNodeList::iterator newIt,
322 13 : patchIt = it, // (pointless default value, fixes compiler warnings)
323 13 : insIt;
324 26 : for(newIt = pNewNodes->begin(); newIt != pNewNodes->end(); ++newIt){
325 13 : insIt = pLineList->insert(it, *newIt);
326 13 : if(newIt == pNewNodes->begin())
327 13 : patchIt = insIt;
328 13 : if((*newIt)->GetType() == NTEXT)
329 9 : PosAfterInsert = SmCaretPos(*newIt, static_cast<SmTextNode*>(*newIt)->GetText().getLength());
330 : else
331 4 : PosAfterInsert = SmCaretPos(*newIt, 1);
332 : }
333 : //Patch the places we've changed stuff
334 13 : PatchLineList(pLineList, patchIt);
335 13 : PosAfterInsert = PatchLineList(pLineList, it);
336 : //Release list, we've taken the nodes
337 13 : delete pNewNodes;
338 13 : pNewNodes = NULL;
339 :
340 : //Finish editing
341 13 : FinishEdit(pLineList, pLineParent, nParentIndex, PosAfterInsert);
342 : }
343 :
344 14 : SmNodeList::iterator SmCursor::FindPositionInLineList(SmNodeList* pLineList, SmCaretPos aCaretPos) {
345 : //Find iterator for position
346 14 : SmNodeList::iterator it;
347 21 : for(it = pLineList->begin(); it != pLineList->end(); ++it){
348 18 : if(*it == aCaretPos.pSelectedNode){
349 11 : if((*it)->GetType() == NTEXT){
350 : //Split textnode if needed
351 2 : if(aCaretPos.Index > 0){
352 2 : SmTextNode* pText = static_cast<SmTextNode*>(aCaretPos.pSelectedNode);
353 2 : OUString str1 = pText->GetText().copy(0, aCaretPos.Index);
354 4 : OUString str2 = pText->GetText().copy(aCaretPos.Index);
355 2 : pText->ChangeText(str1);
356 2 : ++it;
357 : //Insert str2 as new text node
358 2 : if(!str2.isEmpty()){
359 0 : SmTextNode* pNewText = new SmTextNode(pText->GetToken(), pText->GetFontDesc());
360 0 : pNewText->ChangeText(str2);
361 0 : it = pLineList->insert(it, pNewText);
362 2 : }
363 : }
364 : }else
365 9 : ++it;
366 : //it now pointer to the node following pos, so pLineList->insert(it, ...) will insert correctly
367 11 : return it;
368 :
369 : }
370 : }
371 : //If we didn't find pSelectedNode, it must be because the caret is in front of the line
372 3 : return pLineList->begin();
373 : }
374 :
375 30 : SmCaretPos SmCursor::PatchLineList(SmNodeList* pLineList, SmNodeList::iterator aIter) {
376 : //The nodes we should consider merging
377 30 : SmNode *prev = NULL,
378 30 : *next = NULL;
379 30 : if(aIter != pLineList->end())
380 16 : next = *aIter;
381 30 : if(aIter != pLineList->begin()) {
382 26 : --aIter;
383 26 : prev = *aIter;
384 26 : ++aIter;
385 : }
386 :
387 : //Check if there's textnodes to merge
388 56 : if( prev &&
389 12 : next &&
390 14 : prev->GetType() == NTEXT &&
391 32 : next->GetType() == NTEXT &&
392 0 : ( prev->GetToken().eType != TNUMBER ||
393 0 : next->GetToken().eType == TNUMBER) ){
394 0 : SmTextNode *pText = static_cast<SmTextNode*>(prev),
395 0 : *pOldN = static_cast<SmTextNode*>(next);
396 0 : SmCaretPos retval(pText, pText->GetText().getLength());
397 0 : OUString newText;
398 0 : newText += pText->GetText();
399 0 : newText += pOldN->GetText();
400 0 : pText->ChangeText(newText);
401 0 : delete pOldN;
402 0 : pLineList->erase(aIter);
403 0 : return retval;
404 : }
405 :
406 : //Check if there's a SmPlaceNode to remove:
407 30 : if(prev && next && prev->GetType() == NPLACE && !SmNodeListParser::IsOperator(next->GetToken())){
408 2 : --aIter;
409 2 : aIter = pLineList->erase(aIter);
410 2 : delete prev;
411 : //Return caret pos in front of aIter
412 2 : if(aIter != pLineList->begin())
413 0 : --aIter; //Thus find node before aIter
414 2 : if(aIter == pLineList->begin())
415 2 : return SmCaretPos();
416 0 : if((*aIter)->GetType() == NTEXT)
417 0 : return SmCaretPos(*aIter, static_cast<SmTextNode*>(*aIter)->GetText().getLength());
418 0 : return SmCaretPos(*aIter, 1);
419 : }
420 28 : if(prev && next && next->GetType() == NPLACE && !SmNodeListParser::IsOperator(prev->GetToken())){
421 0 : aIter = pLineList->erase(aIter);
422 0 : delete next;
423 0 : if(prev->GetType() == NTEXT)
424 0 : return SmCaretPos(prev, static_cast<SmTextNode*>(prev)->GetText().getLength());
425 0 : return SmCaretPos(prev, 1);
426 : }
427 :
428 : //If we didn't do anything return
429 28 : if(!prev) //return an invalid to indicate we're in front of line
430 4 : return SmCaretPos();
431 24 : if(prev->GetType() == NTEXT)
432 10 : return SmCaretPos(prev, static_cast<SmTextNode*>(prev)->GetText().getLength());
433 14 : return SmCaretPos(prev, 1);
434 : }
435 :
436 2 : SmNodeList::iterator SmCursor::TakeSelectedNodesFromList(SmNodeList *pLineList,
437 : SmNodeList *pSelectedNodes) {
438 2 : SmNodeList::iterator retval;
439 2 : SmNodeList::iterator it = pLineList->begin();
440 8 : while(it != pLineList->end()){
441 4 : if((*it)->IsSelected()){
442 : //Split text nodes
443 3 : if((*it)->GetType() == NTEXT) {
444 2 : SmTextNode* pText = static_cast<SmTextNode*>(*it);
445 2 : OUString aText = pText->GetText();
446 : //Start and lengths of the segments, 2 is the selected segment
447 2 : int start2 = pText->GetSelectionStart(),
448 2 : start3 = pText->GetSelectionEnd(),
449 2 : len1 = start2 - 0,
450 2 : len2 = start3 - start2,
451 2 : len3 = aText.getLength() - start3;
452 4 : SmToken aToken = pText->GetToken();
453 2 : sal_uInt16 eFontDesc = pText->GetFontDesc();
454 : //If we need make segment 1
455 2 : if(len1 > 0) {
456 0 : int start1 = 0;
457 0 : OUString str = aText.copy(start1, len1);
458 0 : pText->ChangeText(str);
459 0 : ++it;
460 : } else {//Remove it if not needed
461 2 : it = pLineList->erase(it);
462 2 : delete pText;
463 : }
464 : //Set retval to be right after the selection
465 2 : retval = it;
466 : //if we need make segment 3
467 2 : if(len3 > 0) {
468 0 : OUString str = aText.copy(start3, len3);
469 0 : SmTextNode* pSeg3 = new SmTextNode(aToken, eFontDesc);
470 0 : pSeg3->ChangeText(str);
471 0 : retval = pLineList->insert(it, pSeg3);
472 : }
473 : //If we need to save the selected text
474 2 : if(pSelectedNodes && len2 > 0) {
475 2 : OUString str = aText.copy(start2, len2);
476 2 : SmTextNode* pSeg2 = new SmTextNode(aToken, eFontDesc);
477 2 : pSeg2->ChangeText(str);
478 2 : pSelectedNodes->push_back(pSeg2);
479 2 : }
480 : } else { //if it's not textnode
481 1 : SmNode* pNode = *it;
482 1 : retval = it = pLineList->erase(it);
483 1 : if(pSelectedNodes)
484 1 : pSelectedNodes->push_back(pNode);
485 : else
486 0 : delete pNode;
487 : }
488 : } else
489 1 : ++it;
490 : }
491 2 : return retval;
492 : }
493 :
494 1 : void SmCursor::InsertSubSup(SmSubSup eSubSup) {
495 1 : AnnotateSelection();
496 :
497 : //Find line
498 : SmNode *pLine;
499 1 : if(HasSelection()) {
500 0 : SmNode *pSNode = FindSelectedNode(pTree);
501 : OSL_ENSURE(pSNode != NULL, "There must be a selected node when HasSelection is true!");
502 0 : pLine = FindTopMostNodeInLine(pSNode, true);
503 : } else
504 1 : pLine = FindTopMostNodeInLine(position->CaretPos.pSelectedNode, false);
505 :
506 : //Find Parent and offset in parent
507 1 : SmStructureNode *pLineParent = pLine->GetParent();
508 1 : int nParentIndex = pLineParent->IndexOfSubNode(pLine);
509 : OSL_ENSURE(nParentIndex != -1, "pLine must be a subnode of pLineParent!");
510 :
511 : //TODO: Consider handling special cases where parent is an SmOperNode,
512 : // Maybe this method should be able to add limits to an SmOperNode...
513 :
514 : //We begin modifying the tree here
515 1 : BeginEdit();
516 :
517 : //Convert line to list
518 1 : SmNodeList* pLineList = NodeToList(pLine);
519 :
520 : //Take the selection, and/or find iterator for current position
521 1 : SmNodeList* pSelectedNodesList = new SmNodeList();
522 1 : SmNodeList::iterator it;
523 1 : if(HasSelection())
524 0 : it = TakeSelectedNodesFromList(pLineList, pSelectedNodesList);
525 : else
526 1 : it = FindPositionInLineList(pLineList, position->CaretPos);
527 :
528 : //Find node that this should be applied to
529 : SmNode* pSubject;
530 1 : bool bPatchLine = pSelectedNodesList->size() > 0; //If the line should be patched later
531 1 : if(it != pLineList->begin()) {
532 1 : --it;
533 1 : pSubject = *it;
534 1 : ++it;
535 : } else {
536 : //Create a new place node
537 0 : pSubject = new SmPlaceNode();
538 0 : pSubject->Prepare(pDocShell->GetFormat(), *pDocShell);
539 0 : it = pLineList->insert(it, pSubject);
540 0 : ++it;
541 0 : bPatchLine = true; //We've modified the line it should be patched later.
542 : }
543 :
544 : //Wrap the subject in a SmSubSupNode
545 : SmSubSupNode* pSubSup;
546 1 : if(pSubject->GetType() != NSUBSUP){
547 1 : SmToken token;
548 1 : token.nGroup = TGPOWER;
549 1 : pSubSup = new SmSubSupNode(token);
550 1 : pSubSup->SetBody(pSubject);
551 1 : *(--it) = pSubSup;
552 1 : ++it;
553 : }else
554 0 : pSubSup = static_cast<SmSubSupNode*>(pSubject);
555 : //pSubject shouldn't be referenced anymore, pSubSup is the SmSubSupNode in pLineList we wish to edit.
556 : //and it pointer to the element following pSubSup in pLineList.
557 1 : pSubject = NULL;
558 :
559 : //Patch the line if we noted that was needed previously
560 1 : if(bPatchLine)
561 0 : PatchLineList(pLineList, it);
562 :
563 : //Convert existing, if any, sub-/superscript line to list
564 1 : SmNode *pScriptLine = pSubSup->GetSubSup(eSubSup);
565 1 : SmNodeList* pScriptLineList = NodeToList(pScriptLine);
566 :
567 : //Add selection to pScriptLineList
568 1 : unsigned int nOldSize = pScriptLineList->size();
569 1 : pScriptLineList->insert(pScriptLineList->end(), pSelectedNodesList->begin(), pSelectedNodesList->end());
570 1 : delete pSelectedNodesList;
571 1 : pSelectedNodesList = NULL;
572 :
573 : //Patch pScriptLineList if needed
574 1 : if(0 < nOldSize && nOldSize < pScriptLineList->size()) {
575 0 : SmNodeList::iterator iPatchPoint = pScriptLineList->begin();
576 0 : std::advance(iPatchPoint, nOldSize);
577 0 : PatchLineList(pScriptLineList, iPatchPoint);
578 : }
579 :
580 : //Find caret pos, that should be used after sub-/superscription.
581 1 : SmCaretPos PosAfterScript; //Leave invalid for first position
582 1 : if(pScriptLineList->size() > 0)
583 0 : PosAfterScript = SmCaretPos::GetPosAfter(pScriptLineList->back());
584 :
585 : //Parse pScriptLineList
586 1 : pScriptLine = SmNodeListParser().Parse(pScriptLineList);
587 1 : delete pScriptLineList;
588 1 : pScriptLineList = NULL;
589 :
590 : //Insert pScriptLine back into the tree
591 1 : pSubSup->SetSubSup(eSubSup, pScriptLine);
592 :
593 : //Finish editing
594 1 : FinishEdit(pLineList, pLineParent, nParentIndex, PosAfterScript, pScriptLine);
595 1 : }
596 :
597 0 : bool SmCursor::InsertLimit(SmSubSup eSubSup, bool bMoveCaret) {
598 : //Find a subject to set limits on
599 0 : SmOperNode *pSubject = NULL;
600 : //Check if pSelectedNode might be a subject
601 0 : if(position->CaretPos.pSelectedNode->GetType() == NOPER)
602 0 : pSubject = static_cast<SmOperNode*>(position->CaretPos.pSelectedNode);
603 : else {
604 : //If not, check if parent of the current line is a SmOperNode
605 0 : SmNode *pLineNode = FindTopMostNodeInLine(position->CaretPos.pSelectedNode, false);
606 0 : if(pLineNode->GetParent() && pLineNode->GetParent()->GetType() == NOPER)
607 0 : pSubject = static_cast<SmOperNode*>(pLineNode->GetParent());
608 : }
609 :
610 : //Abort operation if we're not in the appropriate context
611 0 : if(!pSubject)
612 0 : return false;
613 :
614 0 : BeginEdit();
615 :
616 : //Find the sub sup node
617 0 : SmSubSupNode *pSubSup = NULL;
618 : //Check if there's already one there...
619 0 : if(pSubject->GetSubNode(0)->GetType() == NSUBSUP)
620 0 : pSubSup = static_cast<SmSubSupNode*>(pSubject->GetSubNode(0));
621 : else { //if not create a new SmSubSupNode
622 0 : SmToken token;
623 0 : token.nGroup = TGLIMIT;
624 0 : pSubSup = new SmSubSupNode(token);
625 : //Set it's body
626 0 : pSubSup->SetBody(pSubject->GetSubNode(0));
627 : //Replace the operation of the SmOperNode
628 0 : pSubject->SetSubNode(0, pSubSup);
629 : }
630 :
631 : //Create the limit, if needed
632 0 : SmCaretPos PosAfterLimit;
633 0 : SmNode *pLine = NULL;
634 0 : if(!pSubSup->GetSubSup(eSubSup)){
635 0 : pLine = new SmPlaceNode();
636 0 : pSubSup->SetSubSup(eSubSup, pLine);
637 0 : PosAfterLimit = SmCaretPos(pLine, 1);
638 : //If it's already there... let's move the caret
639 0 : } else if(bMoveCaret){
640 0 : pLine = pSubSup->GetSubSup(eSubSup);
641 0 : SmNodeList* pLineList = NodeToList(pLine);
642 0 : if(pLineList->size() > 0)
643 0 : PosAfterLimit = SmCaretPos::GetPosAfter(pLineList->back());
644 0 : pLine = SmNodeListParser().Parse(pLineList);
645 0 : delete pLineList;
646 0 : pSubSup->SetSubSup(eSubSup, pLine);
647 : }
648 :
649 : //Rebuild graph of caret positions
650 0 : BuildGraph();
651 0 : AnnotateSelection();
652 :
653 : //Set caret position
654 0 : if(bMoveCaret)
655 0 : if(!SetCaretPosition(PosAfterLimit, true))
656 0 : SetCaretPosition(SmCaretPos(pLine, 0), true);
657 :
658 0 : EndEdit();
659 :
660 0 : return true;
661 : }
662 :
663 0 : void SmCursor::InsertBrackets(SmBracketType eBracketType) {
664 0 : BeginEdit();
665 :
666 0 : AnnotateSelection();
667 :
668 : //Find line
669 : SmNode *pLine;
670 0 : if(HasSelection()) {
671 0 : SmNode *pSNode = FindSelectedNode(pTree);
672 : OSL_ENSURE(pSNode != NULL, "There must be a selected node if HasSelection()");
673 0 : pLine = FindTopMostNodeInLine(pSNode, true);
674 : } else
675 0 : pLine = FindTopMostNodeInLine(position->CaretPos.pSelectedNode, false);
676 :
677 : //Find parent and offset in parent
678 0 : SmStructureNode *pLineParent = pLine->GetParent();
679 0 : int nParentIndex = pLineParent->IndexOfSubNode(pLine);
680 : OSL_ENSURE( nParentIndex != -1, "pLine must be a subnode of pLineParent!");
681 0 : if (nParentIndex < 0)
682 0 : return;
683 :
684 : //Convert line to list
685 0 : SmNodeList *pLineList = NodeToList(pLine);
686 :
687 : //Take the selection, and/or find iterator for current position
688 0 : SmNodeList *pSelectedNodesList = new SmNodeList();
689 0 : SmNodeList::iterator it;
690 0 : if(HasSelection())
691 0 : it = TakeSelectedNodesFromList(pLineList, pSelectedNodesList);
692 : else
693 0 : it = FindPositionInLineList(pLineList, position->CaretPos);
694 :
695 : //If there's no selected nodes, create a place node
696 : SmNode *pBodyNode;
697 0 : SmCaretPos PosAfterInsert;
698 0 : if(pSelectedNodesList->empty()) {
699 0 : pBodyNode = new SmPlaceNode();
700 0 : PosAfterInsert = SmCaretPos(pBodyNode, 1);
701 : } else
702 0 : pBodyNode = SmNodeListParser().Parse(pSelectedNodesList);
703 :
704 0 : delete pSelectedNodesList;
705 :
706 : //Create SmBraceNode
707 0 : SmToken aTok(TLEFT, '\0', "left", 0, 5);
708 0 : SmBraceNode *pBrace = new SmBraceNode(aTok);
709 0 : pBrace->SetScaleMode(SCALE_HEIGHT);
710 0 : SmNode *pLeft = CreateBracket(eBracketType, true),
711 0 : *pRight = CreateBracket(eBracketType, false);
712 0 : SmBracebodyNode *pBody = new SmBracebodyNode(SmToken());
713 0 : pBody->SetSubNodes(pBodyNode, NULL);
714 0 : pBrace->SetSubNodes(pLeft, pBody, pRight);
715 0 : pBrace->Prepare(pDocShell->GetFormat(), *pDocShell);
716 :
717 : //Insert into line
718 0 : pLineList->insert(it, pBrace);
719 : //Patch line (I think this is good enough)
720 0 : SmCaretPos pAfter = PatchLineList(pLineList, it);
721 0 : if( !PosAfterInsert.IsValid() )
722 0 : PosAfterInsert = pAfter;
723 :
724 : //Finish editing
725 0 : FinishEdit(pLineList, pLineParent, nParentIndex, PosAfterInsert);
726 : }
727 :
728 0 : SmNode *SmCursor::CreateBracket(SmBracketType eBracketType, bool bIsLeft) {
729 0 : SmToken aTok;
730 0 : if(bIsLeft){
731 0 : switch(eBracketType){
732 : case NoneBrackets:
733 0 : aTok = SmToken(TNONE, '\0', "none", TGLBRACES | TGRBRACES, 0);
734 0 : break;
735 : case RoundBrackets:
736 0 : aTok = SmToken(TLPARENT, MS_LPARENT, "(", TGLBRACES, 5);
737 0 : break;
738 : case SquareBrackets:
739 0 : aTok = SmToken(TLBRACKET, MS_LBRACKET, "[", TGLBRACES, 5);
740 0 : break;
741 : case DoubleSquareBrackets:
742 0 : aTok = SmToken(TLDBRACKET, MS_LDBRACKET, "ldbracket", TGLBRACES, 5);
743 0 : break;
744 : case LineBrackets:
745 0 : aTok = SmToken(TLLINE, MS_VERTLINE, "lline", TGLBRACES, 5);
746 0 : break;
747 : case DoubleLineBrackets:
748 0 : aTok = SmToken(TLDLINE, MS_DVERTLINE, "ldline", TGLBRACES, 5);
749 0 : break;
750 : case CurlyBrackets:
751 0 : aTok = SmToken(TLBRACE, MS_LBRACE, "lbrace", TGLBRACES, 5);
752 0 : break;
753 : case AngleBrackets:
754 0 : aTok = SmToken(TLANGLE, MS_LMATHANGLE, "langle", TGLBRACES, 5);
755 0 : break;
756 : case CeilBrackets:
757 0 : aTok = SmToken(TLCEIL, MS_LCEIL, "lceil", TGLBRACES, 5);
758 0 : break;
759 : case FloorBrackets:
760 0 : aTok = SmToken(TLFLOOR, MS_LFLOOR, "lfloor", TGLBRACES, 5);
761 0 : break;
762 : }
763 : } else {
764 0 : switch(eBracketType) {
765 : case NoneBrackets:
766 0 : aTok = SmToken(TNONE, '\0', "none", TGLBRACES | TGRBRACES, 0);
767 0 : break;
768 : case RoundBrackets:
769 0 : aTok = SmToken(TRPARENT, MS_RPARENT, ")", TGRBRACES, 5);
770 0 : break;
771 : case SquareBrackets:
772 0 : aTok = SmToken(TRBRACKET, MS_RBRACKET, "]", TGRBRACES, 5);
773 0 : break;
774 : case DoubleSquareBrackets:
775 0 : aTok = SmToken(TRDBRACKET, MS_RDBRACKET, "rdbracket", TGRBRACES, 5);
776 0 : break;
777 : case LineBrackets:
778 0 : aTok = SmToken(TRLINE, MS_VERTLINE, "rline", TGRBRACES, 5);
779 0 : break;
780 : case DoubleLineBrackets:
781 0 : aTok = SmToken(TRDLINE, MS_DVERTLINE, "rdline", TGRBRACES, 5);
782 0 : break;
783 : case CurlyBrackets:
784 0 : aTok = SmToken(TRBRACE, MS_RBRACE, "rbrace", TGRBRACES, 5);
785 0 : break;
786 : case AngleBrackets:
787 0 : aTok = SmToken(TRANGLE, MS_RMATHANGLE, "rangle", TGRBRACES, 5);
788 0 : break;
789 : case CeilBrackets:
790 0 : aTok = SmToken(TRCEIL, MS_RCEIL, "rceil", TGRBRACES, 5);
791 0 : break;
792 : case FloorBrackets:
793 0 : aTok = SmToken(TRFLOOR, MS_RFLOOR, "rfloor", TGRBRACES, 5);
794 0 : break;
795 : }
796 : }
797 0 : SmNode* pRetVal = new SmMathSymbolNode(aTok);
798 0 : pRetVal->SetScaleMode(SCALE_HEIGHT);
799 0 : return pRetVal;
800 : }
801 :
802 0 : bool SmCursor::InsertRow() {
803 0 : AnnotateSelection();
804 :
805 : //Find line
806 : SmNode *pLine;
807 0 : if(HasSelection()) {
808 0 : SmNode *pSNode = FindSelectedNode(pTree);
809 : OSL_ENSURE(pSNode != NULL, "There must be a selected node if HasSelection()");
810 0 : pLine = FindTopMostNodeInLine(pSNode, true);
811 : } else
812 0 : pLine = FindTopMostNodeInLine(position->CaretPos.pSelectedNode, false);
813 :
814 : //Find parent and offset in parent
815 0 : SmStructureNode *pLineParent = pLine->GetParent();
816 0 : int nParentIndex = pLineParent->IndexOfSubNode(pLine);
817 :
818 0 : if (nParentIndex == -1)
819 : {
820 : SAL_WARN("starmath", "pLine must be a subnode of pLineParent!");
821 0 : return false;
822 : }
823 :
824 : //Discover the context of this command
825 0 : SmTableNode *pTable = NULL;
826 0 : SmMatrixNode *pMatrix = NULL;
827 0 : int nTableIndex = nParentIndex;
828 0 : if(pLineParent->GetType() == NTABLE)
829 0 : pTable = static_cast<SmTableNode*>(pLineParent);
830 : //If it's warped in a SmLineNode, we can still insert a newline
831 0 : else if(pLineParent->GetType() == NLINE &&
832 0 : pLineParent->GetParent() &&
833 0 : pLineParent->GetParent()->GetType() == NTABLE) {
834 : //NOTE: This hack might give problems if we stop ignoring SmAlignNode
835 0 : pTable = static_cast<SmTableNode*>(pLineParent->GetParent());
836 0 : nTableIndex = pTable->IndexOfSubNode(pLineParent);
837 : OSL_ENSURE(nTableIndex != -1, "pLineParent must be a child of its parent!");
838 : }
839 0 : if(pLineParent->GetType() == NMATRIX)
840 0 : pMatrix = static_cast<SmMatrixNode*>(pLineParent);
841 :
842 : //If we're not in a context that supports InsertRow, return sal_False
843 0 : if(!pTable && !pMatrix)
844 0 : return false;
845 :
846 : //Now we start editing
847 0 : BeginEdit();
848 :
849 : //Convert line to list
850 0 : SmNodeList *pLineList = NodeToList(pLine);
851 :
852 : //Find position in line
853 0 : SmNodeList::iterator it;
854 0 : if(HasSelection()) {
855 : //Take the selected nodes and delete them...
856 0 : it = TakeSelectedNodesFromList(pLineList);
857 : } else
858 0 : it = FindPositionInLineList(pLineList, position->CaretPos);
859 :
860 : //New caret position after inserting the newline/row in whatever context
861 0 : SmCaretPos PosAfterInsert;
862 :
863 : //If we're in the context of a table
864 0 : if(pTable) {
865 0 : SmNodeList *pNewLineList = new SmNodeList();
866 : //Move elements from pLineList to pNewLineList
867 0 : pNewLineList->splice(pNewLineList->begin(), *pLineList, it, pLineList->end());
868 : //Make sure it is valid again
869 0 : it = pLineList->end();
870 0 : if(it != pLineList->begin())
871 0 : --it;
872 0 : if(pNewLineList->empty())
873 0 : pNewLineList->push_front(new SmPlaceNode());
874 : //Parse new line
875 0 : SmNode *pNewLine = SmNodeListParser().Parse(pNewLineList);
876 0 : delete pNewLineList;
877 : //Wrap pNewLine in SmLineNode if needed
878 0 : if(pLineParent->GetType() == NLINE) {
879 0 : SmLineNode *pNewLineNode = new SmLineNode(SmToken(TNEWLINE, '\0', "newline"));
880 0 : pNewLineNode->SetSubNodes(pNewLine, NULL);
881 0 : pNewLine = pNewLineNode;
882 : }
883 : //Get position
884 0 : PosAfterInsert = SmCaretPos(pNewLine, 0);
885 : //Move other nodes if needed
886 0 : for( int i = pTable->GetNumSubNodes(); i > nTableIndex + 1; i--)
887 0 : pTable->SetSubNode(i, pTable->GetSubNode(i-1));
888 :
889 : //Insert new line
890 0 : pTable->SetSubNode(nTableIndex + 1, pNewLine);
891 :
892 : //Check if we need to change token type:
893 0 : if(pTable->GetNumSubNodes() > 2 && pTable->GetToken().eType == TBINOM) {
894 0 : SmToken tok = pTable->GetToken();
895 0 : tok.eType = TSTACK;
896 0 : pTable->SetToken(tok);
897 : }
898 : }
899 : //If we're in the context of a matrix
900 : else {
901 : //Find position after insert and patch the list
902 0 : PosAfterInsert = PatchLineList(pLineList, it);
903 : //Move other children
904 0 : sal_uInt16 rows = pMatrix->GetNumRows();
905 0 : sal_uInt16 cols = pMatrix->GetNumCols();
906 0 : int nRowStart = (nParentIndex - nParentIndex % cols) + cols;
907 0 : for( int i = pMatrix->GetNumSubNodes() + cols - 1; i >= nRowStart + cols; i--)
908 0 : pMatrix->SetSubNode(i, pMatrix->GetSubNode(i - cols));
909 0 : for( int i = nRowStart; i < nRowStart + cols; i++) {
910 0 : SmPlaceNode *pNewLine = new SmPlaceNode();
911 0 : if(i == nParentIndex + cols)
912 0 : PosAfterInsert = SmCaretPos(pNewLine, 0);
913 0 : pMatrix->SetSubNode(i, pNewLine);
914 : }
915 0 : pMatrix->SetRowCol(rows + 1, cols);
916 : }
917 :
918 : //Finish editing
919 0 : FinishEdit(pLineList, pLineParent, nParentIndex, PosAfterInsert);
920 : //FinishEdit is actually used to handle siturations where parent is an instance of
921 : //SmSubSupNode. In this case parent should always be a table or matrix, however, for
922 : //code reuse we just use FinishEdit() here too.
923 0 : return true;
924 : }
925 :
926 2 : void SmCursor::InsertFraction() {
927 2 : AnnotateSelection();
928 :
929 : //Find line
930 : SmNode *pLine;
931 2 : if(HasSelection()) {
932 2 : SmNode *pSNode = FindSelectedNode(pTree);
933 : OSL_ENSURE(pSNode != NULL, "There must be a selected node when HasSelection is true!");
934 2 : pLine = FindTopMostNodeInLine(pSNode, true);
935 : } else
936 0 : pLine = FindTopMostNodeInLine(position->CaretPos.pSelectedNode, false);
937 :
938 : //Find Parent and offset in parent
939 2 : SmStructureNode *pLineParent = pLine->GetParent();
940 2 : int nParentIndex = pLineParent->IndexOfSubNode(pLine);
941 2 : if (nParentIndex == -1)
942 : {
943 : SAL_WARN("starmath", "pLine must be a subnode of pLineParent!");
944 0 : return;
945 : }
946 :
947 : //We begin modifying the tree here
948 2 : BeginEdit();
949 :
950 : //Convert line to list
951 2 : SmNodeList* pLineList = NodeToList(pLine);
952 :
953 : //Take the selection, and/or find iterator for current position
954 2 : SmNodeList* pSelectedNodesList = new SmNodeList();
955 2 : SmNodeList::iterator it;
956 2 : if(HasSelection())
957 2 : it = TakeSelectedNodesFromList(pLineList, pSelectedNodesList);
958 : else
959 0 : it = FindPositionInLineList(pLineList, position->CaretPos);
960 :
961 : //Create pNum, and pDenom
962 2 : bool bEmptyFraction = pSelectedNodesList->empty();
963 : SmNode *pNum = bEmptyFraction
964 0 : ? new SmPlaceNode()
965 2 : : SmNodeListParser().Parse(pSelectedNodesList);
966 2 : SmNode *pDenom = new SmPlaceNode();
967 2 : delete pSelectedNodesList;
968 2 : pSelectedNodesList = NULL;
969 :
970 : //Create new fraction
971 2 : SmBinVerNode *pFrac = new SmBinVerNode(SmToken(TOVER, '\0', "over", TGPRODUCT, 0));
972 2 : SmNode *pRect = new SmRectangleNode(SmToken());
973 2 : pFrac->SetSubNodes(pNum, pRect, pDenom);
974 :
975 : //Insert in pLineList
976 2 : SmNodeList::iterator patchIt = pLineList->insert(it, pFrac);
977 2 : PatchLineList(pLineList, patchIt);
978 2 : PatchLineList(pLineList, it);
979 :
980 : //Finish editing
981 2 : SmNode *pSelectedNode = bEmptyFraction ? pNum : pDenom;
982 2 : FinishEdit(pLineList, pLineParent, nParentIndex, SmCaretPos(pSelectedNode, 1));
983 : }
984 :
985 9 : void SmCursor::InsertText(const OUString& aString)
986 : {
987 9 : BeginEdit();
988 :
989 9 : Delete();
990 :
991 9 : SmToken token;
992 9 : token.eType = TIDENT;
993 9 : token.cMathChar = '\0';
994 9 : token.nGroup = 0;
995 9 : token.nLevel = 5;
996 9 : token.aText = aString;
997 :
998 9 : SmTextNode* pText = new SmTextNode(token, FNT_VARIABLE);
999 :
1000 : //Prepare the new node
1001 9 : pText->Prepare(pDocShell->GetFormat(), *pDocShell);
1002 9 : pText->AdjustFontDesc();
1003 :
1004 9 : SmNodeList* pList = new SmNodeList();
1005 9 : pList->push_front(pText);
1006 9 : InsertNodes(pList);
1007 :
1008 9 : EndEdit();
1009 9 : }
1010 :
1011 4 : void SmCursor::InsertElement(SmFormulaElement element){
1012 4 : BeginEdit();
1013 :
1014 4 : Delete();
1015 :
1016 : //Create new node
1017 4 : SmNode* pNewNode = NULL;
1018 4 : switch(element){
1019 : case BlankElement:
1020 : {
1021 0 : SmToken token;
1022 0 : token.nGroup = TGBLANK;
1023 0 : token.aText = "~";
1024 0 : pNewNode = new SmBlankNode(token);
1025 0 : }break;
1026 : case FactorialElement:
1027 : {
1028 0 : SmToken token(TFACT, MS_FACT, "fact", TGUNOPER, 5);
1029 0 : pNewNode = new SmMathSymbolNode(token);
1030 0 : }break;
1031 : case PlusElement:
1032 : {
1033 4 : SmToken token;
1034 4 : token.eType = TPLUS;
1035 4 : token.cMathChar = MS_PLUS;
1036 4 : token.nGroup = TGUNOPER | TGSUM;
1037 4 : token.nLevel = 5;
1038 4 : token.aText = "+";
1039 4 : pNewNode = new SmMathSymbolNode(token);
1040 4 : }break;
1041 : case MinusElement:
1042 : {
1043 0 : SmToken token;
1044 0 : token.eType = TMINUS;
1045 0 : token.cMathChar = MS_MINUS;
1046 0 : token.nGroup = TGUNOPER | TGSUM;
1047 0 : token.nLevel = 5;
1048 0 : token.aText = "-";
1049 0 : pNewNode = new SmMathSymbolNode(token);
1050 0 : }break;
1051 : case CDotElement:
1052 : {
1053 0 : SmToken token;
1054 0 : token.eType = TCDOT;
1055 0 : token.cMathChar = MS_CDOT;
1056 0 : token.nGroup = TGPRODUCT;
1057 0 : token.aText = "cdot";
1058 0 : pNewNode = new SmMathSymbolNode(token);
1059 0 : }break;
1060 : case EqualElement:
1061 : {
1062 0 : SmToken token;
1063 0 : token.eType = TASSIGN;
1064 0 : token.cMathChar = MS_ASSIGN;
1065 0 : token.nGroup = TGRELATION;
1066 0 : token.aText = "=";
1067 0 : pNewNode = new SmMathSymbolNode(token);
1068 0 : }break;
1069 : case LessThanElement:
1070 : {
1071 0 : SmToken token;
1072 0 : token.eType = TLT;
1073 0 : token.cMathChar = MS_LT;
1074 0 : token.nGroup = TGRELATION;
1075 0 : token.aText = "<";
1076 0 : pNewNode = new SmMathSymbolNode(token);
1077 0 : }break;
1078 : case GreaterThanElement:
1079 : {
1080 0 : SmToken token;
1081 0 : token.eType = TGT;
1082 0 : token.cMathChar = MS_GT;
1083 0 : token.nGroup = TGRELATION;
1084 0 : token.aText = ">";
1085 0 : pNewNode = new SmMathSymbolNode(token);
1086 0 : }break;
1087 : case PercentElement:
1088 : {
1089 0 : SmToken token;
1090 0 : token.eType = TTEXT;
1091 0 : token.cMathChar = MS_PERCENT;
1092 0 : token.nGroup = 0;
1093 0 : token.aText = "\"%\"";
1094 0 : pNewNode = new SmMathSymbolNode(token);
1095 0 : }break;
1096 : default:
1097 : SAL_WARN("starmath", "Element unknown!");
1098 : }
1099 : OSL_ENSURE(pNewNode != NULL, "No new node was created!");
1100 4 : if(!pNewNode)
1101 4 : return;
1102 :
1103 : //Prepare the new node
1104 4 : pNewNode->Prepare(pDocShell->GetFormat(), *pDocShell);
1105 :
1106 : //Insert new node
1107 4 : SmNodeList* pList = new SmNodeList();
1108 4 : pList->push_front(pNewNode);
1109 4 : InsertNodes(pList);
1110 :
1111 4 : EndEdit();
1112 : }
1113 :
1114 0 : void SmCursor::InsertSpecial(const OUString& _aString)
1115 : {
1116 0 : BeginEdit();
1117 0 : Delete();
1118 :
1119 0 : OUString aString = comphelper::string::strip(_aString, ' ');
1120 :
1121 : //Create instance of special node
1122 0 : SmToken token;
1123 0 : token.eType = TSPECIAL;
1124 0 : token.cMathChar = '\0';
1125 0 : token.nGroup = 0;
1126 0 : token.nLevel = 5;
1127 0 : token.aText = aString;
1128 0 : SmSpecialNode* pSpecial = new SmSpecialNode(token);
1129 :
1130 : //Prepare the special node
1131 0 : pSpecial->Prepare(pDocShell->GetFormat(), *pDocShell);
1132 :
1133 : //Insert the node
1134 0 : SmNodeList* pList = new SmNodeList();
1135 0 : pList->push_front(pSpecial);
1136 0 : InsertNodes(pList);
1137 :
1138 0 : EndEdit();
1139 0 : }
1140 :
1141 0 : void SmCursor::InsertCommand(sal_uInt16 nCommand) {
1142 0 : switch(nCommand){
1143 : case RID_NEWLINE:
1144 0 : InsertRow();
1145 0 : break;
1146 : case RID_FROMX:
1147 0 : InsertLimit(CSUB, true);
1148 0 : break;
1149 : case RID_TOX:
1150 0 : InsertLimit(CSUP, true);
1151 0 : break;
1152 : case RID_FROMXTOY:
1153 0 : if(InsertLimit(CSUB, true))
1154 0 : InsertLimit(CSUP, true);
1155 0 : break;
1156 : default:
1157 0 : InsertCommandText(SM_RESSTR(nCommand));
1158 0 : break;
1159 : }
1160 0 : }
1161 :
1162 0 : void SmCursor::InsertCommandText(const OUString& aCommandText) {
1163 : //Parse the sub expression
1164 0 : SmNode* pSubExpr = SmParser().ParseExpression(aCommandText);
1165 :
1166 : //Prepare the subtree
1167 0 : pSubExpr->Prepare(pDocShell->GetFormat(), *pDocShell);
1168 :
1169 : //Convert subtree to list
1170 0 : SmNodeList* pLineList = NodeToList(pSubExpr);
1171 :
1172 0 : BeginEdit();
1173 :
1174 : //Delete any selection
1175 0 : Delete();
1176 :
1177 : //Insert it
1178 0 : InsertNodes(pLineList);
1179 :
1180 0 : EndEdit();
1181 0 : }
1182 :
1183 0 : void SmCursor::Copy(){
1184 0 : if(!HasSelection())
1185 0 : return;
1186 :
1187 : //Find selected node
1188 0 : SmNode* pSNode = FindSelectedNode(pTree);
1189 : //Find visual line
1190 0 : SmNode* pLine = FindTopMostNodeInLine(pSNode, true);
1191 :
1192 : //Clone selected nodes
1193 : SmNodeList* pList;
1194 0 : if(IsLineCompositionNode(pLine))
1195 0 : pList = CloneLineToList(static_cast<SmStructureNode*>(pLine), true);
1196 : else{
1197 0 : pList = new SmNodeList();
1198 : //Special care to only clone selected text
1199 0 : if(pLine->GetType() == NTEXT) {
1200 0 : SmTextNode *pText = static_cast<SmTextNode*>(pLine);
1201 0 : SmTextNode *pClone = new SmTextNode( pText->GetToken(), pText->GetFontDesc() );
1202 0 : int start = pText->GetSelectionStart(),
1203 0 : length = pText->GetSelectionEnd() - pText->GetSelectionStart();
1204 0 : pClone->ChangeText(pText->GetText().copy(start, length));
1205 0 : pClone->SetScaleMode(pText->GetScaleMode());
1206 0 : pList->push_front(pClone);
1207 : } else {
1208 0 : SmCloningVisitor aCloneFactory;
1209 0 : pList->push_front(aCloneFactory.Clone(pLine));
1210 : }
1211 : }
1212 :
1213 : //Set clipboard
1214 0 : if (pList->size() > 0)
1215 0 : SetClipboard(pList);
1216 : else
1217 0 : delete 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 until 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 66 : while(pSNode->GetParent() &&
1268 6 : ((MoveUpIfSelected &&
1269 39 : pSNode->GetParent()->IsSelected()) ||
1270 33 : IsLineCompositionNode(pSNode->GetParent())))
1271 17 : pSNode = pSNode->GetParent();
1272 : //Now we have the selection line node
1273 16 : return pSNode;
1274 : }
1275 :
1276 7 : SmNode* SmCursor::FindSelectedNode(SmNode* pNode){
1277 7 : SmNodeIterator it(pNode);
1278 15 : while(it.Next()){
1279 7 : if(it->IsSelected())
1280 2 : return it.Current();
1281 5 : SmNode* pRetVal = FindSelectedNode(it.Current());
1282 5 : if(pRetVal)
1283 4 : return pRetVal;
1284 : }
1285 1 : return NULL;
1286 : }
1287 :
1288 21 : SmNodeList* SmCursor::LineToList(SmStructureNode* pLine, SmNodeList* list){
1289 21 : SmNodeIterator it(pLine);
1290 73 : while(it.Next()){
1291 31 : switch(it->GetType()){
1292 : case NLINE:
1293 : case NUNHOR:
1294 : case NEXPRESSION:
1295 : case NBINHOR:
1296 : case NALIGN:
1297 : case NFONT:
1298 7 : LineToList(static_cast<SmStructureNode*>(it.Current()), list);
1299 7 : break;
1300 : case NERROR:
1301 4 : delete it.Current();
1302 4 : break;
1303 : default:
1304 20 : list->push_back(it.Current());
1305 : }
1306 : }
1307 21 : SmNodeArray emptyArray(0);
1308 21 : pLine->SetSubNodes(emptyArray);
1309 21 : delete pLine;
1310 21 : 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( static_cast<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 = static_cast<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 49 : bool SmCursor::IsLineCompositionNode(SmNode* pNode){
1337 49 : switch(pNode->GetType()){
1338 : case NLINE:
1339 : case NUNHOR:
1340 : case NEXPRESSION:
1341 : case NBINHOR:
1342 : case NALIGN:
1343 : case NFONT:
1344 31 : 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 35 : if(pParent->GetType() == NSUBSUP &&
1384 16 : 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 chosen. 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( false );
1438 : }
1439 :
1440 29 : void SmCursor::EndEdit(){
1441 58 : if(--nEditSections > 0) return;
1442 :
1443 16 : pDocShell->SetFormulaArranged(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(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() == SfxObjectCreateMode::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_TEXTPOS_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 ( SfxObjectCreateMode::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 : 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 54 : return IsRelationOperator(token) ||
1703 54 : IsSumOperator(token) ||
1704 54 : IsProductOperator(token) ||
1705 81 : IsUnaryOperator(token) ||
1706 54 : 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 46 : return token.nGroup & TGPRODUCT &&
1719 16 : token.eType != TWIDESLASH &&
1720 16 : token.eType != TWIDEBACKSLASH &&
1721 16 : token.eType != TUNDERBRACE &&
1722 54 : token.eType != TOVERBRACE &&
1723 46 : token.eType != TOVER;
1724 : }
1725 :
1726 54 : bool SmNodeListParser::IsUnaryOperator(const SmToken &token) {
1727 58 : return token.nGroup & TGUNOPER &&
1728 4 : (token.eType == TPLUS ||
1729 2 : token.eType == TMINUS ||
1730 0 : token.eType == TPLUSMINUS ||
1731 0 : token.eType == TMINUSPLUS ||
1732 0 : 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 42 : }
1739 :
1740 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|