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/.
10 #include <visitors.hxx>
11 #include <document.hxx>
13 #include <comphelper/string.hxx>
14 #include <comphelper/lok.hxx>
15 #include <editeng/editeng.hxx>
16 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
17 #include <osl/diagnose.h>
18 #include <sfx2/lokhelper.hxx>
20 void SmCursor::Move(OutputDevice
* pDev
, SmMovementDirection direction
, bool bMoveAnchor
){
21 SmCaretPosGraphEntry
* NewPos
= nullptr;
26 NewPos
= mpPosition
->Left
;
27 OSL_ENSURE(NewPos
, "NewPos shouldn't be NULL here!");
31 NewPos
= mpPosition
->Right
;
32 OSL_ENSURE(NewPos
, "NewPos shouldn't be NULL here!");
35 //Implementation is practically identical to MoveDown, except for a single if statement
36 //so I've implemented them together and added a direction == MoveDown to the if statements.
40 SmCaretLine from_line
= SmCaretPos2LineVisitor(pDev
, mpPosition
->CaretPos
).GetResult(),
41 best_line
, //Best approximated line found so far
42 curr_line
; //Current line
43 tools::Long dbp_sq
= 0; //Distance squared to best line
44 for(const auto &pEntry
: *mpGraph
)
46 //Reject it if it's the current position
47 if(pEntry
->CaretPos
== mpPosition
->CaretPos
) continue;
49 curr_line
= SmCaretPos2LineVisitor(pDev
, pEntry
->CaretPos
).GetResult();
50 //Reject anything above if we're moving down
51 if(curr_line
.GetTop() <= from_line
.GetTop() && direction
== MoveDown
) continue;
52 //Reject anything below if we're moving up
53 if(curr_line
.GetTop() + curr_line
.GetHeight() >= from_line
.GetTop() + from_line
.GetHeight()
54 && direction
== MoveUp
) continue;
55 //Compare if it to what we have, if we have anything yet
57 //Compute distance to current line squared, multiplied with a horizontal factor
58 tools::Long dp_sq
= curr_line
.SquaredDistanceX(from_line
) * HORIZONTICAL_DISTANCE_FACTOR
+
59 curr_line
.SquaredDistanceY(from_line
);
60 //Discard current line if best line is closer
61 if(dbp_sq
<= dp_sq
) continue;
63 //Take current line as the best
64 best_line
= curr_line
;
65 NewPos
= pEntry
.get();
66 //Update distance to best line
67 dbp_sq
= best_line
.SquaredDistanceX(from_line
) * HORIZONTICAL_DISTANCE_FACTOR
+
68 best_line
.SquaredDistanceY(from_line
);
83 void SmCursor::MoveTo(OutputDevice
* pDev
, const Point
& pos
, bool bMoveAnchor
)
85 SmCaretPosGraphEntry
* NewPos
= nullptr;
86 tools::Long dp_sq
= 0, //Distance to current line squared
87 dbp_sq
= 1; //Distance to best line squared
88 for(const auto &pEntry
: *mpGraph
)
90 OSL_ENSURE(pEntry
->CaretPos
.IsValid(), "The caret position graph may not have invalid positions!");
91 //Compute current line
92 SmCaretLine curr_line
= SmCaretPos2LineVisitor(pDev
, pEntry
->CaretPos
).GetResult();
93 //Compute squared distance to current line
94 dp_sq
= curr_line
.SquaredDistanceX(pos
) + curr_line
.SquaredDistanceY(pos
);
95 //If we have a position compare to it
97 //If best line is closer, reject current line
98 if(dbp_sq
<= dp_sq
) continue;
100 //Accept current position as the best
101 NewPos
= pEntry
.get();
102 //Update distance to best line
113 void SmCursor::BuildGraph(){
114 //Save the current anchor and position
115 SmCaretPos _anchor
, _position
;
116 //Release mpGraph if allocated
119 _anchor
= mpAnchor
->CaretPos
;
121 _position
= mpPosition
->CaretPos
;
123 //Reset anchor and position as they point into an old graph
125 mpPosition
= nullptr;
128 //Build the new graph
129 mpGraph
.reset(SmCaretPosGraphBuildingVisitor(mpTree
).takeGraph());
131 //Restore anchor and position pointers
132 if(_anchor
.IsValid() || _position
.IsValid()){
133 for(const auto &pEntry
: *mpGraph
)
135 if(_anchor
== pEntry
->CaretPos
)
136 mpAnchor
= pEntry
.get();
137 if(_position
== pEntry
->CaretPos
)
138 mpPosition
= pEntry
.get();
141 //Set position and anchor to first caret position
142 auto it
= mpGraph
->begin();
143 assert(it
!= mpGraph
->end());
145 mpPosition
= it
->get();
147 mpAnchor
= mpPosition
;
151 OSL_ENSURE(mpPosition
->CaretPos
.IsValid(), "Position must be valid");
152 OSL_ENSURE(mpAnchor
->CaretPos
.IsValid(), "Anchor must be valid");
155 bool SmCursor::SetCaretPosition(SmCaretPos pos
){
156 for(const auto &pEntry
: *mpGraph
)
158 if(pEntry
->CaretPos
== pos
)
160 mpPosition
= pEntry
.get();
161 mpAnchor
= pEntry
.get();
168 void SmCursor::AnnotateSelection() const {
169 //TODO: Manage a state, reset it upon modification and optimize this call
170 SmSetSelectionVisitor(mpAnchor
->CaretPos
, mpPosition
->CaretPos
, mpTree
);
173 void SmCursor::Draw(OutputDevice
& pDev
, Point Offset
, bool isCaretVisible
){
174 SmCaretDrawingVisitor(pDev
, GetPosition(), Offset
, isCaretVisible
);
177 tools::Rectangle
SmCursor::GetCaretRectangle(OutputDevice
& rOutDev
) const
179 return SmCaretRectanglesVisitor(rOutDev
, GetPosition()).getCaret();
182 tools::Rectangle
SmCursor::GetSelectionRectangle(OutputDevice
& rOutDev
) const
185 return SmSelectionRectanglesVisitor(rOutDev
, mpTree
).GetSelection();
188 void SmCursor::DeletePrev(OutputDevice
* pDev
){
189 //Delete only a selection if there's a selection
195 SmNode
* pLine
= FindTopMostNodeInLine(mpPosition
->CaretPos
.pSelectedNode
);
196 SmStructureNode
* pLineParent
= pLine
->GetParent();
197 int nLineOffsetIdx
= pLineParent
->IndexOfSubNode(pLine
);
198 assert(nLineOffsetIdx
>= 0);
200 //If we're in front of a node who's parent is a TABLE
201 if (pLineParent
->GetType() == SmNodeType::Table
&& mpPosition
->CaretPos
.nIndex
== 0 && nLineOffsetIdx
> 0)
203 size_t nLineOffset
= nLineOffsetIdx
;
204 //Now we can merge with nLineOffset - 1
206 //Line to merge things into, so we can delete pLine
207 SmNode
* pMergeLine
= pLineParent
->GetSubNode(nLineOffset
-1);
208 OSL_ENSURE(pMergeLine
, "pMergeLine cannot be NULL!");
209 SmCaretPos PosAfterDelete
;
210 //Convert first line to list
211 std::unique_ptr
<SmNodeList
> pLineList(new SmNodeList
);
212 NodeToList(pMergeLine
, *pLineList
);
213 if(!pLineList
->empty()){
214 //Find iterator to patch
215 SmNodeList::iterator patchPoint
= pLineList
->end();
217 //Convert second line to list
218 NodeToList(pLine
, *pLineList
);
219 //Patch the line list
221 PosAfterDelete
= PatchLineList(pLineList
.get(), patchPoint
);
223 pLine
= SmNodeListParser().Parse(pLineList
.get());
226 pLineParent
->SetSubNode(nLineOffset
-1, pLine
);
227 //Delete the removed line slot
228 SmNodeArray
lines(pLineParent
->GetNumSubNodes()-1);
229 for (size_t i
= 0; i
< pLineParent
->GetNumSubNodes(); ++i
)
232 lines
[i
] = pLineParent
->GetSubNode(i
);
233 else if(i
> nLineOffset
)
234 lines
[i
-1] = pLineParent
->GetSubNode(i
);
236 pLineParent
->SetSubNodes(std::move(lines
));
239 mpPosition
= nullptr;
243 if(!SetCaretPosition(PosAfterDelete
))
244 SetCaretPosition(SmCaretPos(pLine
, 0));
248 //TODO: If we're in an empty (sub/super/*) script
249 /*}else if(pLineParent->GetType() == SmNodeType::SubSup &&
251 pLine->GetType() == SmNodeType::Expression &&
252 pLine->GetNumSubNodes() == 0){
253 //There's a (sub/super) script we can delete
254 //Consider selecting the entire script if GetNumSubNodes() != 0 or pLine->GetType() != SmNodeType::Expression
255 //TODO: Handle case where we delete a limit
258 //Else move select, and delete if not complex
260 Move(pDev
, MoveLeft
, false);
261 if(!HasComplexSelection())
266 void SmCursor::Delete(){
267 //Return if we don't have a selection to delete
274 //Set selected on nodes
277 //Find an arbitrary selected node
278 SmNode
* pSNode
= FindSelectedNode(mpTree
);
281 //Find the topmost node of the line that holds the selection
282 SmNode
* pLine
= FindTopMostNodeInLine(pSNode
, true);
283 OSL_ENSURE(pLine
!= mpTree
, "Shouldn't be able to select the entire tree");
285 //Get the parent of the line
286 SmStructureNode
* pLineParent
= pLine
->GetParent();
287 //Find line offset in parent
288 int nLineOffset
= pLineParent
->IndexOfSubNode(pLine
);
289 assert(nLineOffset
>= 0);
291 //Position after delete
292 SmCaretPos PosAfterDelete
;
294 std::unique_ptr
<SmNodeList
> pLineList(new SmNodeList
);
295 NodeToList(pLine
, *pLineList
);
297 //Take the selected nodes and delete them...
298 SmNodeList::iterator patchIt
= TakeSelectedNodesFromList(pLineList
.get());
300 //Get the position to set after delete
301 PosAfterDelete
= PatchLineList(pLineList
.get(), patchIt
);
304 FinishEdit(std::move(pLineList
), pLineParent
, nLineOffset
, PosAfterDelete
);
307 void SmCursor::InsertNodes(std::unique_ptr
<SmNodeList
> pNewNodes
){
308 if(pNewNodes
->empty()){
315 //Get the current position
316 const SmCaretPos pos
= mpPosition
->CaretPos
;
318 //Find top most of line that holds position
319 SmNode
* pLine
= FindTopMostNodeInLine(pos
.pSelectedNode
);
320 const bool bSelectedIsTopMost
= pLine
== pos
.pSelectedNode
;
322 //Find line parent and line index in parent
323 SmStructureNode
* pLineParent
= pLine
->GetParent();
324 int nParentIndex
= pLineParent
->IndexOfSubNode(pLine
);
325 assert(nParentIndex
>= 0);
327 //Convert line to list
328 std::unique_ptr
<SmNodeList
> pLineList(new SmNodeList
);
329 NodeToList(pLine
, *pLineList
); // deletes pLine, potentially deleting pos.pSelectedNode
331 //Find iterator for place to insert nodes
332 SmNodeList::iterator it
= bSelectedIsTopMost
? pLineList
->begin()
333 : FindPositionInLineList(pLineList
.get(), pos
);
335 //Insert all new nodes
336 SmNodeList::iterator newIt
,
337 patchIt
= it
, // (pointless default value, fixes compiler warnings)
339 for(newIt
= pNewNodes
->begin(); newIt
!= pNewNodes
->end(); ++newIt
){
340 insIt
= pLineList
->insert(it
, *newIt
);
341 if(newIt
== pNewNodes
->begin())
344 //Patch the places we've changed stuff
345 PatchLineList(pLineList
.get(), patchIt
);
346 SmCaretPos PosAfterInsert
= PatchLineList(pLineList
.get(), it
);
347 //Release list, we've taken the nodes
351 FinishEdit(std::move(pLineList
), pLineParent
, nParentIndex
, PosAfterInsert
);
354 SmNodeList::iterator
SmCursor::FindPositionInLineList(SmNodeList
* pLineList
,
355 const SmCaretPos
& rCaretPos
)
357 //Find iterator for position
358 SmNodeList::iterator it
= std::find(pLineList
->begin(), pLineList
->end(), rCaretPos
.pSelectedNode
);
359 if (it
!= pLineList
->end())
361 if((*it
)->GetType() == SmNodeType::Text
)
363 //Split textnode if needed
364 if(rCaretPos
.nIndex
> 0)
366 SmTextNode
* pText
= static_cast<SmTextNode
*>(rCaretPos
.pSelectedNode
);
367 if (rCaretPos
.nIndex
== pText
->GetText().getLength())
369 OUString str1
= pText
->GetText().copy(0, rCaretPos
.nIndex
);
370 OUString str2
= pText
->GetText().copy(rCaretPos
.nIndex
);
371 pText
->ChangeText(str1
);
373 //Insert str2 as new text node
374 assert(!str2
.isEmpty());
375 SmTextNode
* pNewText
= new SmTextNode(pText
->GetToken(), pText
->GetFontDesc());
376 pNewText
->ChangeText(str2
);
377 it
= pLineList
->insert(it
, pNewText
);
381 //it now pointer to the node following pos, so pLineList->insert(it, ...) will insert correctly
384 //If we didn't find pSelectedNode, it must be because the caret is in front of the line
385 return pLineList
->begin();
388 SmCaretPos
SmCursor::PatchLineList(SmNodeList
* pLineList
, SmNodeList::iterator aIter
) {
389 //The nodes we should consider merging
390 SmNode
*prev
= nullptr,
392 if(aIter
!= pLineList
->end())
394 if(aIter
!= pLineList
->begin()) {
400 //Check if there's textnodes to merge
403 prev
->GetType() == SmNodeType::Text
&&
404 next
->GetType() == SmNodeType::Text
&&
405 ( prev
->GetToken().eType
!= TNUMBER
||
406 next
->GetToken().eType
== TNUMBER
) ){
407 SmTextNode
*pText
= static_cast<SmTextNode
*>(prev
),
408 *pOldN
= static_cast<SmTextNode
*>(next
);
409 SmCaretPos
retval(pText
, pText
->GetText().getLength());
410 OUString newText
= pText
->GetText() + pOldN
->GetText();
411 pText
->ChangeText(newText
);
413 pLineList
->erase(aIter
);
417 //Check if there's a SmPlaceNode to remove:
418 if(prev
&& next
&& prev
->GetType() == SmNodeType::Place
&& !SmNodeListParser::IsOperator(next
->GetToken())){
420 aIter
= pLineList
->erase(aIter
);
422 //Return caret pos in front of aIter
423 if(aIter
!= pLineList
->begin())
424 --aIter
; //Thus find node before aIter
425 if(aIter
== pLineList
->begin())
427 return SmCaretPos::GetPosAfter(*aIter
);
429 if(prev
&& next
&& next
->GetType() == SmNodeType::Place
&& !SmNodeListParser::IsOperator(prev
->GetToken())){
430 aIter
= pLineList
->erase(aIter
);
432 return SmCaretPos::GetPosAfter(prev
);
435 //If we didn't do anything return
436 if(!prev
) //return an invalid to indicate we're in front of line
438 return SmCaretPos::GetPosAfter(prev
);
441 SmNodeList::iterator
SmCursor::TakeSelectedNodesFromList(SmNodeList
*pLineList
,
442 SmNodeList
*pSelectedNodes
) {
443 SmNodeList::iterator retval
;
444 SmNodeList::iterator it
= pLineList
->begin();
445 while(it
!= pLineList
->end()){
446 if((*it
)->IsSelected()){
448 if((*it
)->GetType() == SmNodeType::Text
) {
449 SmTextNode
* pText
= static_cast<SmTextNode
*>(*it
);
450 OUString aText
= pText
->GetText();
451 //Start and lengths of the segments, 2 is the selected segment
452 int start2
= pText
->GetSelectionStart(),
453 start3
= pText
->GetSelectionEnd(),
455 len2
= start3
- start2
,
456 len3
= aText
.getLength() - start3
;
457 SmToken aToken
= pText
->GetToken();
458 sal_uInt16 eFontDesc
= pText
->GetFontDesc();
459 //If we need make segment 1
461 OUString str
= aText
.copy(0, len1
);
462 pText
->ChangeText(str
);
464 } else {//Remove it if not needed
465 it
= pLineList
->erase(it
);
468 //Set retval to be right after the selection
470 //if we need make segment 3
472 OUString str
= aText
.copy(start3
, len3
);
473 SmTextNode
* pSeg3
= new SmTextNode(aToken
, eFontDesc
);
474 pSeg3
->ChangeText(str
);
475 retval
= pLineList
->insert(it
, pSeg3
);
477 //If we need to save the selected text
478 if(pSelectedNodes
&& len2
> 0) {
479 OUString str
= aText
.copy(start2
, len2
);
480 SmTextNode
* pSeg2
= new SmTextNode(aToken
, eFontDesc
);
481 pSeg2
->ChangeText(str
);
482 pSelectedNodes
->push_back(pSeg2
);
484 } else { //if it's not textnode
486 retval
= it
= pLineList
->erase(it
);
488 pSelectedNodes
->push_back(pNode
);
498 void SmCursor::InsertSubSup(SmSubSup eSubSup
) {
504 SmNode
*pSNode
= FindSelectedNode(mpTree
);
506 pLine
= FindTopMostNodeInLine(pSNode
, true);
508 pLine
= FindTopMostNodeInLine(mpPosition
->CaretPos
.pSelectedNode
);
510 //Find Parent and offset in parent
511 SmStructureNode
*pLineParent
= pLine
->GetParent();
512 int nParentIndex
= pLineParent
->IndexOfSubNode(pLine
);
513 assert(nParentIndex
>= 0);
515 //TODO: Consider handling special cases where parent is an SmOperNode,
516 // Maybe this method should be able to add limits to an SmOperNode...
518 //We begin modifying the tree here
521 //Convert line to list
522 std::unique_ptr
<SmNodeList
> pLineList(new SmNodeList
);
523 NodeToList(pLine
, *pLineList
);
525 //Take the selection, and/or find iterator for current position
526 std::unique_ptr
<SmNodeList
> pSelectedNodesList(new SmNodeList
);
527 SmNodeList::iterator it
;
529 it
= TakeSelectedNodesFromList(pLineList
.get(), pSelectedNodesList
.get());
531 it
= FindPositionInLineList(pLineList
.get(), mpPosition
->CaretPos
);
533 //Find node that this should be applied to
535 bool bPatchLine
= !pSelectedNodesList
->empty(); //If the line should be patched later
536 if(it
!= pLineList
->begin()) {
541 //Create a new place node
542 pSubject
= new SmPlaceNode();
543 pSubject
->Prepare(mpDocShell
->GetFormat(), *mpDocShell
, 0);
544 it
= pLineList
->insert(it
, pSubject
);
546 bPatchLine
= true; //We've modified the line it should be patched later.
549 //Wrap the subject in a SmSubSupNode
550 SmSubSupNode
* pSubSup
;
551 if(pSubject
->GetType() != SmNodeType::SubSup
){
553 token
.nGroup
= TG::Power
;
554 pSubSup
= new SmSubSupNode(token
);
555 pSubSup
->SetBody(pSubject
);
559 pSubSup
= static_cast<SmSubSupNode
*>(pSubject
);
560 //pSubject shouldn't be referenced anymore, pSubSup is the SmSubSupNode in pLineList we wish to edit.
561 //and it pointer to the element following pSubSup in pLineList.
564 //Patch the line if we noted that was needed previously
566 PatchLineList(pLineList
.get(), it
);
568 //Convert existing, if any, sub-/superscript line to list
569 SmNode
*pScriptLine
= pSubSup
->GetSubSup(eSubSup
);
570 std::unique_ptr
<SmNodeList
> pScriptLineList(new SmNodeList
);
571 NodeToList(pScriptLine
, *pScriptLineList
);
573 //Add selection to pScriptLineList
574 unsigned int nOldSize
= pScriptLineList
->size();
575 pScriptLineList
->insert(pScriptLineList
->end(), pSelectedNodesList
->begin(), pSelectedNodesList
->end());
576 pSelectedNodesList
.reset();
578 //Patch pScriptLineList if needed
579 if(0 < nOldSize
&& nOldSize
< pScriptLineList
->size()) {
580 SmNodeList::iterator iPatchPoint
= pScriptLineList
->begin();
581 std::advance(iPatchPoint
, nOldSize
);
582 PatchLineList(pScriptLineList
.get(), iPatchPoint
);
585 //Find caret pos, that should be used after sub-/superscription.
586 SmCaretPos PosAfterScript
; //Leave invalid for first position
587 if (!pScriptLineList
->empty())
588 PosAfterScript
= SmCaretPos::GetPosAfter(pScriptLineList
->back());
590 //Parse pScriptLineList
591 pScriptLine
= SmNodeListParser().Parse(pScriptLineList
.get());
592 pScriptLineList
.reset();
594 //Insert pScriptLine back into the tree
595 pSubSup
->SetSubSup(eSubSup
, pScriptLine
);
598 FinishEdit(std::move(pLineList
), pLineParent
, nParentIndex
, PosAfterScript
, pScriptLine
);
601 void SmCursor::InsertBrackets(SmBracketType eBracketType
) {
609 SmNode
*pSNode
= FindSelectedNode(mpTree
);
611 pLine
= FindTopMostNodeInLine(pSNode
, true);
613 pLine
= FindTopMostNodeInLine(mpPosition
->CaretPos
.pSelectedNode
);
615 //Find parent and offset in parent
616 SmStructureNode
*pLineParent
= pLine
->GetParent();
617 int nParentIndex
= pLineParent
->IndexOfSubNode(pLine
);
618 assert(nParentIndex
>= 0);
620 //Convert line to list
621 std::unique_ptr
<SmNodeList
> pLineList(new SmNodeList
);
622 NodeToList(pLine
, *pLineList
);
624 //Take the selection, and/or find iterator for current position
625 std::unique_ptr
<SmNodeList
> pSelectedNodesList(new SmNodeList
);
626 SmNodeList::iterator it
;
628 it
= TakeSelectedNodesFromList(pLineList
.get(), pSelectedNodesList
.get());
630 it
= FindPositionInLineList(pLineList
.get(), mpPosition
->CaretPos
);
632 //If there's no selected nodes, create a place node
633 std::unique_ptr
<SmNode
> pBodyNode
;
634 SmCaretPos PosAfterInsert
;
635 if(pSelectedNodesList
->empty()) {
636 pBodyNode
.reset(new SmPlaceNode());
637 PosAfterInsert
= SmCaretPos(pBodyNode
.get(), 1);
639 pBodyNode
.reset(SmNodeListParser().Parse(pSelectedNodesList
.get()));
641 pSelectedNodesList
.reset();
644 SmToken
aTok(TLEFT
, '\0', "left", TG::NONE
, 5);
645 SmBraceNode
*pBrace
= new SmBraceNode(aTok
);
646 pBrace
->SetScaleMode(SmScaleMode::Height
);
647 std::unique_ptr
<SmNode
> pLeft( CreateBracket(eBracketType
, true) ),
648 pRight( CreateBracket(eBracketType
, false) );
649 std::unique_ptr
<SmBracebodyNode
> pBody(new SmBracebodyNode(SmToken()));
650 pBody
->SetSubNodes(std::move(pBodyNode
), nullptr);
651 pBrace
->SetSubNodes(std::move(pLeft
), std::move(pBody
), std::move(pRight
));
652 pBrace
->Prepare(mpDocShell
->GetFormat(), *mpDocShell
, 0);
655 pLineList
->insert(it
, pBrace
);
656 //Patch line (I think this is good enough)
657 SmCaretPos aAfter
= PatchLineList(pLineList
.get(), it
);
658 if( !PosAfterInsert
.IsValid() )
659 PosAfterInsert
= aAfter
;
662 FinishEdit(std::move(pLineList
), pLineParent
, nParentIndex
, PosAfterInsert
);
665 SmNode
*SmCursor::CreateBracket(SmBracketType eBracketType
, bool bIsLeft
) {
668 switch(eBracketType
){
669 case SmBracketType::Round
:
670 aTok
= SmToken(TLPARENT
, MS_LPARENT
, "(", TG::LBrace
, 5);
672 case SmBracketType::Square
:
673 aTok
= SmToken(TLBRACKET
, MS_LBRACKET
, "[", TG::LBrace
, 5);
675 case SmBracketType::Curly
:
676 aTok
= SmToken(TLBRACE
, MS_LBRACE
, "lbrace", TG::LBrace
, 5);
680 switch(eBracketType
) {
681 case SmBracketType::Round
:
682 aTok
= SmToken(TRPARENT
, MS_RPARENT
, ")", TG::RBrace
, 5);
684 case SmBracketType::Square
:
685 aTok
= SmToken(TRBRACKET
, MS_RBRACKET
, "]", TG::RBrace
, 5);
687 case SmBracketType::Curly
:
688 aTok
= SmToken(TRBRACE
, MS_RBRACE
, "rbrace", TG::RBrace
, 5);
692 SmNode
* pRetVal
= new SmMathSymbolNode(aTok
);
693 pRetVal
->SetScaleMode(SmScaleMode::Height
);
697 bool SmCursor::InsertRow() {
703 SmNode
*pSNode
= FindSelectedNode(mpTree
);
705 pLine
= FindTopMostNodeInLine(pSNode
, true);
707 pLine
= FindTopMostNodeInLine(mpPosition
->CaretPos
.pSelectedNode
);
709 //Find parent and offset in parent
710 SmStructureNode
*pLineParent
= pLine
->GetParent();
711 int nParentIndex
= pLineParent
->IndexOfSubNode(pLine
);
712 assert(nParentIndex
>= 0);
714 //Discover the context of this command
715 SmTableNode
*pTable
= nullptr;
716 SmMatrixNode
*pMatrix
= nullptr;
717 int nTableIndex
= nParentIndex
;
718 if(pLineParent
->GetType() == SmNodeType::Table
)
719 pTable
= static_cast<SmTableNode
*>(pLineParent
);
720 //If it's wrapped in a SmLineNode, we can still insert a newline
721 else if(pLineParent
->GetType() == SmNodeType::Line
&&
722 pLineParent
->GetParent() &&
723 pLineParent
->GetParent()->GetType() == SmNodeType::Table
) {
724 //NOTE: This hack might give problems if we stop ignoring SmAlignNode
725 pTable
= static_cast<SmTableNode
*>(pLineParent
->GetParent());
726 nTableIndex
= pTable
->IndexOfSubNode(pLineParent
);
727 assert(nTableIndex
>= 0);
729 if(pLineParent
->GetType() == SmNodeType::Matrix
)
730 pMatrix
= static_cast<SmMatrixNode
*>(pLineParent
);
732 //If we're not in a context that supports InsertRow, return sal_False
733 if(!pTable
&& !pMatrix
)
736 //Now we start editing
739 //Convert line to list
740 std::unique_ptr
<SmNodeList
> pLineList(new SmNodeList
);
741 NodeToList(pLine
, *pLineList
);
743 //Find position in line
744 SmNodeList::iterator it
;
746 //Take the selected nodes and delete them...
747 it
= TakeSelectedNodesFromList(pLineList
.get());
749 it
= FindPositionInLineList(pLineList
.get(), mpPosition
->CaretPos
);
751 //New caret position after inserting the newline/row in whatever context
752 SmCaretPos PosAfterInsert
;
754 //If we're in the context of a table
756 std::unique_ptr
<SmNodeList
> pNewLineList(new SmNodeList
);
757 //Move elements from pLineList to pNewLineList
758 SmNodeList
& rLineList
= *pLineList
;
759 pNewLineList
->splice(pNewLineList
->begin(), rLineList
, it
, rLineList
.end());
760 //Make sure it is valid again
761 it
= pLineList
->end();
762 if(it
!= pLineList
->begin())
764 if(pNewLineList
->empty())
765 pNewLineList
->push_front(new SmPlaceNode());
767 std::unique_ptr
<SmNode
> pNewLine(SmNodeListParser().Parse(pNewLineList
.get()));
768 pNewLineList
.reset();
769 //Wrap pNewLine in SmLineNode if needed
770 if(pLineParent
->GetType() == SmNodeType::Line
) {
771 std::unique_ptr
<SmLineNode
> pNewLineNode(new SmLineNode(SmToken(TNEWLINE
, '\0', "newline")));
772 pNewLineNode
->SetSubNodes(std::move(pNewLine
), nullptr);
773 pNewLine
= std::move(pNewLineNode
);
776 PosAfterInsert
= SmCaretPos(pNewLine
.get(), 0);
777 //Move other nodes if needed
778 for( int i
= pTable
->GetNumSubNodes(); i
> nTableIndex
+ 1; i
--)
779 pTable
->SetSubNode(i
, pTable
->GetSubNode(i
-1));
782 pTable
->SetSubNode(nTableIndex
+ 1, pNewLine
.release());
784 //Check if we need to change token type:
785 if(pTable
->GetNumSubNodes() > 2 && pTable
->GetToken().eType
== TBINOM
) {
786 SmToken tok
= pTable
->GetToken();
788 pTable
->SetToken(tok
);
791 //If we're in the context of a matrix
793 //Find position after insert and patch the list
794 PosAfterInsert
= PatchLineList(pLineList
.get(), it
);
795 //Move other children
796 sal_uInt16 rows
= pMatrix
->GetNumRows();
797 sal_uInt16 cols
= pMatrix
->GetNumCols();
798 int nRowStart
= (nParentIndex
- nParentIndex
% cols
) + cols
;
799 for( int i
= pMatrix
->GetNumSubNodes() + cols
- 1; i
>= nRowStart
+ cols
; i
--)
800 pMatrix
->SetSubNode(i
, pMatrix
->GetSubNode(i
- cols
));
801 for( int i
= nRowStart
; i
< nRowStart
+ cols
; i
++) {
802 SmPlaceNode
*pNewLine
= new SmPlaceNode();
803 if(i
== nParentIndex
+ cols
)
804 PosAfterInsert
= SmCaretPos(pNewLine
, 0);
805 pMatrix
->SetSubNode(i
, pNewLine
);
807 pMatrix
->SetRowCol(rows
+ 1, cols
);
811 FinishEdit(std::move(pLineList
), pLineParent
, nParentIndex
, PosAfterInsert
);
812 //FinishEdit is actually used to handle situations where parent is an instance of
813 //SmSubSupNode. In this case parent should always be a table or matrix, however, for
814 //code reuse we just use FinishEdit() here too.
818 void SmCursor::InsertFraction() {
824 SmNode
*pSNode
= FindSelectedNode(mpTree
);
826 pLine
= FindTopMostNodeInLine(pSNode
, true);
828 pLine
= FindTopMostNodeInLine(mpPosition
->CaretPos
.pSelectedNode
);
830 //Find Parent and offset in parent
831 SmStructureNode
*pLineParent
= pLine
->GetParent();
832 int nParentIndex
= pLineParent
->IndexOfSubNode(pLine
);
833 assert(nParentIndex
>= 0);
835 //We begin modifying the tree here
838 //Convert line to list
839 std::unique_ptr
<SmNodeList
> pLineList(new SmNodeList
);
840 NodeToList(pLine
, *pLineList
);
842 //Take the selection, and/or find iterator for current position
843 std::unique_ptr
<SmNodeList
> pSelectedNodesList(new SmNodeList
);
844 SmNodeList::iterator it
;
846 it
= TakeSelectedNodesFromList(pLineList
.get(), pSelectedNodesList
.get());
848 it
= FindPositionInLineList(pLineList
.get(), mpPosition
->CaretPos
);
850 //Create pNum, and pDenom
851 bool bEmptyFraction
= pSelectedNodesList
->empty();
852 std::unique_ptr
<SmNode
> pNum( bEmptyFraction
854 : SmNodeListParser().Parse(pSelectedNodesList
.get()) );
855 std::unique_ptr
<SmNode
> pDenom(new SmPlaceNode());
856 pSelectedNodesList
.reset();
858 //Create new fraction
859 SmBinVerNode
*pFrac
= new SmBinVerNode(SmToken(TOVER
, '\0', "over", TG::Product
, 0));
860 std::unique_ptr
<SmNode
> pRect(new SmRectangleNode(SmToken()));
861 pFrac
->SetSubNodes(std::move(pNum
), std::move(pRect
), std::move(pDenom
));
863 //Insert in pLineList
864 SmNodeList::iterator patchIt
= pLineList
->insert(it
, pFrac
);
865 PatchLineList(pLineList
.get(), patchIt
);
866 PatchLineList(pLineList
.get(), it
);
869 SmNode
*pSelectedNode
= bEmptyFraction
? pFrac
->GetSubNode(0) : pFrac
->GetSubNode(2);
870 FinishEdit(std::move(pLineList
), pLineParent
, nParentIndex
, SmCaretPos(pSelectedNode
, 1));
873 void SmCursor::InsertText(const OUString
& aString
)
880 token
.eType
= TIDENT
;
881 token
.cMathChar
= u
"";
882 token
.nGroup
= TG::NONE
;
884 token
.aText
= aString
;
886 SmTextNode
* pText
= new SmTextNode(token
, FNT_VARIABLE
);
887 pText
->SetText(aString
);
888 pText
->AdjustFontDesc();
889 pText
->Prepare(mpDocShell
->GetFormat(), *mpDocShell
, 0);
891 std::unique_ptr
<SmNodeList
> pList(new SmNodeList
);
892 pList
->push_front(pText
);
893 InsertNodes(std::move(pList
));
898 void SmCursor::InsertElement(SmFormulaElement element
){
904 SmNode
* pNewNode
= nullptr;
909 token
.eType
= TBLANK
;
910 token
.nGroup
= TG::Blank
;
912 SmBlankNode
* pBlankNode
= new SmBlankNode(token
);
913 pBlankNode
->IncreaseBy(token
);
914 pNewNode
= pBlankNode
;
916 case FactorialElement
:
918 SmToken
token(TFACT
, MS_FACT
, "fact", TG::UnOper
, 5);
919 pNewNode
= new SmMathSymbolNode(token
);
925 token
.setChar(MS_PLUS
);
926 token
.nGroup
= TG::UnOper
| TG::Sum
;
929 pNewNode
= new SmMathSymbolNode(token
);
934 token
.eType
= TMINUS
;
935 token
.setChar(MS_MINUS
);
936 token
.nGroup
= TG::UnOper
| TG::Sum
;
939 pNewNode
= new SmMathSymbolNode(token
);
945 token
.setChar(MS_CDOT
);
946 token
.nGroup
= TG::Product
;
947 token
.aText
= "cdot";
948 pNewNode
= new SmMathSymbolNode(token
);
953 token
.eType
= TASSIGN
;
954 token
.setChar(MS_ASSIGN
);
955 token
.nGroup
= TG::Relation
;
957 pNewNode
= new SmMathSymbolNode(token
);
959 case LessThanElement
:
963 token
.setChar(MS_LT
);
964 token
.nGroup
= TG::Relation
;
966 pNewNode
= new SmMathSymbolNode(token
);
968 case GreaterThanElement
:
972 token
.setChar(MS_GT
);
973 token
.nGroup
= TG::Relation
;
975 pNewNode
= new SmMathSymbolNode(token
);
981 token
.setChar(MS_PERCENT
);
982 token
.nGroup
= TG::NONE
;
983 token
.aText
= "\"%\"";
984 pNewNode
= new SmMathSymbolNode(token
);
989 //Prepare the new node
990 pNewNode
->Prepare(mpDocShell
->GetFormat(), *mpDocShell
, 0);
993 std::unique_ptr
<SmNodeList
> pList(new SmNodeList
);
994 pList
->push_front(pNewNode
);
995 InsertNodes(std::move(pList
));
1000 void SmCursor::InsertSpecial(std::u16string_view _aString
)
1005 OUString
aString( comphelper::string::strip(_aString
, ' ') );
1007 //Create instance of special node
1009 token
.eType
= TSPECIAL
;
1010 token
.cMathChar
= u
"";
1011 token
.nGroup
= TG::NONE
;
1013 token
.aText
= aString
;
1014 SmSpecialNode
* pSpecial
= new SmSpecialNode(token
);
1016 //Prepare the special node
1017 pSpecial
->Prepare(mpDocShell
->GetFormat(), *mpDocShell
, 0);
1020 std::unique_ptr
<SmNodeList
> pList(new SmNodeList
);
1021 pList
->push_front(pSpecial
);
1022 InsertNodes(std::move(pList
));
1027 void SmCursor::InsertCommandText(const OUString
& aCommandText
) {
1028 //Parse the sub expression
1029 auto xSubExpr
= mpDocShell
->GetParser()->ParseExpression(aCommandText
);
1031 //Prepare the subtree
1032 xSubExpr
->Prepare(mpDocShell
->GetFormat(), *mpDocShell
, 0);
1034 //Convert subtree to list
1035 SmNode
* pSubExpr
= xSubExpr
.release();
1036 std::unique_ptr
<SmNodeList
> pLineList(new SmNodeList
);
1037 NodeToList(pSubExpr
, *pLineList
);
1041 //Delete any selection
1045 InsertNodes(std::move(pLineList
));
1050 void SmCursor::Copy(){
1054 AnnotateSelection();
1055 //Find selected node
1056 SmNode
* pSNode
= FindSelectedNode(mpTree
);
1059 SmNode
* pLine
= FindTopMostNodeInLine(pSNode
, true);
1062 //Clone selected nodes
1063 SmClipboard aClipboard
;
1064 if(IsLineCompositionNode(pLine
))
1065 CloneLineToClipboard(static_cast<SmStructureNode
*>(pLine
), &aClipboard
);
1067 //Special care to only clone selected text
1068 if(pLine
->GetType() == SmNodeType::Text
) {
1069 SmTextNode
*pText
= static_cast<SmTextNode
*>(pLine
);
1070 std::unique_ptr
<SmTextNode
> pClone(new SmTextNode( pText
->GetToken(), pText
->GetFontDesc() ));
1071 int start
= pText
->GetSelectionStart(),
1072 length
= pText
->GetSelectionEnd() - pText
->GetSelectionStart();
1073 pClone
->ChangeText(pText
->GetText().copy(start
, length
));
1074 pClone
->SetScaleMode(pText
->GetScaleMode());
1075 aClipboard
.push_front(std::move(pClone
));
1077 SmCloningVisitor aCloneFactory
;
1078 aClipboard
.push_front(std::unique_ptr
<SmNode
>(aCloneFactory
.Clone(pLine
)));
1083 if (!aClipboard
.empty())
1084 maClipboard
= std::move(aClipboard
);
1087 void SmCursor::Paste() {
1091 if (!maClipboard
.empty())
1092 InsertNodes(CloneList(maClipboard
));
1097 std::unique_ptr
<SmNodeList
> SmCursor::CloneList(SmClipboard
&rClipboard
){
1098 SmCloningVisitor aCloneFactory
;
1099 std::unique_ptr
<SmNodeList
> pClones(new SmNodeList
);
1101 for(auto &xNode
: rClipboard
){
1102 SmNode
*pClone
= aCloneFactory
.Clone(xNode
.get());
1103 pClones
->push_back(pClone
);
1109 SmNode
* SmCursor::FindTopMostNodeInLine(SmNode
* pSNode
, bool MoveUpIfSelected
){
1111 //Move up parent until we find a node who's
1112 //parent is NULL or isn't selected and not a type of:
1119 while(pSNode
->GetParent() &&
1120 ((MoveUpIfSelected
&&
1121 pSNode
->GetParent()->IsSelected()) ||
1122 IsLineCompositionNode(pSNode
->GetParent())))
1123 pSNode
= pSNode
->GetParent();
1124 //Now we have the selection line node
1128 SmNode
* SmCursor::FindSelectedNode(SmNode
* pNode
){
1129 if(pNode
->GetNumSubNodes() == 0)
1131 for(auto pChild
: *static_cast<SmStructureNode
*>(pNode
))
1135 if(pChild
->IsSelected())
1137 SmNode
* pRetVal
= FindSelectedNode(pChild
);
1144 void SmCursor::LineToList(SmStructureNode
* pLine
, SmNodeList
& list
){
1145 for(auto pChild
: *pLine
)
1149 switch(pChild
->GetType()){
1150 case SmNodeType::Line
:
1151 case SmNodeType::UnHor
:
1152 case SmNodeType::Expression
:
1153 case SmNodeType::BinHor
:
1154 case SmNodeType::Align
:
1155 case SmNodeType::Font
:
1156 LineToList(static_cast<SmStructureNode
*>(pChild
), list
);
1158 case SmNodeType::Error
:
1162 list
.push_back(pChild
);
1165 pLine
->ClearSubNodes();
1169 void SmCursor::CloneLineToClipboard(SmStructureNode
* pLine
, SmClipboard
* pClipboard
){
1170 SmCloningVisitor aCloneFactory
;
1171 for(auto pChild
: *pLine
)
1175 if( IsLineCompositionNode( pChild
) )
1176 CloneLineToClipboard( static_cast<SmStructureNode
*>(pChild
), pClipboard
);
1177 else if( pChild
->IsSelected() && pChild
->GetType() != SmNodeType::Error
) {
1178 //Only clone selected text from SmTextNode
1179 if(pChild
->GetType() == SmNodeType::Text
) {
1180 SmTextNode
*pText
= static_cast<SmTextNode
*>(pChild
);
1181 std::unique_ptr
<SmTextNode
> pClone(new SmTextNode( pChild
->GetToken(), pText
->GetFontDesc() ));
1182 int start
= pText
->GetSelectionStart(),
1183 length
= pText
->GetSelectionEnd() - pText
->GetSelectionStart();
1184 pClone
->ChangeText(pText
->GetText().copy(start
, length
));
1185 pClone
->SetScaleMode(pText
->GetScaleMode());
1186 pClipboard
->push_back(std::move(pClone
));
1188 pClipboard
->push_back(std::unique_ptr
<SmNode
>(aCloneFactory
.Clone(pChild
)));
1193 bool SmCursor::IsLineCompositionNode(SmNode
const * pNode
){
1194 switch(pNode
->GetType()){
1195 case SmNodeType::Line
:
1196 case SmNodeType::UnHor
:
1197 case SmNodeType::Expression
:
1198 case SmNodeType::BinHor
:
1199 case SmNodeType::Align
:
1200 case SmNodeType::Font
:
1207 int SmCursor::CountSelectedNodes(SmNode
* pNode
){
1208 if(pNode
->GetNumSubNodes() == 0)
1211 for(auto pChild
: *static_cast<SmStructureNode
*>(pNode
))
1215 if(pChild
->IsSelected() && !IsLineCompositionNode(pChild
))
1217 nCount
+= CountSelectedNodes(pChild
);
1222 bool SmCursor::HasComplexSelection(){
1225 AnnotateSelection();
1227 return CountSelectedNodes(mpTree
) > 1;
1230 void SmCursor::FinishEdit(std::unique_ptr
<SmNodeList
> pLineList
,
1231 SmStructureNode
* pParent
,
1233 SmCaretPos PosAfterEdit
,
1234 SmNode
* pStartLine
) {
1235 //Store number of nodes in line for later
1236 int entries
= pLineList
->size();
1238 //Parse list of nodes to a tree
1239 SmNodeListParser parser
;
1240 std::unique_ptr
<SmNode
> pLine(parser
.Parse(pLineList
.get()));
1243 //Check if we're making the body of a subsup node bigger than one
1244 if(pParent
->GetType() == SmNodeType::SubSup
&&
1245 nParentIndex
== 0 &&
1247 //Wrap pLine in scalable round brackets
1248 SmToken
aTok(TLEFT
, '\0', "left", TG::NONE
, 5);
1249 std::unique_ptr
<SmBraceNode
> pBrace(new SmBraceNode(aTok
));
1250 pBrace
->SetScaleMode(SmScaleMode::Height
);
1251 std::unique_ptr
<SmNode
> pLeft( CreateBracket(SmBracketType::Round
, true) ),
1252 pRight( CreateBracket(SmBracketType::Round
, false) );
1253 std::unique_ptr
<SmBracebodyNode
> pBody(new SmBracebodyNode(SmToken()));
1254 pBody
->SetSubNodes(std::move(pLine
), nullptr);
1255 pBrace
->SetSubNodes(std::move(pLeft
), std::move(pBody
), std::move(pRight
));
1256 pBrace
->Prepare(mpDocShell
->GetFormat(), *mpDocShell
, 0);
1257 pLine
= std::move(pBrace
);
1258 //TODO: Consider the following alternative behavior:
1259 //Consider the line: A + {B + C}^D lsub E
1260 //Here pLineList is B, + and C and pParent is a subsup node with
1261 //both RSUP and LSUB set. Imagine the user just inserted "B +" in
1262 //the body of the subsup node...
1263 //The most natural thing to do would be to make the line like this:
1264 //A + B lsub E + C ^ D
1265 //E.g. apply LSUB and LSUP to the first element in pLineList and RSUP
1266 //and RSUB to the last element in pLineList. But how should this act
1267 //for CSUP and CSUB ???
1268 //For this reason and because brackets was faster to implement, this solution
1269 //have been chosen. It might be worth working on the other solution later...
1272 //Set pStartLine if NULL
1274 pStartLine
= pLine
.get();
1276 //Insert it back into the parent
1277 pParent
->SetSubNode(nParentIndex
, pLine
.release());
1279 //Rebuild graph of caret position
1281 mpPosition
= nullptr;
1283 AnnotateSelection(); //Update selection annotation!
1285 //Set caret position
1286 if(!SetCaretPosition(PosAfterEdit
))
1287 SetCaretPosition(SmCaretPos(pStartLine
, 0));
1293 void SmCursor::BeginEdit(){
1294 if(mnEditSections
++ > 0) return;
1296 mbIsEnabledSetModifiedSmDocShell
= mpDocShell
->IsEnableSetModified();
1297 if( mbIsEnabledSetModifiedSmDocShell
)
1298 mpDocShell
->EnableSetModified( false );
1301 void SmCursor::EndEdit(){
1302 if(--mnEditSections
> 0) return;
1304 mpDocShell
->SetFormulaArranged(false);
1305 //Okay, I don't know what this does... :)
1306 //It's used in SmDocShell::SetText and with places where everything is modified.
1307 //I think it does some magic, with sfx, but everything is totally undocumented so
1308 //it's kinda hard to tell...
1309 if ( mbIsEnabledSetModifiedSmDocShell
)
1310 mpDocShell
->EnableSetModified( mbIsEnabledSetModifiedSmDocShell
);
1311 //I think this notifies people around us that we've modified this document...
1312 mpDocShell
->SetModified();
1313 //I think SmDocShell uses this value when it sends an update graphics event
1314 //Anyway comments elsewhere suggests it needs to be updated...
1315 mpDocShell
->mnModifyCount
++;
1317 //TODO: Consider copying the update accessibility code from SmDocShell::SetText in here...
1318 //This somehow updates the size of SmGraphicView if it is running in embedded mode
1319 if( mpDocShell
->GetCreateMode() == SfxObjectCreateMode::EMBEDDED
)
1320 mpDocShell
->OnDocumentPrinterChanged(nullptr);
1322 //Request a repaint...
1325 //Update the edit engine and text of the document
1327 SmNodeToTextVisitor(mpTree
, formula
);
1328 mpDocShell
->maText
= formula
;
1329 mpDocShell
->GetEditEngine().QuickInsertText( formula
, ESelection( 0, 0, EE_PARA_ALL
, EE_TEXTPOS_ALL
) );
1330 mpDocShell
->GetEditEngine().QuickFormatDoc();
1333 void SmCursor::RequestRepaint()
1335 if (SmViewShell
*pViewSh
= SmGetActiveView())
1337 if (comphelper::LibreOfficeKit::isActive())
1339 pViewSh
->SendCaretToLOK();
1341 else if ( SfxObjectCreateMode::EMBEDDED
== mpDocShell
->GetCreateMode() )
1342 mpDocShell
->Repaint();
1344 pViewSh
->GetGraphicWidget().Invalidate();
1348 bool SmCursor::IsAtTailOfBracket(SmBracketType eBracketType
) const
1350 const SmCaretPos pos
= GetPosition();
1351 if (!pos
.IsValid()) {
1355 SmNode
* pNode
= pos
.pSelectedNode
;
1357 if (pNode
->GetType() == SmNodeType::Text
) {
1358 SmTextNode
* pTextNode
= static_cast<SmTextNode
*>(pNode
);
1359 if (pos
.nIndex
< pTextNode
->GetText().getLength()) {
1360 // The cursor is on a text node and at the middle of it.
1364 if (pos
.nIndex
< 1) {
1370 SmStructureNode
* pParentNode
= pNode
->GetParent();
1372 // There's no brace body node in the ancestors.
1376 int index
= pParentNode
->IndexOfSubNode(pNode
);
1378 if (static_cast<size_t>(index
+ 1) != pParentNode
->GetNumSubNodes()) {
1379 // The cursor is not at the tail at one of ancestor nodes.
1383 pNode
= pParentNode
;
1384 if (pNode
->GetType() == SmNodeType::Bracebody
) {
1385 // Found the brace body node.
1390 SmStructureNode
* pBraceNodeTmp
= pNode
->GetParent();
1391 if (!pBraceNodeTmp
|| pBraceNodeTmp
->GetType() != SmNodeType::Brace
) {
1392 // Brace node is invalid.
1396 SmBraceNode
* pBraceNode
= static_cast<SmBraceNode
*>(pBraceNodeTmp
);
1397 SmMathSymbolNode
* pClosingNode
= pBraceNode
->ClosingBrace();
1398 if (!pClosingNode
) {
1399 // Couldn't get closing symbol node.
1403 // Check if the closing brace matches eBracketType.
1404 SmTokenType eClosingTokenType
= pClosingNode
->GetToken().eType
;
1405 switch (eBracketType
) {
1406 case SmBracketType::Round
: if (eClosingTokenType
!= TRPARENT
) { return false; } break;
1407 case SmBracketType::Square
: if (eClosingTokenType
!= TRBRACKET
) { return false; } break;
1408 case SmBracketType::Curly
: if (eClosingTokenType
!= TRBRACE
) { return false; } break;
1416 /////////////////////////////////////// SmNodeListParser
1418 SmNode
* SmNodeListParser::Parse(SmNodeList
* list
){
1420 //Delete error nodes
1421 SmNodeList::iterator it
= pList
->begin();
1422 while(it
!= pList
->end()) {
1423 if((*it
)->GetType() == SmNodeType::Error
){
1426 it
= pList
->erase(it
);
1430 SmNode
* retval
= Expression();
1435 SmNode
* SmNodeListParser::Expression(){
1436 SmNodeArray NodeArray
;
1437 //Accept as many relations as there is
1439 NodeArray
.push_back(Relation());
1441 //Create SmExpressionNode, I hope SmToken() will do :)
1442 SmStructureNode
* pExpr
= new SmExpressionNode(SmToken());
1443 pExpr
->SetSubNodes(std::move(NodeArray
));
1447 SmNode
* SmNodeListParser::Relation(){
1449 std::unique_ptr
<SmNode
> pLeft(Sum());
1450 //While we have tokens and the next is a relation
1451 while(Terminal() && IsRelationOperator(Terminal()->GetToken())){
1453 std::unique_ptr
<SmNode
> pOper(Take());
1454 //Find the right side of the relation
1455 std::unique_ptr
<SmNode
> pRight(Sum());
1456 //Create new SmBinHorNode
1457 std::unique_ptr
<SmStructureNode
> pNewNode(new SmBinHorNode(SmToken()));
1458 pNewNode
->SetSubNodes(std::move(pLeft
), std::move(pOper
), std::move(pRight
));
1459 pLeft
= std::move(pNewNode
);
1461 return pLeft
.release();
1464 SmNode
* SmNodeListParser::Sum(){
1466 std::unique_ptr
<SmNode
> pLeft(Product());
1467 //While we have tokens and the next is a sum
1468 while(Terminal() && IsSumOperator(Terminal()->GetToken())){
1470 std::unique_ptr
<SmNode
> pOper(Take());
1471 //Find the right side of the sum
1472 std::unique_ptr
<SmNode
> pRight(Product());
1473 //Create new SmBinHorNode
1474 std::unique_ptr
<SmStructureNode
> pNewNode(new SmBinHorNode(SmToken()));
1475 pNewNode
->SetSubNodes(std::move(pLeft
), std::move(pOper
), std::move(pRight
));
1476 pLeft
= std::move(pNewNode
);
1478 return pLeft
.release();
1481 SmNode
* SmNodeListParser::Product(){
1483 std::unique_ptr
<SmNode
> pLeft(Factor());
1484 //While we have tokens and the next is a product
1485 while(Terminal() && IsProductOperator(Terminal()->GetToken())){
1487 std::unique_ptr
<SmNode
> pOper(Take());
1488 //Find the right side of the operation
1489 std::unique_ptr
<SmNode
> pRight(Factor());
1490 //Create new SmBinHorNode
1491 std::unique_ptr
<SmStructureNode
> pNewNode(new SmBinHorNode(SmToken()));
1492 pNewNode
->SetSubNodes(std::move(pLeft
), std::move(pOper
), std::move(pRight
));
1493 pLeft
= std::move(pNewNode
);
1495 return pLeft
.release();
1498 SmNode
* SmNodeListParser::Factor(){
1499 //Read unary operations
1502 //Take care of unary operators
1503 else if(IsUnaryOperator(Terminal()->GetToken()))
1505 SmStructureNode
*pUnary
= new SmUnHorNode(SmToken());
1506 std::unique_ptr
<SmNode
> pOper(Terminal()),
1510 pArg
.reset(Factor());
1512 pArg
.reset(Error());
1514 pUnary
->SetSubNodes(std::move(pOper
), std::move(pArg
));
1520 SmNode
* SmNodeListParser::Postfix(){
1523 std::unique_ptr
<SmNode
> pArg
;
1524 if(IsPostfixOperator(Terminal()->GetToken()))
1525 pArg
.reset(Error());
1526 else if(IsOperator(Terminal()->GetToken()))
1530 while(Terminal() && IsPostfixOperator(Terminal()->GetToken())) {
1531 std::unique_ptr
<SmStructureNode
> pUnary(new SmUnHorNode(SmToken()) );
1532 std::unique_ptr
<SmNode
> pOper(Take());
1533 pUnary
->SetSubNodes(std::move(pArg
), std::move(pOper
));
1534 pArg
= std::move(pUnary
);
1536 return pArg
.release();
1539 SmNode
* SmNodeListParser::Error(){
1540 return new SmErrorNode(SmToken());
1543 bool SmNodeListParser::IsOperator(const SmToken
&token
) {
1544 return IsRelationOperator(token
) ||
1545 IsSumOperator(token
) ||
1546 IsProductOperator(token
) ||
1547 IsUnaryOperator(token
) ||
1548 IsPostfixOperator(token
);
1551 bool SmNodeListParser::IsRelationOperator(const SmToken
&token
) {
1552 return bool(token
.nGroup
& TG::Relation
);
1555 bool SmNodeListParser::IsSumOperator(const SmToken
&token
) {
1556 return bool(token
.nGroup
& TG::Sum
);
1559 bool SmNodeListParser::IsProductOperator(const SmToken
&token
) {
1560 return token
.nGroup
& TG::Product
&&
1561 token
.eType
!= TWIDESLASH
&&
1562 token
.eType
!= TWIDEBACKSLASH
&&
1563 token
.eType
!= TUNDERBRACE
&&
1564 token
.eType
!= TOVERBRACE
&&
1565 token
.eType
!= TOVER
;
1568 bool SmNodeListParser::IsUnaryOperator(const SmToken
&token
) {
1569 return token
.nGroup
& TG::UnOper
&&
1570 (token
.eType
== TPLUS
||
1571 token
.eType
== TMINUS
||
1572 token
.eType
== TPLUSMINUS
||
1573 token
.eType
== TMINUSPLUS
||
1574 token
.eType
== TNEG
||
1575 token
.eType
== TUOPER
);
1578 bool SmNodeListParser::IsPostfixOperator(const SmToken
&token
) {
1579 return token
.eType
== TFACT
;
1582 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */