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