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>
19 #include <vcl/transfer.hxx>
20 #include <vcl/unohelp2.hxx>
22 void SmCursor::Move(OutputDevice
* pDev
, SmMovementDirection direction
, bool bMoveAnchor
){
23 SmCaretPosGraphEntry
* NewPos
= nullptr;
28 NewPos
= mpPosition
->Left
;
29 OSL_ENSURE(NewPos
, "NewPos shouldn't be NULL here!");
33 NewPos
= mpPosition
->Right
;
34 OSL_ENSURE(NewPos
, "NewPos shouldn't be NULL here!");
37 //Implementation is practically identical to MoveDown, except for a single if statement
38 //so I've implemented them together and added a direction == MoveDown to the if statements.
42 SmCaretLine from_line
= SmCaretPos2LineVisitor(pDev
, mpPosition
->CaretPos
).GetResult(),
43 best_line
, //Best approximated line found so far
44 curr_line
; //Current line
45 tools::Long dbp_sq
= 0; //Distance squared to best line
46 for(const auto &pEntry
: *mpGraph
)
48 //Reject it if it's the current position
49 if(pEntry
->CaretPos
== mpPosition
->CaretPos
) continue;
51 curr_line
= SmCaretPos2LineVisitor(pDev
, pEntry
->CaretPos
).GetResult();
52 //Reject anything above if we're moving down
53 if(curr_line
.GetTop() <= from_line
.GetTop() && direction
== MoveDown
) continue;
54 //Reject anything below if we're moving up
55 if(curr_line
.GetTop() + curr_line
.GetHeight() >= from_line
.GetTop() + from_line
.GetHeight()
56 && direction
== MoveUp
) continue;
57 //Compare if it to what we have, if we have anything yet
59 //Compute distance to current line squared, multiplied with a horizontal factor
60 tools::Long dp_sq
= curr_line
.SquaredDistanceX(from_line
) * HORIZONTICAL_DISTANCE_FACTOR
+
61 curr_line
.SquaredDistanceY(from_line
);
62 //Discard current line if best line is closer
63 if(dbp_sq
<= dp_sq
) continue;
65 //Take current line as the best
66 best_line
= curr_line
;
67 NewPos
= pEntry
.get();
68 //Update distance to best line
69 dbp_sq
= best_line
.SquaredDistanceX(from_line
) * HORIZONTICAL_DISTANCE_FACTOR
+
70 best_line
.SquaredDistanceY(from_line
);
85 void SmCursor::MoveTo(OutputDevice
* pDev
, const Point
& pos
, bool bMoveAnchor
)
87 SmCaretPosGraphEntry
* NewPos
= nullptr;
88 tools::Long dp_sq
= 0, //Distance to current line squared
89 dbp_sq
= 1; //Distance to best line squared
90 for(const auto &pEntry
: *mpGraph
)
92 OSL_ENSURE(pEntry
->CaretPos
.IsValid(), "The caret position graph may not have invalid positions!");
93 //Compute current line
94 SmCaretLine curr_line
= SmCaretPos2LineVisitor(pDev
, pEntry
->CaretPos
).GetResult();
95 //Compute squared distance to current line
96 dp_sq
= curr_line
.SquaredDistanceX(pos
) + curr_line
.SquaredDistanceY(pos
);
97 //If we have a position compare to it
99 //If best line is closer, reject current line
100 if(dbp_sq
<= dp_sq
) continue;
102 //Accept current position as the best
103 NewPos
= pEntry
.get();
104 //Update distance to best line
115 void SmCursor::BuildGraph(){
116 //Save the current anchor and position
117 SmCaretPos _anchor
, _position
;
118 //Release mpGraph if allocated
121 _anchor
= mpAnchor
->CaretPos
;
123 _position
= mpPosition
->CaretPos
;
125 //Reset anchor and position as they point into an old graph
127 mpPosition
= nullptr;
130 //Build the new graph
131 mpGraph
.reset(SmCaretPosGraphBuildingVisitor(mpTree
).takeGraph());
133 //Restore anchor and position pointers
134 if(_anchor
.IsValid() || _position
.IsValid()){
135 for(const auto &pEntry
: *mpGraph
)
137 if(_anchor
== pEntry
->CaretPos
)
138 mpAnchor
= pEntry
.get();
139 if(_position
== pEntry
->CaretPos
)
140 mpPosition
= pEntry
.get();
143 //Set position and anchor to first caret position
144 auto it
= mpGraph
->begin();
145 assert(it
!= mpGraph
->end());
147 mpPosition
= it
->get();
149 mpAnchor
= mpPosition
;
153 OSL_ENSURE(mpPosition
->CaretPos
.IsValid(), "Position must be valid");
154 OSL_ENSURE(mpAnchor
->CaretPos
.IsValid(), "Anchor must be valid");
157 bool SmCursor::SetCaretPosition(SmCaretPos pos
){
158 for(const auto &pEntry
: *mpGraph
)
160 if(pEntry
->CaretPos
== pos
)
162 mpPosition
= pEntry
.get();
163 mpAnchor
= pEntry
.get();
170 void SmCursor::AnnotateSelection() const {
171 //TODO: Manage a state, reset it upon modification and optimize this call
172 SmSetSelectionVisitor(mpAnchor
->CaretPos
, mpPosition
->CaretPos
, mpTree
);
175 void SmCursor::Draw(OutputDevice
& pDev
, Point Offset
, bool isCaretVisible
){
176 SmCaretDrawingVisitor(pDev
, GetPosition(), Offset
, isCaretVisible
);
179 tools::Rectangle
SmCursor::GetCaretRectangle(OutputDevice
& rOutDev
) const
181 return SmCaretRectanglesVisitor(rOutDev
, GetPosition()).getCaret();
184 tools::Rectangle
SmCursor::GetSelectionRectangle(OutputDevice
& rOutDev
) const
187 return SmSelectionRectanglesVisitor(rOutDev
, mpTree
).GetSelection();
190 void SmCursor::DeletePrev(OutputDevice
* pDev
){
191 //Delete only a selection if there's a selection
197 SmNode
* pLine
= FindTopMostNodeInLine(mpPosition
->CaretPos
.pSelectedNode
);
198 SmStructureNode
* pLineParent
= pLine
->GetParent();
199 int nLineOffsetIdx
= pLineParent
->IndexOfSubNode(pLine
);
200 assert(nLineOffsetIdx
>= 0);
202 //If we're in front of a node who's parent is a TABLE
203 if (pLineParent
->GetType() == SmNodeType::Table
&& mpPosition
->CaretPos
.nIndex
== 0 && nLineOffsetIdx
> 0)
205 size_t nLineOffset
= nLineOffsetIdx
;
206 //Now we can merge with nLineOffset - 1
208 //Line to merge things into, so we can delete pLine
209 SmNode
* pMergeLine
= pLineParent
->GetSubNode(nLineOffset
-1);
210 OSL_ENSURE(pMergeLine
, "pMergeLine cannot be NULL!");
211 SmCaretPos PosAfterDelete
;
212 //Convert first line to list
213 std::unique_ptr
<SmNodeList
> pLineList(new SmNodeList
);
214 NodeToList(pMergeLine
, *pLineList
);
215 if(!pLineList
->empty()){
216 //Find iterator to patch
217 SmNodeList::iterator patchPoint
= pLineList
->end();
219 //Convert second line to list
220 NodeToList(pLine
, *pLineList
);
221 //Patch the line list
223 PosAfterDelete
= PatchLineList(pLineList
.get(), patchPoint
);
225 pLine
= SmNodeListParser().Parse(pLineList
.get());
228 pLineParent
->SetSubNode(nLineOffset
-1, pLine
);
229 //Delete the removed line slot
230 SmNodeArray
lines(pLineParent
->GetNumSubNodes()-1);
231 for (size_t i
= 0; i
< pLineParent
->GetNumSubNodes(); ++i
)
234 lines
[i
] = pLineParent
->GetSubNode(i
);
235 else if(i
> nLineOffset
)
236 lines
[i
-1] = pLineParent
->GetSubNode(i
);
238 pLineParent
->SetSubNodes(std::move(lines
));
241 mpPosition
= nullptr;
245 if(!SetCaretPosition(PosAfterDelete
))
246 SetCaretPosition(SmCaretPos(pLine
, 0));
250 //TODO: If we're in an empty (sub/super/*) script
251 /*}else if(pLineParent->GetType() == SmNodeType::SubSup &&
253 pLine->GetType() == SmNodeType::Expression &&
254 pLine->GetNumSubNodes() == 0){
255 //There's a (sub/super) script we can delete
256 //Consider selecting the entire script if GetNumSubNodes() != 0 or pLine->GetType() != SmNodeType::Expression
257 //TODO: Handle case where we delete a limit
260 //Else move select, and delete if not complex
262 Move(pDev
, MoveLeft
, false);
263 if(!HasComplexSelection())
268 void SmCursor::Delete(){
269 //Return if we don't have a selection to delete
276 //Set selected on nodes
279 //Find an arbitrary selected node
280 SmNode
* pSNode
= FindSelectedNode(mpTree
);
283 //Find the topmost node of the line that holds the selection
284 SmNode
* pLine
= FindTopMostNodeInLine(pSNode
, true);
285 OSL_ENSURE(pLine
!= mpTree
, "Shouldn't be able to select the entire tree");
287 //Get the parent of the line
288 SmStructureNode
* pLineParent
= pLine
->GetParent();
289 //Find line offset in parent
290 int nLineOffset
= pLineParent
->IndexOfSubNode(pLine
);
291 assert(nLineOffset
>= 0);
293 //Position after delete
294 SmCaretPos PosAfterDelete
;
296 std::unique_ptr
<SmNodeList
> pLineList(new SmNodeList
);
297 NodeToList(pLine
, *pLineList
);
299 //Take the selected nodes and delete them...
300 SmNodeList::iterator patchIt
= TakeSelectedNodesFromList(pLineList
.get());
302 //Get the position to set after delete
303 PosAfterDelete
= PatchLineList(pLineList
.get(), patchIt
);
306 FinishEdit(std::move(pLineList
), pLineParent
, nLineOffset
, PosAfterDelete
);
309 void SmCursor::InsertNodes(std::unique_ptr
<SmNodeList
> pNewNodes
){
310 if(pNewNodes
->empty()){
317 //Get the current position
318 const SmCaretPos pos
= mpPosition
->CaretPos
;
320 //Find top most of line that holds position
321 SmNode
* pLine
= FindTopMostNodeInLine(pos
.pSelectedNode
);
322 const bool bSelectedIsTopMost
= pLine
== pos
.pSelectedNode
;
324 //Find line parent and line index in parent
325 SmStructureNode
* pLineParent
= pLine
->GetParent();
326 int nParentIndex
= pLineParent
->IndexOfSubNode(pLine
);
327 assert(nParentIndex
>= 0);
329 //Convert line to list
330 std::unique_ptr
<SmNodeList
> pLineList(new SmNodeList
);
331 NodeToList(pLine
, *pLineList
); // deletes pLine, potentially deleting pos.pSelectedNode
333 //Find iterator for place to insert nodes
334 SmNodeList::iterator it
= bSelectedIsTopMost
? pLineList
->begin()
335 : FindPositionInLineList(pLineList
.get(), pos
);
337 //Insert all new nodes
338 SmNodeList::iterator newIt
,
339 patchIt
= it
, // (pointless default value, fixes compiler warnings)
341 for(newIt
= pNewNodes
->begin(); newIt
!= pNewNodes
->end(); ++newIt
){
342 insIt
= pLineList
->insert(it
, *newIt
);
343 if(newIt
== pNewNodes
->begin())
346 //Patch the places we've changed stuff
347 PatchLineList(pLineList
.get(), patchIt
);
348 SmCaretPos PosAfterInsert
= PatchLineList(pLineList
.get(), it
);
349 //Release list, we've taken the nodes
353 FinishEdit(std::move(pLineList
), pLineParent
, nParentIndex
, PosAfterInsert
);
356 SmNodeList::iterator
SmCursor::FindPositionInLineList(SmNodeList
* pLineList
,
357 const SmCaretPos
& rCaretPos
)
359 //Find iterator for position
360 SmNodeList::iterator it
= std::find(pLineList
->begin(), pLineList
->end(), rCaretPos
.pSelectedNode
);
361 if (it
!= pLineList
->end())
363 if((*it
)->GetType() == SmNodeType::Text
)
365 //Split textnode if needed
366 if(rCaretPos
.nIndex
> 0)
368 SmTextNode
* pText
= static_cast<SmTextNode
*>(rCaretPos
.pSelectedNode
);
369 if (rCaretPos
.nIndex
== pText
->GetText().getLength())
371 OUString str1
= pText
->GetText().copy(0, rCaretPos
.nIndex
);
372 OUString str2
= pText
->GetText().copy(rCaretPos
.nIndex
);
373 pText
->ChangeText(str1
);
375 //Insert str2 as new text node
376 assert(!str2
.isEmpty());
377 SmTextNode
* pNewText
= new SmTextNode(pText
->GetToken(), pText
->GetFontDesc());
378 pNewText
->ChangeText(str2
);
379 it
= pLineList
->insert(it
, pNewText
);
383 //it now pointer to the node following pos, so pLineList->insert(it, ...) will insert correctly
386 //If we didn't find pSelectedNode, it must be because the caret is in front of the line
387 return pLineList
->begin();
390 SmCaretPos
SmCursor::PatchLineList(SmNodeList
* pLineList
, SmNodeList::iterator aIter
) {
391 //The nodes we should consider merging
392 SmNode
*prev
= nullptr,
394 if(aIter
!= pLineList
->end())
396 if(aIter
!= pLineList
->begin()) {
402 //Check if there's textnodes to merge
405 prev
->GetType() == SmNodeType::Text
&&
406 next
->GetType() == SmNodeType::Text
&&
407 ( prev
->GetToken().eType
!= TNUMBER
||
408 next
->GetToken().eType
== TNUMBER
) ){
409 SmTextNode
*pText
= static_cast<SmTextNode
*>(prev
),
410 *pOldN
= static_cast<SmTextNode
*>(next
);
411 SmCaretPos
retval(pText
, pText
->GetText().getLength());
412 OUString newText
= pText
->GetText() + pOldN
->GetText();
413 pText
->ChangeText(newText
);
415 pLineList
->erase(aIter
);
419 //Check if there's a SmPlaceNode to remove:
420 if(prev
&& next
&& prev
->GetType() == SmNodeType::Place
&& !SmNodeListParser::IsOperator(next
->GetToken())){
422 aIter
= pLineList
->erase(aIter
);
424 //Return caret pos in front of aIter
425 if(aIter
!= pLineList
->begin())
426 --aIter
; //Thus find node before aIter
427 if(aIter
== pLineList
->begin())
429 return SmCaretPos::GetPosAfter(*aIter
);
431 if(prev
&& next
&& next
->GetType() == SmNodeType::Place
&& !SmNodeListParser::IsOperator(prev
->GetToken())){
432 aIter
= pLineList
->erase(aIter
);
434 return SmCaretPos::GetPosAfter(prev
);
437 //If we didn't do anything return
438 if(!prev
) //return an invalid to indicate we're in front of line
440 return SmCaretPos::GetPosAfter(prev
);
443 SmNodeList::iterator
SmCursor::TakeSelectedNodesFromList(SmNodeList
*pLineList
,
444 SmNodeList
*pSelectedNodes
) {
445 SmNodeList::iterator retval
;
446 SmNodeList::iterator it
= pLineList
->begin();
447 while(it
!= pLineList
->end()){
448 if((*it
)->IsSelected()){
450 if((*it
)->GetType() == SmNodeType::Text
) {
451 SmTextNode
* pText
= static_cast<SmTextNode
*>(*it
);
452 OUString aText
= pText
->GetText();
453 //Start and lengths of the segments, 2 is the selected segment
454 int start2
= pText
->GetSelectionStart(),
455 start3
= pText
->GetSelectionEnd(),
457 len2
= start3
- start2
,
458 len3
= aText
.getLength() - start3
;
459 SmToken aToken
= pText
->GetToken();
460 sal_uInt16 eFontDesc
= pText
->GetFontDesc();
461 //If we need make segment 1
463 OUString str
= aText
.copy(0, len1
);
464 pText
->ChangeText(str
);
466 } else {//Remove it if not needed
467 it
= pLineList
->erase(it
);
470 //Set retval to be right after the selection
472 //if we need make segment 3
474 OUString str
= aText
.copy(start3
, len3
);
475 SmTextNode
* pSeg3
= new SmTextNode(aToken
, eFontDesc
);
476 pSeg3
->ChangeText(str
);
477 retval
= pLineList
->insert(it
, pSeg3
);
479 //If we need to save the selected text
480 if(pSelectedNodes
&& len2
> 0) {
481 OUString str
= aText
.copy(start2
, len2
);
482 SmTextNode
* pSeg2
= new SmTextNode(aToken
, eFontDesc
);
483 pSeg2
->ChangeText(str
);
484 pSelectedNodes
->push_back(pSeg2
);
486 } else { //if it's not textnode
488 retval
= it
= pLineList
->erase(it
);
490 pSelectedNodes
->push_back(pNode
);
500 void SmCursor::InsertSubSup(SmSubSup eSubSup
) {
506 SmNode
*pSNode
= FindSelectedNode(mpTree
);
508 pLine
= FindTopMostNodeInLine(pSNode
, true);
510 pLine
= FindTopMostNodeInLine(mpPosition
->CaretPos
.pSelectedNode
);
512 //Find Parent and offset in parent
513 SmStructureNode
*pLineParent
= pLine
->GetParent();
514 int nParentIndex
= pLineParent
->IndexOfSubNode(pLine
);
515 assert(nParentIndex
>= 0);
517 //TODO: Consider handling special cases where parent is an SmOperNode,
518 // Maybe this method should be able to add limits to an SmOperNode...
520 //We begin modifying the tree here
523 //Convert line to list
524 std::unique_ptr
<SmNodeList
> pLineList(new SmNodeList
);
525 NodeToList(pLine
, *pLineList
);
527 //Take the selection, and/or find iterator for current position
528 std::unique_ptr
<SmNodeList
> pSelectedNodesList(new SmNodeList
);
529 SmNodeList::iterator it
;
531 it
= TakeSelectedNodesFromList(pLineList
.get(), pSelectedNodesList
.get());
533 it
= FindPositionInLineList(pLineList
.get(), mpPosition
->CaretPos
);
535 //Find node that this should be applied to
537 bool bPatchLine
= !pSelectedNodesList
->empty(); //If the line should be patched later
538 if(it
!= pLineList
->begin()) {
543 //Create a new place node
544 pSubject
= new SmPlaceNode();
545 pSubject
->Prepare(mpDocShell
->GetFormat(), *mpDocShell
, 0);
546 it
= pLineList
->insert(it
, pSubject
);
548 bPatchLine
= true; //We've modified the line it should be patched later.
551 //Wrap the subject in a SmSubSupNode
552 SmSubSupNode
* pSubSup
;
553 if(pSubject
->GetType() != SmNodeType::SubSup
){
555 token
.nGroup
= TG::Power
;
556 pSubSup
= new SmSubSupNode(token
);
557 pSubSup
->SetBody(pSubject
);
561 pSubSup
= static_cast<SmSubSupNode
*>(pSubject
);
562 //pSubject shouldn't be referenced anymore, pSubSup is the SmSubSupNode in pLineList we wish to edit.
563 //and it pointer to the element following pSubSup in pLineList.
566 //Patch the line if we noted that was needed previously
568 PatchLineList(pLineList
.get(), it
);
570 //Convert existing, if any, sub-/superscript line to list
571 SmNode
*pScriptLine
= pSubSup
->GetSubSup(eSubSup
);
572 std::unique_ptr
<SmNodeList
> pScriptLineList(new SmNodeList
);
573 NodeToList(pScriptLine
, *pScriptLineList
);
575 //Add selection to pScriptLineList
576 unsigned int nOldSize
= pScriptLineList
->size();
577 pScriptLineList
->insert(pScriptLineList
->end(), pSelectedNodesList
->begin(), pSelectedNodesList
->end());
578 pSelectedNodesList
.reset();
580 //Patch pScriptLineList if needed
581 if(0 < nOldSize
&& nOldSize
< pScriptLineList
->size()) {
582 SmNodeList::iterator iPatchPoint
= pScriptLineList
->begin();
583 std::advance(iPatchPoint
, nOldSize
);
584 PatchLineList(pScriptLineList
.get(), iPatchPoint
);
587 //Find caret pos, that should be used after sub-/superscription.
588 SmCaretPos PosAfterScript
; //Leave invalid for first position
589 if (!pScriptLineList
->empty())
590 PosAfterScript
= SmCaretPos::GetPosAfter(pScriptLineList
->back());
592 //Parse pScriptLineList
593 pScriptLine
= SmNodeListParser().Parse(pScriptLineList
.get());
594 pScriptLineList
.reset();
596 //Insert pScriptLine back into the tree
597 pSubSup
->SetSubSup(eSubSup
, pScriptLine
);
600 FinishEdit(std::move(pLineList
), pLineParent
, nParentIndex
, PosAfterScript
, pScriptLine
);
603 void SmCursor::InsertBrackets(SmBracketType eBracketType
) {
611 SmNode
*pSNode
= FindSelectedNode(mpTree
);
613 pLine
= FindTopMostNodeInLine(pSNode
, true);
615 pLine
= FindTopMostNodeInLine(mpPosition
->CaretPos
.pSelectedNode
);
617 //Find parent and offset in parent
618 SmStructureNode
*pLineParent
= pLine
->GetParent();
619 int nParentIndex
= pLineParent
->IndexOfSubNode(pLine
);
620 assert(nParentIndex
>= 0);
622 //Convert line to list
623 std::unique_ptr
<SmNodeList
> pLineList(new SmNodeList
);
624 NodeToList(pLine
, *pLineList
);
626 //Take the selection, and/or find iterator for current position
627 std::unique_ptr
<SmNodeList
> pSelectedNodesList(new SmNodeList
);
628 SmNodeList::iterator it
;
630 it
= TakeSelectedNodesFromList(pLineList
.get(), pSelectedNodesList
.get());
632 it
= FindPositionInLineList(pLineList
.get(), mpPosition
->CaretPos
);
634 //If there's no selected nodes, create a place node
635 std::unique_ptr
<SmNode
> pBodyNode
;
636 SmCaretPos PosAfterInsert
;
637 if(pSelectedNodesList
->empty()) {
638 pBodyNode
.reset(new SmPlaceNode());
639 PosAfterInsert
= SmCaretPos(pBodyNode
.get(), 1);
641 pBodyNode
.reset(SmNodeListParser().Parse(pSelectedNodesList
.get()));
643 pSelectedNodesList
.reset();
646 SmToken
aTok(TLEFT
, '\0', u
"left"_ustr
, TG::NONE
, 5);
647 SmBraceNode
*pBrace
= new SmBraceNode(aTok
);
648 pBrace
->SetScaleMode(SmScaleMode::Height
);
649 std::unique_ptr
<SmNode
> pLeft( CreateBracket(eBracketType
, true) ),
650 pRight( CreateBracket(eBracketType
, false) );
651 std::unique_ptr
<SmBracebodyNode
> pBody(new SmBracebodyNode(SmToken()));
652 pBody
->SetSubNodes(std::move(pBodyNode
), nullptr);
653 pBrace
->SetSubNodes(std::move(pLeft
), std::move(pBody
), std::move(pRight
));
654 pBrace
->Prepare(mpDocShell
->GetFormat(), *mpDocShell
, 0);
657 pLineList
->insert(it
, pBrace
);
658 //Patch line (I think this is good enough)
659 SmCaretPos aAfter
= PatchLineList(pLineList
.get(), it
);
660 if( !PosAfterInsert
.IsValid() )
661 PosAfterInsert
= aAfter
;
664 FinishEdit(std::move(pLineList
), pLineParent
, nParentIndex
, PosAfterInsert
);
667 SmNode
*SmCursor::CreateBracket(SmBracketType eBracketType
, bool bIsLeft
) {
670 switch(eBracketType
){
671 case SmBracketType::Round
:
672 aTok
= SmToken(TLPARENT
, MS_LPARENT
, u
"("_ustr
, TG::LBrace
, 5);
674 case SmBracketType::Square
:
675 aTok
= SmToken(TLBRACKET
, MS_LBRACKET
, u
"["_ustr
, TG::LBrace
, 5);
677 case SmBracketType::Curly
:
678 aTok
= SmToken(TLBRACE
, MS_LBRACE
, u
"lbrace"_ustr
, TG::LBrace
, 5);
682 switch(eBracketType
) {
683 case SmBracketType::Round
:
684 aTok
= SmToken(TRPARENT
, MS_RPARENT
, u
")"_ustr
, TG::RBrace
, 5);
686 case SmBracketType::Square
:
687 aTok
= SmToken(TRBRACKET
, MS_RBRACKET
, u
"]"_ustr
, TG::RBrace
, 5);
689 case SmBracketType::Curly
:
690 aTok
= SmToken(TRBRACE
, MS_RBRACE
, u
"rbrace"_ustr
, TG::RBrace
, 5);
694 SmNode
* pRetVal
= new SmMathSymbolNode(aTok
);
695 pRetVal
->SetScaleMode(SmScaleMode::Height
);
699 bool SmCursor::InsertRow() {
705 SmNode
*pSNode
= FindSelectedNode(mpTree
);
707 pLine
= FindTopMostNodeInLine(pSNode
, true);
709 pLine
= FindTopMostNodeInLine(mpPosition
->CaretPos
.pSelectedNode
);
711 //Find parent and offset in parent
712 SmStructureNode
*pLineParent
= pLine
->GetParent();
713 int nParentIndex
= pLineParent
->IndexOfSubNode(pLine
);
714 assert(nParentIndex
>= 0 && nParentIndex
< INT_MAX
);
716 //Discover the context of this command
717 SmTableNode
*pTable
= nullptr;
718 SmMatrixNode
*pMatrix
= nullptr;
719 int nTableIndex
= nParentIndex
;
720 if(pLineParent
->GetType() == SmNodeType::Table
)
721 pTable
= static_cast<SmTableNode
*>(pLineParent
);
722 //If it's wrapped in a SmLineNode, we can still insert a newline
723 else if(pLineParent
->GetType() == SmNodeType::Line
&&
724 pLineParent
->GetParent() &&
725 pLineParent
->GetParent()->GetType() == SmNodeType::Table
) {
726 //NOTE: This hack might give problems if we stop ignoring SmAlignNode
727 pTable
= static_cast<SmTableNode
*>(pLineParent
->GetParent());
728 nTableIndex
= pTable
->IndexOfSubNode(pLineParent
);
729 assert(nTableIndex
>= 0 && nTableIndex
< INT_MAX
);
731 if(pLineParent
->GetType() == SmNodeType::Matrix
)
732 pMatrix
= static_cast<SmMatrixNode
*>(pLineParent
);
734 //If we're not in a context that supports InsertRow, return sal_False
735 if(!pTable
&& !pMatrix
)
738 //Now we start editing
741 //Convert line to list
742 std::unique_ptr
<SmNodeList
> pLineList(new SmNodeList
);
743 NodeToList(pLine
, *pLineList
);
745 //Find position in line
746 SmNodeList::iterator it
;
748 //Take the selected nodes and delete them...
749 it
= TakeSelectedNodesFromList(pLineList
.get());
751 it
= FindPositionInLineList(pLineList
.get(), mpPosition
->CaretPos
);
753 //New caret position after inserting the newline/row in whatever context
754 SmCaretPos PosAfterInsert
;
756 //If we're in the context of a table
758 std::unique_ptr
<SmNodeList
> pNewLineList(new SmNodeList
);
759 //Move elements from pLineList to pNewLineList
760 SmNodeList
& rLineList
= *pLineList
;
761 pNewLineList
->splice(pNewLineList
->begin(), rLineList
, it
, rLineList
.end());
762 //Make sure it is valid again
763 it
= pLineList
->end();
764 if(it
!= pLineList
->begin())
766 if(pNewLineList
->empty())
767 pNewLineList
->push_front(new SmPlaceNode());
769 std::unique_ptr
<SmNode
> pNewLine(SmNodeListParser().Parse(pNewLineList
.get()));
770 pNewLineList
.reset();
771 //Wrap pNewLine in SmLineNode if needed
772 if(pLineParent
->GetType() == SmNodeType::Line
) {
773 std::unique_ptr
<SmLineNode
> pNewLineNode(new SmLineNode(SmToken(TNEWLINE
, '\0', u
"newline"_ustr
)));
774 pNewLineNode
->SetSubNodes(std::move(pNewLine
), nullptr);
775 pNewLine
= std::move(pNewLineNode
);
778 PosAfterInsert
= SmCaretPos(pNewLine
.get(), 0);
779 //Move other nodes if needed
780 for( int i
= pTable
->GetNumSubNodes(); i
> nTableIndex
+ 1; i
--)
781 pTable
->SetSubNode(i
, pTable
->GetSubNode(i
-1));
784 pTable
->SetSubNode(nTableIndex
+ 1, pNewLine
.release());
786 //Check if we need to change token type:
787 if(pTable
->GetNumSubNodes() > 2 && pTable
->GetToken().eType
== TBINOM
) {
788 SmToken tok
= pTable
->GetToken();
790 pTable
->SetToken(tok
);
793 //If we're in the context of a matrix
795 //Find position after insert and patch the list
796 PosAfterInsert
= PatchLineList(pLineList
.get(), it
);
797 //Move other children
798 sal_uInt16 rows
= pMatrix
->GetNumRows();
799 sal_uInt16 cols
= pMatrix
->GetNumCols();
800 int nRowStart
= (nParentIndex
- nParentIndex
% cols
) + cols
;
801 for( int i
= pMatrix
->GetNumSubNodes() + cols
- 1; i
>= nRowStart
+ cols
; i
--)
802 pMatrix
->SetSubNode(i
, pMatrix
->GetSubNode(i
- cols
));
803 for( int i
= nRowStart
; i
< nRowStart
+ cols
; i
++) {
804 SmPlaceNode
*pNewLine
= new SmPlaceNode();
805 if(i
== nParentIndex
+ cols
)
806 PosAfterInsert
= SmCaretPos(pNewLine
, 0);
807 pMatrix
->SetSubNode(i
, pNewLine
);
809 pMatrix
->SetRowCol(rows
+ 1, cols
);
813 FinishEdit(std::move(pLineList
), pLineParent
, nParentIndex
, PosAfterInsert
);
814 //FinishEdit is actually used to handle situations where parent is an instance of
815 //SmSubSupNode. In this case parent should always be a table or matrix, however, for
816 //code reuse we just use FinishEdit() here too.
820 void SmCursor::InsertFraction() {
826 SmNode
*pSNode
= FindSelectedNode(mpTree
);
828 pLine
= FindTopMostNodeInLine(pSNode
, true);
830 pLine
= FindTopMostNodeInLine(mpPosition
->CaretPos
.pSelectedNode
);
832 //Find Parent and offset in parent
833 SmStructureNode
*pLineParent
= pLine
->GetParent();
834 int nParentIndex
= pLineParent
->IndexOfSubNode(pLine
);
835 assert(nParentIndex
>= 0);
837 //We begin modifying the tree here
840 //Convert line to list
841 std::unique_ptr
<SmNodeList
> pLineList(new SmNodeList
);
842 NodeToList(pLine
, *pLineList
);
844 //Take the selection, and/or find iterator for current position
845 std::unique_ptr
<SmNodeList
> pSelectedNodesList(new SmNodeList
);
846 SmNodeList::iterator it
;
848 it
= TakeSelectedNodesFromList(pLineList
.get(), pSelectedNodesList
.get());
850 it
= FindPositionInLineList(pLineList
.get(), mpPosition
->CaretPos
);
852 //Create pNum, and pDenom
853 bool bEmptyFraction
= pSelectedNodesList
->empty();
854 std::unique_ptr
<SmNode
> pNum( bEmptyFraction
856 : SmNodeListParser().Parse(pSelectedNodesList
.get()) );
857 std::unique_ptr
<SmNode
> pDenom(new SmPlaceNode());
858 pSelectedNodesList
.reset();
860 //Create new fraction
861 SmBinVerNode
*pFrac
= new SmBinVerNode(SmToken(TOVER
, '\0', u
"over"_ustr
, TG::Product
, 0));
862 std::unique_ptr
<SmNode
> pRect(new SmRectangleNode(SmToken()));
863 pFrac
->SetSubNodes(std::move(pNum
), std::move(pRect
), std::move(pDenom
));
865 //Insert in pLineList
866 SmNodeList::iterator patchIt
= pLineList
->insert(it
, pFrac
);
867 PatchLineList(pLineList
.get(), patchIt
);
868 PatchLineList(pLineList
.get(), it
);
871 SmNode
*pSelectedNode
= bEmptyFraction
? pFrac
->GetSubNode(0) : pFrac
->GetSubNode(2);
872 FinishEdit(std::move(pLineList
), pLineParent
, nParentIndex
, SmCaretPos(pSelectedNode
, 1));
875 void SmCursor::InsertText(const OUString
& aString
)
882 token
.eType
= TIDENT
;
883 token
.cMathChar
= u
""_ustr
;
884 token
.nGroup
= TG::NONE
;
886 token
.aText
= aString
;
888 SmTextNode
* pText
= new SmTextNode(token
, FNT_VARIABLE
);
889 pText
->SetText(aString
);
890 pText
->AdjustFontDesc();
891 pText
->Prepare(mpDocShell
->GetFormat(), *mpDocShell
, 0);
893 std::unique_ptr
<SmNodeList
> pList(new SmNodeList
);
894 pList
->push_front(pText
);
895 InsertNodes(std::move(pList
));
900 void SmCursor::InsertElement(SmFormulaElement element
){
906 SmNode
* pNewNode
= nullptr;
911 token
.eType
= TBLANK
;
912 token
.nGroup
= TG::Blank
;
914 SmBlankNode
* pBlankNode
= new SmBlankNode(token
);
915 pBlankNode
->IncreaseBy(token
);
916 pNewNode
= pBlankNode
;
918 case FactorialElement
:
920 SmToken
token(TFACT
, MS_FACT
, u
"fact"_ustr
, TG::UnOper
, 5);
921 pNewNode
= new SmMathSymbolNode(token
);
927 token
.setChar(MS_PLUS
);
928 token
.nGroup
= TG::UnOper
| TG::Sum
;
931 pNewNode
= new SmMathSymbolNode(token
);
936 token
.eType
= TMINUS
;
937 token
.setChar(MS_MINUS
);
938 token
.nGroup
= TG::UnOper
| TG::Sum
;
941 pNewNode
= new SmMathSymbolNode(token
);
947 token
.setChar(MS_CDOT
);
948 token
.nGroup
= TG::Product
;
949 token
.aText
= "cdot";
950 pNewNode
= new SmMathSymbolNode(token
);
955 token
.eType
= TASSIGN
;
956 token
.setChar(MS_ASSIGN
);
957 token
.nGroup
= TG::Relation
;
959 pNewNode
= new SmMathSymbolNode(token
);
961 case LessThanElement
:
965 token
.setChar(MS_LT
);
966 token
.nGroup
= TG::Relation
;
968 pNewNode
= new SmMathSymbolNode(token
);
970 case GreaterThanElement
:
974 token
.setChar(MS_GT
);
975 token
.nGroup
= TG::Relation
;
977 pNewNode
= new SmMathSymbolNode(token
);
983 token
.setChar(MS_PERCENT
);
984 token
.nGroup
= TG::NONE
;
985 token
.aText
= "\"%\"";
986 pNewNode
= new SmMathSymbolNode(token
);
991 //Prepare the new node
992 pNewNode
->Prepare(mpDocShell
->GetFormat(), *mpDocShell
, 0);
995 std::unique_ptr
<SmNodeList
> pList(new SmNodeList
);
996 pList
->push_front(pNewNode
);
997 InsertNodes(std::move(pList
));
1002 void SmCursor::InsertSpecial(std::u16string_view _aString
)
1007 OUString
aString( comphelper::string::strip(_aString
, ' ') );
1009 //Create instance of special node
1011 token
.eType
= TSPECIAL
;
1012 token
.cMathChar
= u
""_ustr
;
1013 token
.nGroup
= TG::NONE
;
1015 token
.aText
= aString
;
1016 SmSpecialNode
* pSpecial
= new SmSpecialNode(token
);
1018 //Prepare the special node
1019 pSpecial
->Prepare(mpDocShell
->GetFormat(), *mpDocShell
, 0);
1022 std::unique_ptr
<SmNodeList
> pList(new SmNodeList
);
1023 pList
->push_front(pSpecial
);
1024 InsertNodes(std::move(pList
));
1029 void SmCursor::InsertCommandText(const OUString
& aCommandText
) {
1030 //Parse the sub expression
1031 auto xSubExpr
= mpDocShell
->GetParser()->ParseExpression(aCommandText
);
1033 //Prepare the subtree
1034 xSubExpr
->Prepare(mpDocShell
->GetFormat(), *mpDocShell
, 0);
1036 //Convert subtree to list
1037 SmNode
* pSubExpr
= xSubExpr
.release();
1038 std::unique_ptr
<SmNodeList
> pLineList(new SmNodeList
);
1039 NodeToList(pSubExpr
, *pLineList
);
1043 //Delete any selection
1047 InsertNodes(std::move(pLineList
));
1052 void SmCursor::Copy(vcl::Window
* pWindow
)
1057 AnnotateSelection();
1058 //Find selected node
1059 SmNode
* pSNode
= FindSelectedNode(mpTree
);
1062 SmNode
* pLine
= FindTopMostNodeInLine(pSNode
, true);
1065 //Clone selected nodes
1066 // TODO: Simplify all this cloning since we only need a formula string now.
1067 SmClipboard aClipboard
;
1068 if(IsLineCompositionNode(pLine
))
1069 CloneLineToClipboard(static_cast<SmStructureNode
*>(pLine
), &aClipboard
);
1071 //Special care to only clone selected text
1072 if(pLine
->GetType() == SmNodeType::Text
) {
1073 SmTextNode
*pText
= static_cast<SmTextNode
*>(pLine
);
1074 std::unique_ptr
<SmTextNode
> pClone(new SmTextNode( pText
->GetToken(), pText
->GetFontDesc() ));
1075 int start
= pText
->GetSelectionStart(),
1076 length
= pText
->GetSelectionEnd() - pText
->GetSelectionStart();
1077 pClone
->ChangeText(pText
->GetText().copy(start
, length
));
1078 pClone
->SetScaleMode(pText
->GetScaleMode());
1079 aClipboard
.push_front(std::move(pClone
));
1081 SmCloningVisitor aCloneFactory
;
1082 aClipboard
.push_front(std::unique_ptr
<SmNode
>(aCloneFactory
.Clone(pLine
)));
1086 // Parse list of nodes to a tree
1087 SmNodeListParser parser
;
1088 SmNode
* pTree(parser
.Parse(CloneList(aClipboard
).get()));
1090 // Parse the tree to a string
1092 SmNodeToTextVisitor(pTree
, aString
);
1095 auto xClipboard(pWindow
? pWindow
->GetClipboard() : GetSystemClipboard());
1096 vcl::unohelper::TextDataObject::CopyStringTo(aString
, xClipboard
);
1099 void SmCursor::Paste(vcl::Window
* pWindow
)
1104 auto xClipboard(pWindow
? pWindow
->GetClipboard() : GetSystemClipboard());
1105 auto aDataHelper(TransferableDataHelper::CreateFromClipboard(xClipboard
));
1106 if (aDataHelper
.GetTransferable().is())
1108 // TODO: Support MATHML
1109 auto nId
= SotClipboardFormatId::STRING
;
1110 if (aDataHelper
.HasFormat(nId
))
1113 if (aDataHelper
.GetString(nId
, aString
))
1114 InsertCommandText(aString
);
1121 std::unique_ptr
<SmNodeList
> SmCursor::CloneList(SmClipboard
&rClipboard
){
1122 SmCloningVisitor aCloneFactory
;
1123 std::unique_ptr
<SmNodeList
> pClones(new SmNodeList
);
1125 for(auto &xNode
: rClipboard
){
1126 SmNode
*pClone
= aCloneFactory
.Clone(xNode
.get());
1127 pClones
->push_back(pClone
);
1133 SmNode
* SmCursor::FindTopMostNodeInLine(SmNode
* pSNode
, bool MoveUpIfSelected
){
1135 //Move up parent until we find a node who's
1136 //parent is NULL or isn't selected and not a type of:
1143 while(pSNode
->GetParent() &&
1144 ((MoveUpIfSelected
&&
1145 pSNode
->GetParent()->IsSelected()) ||
1146 IsLineCompositionNode(pSNode
->GetParent())))
1147 pSNode
= pSNode
->GetParent();
1148 //Now we have the selection line node
1152 SmNode
* SmCursor::FindSelectedNode(SmNode
* pNode
){
1153 if(pNode
->GetNumSubNodes() == 0)
1155 for(auto pChild
: *static_cast<SmStructureNode
*>(pNode
))
1159 if(pChild
->IsSelected())
1161 SmNode
* pRetVal
= FindSelectedNode(pChild
);
1168 void SmCursor::LineToList(SmStructureNode
* pLine
, SmNodeList
& list
){
1169 for(auto pChild
: *pLine
)
1173 switch(pChild
->GetType()){
1174 case SmNodeType::Line
:
1175 case SmNodeType::UnHor
:
1176 case SmNodeType::Expression
:
1177 case SmNodeType::BinHor
:
1178 case SmNodeType::Align
:
1179 case SmNodeType::Font
:
1180 LineToList(static_cast<SmStructureNode
*>(pChild
), list
);
1182 case SmNodeType::Error
:
1186 list
.push_back(pChild
);
1189 pLine
->ClearSubNodes();
1193 void SmCursor::CloneLineToClipboard(SmStructureNode
* pLine
, SmClipboard
* pClipboard
){
1194 SmCloningVisitor aCloneFactory
;
1195 for(auto pChild
: *pLine
)
1199 if( IsLineCompositionNode( pChild
) )
1200 CloneLineToClipboard( static_cast<SmStructureNode
*>(pChild
), pClipboard
);
1201 else if( pChild
->IsSelected() && pChild
->GetType() != SmNodeType::Error
) {
1202 //Only clone selected text from SmTextNode
1203 if(pChild
->GetType() == SmNodeType::Text
) {
1204 SmTextNode
*pText
= static_cast<SmTextNode
*>(pChild
);
1205 std::unique_ptr
<SmTextNode
> pClone(new SmTextNode( pChild
->GetToken(), pText
->GetFontDesc() ));
1206 int start
= pText
->GetSelectionStart(),
1207 length
= pText
->GetSelectionEnd() - pText
->GetSelectionStart();
1208 pClone
->ChangeText(pText
->GetText().copy(start
, length
));
1209 pClone
->SetScaleMode(pText
->GetScaleMode());
1210 pClipboard
->push_back(std::move(pClone
));
1212 pClipboard
->push_back(std::unique_ptr
<SmNode
>(aCloneFactory
.Clone(pChild
)));
1217 bool SmCursor::IsLineCompositionNode(SmNode
const * pNode
){
1218 switch(pNode
->GetType()){
1219 case SmNodeType::Line
:
1220 case SmNodeType::UnHor
:
1221 case SmNodeType::Expression
:
1222 case SmNodeType::BinHor
:
1223 case SmNodeType::Align
:
1224 case SmNodeType::Font
:
1231 int SmCursor::CountSelectedNodes(SmNode
* pNode
){
1232 if(pNode
->GetNumSubNodes() == 0)
1235 for(auto pChild
: *static_cast<SmStructureNode
*>(pNode
))
1239 if(pChild
->IsSelected() && !IsLineCompositionNode(pChild
))
1241 nCount
+= CountSelectedNodes(pChild
);
1246 bool SmCursor::HasComplexSelection(){
1249 AnnotateSelection();
1251 return CountSelectedNodes(mpTree
) > 1;
1254 void SmCursor::FinishEdit(std::unique_ptr
<SmNodeList
> pLineList
,
1255 SmStructureNode
* pParent
,
1257 SmCaretPos PosAfterEdit
,
1258 SmNode
* pStartLine
) {
1259 //Store number of nodes in line for later
1260 int entries
= pLineList
->size();
1262 //Parse list of nodes to a tree
1263 SmNodeListParser parser
;
1264 std::unique_ptr
<SmNode
> pLine(parser
.Parse(pLineList
.get()));
1267 //Check if we're making the body of a subsup node bigger than one
1268 if(pParent
->GetType() == SmNodeType::SubSup
&&
1269 nParentIndex
== 0 &&
1271 //Wrap pLine in scalable round brackets
1272 SmToken
aTok(TLEFT
, '\0', u
"left"_ustr
, TG::NONE
, 5);
1273 std::unique_ptr
<SmBraceNode
> pBrace(new SmBraceNode(aTok
));
1274 pBrace
->SetScaleMode(SmScaleMode::Height
);
1275 std::unique_ptr
<SmNode
> pLeft( CreateBracket(SmBracketType::Round
, true) ),
1276 pRight( CreateBracket(SmBracketType::Round
, false) );
1277 std::unique_ptr
<SmBracebodyNode
> pBody(new SmBracebodyNode(SmToken()));
1278 pBody
->SetSubNodes(std::move(pLine
), nullptr);
1279 pBrace
->SetSubNodes(std::move(pLeft
), std::move(pBody
), std::move(pRight
));
1280 pBrace
->Prepare(mpDocShell
->GetFormat(), *mpDocShell
, 0);
1281 pLine
= std::move(pBrace
);
1282 //TODO: Consider the following alternative behavior:
1283 //Consider the line: A + {B + C}^D lsub E
1284 //Here pLineList is B, + and C and pParent is a subsup node with
1285 //both RSUP and LSUB set. Imagine the user just inserted "B +" in
1286 //the body of the subsup node...
1287 //The most natural thing to do would be to make the line like this:
1288 //A + B lsub E + C ^ D
1289 //E.g. apply LSUB and LSUP to the first element in pLineList and RSUP
1290 //and RSUB to the last element in pLineList. But how should this act
1291 //for CSUP and CSUB ???
1292 //For this reason and because brackets was faster to implement, this solution
1293 //have been chosen. It might be worth working on the other solution later...
1296 //Set pStartLine if NULL
1298 pStartLine
= pLine
.get();
1300 //Insert it back into the parent
1301 pParent
->SetSubNode(nParentIndex
, pLine
.release());
1303 //Rebuild graph of caret position
1305 mpPosition
= nullptr;
1307 AnnotateSelection(); //Update selection annotation!
1309 //Set caret position
1310 if(!SetCaretPosition(PosAfterEdit
))
1311 SetCaretPosition(SmCaretPos(pStartLine
, 0));
1317 void SmCursor::BeginEdit(){
1318 if(mnEditSections
++ > 0) return;
1320 mbIsEnabledSetModifiedSmDocShell
= mpDocShell
->IsEnableSetModified();
1321 if( mbIsEnabledSetModifiedSmDocShell
)
1322 mpDocShell
->EnableSetModified( false );
1325 void SmCursor::EndEdit(){
1326 if(--mnEditSections
> 0) return;
1328 mpDocShell
->SetFormulaArranged(false);
1329 //Okay, I don't know what this does... :)
1330 //It's used in SmDocShell::SetText and with places where everything is modified.
1331 //I think it does some magic, with sfx, but everything is totally undocumented so
1332 //it's kinda hard to tell...
1333 if ( mbIsEnabledSetModifiedSmDocShell
)
1334 mpDocShell
->EnableSetModified( mbIsEnabledSetModifiedSmDocShell
);
1335 //I think this notifies people around us that we've modified this document...
1336 mpDocShell
->SetModified();
1337 //I think SmDocShell uses this value when it sends an update graphics event
1338 //Anyway comments elsewhere suggests it needs to be updated...
1339 mpDocShell
->mnModifyCount
++;
1341 //TODO: Consider copying the update accessibility code from SmDocShell::SetText in here...
1342 //This somehow updates the size of SmGraphicView if it is running in embedded mode
1343 if( mpDocShell
->GetCreateMode() == SfxObjectCreateMode::EMBEDDED
)
1344 mpDocShell
->OnDocumentPrinterChanged(nullptr);
1346 //Request a repaint...
1349 //Update the edit engine and text of the document
1351 SmNodeToTextVisitor(mpTree
, formula
);
1352 mpDocShell
->maText
= formula
;
1353 mpDocShell
->GetEditEngine().QuickInsertText(formula
, ESelection::All());
1354 mpDocShell
->GetEditEngine().QuickFormatDoc();
1357 void SmCursor::RequestRepaint()
1359 if (SmViewShell
*pViewSh
= SmGetActiveView())
1361 if (comphelper::LibreOfficeKit::isActive())
1363 pViewSh
->SendCaretToLOK();
1365 else if ( SfxObjectCreateMode::EMBEDDED
== mpDocShell
->GetCreateMode() )
1366 mpDocShell
->Repaint();
1368 pViewSh
->GetGraphicWidget().Invalidate();
1372 bool SmCursor::IsAtTailOfBracket(SmBracketType eBracketType
) const
1374 const SmCaretPos pos
= GetPosition();
1375 if (!pos
.IsValid()) {
1379 SmNode
* pNode
= pos
.pSelectedNode
;
1381 if (pNode
->GetType() == SmNodeType::Text
) {
1382 SmTextNode
* pTextNode
= static_cast<SmTextNode
*>(pNode
);
1383 if (pos
.nIndex
< pTextNode
->GetText().getLength()) {
1384 // The cursor is on a text node and at the middle of it.
1388 if (pos
.nIndex
< 1) {
1394 SmStructureNode
* pParentNode
= pNode
->GetParent();
1396 // There's no brace body node in the ancestors.
1400 int index
= pParentNode
->IndexOfSubNode(pNode
);
1402 if (static_cast<size_t>(index
+ 1) != pParentNode
->GetNumSubNodes()) {
1403 // The cursor is not at the tail at one of ancestor nodes.
1407 pNode
= pParentNode
;
1408 if (pNode
->GetType() == SmNodeType::Bracebody
) {
1409 // Found the brace body node.
1414 SmStructureNode
* pBraceNodeTmp
= pNode
->GetParent();
1415 if (!pBraceNodeTmp
|| pBraceNodeTmp
->GetType() != SmNodeType::Brace
) {
1416 // Brace node is invalid.
1420 SmBraceNode
* pBraceNode
= static_cast<SmBraceNode
*>(pBraceNodeTmp
);
1421 SmMathSymbolNode
* pClosingNode
= pBraceNode
->ClosingBrace();
1422 if (!pClosingNode
) {
1423 // Couldn't get closing symbol node.
1427 // Check if the closing brace matches eBracketType.
1428 SmTokenType eClosingTokenType
= pClosingNode
->GetToken().eType
;
1429 switch (eBracketType
) {
1430 case SmBracketType::Round
: if (eClosingTokenType
!= TRPARENT
) { return false; } break;
1431 case SmBracketType::Square
: if (eClosingTokenType
!= TRBRACKET
) { return false; } break;
1432 case SmBracketType::Curly
: if (eClosingTokenType
!= TRBRACE
) { return false; } break;
1440 /////////////////////////////////////// SmNodeListParser
1442 SmNode
* SmNodeListParser::Parse(SmNodeList
* list
){
1444 //Delete error nodes
1445 SmNodeList::iterator it
= pList
->begin();
1446 while(it
!= pList
->end()) {
1447 if((*it
)->GetType() == SmNodeType::Error
){
1450 it
= pList
->erase(it
);
1454 SmNode
* retval
= Expression();
1459 SmNode
* SmNodeListParser::Expression(){
1460 SmNodeArray NodeArray
;
1461 //Accept as many relations as there is
1463 NodeArray
.push_back(Relation());
1465 //Create SmExpressionNode, I hope SmToken() will do :)
1466 SmStructureNode
* pExpr
= new SmExpressionNode(SmToken());
1467 pExpr
->SetSubNodes(std::move(NodeArray
));
1471 SmNode
* SmNodeListParser::Relation(){
1473 std::unique_ptr
<SmNode
> pLeft(Sum());
1474 //While we have tokens and the next is a relation
1475 while(Terminal() && IsRelationOperator(Terminal()->GetToken())){
1477 std::unique_ptr
<SmNode
> pOper(Take());
1478 //Find the right side of the relation
1479 std::unique_ptr
<SmNode
> pRight(Sum());
1480 //Create new SmBinHorNode
1481 std::unique_ptr
<SmStructureNode
> pNewNode(new SmBinHorNode(SmToken()));
1482 pNewNode
->SetSubNodes(std::move(pLeft
), std::move(pOper
), std::move(pRight
));
1483 pLeft
= std::move(pNewNode
);
1485 return pLeft
.release();
1488 SmNode
* SmNodeListParser::Sum(){
1490 std::unique_ptr
<SmNode
> pLeft(Product());
1491 //While we have tokens and the next is a sum
1492 while(Terminal() && IsSumOperator(Terminal()->GetToken())){
1494 std::unique_ptr
<SmNode
> pOper(Take());
1495 //Find the right side of the sum
1496 std::unique_ptr
<SmNode
> pRight(Product());
1497 //Create new SmBinHorNode
1498 std::unique_ptr
<SmStructureNode
> pNewNode(new SmBinHorNode(SmToken()));
1499 pNewNode
->SetSubNodes(std::move(pLeft
), std::move(pOper
), std::move(pRight
));
1500 pLeft
= std::move(pNewNode
);
1502 return pLeft
.release();
1505 SmNode
* SmNodeListParser::Product(){
1507 std::unique_ptr
<SmNode
> pLeft(Factor());
1508 //While we have tokens and the next is a product
1509 while(Terminal() && IsProductOperator(Terminal()->GetToken())){
1511 std::unique_ptr
<SmNode
> pOper(Take());
1512 //Find the right side of the operation
1513 std::unique_ptr
<SmNode
> pRight(Factor());
1514 //Create new SmBinHorNode
1515 std::unique_ptr
<SmStructureNode
> pNewNode(new SmBinHorNode(SmToken()));
1516 pNewNode
->SetSubNodes(std::move(pLeft
), std::move(pOper
), std::move(pRight
));
1517 pLeft
= std::move(pNewNode
);
1519 return pLeft
.release();
1522 SmNode
* SmNodeListParser::Factor(){
1523 //Read unary operations
1526 //Take care of unary operators
1527 else if(IsUnaryOperator(Terminal()->GetToken()))
1529 SmStructureNode
*pUnary
= new SmUnHorNode(SmToken());
1530 std::unique_ptr
<SmNode
> pOper(Terminal()),
1534 pArg
.reset(Factor());
1536 pArg
.reset(Error());
1538 pUnary
->SetSubNodes(std::move(pOper
), std::move(pArg
));
1544 SmNode
* SmNodeListParser::Postfix(){
1547 std::unique_ptr
<SmNode
> pArg
;
1548 if(IsPostfixOperator(Terminal()->GetToken()))
1549 pArg
.reset(Error());
1550 else if(IsOperator(Terminal()->GetToken()))
1554 while(Terminal() && IsPostfixOperator(Terminal()->GetToken())) {
1555 std::unique_ptr
<SmStructureNode
> pUnary(new SmUnHorNode(SmToken()) );
1556 std::unique_ptr
<SmNode
> pOper(Take());
1557 pUnary
->SetSubNodes(std::move(pArg
), std::move(pOper
));
1558 pArg
= std::move(pUnary
);
1560 return pArg
.release();
1563 SmNode
* SmNodeListParser::Error(){
1564 return new SmErrorNode(SmToken());
1567 bool SmNodeListParser::IsOperator(const SmToken
&token
) {
1568 return IsRelationOperator(token
) ||
1569 IsSumOperator(token
) ||
1570 IsProductOperator(token
) ||
1571 IsUnaryOperator(token
) ||
1572 IsPostfixOperator(token
);
1575 bool SmNodeListParser::IsRelationOperator(const SmToken
&token
) {
1576 return bool(token
.nGroup
& TG::Relation
);
1579 bool SmNodeListParser::IsSumOperator(const SmToken
&token
) {
1580 return bool(token
.nGroup
& TG::Sum
);
1583 bool SmNodeListParser::IsProductOperator(const SmToken
&token
) {
1584 return token
.nGroup
& TG::Product
&&
1585 token
.eType
!= TWIDESLASH
&&
1586 token
.eType
!= TWIDEBACKSLASH
&&
1587 token
.eType
!= TUNDERBRACE
&&
1588 token
.eType
!= TOVERBRACE
&&
1589 token
.eType
!= TOVER
;
1592 bool SmNodeListParser::IsUnaryOperator(const SmToken
&token
) {
1593 return token
.nGroup
& TG::UnOper
&&
1594 (token
.eType
== TPLUS
||
1595 token
.eType
== TMINUS
||
1596 token
.eType
== TPLUSMINUS
||
1597 token
.eType
== TMINUSPLUS
||
1598 token
.eType
== TNEG
||
1599 token
.eType
== TUOPER
);
1602 bool SmNodeListParser::IsPostfixOperator(const SmToken
&token
) {
1603 return token
.eType
== TFACT
;
1606 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */