fdo#74697 Add Bluez 5 support for impress remote.
[LibreOffice.git] / starmath / source / cursor.cxx
blobcb63ef4d89ec2ddcdd219ce4de0676df738e4faa
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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/.
8 */
9 #include "cursor.hxx"
10 #include "parse.hxx"
11 #include "visitors.hxx"
12 #include "document.hxx"
13 #include "view.hxx"
14 #include "accessibility.hxx"
15 #include <comphelper/string.hxx>
17 void SmCursor::Move(OutputDevice* pDev, SmMovementDirection direction, bool bMoveAnchor){
18 SmCaretPosGraphEntry* NewPos = NULL;
19 switch(direction){
20 case MoveLeft:
22 NewPos = position->Left;
23 OSL_ENSURE(NewPos, "NewPos shouldn't be NULL here!");
24 }break;
25 case MoveRight:
27 NewPos = position->Right;
28 OSL_ENSURE(NewPos, "NewPos shouldn't be NULL here!");
29 }break;
30 case MoveUp:
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.
33 case MoveDown:
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();
40 while(it.Next()){
41 //Reject it if it's the current position
42 if(it->CaretPos == position->CaretPos) continue;
43 //Compute caret line
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
51 if(NewPos){
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);
65 }break;
66 default:
67 OSL_FAIL("Movement direction not supported!");
69 if(NewPos){
70 position = NewPos;
71 if(bMoveAnchor)
72 anchor = NewPos;
73 RequestRepaint();
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();
84 while(it.Next()){
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
89 if(NewPos){
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);
101 if(NewPos){
102 position = NewPos;
103 if(bMoveAnchor)
104 anchor = NewPos;
105 RequestRepaint();
109 void SmCursor::BuildGraph(){
110 //Save the current anchor and position
111 SmCaretPos _anchor, _position;
112 //Release pGraph if allocated
113 if(pGraph){
114 if(anchor)
115 _anchor = anchor->CaretPos;
116 if(position)
117 _position = position->CaretPos;
118 delete pGraph;
119 //Reset anchor and position as they point into an old graph
120 anchor = NULL;
121 position = NULL;
123 pGraph = NULL;
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();
131 while(it.Next()){
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();
140 if(!position)
141 position = it.Next();
142 if(!anchor)
143 anchor = position;
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();
151 while(it.Next()){
152 if(it->CaretPos == pos){
153 position = it.Current();
154 if(moveAnchor)
155 anchor = it.Current();
156 return true;
159 return false;
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
173 if(HasSelection()){
174 Delete();
175 return;
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
185 BeginEdit();
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();
195 --patchPoint;
196 //Convert second line to list
197 NodeToList(pLine, pLineList);
198 //Patch the line list
199 ++patchPoint;
200 PosAfterDelete = PatchLineList(pLineList, patchPoint);
201 //Parse the line
202 pLine = SmNodeListParser().Parse(pLineList);
204 delete 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++){
209 if(i < nLineOffset)
210 lines[i] = pLineParent->GetSubNode(i);
211 else if(i > nLineOffset)
212 lines[i-1] = pLineParent->GetSubNode(i);
214 pLineParent->SetSubNodes(lines);
215 //Rebuild graph
216 anchor = NULL;
217 position = NULL;
218 BuildGraph();
219 AnnotateSelection();
220 //Set caret position
221 if(!SetCaretPosition(PosAfterDelete, true))
222 SetCaretPosition(SmCaretPos(pLine, 0), true);
223 //Finish editing
224 EndEdit();
226 //TODO: If we're in an empty (sub/super/*) script
227 /*}else if(pLineParent->GetType() == NSUBSUP &&
228 nLineOffset != 0 &&
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
237 }else{
238 this->Move(pDev, MoveLeft, false);
239 if(!this->HasComplexSelection())
240 Delete();
244 void SmCursor::Delete(){
245 //Return if we don't have a selection to delete
246 if(!HasSelection())
247 return;
249 //Enter edit setion
250 BeginEdit();
252 //Set selected on nodes
253 AnnotateSelection();
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);
280 //Finish editing
281 FinishEdit(pLineList, pLineParent, nLineOffset, PosAfterDelete);
284 void SmCursor::InsertNodes(SmNodeList* pNewNodes){
285 if(pNewNodes->empty()){
286 delete pNewNodes;
287 return;
290 //Begin edit section
291 BeginEdit();
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)
316 insIt;
317 for(newIt = pNewNodes->begin(); newIt != pNewNodes->end(); ++newIt){
318 insIt = pLineList->insert(it, *newIt);
319 if(newIt == pNewNodes->begin())
320 patchIt = insIt;
321 if((*newIt)->GetType() == NTEXT)
322 PosAfterInsert = SmCaretPos(*newIt, ((SmTextNode*)*newIt)->GetText().getLength());
323 else
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
330 delete pNewNodes;
331 pNewNodes = NULL;
333 //Finish editing
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);
349 ++it;
350 //Insert str2 as new text node
351 if(!str2.isEmpty()){
352 SmTextNode* pNewText = new SmTextNode(pText->GetToken(), pText->GetFontDesc());
353 pNewText->ChangeText(str2);
354 it = pLineList->insert(it, pNewText);
357 }else
358 ++it;
359 //it now pointer to the node following pos, so pLineList->insert(it, ...) will insert correctly
360 return it;
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
370 SmNode *prev = NULL,
371 *next = NULL;
372 if(aIter != pLineList->end())
373 next = *aIter;
374 if(aIter != pLineList->begin()) {
375 --aIter;
376 prev = *aIter;
377 ++aIter;
380 //Check if there's textnodes to merge
381 if( prev &&
382 next &&
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());
390 OUString newText;
391 newText += pText->GetText();
392 newText += pOldN->GetText();
393 pText->ChangeText(newText);
394 delete pOldN;
395 pLineList->erase(aIter);
396 return retval;
399 //Check if there's a SmPlaceNode to remove:
400 if(prev && next && prev->GetType() == NPLACE && !SmNodeListParser::IsOperator(next->GetToken())){
401 --aIter;
402 aIter = pLineList->erase(aIter);
403 delete prev;
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())
408 return SmCaretPos();
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);
415 delete next;
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
423 return SmCaretPos();
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()){
435 //Split text nodes
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(),
442 len1 = start2 - 0,
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
448 if(len1 > 0) {
449 int start1 = 0;
450 OUString str = aText.copy(start1, len1);
451 pText->ChangeText(str);
452 ++it;
453 } else {//Remove it if not needed
454 it = pLineList->erase(it);
455 delete pText;
457 //Set retval to be right after the selection
458 retval = it;
459 //if we need make segment 3
460 if(len3 > 0) {
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
474 SmNode* pNode = *it;
475 retval = it = pLineList->erase(it);
476 if(pSelectedNodes)
477 pSelectedNodes->push_back(pNode);
478 else
479 delete pNode;
481 } else
482 ++it;
484 return retval;
487 void SmCursor::InsertSubSup(SmSubSup eSubSup) {
488 AnnotateSelection();
490 //Find line
491 SmNode *pLine;
492 if(HasSelection()) {
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);
496 } else
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
508 BeginEdit();
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;
516 if(HasSelection())
517 it = TakeSelectedNodesFromList(pLineList, pSelectedNodesList);
518 else
519 it = FindPositionInLineList(pLineList, position->CaretPos);
521 //Find node that this should be applied to
522 SmNode* pSubject;
523 bool bPatchLine = pSelectedNodesList->size() > 0; //If the line should be patched later
524 if(it != pLineList->begin()) {
525 --it;
526 pSubject = *it;
527 ++it;
528 } else {
529 //Create a new place node
530 pSubject = new SmPlaceNode();
531 pSubject->Prepare(pDocShell->GetFormat(), *pDocShell);
532 it = pLineList->insert(it, pSubject);
533 ++it;
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){
540 SmToken token;
541 token.nGroup = TGPOWER;
542 pSubSup = new SmSubSupNode(token);
543 pSubSup->SetBody(pSubject);
544 *(--it) = pSubSup;
545 ++it;
546 }else
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.
550 pSubject = NULL;
552 //Patch the line if we noted that was needed previously
553 if(bPatchLine)
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);
586 //Finish editing
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;
596 else {
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
604 if(!pSubject)
605 return false;
607 BeginEdit();
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
615 SmToken token;
616 token.nGroup = TGLIMIT;
617 pSubSup = new SmSubSupNode(token);
618 //Set it's body
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);
638 delete pLineList;
639 pSubSup->SetSubSup(eSubSup, pLine);
642 //Rebuild graph of caret positions
643 BuildGraph();
644 AnnotateSelection();
646 //Set caret position
647 if(bMoveCaret)
648 if(!SetCaretPosition(PosAfterLimit, true))
649 SetCaretPosition(SmCaretPos(pLine, 0), true);
651 EndEdit();
653 return true;
656 void SmCursor::InsertBrackets(SmBracketType eBracketType) {
657 BeginEdit();
659 AnnotateSelection();
661 //Find line
662 SmNode *pLine;
663 if(HasSelection()) {
664 SmNode *pSNode = FindSelectedNode(pTree);
665 OSL_ENSURE(pSNode != NULL, "There must be a selected node if HasSelection()");
666 pLine = FindTopMostNodeInLine(pSNode, sal_True);
667 } else
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;
681 if(HasSelection())
682 it = TakeSelectedNodesFromList(pLineList, pSelectedNodesList);
683 else
684 it = FindPositionInLineList(pLineList, position->CaretPos);
686 //If there's no selected nodes, create a place node
687 SmNode *pBodyNode;
688 SmCaretPos PosAfterInsert;
689 if(pSelectedNodesList->empty()) {
690 pBodyNode = new SmPlaceNode();
691 PosAfterInsert = SmCaretPos(pBodyNode, 1);
692 } else
693 pBodyNode = SmNodeListParser().Parse(pSelectedNodesList);
695 delete pSelectedNodesList;
697 //Create SmBraceNode
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);
708 //Insert into line
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;
715 //Finish editing
716 FinishEdit(pLineList, pLineParent, nParentIndex, PosAfterInsert);
719 SmNode *SmCursor::CreateBracket(SmBracketType eBracketType, bool bIsLeft) {
720 SmToken aTok;
721 if(bIsLeft){
722 switch(eBracketType){
723 case NoneBrackets:
724 aTok = SmToken(TNONE, '\0', "none", TGLBRACES | TGRBRACES, 0);
725 break;
726 case RoundBrackets:
727 aTok = SmToken(TLPARENT, MS_LPARENT, "(", TGLBRACES, 5);
728 break;
729 case SquareBrackets:
730 aTok = SmToken(TLBRACKET, MS_LBRACKET, "[", TGLBRACES, 5);
731 break;
732 case DoubleSquareBrackets:
733 aTok = SmToken(TLDBRACKET, MS_LDBRACKET, "ldbracket", TGLBRACES, 5);
734 break;
735 case LineBrackets:
736 aTok = SmToken(TLLINE, MS_LINE, "lline", TGLBRACES, 5);
737 break;
738 case DoubleLineBrackets:
739 aTok = SmToken(TLDLINE, MS_DLINE, "ldline", TGLBRACES, 5);
740 break;
741 case CurlyBrackets:
742 aTok = SmToken(TLBRACE, MS_LBRACE, "lbrace", TGLBRACES, 5);
743 break;
744 case AngleBrackets:
745 aTok = SmToken(TLANGLE, MS_LANGLE, "langle", TGLBRACES, 5);
746 break;
747 case CeilBrackets:
748 aTok = SmToken(TLCEIL, MS_LCEIL, "lceil", TGLBRACES, 5);
749 break;
750 case FloorBrackets:
751 aTok = SmToken(TLFLOOR, MS_LFLOOR, "lfloor", TGLBRACES, 5);
752 break;
754 } else {
755 switch(eBracketType) {
756 case NoneBrackets:
757 aTok = SmToken(TNONE, '\0', "none", TGLBRACES | TGRBRACES, 0);
758 break;
759 case RoundBrackets:
760 aTok = SmToken(TRPARENT, MS_RPARENT, ")", TGRBRACES, 5);
761 break;
762 case SquareBrackets:
763 aTok = SmToken(TRBRACKET, MS_RBRACKET, "]", TGRBRACES, 5);
764 break;
765 case DoubleSquareBrackets:
766 aTok = SmToken(TRDBRACKET, MS_RDBRACKET, "rdbracket", TGRBRACES, 5);
767 break;
768 case LineBrackets:
769 aTok = SmToken(TRLINE, MS_LINE, "rline", TGRBRACES, 5);
770 break;
771 case DoubleLineBrackets:
772 aTok = SmToken(TRDLINE, MS_DLINE, "rdline", TGRBRACES, 5);
773 break;
774 case CurlyBrackets:
775 aTok = SmToken(TRBRACE, MS_RBRACE, "rbrace", TGRBRACES, 5);
776 break;
777 case AngleBrackets:
778 aTok = SmToken(TRANGLE, MS_RANGLE, "rangle", TGRBRACES, 5);
779 break;
780 case CeilBrackets:
781 aTok = SmToken(TRCEIL, MS_RCEIL, "rceil", TGRBRACES, 5);
782 break;
783 case FloorBrackets:
784 aTok = SmToken(TRFLOOR, MS_RFLOOR, "rfloor", TGRBRACES, 5);
785 break;
788 SmNode* pRetVal = new SmMathSymbolNode(aTok);
789 pRetVal->SetScaleMode(SCALE_HEIGHT);
790 return pRetVal;
793 bool SmCursor::InsertRow() {
794 AnnotateSelection();
796 //Find line
797 SmNode *pLine;
798 if(HasSelection()) {
799 SmNode *pSNode = FindSelectedNode(pTree);
800 OSL_ENSURE(pSNode != NULL, "There must be a selected node if HasSelection()");
801 pLine = FindTopMostNodeInLine(pSNode, sal_True);
802 } else
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)
830 return false;
832 //Now we start editing
833 BeginEdit();
835 //Convert line to list
836 SmNodeList *pLineList = NodeToList(pLine);
838 //Find position in line
839 SmNodeList::iterator it;
840 if(HasSelection()) {
841 //Take the selected nodes and delete them...
842 it = TakeSelectedNodesFromList(pLineList);
843 } else
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
850 if(pTable) {
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())
857 --it;
858 if(pNewLineList->empty())
859 pNewLineList->push_front(new SmPlaceNode());
860 //Parse new line
861 SmNode *pNewLine = SmNodeListParser().Parse(pNewLineList);
862 delete 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;
869 //Get position
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));
875 //Insert new line
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();
881 tok.eType = TSTACK;
882 pTable->SetToken(tok);
885 //If we're in the context of a matrix
886 else if(pMatrix) {
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);
902 } else
903 OSL_FAIL("We must be either the context of a table or matrix!");
905 //Finish editing
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.
910 return true;
913 void SmCursor::InsertFraction() {
914 AnnotateSelection();
916 //Find line
917 SmNode *pLine;
918 if(HasSelection()) {
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);
922 } else
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
931 BeginEdit();
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;
939 if(HasSelection())
940 it = TakeSelectedNodesFromList(pLineList, pSelectedNodesList);
941 else
942 it = FindPositionInLineList(pLineList, position->CaretPos);
944 //Create pNum, and pDenom
945 bool bEmptyFraction = pSelectedNodesList->empty();
946 SmNode *pNum = bEmptyFraction
947 ? new SmPlaceNode()
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);
963 //Finish editing
964 SmNode *pSelectedNode = bEmptyFraction ? pNum : pDenom;
965 FinishEdit(pLineList, pLineParent, nParentIndex, SmCaretPos(pSelectedNode, 1));
968 void SmCursor::InsertText(OUString aString)
970 BeginEdit();
972 Delete();
974 SmToken token;
975 token.eType = TIDENT;
976 token.cMathChar = '\0';
977 token.nGroup = 0;
978 token.nLevel = 5;
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);
989 InsertNodes(pList);
991 EndEdit();
994 void SmCursor::InsertElement(SmFormulaElement element){
995 BeginEdit();
997 Delete();
999 //Create new node
1000 SmNode* pNewNode = NULL;
1001 switch(element){
1002 case BlankElement:
1004 SmToken token;
1005 token.nGroup = TGBLANK;
1006 token.aText = "~";
1007 pNewNode = new SmBlankNode(token);
1008 }break;
1009 case FactorialElement:
1011 SmToken token(TFACT, MS_FACT, "fact", TGUNOPER, 5);
1012 pNewNode = new SmMathSymbolNode(token);
1013 }break;
1014 case PlusElement:
1016 SmToken token;
1017 token.eType = TPLUS;
1018 token.cMathChar = MS_PLUS;
1019 token.nGroup = TGUNOPER | TGSUM;
1020 token.nLevel = 5;
1021 token.aText = "+";
1022 pNewNode = new SmMathSymbolNode(token);
1023 }break;
1024 case MinusElement:
1026 SmToken token;
1027 token.eType = TMINUS;
1028 token.cMathChar = MS_MINUS;
1029 token.nGroup = TGUNOPER | TGSUM;
1030 token.nLevel = 5;
1031 token.aText = "-";
1032 pNewNode = new SmMathSymbolNode(token);
1033 }break;
1034 case CDotElement:
1036 SmToken token;
1037 token.eType = TCDOT;
1038 token.cMathChar = MS_CDOT;
1039 token.nGroup = TGPRODUCT;
1040 token.aText = "cdot";
1041 pNewNode = new SmMathSymbolNode(token);
1042 }break;
1043 case EqualElement:
1045 SmToken token;
1046 token.eType = TASSIGN;
1047 token.cMathChar = MS_ASSIGN;
1048 token.nGroup = TGRELATION;
1049 token.aText = "=";
1050 pNewNode = new SmMathSymbolNode(token);
1051 }break;
1052 case LessThanElement:
1054 SmToken token;
1055 token.eType = TLT;
1056 token.cMathChar = MS_LT;
1057 token.nGroup = TGRELATION;
1058 token.aText = "<";
1059 pNewNode = new SmMathSymbolNode(token);
1060 }break;
1061 case GreaterThanElement:
1063 SmToken token;
1064 token.eType = TGT;
1065 token.cMathChar = MS_GT;
1066 token.nGroup = TGRELATION;
1067 token.aText = ">";
1068 pNewNode = new SmMathSymbolNode(token);
1069 }break;
1070 case PercentElement:
1072 SmToken token;
1073 token.eType = TTEXT;
1074 token.cMathChar = MS_PERCENT;
1075 token.nGroup = 0;
1076 token.aText = "\"%\"";
1077 pNewNode = new SmMathSymbolNode(token);
1078 }break;
1079 default:
1080 OSL_FAIL("Element unknown!");
1082 OSL_ENSURE(pNewNode != NULL, "No new node was created!");
1083 if(!pNewNode)
1084 return;
1086 //Prepare the new node
1087 pNewNode->Prepare(pDocShell->GetFormat(), *pDocShell);
1089 //Insert new node
1090 SmNodeList* pList = new SmNodeList();
1091 pList->push_front(pNewNode);
1092 InsertNodes(pList);
1094 EndEdit();
1097 void SmCursor::InsertSpecial(OUString aString)
1099 BeginEdit();
1100 Delete();
1102 aString = comphelper::string::strip(aString, ' ');
1104 //Create instance of special node
1105 SmToken token;
1106 token.eType = TSPECIAL;
1107 token.cMathChar = '\0';
1108 token.nGroup = 0;
1109 token.nLevel = 5;
1110 token.aText = aString;
1111 SmSpecialNode* pSpecial = new SmSpecialNode(token);
1113 //Prepare the special node
1114 pSpecial->Prepare(pDocShell->GetFormat(), *pDocShell);
1116 //Insert the node
1117 SmNodeList* pList = new SmNodeList();
1118 pList->push_front(pSpecial);
1119 InsertNodes(pList);
1121 EndEdit();
1124 void SmCursor::InsertCommand(sal_uInt16 nCommand) {
1125 switch(nCommand){
1126 case RID_NEWLINE:
1127 InsertRow();
1128 break;
1129 case RID_FROMX:
1130 InsertLimit(CSUB, true);
1131 break;
1132 case RID_TOX:
1133 InsertLimit(CSUP, true);
1134 break;
1135 case RID_FROMXTOY:
1136 if(InsertLimit(CSUB, true))
1137 InsertLimit(CSUP, true);
1138 break;
1139 default:
1140 InsertCommandText(SM_RESSTR(nCommand));
1141 break;
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);
1155 BeginEdit();
1157 //Delete any selection
1158 Delete();
1160 //Insert it
1161 InsertNodes(pLineList);
1163 EndEdit();
1166 void SmCursor::Copy(){
1167 if(!HasSelection())
1168 return;
1170 //Find selected node
1171 SmNode* pSNode = FindSelectedNode(pTree);
1172 //Find visual line
1173 SmNode* pLine = FindTopMostNodeInLine(pSNode, true);
1175 //Clone selected nodes
1176 SmNodeList* pList;
1177 if(IsLineCompositionNode(pLine))
1178 pList = CloneLineToList((SmStructureNode*)pLine, true);
1179 else{
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);
1190 } else {
1191 SmCloningVisitor aCloneFactory;
1192 pList->push_front(aCloneFactory.Clone(pLine));
1196 //Set clipboard
1197 if(pList->size() > 0)
1198 SetClipboard(pList);
1201 void SmCursor::Paste() {
1202 BeginEdit();
1203 Delete();
1205 if(pClipboard && pClipboard->size() > 0)
1206 InsertNodes(CloneList(pClipboard));
1208 EndEdit();
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);
1221 return pClones;
1224 void SmCursor::SetClipboard(SmNodeList* pList){
1225 if(pClipboard){
1226 //Delete all nodes on the clipboard
1227 SmNodeList::iterator it;
1228 for(it = pClipboard->begin(); it != pClipboard->end(); ++it)
1229 delete (*it);
1230 delete pClipboard;
1232 pClipboard = pList;
1235 SmNode* SmCursor::FindTopMostNodeInLine(SmNode* pSNode, bool MoveUpIfSelected){
1236 //If we haven't got a subnode
1237 if(!pSNode)
1238 return NULL;
1240 //Move up parent until we find a node who's
1241 //parent is NULL or isn't selected and not a type of:
1242 // SmExpressionNode
1243 // SmLineNode
1244 // SmBinHorNode
1245 // SmUnHorNode
1246 // SmAlignNode
1247 // SmFontNode
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
1254 return pSNode;
1257 SmNode* SmCursor::FindSelectedNode(SmNode* pNode){
1258 SmNodeIterator it(pNode);
1259 while(it.Next()){
1260 if(it->IsSelected())
1261 return it.Current();
1262 SmNode* pRetVal = FindSelectedNode(it.Current());
1263 if(pRetVal)
1264 return pRetVal;
1266 return NULL;
1269 SmNodeList* SmCursor::LineToList(SmStructureNode* pLine, SmNodeList* list){
1270 SmNodeIterator it(pLine);
1271 while(it.Next()){
1272 switch(it->GetType()){
1273 case NLINE:
1274 case NUNHOR:
1275 case NEXPRESSION:
1276 case NBINHOR:
1277 case NALIGN:
1278 case NFONT:
1279 LineToList((SmStructureNode*)it.Current(), list);
1280 break;
1281 case NERROR:
1282 delete it.Current();
1283 break;
1284 default:
1285 list->push_back(it.Current());
1288 SmNodeArray emptyArray(0);
1289 pLine->SetSubNodes(emptyArray);
1290 delete pLine;
1291 return list;
1294 SmNodeList* SmCursor::CloneLineToList(SmStructureNode* pLine, bool bOnlyIfSelected, SmNodeList* pList){
1295 SmCloningVisitor aCloneFactory;
1296 SmNodeIterator it(pLine);
1297 while(it.Next()){
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);
1310 } else
1311 pList->push_back(aCloneFactory.Clone(it.Current()));
1314 return pList;
1317 bool SmCursor::IsLineCompositionNode(SmNode* pNode){
1318 switch(pNode->GetType()){
1319 case NLINE:
1320 case NUNHOR:
1321 case NEXPRESSION:
1322 case NBINHOR:
1323 case NALIGN:
1324 case NFONT:
1325 return true;
1326 default:
1327 return false;
1331 int SmCursor::CountSelectedNodes(SmNode* pNode){
1332 int nCount = 0;
1333 SmNodeIterator it(pNode);
1334 while(it.Next()){
1335 if(it->IsSelected() && !IsLineCompositionNode(it.Current()))
1336 nCount++;
1337 nCount += CountSelectedNodes(it.Current());
1339 return nCount;
1342 bool SmCursor::HasComplexSelection(){
1343 if(!HasSelection())
1344 return false;
1345 AnnotateSelection();
1347 return CountSelectedNodes(pTree) > 1;
1350 void SmCursor::FinishEdit(SmNodeList* pLineList,
1351 SmStructureNode* pParent,
1352 int nParentIndex,
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);
1361 delete pLineList;
1363 //Check if we're making the body of a subsup node bigger than one
1364 if(pParent->GetType() == NSUBSUP &&
1365 nParentIndex == 0 &&
1366 entries > 1) {
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);
1377 pLine = pBrace;
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
1393 if(!pStartLine)
1394 pStartLine = pLine;
1396 //Insert it back into the parent
1397 pParent->SetSubNode(nParentIndex, pLine);
1399 //Rebuild graph of caret position
1400 anchor = NULL;
1401 position = NULL;
1402 BuildGraph();
1403 AnnotateSelection(); //Update selection annotation!
1405 //Set caret position
1406 if(!SetCaretPosition(PosAfterEdit, true))
1407 SetCaretPosition(SmCaretPos(pStartLine, 0), true);
1409 //End edit section
1410 EndEdit();
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...
1443 RequestRepaint();
1445 //Update the edit engine and text of the document
1446 OUString formula;
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();
1456 if( pViewSh ) {
1457 if ( SFX_CREATE_MODE_EMBEDDED == pDocShell->GetCreateMode() )
1458 pDocShell->Repaint();
1459 else
1460 pViewSh->GetGraphicWindow().Invalidate();
1464 bool SmCursor::IsAtTailOfBracket(SmBracketType eBracketType, SmBraceNode** ppBraceNode) const {
1465 const SmCaretPos pos = GetPosition();
1466 if (!pos.IsValid()) {
1467 return false;
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.
1476 return false;
1478 } else {
1479 if (pos.Index < 1) {
1480 return false;
1484 while (true) {
1485 SmStructureNode* pParentNode = pNode->GetParent();
1486 if (!pParentNode) {
1487 // There's no brace body node in the ancestors.
1488 return false;
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.
1494 return false;
1497 pNode = pParentNode;
1498 if (pNode->GetType() == NBRACEBODY) {
1499 // Found the brace body node.
1500 break;
1504 SmStructureNode* pBraceNodeTmp = pNode->GetParent();
1505 if (!pBraceNodeTmp || pBraceNodeTmp->GetType() != NBRACE) {
1506 // Brace node is invalid.
1507 return false;
1510 SmBraceNode* pBraceNode = static_cast<SmBraceNode*>(pBraceNodeTmp);
1511 SmMathSymbolNode* pClosingNode = pBraceNode->ClosingBrace();
1512 if (!pClosingNode) {
1513 // Couldn't get closing symbol node.
1514 return false;
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;
1530 default:
1531 return false;
1534 if (ppBraceNode) {
1535 *ppBraceNode = static_cast<SmBraceNode*>(pBraceNode);
1538 return true;
1541 void SmCursor::MoveAfterBracket(SmBraceNode* pBraceNode, bool bMoveAnchor)
1543 position->CaretPos.pSelectedNode = pBraceNode;
1544 position->CaretPos.Index = 1;
1545 if (bMoveAnchor) {
1546 anchor->CaretPos.pSelectedNode = pBraceNode;
1547 anchor->CaretPos.Index = 1;
1549 RequestRepaint();
1553 /////////////////////////////////////// SmNodeListParser ///////////////////////////////////////
1555 SmNode* SmNodeListParser::Parse(SmNodeList* list, bool bDeleteErrorNodes){
1556 pList = list;
1557 if(bDeleteErrorNodes){
1558 //Delete error nodes
1559 SmNodeList::iterator it = pList->begin();
1560 while(it != pList->end()) {
1561 if((*it)->GetType() == NERROR){
1562 //Delete and erase
1563 delete *it;
1564 it = pList->erase(it);
1565 }else
1566 ++it;
1569 SmNode* retval = Expression();
1570 pList = NULL;
1571 return retval;
1574 SmNode* SmNodeListParser::Expression(){
1575 SmNodeArray NodeArray;
1576 //Accept as many relations as there is
1577 while(Terminal())
1578 NodeArray.push_back(Relation());
1580 //Create SmExpressionNode, I hope SmToken() will do :)
1581 SmStructureNode* pExpr = new SmExpressionNode(SmToken());
1582 pExpr->SetSubNodes(NodeArray);
1583 return pExpr;
1586 SmNode* SmNodeListParser::Relation(){
1587 //Read a sum
1588 SmNode* pLeft = Sum();
1589 //While we have tokens and the next is a relation
1590 while(Terminal() && IsRelationOperator(Terminal()->GetToken())){
1591 //Take the operator
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);
1598 pLeft = pNewNode;
1600 return pLeft;
1603 SmNode* SmNodeListParser::Sum(){
1604 //Read a product
1605 SmNode* pLeft = Product();
1606 //While we have tokens and the next is a sum
1607 while(Terminal() && IsSumOperator(Terminal()->GetToken())){
1608 //Take the operator
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);
1615 pLeft = pNewNode;
1617 return pLeft;
1620 SmNode* SmNodeListParser::Product(){
1621 //Read a Factor
1622 SmNode* pLeft = Factor();
1623 //While we have tokens and the next is a product
1624 while(Terminal() && IsProductOperator(Terminal()->GetToken())){
1625 //Take the operator
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);
1632 pLeft = pNewNode;
1634 return pLeft;
1637 SmNode* SmNodeListParser::Factor(){
1638 //Read unary operations
1639 if(!Terminal())
1640 return Error();
1641 //Take care of unary operators
1642 else if(IsUnaryOperator(Terminal()->GetToken()))
1644 SmStructureNode *pUnary = new SmUnHorNode(SmToken());
1645 SmNode *pOper = Terminal(),
1646 *pArg;
1648 if(Next())
1649 pArg = Factor();
1650 else
1651 pArg = Error();
1653 pUnary->SetSubNodes(pOper, pArg);
1654 return pUnary;
1656 return Postfix();
1659 SmNode* SmNodeListParser::Postfix(){
1660 if(!Terminal())
1661 return Error();
1662 SmNode *pArg = NULL;
1663 if(IsPostfixOperator(Terminal()->GetToken()))
1664 pArg = Error();
1665 else if(IsOperator(Terminal()->GetToken()))
1666 return Error();
1667 else
1668 pArg = Take();
1669 while(Terminal() && IsPostfixOperator(Terminal()->GetToken())) {
1670 SmStructureNode *pUnary = new SmUnHorNode(SmToken());
1671 SmNode *pOper = Take();
1672 pUnary->SetSubNodes(pArg, pOper);
1673 pArg = pUnary;
1675 return pArg;
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: */