1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
11 #include <visitors.hxx>
12 #include <document.hxx>
14 #include <comphelper/string.hxx>
15 #include <editeng/editeng.hxx>
18 void SmCursor::Move(OutputDevice
* pDev
, SmMovementDirection direction
, bool bMoveAnchor
){
19 SmCaretPosGraphEntry
* NewPos
= nullptr;
24 NewPos
= mpPosition
->Left
;
25 OSL_ENSURE(NewPos
, "NewPos shouldn't be NULL here!");
29 NewPos
= mpPosition
->Right
;
30 OSL_ENSURE(NewPos
, "NewPos shouldn't be NULL here!");
33 //Implementation is practically identical to MoveDown, except for a single if statement
34 //so I've implemented them together and added a direction == MoveDown to the if statements.
38 SmCaretLine from_line
= SmCaretPos2LineVisitor(pDev
, mpPosition
->CaretPos
).GetResult(),
39 best_line
, //Best approximated line found so far
40 curr_line
; //Current line
41 long dbp_sq
= 0; //Distance squared to best line
42 for(auto &pEntry
: *mpGraph
)
44 //Reject it if it's the current position
45 if(pEntry
->CaretPos
== mpPosition
->CaretPos
) continue;
47 curr_line
= SmCaretPos2LineVisitor(pDev
, pEntry
->CaretPos
).GetResult();
48 //Reject anything above if we're moving down
49 if(curr_line
.GetTop() <= from_line
.GetTop() && direction
== MoveDown
) continue;
50 //Reject anything below if we're moving up
51 if(curr_line
.GetTop() + curr_line
.GetHeight() >= from_line
.GetTop() + from_line
.GetHeight()
52 && direction
== MoveUp
) continue;
53 //Compare if it to what we have, if we have anything yet
55 //Compute distance to current line squared, multiplied with a horizontal factor
56 long dp_sq
= curr_line
.SquaredDistanceX(from_line
) * HORIZONTICAL_DISTANCE_FACTOR
+
57 curr_line
.SquaredDistanceY(from_line
);
58 //Discard current line if best line is closer
59 if(dbp_sq
<= dp_sq
) continue;
61 //Take current line as the best
62 best_line
= curr_line
;
63 NewPos
= pEntry
.get();
64 //Update distance to best line
65 dbp_sq
= best_line
.SquaredDistanceX(from_line
) * HORIZONTICAL_DISTANCE_FACTOR
+
66 best_line
.SquaredDistanceY(from_line
);
81 void SmCursor::MoveTo(OutputDevice
* pDev
, const Point
& pos
, bool bMoveAnchor
)
83 SmCaretPosGraphEntry
* NewPos
= nullptr;
84 long dp_sq
= 0, //Distance to current line squared
85 dbp_sq
= 1; //Distance to best line squared
86 for(auto &pEntry
: *mpGraph
)
88 OSL_ENSURE(pEntry
->CaretPos
.IsValid(), "The caret position graph may not have invalid positions!");
89 //Compute current line
90 SmCaretLine curr_line
= SmCaretPos2LineVisitor(pDev
, pEntry
->CaretPos
).GetResult();
91 //Compute squared distance to current line
92 dp_sq
= curr_line
.SquaredDistanceX(pos
) + curr_line
.SquaredDistanceY(pos
);
93 //If we have a position compare to it
95 //If best line is closer, reject current line
96 if(dbp_sq
<= dp_sq
) continue;
98 //Accept current position as the best
99 NewPos
= pEntry
.get();
100 //Update distance to best line
111 void SmCursor::BuildGraph(){
112 //Save the current anchor and position
113 SmCaretPos _anchor
, _position
;
114 //Release mpGraph if allocated
117 _anchor
= mpAnchor
->CaretPos
;
119 _position
= mpPosition
->CaretPos
;
121 //Reset anchor and position as they point into an old graph
123 mpPosition
= nullptr;
126 //Build the new graph
127 mpGraph
.reset(SmCaretPosGraphBuildingVisitor(mpTree
).takeGraph());
129 //Restore anchor and position pointers
130 if(_anchor
.IsValid() || _position
.IsValid()){
131 for(auto &pEntry
: *mpGraph
)
133 if(_anchor
== pEntry
->CaretPos
)
134 mpAnchor
= pEntry
.get();
135 if(_position
== pEntry
->CaretPos
)
136 mpPosition
= pEntry
.get();
139 //Set position and anchor to first caret position
140 auto it
= mpGraph
->begin();
141 assert(it
!= mpGraph
->end());
143 mpPosition
= it
->get();
145 mpAnchor
= mpPosition
;
149 OSL_ENSURE(mpPosition
->CaretPos
.IsValid(), "Position must be valid");
150 OSL_ENSURE(mpAnchor
->CaretPos
.IsValid(), "Anchor must be valid");
153 bool SmCursor::SetCaretPosition(SmCaretPos pos
){
154 for(auto &pEntry
: *mpGraph
)
156 if(pEntry
->CaretPos
== pos
)
158 mpPosition
= pEntry
.get();
159 mpAnchor
= pEntry
.get();
166 void SmCursor::AnnotateSelection(){
167 //TODO: Manage a state, reset it upon modification and optimize this call
168 SmSetSelectionVisitor(mpAnchor
->CaretPos
, mpPosition
->CaretPos
, mpTree
);
171 void SmCursor::Draw(OutputDevice
& pDev
, Point Offset
, bool isCaretVisible
){
172 SmCaretDrawingVisitor(pDev
, GetPosition(), Offset
, isCaretVisible
);
175 void SmCursor::DeletePrev(OutputDevice
* pDev
){
176 //Delete only a selection if there's a selection
182 SmNode
* pLine
= FindTopMostNodeInLine(mpPosition
->CaretPos
.pSelectedNode
);
183 SmStructureNode
* pLineParent
= pLine
->GetParent();
184 int nLineOffsetIdx
= pLineParent
->IndexOfSubNode(pLine
);
185 assert(nLineOffsetIdx
>= 0);
187 //If we're in front of a node who's parent is a TABLE
188 if (pLineParent
->GetType() == SmNodeType::Table
&& mpPosition
->CaretPos
.nIndex
== 0 && nLineOffsetIdx
> 0)
190 size_t nLineOffset
= nLineOffsetIdx
;
191 //Now we can merge with nLineOffset - 1
193 //Line to merge things into, so we can delete pLine
194 SmNode
* pMergeLine
= pLineParent
->GetSubNode(nLineOffset
-1);
195 OSL_ENSURE(pMergeLine
, "pMergeLine cannot be NULL!");
196 SmCaretPos PosAfterDelete
;
197 //Convert first line to list
198 SmNodeList
*pLineList
= NodeToList(pMergeLine
);
199 if(!pLineList
->empty()){
200 //Find iterator to patch
201 SmNodeList::iterator patchPoint
= pLineList
->end();
203 //Convert second line to list
204 NodeToList(pLine
, pLineList
);
205 //Patch the line list
207 PosAfterDelete
= PatchLineList(pLineList
, patchPoint
);
209 pLine
= SmNodeListParser().Parse(pLineList
);
212 pLineParent
->SetSubNode(nLineOffset
-1, pLine
);
213 //Delete the removed line slot
214 SmNodeArray
lines(pLineParent
->GetNumSubNodes()-1);
215 for (size_t i
= 0; i
< pLineParent
->GetNumSubNodes(); ++i
)
218 lines
[i
] = pLineParent
->GetSubNode(i
);
219 else if(i
> nLineOffset
)
220 lines
[i
-1] = pLineParent
->GetSubNode(i
);
222 pLineParent
->SetSubNodes(std::move(lines
));
225 mpPosition
= nullptr;
229 if(!SetCaretPosition(PosAfterDelete
))
230 SetCaretPosition(SmCaretPos(pLine
, 0));
234 //TODO: If we're in an empty (sub/super/*) script
235 /*}else if(pLineParent->GetType() == SmNodeType::SubSup &&
237 pLine->GetType() == SmNodeType::Expression &&
238 pLine->GetNumSubNodes() == 0){
239 //There's a (sub/super) script we can delete
240 //Consider selecting the entire script if GetNumSubNodes() != 0 or pLine->GetType() != SmNodeType::Expression
241 //TODO: Handle case where we delete a limit
244 //Else move select, and delete if not complex
246 Move(pDev
, MoveLeft
, false);
247 if(!HasComplexSelection())
252 void SmCursor::Delete(){
253 //Return if we don't have a selection to delete
260 //Set selected on nodes
263 //Find an arbitrary selected node
264 SmNode
* pSNode
= FindSelectedNode(mpTree
);
267 //Find the topmost node of the line that holds the selection
268 SmNode
* pLine
= FindTopMostNodeInLine(pSNode
, true);
269 OSL_ENSURE(pLine
!= mpTree
, "Shouldn't be able to select the entire tree");
271 //Get the parent of the line
272 SmStructureNode
* pLineParent
= pLine
->GetParent();
273 //Find line offset in parent
274 int nLineOffset
= pLineParent
->IndexOfSubNode(pLine
);
275 assert(nLineOffset
>= 0);
277 //Position after delete
278 SmCaretPos PosAfterDelete
;
280 SmNodeList
* pLineList
= NodeToList(pLine
);
282 //Take the selected nodes and delete them...
283 SmNodeList::iterator patchIt
= TakeSelectedNodesFromList(pLineList
);
285 //Get the position to set after delete
286 PosAfterDelete
= PatchLineList(pLineList
, patchIt
);
289 FinishEdit(pLineList
, pLineParent
, nLineOffset
, PosAfterDelete
);
292 void SmCursor::InsertNodes(SmNodeList
* pNewNodes
){
293 if(pNewNodes
->empty()){
301 //Get the current position
302 const SmCaretPos pos
= mpPosition
->CaretPos
;
304 //Find top most of line that holds position
305 SmNode
* pLine
= FindTopMostNodeInLine(pos
.pSelectedNode
);
307 //Find line parent and line index in parent
308 SmStructureNode
* pLineParent
= pLine
->GetParent();
309 int nParentIndex
= pLineParent
->IndexOfSubNode(pLine
);
310 assert(nParentIndex
>= 0);
312 //Convert line to list
313 SmNodeList
* pLineList
= NodeToList(pLine
);
315 //Find iterator for place to insert nodes
316 SmNodeList::iterator it
= FindPositionInLineList(pLineList
, pos
);
318 //Insert all new nodes
319 SmNodeList::iterator newIt
,
320 patchIt
= it
, // (pointless default value, fixes compiler warnings)
322 for(newIt
= pNewNodes
->begin(); newIt
!= pNewNodes
->end(); ++newIt
){
323 insIt
= pLineList
->insert(it
, *newIt
);
324 if(newIt
== pNewNodes
->begin())
327 //Patch the places we've changed stuff
328 PatchLineList(pLineList
, patchIt
);
329 SmCaretPos PosAfterInsert
= PatchLineList(pLineList
, it
);
330 //Release list, we've taken the nodes
335 FinishEdit(pLineList
, pLineParent
, nParentIndex
, PosAfterInsert
);
338 SmNodeList::iterator
SmCursor::FindPositionInLineList(SmNodeList
* pLineList
,
339 const SmCaretPos
& rCaretPos
)
341 //Find iterator for position
342 SmNodeList::iterator it
;
343 for(it
= pLineList
->begin(); it
!= pLineList
->end(); ++it
){
344 if(*it
== rCaretPos
.pSelectedNode
)
346 if((*it
)->GetType() == SmNodeType::Text
)
348 //Split textnode if needed
349 if(rCaretPos
.nIndex
> 0)
351 SmTextNode
* pText
= static_cast<SmTextNode
*>(rCaretPos
.pSelectedNode
);
352 if (rCaretPos
.nIndex
== pText
->GetText().getLength())
354 OUString str1
= pText
->GetText().copy(0, rCaretPos
.nIndex
);
355 OUString str2
= pText
->GetText().copy(rCaretPos
.nIndex
);
356 pText
->ChangeText(str1
);
358 //Insert str2 as new text node
359 assert(!str2
.isEmpty());
360 SmTextNode
* pNewText
= new SmTextNode(pText
->GetToken(), pText
->GetFontDesc());
361 pNewText
->ChangeText(str2
);
362 it
= pLineList
->insert(it
, pNewText
);
366 //it now pointer to the node following pos, so pLineList->insert(it, ...) will insert correctly
371 //If we didn't find pSelectedNode, it must be because the caret is in front of the line
372 return pLineList
->begin();
375 SmCaretPos
SmCursor::PatchLineList(SmNodeList
* pLineList
, SmNodeList::iterator aIter
) {
376 //The nodes we should consider merging
377 SmNode
*prev
= nullptr,
379 if(aIter
!= pLineList
->end())
381 if(aIter
!= pLineList
->begin()) {
387 //Check if there's textnodes to merge
390 prev
->GetType() == SmNodeType::Text
&&
391 next
->GetType() == SmNodeType::Text
&&
392 ( prev
->GetToken().eType
!= TNUMBER
||
393 next
->GetToken().eType
== TNUMBER
) ){
394 SmTextNode
*pText
= static_cast<SmTextNode
*>(prev
),
395 *pOldN
= static_cast<SmTextNode
*>(next
);
396 SmCaretPos
retval(pText
, pText
->GetText().getLength());
398 newText
+= pText
->GetText();
399 newText
+= pOldN
->GetText();
400 pText
->ChangeText(newText
);
402 pLineList
->erase(aIter
);
406 //Check if there's a SmPlaceNode to remove:
407 if(prev
&& next
&& prev
->GetType() == SmNodeType::Place
&& !SmNodeListParser::IsOperator(next
->GetToken())){
409 aIter
= pLineList
->erase(aIter
);
411 //Return caret pos in front of aIter
412 if(aIter
!= pLineList
->begin())
413 --aIter
; //Thus find node before aIter
414 if(aIter
== pLineList
->begin())
416 return SmCaretPos::GetPosAfter(*aIter
);
418 if(prev
&& next
&& next
->GetType() == SmNodeType::Place
&& !SmNodeListParser::IsOperator(prev
->GetToken())){
419 aIter
= pLineList
->erase(aIter
);
421 return SmCaretPos::GetPosAfter(prev
);
424 //If we didn't do anything return
425 if(!prev
) //return an invalid to indicate we're in front of line
427 return SmCaretPos::GetPosAfter(prev
);
430 SmNodeList::iterator
SmCursor::TakeSelectedNodesFromList(SmNodeList
*pLineList
,
431 SmNodeList
*pSelectedNodes
) {
432 SmNodeList::iterator retval
;
433 SmNodeList::iterator it
= pLineList
->begin();
434 while(it
!= pLineList
->end()){
435 if((*it
)->IsSelected()){
437 if((*it
)->GetType() == SmNodeType::Text
) {
438 SmTextNode
* pText
= static_cast<SmTextNode
*>(*it
);
439 OUString aText
= pText
->GetText();
440 //Start and lengths of the segments, 2 is the selected segment
441 int start2
= pText
->GetSelectionStart(),
442 start3
= pText
->GetSelectionEnd(),
444 len2
= start3
- start2
,
445 len3
= aText
.getLength() - start3
;
446 SmToken aToken
= pText
->GetToken();
447 sal_uInt16 eFontDesc
= pText
->GetFontDesc();
448 //If we need make segment 1
450 OUString str
= aText
.copy(0, len1
);
451 pText
->ChangeText(str
);
453 } else {//Remove it if not needed
454 it
= pLineList
->erase(it
);
457 //Set retval to be right after the selection
459 //if we need make segment 3
461 OUString str
= aText
.copy(start3
, len3
);
462 SmTextNode
* pSeg3
= new SmTextNode(aToken
, eFontDesc
);
463 pSeg3
->ChangeText(str
);
464 retval
= pLineList
->insert(it
, pSeg3
);
466 //If we need to save the selected text
467 if(pSelectedNodes
&& len2
> 0) {
468 OUString str
= aText
.copy(start2
, len2
);
469 SmTextNode
* pSeg2
= new SmTextNode(aToken
, eFontDesc
);
470 pSeg2
->ChangeText(str
);
471 pSelectedNodes
->push_back(pSeg2
);
473 } else { //if it's not textnode
475 retval
= it
= pLineList
->erase(it
);
477 pSelectedNodes
->push_back(pNode
);
487 void SmCursor::InsertSubSup(SmSubSup eSubSup
) {
493 SmNode
*pSNode
= FindSelectedNode(mpTree
);
495 pLine
= FindTopMostNodeInLine(pSNode
, true);
497 pLine
= FindTopMostNodeInLine(mpPosition
->CaretPos
.pSelectedNode
);
499 //Find Parent and offset in parent
500 SmStructureNode
*pLineParent
= pLine
->GetParent();
501 int nParentIndex
= pLineParent
->IndexOfSubNode(pLine
);
502 assert(nParentIndex
>= 0);
504 //TODO: Consider handling special cases where parent is an SmOperNode,
505 // Maybe this method should be able to add limits to an SmOperNode...
507 //We begin modifying the tree here
510 //Convert line to list
511 SmNodeList
* pLineList
= NodeToList(pLine
);
513 //Take the selection, and/or find iterator for current position
514 SmNodeList
* pSelectedNodesList
= new SmNodeList
;
515 SmNodeList::iterator it
;
517 it
= TakeSelectedNodesFromList(pLineList
, pSelectedNodesList
);
519 it
= FindPositionInLineList(pLineList
, mpPosition
->CaretPos
);
521 //Find node that this should be applied to
523 bool bPatchLine
= !pSelectedNodesList
->empty(); //If the line should be patched later
524 if(it
!= pLineList
->begin()) {
529 //Create a new place node
530 pSubject
= new SmPlaceNode();
531 pSubject
->Prepare(mpDocShell
->GetFormat(), *mpDocShell
, 0);
532 it
= pLineList
->insert(it
, pSubject
);
534 bPatchLine
= true; //We've modified the line it should be patched later.
537 //Wrap the subject in a SmSubSupNode
538 SmSubSupNode
* pSubSup
;
539 if(pSubject
->GetType() != SmNodeType::SubSup
){
541 token
.nGroup
= TG::Power
;
542 pSubSup
= new SmSubSupNode(token
);
543 pSubSup
->SetBody(pSubject
);
547 pSubSup
= static_cast<SmSubSupNode
*>(pSubject
);
548 //pSubject shouldn't be referenced anymore, pSubSup is the SmSubSupNode in pLineList we wish to edit.
549 //and it pointer to the element following pSubSup in pLineList.
552 //Patch the line if we noted that was needed previously
554 PatchLineList(pLineList
, it
);
556 //Convert existing, if any, sub-/superscript line to list
557 SmNode
*pScriptLine
= pSubSup
->GetSubSup(eSubSup
);
558 SmNodeList
* pScriptLineList
= NodeToList(pScriptLine
);
560 //Add selection to pScriptLineList
561 unsigned int nOldSize
= pScriptLineList
->size();
562 pScriptLineList
->insert(pScriptLineList
->end(), pSelectedNodesList
->begin(), pSelectedNodesList
->end());
563 delete pSelectedNodesList
;
564 pSelectedNodesList
= nullptr;
566 //Patch pScriptLineList if needed
567 if(0 < nOldSize
&& nOldSize
< pScriptLineList
->size()) {
568 SmNodeList::iterator iPatchPoint
= pScriptLineList
->begin();
569 std::advance(iPatchPoint
, nOldSize
);
570 PatchLineList(pScriptLineList
, iPatchPoint
);
573 //Find caret pos, that should be used after sub-/superscription.
574 SmCaretPos PosAfterScript
; //Leave invalid for first position
575 if (!pScriptLineList
->empty())
576 PosAfterScript
= SmCaretPos::GetPosAfter(pScriptLineList
->back());
578 //Parse pScriptLineList
579 pScriptLine
= SmNodeListParser().Parse(pScriptLineList
);
580 delete pScriptLineList
;
581 pScriptLineList
= nullptr;
583 //Insert pScriptLine back into the tree
584 pSubSup
->SetSubSup(eSubSup
, pScriptLine
);
587 FinishEdit(pLineList
, pLineParent
, nParentIndex
, PosAfterScript
, pScriptLine
);
590 void SmCursor::InsertBrackets(SmBracketType eBracketType
) {
598 SmNode
*pSNode
= FindSelectedNode(mpTree
);
600 pLine
= FindTopMostNodeInLine(pSNode
, true);
602 pLine
= FindTopMostNodeInLine(mpPosition
->CaretPos
.pSelectedNode
);
604 //Find parent and offset in parent
605 SmStructureNode
*pLineParent
= pLine
->GetParent();
606 int nParentIndex
= pLineParent
->IndexOfSubNode(pLine
);
607 assert(nParentIndex
>= 0);
609 //Convert line to list
610 SmNodeList
*pLineList
= NodeToList(pLine
);
612 //Take the selection, and/or find iterator for current position
613 SmNodeList
*pSelectedNodesList
= new SmNodeList
;
614 SmNodeList::iterator it
;
616 it
= TakeSelectedNodesFromList(pLineList
, pSelectedNodesList
);
618 it
= FindPositionInLineList(pLineList
, mpPosition
->CaretPos
);
620 //If there's no selected nodes, create a place node
622 SmCaretPos PosAfterInsert
;
623 if(pSelectedNodesList
->empty()) {
624 pBodyNode
= new SmPlaceNode();
625 PosAfterInsert
= SmCaretPos(pBodyNode
, 1);
627 pBodyNode
= SmNodeListParser().Parse(pSelectedNodesList
);
629 delete pSelectedNodesList
;
632 SmToken
aTok(TLEFT
, '\0', "left", TG::NONE
, 5);
633 SmBraceNode
*pBrace
= new SmBraceNode(aTok
);
634 pBrace
->SetScaleMode(SmScaleMode::Height
);
635 SmNode
*pLeft
= CreateBracket(eBracketType
, true),
636 *pRight
= CreateBracket(eBracketType
, false);
637 SmBracebodyNode
*pBody
= new SmBracebodyNode(SmToken());
638 pBody
->SetSubNodes(pBodyNode
, nullptr);
639 pBrace
->SetSubNodes(pLeft
, pBody
, pRight
);
640 pBrace
->Prepare(mpDocShell
->GetFormat(), *mpDocShell
, 0);
643 pLineList
->insert(it
, pBrace
);
644 //Patch line (I think this is good enough)
645 SmCaretPos aAfter
= PatchLineList(pLineList
, it
);
646 if( !PosAfterInsert
.IsValid() )
647 PosAfterInsert
= aAfter
;
650 FinishEdit(pLineList
, pLineParent
, nParentIndex
, PosAfterInsert
);
653 SmNode
*SmCursor::CreateBracket(SmBracketType eBracketType
, bool bIsLeft
) {
656 switch(eBracketType
){
657 case SmBracketType::Round
:
658 aTok
= SmToken(TLPARENT
, MS_LPARENT
, "(", TG::LBrace
, 5);
660 case SmBracketType::Square
:
661 aTok
= SmToken(TLBRACKET
, MS_LBRACKET
, "[", TG::LBrace
, 5);
663 case SmBracketType::Curly
:
664 aTok
= SmToken(TLBRACE
, MS_LBRACE
, "lbrace", TG::LBrace
, 5);
668 switch(eBracketType
) {
669 case SmBracketType::Round
:
670 aTok
= SmToken(TRPARENT
, MS_RPARENT
, ")", TG::RBrace
, 5);
672 case SmBracketType::Square
:
673 aTok
= SmToken(TRBRACKET
, MS_RBRACKET
, "]", TG::RBrace
, 5);
675 case SmBracketType::Curly
:
676 aTok
= SmToken(TRBRACE
, MS_RBRACE
, "rbrace", TG::RBrace
, 5);
680 SmNode
* pRetVal
= new SmMathSymbolNode(aTok
);
681 pRetVal
->SetScaleMode(SmScaleMode::Height
);
685 bool SmCursor::InsertRow() {
691 SmNode
*pSNode
= FindSelectedNode(mpTree
);
693 pLine
= FindTopMostNodeInLine(pSNode
, true);
695 pLine
= FindTopMostNodeInLine(mpPosition
->CaretPos
.pSelectedNode
);
697 //Find parent and offset in parent
698 SmStructureNode
*pLineParent
= pLine
->GetParent();
699 int nParentIndex
= pLineParent
->IndexOfSubNode(pLine
);
700 assert(nParentIndex
>= 0);
702 //Discover the context of this command
703 SmTableNode
*pTable
= nullptr;
704 SmMatrixNode
*pMatrix
= nullptr;
705 int nTableIndex
= nParentIndex
;
706 if(pLineParent
->GetType() == SmNodeType::Table
)
707 pTable
= static_cast<SmTableNode
*>(pLineParent
);
708 //If it's wrapped in a SmLineNode, we can still insert a newline
709 else if(pLineParent
->GetType() == SmNodeType::Line
&&
710 pLineParent
->GetParent() &&
711 pLineParent
->GetParent()->GetType() == SmNodeType::Table
) {
712 //NOTE: This hack might give problems if we stop ignoring SmAlignNode
713 pTable
= static_cast<SmTableNode
*>(pLineParent
->GetParent());
714 nTableIndex
= pTable
->IndexOfSubNode(pLineParent
);
715 assert(nTableIndex
>= 0);
717 if(pLineParent
->GetType() == SmNodeType::Matrix
)
718 pMatrix
= static_cast<SmMatrixNode
*>(pLineParent
);
720 //If we're not in a context that supports InsertRow, return sal_False
721 if(!pTable
&& !pMatrix
)
724 //Now we start editing
727 //Convert line to list
728 SmNodeList
*pLineList
= NodeToList(pLine
);
730 //Find position in line
731 SmNodeList::iterator it
;
733 //Take the selected nodes and delete them...
734 it
= TakeSelectedNodesFromList(pLineList
);
736 it
= FindPositionInLineList(pLineList
, mpPosition
->CaretPos
);
738 //New caret position after inserting the newline/row in whatever context
739 SmCaretPos PosAfterInsert
;
741 //If we're in the context of a table
743 SmNodeList
*pNewLineList
= new SmNodeList
;
744 //Move elements from pLineList to pNewLineList
745 pNewLineList
->splice(pNewLineList
->begin(), *pLineList
, it
, pLineList
->end());
746 //Make sure it is valid again
747 it
= pLineList
->end();
748 if(it
!= pLineList
->begin())
750 if(pNewLineList
->empty())
751 pNewLineList
->push_front(new SmPlaceNode());
753 SmNode
*pNewLine
= SmNodeListParser().Parse(pNewLineList
);
755 //Wrap pNewLine in SmLineNode if needed
756 if(pLineParent
->GetType() == SmNodeType::Line
) {
757 SmLineNode
*pNewLineNode
= new SmLineNode(SmToken(TNEWLINE
, '\0', "newline"));
758 pNewLineNode
->SetSubNodes(pNewLine
, nullptr);
759 pNewLine
= pNewLineNode
;
762 PosAfterInsert
= SmCaretPos(pNewLine
, 0);
763 //Move other nodes if needed
764 for( int i
= pTable
->GetNumSubNodes(); i
> nTableIndex
+ 1; i
--)
765 pTable
->SetSubNode(i
, pTable
->GetSubNode(i
-1));
768 pTable
->SetSubNode(nTableIndex
+ 1, pNewLine
);
770 //Check if we need to change token type:
771 if(pTable
->GetNumSubNodes() > 2 && pTable
->GetToken().eType
== TBINOM
) {
772 SmToken tok
= pTable
->GetToken();
774 pTable
->SetToken(tok
);
777 //If we're in the context of a matrix
779 //Find position after insert and patch the list
780 PosAfterInsert
= PatchLineList(pLineList
, it
);
781 //Move other children
782 sal_uInt16 rows
= pMatrix
->GetNumRows();
783 sal_uInt16 cols
= pMatrix
->GetNumCols();
784 int nRowStart
= (nParentIndex
- nParentIndex
% cols
) + cols
;
785 for( int i
= pMatrix
->GetNumSubNodes() + cols
- 1; i
>= nRowStart
+ cols
; i
--)
786 pMatrix
->SetSubNode(i
, pMatrix
->GetSubNode(i
- cols
));
787 for( int i
= nRowStart
; i
< nRowStart
+ cols
; i
++) {
788 SmPlaceNode
*pNewLine
= new SmPlaceNode();
789 if(i
== nParentIndex
+ cols
)
790 PosAfterInsert
= SmCaretPos(pNewLine
, 0);
791 pMatrix
->SetSubNode(i
, pNewLine
);
793 pMatrix
->SetRowCol(rows
+ 1, cols
);
797 FinishEdit(pLineList
, pLineParent
, nParentIndex
, PosAfterInsert
);
798 //FinishEdit is actually used to handle situations where parent is an instance of
799 //SmSubSupNode. In this case parent should always be a table or matrix, however, for
800 //code reuse we just use FinishEdit() here too.
804 void SmCursor::InsertFraction() {
810 SmNode
*pSNode
= FindSelectedNode(mpTree
);
812 pLine
= FindTopMostNodeInLine(pSNode
, true);
814 pLine
= FindTopMostNodeInLine(mpPosition
->CaretPos
.pSelectedNode
);
816 //Find Parent and offset in parent
817 SmStructureNode
*pLineParent
= pLine
->GetParent();
818 int nParentIndex
= pLineParent
->IndexOfSubNode(pLine
);
819 assert(nParentIndex
>= 0);
821 //We begin modifying the tree here
824 //Convert line to list
825 SmNodeList
* pLineList
= NodeToList(pLine
);
827 //Take the selection, and/or find iterator for current position
828 SmNodeList
* pSelectedNodesList
= new SmNodeList
;
829 SmNodeList::iterator it
;
831 it
= TakeSelectedNodesFromList(pLineList
, pSelectedNodesList
);
833 it
= FindPositionInLineList(pLineList
, mpPosition
->CaretPos
);
835 //Create pNum, and pDenom
836 bool bEmptyFraction
= pSelectedNodesList
->empty();
837 SmNode
*pNum
= bEmptyFraction
839 : SmNodeListParser().Parse(pSelectedNodesList
);
840 SmNode
*pDenom
= new SmPlaceNode();
841 delete pSelectedNodesList
;
842 pSelectedNodesList
= nullptr;
844 //Create new fraction
845 SmBinVerNode
*pFrac
= new SmBinVerNode(SmToken(TOVER
, '\0', "over", TG::Product
, 0));
846 SmNode
*pRect
= new SmRectangleNode(SmToken());
847 pFrac
->SetSubNodes(pNum
, pRect
, pDenom
);
849 //Insert in pLineList
850 SmNodeList::iterator patchIt
= pLineList
->insert(it
, pFrac
);
851 PatchLineList(pLineList
, patchIt
);
852 PatchLineList(pLineList
, it
);
855 SmNode
*pSelectedNode
= bEmptyFraction
? pNum
: pDenom
;
856 FinishEdit(pLineList
, pLineParent
, nParentIndex
, SmCaretPos(pSelectedNode
, 1));
859 void SmCursor::InsertText(const OUString
& aString
)
866 token
.eType
= TIDENT
;
867 token
.cMathChar
= '\0';
868 token
.nGroup
= TG::NONE
;
870 token
.aText
= aString
;
872 SmTextNode
* pText
= new SmTextNode(token
, FNT_VARIABLE
);
873 pText
->SetText(aString
);
874 pText
->AdjustFontDesc();
875 pText
->Prepare(mpDocShell
->GetFormat(), *mpDocShell
, 0);
877 SmNodeList
* pList
= new SmNodeList
;
878 pList
->push_front(pText
);
884 void SmCursor::InsertElement(SmFormulaElement element
){
890 SmNode
* pNewNode
= nullptr;
895 token
.eType
= TBLANK
;
896 token
.nGroup
= TG::Blank
;
898 SmBlankNode
* pBlankNode
= new SmBlankNode(token
);
899 pBlankNode
->IncreaseBy(token
);
900 pNewNode
= pBlankNode
;
902 case FactorialElement
:
904 SmToken
token(TFACT
, MS_FACT
, "fact", TG::UnOper
, 5);
905 pNewNode
= new SmMathSymbolNode(token
);
911 token
.cMathChar
= MS_PLUS
;
912 token
.nGroup
= TG::UnOper
| TG::Sum
;
915 pNewNode
= new SmMathSymbolNode(token
);
920 token
.eType
= TMINUS
;
921 token
.cMathChar
= MS_MINUS
;
922 token
.nGroup
= TG::UnOper
| TG::Sum
;
925 pNewNode
= new SmMathSymbolNode(token
);
931 token
.cMathChar
= MS_CDOT
;
932 token
.nGroup
= TG::Product
;
933 token
.aText
= "cdot";
934 pNewNode
= new SmMathSymbolNode(token
);
939 token
.eType
= TASSIGN
;
940 token
.cMathChar
= MS_ASSIGN
;
941 token
.nGroup
= TG::Relation
;
943 pNewNode
= new SmMathSymbolNode(token
);
945 case LessThanElement
:
949 token
.cMathChar
= MS_LT
;
950 token
.nGroup
= TG::Relation
;
952 pNewNode
= new SmMathSymbolNode(token
);
954 case GreaterThanElement
:
958 token
.cMathChar
= MS_GT
;
959 token
.nGroup
= TG::Relation
;
961 pNewNode
= new SmMathSymbolNode(token
);
967 token
.cMathChar
= MS_PERCENT
;
968 token
.nGroup
= TG::NONE
;
969 token
.aText
= "\"%\"";
970 pNewNode
= new SmMathSymbolNode(token
);
975 //Prepare the new node
976 pNewNode
->Prepare(mpDocShell
->GetFormat(), *mpDocShell
, 0);
979 SmNodeList
* pList
= new SmNodeList
;
980 pList
->push_front(pNewNode
);
986 void SmCursor::InsertSpecial(const OUString
& _aString
)
991 OUString aString
= comphelper::string::strip(_aString
, ' ');
993 //Create instance of special node
995 token
.eType
= TSPECIAL
;
996 token
.cMathChar
= '\0';
997 token
.nGroup
= TG::NONE
;
999 token
.aText
= aString
;
1000 SmSpecialNode
* pSpecial
= new SmSpecialNode(token
);
1002 //Prepare the special node
1003 pSpecial
->Prepare(mpDocShell
->GetFormat(), *mpDocShell
, 0);
1006 SmNodeList
* pList
= new SmNodeList
;
1007 pList
->push_front(pSpecial
);
1013 void SmCursor::InsertCommandText(const OUString
& aCommandText
) {
1014 //Parse the sub expression
1015 auto xSubExpr
= SmParser().ParseExpression(aCommandText
);
1017 //Prepare the subtree
1018 xSubExpr
->Prepare(mpDocShell
->GetFormat(), *mpDocShell
, 0);
1020 //Convert subtree to list
1021 SmNode
* pSubExpr
= xSubExpr
.release();
1022 SmNodeList
* pLineList
= NodeToList(pSubExpr
);
1026 //Delete any selection
1030 InsertNodes(pLineList
);
1035 void SmCursor::Copy(){
1039 AnnotateSelection();
1040 //Find selected node
1041 SmNode
* pSNode
= FindSelectedNode(mpTree
);
1044 SmNode
* pLine
= FindTopMostNodeInLine(pSNode
, true);
1047 //Clone selected nodes
1048 SmClipboard aClipboard
;
1049 if(IsLineCompositionNode(pLine
))
1050 CloneLineToClipboard(static_cast<SmStructureNode
*>(pLine
), &aClipboard
);
1052 //Special care to only clone selected text
1053 if(pLine
->GetType() == SmNodeType::Text
) {
1054 SmTextNode
*pText
= static_cast<SmTextNode
*>(pLine
);
1055 std::unique_ptr
<SmTextNode
> pClone(new SmTextNode( pText
->GetToken(), pText
->GetFontDesc() ));
1056 int start
= pText
->GetSelectionStart(),
1057 length
= pText
->GetSelectionEnd() - pText
->GetSelectionStart();
1058 pClone
->ChangeText(pText
->GetText().copy(start
, length
));
1059 pClone
->SetScaleMode(pText
->GetScaleMode());
1060 aClipboard
.push_front(std::move(pClone
));
1062 SmCloningVisitor aCloneFactory
;
1063 aClipboard
.push_front(std::unique_ptr
<SmNode
>(aCloneFactory
.Clone(pLine
)));
1068 if (!aClipboard
.empty())
1069 maClipboard
= std::move(aClipboard
);
1072 void SmCursor::Paste() {
1076 if (!maClipboard
.empty())
1077 InsertNodes(CloneList(maClipboard
));
1082 SmNodeList
* SmCursor::CloneList(SmClipboard
&rClipboard
){
1083 SmCloningVisitor aCloneFactory
;
1084 SmNodeList
* pClones
= new SmNodeList
;
1086 for(auto &xNode
: rClipboard
){
1087 SmNode
*pClone
= aCloneFactory
.Clone(xNode
.get());
1088 pClones
->push_back(pClone
);
1094 SmNode
* SmCursor::FindTopMostNodeInLine(SmNode
* pSNode
, bool MoveUpIfSelected
){
1096 //Move up parent until we find a node who's
1097 //parent is NULL or isn't selected and not a type of:
1104 while(pSNode
->GetParent() &&
1105 ((MoveUpIfSelected
&&
1106 pSNode
->GetParent()->IsSelected()) ||
1107 IsLineCompositionNode(pSNode
->GetParent())))
1108 pSNode
= pSNode
->GetParent();
1109 //Now we have the selection line node
1113 SmNode
* SmCursor::FindSelectedNode(SmNode
* pNode
){
1114 if(pNode
->GetNumSubNodes() == 0)
1116 for(auto pChild
: *static_cast<SmStructureNode
*>(pNode
))
1120 if(pChild
->IsSelected())
1122 SmNode
* pRetVal
= FindSelectedNode(pChild
);
1129 SmNodeList
* SmCursor::LineToList(SmStructureNode
* pLine
, SmNodeList
* list
){
1130 for(auto pChild
: *pLine
)
1134 switch(pChild
->GetType()){
1135 case SmNodeType::Line
:
1136 case SmNodeType::UnHor
:
1137 case SmNodeType::Expression
:
1138 case SmNodeType::BinHor
:
1139 case SmNodeType::Align
:
1140 case SmNodeType::Font
:
1141 LineToList(static_cast<SmStructureNode
*>(pChild
), list
);
1143 case SmNodeType::Error
:
1147 list
->push_back(pChild
);
1150 pLine
->ClearSubNodes();
1155 void SmCursor::CloneLineToClipboard(SmStructureNode
* pLine
, SmClipboard
* pClipboard
){
1156 SmCloningVisitor aCloneFactory
;
1157 for(auto pChild
: *pLine
)
1161 if( IsLineCompositionNode( pChild
) )
1162 CloneLineToClipboard( static_cast<SmStructureNode
*>(pChild
), pClipboard
);
1163 else if( pChild
->IsSelected() && pChild
->GetType() != SmNodeType::Error
) {
1164 //Only clone selected text from SmTextNode
1165 if(pChild
->GetType() == SmNodeType::Text
) {
1166 SmTextNode
*pText
= static_cast<SmTextNode
*>(pChild
);
1167 std::unique_ptr
<SmTextNode
> pClone(new SmTextNode( pChild
->GetToken(), pText
->GetFontDesc() ));
1168 int start
= pText
->GetSelectionStart(),
1169 length
= pText
->GetSelectionEnd() - pText
->GetSelectionStart();
1170 pClone
->ChangeText(pText
->GetText().copy(start
, length
));
1171 pClone
->SetScaleMode(pText
->GetScaleMode());
1172 pClipboard
->push_back(std::move(pClone
));
1174 pClipboard
->push_back(std::unique_ptr
<SmNode
>(aCloneFactory
.Clone(pChild
)));
1179 bool SmCursor::IsLineCompositionNode(SmNode
const * pNode
){
1180 switch(pNode
->GetType()){
1181 case SmNodeType::Line
:
1182 case SmNodeType::UnHor
:
1183 case SmNodeType::Expression
:
1184 case SmNodeType::BinHor
:
1185 case SmNodeType::Align
:
1186 case SmNodeType::Font
:
1193 int SmCursor::CountSelectedNodes(SmNode
* pNode
){
1194 if(pNode
->GetNumSubNodes() == 0)
1197 for(auto pChild
: *static_cast<SmStructureNode
*>(pNode
))
1201 if(pChild
->IsSelected() && !IsLineCompositionNode(pChild
))
1203 nCount
+= CountSelectedNodes(pChild
);
1208 bool SmCursor::HasComplexSelection(){
1211 AnnotateSelection();
1213 return CountSelectedNodes(mpTree
) > 1;
1216 void SmCursor::FinishEdit(SmNodeList
* pLineList
,
1217 SmStructureNode
* pParent
,
1219 SmCaretPos PosAfterEdit
,
1220 SmNode
* pStartLine
) {
1221 //Store number of nodes in line for later
1222 int entries
= pLineList
->size();
1224 //Parse list of nodes to a tree
1225 SmNodeListParser parser
;
1226 SmNode
* pLine
= parser
.Parse(pLineList
);
1229 //Check if we're making the body of a subsup node bigger than one
1230 if(pParent
->GetType() == SmNodeType::SubSup
&&
1231 nParentIndex
== 0 &&
1233 //Wrap pLine in scalable round brackets
1234 SmToken
aTok(TLEFT
, '\0', "left", TG::NONE
, 5);
1235 SmBraceNode
*pBrace
= new SmBraceNode(aTok
);
1236 pBrace
->SetScaleMode(SmScaleMode::Height
);
1237 SmNode
*pLeft
= CreateBracket(SmBracketType::Round
, true),
1238 *pRight
= CreateBracket(SmBracketType::Round
, false);
1239 SmBracebodyNode
*pBody
= new SmBracebodyNode(SmToken());
1240 pBody
->SetSubNodes(pLine
, nullptr);
1241 pBrace
->SetSubNodes(pLeft
, pBody
, pRight
);
1242 pBrace
->Prepare(mpDocShell
->GetFormat(), *mpDocShell
, 0);
1244 //TODO: Consider the following alternative behavior:
1245 //Consider the line: A + {B + C}^D lsub E
1246 //Here pLineList is B, + and C and pParent is a subsup node with
1247 //both RSUP and LSUB set. Imagine the user just inserted "B +" in
1248 //the body of the subsup node...
1249 //The most natural thing to do would be to make the line like this:
1250 //A + B lsub E + C ^ D
1251 //E.g. apply LSUB and LSUP to the first element in pLineList and RSUP
1252 //and RSUB to the last element in pLineList. But how should this act
1253 //for CSUP and CSUB ???
1254 //For this reason and because brackets was faster to implement, this solution
1255 //have been chosen. It might be worth working on the other solution later...
1258 //Set pStartLine if NULL
1262 //Insert it back into the parent
1263 pParent
->SetSubNode(nParentIndex
, pLine
);
1265 //Rebuild graph of caret position
1267 mpPosition
= nullptr;
1269 AnnotateSelection(); //Update selection annotation!
1271 //Set caret position
1272 if(!SetCaretPosition(PosAfterEdit
))
1273 SetCaretPosition(SmCaretPos(pStartLine
, 0));
1279 void SmCursor::BeginEdit(){
1280 if(mnEditSections
++ > 0) return;
1282 mbIsEnabledSetModifiedSmDocShell
= mpDocShell
->IsEnableSetModified();
1283 if( mbIsEnabledSetModifiedSmDocShell
)
1284 mpDocShell
->EnableSetModified( false );
1287 void SmCursor::EndEdit(){
1288 if(--mnEditSections
> 0) return;
1290 mpDocShell
->SetFormulaArranged(false);
1291 //Okay, I don't know what this does... :)
1292 //It's used in SmDocShell::SetText and with places where everything is modified.
1293 //I think it does some magic, with sfx, but everything is totally undocumented so
1294 //it's kinda hard to tell...
1295 if ( mbIsEnabledSetModifiedSmDocShell
)
1296 mpDocShell
->EnableSetModified( mbIsEnabledSetModifiedSmDocShell
);
1297 //I think this notifies people around us that we've modified this document...
1298 mpDocShell
->SetModified();
1299 //I think SmDocShell uses this value when it sends an update graphics event
1300 //Anyway comments elsewhere suggests it need to be updated...
1301 mpDocShell
->mnModifyCount
++;
1303 //TODO: Consider copying the update accessibility code from SmDocShell::SetText in here...
1304 //This somehow updates the size of SmGraphicView if it is running in embedded mode
1305 if( mpDocShell
->GetCreateMode() == SfxObjectCreateMode::EMBEDDED
)
1306 mpDocShell
->OnDocumentPrinterChanged(nullptr);
1308 //Request a repaint...
1311 //Update the edit engine and text of the document
1313 SmNodeToTextVisitor(mpTree
, formula
);
1314 //mpTree->CreateTextFromNode(formula);
1315 mpDocShell
->maText
= formula
;
1316 mpDocShell
->GetEditEngine().QuickInsertText( formula
, ESelection( 0, 0, EE_PARA_ALL
, EE_TEXTPOS_ALL
) );
1317 mpDocShell
->GetEditEngine().QuickFormatDoc();
1320 void SmCursor::RequestRepaint(){
1321 SmViewShell
*pViewSh
= SmGetActiveView();
1323 if ( SfxObjectCreateMode::EMBEDDED
== mpDocShell
->GetCreateMode() )
1324 mpDocShell
->Repaint();
1326 pViewSh
->GetGraphicWindow().Invalidate();
1330 bool SmCursor::IsAtTailOfBracket(SmBracketType eBracketType
, SmBraceNode
** ppBraceNode
) const {
1331 const SmCaretPos pos
= GetPosition();
1332 if (!pos
.IsValid()) {
1336 SmNode
* pNode
= pos
.pSelectedNode
;
1338 if (pNode
->GetType() == SmNodeType::Text
) {
1339 SmTextNode
* pTextNode
= static_cast<SmTextNode
*>(pNode
);
1340 if (pos
.nIndex
< pTextNode
->GetText().getLength()) {
1341 // The cursor is on a text node and at the middle of it.
1345 if (pos
.nIndex
< 1) {
1351 SmStructureNode
* pParentNode
= pNode
->GetParent();
1353 // There's no brace body node in the ancestors.
1357 int index
= pParentNode
->IndexOfSubNode(pNode
);
1359 if (static_cast<size_t>(index
+ 1) != pParentNode
->GetNumSubNodes()) {
1360 // The cursor is not at the tail at one of ancestor nodes.
1364 pNode
= pParentNode
;
1365 if (pNode
->GetType() == SmNodeType::Bracebody
) {
1366 // Found the brace body node.
1371 SmStructureNode
* pBraceNodeTmp
= pNode
->GetParent();
1372 if (!pBraceNodeTmp
|| pBraceNodeTmp
->GetType() != SmNodeType::Brace
) {
1373 // Brace node is invalid.
1377 SmBraceNode
* pBraceNode
= static_cast<SmBraceNode
*>(pBraceNodeTmp
);
1378 SmMathSymbolNode
* pClosingNode
= pBraceNode
->ClosingBrace();
1379 if (!pClosingNode
) {
1380 // Couldn't get closing symbol node.
1384 // Check if the closing brace matches eBracketType.
1385 SmTokenType eClosingTokenType
= pClosingNode
->GetToken().eType
;
1386 switch (eBracketType
) {
1387 case SmBracketType::Round
: if (eClosingTokenType
!= TRPARENT
) { return false; } break;
1388 case SmBracketType::Square
: if (eClosingTokenType
!= TRBRACKET
) { return false; } break;
1389 case SmBracketType::Curly
: if (eClosingTokenType
!= TRBRACE
) { return false; } break;
1395 *ppBraceNode
= pBraceNode
;
1401 void SmCursor::MoveAfterBracket(SmBraceNode
* pBraceNode
)
1403 mpPosition
->CaretPos
.pSelectedNode
= pBraceNode
;
1404 mpPosition
->CaretPos
.nIndex
= 1;
1405 mpAnchor
->CaretPos
.pSelectedNode
= pBraceNode
;
1406 mpAnchor
->CaretPos
.nIndex
= 1;
1411 /////////////////////////////////////// SmNodeListParser
1413 SmNode
* SmNodeListParser::Parse(SmNodeList
* list
){
1415 //Delete error nodes
1416 SmNodeList::iterator it
= pList
->begin();
1417 while(it
!= pList
->end()) {
1418 if((*it
)->GetType() == SmNodeType::Error
){
1421 it
= pList
->erase(it
);
1425 SmNode
* retval
= Expression();
1430 SmNode
* SmNodeListParser::Expression(){
1431 SmNodeArray NodeArray
;
1432 //Accept as many relations as there is
1434 NodeArray
.push_back(Relation());
1436 //Create SmExpressionNode, I hope SmToken() will do :)
1437 SmStructureNode
* pExpr
= new SmExpressionNode(SmToken());
1438 pExpr
->SetSubNodes(std::move(NodeArray
));
1442 SmNode
* SmNodeListParser::Relation(){
1444 SmNode
* pLeft
= Sum();
1445 //While we have tokens and the next is a relation
1446 while(Terminal() && IsRelationOperator(Terminal()->GetToken())){
1448 SmNode
* pOper
= Take();
1449 //Find the right side of the relation
1450 SmNode
* pRight
= Sum();
1451 //Create new SmBinHorNode
1452 SmStructureNode
* pNewNode
= new SmBinHorNode(SmToken());
1453 pNewNode
->SetSubNodes(pLeft
, pOper
, pRight
);
1459 SmNode
* SmNodeListParser::Sum(){
1461 SmNode
* pLeft
= Product();
1462 //While we have tokens and the next is a sum
1463 while(Terminal() && IsSumOperator(Terminal()->GetToken())){
1465 SmNode
* pOper
= Take();
1466 //Find the right side of the sum
1467 SmNode
* pRight
= Product();
1468 //Create new SmBinHorNode
1469 SmStructureNode
* pNewNode
= new SmBinHorNode(SmToken());
1470 pNewNode
->SetSubNodes(pLeft
, pOper
, pRight
);
1476 SmNode
* SmNodeListParser::Product(){
1478 SmNode
* pLeft
= Factor();
1479 //While we have tokens and the next is a product
1480 while(Terminal() && IsProductOperator(Terminal()->GetToken())){
1482 SmNode
* pOper
= Take();
1483 //Find the right side of the operation
1484 SmNode
* pRight
= Factor();
1485 //Create new SmBinHorNode
1486 SmStructureNode
* pNewNode
= new SmBinHorNode(SmToken());
1487 pNewNode
->SetSubNodes(pLeft
, pOper
, pRight
);
1493 SmNode
* SmNodeListParser::Factor(){
1494 //Read unary operations
1497 //Take care of unary operators
1498 else if(IsUnaryOperator(Terminal()->GetToken()))
1500 SmStructureNode
*pUnary
= new SmUnHorNode(SmToken());
1501 SmNode
*pOper
= Terminal(),
1509 pUnary
->SetSubNodes(pOper
, pArg
);
1515 SmNode
* SmNodeListParser::Postfix(){
1518 SmNode
*pArg
= nullptr;
1519 if(IsPostfixOperator(Terminal()->GetToken()))
1521 else if(IsOperator(Terminal()->GetToken()))
1525 while(Terminal() && IsPostfixOperator(Terminal()->GetToken())) {
1526 SmStructureNode
*pUnary
= new SmUnHorNode(SmToken());
1527 SmNode
*pOper
= Take();
1528 pUnary
->SetSubNodes(pArg
, pOper
);
1534 SmNode
* SmNodeListParser::Error(){
1535 return new SmErrorNode(SmToken());
1538 bool SmNodeListParser::IsOperator(const SmToken
&token
) {
1539 return IsRelationOperator(token
) ||
1540 IsSumOperator(token
) ||
1541 IsProductOperator(token
) ||
1542 IsUnaryOperator(token
) ||
1543 IsPostfixOperator(token
);
1546 bool SmNodeListParser::IsRelationOperator(const SmToken
&token
) {
1547 return bool(token
.nGroup
& TG::Relation
);
1550 bool SmNodeListParser::IsSumOperator(const SmToken
&token
) {
1551 return bool(token
.nGroup
& TG::Sum
);
1554 bool SmNodeListParser::IsProductOperator(const SmToken
&token
) {
1555 return token
.nGroup
& TG::Product
&&
1556 token
.eType
!= TWIDESLASH
&&
1557 token
.eType
!= TWIDEBACKSLASH
&&
1558 token
.eType
!= TUNDERBRACE
&&
1559 token
.eType
!= TOVERBRACE
&&
1560 token
.eType
!= TOVER
;
1563 bool SmNodeListParser::IsUnaryOperator(const SmToken
&token
) {
1564 return token
.nGroup
& TG::UnOper
&&
1565 (token
.eType
== TPLUS
||
1566 token
.eType
== TMINUS
||
1567 token
.eType
== TPLUSMINUS
||
1568 token
.eType
== TMINUSPLUS
||
1569 token
.eType
== TNEG
||
1570 token
.eType
== TUOPER
);
1573 bool SmNodeListParser::IsPostfixOperator(const SmToken
&token
) {
1574 return token
.eType
== TFACT
;
1577 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */