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