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 "accessibility.hxx"
14 #include <comphelper/string.hxx>
16 void SmCursor::Move(OutputDevice
* pDev
, SmMovementDirection direction
, bool bMoveAnchor
){
17 SmCaretPosGraphEntry
* NewPos
= NULL
;
21 NewPos
= position
->Left
;
22 OSL_ENSURE(NewPos
, "NewPos shouldn't be NULL here!");
26 NewPos
= position
->Right
;
27 OSL_ENSURE(NewPos
, "NewPos shouldn't be NULL here!");
30 //Implementation is practically identical to MoveDown, except for a single if statement
31 //so I've implemented them together and added a direction == MoveDown to the if statements.
34 SmCaretLine from_line
= SmCaretPos2LineVisitor(pDev
, position
->CaretPos
).GetResult(),
35 best_line
, //Best approximated line found so far
36 curr_line
; //Current line
37 long dbp_sq
= 0; //Distance squared to best line
38 SmCaretPosGraphIterator it
= pGraph
->GetIterator();
40 //Reject it if it's the current position
41 if(it
->CaretPos
== position
->CaretPos
) continue;
43 curr_line
= SmCaretPos2LineVisitor(pDev
, it
->CaretPos
).GetResult();
44 //Reject anything above if we're moving down
45 if(curr_line
.GetTop() <= from_line
.GetTop() && direction
== MoveDown
) continue;
46 //Reject anything below if we're moving up
47 if(curr_line
.GetTop() + curr_line
.GetHeight() >= from_line
.GetTop() + from_line
.GetHeight()
48 && direction
== MoveUp
) continue;
49 //Compare if it to what we have, if we have anything yet
51 //Compute distance to current line squared, multiplied with a horizontal factor
52 long dp_sq
= curr_line
.SquaredDistanceX(from_line
) * HORIZONTICAL_DISTANCE_FACTOR
+
53 curr_line
.SquaredDistanceY(from_line
);
54 //Discard current line if best line is closer
55 if(dbp_sq
<= dp_sq
) continue;
57 //Take current line as the best
58 best_line
= curr_line
;
59 NewPos
= it
.Current();
60 //Update distance to best line
61 dbp_sq
= best_line
.SquaredDistanceX(from_line
) * HORIZONTICAL_DISTANCE_FACTOR
+
62 best_line
.SquaredDistanceY(from_line
);
66 SAL_WARN("starmath", "Movement direction not supported!");
76 void SmCursor::MoveTo(OutputDevice
* pDev
, Point pos
, bool bMoveAnchor
){
77 SmCaretLine best_line
, //Best line found so far, when iterating
78 curr_line
; //Current line, when iterating
79 SmCaretPosGraphEntry
* NewPos
= NULL
;
80 long dp_sq
= 0, //Distance to current line squared
81 dbp_sq
= 1; //Distance to best line squared
82 SmCaretPosGraphIterator it
= pGraph
->GetIterator();
84 OSL_ENSURE(it
->CaretPos
.IsValid(), "The caret position graph may not have invalid positions!");
85 //Compute current line
86 curr_line
= SmCaretPos2LineVisitor(pDev
, it
->CaretPos
).GetResult();
87 //If we have a position compare to it
89 //Compute squared distance to current line
90 dp_sq
= curr_line
.SquaredDistanceX(pos
) + curr_line
.SquaredDistanceY(pos
);
91 //If best line is closer, reject current line
92 if(dbp_sq
<= dp_sq
) continue;
94 //Accept current position as the best
95 best_line
= curr_line
;
96 NewPos
= it
.Current();
97 //Update distance to best line
98 dbp_sq
= best_line
.SquaredDistanceX(pos
) + best_line
.SquaredDistanceY(pos
);
108 void SmCursor::BuildGraph(){
109 //Save the current anchor and position
110 SmCaretPos _anchor
, _position
;
111 //Release pGraph if allocated
114 _anchor
= anchor
->CaretPos
;
116 _position
= position
->CaretPos
;
118 //Reset anchor and position as they point into an old graph
123 //Build the new graph
124 pGraph
= SmCaretPosGraphBuildingVisitor(pTree
).takeGraph();
126 //Restore anchor and position pointers
127 if(_anchor
.IsValid() || _position
.IsValid()){
128 SmCaretPosGraphIterator it
= pGraph
->GetIterator();
130 if(_anchor
== it
->CaretPos
)
131 anchor
= it
.Current();
132 if(_position
== it
->CaretPos
)
133 position
= it
.Current();
136 //Set position and anchor to first caret position
137 SmCaretPosGraphIterator it
= pGraph
->GetIterator();
139 position
= it
.Next();
143 OSL_ENSURE(position
->CaretPos
.IsValid(), "Position must be valid");
144 OSL_ENSURE(anchor
->CaretPos
.IsValid(), "Anchor must be valid");
147 bool SmCursor::SetCaretPosition(SmCaretPos pos
, bool moveAnchor
){
148 SmCaretPosGraphIterator it
= pGraph
->GetIterator();
150 if(it
->CaretPos
== pos
){
151 position
= it
.Current();
153 anchor
= it
.Current();
160 void SmCursor::AnnotateSelection(){
161 //TODO: Manage a state, reset it upon modification and optimize this call
162 SmSetSelectionVisitor(anchor
->CaretPos
, position
->CaretPos
, pTree
);
165 void SmCursor::Draw(OutputDevice
& pDev
, Point Offset
, bool isCaretVisible
){
166 SmCaretDrawingVisitor(pDev
, GetPosition(), Offset
, isCaretVisible
);
169 void SmCursor::DeletePrev(OutputDevice
* pDev
){
170 //Delete only a selection if there's a selection
176 SmNode
* pLine
= FindTopMostNodeInLine(position
->CaretPos
.pSelectedNode
);
177 SmStructureNode
* pLineParent
= pLine
->GetParent();
178 int nLineOffset
= pLineParent
->IndexOfSubNode(pLine
);
180 //If we're in front of a node who's parent is a TABLE
181 if(pLineParent
->GetType() == NTABLE
&& position
->CaretPos
.Index
== 0 && nLineOffset
> 0){
182 //Now we can merge with nLineOffset - 1
184 //Line to merge things into, so we can delete pLine
185 SmNode
* pMergeLine
= pLineParent
->GetSubNode(nLineOffset
-1);
186 OSL_ENSURE(pMergeLine
, "pMergeLine cannot be NULL!");
187 SmCaretPos PosAfterDelete
;
188 //Convert first line to list
189 SmNodeList
*pLineList
= NodeToList(pMergeLine
);
190 if(!pLineList
->empty()){
191 //Find iterator to patch
192 SmNodeList::iterator patchPoint
= pLineList
->end();
194 //Convert second line to list
195 NodeToList(pLine
, pLineList
);
196 //Patch the line list
198 PosAfterDelete
= PatchLineList(pLineList
, patchPoint
);
200 pLine
= SmNodeListParser().Parse(pLineList
);
203 pLineParent
->SetSubNode(nLineOffset
-1, pLine
);
204 //Delete the removed line slot
205 SmNodeArray
lines(pLineParent
->GetNumSubNodes()-1);
206 for(int i
= 0; i
< pLineParent
->GetNumSubNodes(); i
++){
208 lines
[i
] = pLineParent
->GetSubNode(i
);
209 else if(i
> nLineOffset
)
210 lines
[i
-1] = pLineParent
->GetSubNode(i
);
212 pLineParent
->SetSubNodes(lines
);
219 if(!SetCaretPosition(PosAfterDelete
, true))
220 SetCaretPosition(SmCaretPos(pLine
, 0), true);
224 //TODO: If we're in an empty (sub/super/*) script
225 /*}else if(pLineParent->GetType() == NSUBSUP &&
227 pLine->GetType() == NEXPRESSION &&
228 pLine->GetNumSubNodes() == 0){
229 //There's a (sub/super) script we can delete
230 //Consider selecting the entire script if GetNumSubNodes() != 0 or pLine->GetType() != NEXPRESSION
231 //TODO: Handle case where we delete a limit
234 //Else move select, and delete if not complex
236 this->Move(pDev
, MoveLeft
, false);
237 if(!this->HasComplexSelection())
242 void SmCursor::Delete(){
243 //Return if we don't have a selection to delete
250 //Set selected on nodes
253 //Find an arbitrary selected node
254 SmNode
* pSNode
= FindSelectedNode(pTree
);
255 OSL_ENSURE(pSNode
!= NULL
, "There must be a selection when HasSelection is true!");
257 //Find the topmost node of the line that holds the selection
258 SmNode
* pLine
= FindTopMostNodeInLine(pSNode
, true);
259 OSL_ENSURE(pLine
!= pTree
, "Shouldn't be able to select the entire tree");
261 //Get the parent of the line
262 SmStructureNode
* pLineParent
= pLine
->GetParent();
263 //Find line offset in parent
264 int nLineOffset
= pLineParent
->IndexOfSubNode(pLine
);
265 if (nLineOffset
== -1)
267 SAL_WARN("starmath", "pLine must be a child of its parent!");
271 //Position after delete
272 SmCaretPos PosAfterDelete
;
274 SmNodeList
* pLineList
= NodeToList(pLine
);
276 //Take the selected nodes and delete them...
277 SmNodeList::iterator patchIt
= TakeSelectedNodesFromList(pLineList
);
279 //Get the position to set after delete
280 PosAfterDelete
= PatchLineList(pLineList
, patchIt
);
283 FinishEdit(pLineList
, pLineParent
, nLineOffset
, PosAfterDelete
);
286 void SmCursor::InsertNodes(SmNodeList
* pNewNodes
){
287 if(pNewNodes
->empty()){
295 //Position after insert should be after pNewNode
296 SmCaretPos PosAfterInsert
= SmCaretPos(pNewNodes
->back(), 1);
298 //Get the current position
299 const SmCaretPos pos
= position
->CaretPos
;
301 //Find top most of line that holds position
302 SmNode
* pLine
= FindTopMostNodeInLine(pos
.pSelectedNode
, false);
304 //Find line parent and line index in parent
305 SmStructureNode
* pLineParent
= pLine
->GetParent();
306 int nParentIndex
= pLineParent
->IndexOfSubNode(pLine
);
307 OSL_ENSURE(nParentIndex
!= -1, "pLine must be a subnode of pLineParent!");
308 if (nParentIndex
== -1)
314 //Convert line to list
315 SmNodeList
* pLineList
= NodeToList(pLine
);
317 //Find iterator for place to insert nodes
318 SmNodeList::iterator it
= FindPositionInLineList(pLineList
, pos
);
320 //Insert all new nodes
321 SmNodeList::iterator newIt
,
322 patchIt
= it
, // (pointless default value, fixes compiler warnings)
324 for(newIt
= pNewNodes
->begin(); newIt
!= pNewNodes
->end(); ++newIt
){
325 insIt
= pLineList
->insert(it
, *newIt
);
326 if(newIt
== pNewNodes
->begin())
328 if((*newIt
)->GetType() == NTEXT
)
329 PosAfterInsert
= SmCaretPos(*newIt
, static_cast<SmTextNode
*>(*newIt
)->GetText().getLength());
331 PosAfterInsert
= SmCaretPos(*newIt
, 1);
333 //Patch the places we've changed stuff
334 PatchLineList(pLineList
, patchIt
);
335 PosAfterInsert
= PatchLineList(pLineList
, it
);
336 //Release list, we've taken the nodes
341 FinishEdit(pLineList
, pLineParent
, nParentIndex
, PosAfterInsert
);
344 SmNodeList::iterator
SmCursor::FindPositionInLineList(SmNodeList
* pLineList
, SmCaretPos aCaretPos
) {
345 //Find iterator for position
346 SmNodeList::iterator it
;
347 for(it
= pLineList
->begin(); it
!= pLineList
->end(); ++it
){
348 if(*it
== aCaretPos
.pSelectedNode
){
349 if((*it
)->GetType() == NTEXT
){
350 //Split textnode if needed
351 if(aCaretPos
.Index
> 0){
352 SmTextNode
* pText
= static_cast<SmTextNode
*>(aCaretPos
.pSelectedNode
);
353 OUString str1
= pText
->GetText().copy(0, aCaretPos
.Index
);
354 OUString str2
= pText
->GetText().copy(aCaretPos
.Index
);
355 pText
->ChangeText(str1
);
357 //Insert str2 as new text node
359 SmTextNode
* pNewText
= new SmTextNode(pText
->GetToken(), pText
->GetFontDesc());
360 pNewText
->ChangeText(str2
);
361 it
= pLineList
->insert(it
, pNewText
);
366 //it now pointer to the node following pos, so pLineList->insert(it, ...) will insert correctly
371 //If we didn't find pSelectedNode, it must be because the caret is in front of the line
372 return pLineList
->begin();
375 SmCaretPos
SmCursor::PatchLineList(SmNodeList
* pLineList
, SmNodeList::iterator aIter
) {
376 //The nodes we should consider merging
379 if(aIter
!= pLineList
->end())
381 if(aIter
!= pLineList
->begin()) {
387 //Check if there's textnodes to merge
390 prev
->GetType() == NTEXT
&&
391 next
->GetType() == NTEXT
&&
392 ( prev
->GetToken().eType
!= TNUMBER
||
393 next
->GetToken().eType
== TNUMBER
) ){
394 SmTextNode
*pText
= static_cast<SmTextNode
*>(prev
),
395 *pOldN
= static_cast<SmTextNode
*>(next
);
396 SmCaretPos
retval(pText
, pText
->GetText().getLength());
398 newText
+= pText
->GetText();
399 newText
+= pOldN
->GetText();
400 pText
->ChangeText(newText
);
402 pLineList
->erase(aIter
);
406 //Check if there's a SmPlaceNode to remove:
407 if(prev
&& next
&& prev
->GetType() == NPLACE
&& !SmNodeListParser::IsOperator(next
->GetToken())){
409 aIter
= pLineList
->erase(aIter
);
411 //Return caret pos in front of aIter
412 if(aIter
!= pLineList
->begin())
413 --aIter
; //Thus find node before aIter
414 if(aIter
== pLineList
->begin())
416 if((*aIter
)->GetType() == NTEXT
)
417 return SmCaretPos(*aIter
, static_cast<SmTextNode
*>(*aIter
)->GetText().getLength());
418 return SmCaretPos(*aIter
, 1);
420 if(prev
&& next
&& next
->GetType() == NPLACE
&& !SmNodeListParser::IsOperator(prev
->GetToken())){
421 aIter
= pLineList
->erase(aIter
);
423 if(prev
->GetType() == NTEXT
)
424 return SmCaretPos(prev
, static_cast<SmTextNode
*>(prev
)->GetText().getLength());
425 return SmCaretPos(prev
, 1);
428 //If we didn't do anything return
429 if(!prev
) //return an invalid to indicate we're in front of line
431 if(prev
->GetType() == NTEXT
)
432 return SmCaretPos(prev
, static_cast<SmTextNode
*>(prev
)->GetText().getLength());
433 return SmCaretPos(prev
, 1);
436 SmNodeList::iterator
SmCursor::TakeSelectedNodesFromList(SmNodeList
*pLineList
,
437 SmNodeList
*pSelectedNodes
) {
438 SmNodeList::iterator retval
;
439 SmNodeList::iterator it
= pLineList
->begin();
440 while(it
!= pLineList
->end()){
441 if((*it
)->IsSelected()){
443 if((*it
)->GetType() == NTEXT
) {
444 SmTextNode
* pText
= static_cast<SmTextNode
*>(*it
);
445 OUString aText
= pText
->GetText();
446 //Start and lengths of the segments, 2 is the selected segment
447 int start2
= pText
->GetSelectionStart(),
448 start3
= pText
->GetSelectionEnd(),
450 len2
= start3
- start2
,
451 len3
= aText
.getLength() - start3
;
452 SmToken aToken
= pText
->GetToken();
453 sal_uInt16 eFontDesc
= pText
->GetFontDesc();
454 //If we need make segment 1
457 OUString str
= aText
.copy(start1
, len1
);
458 pText
->ChangeText(str
);
460 } else {//Remove it if not needed
461 it
= pLineList
->erase(it
);
464 //Set retval to be right after the selection
466 //if we need make segment 3
468 OUString str
= aText
.copy(start3
, len3
);
469 SmTextNode
* pSeg3
= new SmTextNode(aToken
, eFontDesc
);
470 pSeg3
->ChangeText(str
);
471 retval
= pLineList
->insert(it
, pSeg3
);
473 //If we need to save the selected text
474 if(pSelectedNodes
&& len2
> 0) {
475 OUString str
= aText
.copy(start2
, len2
);
476 SmTextNode
* pSeg2
= new SmTextNode(aToken
, eFontDesc
);
477 pSeg2
->ChangeText(str
);
478 pSelectedNodes
->push_back(pSeg2
);
480 } else { //if it's not textnode
482 retval
= it
= pLineList
->erase(it
);
484 pSelectedNodes
->push_back(pNode
);
494 void SmCursor::InsertSubSup(SmSubSup eSubSup
) {
500 SmNode
*pSNode
= FindSelectedNode(pTree
);
501 OSL_ENSURE(pSNode
!= NULL
, "There must be a selected node when HasSelection is true!");
502 pLine
= FindTopMostNodeInLine(pSNode
, true);
504 pLine
= FindTopMostNodeInLine(position
->CaretPos
.pSelectedNode
, false);
506 //Find Parent and offset in parent
507 SmStructureNode
*pLineParent
= pLine
->GetParent();
508 int nParentIndex
= pLineParent
->IndexOfSubNode(pLine
);
509 OSL_ENSURE(nParentIndex
!= -1, "pLine must be a subnode of pLineParent!");
511 //TODO: Consider handling special cases where parent is an SmOperNode,
512 // Maybe this method should be able to add limits to an SmOperNode...
514 //We begin modifying the tree here
517 //Convert line to list
518 SmNodeList
* pLineList
= NodeToList(pLine
);
520 //Take the selection, and/or find iterator for current position
521 SmNodeList
* pSelectedNodesList
= new SmNodeList();
522 SmNodeList::iterator it
;
524 it
= TakeSelectedNodesFromList(pLineList
, pSelectedNodesList
);
526 it
= FindPositionInLineList(pLineList
, position
->CaretPos
);
528 //Find node that this should be applied to
530 bool bPatchLine
= pSelectedNodesList
->size() > 0; //If the line should be patched later
531 if(it
!= pLineList
->begin()) {
536 //Create a new place node
537 pSubject
= new SmPlaceNode();
538 pSubject
->Prepare(pDocShell
->GetFormat(), *pDocShell
);
539 it
= pLineList
->insert(it
, pSubject
);
541 bPatchLine
= true; //We've modified the line it should be patched later.
544 //Wrap the subject in a SmSubSupNode
545 SmSubSupNode
* pSubSup
;
546 if(pSubject
->GetType() != NSUBSUP
){
548 token
.nGroup
= TGPOWER
;
549 pSubSup
= new SmSubSupNode(token
);
550 pSubSup
->SetBody(pSubject
);
554 pSubSup
= static_cast<SmSubSupNode
*>(pSubject
);
555 //pSubject shouldn't be referenced anymore, pSubSup is the SmSubSupNode in pLineList we wish to edit.
556 //and it pointer to the element following pSubSup in pLineList.
559 //Patch the line if we noted that was needed previously
561 PatchLineList(pLineList
, it
);
563 //Convert existing, if any, sub-/superscript line to list
564 SmNode
*pScriptLine
= pSubSup
->GetSubSup(eSubSup
);
565 SmNodeList
* pScriptLineList
= NodeToList(pScriptLine
);
567 //Add selection to pScriptLineList
568 unsigned int nOldSize
= pScriptLineList
->size();
569 pScriptLineList
->insert(pScriptLineList
->end(), pSelectedNodesList
->begin(), pSelectedNodesList
->end());
570 delete pSelectedNodesList
;
571 pSelectedNodesList
= NULL
;
573 //Patch pScriptLineList if needed
574 if(0 < nOldSize
&& nOldSize
< pScriptLineList
->size()) {
575 SmNodeList::iterator iPatchPoint
= pScriptLineList
->begin();
576 std::advance(iPatchPoint
, nOldSize
);
577 PatchLineList(pScriptLineList
, iPatchPoint
);
580 //Find caret pos, that should be used after sub-/superscription.
581 SmCaretPos PosAfterScript
; //Leave invalid for first position
582 if(pScriptLineList
->size() > 0)
583 PosAfterScript
= SmCaretPos::GetPosAfter(pScriptLineList
->back());
585 //Parse pScriptLineList
586 pScriptLine
= SmNodeListParser().Parse(pScriptLineList
);
587 delete pScriptLineList
;
588 pScriptLineList
= NULL
;
590 //Insert pScriptLine back into the tree
591 pSubSup
->SetSubSup(eSubSup
, pScriptLine
);
594 FinishEdit(pLineList
, pLineParent
, nParentIndex
, PosAfterScript
, pScriptLine
);
597 bool SmCursor::InsertLimit(SmSubSup eSubSup
, bool bMoveCaret
) {
598 //Find a subject to set limits on
599 SmOperNode
*pSubject
= NULL
;
600 //Check if pSelectedNode might be a subject
601 if(position
->CaretPos
.pSelectedNode
->GetType() == NOPER
)
602 pSubject
= static_cast<SmOperNode
*>(position
->CaretPos
.pSelectedNode
);
604 //If not, check if parent of the current line is a SmOperNode
605 SmNode
*pLineNode
= FindTopMostNodeInLine(position
->CaretPos
.pSelectedNode
, false);
606 if(pLineNode
->GetParent() && pLineNode
->GetParent()->GetType() == NOPER
)
607 pSubject
= static_cast<SmOperNode
*>(pLineNode
->GetParent());
610 //Abort operation if we're not in the appropriate context
616 //Find the sub sup node
617 SmSubSupNode
*pSubSup
= NULL
;
618 //Check if there's already one there...
619 if(pSubject
->GetSubNode(0)->GetType() == NSUBSUP
)
620 pSubSup
= static_cast<SmSubSupNode
*>(pSubject
->GetSubNode(0));
621 else { //if not create a new SmSubSupNode
623 token
.nGroup
= TGLIMIT
;
624 pSubSup
= new SmSubSupNode(token
);
626 pSubSup
->SetBody(pSubject
->GetSubNode(0));
627 //Replace the operation of the SmOperNode
628 pSubject
->SetSubNode(0, pSubSup
);
631 //Create the limit, if needed
632 SmCaretPos PosAfterLimit
;
633 SmNode
*pLine
= NULL
;
634 if(!pSubSup
->GetSubSup(eSubSup
)){
635 pLine
= new SmPlaceNode();
636 pSubSup
->SetSubSup(eSubSup
, pLine
);
637 PosAfterLimit
= SmCaretPos(pLine
, 1);
638 //If it's already there... let's move the caret
639 } else if(bMoveCaret
){
640 pLine
= pSubSup
->GetSubSup(eSubSup
);
641 SmNodeList
* pLineList
= NodeToList(pLine
);
642 if(pLineList
->size() > 0)
643 PosAfterLimit
= SmCaretPos::GetPosAfter(pLineList
->back());
644 pLine
= SmNodeListParser().Parse(pLineList
);
646 pSubSup
->SetSubSup(eSubSup
, pLine
);
649 //Rebuild graph of caret positions
655 if(!SetCaretPosition(PosAfterLimit
, true))
656 SetCaretPosition(SmCaretPos(pLine
, 0), true);
663 void SmCursor::InsertBrackets(SmBracketType eBracketType
) {
671 SmNode
*pSNode
= FindSelectedNode(pTree
);
672 OSL_ENSURE(pSNode
!= NULL
, "There must be a selected node if HasSelection()");
673 pLine
= FindTopMostNodeInLine(pSNode
, true);
675 pLine
= FindTopMostNodeInLine(position
->CaretPos
.pSelectedNode
, false);
677 //Find parent and offset in parent
678 SmStructureNode
*pLineParent
= pLine
->GetParent();
679 int nParentIndex
= pLineParent
->IndexOfSubNode(pLine
);
680 OSL_ENSURE( nParentIndex
!= -1, "pLine must be a subnode of pLineParent!");
681 if (nParentIndex
< 0)
684 //Convert line to list
685 SmNodeList
*pLineList
= NodeToList(pLine
);
687 //Take the selection, and/or find iterator for current position
688 SmNodeList
*pSelectedNodesList
= new SmNodeList();
689 SmNodeList::iterator it
;
691 it
= TakeSelectedNodesFromList(pLineList
, pSelectedNodesList
);
693 it
= FindPositionInLineList(pLineList
, position
->CaretPos
);
695 //If there's no selected nodes, create a place node
697 SmCaretPos PosAfterInsert
;
698 if(pSelectedNodesList
->empty()) {
699 pBodyNode
= new SmPlaceNode();
700 PosAfterInsert
= SmCaretPos(pBodyNode
, 1);
702 pBodyNode
= SmNodeListParser().Parse(pSelectedNodesList
);
704 delete pSelectedNodesList
;
707 SmToken
aTok(TLEFT
, '\0', "left", 0, 5);
708 SmBraceNode
*pBrace
= new SmBraceNode(aTok
);
709 pBrace
->SetScaleMode(SCALE_HEIGHT
);
710 SmNode
*pLeft
= CreateBracket(eBracketType
, true),
711 *pRight
= CreateBracket(eBracketType
, false);
712 SmBracebodyNode
*pBody
= new SmBracebodyNode(SmToken());
713 pBody
->SetSubNodes(pBodyNode
, NULL
);
714 pBrace
->SetSubNodes(pLeft
, pBody
, pRight
);
715 pBrace
->Prepare(pDocShell
->GetFormat(), *pDocShell
);
718 pLineList
->insert(it
, pBrace
);
719 //Patch line (I think this is good enough)
720 SmCaretPos pAfter
= PatchLineList(pLineList
, it
);
721 if( !PosAfterInsert
.IsValid() )
722 PosAfterInsert
= pAfter
;
725 FinishEdit(pLineList
, pLineParent
, nParentIndex
, PosAfterInsert
);
728 SmNode
*SmCursor::CreateBracket(SmBracketType eBracketType
, bool bIsLeft
) {
731 switch(eBracketType
){
733 aTok
= SmToken(TNONE
, '\0', "none", TGLBRACES
| TGRBRACES
, 0);
736 aTok
= SmToken(TLPARENT
, MS_LPARENT
, "(", TGLBRACES
, 5);
739 aTok
= SmToken(TLBRACKET
, MS_LBRACKET
, "[", TGLBRACES
, 5);
741 case DoubleSquareBrackets
:
742 aTok
= SmToken(TLDBRACKET
, MS_LDBRACKET
, "ldbracket", TGLBRACES
, 5);
745 aTok
= SmToken(TLLINE
, MS_VERTLINE
, "lline", TGLBRACES
, 5);
747 case DoubleLineBrackets
:
748 aTok
= SmToken(TLDLINE
, MS_DVERTLINE
, "ldline", TGLBRACES
, 5);
751 aTok
= SmToken(TLBRACE
, MS_LBRACE
, "lbrace", TGLBRACES
, 5);
754 aTok
= SmToken(TLANGLE
, MS_LMATHANGLE
, "langle", TGLBRACES
, 5);
757 aTok
= SmToken(TLCEIL
, MS_LCEIL
, "lceil", TGLBRACES
, 5);
760 aTok
= SmToken(TLFLOOR
, MS_LFLOOR
, "lfloor", TGLBRACES
, 5);
764 switch(eBracketType
) {
766 aTok
= SmToken(TNONE
, '\0', "none", TGLBRACES
| TGRBRACES
, 0);
769 aTok
= SmToken(TRPARENT
, MS_RPARENT
, ")", TGRBRACES
, 5);
772 aTok
= SmToken(TRBRACKET
, MS_RBRACKET
, "]", TGRBRACES
, 5);
774 case DoubleSquareBrackets
:
775 aTok
= SmToken(TRDBRACKET
, MS_RDBRACKET
, "rdbracket", TGRBRACES
, 5);
778 aTok
= SmToken(TRLINE
, MS_VERTLINE
, "rline", TGRBRACES
, 5);
780 case DoubleLineBrackets
:
781 aTok
= SmToken(TRDLINE
, MS_DVERTLINE
, "rdline", TGRBRACES
, 5);
784 aTok
= SmToken(TRBRACE
, MS_RBRACE
, "rbrace", TGRBRACES
, 5);
787 aTok
= SmToken(TRANGLE
, MS_RMATHANGLE
, "rangle", TGRBRACES
, 5);
790 aTok
= SmToken(TRCEIL
, MS_RCEIL
, "rceil", TGRBRACES
, 5);
793 aTok
= SmToken(TRFLOOR
, MS_RFLOOR
, "rfloor", TGRBRACES
, 5);
797 SmNode
* pRetVal
= new SmMathSymbolNode(aTok
);
798 pRetVal
->SetScaleMode(SCALE_HEIGHT
);
802 bool SmCursor::InsertRow() {
808 SmNode
*pSNode
= FindSelectedNode(pTree
);
809 OSL_ENSURE(pSNode
!= NULL
, "There must be a selected node if HasSelection()");
810 pLine
= FindTopMostNodeInLine(pSNode
, true);
812 pLine
= FindTopMostNodeInLine(position
->CaretPos
.pSelectedNode
, false);
814 //Find parent and offset in parent
815 SmStructureNode
*pLineParent
= pLine
->GetParent();
816 int nParentIndex
= pLineParent
->IndexOfSubNode(pLine
);
818 if (nParentIndex
== -1)
820 SAL_WARN("starmath", "pLine must be a subnode of pLineParent!");
824 //Discover the context of this command
825 SmTableNode
*pTable
= NULL
;
826 SmMatrixNode
*pMatrix
= NULL
;
827 int nTableIndex
= nParentIndex
;
828 if(pLineParent
->GetType() == NTABLE
)
829 pTable
= static_cast<SmTableNode
*>(pLineParent
);
830 //If it's warped in a SmLineNode, we can still insert a newline
831 else if(pLineParent
->GetType() == NLINE
&&
832 pLineParent
->GetParent() &&
833 pLineParent
->GetParent()->GetType() == NTABLE
) {
834 //NOTE: This hack might give problems if we stop ignoring SmAlignNode
835 pTable
= static_cast<SmTableNode
*>(pLineParent
->GetParent());
836 nTableIndex
= pTable
->IndexOfSubNode(pLineParent
);
837 OSL_ENSURE(nTableIndex
!= -1, "pLineParent must be a child of its parent!");
839 if(pLineParent
->GetType() == NMATRIX
)
840 pMatrix
= static_cast<SmMatrixNode
*>(pLineParent
);
842 //If we're not in a context that supports InsertRow, return sal_False
843 if(!pTable
&& !pMatrix
)
846 //Now we start editing
849 //Convert line to list
850 SmNodeList
*pLineList
= NodeToList(pLine
);
852 //Find position in line
853 SmNodeList::iterator it
;
855 //Take the selected nodes and delete them...
856 it
= TakeSelectedNodesFromList(pLineList
);
858 it
= FindPositionInLineList(pLineList
, position
->CaretPos
);
860 //New caret position after inserting the newline/row in whatever context
861 SmCaretPos PosAfterInsert
;
863 //If we're in the context of a table
865 SmNodeList
*pNewLineList
= new SmNodeList();
866 //Move elements from pLineList to pNewLineList
867 pNewLineList
->splice(pNewLineList
->begin(), *pLineList
, it
, pLineList
->end());
868 //Make sure it is valid again
869 it
= pLineList
->end();
870 if(it
!= pLineList
->begin())
872 if(pNewLineList
->empty())
873 pNewLineList
->push_front(new SmPlaceNode());
875 SmNode
*pNewLine
= SmNodeListParser().Parse(pNewLineList
);
877 //Wrap pNewLine in SmLineNode if needed
878 if(pLineParent
->GetType() == NLINE
) {
879 SmLineNode
*pNewLineNode
= new SmLineNode(SmToken(TNEWLINE
, '\0', "newline"));
880 pNewLineNode
->SetSubNodes(pNewLine
, NULL
);
881 pNewLine
= pNewLineNode
;
884 PosAfterInsert
= SmCaretPos(pNewLine
, 0);
885 //Move other nodes if needed
886 for( int i
= pTable
->GetNumSubNodes(); i
> nTableIndex
+ 1; i
--)
887 pTable
->SetSubNode(i
, pTable
->GetSubNode(i
-1));
890 pTable
->SetSubNode(nTableIndex
+ 1, pNewLine
);
892 //Check if we need to change token type:
893 if(pTable
->GetNumSubNodes() > 2 && pTable
->GetToken().eType
== TBINOM
) {
894 SmToken tok
= pTable
->GetToken();
896 pTable
->SetToken(tok
);
899 //If we're in the context of a matrix
901 //Find position after insert and patch the list
902 PosAfterInsert
= PatchLineList(pLineList
, it
);
903 //Move other children
904 sal_uInt16 rows
= pMatrix
->GetNumRows();
905 sal_uInt16 cols
= pMatrix
->GetNumCols();
906 int nRowStart
= (nParentIndex
- nParentIndex
% cols
) + cols
;
907 for( int i
= pMatrix
->GetNumSubNodes() + cols
- 1; i
>= nRowStart
+ cols
; i
--)
908 pMatrix
->SetSubNode(i
, pMatrix
->GetSubNode(i
- cols
));
909 for( int i
= nRowStart
; i
< nRowStart
+ cols
; i
++) {
910 SmPlaceNode
*pNewLine
= new SmPlaceNode();
911 if(i
== nParentIndex
+ cols
)
912 PosAfterInsert
= SmCaretPos(pNewLine
, 0);
913 pMatrix
->SetSubNode(i
, pNewLine
);
915 pMatrix
->SetRowCol(rows
+ 1, cols
);
919 FinishEdit(pLineList
, pLineParent
, nParentIndex
, PosAfterInsert
);
920 //FinishEdit is actually used to handle siturations where parent is an instance of
921 //SmSubSupNode. In this case parent should always be a table or matrix, however, for
922 //code reuse we just use FinishEdit() here too.
926 void SmCursor::InsertFraction() {
932 SmNode
*pSNode
= FindSelectedNode(pTree
);
933 OSL_ENSURE(pSNode
!= NULL
, "There must be a selected node when HasSelection is true!");
934 pLine
= FindTopMostNodeInLine(pSNode
, true);
936 pLine
= FindTopMostNodeInLine(position
->CaretPos
.pSelectedNode
, false);
938 //Find Parent and offset in parent
939 SmStructureNode
*pLineParent
= pLine
->GetParent();
940 int nParentIndex
= pLineParent
->IndexOfSubNode(pLine
);
941 if (nParentIndex
== -1)
943 SAL_WARN("starmath", "pLine must be a subnode of pLineParent!");
947 //We begin modifying the tree here
950 //Convert line to list
951 SmNodeList
* pLineList
= NodeToList(pLine
);
953 //Take the selection, and/or find iterator for current position
954 SmNodeList
* pSelectedNodesList
= new SmNodeList();
955 SmNodeList::iterator it
;
957 it
= TakeSelectedNodesFromList(pLineList
, pSelectedNodesList
);
959 it
= FindPositionInLineList(pLineList
, position
->CaretPos
);
961 //Create pNum, and pDenom
962 bool bEmptyFraction
= pSelectedNodesList
->empty();
963 SmNode
*pNum
= bEmptyFraction
965 : SmNodeListParser().Parse(pSelectedNodesList
);
966 SmNode
*pDenom
= new SmPlaceNode();
967 delete pSelectedNodesList
;
968 pSelectedNodesList
= NULL
;
970 //Create new fraction
971 SmBinVerNode
*pFrac
= new SmBinVerNode(SmToken(TOVER
, '\0', "over", TGPRODUCT
, 0));
972 SmNode
*pRect
= new SmRectangleNode(SmToken());
973 pFrac
->SetSubNodes(pNum
, pRect
, pDenom
);
975 //Insert in pLineList
976 SmNodeList::iterator patchIt
= pLineList
->insert(it
, pFrac
);
977 PatchLineList(pLineList
, patchIt
);
978 PatchLineList(pLineList
, it
);
981 SmNode
*pSelectedNode
= bEmptyFraction
? pNum
: pDenom
;
982 FinishEdit(pLineList
, pLineParent
, nParentIndex
, SmCaretPos(pSelectedNode
, 1));
985 void SmCursor::InsertText(const OUString
& aString
)
992 token
.eType
= TIDENT
;
993 token
.cMathChar
= '\0';
996 token
.aText
= aString
;
998 SmTextNode
* pText
= new SmTextNode(token
, FNT_VARIABLE
);
1000 //Prepare the new node
1001 pText
->Prepare(pDocShell
->GetFormat(), *pDocShell
);
1002 pText
->AdjustFontDesc();
1004 SmNodeList
* pList
= new SmNodeList();
1005 pList
->push_front(pText
);
1011 void SmCursor::InsertElement(SmFormulaElement element
){
1017 SmNode
* pNewNode
= NULL
;
1022 token
.nGroup
= TGBLANK
;
1024 pNewNode
= new SmBlankNode(token
);
1026 case FactorialElement
:
1028 SmToken
token(TFACT
, MS_FACT
, "fact", TGUNOPER
, 5);
1029 pNewNode
= new SmMathSymbolNode(token
);
1034 token
.eType
= TPLUS
;
1035 token
.cMathChar
= MS_PLUS
;
1036 token
.nGroup
= TGUNOPER
| TGSUM
;
1039 pNewNode
= new SmMathSymbolNode(token
);
1044 token
.eType
= TMINUS
;
1045 token
.cMathChar
= MS_MINUS
;
1046 token
.nGroup
= TGUNOPER
| TGSUM
;
1049 pNewNode
= new SmMathSymbolNode(token
);
1054 token
.eType
= TCDOT
;
1055 token
.cMathChar
= MS_CDOT
;
1056 token
.nGroup
= TGPRODUCT
;
1057 token
.aText
= "cdot";
1058 pNewNode
= new SmMathSymbolNode(token
);
1063 token
.eType
= TASSIGN
;
1064 token
.cMathChar
= MS_ASSIGN
;
1065 token
.nGroup
= TGRELATION
;
1067 pNewNode
= new SmMathSymbolNode(token
);
1069 case LessThanElement
:
1073 token
.cMathChar
= MS_LT
;
1074 token
.nGroup
= TGRELATION
;
1076 pNewNode
= new SmMathSymbolNode(token
);
1078 case GreaterThanElement
:
1082 token
.cMathChar
= MS_GT
;
1083 token
.nGroup
= TGRELATION
;
1085 pNewNode
= new SmMathSymbolNode(token
);
1087 case PercentElement
:
1090 token
.eType
= TTEXT
;
1091 token
.cMathChar
= MS_PERCENT
;
1093 token
.aText
= "\"%\"";
1094 pNewNode
= new SmMathSymbolNode(token
);
1097 SAL_WARN("starmath", "Element unknown!");
1099 OSL_ENSURE(pNewNode
!= NULL
, "No new node was created!");
1103 //Prepare the new node
1104 pNewNode
->Prepare(pDocShell
->GetFormat(), *pDocShell
);
1107 SmNodeList
* pList
= new SmNodeList();
1108 pList
->push_front(pNewNode
);
1114 void SmCursor::InsertSpecial(const OUString
& _aString
)
1119 OUString aString
= comphelper::string::strip(_aString
, ' ');
1121 //Create instance of special node
1123 token
.eType
= TSPECIAL
;
1124 token
.cMathChar
= '\0';
1127 token
.aText
= aString
;
1128 SmSpecialNode
* pSpecial
= new SmSpecialNode(token
);
1130 //Prepare the special node
1131 pSpecial
->Prepare(pDocShell
->GetFormat(), *pDocShell
);
1134 SmNodeList
* pList
= new SmNodeList();
1135 pList
->push_front(pSpecial
);
1141 void SmCursor::InsertCommand(sal_uInt16 nCommand
) {
1147 InsertLimit(CSUB
, true);
1150 InsertLimit(CSUP
, true);
1153 if(InsertLimit(CSUB
, true))
1154 InsertLimit(CSUP
, true);
1157 InsertCommandText(SM_RESSTR(nCommand
));
1162 void SmCursor::InsertCommandText(const OUString
& aCommandText
) {
1163 //Parse the sub expression
1164 SmNode
* pSubExpr
= SmParser().ParseExpression(aCommandText
);
1166 //Prepare the subtree
1167 pSubExpr
->Prepare(pDocShell
->GetFormat(), *pDocShell
);
1169 //Convert subtree to list
1170 SmNodeList
* pLineList
= NodeToList(pSubExpr
);
1174 //Delete any selection
1178 InsertNodes(pLineList
);
1183 void SmCursor::Copy(){
1187 //Find selected node
1188 SmNode
* pSNode
= FindSelectedNode(pTree
);
1190 SmNode
* pLine
= FindTopMostNodeInLine(pSNode
, true);
1192 //Clone selected nodes
1194 if(IsLineCompositionNode(pLine
))
1195 pList
= CloneLineToList(static_cast<SmStructureNode
*>(pLine
), true);
1197 pList
= new SmNodeList();
1198 //Special care to only clone selected text
1199 if(pLine
->GetType() == NTEXT
) {
1200 SmTextNode
*pText
= static_cast<SmTextNode
*>(pLine
);
1201 SmTextNode
*pClone
= new SmTextNode( pText
->GetToken(), pText
->GetFontDesc() );
1202 int start
= pText
->GetSelectionStart(),
1203 length
= pText
->GetSelectionEnd() - pText
->GetSelectionStart();
1204 pClone
->ChangeText(pText
->GetText().copy(start
, length
));
1205 pClone
->SetScaleMode(pText
->GetScaleMode());
1206 pList
->push_front(pClone
);
1208 SmCloningVisitor aCloneFactory
;
1209 pList
->push_front(aCloneFactory
.Clone(pLine
));
1214 if (pList
->size() > 0)
1215 SetClipboard(pList
);
1220 void SmCursor::Paste() {
1224 if(pClipboard
&& pClipboard
->size() > 0)
1225 InsertNodes(CloneList(pClipboard
));
1230 SmNodeList
* SmCursor::CloneList(SmNodeList
* pList
){
1231 SmCloningVisitor aCloneFactory
;
1232 SmNodeList
* pClones
= new SmNodeList();
1234 SmNodeList::iterator it
;
1235 for(it
= pList
->begin(); it
!= pList
->end(); ++it
){
1236 SmNode
*pClone
= aCloneFactory
.Clone(*it
);
1237 pClones
->push_back(pClone
);
1243 void SmCursor::SetClipboard(SmNodeList
* pList
){
1245 //Delete all nodes on the clipboard
1246 SmNodeList::iterator it
;
1247 for(it
= pClipboard
->begin(); it
!= pClipboard
->end(); ++it
)
1254 SmNode
* SmCursor::FindTopMostNodeInLine(SmNode
* pSNode
, bool MoveUpIfSelected
){
1255 //If we haven't got a subnode
1259 //Move up parent until we find a node who's
1260 //parent is NULL or isn't selected and not a type of:
1267 while(pSNode
->GetParent() &&
1268 ((MoveUpIfSelected
&&
1269 pSNode
->GetParent()->IsSelected()) ||
1270 IsLineCompositionNode(pSNode
->GetParent())))
1271 pSNode
= pSNode
->GetParent();
1272 //Now we have the selection line node
1276 SmNode
* SmCursor::FindSelectedNode(SmNode
* pNode
){
1277 SmNodeIterator
it(pNode
);
1279 if(it
->IsSelected())
1280 return it
.Current();
1281 SmNode
* pRetVal
= FindSelectedNode(it
.Current());
1288 SmNodeList
* SmCursor::LineToList(SmStructureNode
* pLine
, SmNodeList
* list
){
1289 SmNodeIterator
it(pLine
);
1291 switch(it
->GetType()){
1298 LineToList(static_cast<SmStructureNode
*>(it
.Current()), list
);
1301 delete it
.Current();
1304 list
->push_back(it
.Current());
1307 SmNodeArray
emptyArray(0);
1308 pLine
->SetSubNodes(emptyArray
);
1313 SmNodeList
* SmCursor::CloneLineToList(SmStructureNode
* pLine
, bool bOnlyIfSelected
, SmNodeList
* pList
){
1314 SmCloningVisitor aCloneFactory
;
1315 SmNodeIterator
it(pLine
);
1317 if( IsLineCompositionNode( it
.Current() ) )
1318 CloneLineToList( static_cast<SmStructureNode
*>(it
.Current()), bOnlyIfSelected
, pList
);
1319 else if( (!bOnlyIfSelected
|| it
->IsSelected()) && it
->GetType() != NERROR
) {
1320 //Only clone selected text from SmTextNode
1321 if(it
->GetType() == NTEXT
) {
1322 SmTextNode
*pText
= static_cast<SmTextNode
*>(it
.Current());
1323 SmTextNode
*pClone
= new SmTextNode( it
->GetToken(), pText
->GetFontDesc() );
1324 int start
= pText
->GetSelectionStart(),
1325 length
= pText
->GetSelectionEnd() - pText
->GetSelectionStart();
1326 pClone
->ChangeText(pText
->GetText().copy(start
, length
));
1327 pClone
->SetScaleMode(pText
->GetScaleMode());
1328 pList
->push_back(pClone
);
1330 pList
->push_back(aCloneFactory
.Clone(it
.Current()));
1336 bool SmCursor::IsLineCompositionNode(SmNode
* pNode
){
1337 switch(pNode
->GetType()){
1350 int SmCursor::CountSelectedNodes(SmNode
* pNode
){
1352 SmNodeIterator
it(pNode
);
1354 if(it
->IsSelected() && !IsLineCompositionNode(it
.Current()))
1356 nCount
+= CountSelectedNodes(it
.Current());
1361 bool SmCursor::HasComplexSelection(){
1364 AnnotateSelection();
1366 return CountSelectedNodes(pTree
) > 1;
1369 void SmCursor::FinishEdit(SmNodeList
* pLineList
,
1370 SmStructureNode
* pParent
,
1372 SmCaretPos PosAfterEdit
,
1373 SmNode
* pStartLine
) {
1374 //Store number of nodes in line for later
1375 int entries
= pLineList
->size();
1377 //Parse list of nodes to a tree
1378 SmNodeListParser parser
;
1379 SmNode
* pLine
= parser
.Parse(pLineList
);
1382 //Check if we're making the body of a subsup node bigger than one
1383 if(pParent
->GetType() == NSUBSUP
&&
1384 nParentIndex
== 0 &&
1386 //Wrap pLine in scalable round brackets
1387 SmToken
aTok(TLEFT
, '\0', "left", 0, 5);
1388 SmBraceNode
*pBrace
= new SmBraceNode(aTok
);
1389 pBrace
->SetScaleMode(SCALE_HEIGHT
);
1390 SmNode
*pLeft
= CreateBracket(RoundBrackets
, true),
1391 *pRight
= CreateBracket(RoundBrackets
, false);
1392 SmBracebodyNode
*pBody
= new SmBracebodyNode(SmToken());
1393 pBody
->SetSubNodes(pLine
, NULL
);
1394 pBrace
->SetSubNodes(pLeft
, pBody
, pRight
);
1395 pBrace
->Prepare(pDocShell
->GetFormat(), *pDocShell
);
1397 //TODO: Consider the following alternative behavior:
1398 //Consider the line: A + {B + C}^D lsub E
1399 //Here pLineList is B, + and C and pParent is a subsup node with
1400 //both RSUP and LSUB set. Imagine the user just inserted "B +" in
1401 //the body of the subsup node...
1402 //The most natural thing to do would be to make the line like this:
1403 //A + B lsub E + C ^ D
1404 //E.g. apply LSUB and LSUP to the first element in pLineList and RSUP
1405 //and RSUB to the last eleent in pLineList. But how should this act
1406 //for CSUP and CSUB ???
1407 //For this reason and because brackets was faster to implement, this solution
1408 //have been chosen. It might be worth working on the other solution later...
1411 //Set pStartLine if NULL
1415 //Insert it back into the parent
1416 pParent
->SetSubNode(nParentIndex
, pLine
);
1418 //Rebuild graph of caret position
1422 AnnotateSelection(); //Update selection annotation!
1424 //Set caret position
1425 if(!SetCaretPosition(PosAfterEdit
, true))
1426 SetCaretPosition(SmCaretPos(pStartLine
, 0), true);
1432 void SmCursor::BeginEdit(){
1433 if(nEditSections
++ > 0) return;
1435 bIsEnabledSetModifiedSmDocShell
= pDocShell
->IsEnableSetModified();
1436 if( bIsEnabledSetModifiedSmDocShell
)
1437 pDocShell
->EnableSetModified( false );
1440 void SmCursor::EndEdit(){
1441 if(--nEditSections
> 0) return;
1443 pDocShell
->SetFormulaArranged(false);
1444 //Okay, I don't know what this does... :)
1445 //It's used in SmDocShell::SetText and with places where everything is modified.
1446 //I think it does some magic, with sfx, but everything is totally undocumented so
1447 //it's kinda hard to tell...
1448 if ( bIsEnabledSetModifiedSmDocShell
)
1449 pDocShell
->EnableSetModified( bIsEnabledSetModifiedSmDocShell
);
1450 //I think this notifies people around us that we've modified this document...
1451 pDocShell
->SetModified(true);
1452 //I think SmDocShell uses this value when it sends an update graphics event
1453 //Anyway comments elsewhere suggests it need to be updated...
1454 pDocShell
->nModifyCount
++;
1456 //TODO: Consider copying the update accessability code from SmDocShell::SetText in here...
1457 //This somehow updates the size of SmGraphicView if it is running in embedded mode
1458 if( pDocShell
->GetCreateMode() == SfxObjectCreateMode::EMBEDDED
)
1459 pDocShell
->OnDocumentPrinterChanged(0);
1461 //Request a replaint...
1464 //Update the edit engine and text of the document
1466 SmNodeToTextVisitor(pTree
, formula
);
1467 //pTree->CreateTextFromNode(formula);
1468 pDocShell
->aText
= formula
;
1469 pDocShell
->GetEditEngine().QuickInsertText( formula
, ESelection( 0, 0, EE_PARA_ALL
, EE_TEXTPOS_ALL
) );
1470 pDocShell
->GetEditEngine().QuickFormatDoc();
1473 void SmCursor::RequestRepaint(){
1474 SmViewShell
*pViewSh
= SmGetActiveView();
1476 if ( SfxObjectCreateMode::EMBEDDED
== pDocShell
->GetCreateMode() )
1477 pDocShell
->Repaint();
1479 pViewSh
->GetGraphicWindow().Invalidate();
1483 bool SmCursor::IsAtTailOfBracket(SmBracketType eBracketType
, SmBraceNode
** ppBraceNode
) const {
1484 const SmCaretPos pos
= GetPosition();
1485 if (!pos
.IsValid()) {
1489 SmNode
* pNode
= pos
.pSelectedNode
;
1491 if (pNode
->GetType() == NTEXT
) {
1492 SmTextNode
* pTextNode
= static_cast<SmTextNode
*>(pNode
);
1493 if (pos
.Index
< pTextNode
->GetText().getLength()) {
1494 // The cursor is on a text node and at the middle of it.
1498 if (pos
.Index
< 1) {
1504 SmStructureNode
* pParentNode
= pNode
->GetParent();
1506 // There's no brace body node in the ancestors.
1510 sal_uInt16 index
= pNode
->FindIndex();
1511 if (index
+ 1 != pParentNode
->GetNumSubNodes()) {
1512 // The cursor is not at the tail at one of ancestor nodes.
1516 pNode
= pParentNode
;
1517 if (pNode
->GetType() == NBRACEBODY
) {
1518 // Found the brace body node.
1523 SmStructureNode
* pBraceNodeTmp
= pNode
->GetParent();
1524 if (!pBraceNodeTmp
|| pBraceNodeTmp
->GetType() != NBRACE
) {
1525 // Brace node is invalid.
1529 SmBraceNode
* pBraceNode
= static_cast<SmBraceNode
*>(pBraceNodeTmp
);
1530 SmMathSymbolNode
* pClosingNode
= pBraceNode
->ClosingBrace();
1531 if (!pClosingNode
) {
1532 // Couldn't get closing symbol node.
1536 // Check if the closing brace matches eBracketType.
1537 SmTokenType eClosingTokenType
= pClosingNode
->GetToken().eType
;
1538 switch (eBracketType
) {
1539 case NoneBrackets
: if (eClosingTokenType
!= TNONE
) { return false; } break;
1540 case RoundBrackets
: if (eClosingTokenType
!= TRPARENT
) { return false; } break;
1541 case SquareBrackets
: if (eClosingTokenType
!= TRBRACKET
) { return false; } break;
1542 case DoubleSquareBrackets
: if (eClosingTokenType
!= TRDBRACKET
) { return false; } break;
1543 case LineBrackets
: if (eClosingTokenType
!= TRLINE
) { return false; } break;
1544 case DoubleLineBrackets
: if (eClosingTokenType
!= TRDLINE
) { return false; } break;
1545 case CurlyBrackets
: if (eClosingTokenType
!= TRBRACE
) { return false; } break;
1546 case AngleBrackets
: if (eClosingTokenType
!= TRANGLE
) { return false; } break;
1547 case CeilBrackets
: if (eClosingTokenType
!= TRCEIL
) { return false; } break;
1548 case FloorBrackets
: if (eClosingTokenType
!= TRFLOOR
) { return false; } break;
1554 *ppBraceNode
= static_cast<SmBraceNode
*>(pBraceNode
);
1560 void SmCursor::MoveAfterBracket(SmBraceNode
* pBraceNode
, bool bMoveAnchor
)
1562 position
->CaretPos
.pSelectedNode
= pBraceNode
;
1563 position
->CaretPos
.Index
= 1;
1565 anchor
->CaretPos
.pSelectedNode
= pBraceNode
;
1566 anchor
->CaretPos
.Index
= 1;
1572 /////////////////////////////////////// SmNodeListParser
1574 SmNode
* SmNodeListParser::Parse(SmNodeList
* list
, bool bDeleteErrorNodes
){
1576 if(bDeleteErrorNodes
){
1577 //Delete error nodes
1578 SmNodeList::iterator it
= pList
->begin();
1579 while(it
!= pList
->end()) {
1580 if((*it
)->GetType() == NERROR
){
1583 it
= pList
->erase(it
);
1588 SmNode
* retval
= Expression();
1593 SmNode
* SmNodeListParser::Expression(){
1594 SmNodeArray NodeArray
;
1595 //Accept as many relations as there is
1597 NodeArray
.push_back(Relation());
1599 //Create SmExpressionNode, I hope SmToken() will do :)
1600 SmStructureNode
* pExpr
= new SmExpressionNode(SmToken());
1601 pExpr
->SetSubNodes(NodeArray
);
1605 SmNode
* SmNodeListParser::Relation(){
1607 SmNode
* pLeft
= Sum();
1608 //While we have tokens and the next is a relation
1609 while(Terminal() && IsRelationOperator(Terminal()->GetToken())){
1611 SmNode
* pOper
= Take();
1612 //Find the right side of the relation
1613 SmNode
* pRight
= Sum();
1614 //Create new SmBinHorNode
1615 SmStructureNode
* pNewNode
= new SmBinHorNode(SmToken());
1616 pNewNode
->SetSubNodes(pLeft
, pOper
, pRight
);
1622 SmNode
* SmNodeListParser::Sum(){
1624 SmNode
* pLeft
= Product();
1625 //While we have tokens and the next is a sum
1626 while(Terminal() && IsSumOperator(Terminal()->GetToken())){
1628 SmNode
* pOper
= Take();
1629 //Find the right side of the sum
1630 SmNode
* pRight
= Product();
1631 //Create new SmBinHorNode
1632 SmStructureNode
* pNewNode
= new SmBinHorNode(SmToken());
1633 pNewNode
->SetSubNodes(pLeft
, pOper
, pRight
);
1639 SmNode
* SmNodeListParser::Product(){
1641 SmNode
* pLeft
= Factor();
1642 //While we have tokens and the next is a product
1643 while(Terminal() && IsProductOperator(Terminal()->GetToken())){
1645 SmNode
* pOper
= Take();
1646 //Find the right side of the operation
1647 SmNode
* pRight
= Factor();
1648 //Create new SmBinHorNode
1649 SmStructureNode
* pNewNode
= new SmBinHorNode(SmToken());
1650 pNewNode
->SetSubNodes(pLeft
, pOper
, pRight
);
1656 SmNode
* SmNodeListParser::Factor(){
1657 //Read unary operations
1660 //Take care of unary operators
1661 else if(IsUnaryOperator(Terminal()->GetToken()))
1663 SmStructureNode
*pUnary
= new SmUnHorNode(SmToken());
1664 SmNode
*pOper
= Terminal(),
1672 pUnary
->SetSubNodes(pOper
, pArg
);
1678 SmNode
* SmNodeListParser::Postfix(){
1681 SmNode
*pArg
= NULL
;
1682 if(IsPostfixOperator(Terminal()->GetToken()))
1684 else if(IsOperator(Terminal()->GetToken()))
1688 while(Terminal() && IsPostfixOperator(Terminal()->GetToken())) {
1689 SmStructureNode
*pUnary
= new SmUnHorNode(SmToken());
1690 SmNode
*pOper
= Take();
1691 pUnary
->SetSubNodes(pArg
, pOper
);
1697 SmNode
* SmNodeListParser::Error(){
1698 return new SmErrorNode(PE_UNEXPECTED_TOKEN
, SmToken());
1701 bool SmNodeListParser::IsOperator(const SmToken
&token
) {
1702 return IsRelationOperator(token
) ||
1703 IsSumOperator(token
) ||
1704 IsProductOperator(token
) ||
1705 IsUnaryOperator(token
) ||
1706 IsPostfixOperator(token
);
1709 bool SmNodeListParser::IsRelationOperator(const SmToken
&token
) {
1710 return token
.nGroup
& TGRELATION
;
1713 bool SmNodeListParser::IsSumOperator(const SmToken
&token
) {
1714 return token
.nGroup
& TGSUM
;
1717 bool SmNodeListParser::IsProductOperator(const SmToken
&token
) {
1718 return token
.nGroup
& TGPRODUCT
&&
1719 token
.eType
!= TWIDESLASH
&&
1720 token
.eType
!= TWIDEBACKSLASH
&&
1721 token
.eType
!= TUNDERBRACE
&&
1722 token
.eType
!= TOVERBRACE
&&
1723 token
.eType
!= TOVER
;
1726 bool SmNodeListParser::IsUnaryOperator(const SmToken
&token
) {
1727 return token
.nGroup
& TGUNOPER
&&
1728 (token
.eType
== TPLUS
||
1729 token
.eType
== TMINUS
||
1730 token
.eType
== TPLUSMINUS
||
1731 token
.eType
== TMINUSPLUS
||
1732 token
.eType
== TNEG
||
1733 token
.eType
== TUOPER
);
1736 bool SmNodeListParser::IsPostfixOperator(const SmToken
&token
) {
1737 return token
.eType
== TFACT
;
1740 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */