1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
11 #include "visitors.hxx"
12 #include "document.hxx"
14 #include "accessibility.hxx"
15 #include <comphelper/string.hxx>
17 void SmCursor::Move(OutputDevice
* pDev
, SmMovementDirection direction
, bool bMoveAnchor
){
18 SmCaretPosGraphEntry
* NewPos
= NULL
;
22 NewPos
= position
->Left
;
23 OSL_ENSURE(NewPos
, "NewPos shouldn't be NULL here!");
27 NewPos
= position
->Right
;
28 OSL_ENSURE(NewPos
, "NewPos shouldn't be NULL here!");
31 //Implementation is practically identical to MoveDown, except for a single if statement
32 //so I've implemented them together and added a direction == MoveDown to the if statements.
35 SmCaretLine from_line
= SmCaretPos2LineVisitor(pDev
, position
->CaretPos
).GetResult(),
36 best_line
, //Best approximated line found so far
37 curr_line
; //Current line
38 long dbp_sq
= 0; //Distance squared to best line
39 SmCaretPosGraphIterator it
= pGraph
->GetIterator();
41 //Reject it if it's the current position
42 if(it
->CaretPos
== position
->CaretPos
) continue;
44 curr_line
= SmCaretPos2LineVisitor(pDev
, it
->CaretPos
).GetResult();
45 //Reject anything above if we're moving down
46 if(curr_line
.GetTop() <= from_line
.GetTop() && direction
== MoveDown
) continue;
47 //Reject anything below if we're moving up
48 if(curr_line
.GetTop() + curr_line
.GetHeight() >= from_line
.GetTop() + from_line
.GetHeight()
49 && direction
== MoveUp
) continue;
50 //Compare if it to what we have, if we have anything yet
52 //Compute distance to current line squared, multiplied with a horizontial factor
53 long dp_sq
= curr_line
.SquaredDistanceX(from_line
) * HORIZONTICAL_DISTANCE_FACTOR
+
54 curr_line
.SquaredDistanceY(from_line
);
55 //Discard current line if best line is closer
56 if(dbp_sq
<= dp_sq
) continue;
58 //Take current line as the best
59 best_line
= curr_line
;
60 NewPos
= it
.Current();
61 //Update distance to best line
62 dbp_sq
= best_line
.SquaredDistanceX(from_line
) * HORIZONTICAL_DISTANCE_FACTOR
+
63 best_line
.SquaredDistanceY(from_line
);
67 OSL_FAIL("Movement direction not supported!");
77 void SmCursor::MoveTo(OutputDevice
* pDev
, Point pos
, bool bMoveAnchor
){
78 SmCaretLine best_line
, //Best line found so far, when iterating
79 curr_line
; //Current line, when iterating
80 SmCaretPosGraphEntry
* NewPos
= NULL
;
81 long dp_sq
= 0, //Distance to current line squared
82 dbp_sq
= 1; //Distance to best line squared
83 SmCaretPosGraphIterator it
= pGraph
->GetIterator();
85 OSL_ENSURE(it
->CaretPos
.IsValid(), "The caret position graph may not have invalid positions!");
86 //Compute current line
87 curr_line
= SmCaretPos2LineVisitor(pDev
, it
->CaretPos
).GetResult();
88 //If we have a position compare to it
90 //Compute squared distance to current line
91 dp_sq
= curr_line
.SquaredDistanceX(pos
) + curr_line
.SquaredDistanceY(pos
);
92 //If best line is closer, reject current line
93 if(dbp_sq
<= dp_sq
) continue;
95 //Accept current position as the best
96 best_line
= curr_line
;
97 NewPos
= it
.Current();
98 //Update distance to best line
99 dbp_sq
= best_line
.SquaredDistanceX(pos
) + best_line
.SquaredDistanceY(pos
);
109 void SmCursor::BuildGraph(){
110 //Save the current anchor and position
111 SmCaretPos _anchor
, _position
;
112 //Release pGraph if allocated
115 _anchor
= anchor
->CaretPos
;
117 _position
= position
->CaretPos
;
119 //Reset anchor and position as they point into an old graph
125 //Build the new graph
126 pGraph
= SmCaretPosGraphBuildingVisitor(pTree
).Graph();
128 //Restore anchor and position pointers
129 if(_anchor
.IsValid() || _position
.IsValid()){
130 SmCaretPosGraphIterator it
= pGraph
->GetIterator();
132 if(_anchor
== it
->CaretPos
)
133 anchor
= it
.Current();
134 if(_position
== it
->CaretPos
)
135 position
= it
.Current();
138 //Set position and anchor to first caret position
139 SmCaretPosGraphIterator it
= pGraph
->GetIterator();
141 position
= it
.Next();
145 OSL_ENSURE(position
->CaretPos
.IsValid(), "Position must be valid");
146 OSL_ENSURE(anchor
->CaretPos
.IsValid(), "Anchor must be valid");
149 bool SmCursor::SetCaretPosition(SmCaretPos pos
, bool moveAnchor
){
150 SmCaretPosGraphIterator it
= pGraph
->GetIterator();
152 if(it
->CaretPos
== pos
){
153 position
= it
.Current();
155 anchor
= it
.Current();
162 void SmCursor::AnnotateSelection(){
163 //TODO: Manage a state, reset it upon modification and optimize this call
164 SmSetSelectionVisitor(anchor
->CaretPos
, position
->CaretPos
, pTree
);
167 void SmCursor::Draw(OutputDevice
& pDev
, Point Offset
, bool isCaretVisible
){
168 SmCaretDrawingVisitor(pDev
, GetPosition(), Offset
, isCaretVisible
);
171 void SmCursor::DeletePrev(OutputDevice
* pDev
){
172 //Delete only a selection if there's a selection
178 SmNode
* pLine
= FindTopMostNodeInLine(position
->CaretPos
.pSelectedNode
);
179 SmStructureNode
* pLineParent
= pLine
->GetParent();
180 int nLineOffset
= pLineParent
->IndexOfSubNode(pLine
);
182 //If we're in front of a node who's parent is a TABLE
183 if(pLineParent
->GetType() == NTABLE
&& position
->CaretPos
.Index
== 0 && nLineOffset
> 0){
184 //Now we can merge with nLineOffset - 1
186 //Line to merge things into, so we can delete pLine
187 SmNode
* pMergeLine
= pLineParent
->GetSubNode(nLineOffset
-1);
188 OSL_ENSURE(pMergeLine
, "pMergeLine cannot be NULL!");
189 SmCaretPos PosAfterDelete
;
190 //Convert first line to list
191 SmNodeList
*pLineList
= NodeToList(pMergeLine
);
192 if(!pLineList
->empty()){
193 //Find iterator to patch
194 SmNodeList::iterator patchPoint
= pLineList
->end();
196 //Convert second line to list
197 NodeToList(pLine
, pLineList
);
198 //Patch the line list
200 PosAfterDelete
= PatchLineList(pLineList
, patchPoint
);
202 pLine
= SmNodeListParser().Parse(pLineList
);
205 pLineParent
->SetSubNode(nLineOffset
-1, pLine
);
206 //Delete the removed line slot
207 SmNodeArray
lines(pLineParent
->GetNumSubNodes()-1);
208 for(int i
= 0; i
< pLineParent
->GetNumSubNodes(); i
++){
210 lines
[i
] = pLineParent
->GetSubNode(i
);
211 else if(i
> nLineOffset
)
212 lines
[i
-1] = pLineParent
->GetSubNode(i
);
214 pLineParent
->SetSubNodes(lines
);
221 if(!SetCaretPosition(PosAfterDelete
, true))
222 SetCaretPosition(SmCaretPos(pLine
, 0), true);
226 //TODO: If we're in an empty (sub/super/*) script
227 /*}else if(pLineParent->GetType() == NSUBSUP &&
229 pLine->GetType() == NEXPRESSION &&
230 pLine->GetNumSubNodes() == 0){
231 //There's a (sub/super) script we can delete
232 //Consider selecting the entire script if GetNumSubNodes() != 0 or pLine->GetType() != NEXPRESSION
233 //TODO: Handle case where we delete a limit
236 //Else move select, and delete if not complex
238 this->Move(pDev
, MoveLeft
, false);
239 if(!this->HasComplexSelection())
244 void SmCursor::Delete(){
245 //Return if we don't have a selection to delete
252 //Set selected on nodes
255 //Find an arbitrary selected node
256 SmNode
* pSNode
= FindSelectedNode(pTree
);
257 OSL_ENSURE(pSNode
!= NULL
, "There must be a selection when HasSelection is true!");
259 //Find the topmost node of the line that holds the selection
260 SmNode
* pLine
= FindTopMostNodeInLine(pSNode
, true);
261 OSL_ENSURE(pLine
!= pTree
, "Shouldn't be able to select the entire tree");
263 //Get the parent of the line
264 SmStructureNode
* pLineParent
= pLine
->GetParent();
265 //Find line offset in parent
266 int nLineOffset
= pLineParent
->IndexOfSubNode(pLine
);
267 OSL_ENSURE(nLineOffset
!= -1, "pLine must be a child of it's parent!");
269 //Position after delete
270 SmCaretPos PosAfterDelete
;
272 SmNodeList
* pLineList
= NodeToList(pLine
);
274 //Take the selected nodes and delete them...
275 SmNodeList::iterator patchIt
= TakeSelectedNodesFromList(pLineList
);
277 //Get the position to set after delete
278 PosAfterDelete
= PatchLineList(pLineList
, patchIt
);
281 FinishEdit(pLineList
, pLineParent
, nLineOffset
, PosAfterDelete
);
284 void SmCursor::InsertNodes(SmNodeList
* pNewNodes
){
285 if(pNewNodes
->empty()){
293 //Position after insert should be after pNewNode
294 SmCaretPos PosAfterInsert
= SmCaretPos(pNewNodes
->back(), 1);
296 //Get the current position
297 const SmCaretPos pos
= position
->CaretPos
;
299 //Find top most of line that holds position
300 SmNode
* pLine
= FindTopMostNodeInLine(pos
.pSelectedNode
, false);
302 //Find line parent and line index in parent
303 SmStructureNode
* pLineParent
= pLine
->GetParent();
304 int nParentIndex
= pLineParent
->IndexOfSubNode(pLine
);
305 OSL_ENSURE(nParentIndex
!= -1, "pLine must be a subnode of pLineParent!");
307 //Convert line to list
308 SmNodeList
* pLineList
= NodeToList(pLine
);
310 //Find iterator for place to insert nodes
311 SmNodeList::iterator it
= FindPositionInLineList(pLineList
, pos
);
313 //Insert all new nodes
314 SmNodeList::iterator newIt
,
315 patchIt
= it
, // (pointless default value, fixes compiler warnings)
317 for(newIt
= pNewNodes
->begin(); newIt
!= pNewNodes
->end(); ++newIt
){
318 insIt
= pLineList
->insert(it
, *newIt
);
319 if(newIt
== pNewNodes
->begin())
321 if((*newIt
)->GetType() == NTEXT
)
322 PosAfterInsert
= SmCaretPos(*newIt
, ((SmTextNode
*)*newIt
)->GetText().getLength());
324 PosAfterInsert
= SmCaretPos(*newIt
, 1);
326 //Patch the places we've changed stuff
327 PatchLineList(pLineList
, patchIt
);
328 PosAfterInsert
= PatchLineList(pLineList
, it
);
329 //Release list, we've taken the nodes
334 FinishEdit(pLineList
, pLineParent
, nParentIndex
, PosAfterInsert
);
337 SmNodeList::iterator
SmCursor::FindPositionInLineList(SmNodeList
* pLineList
, SmCaretPos aCaretPos
) {
338 //Find iterator for position
339 SmNodeList::iterator it
;
340 for(it
= pLineList
->begin(); it
!= pLineList
->end(); ++it
){
341 if(*it
== aCaretPos
.pSelectedNode
){
342 if((*it
)->GetType() == NTEXT
){
343 //Split textnode if needed
344 if(aCaretPos
.Index
> 0){
345 SmTextNode
* pText
= (SmTextNode
*)aCaretPos
.pSelectedNode
;
346 OUString str1
= pText
->GetText().copy(0, aCaretPos
.Index
);
347 OUString str2
= pText
->GetText().copy(aCaretPos
.Index
);
348 pText
->ChangeText(str1
);
350 //Insert str2 as new text node
352 SmTextNode
* pNewText
= new SmTextNode(pText
->GetToken(), pText
->GetFontDesc());
353 pNewText
->ChangeText(str2
);
354 it
= pLineList
->insert(it
, pNewText
);
359 //it now pointer to the node following pos, so pLineList->insert(it, ...) will insert correctly
364 //If we didn't find pSelectedNode, it must be because the caret is in front of the line
365 return pLineList
->begin();
368 SmCaretPos
SmCursor::PatchLineList(SmNodeList
* pLineList
, SmNodeList::iterator aIter
) {
369 //The nodes we should consider merging
372 if(aIter
!= pLineList
->end())
374 if(aIter
!= pLineList
->begin()) {
380 //Check if there's textnodes to merge
383 prev
->GetType() == NTEXT
&&
384 next
->GetType() == NTEXT
&&
385 ( prev
->GetToken().eType
!= TNUMBER
||
386 next
->GetToken().eType
== TNUMBER
) ){
387 SmTextNode
*pText
= (SmTextNode
*)prev
,
388 *pOldN
= (SmTextNode
*)next
;
389 SmCaretPos
retval(pText
, pText
->GetText().getLength());
391 newText
+= pText
->GetText();
392 newText
+= pOldN
->GetText();
393 pText
->ChangeText(newText
);
395 pLineList
->erase(aIter
);
399 //Check if there's a SmPlaceNode to remove:
400 if(prev
&& next
&& prev
->GetType() == NPLACE
&& !SmNodeListParser::IsOperator(next
->GetToken())){
402 aIter
= pLineList
->erase(aIter
);
404 //Return caret pos in front of aIter
405 if(aIter
!= pLineList
->begin())
406 --aIter
; //Thus find node before aIter
407 if(aIter
== pLineList
->begin())
409 if((*aIter
)->GetType() == NTEXT
)
410 return SmCaretPos(*aIter
, ((SmTextNode
*)*aIter
)->GetText().getLength());
411 return SmCaretPos(*aIter
, 1);
413 if(prev
&& next
&& next
->GetType() == NPLACE
&& !SmNodeListParser::IsOperator(prev
->GetToken())){
414 aIter
= pLineList
->erase(aIter
);
416 if(prev
->GetType() == NTEXT
)
417 return SmCaretPos(prev
, ((SmTextNode
*)prev
)->GetText().getLength());
418 return SmCaretPos(prev
, 1);
421 //If we didn't do anything return
422 if(!prev
) //return an invalid to indicate we're in front of line
424 if(prev
->GetType() == NTEXT
)
425 return SmCaretPos(prev
, ((SmTextNode
*)prev
)->GetText().getLength());
426 return SmCaretPos(prev
, 1);
429 SmNodeList::iterator
SmCursor::TakeSelectedNodesFromList(SmNodeList
*pLineList
,
430 SmNodeList
*pSelectedNodes
) {
431 SmNodeList::iterator retval
;
432 SmNodeList::iterator it
= pLineList
->begin();
433 while(it
!= pLineList
->end()){
434 if((*it
)->IsSelected()){
436 if((*it
)->GetType() == NTEXT
) {
437 SmTextNode
* pText
= (SmTextNode
*)*it
;
438 OUString aText
= pText
->GetText();
439 //Start and lengths of the segments, 2 is the selected segment
440 int start2
= pText
->GetSelectionStart(),
441 start3
= pText
->GetSelectionEnd(),
443 len2
= start3
- start2
,
444 len3
= aText
.getLength() - start3
;
445 SmToken aToken
= pText
->GetToken();
446 sal_uInt16 eFontDesc
= pText
->GetFontDesc();
447 //If we need make segment 1
450 OUString str
= aText
.copy(start1
, len1
);
451 pText
->ChangeText(str
);
453 } else {//Remove it if not needed
454 it
= pLineList
->erase(it
);
457 //Set retval to be right after the selection
459 //if we need make segment 3
461 OUString str
= aText
.copy(start3
, len3
);
462 SmTextNode
* pSeg3
= new SmTextNode(aToken
, eFontDesc
);
463 pSeg3
->ChangeText(str
);
464 retval
= pLineList
->insert(it
, pSeg3
);
466 //If we need to save the selected text
467 if(pSelectedNodes
&& len2
> 0) {
468 OUString str
= aText
.copy(start2
, len2
);
469 SmTextNode
* pSeg2
= new SmTextNode(aToken
, eFontDesc
);
470 pSeg2
->ChangeText(str
);
471 pSelectedNodes
->push_back(pSeg2
);
473 } else { //if it's not textnode
475 retval
= it
= pLineList
->erase(it
);
477 pSelectedNodes
->push_back(pNode
);
487 void SmCursor::InsertSubSup(SmSubSup eSubSup
) {
493 SmNode
*pSNode
= FindSelectedNode(pTree
);
494 OSL_ENSURE(pSNode
!= NULL
, "There must be a selected node when HasSelection is true!");
495 pLine
= FindTopMostNodeInLine(pSNode
, sal_True
);
497 pLine
= FindTopMostNodeInLine(position
->CaretPos
.pSelectedNode
, sal_False
);
499 //Find Parent and offset in parent
500 SmStructureNode
*pLineParent
= pLine
->GetParent();
501 int nParentIndex
= pLineParent
->IndexOfSubNode(pLine
);
502 OSL_ENSURE(nParentIndex
!= -1, "pLine must be a subnode of pLineParent!");
504 //TODO: Consider handling special cases where parent is an SmOperNode,
505 // Maybe this method should be able to add limits to an SmOperNode...
507 //We begin modifying the tree here
510 //Convert line to list
511 SmNodeList
* pLineList
= NodeToList(pLine
);
513 //Take the selection, and/or find iterator for current position
514 SmNodeList
* pSelectedNodesList
= new SmNodeList();
515 SmNodeList::iterator it
;
517 it
= TakeSelectedNodesFromList(pLineList
, pSelectedNodesList
);
519 it
= FindPositionInLineList(pLineList
, position
->CaretPos
);
521 //Find node that this should be applied to
523 bool bPatchLine
= pSelectedNodesList
->size() > 0; //If the line should be patched later
524 if(it
!= pLineList
->begin()) {
529 //Create a new place node
530 pSubject
= new SmPlaceNode();
531 pSubject
->Prepare(pDocShell
->GetFormat(), *pDocShell
);
532 it
= pLineList
->insert(it
, pSubject
);
534 bPatchLine
= true; //We've modified the line it should be patched later.
537 //Wrap the subject in a SmSubSupNode
538 SmSubSupNode
* pSubSup
;
539 if(pSubject
->GetType() != NSUBSUP
){
541 token
.nGroup
= TGPOWER
;
542 pSubSup
= new SmSubSupNode(token
);
543 pSubSup
->SetBody(pSubject
);
547 pSubSup
= (SmSubSupNode
*)pSubject
;
548 //pSubject shouldn't be referenced anymore, pSubSup is the SmSubSupNode in pLineList we wish to edit.
549 //and it pointer to the element following pSubSup in pLineList.
552 //Patch the line if we noted that was needed previously
554 PatchLineList(pLineList
, it
);
556 //Convert existing, if any, sub-/superscript line to list
557 SmNode
*pScriptLine
= pSubSup
->GetSubSup(eSubSup
);
558 SmNodeList
* pScriptLineList
= NodeToList(pScriptLine
);
560 //Add selection to pScriptLineList
561 unsigned int nOldSize
= pScriptLineList
->size();
562 pScriptLineList
->insert(pScriptLineList
->end(), pSelectedNodesList
->begin(), pSelectedNodesList
->end());
563 delete pSelectedNodesList
;
564 pSelectedNodesList
= NULL
;
566 //Patch pScriptLineList if needed
567 if(0 < nOldSize
&& nOldSize
< pScriptLineList
->size()) {
568 SmNodeList::iterator iPatchPoint
= pScriptLineList
->begin();
569 std::advance(iPatchPoint
, nOldSize
);
570 PatchLineList(pScriptLineList
, iPatchPoint
);
573 //Find caret pos, that should be used after sub-/superscription.
574 SmCaretPos PosAfterScript
; //Leave invalid for first position
575 if(pScriptLineList
->size() > 0)
576 PosAfterScript
= SmCaretPos::GetPosAfter(pScriptLineList
->back());
578 //Parse pScriptLineList
579 pScriptLine
= SmNodeListParser().Parse(pScriptLineList
);
580 delete pScriptLineList
;
581 pScriptLineList
= NULL
;
583 //Insert pScriptLine back into the tree
584 pSubSup
->SetSubSup(eSubSup
, pScriptLine
);
587 FinishEdit(pLineList
, pLineParent
, nParentIndex
, PosAfterScript
, pScriptLine
);
590 bool SmCursor::InsertLimit(SmSubSup eSubSup
, bool bMoveCaret
) {
591 //Find a subject to set limits on
592 SmOperNode
*pSubject
= NULL
;
593 //Check if pSelectedNode might be a subject
594 if(position
->CaretPos
.pSelectedNode
->GetType() == NOPER
)
595 pSubject
= (SmOperNode
*)position
->CaretPos
.pSelectedNode
;
597 //If not, check if parent of the current line is a SmOperNode
598 SmNode
*pLineNode
= FindTopMostNodeInLine(position
->CaretPos
.pSelectedNode
, sal_False
);
599 if(pLineNode
->GetParent() && pLineNode
->GetParent()->GetType() == NOPER
)
600 pSubject
= (SmOperNode
*)pLineNode
->GetParent();
603 //Abort operation if we're not in the appropriate context
609 //Find the sub sup node
610 SmSubSupNode
*pSubSup
= NULL
;
611 //Check if there's already one there...
612 if(pSubject
->GetSubNode(0)->GetType() == NSUBSUP
)
613 pSubSup
= (SmSubSupNode
*)pSubject
->GetSubNode(0);
614 else { //if not create a new SmSubSupNode
616 token
.nGroup
= TGLIMIT
;
617 pSubSup
= new SmSubSupNode(token
);
619 pSubSup
->SetBody(pSubject
->GetSubNode(0));
620 //Replace the operation of the SmOperNode
621 pSubject
->SetSubNode(0, pSubSup
);
624 //Create the limit, if needed
625 SmCaretPos PosAfterLimit
;
626 SmNode
*pLine
= NULL
;
627 if(!pSubSup
->GetSubSup(eSubSup
)){
628 pLine
= new SmPlaceNode();
629 pSubSup
->SetSubSup(eSubSup
, pLine
);
630 PosAfterLimit
= SmCaretPos(pLine
, 1);
631 //If it's already there... let's move the caret
632 } else if(bMoveCaret
){
633 pLine
= pSubSup
->GetSubSup(eSubSup
);
634 SmNodeList
* pLineList
= NodeToList(pLine
);
635 if(pLineList
->size() > 0)
636 PosAfterLimit
= SmCaretPos::GetPosAfter(pLineList
->back());
637 pLine
= SmNodeListParser().Parse(pLineList
);
639 pSubSup
->SetSubSup(eSubSup
, pLine
);
642 //Rebuild graph of caret positions
648 if(!SetCaretPosition(PosAfterLimit
, true))
649 SetCaretPosition(SmCaretPos(pLine
, 0), true);
656 void SmCursor::InsertBrackets(SmBracketType eBracketType
) {
664 SmNode
*pSNode
= FindSelectedNode(pTree
);
665 OSL_ENSURE(pSNode
!= NULL
, "There must be a selected node if HasSelection()");
666 pLine
= FindTopMostNodeInLine(pSNode
, sal_True
);
668 pLine
= FindTopMostNodeInLine(position
->CaretPos
.pSelectedNode
, sal_False
);
670 //Find parent and offset in parent
671 SmStructureNode
*pLineParent
= pLine
->GetParent();
672 int nParentIndex
= pLineParent
->IndexOfSubNode(pLine
);
673 OSL_ENSURE( nParentIndex
!= -1, "pLine must be a subnode of pLineParent!");
675 //Convert line to list
676 SmNodeList
*pLineList
= NodeToList(pLine
);
678 //Take the selection, and/or find iterator for current position
679 SmNodeList
*pSelectedNodesList
= new SmNodeList();
680 SmNodeList::iterator it
;
682 it
= TakeSelectedNodesFromList(pLineList
, pSelectedNodesList
);
684 it
= FindPositionInLineList(pLineList
, position
->CaretPos
);
686 //If there's no selected nodes, create a place node
688 SmCaretPos PosAfterInsert
;
689 if(pSelectedNodesList
->empty()) {
690 pBodyNode
= new SmPlaceNode();
691 PosAfterInsert
= SmCaretPos(pBodyNode
, 1);
693 pBodyNode
= SmNodeListParser().Parse(pSelectedNodesList
);
695 delete pSelectedNodesList
;
698 SmToken
aTok(TLEFT
, '\0', "left", 0, 5);
699 SmBraceNode
*pBrace
= new SmBraceNode(aTok
);
700 pBrace
->SetScaleMode(SCALE_HEIGHT
);
701 SmNode
*pLeft
= CreateBracket(eBracketType
, true),
702 *pRight
= CreateBracket(eBracketType
, false);
703 SmBracebodyNode
*pBody
= new SmBracebodyNode(SmToken());
704 pBody
->SetSubNodes(pBodyNode
, NULL
);
705 pBrace
->SetSubNodes(pLeft
, pBody
, pRight
);
706 pBrace
->Prepare(pDocShell
->GetFormat(), *pDocShell
);
709 pLineList
->insert(it
, pBrace
);
710 //Patch line (I think this is good enough)
711 SmCaretPos pAfter
= PatchLineList(pLineList
, it
);
712 if( !PosAfterInsert
.IsValid() )
713 PosAfterInsert
= pAfter
;
716 FinishEdit(pLineList
, pLineParent
, nParentIndex
, PosAfterInsert
);
719 SmNode
*SmCursor::CreateBracket(SmBracketType eBracketType
, bool bIsLeft
) {
722 switch(eBracketType
){
724 aTok
= SmToken(TNONE
, '\0', "none", TGLBRACES
| TGRBRACES
, 0);
727 aTok
= SmToken(TLPARENT
, MS_LPARENT
, "(", TGLBRACES
, 5);
730 aTok
= SmToken(TLBRACKET
, MS_LBRACKET
, "[", TGLBRACES
, 5);
732 case DoubleSquareBrackets
:
733 aTok
= SmToken(TLDBRACKET
, MS_LDBRACKET
, "ldbracket", TGLBRACES
, 5);
736 aTok
= SmToken(TLLINE
, MS_LINE
, "lline", TGLBRACES
, 5);
738 case DoubleLineBrackets
:
739 aTok
= SmToken(TLDLINE
, MS_DLINE
, "ldline", TGLBRACES
, 5);
742 aTok
= SmToken(TLBRACE
, MS_LBRACE
, "lbrace", TGLBRACES
, 5);
745 aTok
= SmToken(TLANGLE
, MS_LANGLE
, "langle", TGLBRACES
, 5);
748 aTok
= SmToken(TLCEIL
, MS_LCEIL
, "lceil", TGLBRACES
, 5);
751 aTok
= SmToken(TLFLOOR
, MS_LFLOOR
, "lfloor", TGLBRACES
, 5);
755 switch(eBracketType
) {
757 aTok
= SmToken(TNONE
, '\0', "none", TGLBRACES
| TGRBRACES
, 0);
760 aTok
= SmToken(TRPARENT
, MS_RPARENT
, ")", TGRBRACES
, 5);
763 aTok
= SmToken(TRBRACKET
, MS_RBRACKET
, "]", TGRBRACES
, 5);
765 case DoubleSquareBrackets
:
766 aTok
= SmToken(TRDBRACKET
, MS_RDBRACKET
, "rdbracket", TGRBRACES
, 5);
769 aTok
= SmToken(TRLINE
, MS_LINE
, "rline", TGRBRACES
, 5);
771 case DoubleLineBrackets
:
772 aTok
= SmToken(TRDLINE
, MS_DLINE
, "rdline", TGRBRACES
, 5);
775 aTok
= SmToken(TRBRACE
, MS_RBRACE
, "rbrace", TGRBRACES
, 5);
778 aTok
= SmToken(TRANGLE
, MS_RANGLE
, "rangle", TGRBRACES
, 5);
781 aTok
= SmToken(TRCEIL
, MS_RCEIL
, "rceil", TGRBRACES
, 5);
784 aTok
= SmToken(TRFLOOR
, MS_RFLOOR
, "rfloor", TGRBRACES
, 5);
788 SmNode
* pRetVal
= new SmMathSymbolNode(aTok
);
789 pRetVal
->SetScaleMode(SCALE_HEIGHT
);
793 bool SmCursor::InsertRow() {
799 SmNode
*pSNode
= FindSelectedNode(pTree
);
800 OSL_ENSURE(pSNode
!= NULL
, "There must be a selected node if HasSelection()");
801 pLine
= FindTopMostNodeInLine(pSNode
, sal_True
);
803 pLine
= FindTopMostNodeInLine(position
->CaretPos
.pSelectedNode
, sal_False
);
805 //Find parent and offset in parent
806 SmStructureNode
*pLineParent
= pLine
->GetParent();
807 int nParentIndex
= pLineParent
->IndexOfSubNode(pLine
);
808 OSL_ENSURE( nParentIndex
!= -1, "pLine must be a subnode of pLineParent!");
810 //Discover the context of this command
811 SmTableNode
*pTable
= NULL
;
812 SmMatrixNode
*pMatrix
= NULL
;
813 int nTableIndex
= nParentIndex
;
814 if(pLineParent
->GetType() == NTABLE
)
815 pTable
= (SmTableNode
*)pLineParent
;
816 //If it's warped in a SmLineNode, we can still insert a newline
817 else if(pLineParent
->GetType() == NLINE
&&
818 pLineParent
->GetParent() &&
819 pLineParent
->GetParent()->GetType() == NTABLE
) {
820 //NOTE: This hack might give problems if we stop ignoring SmAlignNode
821 pTable
= (SmTableNode
*)pLineParent
->GetParent();
822 nTableIndex
= pTable
->IndexOfSubNode(pLineParent
);
823 OSL_ENSURE(nTableIndex
!= -1, "pLineParent must be a child of its parent!");
825 if(pLineParent
->GetType() == NMATRIX
)
826 pMatrix
= (SmMatrixNode
*)pLineParent
;
828 //If we're not in a context that supports InsertRow, return sal_False
829 if(!pTable
&& !pMatrix
)
832 //Now we start editing
835 //Convert line to list
836 SmNodeList
*pLineList
= NodeToList(pLine
);
838 //Find position in line
839 SmNodeList::iterator it
;
841 //Take the selected nodes and delete them...
842 it
= TakeSelectedNodesFromList(pLineList
);
844 it
= FindPositionInLineList(pLineList
, position
->CaretPos
);
846 //New caret position after inserting the newline/row in whatever context
847 SmCaretPos PosAfterInsert
;
849 //If we're in the context of a table
851 SmNodeList
*pNewLineList
= new SmNodeList();
852 //Move elements from pLineList to pNewLineList
853 pNewLineList
->splice(pNewLineList
->begin(), *pLineList
, it
, pLineList
->end());
854 //Make sure it is valid again
855 it
= pLineList
->end();
856 if(it
!= pLineList
->begin())
858 if(pNewLineList
->empty())
859 pNewLineList
->push_front(new SmPlaceNode());
861 SmNode
*pNewLine
= SmNodeListParser().Parse(pNewLineList
);
863 //Wrap pNewLine in SmLineNode if needed
864 if(pLineParent
->GetType() == NLINE
) {
865 SmLineNode
*pNewLineNode
= new SmLineNode(SmToken(TNEWLINE
, '\0', "newline"));
866 pNewLineNode
->SetSubNodes(pNewLine
, NULL
);
867 pNewLine
= pNewLineNode
;
870 PosAfterInsert
= SmCaretPos(pNewLine
, 0);
871 //Move other nodes if needed
872 for( int i
= pTable
->GetNumSubNodes(); i
> nTableIndex
+ 1; i
--)
873 pTable
->SetSubNode(i
, pTable
->GetSubNode(i
-1));
876 pTable
->SetSubNode(nTableIndex
+ 1, pNewLine
);
878 //Check if we need to change token type:
879 if(pTable
->GetNumSubNodes() > 2 && pTable
->GetToken().eType
== TBINOM
) {
880 SmToken tok
= pTable
->GetToken();
882 pTable
->SetToken(tok
);
885 //If we're in the context of a matrix
887 //Find position after insert and patch the list
888 PosAfterInsert
= PatchLineList(pLineList
, it
);
889 //Move other children
890 sal_uInt16 rows
= pMatrix
->GetNumRows();
891 sal_uInt16 cols
= pMatrix
->GetNumCols();
892 int nRowStart
= (nParentIndex
- nParentIndex
% cols
) + cols
;
893 for( int i
= pMatrix
->GetNumSubNodes() + cols
- 1; i
>= nRowStart
+ cols
; i
--)
894 pMatrix
->SetSubNode(i
, pMatrix
->GetSubNode(i
- cols
));
895 for( int i
= nRowStart
; i
< nRowStart
+ cols
; i
++) {
896 SmPlaceNode
*pNewLine
= new SmPlaceNode();
897 if(i
== nParentIndex
+ cols
)
898 PosAfterInsert
= SmCaretPos(pNewLine
, 0);
899 pMatrix
->SetSubNode(i
, pNewLine
);
901 pMatrix
->SetRowCol(rows
+ 1, cols
);
903 OSL_FAIL("We must be either the context of a table or matrix!");
906 FinishEdit(pLineList
, pLineParent
, nParentIndex
, PosAfterInsert
);
907 //FinishEdit is actually used to handle siturations where parent is an instance of
908 //SmSubSupNode. In this case parent should always be a table or matrix, however, for
909 //code reuse we just use FinishEdit() here too.
913 void SmCursor::InsertFraction() {
919 SmNode
*pSNode
= FindSelectedNode(pTree
);
920 OSL_ENSURE(pSNode
!= NULL
, "There must be a selected node when HasSelection is true!");
921 pLine
= FindTopMostNodeInLine(pSNode
, sal_True
);
923 pLine
= FindTopMostNodeInLine(position
->CaretPos
.pSelectedNode
, sal_False
);
925 //Find Parent and offset in parent
926 SmStructureNode
*pLineParent
= pLine
->GetParent();
927 int nParentIndex
= pLineParent
->IndexOfSubNode(pLine
);
928 OSL_ENSURE(nParentIndex
!= -1, "pLine must be a subnode of pLineParent!");
930 //We begin modifying the tree here
933 //Convert line to list
934 SmNodeList
* pLineList
= NodeToList(pLine
);
936 //Take the selection, and/or find iterator for current position
937 SmNodeList
* pSelectedNodesList
= new SmNodeList();
938 SmNodeList::iterator it
;
940 it
= TakeSelectedNodesFromList(pLineList
, pSelectedNodesList
);
942 it
= FindPositionInLineList(pLineList
, position
->CaretPos
);
944 //Create pNum, and pDenom
945 bool bEmptyFraction
= pSelectedNodesList
->empty();
946 SmNode
*pNum
= bEmptyFraction
948 : SmNodeListParser().Parse(pSelectedNodesList
);
949 SmNode
*pDenom
= new SmPlaceNode();
950 delete pSelectedNodesList
;
951 pSelectedNodesList
= NULL
;
953 //Create new fraction
954 SmBinVerNode
*pFrac
= new SmBinVerNode(SmToken(TOVER
, '\0', "over", TGPRODUCT
, 0));
955 SmNode
*pRect
= new SmRectangleNode(SmToken());
956 pFrac
->SetSubNodes(pNum
, pRect
, pDenom
);
958 //Insert in pLineList
959 SmNodeList::iterator patchIt
= pLineList
->insert(it
, pFrac
);
960 PatchLineList(pLineList
, patchIt
);
961 PatchLineList(pLineList
, it
);
964 SmNode
*pSelectedNode
= bEmptyFraction
? pNum
: pDenom
;
965 FinishEdit(pLineList
, pLineParent
, nParentIndex
, SmCaretPos(pSelectedNode
, 1));
968 void SmCursor::InsertText(OUString aString
)
975 token
.eType
= TIDENT
;
976 token
.cMathChar
= '\0';
979 token
.aText
= aString
;
981 SmTextNode
* pText
= new SmTextNode(token
, FNT_VARIABLE
);
983 //Prepare the new node
984 pText
->Prepare(pDocShell
->GetFormat(), *pDocShell
);
985 pText
->AdjustFontDesc();
987 SmNodeList
* pList
= new SmNodeList();
988 pList
->push_front(pText
);
994 void SmCursor::InsertElement(SmFormulaElement element
){
1000 SmNode
* pNewNode
= NULL
;
1005 token
.nGroup
= TGBLANK
;
1007 pNewNode
= new SmBlankNode(token
);
1009 case FactorialElement
:
1011 SmToken
token(TFACT
, MS_FACT
, "fact", TGUNOPER
, 5);
1012 pNewNode
= new SmMathSymbolNode(token
);
1017 token
.eType
= TPLUS
;
1018 token
.cMathChar
= MS_PLUS
;
1019 token
.nGroup
= TGUNOPER
| TGSUM
;
1022 pNewNode
= new SmMathSymbolNode(token
);
1027 token
.eType
= TMINUS
;
1028 token
.cMathChar
= MS_MINUS
;
1029 token
.nGroup
= TGUNOPER
| TGSUM
;
1032 pNewNode
= new SmMathSymbolNode(token
);
1037 token
.eType
= TCDOT
;
1038 token
.cMathChar
= MS_CDOT
;
1039 token
.nGroup
= TGPRODUCT
;
1040 token
.aText
= "cdot";
1041 pNewNode
= new SmMathSymbolNode(token
);
1046 token
.eType
= TASSIGN
;
1047 token
.cMathChar
= MS_ASSIGN
;
1048 token
.nGroup
= TGRELATION
;
1050 pNewNode
= new SmMathSymbolNode(token
);
1052 case LessThanElement
:
1056 token
.cMathChar
= MS_LT
;
1057 token
.nGroup
= TGRELATION
;
1059 pNewNode
= new SmMathSymbolNode(token
);
1061 case GreaterThanElement
:
1065 token
.cMathChar
= MS_GT
;
1066 token
.nGroup
= TGRELATION
;
1068 pNewNode
= new SmMathSymbolNode(token
);
1070 case PercentElement
:
1073 token
.eType
= TTEXT
;
1074 token
.cMathChar
= MS_PERCENT
;
1076 token
.aText
= "\"%\"";
1077 pNewNode
= new SmMathSymbolNode(token
);
1080 OSL_FAIL("Element unknown!");
1082 OSL_ENSURE(pNewNode
!= NULL
, "No new node was created!");
1086 //Prepare the new node
1087 pNewNode
->Prepare(pDocShell
->GetFormat(), *pDocShell
);
1090 SmNodeList
* pList
= new SmNodeList();
1091 pList
->push_front(pNewNode
);
1097 void SmCursor::InsertSpecial(OUString aString
)
1102 aString
= comphelper::string::strip(aString
, ' ');
1104 //Create instance of special node
1106 token
.eType
= TSPECIAL
;
1107 token
.cMathChar
= '\0';
1110 token
.aText
= aString
;
1111 SmSpecialNode
* pSpecial
= new SmSpecialNode(token
);
1113 //Prepare the special node
1114 pSpecial
->Prepare(pDocShell
->GetFormat(), *pDocShell
);
1117 SmNodeList
* pList
= new SmNodeList();
1118 pList
->push_front(pSpecial
);
1124 void SmCursor::InsertCommand(sal_uInt16 nCommand
) {
1130 InsertLimit(CSUB
, true);
1133 InsertLimit(CSUP
, true);
1136 if(InsertLimit(CSUB
, true))
1137 InsertLimit(CSUP
, true);
1140 InsertCommandText(SM_RESSTR(nCommand
));
1145 void SmCursor::InsertCommandText(OUString aCommandText
) {
1146 //Parse the sub expression
1147 SmNode
* pSubExpr
= SmParser().ParseExpression(aCommandText
);
1149 //Prepare the subtree
1150 pSubExpr
->Prepare(pDocShell
->GetFormat(), *pDocShell
);
1152 //Convert subtree to list
1153 SmNodeList
* pLineList
= NodeToList(pSubExpr
);
1157 //Delete any selection
1161 InsertNodes(pLineList
);
1166 void SmCursor::Copy(){
1170 //Find selected node
1171 SmNode
* pSNode
= FindSelectedNode(pTree
);
1173 SmNode
* pLine
= FindTopMostNodeInLine(pSNode
, true);
1175 //Clone selected nodes
1177 if(IsLineCompositionNode(pLine
))
1178 pList
= CloneLineToList((SmStructureNode
*)pLine
, true);
1180 pList
= new SmNodeList();
1181 //Special care to only clone selected text
1182 if(pLine
->GetType() == NTEXT
) {
1183 SmTextNode
*pText
= (SmTextNode
*)pLine
;
1184 SmTextNode
*pClone
= new SmTextNode( pText
->GetToken(), pText
->GetFontDesc() );
1185 int start
= pText
->GetSelectionStart(),
1186 length
= pText
->GetSelectionEnd() - pText
->GetSelectionStart();
1187 pClone
->ChangeText(pText
->GetText().copy(start
, length
));
1188 pClone
->SetScaleMode(pText
->GetScaleMode());
1189 pList
->push_front(pClone
);
1191 SmCloningVisitor aCloneFactory
;
1192 pList
->push_front(aCloneFactory
.Clone(pLine
));
1197 if(pList
->size() > 0)
1198 SetClipboard(pList
);
1201 void SmCursor::Paste() {
1205 if(pClipboard
&& pClipboard
->size() > 0)
1206 InsertNodes(CloneList(pClipboard
));
1211 SmNodeList
* SmCursor::CloneList(SmNodeList
* pList
){
1212 SmCloningVisitor aCloneFactory
;
1213 SmNodeList
* pClones
= new SmNodeList();
1215 SmNodeList::iterator it
;
1216 for(it
= pList
->begin(); it
!= pList
->end(); ++it
){
1217 SmNode
*pClone
= aCloneFactory
.Clone(*it
);
1218 pClones
->push_back(pClone
);
1224 void SmCursor::SetClipboard(SmNodeList
* pList
){
1226 //Delete all nodes on the clipboard
1227 SmNodeList::iterator it
;
1228 for(it
= pClipboard
->begin(); it
!= pClipboard
->end(); ++it
)
1235 SmNode
* SmCursor::FindTopMostNodeInLine(SmNode
* pSNode
, bool MoveUpIfSelected
){
1236 //If we haven't got a subnode
1240 //Move up parent until we find a node who's
1241 //parent is NULL or isn't selected and not a type of:
1248 while(pSNode
->GetParent() &&
1249 ((MoveUpIfSelected
&&
1250 pSNode
->GetParent()->IsSelected()) ||
1251 IsLineCompositionNode(pSNode
->GetParent())))
1252 pSNode
= pSNode
->GetParent();
1253 //Now we have the selection line node
1257 SmNode
* SmCursor::FindSelectedNode(SmNode
* pNode
){
1258 SmNodeIterator
it(pNode
);
1260 if(it
->IsSelected())
1261 return it
.Current();
1262 SmNode
* pRetVal
= FindSelectedNode(it
.Current());
1269 SmNodeList
* SmCursor::LineToList(SmStructureNode
* pLine
, SmNodeList
* list
){
1270 SmNodeIterator
it(pLine
);
1272 switch(it
->GetType()){
1279 LineToList((SmStructureNode
*)it
.Current(), list
);
1282 delete it
.Current();
1285 list
->push_back(it
.Current());
1288 SmNodeArray
emptyArray(0);
1289 pLine
->SetSubNodes(emptyArray
);
1294 SmNodeList
* SmCursor::CloneLineToList(SmStructureNode
* pLine
, bool bOnlyIfSelected
, SmNodeList
* pList
){
1295 SmCloningVisitor aCloneFactory
;
1296 SmNodeIterator
it(pLine
);
1298 if( IsLineCompositionNode( it
.Current() ) )
1299 CloneLineToList( (SmStructureNode
*)it
.Current(), bOnlyIfSelected
, pList
);
1300 else if( (!bOnlyIfSelected
|| it
->IsSelected()) && it
->GetType() != NERROR
) {
1301 //Only clone selected text from SmTextNode
1302 if(it
->GetType() == NTEXT
) {
1303 SmTextNode
*pText
= (SmTextNode
*)it
.Current();
1304 SmTextNode
*pClone
= new SmTextNode( it
->GetToken(), pText
->GetFontDesc() );
1305 int start
= pText
->GetSelectionStart(),
1306 length
= pText
->GetSelectionEnd() - pText
->GetSelectionStart();
1307 pClone
->ChangeText(pText
->GetText().copy(start
, length
));
1308 pClone
->SetScaleMode(pText
->GetScaleMode());
1309 pList
->push_back(pClone
);
1311 pList
->push_back(aCloneFactory
.Clone(it
.Current()));
1317 bool SmCursor::IsLineCompositionNode(SmNode
* pNode
){
1318 switch(pNode
->GetType()){
1331 int SmCursor::CountSelectedNodes(SmNode
* pNode
){
1333 SmNodeIterator
it(pNode
);
1335 if(it
->IsSelected() && !IsLineCompositionNode(it
.Current()))
1337 nCount
+= CountSelectedNodes(it
.Current());
1342 bool SmCursor::HasComplexSelection(){
1345 AnnotateSelection();
1347 return CountSelectedNodes(pTree
) > 1;
1350 void SmCursor::FinishEdit(SmNodeList
* pLineList
,
1351 SmStructureNode
* pParent
,
1353 SmCaretPos PosAfterEdit
,
1354 SmNode
* pStartLine
) {
1355 //Store number of nodes in line for later
1356 int entries
= pLineList
->size();
1358 //Parse list of nodes to a tree
1359 SmNodeListParser parser
;
1360 SmNode
* pLine
= parser
.Parse(pLineList
);
1363 //Check if we're making the body of a subsup node bigger than one
1364 if(pParent
->GetType() == NSUBSUP
&&
1365 nParentIndex
== 0 &&
1367 //Wrap pLine in scalable round brackets
1368 SmToken
aTok(TLEFT
, '\0', "left", 0, 5);
1369 SmBraceNode
*pBrace
= new SmBraceNode(aTok
);
1370 pBrace
->SetScaleMode(SCALE_HEIGHT
);
1371 SmNode
*pLeft
= CreateBracket(RoundBrackets
, true),
1372 *pRight
= CreateBracket(RoundBrackets
, false);
1373 SmBracebodyNode
*pBody
= new SmBracebodyNode(SmToken());
1374 pBody
->SetSubNodes(pLine
, NULL
);
1375 pBrace
->SetSubNodes(pLeft
, pBody
, pRight
);
1376 pBrace
->Prepare(pDocShell
->GetFormat(), *pDocShell
);
1378 //TODO: Consider the following alternative behavior:
1379 //Consider the line: A + {B + C}^D lsub E
1380 //Here pLineList is B, + and C and pParent is a subsup node with
1381 //both RSUP and LSUB set. Imagine the user just inserted "B +" in
1382 //the body of the subsup node...
1383 //The most natural thing to do would be to make the line like this:
1384 //A + B lsub E + C ^ D
1385 //E.g. apply LSUB and LSUP to the first element in pLineList and RSUP
1386 //and RSUB to the last eleent in pLineList. But how should this act
1387 //for CSUP and CSUB ???
1388 //For this reason and because brackets was faster to implement, this solution
1389 //have been choosen. It might be worth working on the other solution later...
1392 //Set pStartLine if NULL
1396 //Insert it back into the parent
1397 pParent
->SetSubNode(nParentIndex
, pLine
);
1399 //Rebuild graph of caret position
1403 AnnotateSelection(); //Update selection annotation!
1405 //Set caret position
1406 if(!SetCaretPosition(PosAfterEdit
, true))
1407 SetCaretPosition(SmCaretPos(pStartLine
, 0), true);
1413 void SmCursor::BeginEdit(){
1414 if(nEditSections
++ > 0) return;
1416 bIsEnabledSetModifiedSmDocShell
= pDocShell
->IsEnableSetModified();
1417 if( bIsEnabledSetModifiedSmDocShell
)
1418 pDocShell
->EnableSetModified( sal_False
);
1421 void SmCursor::EndEdit(){
1422 if(--nEditSections
> 0) return;
1424 pDocShell
->SetFormulaArranged(sal_False
);
1425 //Okay, I don't know what this does... :)
1426 //It's used in SmDocShell::SetText and with places where everything is modified.
1427 //I think it does some magic, with sfx, but everything is totally undocumented so
1428 //it's kinda hard to tell...
1429 if ( bIsEnabledSetModifiedSmDocShell
)
1430 pDocShell
->EnableSetModified( bIsEnabledSetModifiedSmDocShell
);
1431 //I think this notifies people around us that we've modified this document...
1432 pDocShell
->SetModified(sal_True
);
1433 //I think SmDocShell uses this value when it sends an update graphics event
1434 //Anyway comments elsewhere suggests it need to be updated...
1435 pDocShell
->nModifyCount
++;
1437 //TODO: Consider copying the update accessability code from SmDocShell::SetText in here...
1438 //This somehow updates the size of SmGraphicView if it is running in embedded mode
1439 if( pDocShell
->GetCreateMode() == SFX_CREATE_MODE_EMBEDDED
)
1440 pDocShell
->OnDocumentPrinterChanged(0);
1442 //Request a replaint...
1445 //Update the edit engine and text of the document
1447 SmNodeToTextVisitor(pTree
, formula
);
1448 //pTree->CreateTextFromNode(formula);
1449 pDocShell
->aText
= formula
;
1450 pDocShell
->GetEditEngine().QuickInsertText( formula
, ESelection( 0, 0, EE_PARA_ALL
, EE_TEXTPOS_ALL
) );
1451 pDocShell
->GetEditEngine().QuickFormatDoc();
1454 void SmCursor::RequestRepaint(){
1455 SmViewShell
*pViewSh
= SmGetActiveView();
1457 if ( SFX_CREATE_MODE_EMBEDDED
== pDocShell
->GetCreateMode() )
1458 pDocShell
->Repaint();
1460 pViewSh
->GetGraphicWindow().Invalidate();
1464 bool SmCursor::IsAtTailOfBracket(SmBracketType eBracketType
, SmBraceNode
** ppBraceNode
) const {
1465 const SmCaretPos pos
= GetPosition();
1466 if (!pos
.IsValid()) {
1470 SmNode
* pNode
= pos
.pSelectedNode
;
1472 if (pNode
->GetType() == NTEXT
) {
1473 SmTextNode
* pTextNode
= static_cast<SmTextNode
*>(pNode
);
1474 if (pos
.Index
< pTextNode
->GetText().getLength()) {
1475 // The cursor is on a text node and at the middle of it.
1479 if (pos
.Index
< 1) {
1485 SmStructureNode
* pParentNode
= pNode
->GetParent();
1487 // There's no brace body node in the ancestors.
1491 sal_uInt16 index
= pNode
->FindIndex();
1492 if (index
+ 1 != pParentNode
->GetNumSubNodes()) {
1493 // The cursor is not at the tail at one of ancestor nodes.
1497 pNode
= pParentNode
;
1498 if (pNode
->GetType() == NBRACEBODY
) {
1499 // Found the brace body node.
1504 SmStructureNode
* pBraceNodeTmp
= pNode
->GetParent();
1505 if (!pBraceNodeTmp
|| pBraceNodeTmp
->GetType() != NBRACE
) {
1506 // Brace node is invalid.
1510 SmBraceNode
* pBraceNode
= static_cast<SmBraceNode
*>(pBraceNodeTmp
);
1511 SmMathSymbolNode
* pClosingNode
= pBraceNode
->ClosingBrace();
1512 if (!pClosingNode
) {
1513 // Couldn't get closing symbol node.
1517 // Check if the closing brace matches eBracketType.
1518 SmTokenType eClosingTokenType
= pClosingNode
->GetToken().eType
;
1519 switch (eBracketType
) {
1520 case NoneBrackets
: if (eClosingTokenType
!= TNONE
) { return false; } break;
1521 case RoundBrackets
: if (eClosingTokenType
!= TRPARENT
) { return false; } break;
1522 case SquareBrackets
: if (eClosingTokenType
!= TRBRACKET
) { return false; } break;
1523 case DoubleSquareBrackets
: if (eClosingTokenType
!= TRDBRACKET
) { return false; } break;
1524 case LineBrackets
: if (eClosingTokenType
!= TRLINE
) { return false; } break;
1525 case DoubleLineBrackets
: if (eClosingTokenType
!= TRDLINE
) { return false; } break;
1526 case CurlyBrackets
: if (eClosingTokenType
!= TRBRACE
) { return false; } break;
1527 case AngleBrackets
: if (eClosingTokenType
!= TRANGLE
) { return false; } break;
1528 case CeilBrackets
: if (eClosingTokenType
!= TRCEIL
) { return false; } break;
1529 case FloorBrackets
: if (eClosingTokenType
!= TRFLOOR
) { return false; } break;
1535 *ppBraceNode
= static_cast<SmBraceNode
*>(pBraceNode
);
1541 void SmCursor::MoveAfterBracket(SmBraceNode
* pBraceNode
, bool bMoveAnchor
)
1543 position
->CaretPos
.pSelectedNode
= pBraceNode
;
1544 position
->CaretPos
.Index
= 1;
1546 anchor
->CaretPos
.pSelectedNode
= pBraceNode
;
1547 anchor
->CaretPos
.Index
= 1;
1553 /////////////////////////////////////// SmNodeListParser ///////////////////////////////////////
1555 SmNode
* SmNodeListParser::Parse(SmNodeList
* list
, bool bDeleteErrorNodes
){
1557 if(bDeleteErrorNodes
){
1558 //Delete error nodes
1559 SmNodeList::iterator it
= pList
->begin();
1560 while(it
!= pList
->end()) {
1561 if((*it
)->GetType() == NERROR
){
1564 it
= pList
->erase(it
);
1569 SmNode
* retval
= Expression();
1574 SmNode
* SmNodeListParser::Expression(){
1575 SmNodeArray NodeArray
;
1576 //Accept as many relations as there is
1578 NodeArray
.push_back(Relation());
1580 //Create SmExpressionNode, I hope SmToken() will do :)
1581 SmStructureNode
* pExpr
= new SmExpressionNode(SmToken());
1582 pExpr
->SetSubNodes(NodeArray
);
1586 SmNode
* SmNodeListParser::Relation(){
1588 SmNode
* pLeft
= Sum();
1589 //While we have tokens and the next is a relation
1590 while(Terminal() && IsRelationOperator(Terminal()->GetToken())){
1592 SmNode
* pOper
= Take();
1593 //Find the right side of the relation
1594 SmNode
* pRight
= Sum();
1595 //Create new SmBinHorNode
1596 SmStructureNode
* pNewNode
= new SmBinHorNode(SmToken());
1597 pNewNode
->SetSubNodes(pLeft
, pOper
, pRight
);
1603 SmNode
* SmNodeListParser::Sum(){
1605 SmNode
* pLeft
= Product();
1606 //While we have tokens and the next is a sum
1607 while(Terminal() && IsSumOperator(Terminal()->GetToken())){
1609 SmNode
* pOper
= Take();
1610 //Find the right side of the sum
1611 SmNode
* pRight
= Product();
1612 //Create new SmBinHorNode
1613 SmStructureNode
* pNewNode
= new SmBinHorNode(SmToken());
1614 pNewNode
->SetSubNodes(pLeft
, pOper
, pRight
);
1620 SmNode
* SmNodeListParser::Product(){
1622 SmNode
* pLeft
= Factor();
1623 //While we have tokens and the next is a product
1624 while(Terminal() && IsProductOperator(Terminal()->GetToken())){
1626 SmNode
* pOper
= Take();
1627 //Find the right side of the operation
1628 SmNode
* pRight
= Factor();
1629 //Create new SmBinHorNode
1630 SmStructureNode
* pNewNode
= new SmBinHorNode(SmToken());
1631 pNewNode
->SetSubNodes(pLeft
, pOper
, pRight
);
1637 SmNode
* SmNodeListParser::Factor(){
1638 //Read unary operations
1641 //Take care of unary operators
1642 else if(IsUnaryOperator(Terminal()->GetToken()))
1644 SmStructureNode
*pUnary
= new SmUnHorNode(SmToken());
1645 SmNode
*pOper
= Terminal(),
1653 pUnary
->SetSubNodes(pOper
, pArg
);
1659 SmNode
* SmNodeListParser::Postfix(){
1662 SmNode
*pArg
= NULL
;
1663 if(IsPostfixOperator(Terminal()->GetToken()))
1665 else if(IsOperator(Terminal()->GetToken()))
1669 while(Terminal() && IsPostfixOperator(Terminal()->GetToken())) {
1670 SmStructureNode
*pUnary
= new SmUnHorNode(SmToken());
1671 SmNode
*pOper
= Take();
1672 pUnary
->SetSubNodes(pArg
, pOper
);
1678 SmNode
* SmNodeListParser::Error(){
1679 return new SmErrorNode(PE_UNEXPECTED_TOKEN
, SmToken());
1682 bool SmNodeListParser::IsOperator(const SmToken
&token
) {
1683 return IsRelationOperator(token
) ||
1684 IsSumOperator(token
) ||
1685 IsProductOperator(token
) ||
1686 IsUnaryOperator(token
) ||
1687 IsPostfixOperator(token
);
1690 bool SmNodeListParser::IsRelationOperator(const SmToken
&token
) {
1691 return token
.nGroup
& TGRELATION
;
1694 bool SmNodeListParser::IsSumOperator(const SmToken
&token
) {
1695 return token
.nGroup
& TGSUM
;
1698 bool SmNodeListParser::IsProductOperator(const SmToken
&token
) {
1699 return token
.nGroup
& TGPRODUCT
&&
1700 token
.eType
!= TWIDESLASH
&&
1701 token
.eType
!= TWIDEBACKSLASH
&&
1702 token
.eType
!= TUNDERBRACE
&&
1703 token
.eType
!= TOVERBRACE
&&
1704 token
.eType
!= TOVER
;
1707 bool SmNodeListParser::IsUnaryOperator(const SmToken
&token
) {
1708 return token
.nGroup
& TGUNOPER
&&
1709 (token
.eType
== TPLUS
||
1710 token
.eType
== TMINUS
||
1711 token
.eType
== TPLUSMINUS
||
1712 token
.eType
== TMINUSPLUS
||
1713 token
.eType
== TNEG
||
1714 token
.eType
== TUOPER
);
1717 bool SmNodeListParser::IsPostfixOperator(const SmToken
&token
) {
1718 return token
.eType
== TFACT
;
1721 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */