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>
16 #include <osl/diagnose.h>
19 void SmCursor::Move(OutputDevice
* pDev
, SmMovementDirection direction
, bool bMoveAnchor
){
20 SmCaretPosGraphEntry
* NewPos
= nullptr;
25 NewPos
= mpPosition
->Left
;
26 OSL_ENSURE(NewPos
, "NewPos shouldn't be NULL here!");
30 NewPos
= mpPosition
->Right
;
31 OSL_ENSURE(NewPos
, "NewPos shouldn't be NULL here!");
34 //Implementation is practically identical to MoveDown, except for a single if statement
35 //so I've implemented them together and added a direction == MoveDown to the if statements.
39 SmCaretLine from_line
= SmCaretPos2LineVisitor(pDev
, mpPosition
->CaretPos
).GetResult(),
40 best_line
, //Best approximated line found so far
41 curr_line
; //Current line
42 long dbp_sq
= 0; //Distance squared to best line
43 for(const auto &pEntry
: *mpGraph
)
45 //Reject it if it's the current position
46 if(pEntry
->CaretPos
== mpPosition
->CaretPos
) continue;
48 curr_line
= SmCaretPos2LineVisitor(pDev
, pEntry
->CaretPos
).GetResult();
49 //Reject anything above if we're moving down
50 if(curr_line
.GetTop() <= from_line
.GetTop() && direction
== MoveDown
) continue;
51 //Reject anything below if we're moving up
52 if(curr_line
.GetTop() + curr_line
.GetHeight() >= from_line
.GetTop() + from_line
.GetHeight()
53 && direction
== MoveUp
) continue;
54 //Compare if it to what we have, if we have anything yet
56 //Compute distance to current line squared, multiplied with a horizontal factor
57 long dp_sq
= curr_line
.SquaredDistanceX(from_line
) * HORIZONTICAL_DISTANCE_FACTOR
+
58 curr_line
.SquaredDistanceY(from_line
);
59 //Discard current line if best line is closer
60 if(dbp_sq
<= dp_sq
) continue;
62 //Take current line as the best
63 best_line
= curr_line
;
64 NewPos
= pEntry
.get();
65 //Update distance to best line
66 dbp_sq
= best_line
.SquaredDistanceX(from_line
) * HORIZONTICAL_DISTANCE_FACTOR
+
67 best_line
.SquaredDistanceY(from_line
);
82 void SmCursor::MoveTo(OutputDevice
* pDev
, const Point
& pos
, bool bMoveAnchor
)
84 SmCaretPosGraphEntry
* NewPos
= nullptr;
85 long dp_sq
= 0, //Distance to current line squared
86 dbp_sq
= 1; //Distance to best line squared
87 for(const auto &pEntry
: *mpGraph
)
89 OSL_ENSURE(pEntry
->CaretPos
.IsValid(), "The caret position graph may not have invalid positions!");
90 //Compute current line
91 SmCaretLine curr_line
= SmCaretPos2LineVisitor(pDev
, pEntry
->CaretPos
).GetResult();
92 //Compute squared distance to current line
93 dp_sq
= curr_line
.SquaredDistanceX(pos
) + curr_line
.SquaredDistanceY(pos
);
94 //If we have a position compare to it
96 //If best line is closer, reject current line
97 if(dbp_sq
<= dp_sq
) continue;
99 //Accept current position as the best
100 NewPos
= pEntry
.get();
101 //Update distance to best line
112 void SmCursor::BuildGraph(){
113 //Save the current anchor and position
114 SmCaretPos _anchor
, _position
;
115 //Release mpGraph if allocated
118 _anchor
= mpAnchor
->CaretPos
;
120 _position
= mpPosition
->CaretPos
;
122 //Reset anchor and position as they point into an old graph
124 mpPosition
= nullptr;
127 //Build the new graph
128 mpGraph
.reset(SmCaretPosGraphBuildingVisitor(mpTree
).takeGraph());
130 //Restore anchor and position pointers
131 if(_anchor
.IsValid() || _position
.IsValid()){
132 for(const auto &pEntry
: *mpGraph
)
134 if(_anchor
== pEntry
->CaretPos
)
135 mpAnchor
= pEntry
.get();
136 if(_position
== pEntry
->CaretPos
)
137 mpPosition
= pEntry
.get();
140 //Set position and anchor to first caret position
141 auto it
= mpGraph
->begin();
142 assert(it
!= mpGraph
->end());
144 mpPosition
= it
->get();
146 mpAnchor
= mpPosition
;
150 OSL_ENSURE(mpPosition
->CaretPos
.IsValid(), "Position must be valid");
151 OSL_ENSURE(mpAnchor
->CaretPos
.IsValid(), "Anchor must be valid");
154 bool SmCursor::SetCaretPosition(SmCaretPos pos
){
155 for(const auto &pEntry
: *mpGraph
)
157 if(pEntry
->CaretPos
== pos
)
159 mpPosition
= pEntry
.get();
160 mpAnchor
= pEntry
.get();
167 void SmCursor::AnnotateSelection(){
168 //TODO: Manage a state, reset it upon modification and optimize this call
169 SmSetSelectionVisitor(mpAnchor
->CaretPos
, mpPosition
->CaretPos
, mpTree
);
172 void SmCursor::Draw(OutputDevice
& pDev
, Point Offset
, bool isCaretVisible
){
173 SmCaretDrawingVisitor(pDev
, GetPosition(), Offset
, isCaretVisible
);
176 void SmCursor::DeletePrev(OutputDevice
* pDev
){
177 //Delete only a selection if there's a selection
183 SmNode
* pLine
= FindTopMostNodeInLine(mpPosition
->CaretPos
.pSelectedNode
);
184 SmStructureNode
* pLineParent
= pLine
->GetParent();
185 int nLineOffsetIdx
= pLineParent
->IndexOfSubNode(pLine
);
186 assert(nLineOffsetIdx
>= 0);
188 //If we're in front of a node who's parent is a TABLE
189 if (pLineParent
->GetType() == SmNodeType::Table
&& mpPosition
->CaretPos
.nIndex
== 0 && nLineOffsetIdx
> 0)
191 size_t nLineOffset
= nLineOffsetIdx
;
192 //Now we can merge with nLineOffset - 1
194 //Line to merge things into, so we can delete pLine
195 SmNode
* pMergeLine
= pLineParent
->GetSubNode(nLineOffset
-1);
196 OSL_ENSURE(pMergeLine
, "pMergeLine cannot be NULL!");
197 SmCaretPos PosAfterDelete
;
198 //Convert first line to list
199 std::unique_ptr
<SmNodeList
> pLineList(new SmNodeList
);
200 NodeToList(pMergeLine
, *pLineList
);
201 if(!pLineList
->empty()){
202 //Find iterator to patch
203 SmNodeList::iterator patchPoint
= pLineList
->end();
205 //Convert second line to list
206 NodeToList(pLine
, *pLineList
);
207 //Patch the line list
209 PosAfterDelete
= PatchLineList(pLineList
.get(), patchPoint
);
211 pLine
= SmNodeListParser().Parse(pLineList
.get());
214 pLineParent
->SetSubNode(nLineOffset
-1, pLine
);
215 //Delete the removed line slot
216 SmNodeArray
lines(pLineParent
->GetNumSubNodes()-1);
217 for (size_t i
= 0; i
< pLineParent
->GetNumSubNodes(); ++i
)
220 lines
[i
] = pLineParent
->GetSubNode(i
);
221 else if(i
> nLineOffset
)
222 lines
[i
-1] = pLineParent
->GetSubNode(i
);
224 pLineParent
->SetSubNodes(std::move(lines
));
227 mpPosition
= nullptr;
231 if(!SetCaretPosition(PosAfterDelete
))
232 SetCaretPosition(SmCaretPos(pLine
, 0));
236 //TODO: If we're in an empty (sub/super/*) script
237 /*}else if(pLineParent->GetType() == SmNodeType::SubSup &&
239 pLine->GetType() == SmNodeType::Expression &&
240 pLine->GetNumSubNodes() == 0){
241 //There's a (sub/super) script we can delete
242 //Consider selecting the entire script if GetNumSubNodes() != 0 or pLine->GetType() != SmNodeType::Expression
243 //TODO: Handle case where we delete a limit
246 //Else move select, and delete if not complex
248 Move(pDev
, MoveLeft
, false);
249 if(!HasComplexSelection())
254 void SmCursor::Delete(){
255 //Return if we don't have a selection to delete
262 //Set selected on nodes
265 //Find an arbitrary selected node
266 SmNode
* pSNode
= FindSelectedNode(mpTree
);
269 //Find the topmost node of the line that holds the selection
270 SmNode
* pLine
= FindTopMostNodeInLine(pSNode
, true);
271 OSL_ENSURE(pLine
!= mpTree
, "Shouldn't be able to select the entire tree");
273 //Get the parent of the line
274 SmStructureNode
* pLineParent
= pLine
->GetParent();
275 //Find line offset in parent
276 int nLineOffset
= pLineParent
->IndexOfSubNode(pLine
);
277 assert(nLineOffset
>= 0);
279 //Position after delete
280 SmCaretPos PosAfterDelete
;
282 std::unique_ptr
<SmNodeList
> pLineList(new SmNodeList
);
283 NodeToList(pLine
, *pLineList
);
285 //Take the selected nodes and delete them...
286 SmNodeList::iterator patchIt
= TakeSelectedNodesFromList(pLineList
.get());
288 //Get the position to set after delete
289 PosAfterDelete
= PatchLineList(pLineList
.get(), patchIt
);
292 FinishEdit(std::move(pLineList
), pLineParent
, nLineOffset
, PosAfterDelete
);
295 void SmCursor::InsertNodes(std::unique_ptr
<SmNodeList
> pNewNodes
){
296 if(pNewNodes
->empty()){
303 //Get the current position
304 const SmCaretPos pos
= mpPosition
->CaretPos
;
306 //Find top most of line that holds position
307 SmNode
* pLine
= FindTopMostNodeInLine(pos
.pSelectedNode
);
309 //Find line parent and line index in parent
310 SmStructureNode
* pLineParent
= pLine
->GetParent();
311 int nParentIndex
= pLineParent
->IndexOfSubNode(pLine
);
312 assert(nParentIndex
>= 0);
314 //Convert line to list
315 std::unique_ptr
<SmNodeList
> pLineList(new SmNodeList
);
316 NodeToList(pLine
, *pLineList
);
318 //Find iterator for place to insert nodes
319 SmNodeList::iterator it
= FindPositionInLineList(pLineList
.get(), pos
);
321 //Insert all new nodes
322 SmNodeList::iterator newIt
,
323 patchIt
= it
, // (pointless default value, fixes compiler warnings)
325 for(newIt
= pNewNodes
->begin(); newIt
!= pNewNodes
->end(); ++newIt
){
326 insIt
= pLineList
->insert(it
, *newIt
);
327 if(newIt
== pNewNodes
->begin())
330 //Patch the places we've changed stuff
331 PatchLineList(pLineList
.get(), patchIt
);
332 SmCaretPos PosAfterInsert
= PatchLineList(pLineList
.get(), it
);
333 //Release list, we've taken the nodes
337 FinishEdit(std::move(pLineList
), pLineParent
, nParentIndex
, PosAfterInsert
);
340 SmNodeList::iterator
SmCursor::FindPositionInLineList(SmNodeList
* pLineList
,
341 const SmCaretPos
& rCaretPos
)
343 //Find iterator for position
344 SmNodeList::iterator it
= std::find(pLineList
->begin(), pLineList
->end(), rCaretPos
.pSelectedNode
);
345 if (it
!= pLineList
->end())
347 if((*it
)->GetType() == SmNodeType::Text
)
349 //Split textnode if needed
350 if(rCaretPos
.nIndex
> 0)
352 SmTextNode
* pText
= static_cast<SmTextNode
*>(rCaretPos
.pSelectedNode
);
353 if (rCaretPos
.nIndex
== pText
->GetText().getLength())
355 OUString str1
= pText
->GetText().copy(0, rCaretPos
.nIndex
);
356 OUString str2
= pText
->GetText().copy(rCaretPos
.nIndex
);
357 pText
->ChangeText(str1
);
359 //Insert str2 as new text node
360 assert(!str2
.isEmpty());
361 SmTextNode
* pNewText
= new SmTextNode(pText
->GetToken(), pText
->GetFontDesc());
362 pNewText
->ChangeText(str2
);
363 it
= pLineList
->insert(it
, pNewText
);
367 //it now pointer to the node following pos, so pLineList->insert(it, ...) will insert correctly
370 //If we didn't find pSelectedNode, it must be because the caret is in front of the line
371 return pLineList
->begin();
374 SmCaretPos
SmCursor::PatchLineList(SmNodeList
* pLineList
, SmNodeList::iterator aIter
) {
375 //The nodes we should consider merging
376 SmNode
*prev
= nullptr,
378 if(aIter
!= pLineList
->end())
380 if(aIter
!= pLineList
->begin()) {
386 //Check if there's textnodes to merge
389 prev
->GetType() == SmNodeType::Text
&&
390 next
->GetType() == SmNodeType::Text
&&
391 ( prev
->GetToken().eType
!= TNUMBER
||
392 next
->GetToken().eType
== TNUMBER
) ){
393 SmTextNode
*pText
= static_cast<SmTextNode
*>(prev
),
394 *pOldN
= static_cast<SmTextNode
*>(next
);
395 SmCaretPos
retval(pText
, pText
->GetText().getLength());
396 OUString newText
= pText
->GetText() + pOldN
->GetText();
397 pText
->ChangeText(newText
);
399 pLineList
->erase(aIter
);
403 //Check if there's a SmPlaceNode to remove:
404 if(prev
&& next
&& prev
->GetType() == SmNodeType::Place
&& !SmNodeListParser::IsOperator(next
->GetToken())){
406 aIter
= pLineList
->erase(aIter
);
408 //Return caret pos in front of aIter
409 if(aIter
!= pLineList
->begin())
410 --aIter
; //Thus find node before aIter
411 if(aIter
== pLineList
->begin())
413 return SmCaretPos::GetPosAfter(*aIter
);
415 if(prev
&& next
&& next
->GetType() == SmNodeType::Place
&& !SmNodeListParser::IsOperator(prev
->GetToken())){
416 aIter
= pLineList
->erase(aIter
);
418 return SmCaretPos::GetPosAfter(prev
);
421 //If we didn't do anything return
422 if(!prev
) //return an invalid to indicate we're in front of line
424 return SmCaretPos::GetPosAfter(prev
);
427 SmNodeList::iterator
SmCursor::TakeSelectedNodesFromList(SmNodeList
*pLineList
,
428 SmNodeList
*pSelectedNodes
) {
429 SmNodeList::iterator retval
;
430 SmNodeList::iterator it
= pLineList
->begin();
431 while(it
!= pLineList
->end()){
432 if((*it
)->IsSelected()){
434 if((*it
)->GetType() == SmNodeType::Text
) {
435 SmTextNode
* pText
= static_cast<SmTextNode
*>(*it
);
436 OUString aText
= pText
->GetText();
437 //Start and lengths of the segments, 2 is the selected segment
438 int start2
= pText
->GetSelectionStart(),
439 start3
= pText
->GetSelectionEnd(),
441 len2
= start3
- start2
,
442 len3
= aText
.getLength() - start3
;
443 SmToken aToken
= pText
->GetToken();
444 sal_uInt16 eFontDesc
= pText
->GetFontDesc();
445 //If we need make segment 1
447 OUString str
= aText
.copy(0, len1
);
448 pText
->ChangeText(str
);
450 } else {//Remove it if not needed
451 it
= pLineList
->erase(it
);
454 //Set retval to be right after the selection
456 //if we need make segment 3
458 OUString str
= aText
.copy(start3
, len3
);
459 SmTextNode
* pSeg3
= new SmTextNode(aToken
, eFontDesc
);
460 pSeg3
->ChangeText(str
);
461 retval
= pLineList
->insert(it
, pSeg3
);
463 //If we need to save the selected text
464 if(pSelectedNodes
&& len2
> 0) {
465 OUString str
= aText
.copy(start2
, len2
);
466 SmTextNode
* pSeg2
= new SmTextNode(aToken
, eFontDesc
);
467 pSeg2
->ChangeText(str
);
468 pSelectedNodes
->push_back(pSeg2
);
470 } else { //if it's not textnode
472 retval
= it
= pLineList
->erase(it
);
474 pSelectedNodes
->push_back(pNode
);
484 void SmCursor::InsertSubSup(SmSubSup eSubSup
) {
490 SmNode
*pSNode
= FindSelectedNode(mpTree
);
492 pLine
= FindTopMostNodeInLine(pSNode
, true);
494 pLine
= FindTopMostNodeInLine(mpPosition
->CaretPos
.pSelectedNode
);
496 //Find Parent and offset in parent
497 SmStructureNode
*pLineParent
= pLine
->GetParent();
498 int nParentIndex
= pLineParent
->IndexOfSubNode(pLine
);
499 assert(nParentIndex
>= 0);
501 //TODO: Consider handling special cases where parent is an SmOperNode,
502 // Maybe this method should be able to add limits to an SmOperNode...
504 //We begin modifying the tree here
507 //Convert line to list
508 std::unique_ptr
<SmNodeList
> pLineList(new SmNodeList
);
509 NodeToList(pLine
, *pLineList
);
511 //Take the selection, and/or find iterator for current position
512 std::unique_ptr
<SmNodeList
> pSelectedNodesList(new SmNodeList
);
513 SmNodeList::iterator it
;
515 it
= TakeSelectedNodesFromList(pLineList
.get(), pSelectedNodesList
.get());
517 it
= FindPositionInLineList(pLineList
.get(), mpPosition
->CaretPos
);
519 //Find node that this should be applied to
521 bool bPatchLine
= !pSelectedNodesList
->empty(); //If the line should be patched later
522 if(it
!= pLineList
->begin()) {
527 //Create a new place node
528 pSubject
= new SmPlaceNode();
529 pSubject
->Prepare(mpDocShell
->GetFormat(), *mpDocShell
, 0);
530 it
= pLineList
->insert(it
, pSubject
);
532 bPatchLine
= true; //We've modified the line it should be patched later.
535 //Wrap the subject in a SmSubSupNode
536 SmSubSupNode
* pSubSup
;
537 if(pSubject
->GetType() != SmNodeType::SubSup
){
539 token
.nGroup
= TG::Power
;
540 pSubSup
= new SmSubSupNode(token
);
541 pSubSup
->SetBody(pSubject
);
545 pSubSup
= static_cast<SmSubSupNode
*>(pSubject
);
546 //pSubject shouldn't be referenced anymore, pSubSup is the SmSubSupNode in pLineList we wish to edit.
547 //and it pointer to the element following pSubSup in pLineList.
550 //Patch the line if we noted that was needed previously
552 PatchLineList(pLineList
.get(), it
);
554 //Convert existing, if any, sub-/superscript line to list
555 SmNode
*pScriptLine
= pSubSup
->GetSubSup(eSubSup
);
556 std::unique_ptr
<SmNodeList
> pScriptLineList(new SmNodeList
);
557 NodeToList(pScriptLine
, *pScriptLineList
);
559 //Add selection to pScriptLineList
560 unsigned int nOldSize
= pScriptLineList
->size();
561 pScriptLineList
->insert(pScriptLineList
->end(), pSelectedNodesList
->begin(), pSelectedNodesList
->end());
562 pSelectedNodesList
.reset();
564 //Patch pScriptLineList if needed
565 if(0 < nOldSize
&& nOldSize
< pScriptLineList
->size()) {
566 SmNodeList::iterator iPatchPoint
= pScriptLineList
->begin();
567 std::advance(iPatchPoint
, nOldSize
);
568 PatchLineList(pScriptLineList
.get(), iPatchPoint
);
571 //Find caret pos, that should be used after sub-/superscription.
572 SmCaretPos PosAfterScript
; //Leave invalid for first position
573 if (!pScriptLineList
->empty())
574 PosAfterScript
= SmCaretPos::GetPosAfter(pScriptLineList
->back());
576 //Parse pScriptLineList
577 pScriptLine
= SmNodeListParser().Parse(pScriptLineList
.get());
578 pScriptLineList
.reset();
580 //Insert pScriptLine back into the tree
581 pSubSup
->SetSubSup(eSubSup
, pScriptLine
);
584 FinishEdit(std::move(pLineList
), pLineParent
, nParentIndex
, PosAfterScript
, pScriptLine
);
587 void SmCursor::InsertBrackets(SmBracketType eBracketType
) {
595 SmNode
*pSNode
= FindSelectedNode(mpTree
);
597 pLine
= FindTopMostNodeInLine(pSNode
, true);
599 pLine
= FindTopMostNodeInLine(mpPosition
->CaretPos
.pSelectedNode
);
601 //Find parent and offset in parent
602 SmStructureNode
*pLineParent
= pLine
->GetParent();
603 int nParentIndex
= pLineParent
->IndexOfSubNode(pLine
);
604 assert(nParentIndex
>= 0);
606 //Convert line to list
607 std::unique_ptr
<SmNodeList
> pLineList(new SmNodeList
);
608 NodeToList(pLine
, *pLineList
);
610 //Take the selection, and/or find iterator for current position
611 std::unique_ptr
<SmNodeList
> pSelectedNodesList(new SmNodeList
);
612 SmNodeList::iterator it
;
614 it
= TakeSelectedNodesFromList(pLineList
.get(), pSelectedNodesList
.get());
616 it
= FindPositionInLineList(pLineList
.get(), mpPosition
->CaretPos
);
618 //If there's no selected nodes, create a place node
619 std::unique_ptr
<SmNode
> pBodyNode
;
620 SmCaretPos PosAfterInsert
;
621 if(pSelectedNodesList
->empty()) {
622 pBodyNode
.reset(new SmPlaceNode());
623 PosAfterInsert
= SmCaretPos(pBodyNode
.get(), 1);
625 pBodyNode
.reset(SmNodeListParser().Parse(pSelectedNodesList
.get()));
627 pSelectedNodesList
.reset();
630 SmToken
aTok(TLEFT
, '\0', "left", TG::NONE
, 5);
631 SmBraceNode
*pBrace
= new SmBraceNode(aTok
);
632 pBrace
->SetScaleMode(SmScaleMode::Height
);
633 std::unique_ptr
<SmNode
> pLeft( CreateBracket(eBracketType
, true) ),
634 pRight( CreateBracket(eBracketType
, false) );
635 std::unique_ptr
<SmBracebodyNode
> pBody(new SmBracebodyNode(SmToken()));
636 pBody
->SetSubNodes(std::move(pBodyNode
), nullptr);
637 pBrace
->SetSubNodes(std::move(pLeft
), std::move(pBody
), std::move(pRight
));
638 pBrace
->Prepare(mpDocShell
->GetFormat(), *mpDocShell
, 0);
641 pLineList
->insert(it
, pBrace
);
642 //Patch line (I think this is good enough)
643 SmCaretPos aAfter
= PatchLineList(pLineList
.get(), it
);
644 if( !PosAfterInsert
.IsValid() )
645 PosAfterInsert
= aAfter
;
648 FinishEdit(std::move(pLineList
), pLineParent
, nParentIndex
, PosAfterInsert
);
651 SmNode
*SmCursor::CreateBracket(SmBracketType eBracketType
, bool bIsLeft
) {
654 switch(eBracketType
){
655 case SmBracketType::Round
:
656 aTok
= SmToken(TLPARENT
, MS_LPARENT
, "(", TG::LBrace
, 5);
658 case SmBracketType::Square
:
659 aTok
= SmToken(TLBRACKET
, MS_LBRACKET
, "[", TG::LBrace
, 5);
661 case SmBracketType::Curly
:
662 aTok
= SmToken(TLBRACE
, MS_LBRACE
, "lbrace", TG::LBrace
, 5);
666 switch(eBracketType
) {
667 case SmBracketType::Round
:
668 aTok
= SmToken(TRPARENT
, MS_RPARENT
, ")", TG::RBrace
, 5);
670 case SmBracketType::Square
:
671 aTok
= SmToken(TRBRACKET
, MS_RBRACKET
, "]", TG::RBrace
, 5);
673 case SmBracketType::Curly
:
674 aTok
= SmToken(TRBRACE
, MS_RBRACE
, "rbrace", TG::RBrace
, 5);
678 SmNode
* pRetVal
= new SmMathSymbolNode(aTok
);
679 pRetVal
->SetScaleMode(SmScaleMode::Height
);
683 bool SmCursor::InsertRow() {
689 SmNode
*pSNode
= FindSelectedNode(mpTree
);
691 pLine
= FindTopMostNodeInLine(pSNode
, true);
693 pLine
= FindTopMostNodeInLine(mpPosition
->CaretPos
.pSelectedNode
);
695 //Find parent and offset in parent
696 SmStructureNode
*pLineParent
= pLine
->GetParent();
697 int nParentIndex
= pLineParent
->IndexOfSubNode(pLine
);
698 assert(nParentIndex
>= 0);
700 //Discover the context of this command
701 SmTableNode
*pTable
= nullptr;
702 SmMatrixNode
*pMatrix
= nullptr;
703 int nTableIndex
= nParentIndex
;
704 if(pLineParent
->GetType() == SmNodeType::Table
)
705 pTable
= static_cast<SmTableNode
*>(pLineParent
);
706 //If it's wrapped in a SmLineNode, we can still insert a newline
707 else if(pLineParent
->GetType() == SmNodeType::Line
&&
708 pLineParent
->GetParent() &&
709 pLineParent
->GetParent()->GetType() == SmNodeType::Table
) {
710 //NOTE: This hack might give problems if we stop ignoring SmAlignNode
711 pTable
= static_cast<SmTableNode
*>(pLineParent
->GetParent());
712 nTableIndex
= pTable
->IndexOfSubNode(pLineParent
);
713 assert(nTableIndex
>= 0);
715 if(pLineParent
->GetType() == SmNodeType::Matrix
)
716 pMatrix
= static_cast<SmMatrixNode
*>(pLineParent
);
718 //If we're not in a context that supports InsertRow, return sal_False
719 if(!pTable
&& !pMatrix
)
722 //Now we start editing
725 //Convert line to list
726 std::unique_ptr
<SmNodeList
> pLineList(new SmNodeList
);
727 NodeToList(pLine
, *pLineList
);
729 //Find position in line
730 SmNodeList::iterator it
;
732 //Take the selected nodes and delete them...
733 it
= TakeSelectedNodesFromList(pLineList
.get());
735 it
= FindPositionInLineList(pLineList
.get(), mpPosition
->CaretPos
);
737 //New caret position after inserting the newline/row in whatever context
738 SmCaretPos PosAfterInsert
;
740 //If we're in the context of a table
742 std::unique_ptr
<SmNodeList
> pNewLineList(new SmNodeList
);
743 //Move elements from pLineList to pNewLineList
744 SmNodeList
& rLineList
= *pLineList
;
745 pNewLineList
->splice(pNewLineList
->begin(), rLineList
, it
, rLineList
.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 std::unique_ptr
<SmNode
> pNewLine(SmNodeListParser().Parse(pNewLineList
.get()));
754 pNewLineList
.reset();
755 //Wrap pNewLine in SmLineNode if needed
756 if(pLineParent
->GetType() == SmNodeType::Line
) {
757 std::unique_ptr
<SmLineNode
> pNewLineNode(new SmLineNode(SmToken(TNEWLINE
, '\0', "newline")));
758 pNewLineNode
->SetSubNodes(std::move(pNewLine
), nullptr);
759 pNewLine
= std::move(pNewLineNode
);
762 PosAfterInsert
= SmCaretPos(pNewLine
.get(), 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
.get());
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
.get(), 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(std::move(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 std::unique_ptr
<SmNodeList
> pLineList(new SmNodeList
);
826 NodeToList(pLine
, *pLineList
);
828 //Take the selection, and/or find iterator for current position
829 std::unique_ptr
<SmNodeList
> pSelectedNodesList(new SmNodeList
);
830 SmNodeList::iterator it
;
832 it
= TakeSelectedNodesFromList(pLineList
.get(), pSelectedNodesList
.get());
834 it
= FindPositionInLineList(pLineList
.get(), mpPosition
->CaretPos
);
836 //Create pNum, and pDenom
837 bool bEmptyFraction
= pSelectedNodesList
->empty();
838 std::unique_ptr
<SmNode
> pNum( bEmptyFraction
840 : SmNodeListParser().Parse(pSelectedNodesList
.get()) );
841 std::unique_ptr
<SmNode
> pDenom(new SmPlaceNode());
842 pSelectedNodesList
.reset();
844 //Create new fraction
845 SmBinVerNode
*pFrac
= new SmBinVerNode(SmToken(TOVER
, '\0', "over", TG::Product
, 0));
846 std::unique_ptr
<SmNode
> pRect(new SmRectangleNode(SmToken()));
847 pFrac
->SetSubNodes(std::move(pNum
), std::move(pRect
), std::move(pDenom
));
849 //Insert in pLineList
850 SmNodeList::iterator patchIt
= pLineList
->insert(it
, pFrac
);
851 PatchLineList(pLineList
.get(), patchIt
);
852 PatchLineList(pLineList
.get(), it
);
855 SmNode
*pSelectedNode
= bEmptyFraction
? pFrac
->GetSubNode(0) : pFrac
->GetSubNode(2);
856 FinishEdit(std::move(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 std::unique_ptr
<SmNodeList
> pList(new SmNodeList
);
878 pList
->push_front(pText
);
879 InsertNodes(std::move(pList
));
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 std::unique_ptr
<SmNodeList
> pList(new SmNodeList
);
980 pList
->push_front(pNewNode
);
981 InsertNodes(std::move(pList
));
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 std::unique_ptr
<SmNodeList
> pList(new SmNodeList
);
1007 pList
->push_front(pSpecial
);
1008 InsertNodes(std::move(pList
));
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 std::unique_ptr
<SmNodeList
> pLineList(new SmNodeList
);
1023 NodeToList(pSubExpr
, *pLineList
);
1027 //Delete any selection
1031 InsertNodes(std::move(pLineList
));
1036 void SmCursor::Copy(){
1040 AnnotateSelection();
1041 //Find selected node
1042 SmNode
* pSNode
= FindSelectedNode(mpTree
);
1045 SmNode
* pLine
= FindTopMostNodeInLine(pSNode
, true);
1048 //Clone selected nodes
1049 SmClipboard aClipboard
;
1050 if(IsLineCompositionNode(pLine
))
1051 CloneLineToClipboard(static_cast<SmStructureNode
*>(pLine
), &aClipboard
);
1053 //Special care to only clone selected text
1054 if(pLine
->GetType() == SmNodeType::Text
) {
1055 SmTextNode
*pText
= static_cast<SmTextNode
*>(pLine
);
1056 std::unique_ptr
<SmTextNode
> pClone(new SmTextNode( pText
->GetToken(), pText
->GetFontDesc() ));
1057 int start
= pText
->GetSelectionStart(),
1058 length
= pText
->GetSelectionEnd() - pText
->GetSelectionStart();
1059 pClone
->ChangeText(pText
->GetText().copy(start
, length
));
1060 pClone
->SetScaleMode(pText
->GetScaleMode());
1061 aClipboard
.push_front(std::move(pClone
));
1063 SmCloningVisitor aCloneFactory
;
1064 aClipboard
.push_front(std::unique_ptr
<SmNode
>(aCloneFactory
.Clone(pLine
)));
1069 if (!aClipboard
.empty())
1070 maClipboard
= std::move(aClipboard
);
1073 void SmCursor::Paste() {
1077 if (!maClipboard
.empty())
1078 InsertNodes(CloneList(maClipboard
));
1083 std::unique_ptr
<SmNodeList
> SmCursor::CloneList(SmClipboard
&rClipboard
){
1084 SmCloningVisitor aCloneFactory
;
1085 std::unique_ptr
<SmNodeList
> pClones(new SmNodeList
);
1087 for(auto &xNode
: rClipboard
){
1088 SmNode
*pClone
= aCloneFactory
.Clone(xNode
.get());
1089 pClones
->push_back(pClone
);
1095 SmNode
* SmCursor::FindTopMostNodeInLine(SmNode
* pSNode
, bool MoveUpIfSelected
){
1097 //Move up parent until we find a node who's
1098 //parent is NULL or isn't selected and not a type of:
1105 while(pSNode
->GetParent() &&
1106 ((MoveUpIfSelected
&&
1107 pSNode
->GetParent()->IsSelected()) ||
1108 IsLineCompositionNode(pSNode
->GetParent())))
1109 pSNode
= pSNode
->GetParent();
1110 //Now we have the selection line node
1114 SmNode
* SmCursor::FindSelectedNode(SmNode
* pNode
){
1115 if(pNode
->GetNumSubNodes() == 0)
1117 for(auto pChild
: *static_cast<SmStructureNode
*>(pNode
))
1121 if(pChild
->IsSelected())
1123 SmNode
* pRetVal
= FindSelectedNode(pChild
);
1130 void SmCursor::LineToList(SmStructureNode
* pLine
, SmNodeList
& list
){
1131 for(auto pChild
: *pLine
)
1135 switch(pChild
->GetType()){
1136 case SmNodeType::Line
:
1137 case SmNodeType::UnHor
:
1138 case SmNodeType::Expression
:
1139 case SmNodeType::BinHor
:
1140 case SmNodeType::Align
:
1141 case SmNodeType::Font
:
1142 LineToList(static_cast<SmStructureNode
*>(pChild
), list
);
1144 case SmNodeType::Error
:
1148 list
.push_back(pChild
);
1151 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(std::unique_ptr
<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 std::unique_ptr
<SmNode
> pLine(parser
.Parse(pLineList
.get()));
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 std::unique_ptr
<SmBraceNode
> pBrace(new SmBraceNode(aTok
));
1236 pBrace
->SetScaleMode(SmScaleMode::Height
);
1237 std::unique_ptr
<SmNode
> pLeft( CreateBracket(SmBracketType::Round
, true) ),
1238 pRight( CreateBracket(SmBracketType::Round
, false) );
1239 std::unique_ptr
<SmBracebodyNode
> pBody(new SmBracebodyNode(SmToken()));
1240 pBody
->SetSubNodes(std::move(pLine
), nullptr);
1241 pBrace
->SetSubNodes(std::move(pLeft
), std::move(pBody
), std::move(pRight
));
1242 pBrace
->Prepare(mpDocShell
->GetFormat(), *mpDocShell
, 0);
1243 pLine
= std::move(pBrace
);
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
1260 pStartLine
= pLine
.get();
1262 //Insert it back into the parent
1263 pParent
->SetSubNode(nParentIndex
, pLine
.release());
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 needs 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 std::unique_ptr
<SmNode
> pLeft(Sum());
1445 //While we have tokens and the next is a relation
1446 while(Terminal() && IsRelationOperator(Terminal()->GetToken())){
1448 std::unique_ptr
<SmNode
> pOper(Take());
1449 //Find the right side of the relation
1450 std::unique_ptr
<SmNode
> pRight(Sum());
1451 //Create new SmBinHorNode
1452 std::unique_ptr
<SmStructureNode
> pNewNode(new SmBinHorNode(SmToken()));
1453 pNewNode
->SetSubNodes(std::move(pLeft
), std::move(pOper
), std::move(pRight
));
1454 pLeft
= std::move(pNewNode
);
1456 return pLeft
.release();
1459 SmNode
* SmNodeListParser::Sum(){
1461 std::unique_ptr
<SmNode
> pLeft(Product());
1462 //While we have tokens and the next is a sum
1463 while(Terminal() && IsSumOperator(Terminal()->GetToken())){
1465 std::unique_ptr
<SmNode
> pOper(Take());
1466 //Find the right side of the sum
1467 std::unique_ptr
<SmNode
> pRight(Product());
1468 //Create new SmBinHorNode
1469 std::unique_ptr
<SmStructureNode
> pNewNode(new SmBinHorNode(SmToken()));
1470 pNewNode
->SetSubNodes(std::move(pLeft
), std::move(pOper
), std::move(pRight
));
1471 pLeft
= std::move(pNewNode
);
1473 return pLeft
.release();
1476 SmNode
* SmNodeListParser::Product(){
1478 std::unique_ptr
<SmNode
> pLeft(Factor());
1479 //While we have tokens and the next is a product
1480 while(Terminal() && IsProductOperator(Terminal()->GetToken())){
1482 std::unique_ptr
<SmNode
> pOper(Take());
1483 //Find the right side of the operation
1484 std::unique_ptr
<SmNode
> pRight(Factor());
1485 //Create new SmBinHorNode
1486 std::unique_ptr
<SmStructureNode
> pNewNode(new SmBinHorNode(SmToken()));
1487 pNewNode
->SetSubNodes(std::move(pLeft
), std::move(pOper
), std::move(pRight
));
1488 pLeft
= std::move(pNewNode
);
1490 return pLeft
.release();
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 std::unique_ptr
<SmNode
> pOper(Terminal()),
1505 pArg
.reset(Factor());
1507 pArg
.reset(Error());
1509 pUnary
->SetSubNodes(std::move(pOper
), std::move(pArg
));
1515 SmNode
* SmNodeListParser::Postfix(){
1518 std::unique_ptr
<SmNode
> pArg
;
1519 if(IsPostfixOperator(Terminal()->GetToken()))
1520 pArg
.reset(Error());
1521 else if(IsOperator(Terminal()->GetToken()))
1525 while(Terminal() && IsPostfixOperator(Terminal()->GetToken())) {
1526 std::unique_ptr
<SmStructureNode
> pUnary(new SmUnHorNode(SmToken()) );
1527 std::unique_ptr
<SmNode
> pOper(Take());
1528 pUnary
->SetSubNodes(std::move(pArg
), std::move(pOper
));
1529 pArg
= std::move(pUnary
);
1531 return pArg
.release();
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: */