LCOV - code coverage report
Current view: top level - starmath/source - cursor.cxx (source / functions) Hit Total Coverage
Test: commit 0e63ca4fde4e446f346e35849c756a30ca294aab Lines: 417 1001 41.7 %
Date: 2014-04-11 Functions: 38 56 67.9 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       2             : /*
       3             :  * This file is part of the LibreOffice project.
       4             :  *
       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>
      16             : 
      17          30 : void SmCursor::Move(OutputDevice* pDev, SmMovementDirection direction, bool bMoveAnchor){
      18          30 :     SmCaretPosGraphEntry* NewPos = NULL;
      19          30 :     switch(direction){
      20             :         case MoveLeft:
      21             :         {
      22           5 :             NewPos = position->Left;
      23             :             OSL_ENSURE(NewPos, "NewPos shouldn't be NULL here!");
      24           5 :         }break;
      25             :         case MoveRight:
      26             :         {
      27          23 :             NewPos = position->Right;
      28             :             OSL_ENSURE(NewPos, "NewPos shouldn't be NULL here!");
      29          23 :         }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:
      34             :         {
      35           2 :             SmCaretLine from_line = SmCaretPos2LineVisitor(pDev, position->CaretPos).GetResult(),
      36           2 :                         best_line,  //Best approximated line found so far
      37           2 :                         curr_line;  //Current line
      38           2 :             long dbp_sq = 0;        //Distance squared to best line
      39           2 :             SmCaretPosGraphIterator it = pGraph->GetIterator();
      40          18 :             while(it.Next()){
      41             :                 //Reject it if it's the current position
      42          14 :                 if(it->CaretPos == position->CaretPos) continue;
      43             :                 //Compute caret line
      44          12 :                 curr_line = SmCaretPos2LineVisitor(pDev, it->CaretPos).GetResult();
      45             :                 //Reject anything above if we're moving down
      46          12 :                 if(curr_line.GetTop() <= from_line.GetTop() && direction == MoveDown) continue;
      47             :                 //Reject anything below if we're moving up
      48           0 :                 if(curr_line.GetTop() + curr_line.GetHeight() >= from_line.GetTop() + from_line.GetHeight()
      49           0 :                         && direction == MoveUp) continue;
      50             :                 //Compare if it to what we have, if we have anything yet
      51           0 :                 if(NewPos){
      52             :                     //Compute distance to current line squared, multiplied with a horizontal factor
      53           0 :                     long dp_sq = curr_line.SquaredDistanceX(from_line) * HORIZONTICAL_DISTANCE_FACTOR +
      54           0 :                                  curr_line.SquaredDistanceY(from_line);
      55             :                     //Discard current line if best line is closer
      56           0 :                     if(dbp_sq <= dp_sq) continue;
      57             :                 }
      58             :                 //Take current line as the best
      59           0 :                 best_line = curr_line;
      60           0 :                 NewPos = it.Current();
      61             :                 //Update distance to best line
      62           0 :                 dbp_sq = best_line.SquaredDistanceX(from_line) * HORIZONTICAL_DISTANCE_FACTOR +
      63           0 :                          best_line.SquaredDistanceY(from_line);
      64             :             }
      65           2 :         }break;
      66             :         default:
      67             :             SAL_WARN("starmath", "Movement direction not supported!");
      68             :     }
      69          30 :     if(NewPos){
      70          28 :         position = NewPos;
      71          28 :         if(bMoveAnchor)
      72          25 :             anchor = NewPos;
      73          28 :         RequestRepaint();
      74             :     }
      75          30 : }
      76             : 
      77           0 : void SmCursor::MoveTo(OutputDevice* pDev, Point pos, bool bMoveAnchor){
      78           0 :     SmCaretLine best_line,  //Best line found so far, when iterating
      79           0 :                 curr_line;  //Current line, when iterating
      80           0 :     SmCaretPosGraphEntry* NewPos = NULL;
      81           0 :     long dp_sq = 0,     //Distance to current line squared
      82           0 :          dbp_sq = 1;    //Distance to best line squared
      83           0 :     SmCaretPosGraphIterator it = pGraph->GetIterator();
      84           0 :     while(it.Next()){
      85             :         OSL_ENSURE(it->CaretPos.IsValid(), "The caret position graph may not have invalid positions!");
      86             :         //Compute current line
      87           0 :         curr_line = SmCaretPos2LineVisitor(pDev, it->CaretPos).GetResult();
      88             :         //If we have a position compare to it
      89           0 :         if(NewPos){
      90             :             //Compute squared distance to current line
      91           0 :             dp_sq = curr_line.SquaredDistanceX(pos) + curr_line.SquaredDistanceY(pos);
      92             :             //If best line is closer, reject current line
      93           0 :             if(dbp_sq <= dp_sq) continue;
      94             :         }
      95             :         //Accept current position as the best
      96           0 :         best_line = curr_line;
      97           0 :         NewPos = it.Current();
      98             :         //Update distance to best line
      99           0 :         dbp_sq = best_line.SquaredDistanceX(pos) + best_line.SquaredDistanceY(pos);
     100             :     }
     101           0 :     if(NewPos){
     102           0 :         position = NewPos;
     103           0 :         if(bMoveAnchor)
     104           0 :             anchor = NewPos;
     105           0 :         RequestRepaint();
     106             :     }
     107           0 : }
     108             : 
     109          20 : void SmCursor::BuildGraph(){
     110             :     //Save the current anchor and position
     111          20 :     SmCaretPos _anchor, _position;
     112             :     //Release pGraph if allocated
     113          20 :     if(pGraph){
     114          16 :         if(anchor)
     115           0 :             _anchor = anchor->CaretPos;
     116          16 :         if(position)
     117           0 :             _position = position->CaretPos;
     118          16 :         delete pGraph;
     119             :         //Reset anchor and position as they point into an old graph
     120          16 :         anchor = NULL;
     121          16 :         position = NULL;
     122             :     }
     123             : 
     124             :     //Build the new graph
     125          20 :     pGraph = SmCaretPosGraphBuildingVisitor(pTree).takeGraph();
     126             : 
     127             :     //Restore anchor and position pointers
     128          20 :     if(_anchor.IsValid() || _position.IsValid()){
     129           0 :         SmCaretPosGraphIterator it = pGraph->GetIterator();
     130           0 :         while(it.Next()){
     131           0 :             if(_anchor == it->CaretPos)
     132           0 :                 anchor = it.Current();
     133           0 :             if(_position == it->CaretPos)
     134           0 :                 position = it.Current();
     135             :         }
     136             :     }
     137             :     //Set position and anchor to first caret position
     138          20 :     SmCaretPosGraphIterator it = pGraph->GetIterator();
     139          20 :     if(!position)
     140          20 :         position = it.Next();
     141          20 :     if(!anchor)
     142          20 :         anchor = position;
     143             : 
     144             :     OSL_ENSURE(position->CaretPos.IsValid(), "Position must be valid");
     145             :     OSL_ENSURE(anchor->CaretPos.IsValid(), "Anchor must be valid");
     146          20 : }
     147             : 
     148          17 : bool SmCursor::SetCaretPosition(SmCaretPos pos, bool moveAnchor){
     149          17 :     SmCaretPosGraphIterator it = pGraph->GetIterator();
     150          17 :     while(it.Next()){
     151         120 :         if(it->CaretPos == pos){
     152          16 :             position = it.Current();
     153          16 :             if(moveAnchor)
     154          16 :                 anchor = it.Current();
     155          16 :             return true;
     156             :         }
     157             :     }
     158           1 :     return false;
     159             : }
     160             : 
     161          19 : void SmCursor::AnnotateSelection(){
     162             :     //TODO: Manage a state, reset it upon modification and optimize this call
     163          19 :     SmSetSelectionVisitor(anchor->CaretPos, position->CaretPos, pTree);
     164          19 : }
     165             : 
     166           0 : void SmCursor::Draw(OutputDevice& pDev, Point Offset, bool isCaretVisible){
     167           0 :     SmCaretDrawingVisitor(pDev, GetPosition(), Offset, isCaretVisible);
     168           0 : }
     169             : 
     170           0 : void SmCursor::DeletePrev(OutputDevice* pDev){
     171             :     //Delete only a selection if there's a selection
     172           0 :     if(HasSelection()){
     173           0 :         Delete();
     174           0 :         return;
     175             :     }
     176             : 
     177           0 :     SmNode* pLine = FindTopMostNodeInLine(position->CaretPos.pSelectedNode);
     178           0 :     SmStructureNode* pLineParent = pLine->GetParent();
     179           0 :     int nLineOffset = pLineParent->IndexOfSubNode(pLine);
     180             : 
     181             :     //If we're in front of a node who's parent is a TABLE
     182           0 :     if(pLineParent->GetType() == NTABLE && position->CaretPos.Index == 0 && nLineOffset > 0){
     183             :         //Now we can merge with nLineOffset - 1
     184           0 :         BeginEdit();
     185             :         //Line to merge things into, so we can delete pLine
     186           0 :         SmNode* pMergeLine = pLineParent->GetSubNode(nLineOffset-1);
     187             :         OSL_ENSURE(pMergeLine, "pMergeLine cannot be NULL!");
     188           0 :         SmCaretPos PosAfterDelete;
     189             :         //Convert first line to list
     190           0 :         SmNodeList *pLineList = NodeToList(pMergeLine);
     191           0 :         if(!pLineList->empty()){
     192             :             //Find iterator to patch
     193           0 :             SmNodeList::iterator patchPoint = pLineList->end();
     194           0 :             --patchPoint;
     195             :             //Convert second line to list
     196           0 :             NodeToList(pLine, pLineList);
     197             :             //Patch the line list
     198           0 :             ++patchPoint;
     199           0 :             PosAfterDelete = PatchLineList(pLineList, patchPoint);
     200             :             //Parse the line
     201           0 :             pLine = SmNodeListParser().Parse(pLineList);
     202             :         }
     203           0 :         delete pLineList;
     204           0 :         pLineParent->SetSubNode(nLineOffset-1, pLine);
     205             :         //Delete the removed line slot
     206           0 :         SmNodeArray lines(pLineParent->GetNumSubNodes()-1);
     207           0 :         for(int i = 0; i < pLineParent->GetNumSubNodes(); i++){
     208           0 :             if(i < nLineOffset)
     209           0 :                 lines[i] = pLineParent->GetSubNode(i);
     210           0 :             else if(i > nLineOffset)
     211           0 :                 lines[i-1] = pLineParent->GetSubNode(i);
     212             :         }
     213           0 :         pLineParent->SetSubNodes(lines);
     214             :         //Rebuild graph
     215           0 :         anchor = NULL;
     216           0 :         position = NULL;
     217           0 :         BuildGraph();
     218           0 :         AnnotateSelection();
     219             :         //Set caret position
     220           0 :         if(!SetCaretPosition(PosAfterDelete, true))
     221           0 :             SetCaretPosition(SmCaretPos(pLine, 0), true);
     222             :         //Finish editing
     223           0 :         EndEdit();
     224             : 
     225             :     //TODO: If we're in an empty (sub/super/*) script
     226             :     /*}else if(pLineParent->GetType() == NSUBSUP &&
     227             :              nLineOffset != 0 &&
     228             :              pLine->GetType() == NEXPRESSION &&
     229             :              pLine->GetNumSubNodes() == 0){
     230             :         //There's a (sub/super) script we can delete
     231             :     //Consider selecting the entire script if GetNumSubNodes() != 0 or pLine->GetType() != NEXPRESSION
     232             :     //TODO: Handle case where we delete a limit
     233             :     */
     234             : 
     235             :     //Else move select, and delete if not complex
     236             :     }else{
     237           0 :         this->Move(pDev, MoveLeft, false);
     238           0 :         if(!this->HasComplexSelection())
     239           0 :             Delete();
     240             :     }
     241             : }
     242             : 
     243          13 : void SmCursor::Delete(){
     244             :     //Return if we don't have a selection to delete
     245          13 :     if(!HasSelection())
     246          26 :         return;
     247             : 
     248             :     //Enter edit setion
     249           0 :     BeginEdit();
     250             : 
     251             :     //Set selected on nodes
     252           0 :     AnnotateSelection();
     253             : 
     254             :     //Find an arbitrary selected node
     255           0 :     SmNode* pSNode = FindSelectedNode(pTree);
     256             :     OSL_ENSURE(pSNode != NULL, "There must be a selection when HasSelection is true!");
     257             : 
     258             :     //Find the topmost node of the line that holds the selection
     259           0 :     SmNode* pLine = FindTopMostNodeInLine(pSNode, true);
     260             :     OSL_ENSURE(pLine != pTree, "Shouldn't be able to select the entire tree");
     261             : 
     262             :     //Get the parent of the line
     263           0 :     SmStructureNode* pLineParent = pLine->GetParent();
     264             :     //Find line offset in parent
     265           0 :     int nLineOffset = pLineParent->IndexOfSubNode(pLine);
     266             :     OSL_ENSURE(nLineOffset != -1, "pLine must be a child of its parent!");
     267             : 
     268             :     //Position after delete
     269           0 :     SmCaretPos PosAfterDelete;
     270             : 
     271           0 :     SmNodeList* pLineList = NodeToList(pLine);
     272             : 
     273             :     //Take the selected nodes and delete them...
     274           0 :     SmNodeList::iterator patchIt = TakeSelectedNodesFromList(pLineList);
     275             : 
     276             :     //Get the position to set after delete
     277           0 :     PosAfterDelete = PatchLineList(pLineList, patchIt);
     278             : 
     279             :     //Finish editing
     280           0 :     FinishEdit(pLineList, pLineParent, nLineOffset, PosAfterDelete);
     281             : }
     282             : 
     283          13 : void SmCursor::InsertNodes(SmNodeList* pNewNodes){
     284          13 :     if(pNewNodes->empty()){
     285           0 :         delete pNewNodes;
     286           0 :         return;
     287             :     }
     288             : 
     289             :     //Begin edit section
     290          13 :     BeginEdit();
     291             : 
     292             :     //Position after insert should be after pNewNode
     293          13 :     SmCaretPos PosAfterInsert = SmCaretPos(pNewNodes->back(), 1);
     294             : 
     295             :     //Get the current position
     296          13 :     const SmCaretPos pos = position->CaretPos;
     297             : 
     298             :     //Find top most of line that holds position
     299          13 :     SmNode* pLine = FindTopMostNodeInLine(pos.pSelectedNode, false);
     300             : 
     301             :     //Find line parent and line index in parent
     302          13 :     SmStructureNode* pLineParent = pLine->GetParent();
     303          13 :     int nParentIndex = pLineParent->IndexOfSubNode(pLine);
     304             :     OSL_ENSURE(nParentIndex != -1, "pLine must be a subnode of pLineParent!");
     305          13 :     if (nParentIndex == -1)
     306             :     {
     307           0 :         delete pNewNodes;
     308           0 :         return;
     309             :     }
     310             : 
     311             :     //Convert line to list
     312          13 :     SmNodeList* pLineList = NodeToList(pLine);
     313             : 
     314             :     //Find iterator for place to insert nodes
     315          13 :     SmNodeList::iterator it = FindPositionInLineList(pLineList, pos);
     316             : 
     317             :     //Insert all new nodes
     318          13 :     SmNodeList::iterator newIt,
     319          13 :                          patchIt = it, // (pointless default value, fixes compiler warnings)
     320          13 :                          insIt;
     321          26 :     for(newIt = pNewNodes->begin(); newIt != pNewNodes->end(); ++newIt){
     322          13 :         insIt = pLineList->insert(it, *newIt);
     323          13 :         if(newIt == pNewNodes->begin())
     324          13 :             patchIt = insIt;
     325          13 :         if((*newIt)->GetType() == NTEXT)
     326           9 :             PosAfterInsert = SmCaretPos(*newIt, ((SmTextNode*)*newIt)->GetText().getLength());
     327             :         else
     328           4 :             PosAfterInsert = SmCaretPos(*newIt, 1);
     329             :     }
     330             :     //Patch the places we've changed stuff
     331          13 :                         PatchLineList(pLineList, patchIt);
     332          13 :     PosAfterInsert =    PatchLineList(pLineList, it);
     333             :     //Release list, we've taken the nodes
     334          13 :     delete pNewNodes;
     335          13 :     pNewNodes = NULL;
     336             : 
     337             :     //Finish editing
     338          13 :     FinishEdit(pLineList, pLineParent, nParentIndex, PosAfterInsert);
     339             : }
     340             : 
     341          14 : SmNodeList::iterator SmCursor::FindPositionInLineList(SmNodeList* pLineList, SmCaretPos aCaretPos) {
     342             :     //Find iterator for position
     343          14 :     SmNodeList::iterator it;
     344          21 :     for(it = pLineList->begin(); it != pLineList->end(); ++it){
     345          18 :         if(*it == aCaretPos.pSelectedNode){
     346          11 :             if((*it)->GetType() == NTEXT){
     347             :                 //Split textnode if needed
     348           2 :                 if(aCaretPos.Index > 0){
     349           2 :                     SmTextNode* pText = (SmTextNode*)aCaretPos.pSelectedNode;
     350           2 :                     OUString str1 = pText->GetText().copy(0, aCaretPos.Index);
     351           4 :                     OUString str2 = pText->GetText().copy(aCaretPos.Index);
     352           2 :                     pText->ChangeText(str1);
     353           2 :                     ++it;
     354             :                     //Insert str2 as new text node
     355           2 :                     if(!str2.isEmpty()){
     356           0 :                         SmTextNode* pNewText = new SmTextNode(pText->GetToken(), pText->GetFontDesc());
     357           0 :                         pNewText->ChangeText(str2);
     358           0 :                         it = pLineList->insert(it, pNewText);
     359           2 :                     }
     360             :                 }
     361             :             }else
     362           9 :                 ++it;
     363             :             //it now pointer to the node following pos, so pLineList->insert(it, ...) will insert correctly
     364          11 :             return it;
     365             : 
     366             :         }
     367             :     }
     368             :     //If we didn't find pSelectedNode, it must be because the caret is in front of the line
     369           3 :     return pLineList->begin();
     370             : }
     371             : 
     372          30 : SmCaretPos SmCursor::PatchLineList(SmNodeList* pLineList, SmNodeList::iterator aIter) {
     373             :     //The nodes we should consider merging
     374          30 :     SmNode *prev = NULL,
     375          30 :            *next = NULL;
     376          30 :     if(aIter != pLineList->end())
     377          16 :         next = *aIter;
     378          30 :     if(aIter != pLineList->begin()) {
     379          26 :         --aIter;
     380          26 :         prev = *aIter;
     381          26 :         ++aIter;
     382             :     }
     383             : 
     384             :     //Check if there's textnodes to merge
     385          56 :     if( prev &&
     386          12 :         next &&
     387          14 :         prev->GetType() == NTEXT &&
     388          32 :         next->GetType() == NTEXT &&
     389           0 :         ( prev->GetToken().eType != TNUMBER ||
     390           0 :           next->GetToken().eType == TNUMBER) ){
     391           0 :         SmTextNode *pText = (SmTextNode*)prev,
     392           0 :                    *pOldN = (SmTextNode*)next;
     393           0 :         SmCaretPos retval(pText, pText->GetText().getLength());
     394           0 :         OUString newText;
     395           0 :         newText += pText->GetText();
     396           0 :         newText += pOldN->GetText();
     397           0 :         pText->ChangeText(newText);
     398           0 :         delete pOldN;
     399           0 :         pLineList->erase(aIter);
     400           0 :         return retval;
     401             :     }
     402             : 
     403             :     //Check if there's a SmPlaceNode to remove:
     404          30 :     if(prev && next && prev->GetType() == NPLACE && !SmNodeListParser::IsOperator(next->GetToken())){
     405           2 :         --aIter;
     406           2 :         aIter = pLineList->erase(aIter);
     407           2 :         delete prev;
     408             :         //Return caret pos in front of aIter
     409           2 :         if(aIter != pLineList->begin())
     410           0 :             --aIter; //Thus find node before aIter
     411           2 :         if(aIter == pLineList->begin())
     412           2 :             return SmCaretPos();
     413           0 :         if((*aIter)->GetType() == NTEXT)
     414           0 :             return SmCaretPos(*aIter, ((SmTextNode*)*aIter)->GetText().getLength());
     415           0 :         return SmCaretPos(*aIter, 1);
     416             :     }
     417          28 :     if(prev && next && next->GetType() == NPLACE && !SmNodeListParser::IsOperator(prev->GetToken())){
     418           0 :         aIter = pLineList->erase(aIter);
     419           0 :         delete next;
     420           0 :         if(prev->GetType() == NTEXT)
     421           0 :             return SmCaretPos(prev, ((SmTextNode*)prev)->GetText().getLength());
     422           0 :         return SmCaretPos(prev, 1);
     423             :     }
     424             : 
     425             :     //If we didn't do anything return
     426          28 :     if(!prev) //return an invalid to indicate we're in front of line
     427           4 :         return SmCaretPos();
     428          24 :     if(prev->GetType() == NTEXT)
     429          10 :         return SmCaretPos(prev, ((SmTextNode*)prev)->GetText().getLength());
     430          14 :     return SmCaretPos(prev, 1);
     431             : }
     432             : 
     433           2 : SmNodeList::iterator SmCursor::TakeSelectedNodesFromList(SmNodeList *pLineList,
     434             :                                                          SmNodeList *pSelectedNodes) {
     435           2 :     SmNodeList::iterator retval;
     436           2 :     SmNodeList::iterator it = pLineList->begin();
     437           8 :     while(it != pLineList->end()){
     438           4 :         if((*it)->IsSelected()){
     439             :             //Split text nodes
     440           3 :             if((*it)->GetType() == NTEXT) {
     441           2 :                 SmTextNode* pText = (SmTextNode*)*it;
     442           2 :                 OUString aText = pText->GetText();
     443             :                 //Start and lengths of the segments, 2 is the selected segment
     444           2 :                 int start2 = pText->GetSelectionStart(),
     445           2 :                     start3 = pText->GetSelectionEnd(),
     446           2 :                     len1 = start2 - 0,
     447           2 :                     len2 = start3 - start2,
     448           2 :                     len3 = aText.getLength() - start3;
     449           4 :                 SmToken aToken = pText->GetToken();
     450           2 :                 sal_uInt16 eFontDesc = pText->GetFontDesc();
     451             :                 //If we need make segment 1
     452           2 :                 if(len1 > 0) {
     453           0 :                     int start1 = 0;
     454           0 :                     OUString str = aText.copy(start1, len1);
     455           0 :                     pText->ChangeText(str);
     456           0 :                     ++it;
     457             :                 } else {//Remove it if not needed
     458           2 :                     it = pLineList->erase(it);
     459           2 :                     delete pText;
     460             :                 }
     461             :                 //Set retval to be right after the selection
     462           2 :                 retval = it;
     463             :                 //if we need make segment 3
     464           2 :                 if(len3 > 0) {
     465           0 :                     OUString str = aText.copy(start3, len3);
     466           0 :                     SmTextNode* pSeg3 = new SmTextNode(aToken, eFontDesc);
     467           0 :                     pSeg3->ChangeText(str);
     468           0 :                     retval = pLineList->insert(it, pSeg3);
     469             :                 }
     470             :                 //If we need to save the selected text
     471           2 :                 if(pSelectedNodes && len2 > 0) {
     472           2 :                     OUString str = aText.copy(start2, len2);
     473           2 :                     SmTextNode* pSeg2 = new SmTextNode(aToken, eFontDesc);
     474           2 :                     pSeg2->ChangeText(str);
     475           2 :                     pSelectedNodes->push_back(pSeg2);
     476           2 :                 }
     477             :             } else { //if it's not textnode
     478           1 :                 SmNode* pNode = *it;
     479           1 :                 retval = it = pLineList->erase(it);
     480           1 :                 if(pSelectedNodes)
     481           1 :                     pSelectedNodes->push_back(pNode);
     482             :                 else
     483           0 :                     delete pNode;
     484             :             }
     485             :         } else
     486           1 :             ++it;
     487             :     }
     488           2 :     return retval;
     489             : }
     490             : 
     491           1 : void SmCursor::InsertSubSup(SmSubSup eSubSup) {
     492           1 :     AnnotateSelection();
     493             : 
     494             :     //Find line
     495             :     SmNode *pLine;
     496           1 :     if(HasSelection()) {
     497           0 :         SmNode *pSNode = FindSelectedNode(pTree);
     498             :         OSL_ENSURE(pSNode != NULL, "There must be a selected node when HasSelection is true!");
     499           0 :         pLine = FindTopMostNodeInLine(pSNode, true);
     500             :     } else
     501           1 :         pLine = FindTopMostNodeInLine(position->CaretPos.pSelectedNode, false);
     502             : 
     503             :     //Find Parent and offset in parent
     504           1 :     SmStructureNode *pLineParent = pLine->GetParent();
     505           1 :     int nParentIndex = pLineParent->IndexOfSubNode(pLine);
     506             :     OSL_ENSURE(nParentIndex != -1, "pLine must be a subnode of pLineParent!");
     507             : 
     508             :     //TODO: Consider handling special cases where parent is an SmOperNode,
     509             :     //      Maybe this method should be able to add limits to an SmOperNode...
     510             : 
     511             :     //We begin modifying the tree here
     512           1 :     BeginEdit();
     513             : 
     514             :     //Convert line to list
     515           1 :     SmNodeList* pLineList = NodeToList(pLine);
     516             : 
     517             :     //Take the selection, and/or find iterator for current position
     518           1 :     SmNodeList* pSelectedNodesList = new SmNodeList();
     519           1 :     SmNodeList::iterator it;
     520           1 :     if(HasSelection())
     521           0 :         it = TakeSelectedNodesFromList(pLineList, pSelectedNodesList);
     522             :     else
     523           1 :         it = FindPositionInLineList(pLineList, position->CaretPos);
     524             : 
     525             :     //Find node that this should be applied to
     526             :     SmNode* pSubject;
     527           1 :     bool bPatchLine = pSelectedNodesList->size() > 0; //If the line should be patched later
     528           1 :     if(it != pLineList->begin()) {
     529           1 :         --it;
     530           1 :         pSubject = *it;
     531           1 :         ++it;
     532             :     } else {
     533             :         //Create a new place node
     534           0 :         pSubject = new SmPlaceNode();
     535           0 :         pSubject->Prepare(pDocShell->GetFormat(), *pDocShell);
     536           0 :         it = pLineList->insert(it, pSubject);
     537           0 :         ++it;
     538           0 :         bPatchLine = true;  //We've modified the line it should be patched later.
     539             :     }
     540             : 
     541             :     //Wrap the subject in a SmSubSupNode
     542             :     SmSubSupNode* pSubSup;
     543           1 :     if(pSubject->GetType() != NSUBSUP){
     544           1 :         SmToken token;
     545           1 :         token.nGroup = TGPOWER;
     546           1 :         pSubSup = new SmSubSupNode(token);
     547           1 :         pSubSup->SetBody(pSubject);
     548           1 :         *(--it) = pSubSup;
     549           1 :         ++it;
     550             :     }else
     551           0 :         pSubSup = (SmSubSupNode*)pSubject;
     552             :     //pSubject shouldn't be referenced anymore, pSubSup is the SmSubSupNode in pLineList we wish to edit.
     553             :     //and it pointer to the element following pSubSup in pLineList.
     554           1 :     pSubject = NULL;
     555             : 
     556             :     //Patch the line if we noted that was needed previously
     557           1 :     if(bPatchLine)
     558           0 :         PatchLineList(pLineList, it);
     559             : 
     560             :     //Convert existing, if any, sub-/superscript line to list
     561           1 :     SmNode *pScriptLine = pSubSup->GetSubSup(eSubSup);
     562           1 :     SmNodeList* pScriptLineList = NodeToList(pScriptLine);
     563             : 
     564             :     //Add selection to pScriptLineList
     565           1 :     unsigned int nOldSize = pScriptLineList->size();
     566           1 :     pScriptLineList->insert(pScriptLineList->end(), pSelectedNodesList->begin(), pSelectedNodesList->end());
     567           1 :     delete pSelectedNodesList;
     568           1 :     pSelectedNodesList = NULL;
     569             : 
     570             :     //Patch pScriptLineList if needed
     571           1 :     if(0 < nOldSize && nOldSize < pScriptLineList->size()) {
     572           0 :         SmNodeList::iterator iPatchPoint = pScriptLineList->begin();
     573           0 :         std::advance(iPatchPoint, nOldSize);
     574           0 :         PatchLineList(pScriptLineList, iPatchPoint);
     575             :     }
     576             : 
     577             :     //Find caret pos, that should be used after sub-/superscription.
     578           1 :     SmCaretPos PosAfterScript; //Leave invalid for first position
     579           1 :     if(pScriptLineList->size() > 0)
     580           0 :         PosAfterScript = SmCaretPos::GetPosAfter(pScriptLineList->back());
     581             : 
     582             :     //Parse pScriptLineList
     583           1 :     pScriptLine = SmNodeListParser().Parse(pScriptLineList);
     584           1 :     delete pScriptLineList;
     585           1 :     pScriptLineList = NULL;
     586             : 
     587             :     //Insert pScriptLine back into the tree
     588           1 :     pSubSup->SetSubSup(eSubSup, pScriptLine);
     589             : 
     590             :     //Finish editing
     591           1 :     FinishEdit(pLineList, pLineParent, nParentIndex, PosAfterScript, pScriptLine);
     592           1 : }
     593             : 
     594           0 : bool SmCursor::InsertLimit(SmSubSup eSubSup, bool bMoveCaret) {
     595             :     //Find a subject to set limits on
     596           0 :     SmOperNode *pSubject = NULL;
     597             :     //Check if pSelectedNode might be a subject
     598           0 :     if(position->CaretPos.pSelectedNode->GetType() == NOPER)
     599           0 :         pSubject = (SmOperNode*)position->CaretPos.pSelectedNode;
     600             :     else {
     601             :         //If not, check if parent of the current line is a SmOperNode
     602           0 :         SmNode *pLineNode = FindTopMostNodeInLine(position->CaretPos.pSelectedNode, false);
     603           0 :         if(pLineNode->GetParent() && pLineNode->GetParent()->GetType() == NOPER)
     604           0 :             pSubject = (SmOperNode*)pLineNode->GetParent();
     605             :     }
     606             : 
     607             :     //Abort operation if we're not in the appropriate context
     608           0 :     if(!pSubject)
     609           0 :         return false;
     610             : 
     611           0 :     BeginEdit();
     612             : 
     613             :     //Find the sub sup node
     614           0 :     SmSubSupNode *pSubSup = NULL;
     615             :     //Check if there's already one there...
     616           0 :     if(pSubject->GetSubNode(0)->GetType() == NSUBSUP)
     617           0 :         pSubSup = (SmSubSupNode*)pSubject->GetSubNode(0);
     618             :     else { //if not create a new SmSubSupNode
     619           0 :         SmToken token;
     620           0 :         token.nGroup = TGLIMIT;
     621           0 :         pSubSup = new SmSubSupNode(token);
     622             :         //Set it's body
     623           0 :         pSubSup->SetBody(pSubject->GetSubNode(0));
     624             :         //Replace the operation of the SmOperNode
     625           0 :         pSubject->SetSubNode(0, pSubSup);
     626             :     }
     627             : 
     628             :     //Create the limit, if needed
     629           0 :     SmCaretPos PosAfterLimit;
     630           0 :     SmNode *pLine = NULL;
     631           0 :     if(!pSubSup->GetSubSup(eSubSup)){
     632           0 :         pLine = new SmPlaceNode();
     633           0 :         pSubSup->SetSubSup(eSubSup, pLine);
     634           0 :         PosAfterLimit = SmCaretPos(pLine, 1);
     635             :     //If it's already there... let's move the caret
     636           0 :     } else if(bMoveCaret){
     637           0 :         pLine = pSubSup->GetSubSup(eSubSup);
     638           0 :         SmNodeList* pLineList = NodeToList(pLine);
     639           0 :         if(pLineList->size() > 0)
     640           0 :             PosAfterLimit = SmCaretPos::GetPosAfter(pLineList->back());
     641           0 :         pLine = SmNodeListParser().Parse(pLineList);
     642           0 :         delete pLineList;
     643           0 :         pSubSup->SetSubSup(eSubSup, pLine);
     644             :     }
     645             : 
     646             :     //Rebuild graph of caret positions
     647           0 :     BuildGraph();
     648           0 :     AnnotateSelection();
     649             : 
     650             :     //Set caret position
     651           0 :     if(bMoveCaret)
     652           0 :         if(!SetCaretPosition(PosAfterLimit, true))
     653           0 :             SetCaretPosition(SmCaretPos(pLine, 0), true);
     654             : 
     655           0 :     EndEdit();
     656             : 
     657           0 :     return true;
     658             : }
     659             : 
     660           0 : void SmCursor::InsertBrackets(SmBracketType eBracketType) {
     661           0 :     BeginEdit();
     662             : 
     663           0 :     AnnotateSelection();
     664             : 
     665             :     //Find line
     666             :     SmNode *pLine;
     667           0 :     if(HasSelection()) {
     668           0 :         SmNode *pSNode = FindSelectedNode(pTree);
     669             :         OSL_ENSURE(pSNode != NULL, "There must be a selected node if HasSelection()");
     670           0 :         pLine = FindTopMostNodeInLine(pSNode, true);
     671             :     } else
     672           0 :         pLine = FindTopMostNodeInLine(position->CaretPos.pSelectedNode, false);
     673             : 
     674             :     //Find parent and offset in parent
     675           0 :     SmStructureNode *pLineParent = pLine->GetParent();
     676           0 :     int nParentIndex = pLineParent->IndexOfSubNode(pLine);
     677             :     OSL_ENSURE( nParentIndex != -1, "pLine must be a subnode of pLineParent!");
     678             : 
     679             :     //Convert line to list
     680           0 :     SmNodeList *pLineList = NodeToList(pLine);
     681             : 
     682             :     //Take the selection, and/or find iterator for current position
     683           0 :     SmNodeList *pSelectedNodesList = new SmNodeList();
     684           0 :     SmNodeList::iterator it;
     685           0 :     if(HasSelection())
     686           0 :         it = TakeSelectedNodesFromList(pLineList, pSelectedNodesList);
     687             :     else
     688           0 :         it = FindPositionInLineList(pLineList, position->CaretPos);
     689             : 
     690             :     //If there's no selected nodes, create a place node
     691             :     SmNode *pBodyNode;
     692           0 :     SmCaretPos PosAfterInsert;
     693           0 :     if(pSelectedNodesList->empty()) {
     694           0 :         pBodyNode = new SmPlaceNode();
     695           0 :         PosAfterInsert = SmCaretPos(pBodyNode, 1);
     696             :     } else
     697           0 :         pBodyNode = SmNodeListParser().Parse(pSelectedNodesList);
     698             : 
     699           0 :     delete pSelectedNodesList;
     700             : 
     701             :     //Create SmBraceNode
     702           0 :     SmToken aTok(TLEFT, '\0', "left", 0, 5);
     703           0 :     SmBraceNode *pBrace = new SmBraceNode(aTok);
     704           0 :     pBrace->SetScaleMode(SCALE_HEIGHT);
     705           0 :     SmNode *pLeft = CreateBracket(eBracketType, true),
     706           0 :            *pRight = CreateBracket(eBracketType, false);
     707           0 :     SmBracebodyNode *pBody = new SmBracebodyNode(SmToken());
     708           0 :     pBody->SetSubNodes(pBodyNode, NULL);
     709           0 :     pBrace->SetSubNodes(pLeft, pBody, pRight);
     710           0 :     pBrace->Prepare(pDocShell->GetFormat(), *pDocShell);
     711             : 
     712             :     //Insert into line
     713           0 :     pLineList->insert(it, pBrace);
     714             :     //Patch line (I think this is good enough)
     715           0 :     SmCaretPos pAfter = PatchLineList(pLineList, it);
     716           0 :     if( !PosAfterInsert.IsValid() )
     717           0 :         PosAfterInsert = pAfter;
     718             : 
     719             :     //Finish editing
     720           0 :     FinishEdit(pLineList, pLineParent, nParentIndex, PosAfterInsert);
     721           0 : }
     722             : 
     723           0 : SmNode *SmCursor::CreateBracket(SmBracketType eBracketType, bool bIsLeft) {
     724           0 :     SmToken aTok;
     725           0 :     if(bIsLeft){
     726           0 :         switch(eBracketType){
     727             :             case NoneBrackets:
     728           0 :                 aTok = SmToken(TNONE, '\0', "none", TGLBRACES | TGRBRACES, 0);
     729           0 :                 break;
     730             :             case RoundBrackets:
     731           0 :                 aTok = SmToken(TLPARENT, MS_LPARENT, "(", TGLBRACES, 5);
     732           0 :                 break;
     733             :             case SquareBrackets:
     734           0 :                 aTok = SmToken(TLBRACKET, MS_LBRACKET, "[", TGLBRACES, 5);
     735           0 :                 break;
     736             :             case DoubleSquareBrackets:
     737           0 :                 aTok = SmToken(TLDBRACKET, MS_LDBRACKET, "ldbracket", TGLBRACES, 5);
     738           0 :                 break;
     739             :             case LineBrackets:
     740           0 :                 aTok = SmToken(TLLINE, MS_VERTLINE, "lline", TGLBRACES, 5);
     741           0 :                 break;
     742             :             case DoubleLineBrackets:
     743           0 :                 aTok = SmToken(TLDLINE, MS_DVERTLINE, "ldline", TGLBRACES, 5);
     744           0 :                 break;
     745             :             case CurlyBrackets:
     746           0 :                 aTok = SmToken(TLBRACE, MS_LBRACE, "lbrace", TGLBRACES, 5);
     747           0 :                 break;
     748             :             case AngleBrackets:
     749           0 :                 aTok = SmToken(TLANGLE, MS_LMATHANGLE, "langle", TGLBRACES, 5);
     750           0 :                 break;
     751             :             case CeilBrackets:
     752           0 :                 aTok = SmToken(TLCEIL, MS_LCEIL, "lceil", TGLBRACES, 5);
     753           0 :                 break;
     754             :             case FloorBrackets:
     755           0 :                 aTok = SmToken(TLFLOOR, MS_LFLOOR, "lfloor", TGLBRACES, 5);
     756           0 :                 break;
     757             :         }
     758             :     } else {
     759           0 :         switch(eBracketType) {
     760             :             case NoneBrackets:
     761           0 :                 aTok = SmToken(TNONE, '\0', "none", TGLBRACES | TGRBRACES, 0);
     762           0 :                 break;
     763             :             case RoundBrackets:
     764           0 :                 aTok = SmToken(TRPARENT, MS_RPARENT, ")", TGRBRACES, 5);
     765           0 :                 break;
     766             :             case SquareBrackets:
     767           0 :                 aTok = SmToken(TRBRACKET, MS_RBRACKET, "]", TGRBRACES, 5);
     768           0 :                 break;
     769             :             case DoubleSquareBrackets:
     770           0 :                 aTok = SmToken(TRDBRACKET, MS_RDBRACKET, "rdbracket", TGRBRACES, 5);
     771           0 :                 break;
     772             :             case LineBrackets:
     773           0 :                 aTok = SmToken(TRLINE, MS_VERTLINE, "rline", TGRBRACES, 5);
     774           0 :                 break;
     775             :             case DoubleLineBrackets:
     776           0 :                 aTok = SmToken(TRDLINE, MS_DVERTLINE, "rdline", TGRBRACES, 5);
     777           0 :                 break;
     778             :             case CurlyBrackets:
     779           0 :                 aTok = SmToken(TRBRACE, MS_RBRACE, "rbrace", TGRBRACES, 5);
     780           0 :                 break;
     781             :             case AngleBrackets:
     782           0 :                 aTok = SmToken(TRANGLE, MS_RMATHANGLE, "rangle", TGRBRACES, 5);
     783           0 :                 break;
     784             :             case CeilBrackets:
     785           0 :                 aTok = SmToken(TRCEIL, MS_RCEIL, "rceil", TGRBRACES, 5);
     786           0 :                 break;
     787             :             case FloorBrackets:
     788           0 :                 aTok = SmToken(TRFLOOR, MS_RFLOOR, "rfloor", TGRBRACES, 5);
     789           0 :                 break;
     790             :         }
     791             :     }
     792           0 :     SmNode* pRetVal = new SmMathSymbolNode(aTok);
     793           0 :     pRetVal->SetScaleMode(SCALE_HEIGHT);
     794           0 :     return pRetVal;
     795             : }
     796             : 
     797           0 : bool SmCursor::InsertRow() {
     798           0 :     AnnotateSelection();
     799             : 
     800             :     //Find line
     801             :     SmNode *pLine;
     802           0 :     if(HasSelection()) {
     803           0 :         SmNode *pSNode = FindSelectedNode(pTree);
     804             :         OSL_ENSURE(pSNode != NULL, "There must be a selected node if HasSelection()");
     805           0 :         pLine = FindTopMostNodeInLine(pSNode, true);
     806             :     } else
     807           0 :         pLine = FindTopMostNodeInLine(position->CaretPos.pSelectedNode, false);
     808             : 
     809             :     //Find parent and offset in parent
     810           0 :     SmStructureNode *pLineParent = pLine->GetParent();
     811           0 :     int nParentIndex = pLineParent->IndexOfSubNode(pLine);
     812             :     OSL_ENSURE( nParentIndex != -1, "pLine must be a subnode of pLineParent!");
     813             : 
     814             :     //Discover the context of this command
     815           0 :     SmTableNode  *pTable  = NULL;
     816           0 :     SmMatrixNode *pMatrix = NULL;
     817           0 :     int nTableIndex = nParentIndex;
     818           0 :     if(pLineParent->GetType() == NTABLE)
     819           0 :         pTable = (SmTableNode*)pLineParent;
     820             :     //If it's warped in a SmLineNode, we can still insert a newline
     821           0 :     else if(pLineParent->GetType() == NLINE &&
     822           0 :             pLineParent->GetParent() &&
     823           0 :             pLineParent->GetParent()->GetType() == NTABLE) {
     824             :         //NOTE: This hack might give problems if we stop ignoring SmAlignNode
     825           0 :         pTable = (SmTableNode*)pLineParent->GetParent();
     826           0 :         nTableIndex = pTable->IndexOfSubNode(pLineParent);
     827             :         OSL_ENSURE(nTableIndex != -1, "pLineParent must be a child of its parent!");
     828             :     }
     829           0 :     if(pLineParent->GetType() == NMATRIX)
     830           0 :         pMatrix = (SmMatrixNode*)pLineParent;
     831             : 
     832             :     //If we're not in a context that supports InsertRow, return sal_False
     833           0 :     if(!pTable && !pMatrix)
     834           0 :         return false;
     835             : 
     836             :     //Now we start editing
     837           0 :     BeginEdit();
     838             : 
     839             :     //Convert line to list
     840           0 :     SmNodeList *pLineList = NodeToList(pLine);
     841             : 
     842             :     //Find position in line
     843           0 :     SmNodeList::iterator it;
     844           0 :     if(HasSelection()) {
     845             :         //Take the selected nodes and delete them...
     846           0 :         it = TakeSelectedNodesFromList(pLineList);
     847             :     } else
     848           0 :         it = FindPositionInLineList(pLineList, position->CaretPos);
     849             : 
     850             :     //New caret position after inserting the newline/row in whatever context
     851           0 :     SmCaretPos PosAfterInsert;
     852             : 
     853             :     //If we're in the context of a table
     854           0 :     if(pTable) {
     855           0 :         SmNodeList *pNewLineList = new SmNodeList();
     856             :         //Move elements from pLineList to pNewLineList
     857           0 :         pNewLineList->splice(pNewLineList->begin(), *pLineList, it, pLineList->end());
     858             :         //Make sure it is valid again
     859           0 :         it = pLineList->end();
     860           0 :         if(it != pLineList->begin())
     861           0 :             --it;
     862           0 :         if(pNewLineList->empty())
     863           0 :             pNewLineList->push_front(new SmPlaceNode());
     864             :         //Parse new line
     865           0 :         SmNode *pNewLine = SmNodeListParser().Parse(pNewLineList);
     866           0 :         delete pNewLineList;
     867             :         //Wrap pNewLine in SmLineNode if needed
     868           0 :         if(pLineParent->GetType() == NLINE) {
     869           0 :             SmLineNode *pNewLineNode = new SmLineNode(SmToken(TNEWLINE, '\0', "newline"));
     870           0 :             pNewLineNode->SetSubNodes(pNewLine, NULL);
     871           0 :             pNewLine = pNewLineNode;
     872             :         }
     873             :         //Get position
     874           0 :         PosAfterInsert = SmCaretPos(pNewLine, 0);
     875             :         //Move other nodes if needed
     876           0 :         for( int i = pTable->GetNumSubNodes(); i > nTableIndex + 1; i--)
     877           0 :             pTable->SetSubNode(i, pTable->GetSubNode(i-1));
     878             : 
     879             :         //Insert new line
     880           0 :         pTable->SetSubNode(nTableIndex + 1, pNewLine);
     881             : 
     882             :         //Check if we need to change token type:
     883           0 :         if(pTable->GetNumSubNodes() > 2 && pTable->GetToken().eType == TBINOM) {
     884           0 :             SmToken tok = pTable->GetToken();
     885           0 :             tok.eType = TSTACK;
     886           0 :             pTable->SetToken(tok);
     887             :         }
     888             :     }
     889             :     //If we're in the context of a matrix
     890             :     else {
     891             :         //Find position after insert and patch the list
     892           0 :         PosAfterInsert = PatchLineList(pLineList, it);
     893             :         //Move other children
     894           0 :         sal_uInt16 rows = pMatrix->GetNumRows();
     895           0 :         sal_uInt16 cols = pMatrix->GetNumCols();
     896           0 :         int nRowStart = (nParentIndex - nParentIndex % cols) + cols;
     897           0 :         for( int i = pMatrix->GetNumSubNodes() + cols - 1; i >= nRowStart + cols; i--)
     898           0 :             pMatrix->SetSubNode(i, pMatrix->GetSubNode(i - cols));
     899           0 :         for( int i = nRowStart; i < nRowStart + cols; i++) {
     900           0 :             SmPlaceNode *pNewLine = new SmPlaceNode();
     901           0 :             if(i == nParentIndex + cols)
     902           0 :                 PosAfterInsert = SmCaretPos(pNewLine, 0);
     903           0 :             pMatrix->SetSubNode(i, pNewLine);
     904             :         }
     905           0 :         pMatrix->SetRowCol(rows + 1, cols);
     906             :     }
     907             : 
     908             :     //Finish editing
     909           0 :     FinishEdit(pLineList, pLineParent, nParentIndex, PosAfterInsert);
     910             :     //FinishEdit is actually used to handle siturations where parent is an instance of
     911             :     //SmSubSupNode. In this case parent should always be a table or matrix, however, for
     912             :     //code reuse we just use FinishEdit() here too.
     913           0 :     return true;
     914             : }
     915             : 
     916           2 : void SmCursor::InsertFraction() {
     917           2 :     AnnotateSelection();
     918             : 
     919             :     //Find line
     920             :     SmNode *pLine;
     921           2 :     if(HasSelection()) {
     922           2 :         SmNode *pSNode = FindSelectedNode(pTree);
     923             :         OSL_ENSURE(pSNode != NULL, "There must be a selected node when HasSelection is true!");
     924           2 :         pLine = FindTopMostNodeInLine(pSNode, true);
     925             :     } else
     926           0 :         pLine = FindTopMostNodeInLine(position->CaretPos.pSelectedNode, false);
     927             : 
     928             :     //Find Parent and offset in parent
     929           2 :     SmStructureNode *pLineParent = pLine->GetParent();
     930           2 :     int nParentIndex = pLineParent->IndexOfSubNode(pLine);
     931             :     OSL_ENSURE(nParentIndex != -1, "pLine must be a subnode of pLineParent!");
     932             : 
     933             :     //We begin modifying the tree here
     934           2 :     BeginEdit();
     935             : 
     936             :     //Convert line to list
     937           2 :     SmNodeList* pLineList = NodeToList(pLine);
     938             : 
     939             :     //Take the selection, and/or find iterator for current position
     940           2 :     SmNodeList* pSelectedNodesList = new SmNodeList();
     941           2 :     SmNodeList::iterator it;
     942           2 :     if(HasSelection())
     943           2 :         it = TakeSelectedNodesFromList(pLineList, pSelectedNodesList);
     944             :     else
     945           0 :         it = FindPositionInLineList(pLineList, position->CaretPos);
     946             : 
     947             :     //Create pNum, and pDenom
     948           2 :     bool bEmptyFraction = pSelectedNodesList->empty();
     949             :     SmNode *pNum = bEmptyFraction
     950           0 :         ? new SmPlaceNode()
     951           2 :         : SmNodeListParser().Parse(pSelectedNodesList);
     952           2 :     SmNode *pDenom = new SmPlaceNode();
     953           2 :     delete pSelectedNodesList;
     954           2 :     pSelectedNodesList = NULL;
     955             : 
     956             :     //Create new fraction
     957           2 :     SmBinVerNode *pFrac = new SmBinVerNode(SmToken(TOVER, '\0', "over", TGPRODUCT, 0));
     958           2 :     SmNode *pRect = new SmRectangleNode(SmToken());
     959           2 :     pFrac->SetSubNodes(pNum, pRect, pDenom);
     960             : 
     961             :     //Insert in pLineList
     962           2 :     SmNodeList::iterator patchIt = pLineList->insert(it, pFrac);
     963           2 :     PatchLineList(pLineList, patchIt);
     964           2 :     PatchLineList(pLineList, it);
     965             : 
     966             :     //Finish editing
     967           2 :     SmNode *pSelectedNode = bEmptyFraction ? pNum : pDenom;
     968           2 :     FinishEdit(pLineList, pLineParent, nParentIndex, SmCaretPos(pSelectedNode, 1));
     969           2 : }
     970             : 
     971           9 : void SmCursor::InsertText(const OUString& aString)
     972             : {
     973           9 :     BeginEdit();
     974             : 
     975           9 :     Delete();
     976             : 
     977           9 :     SmToken token;
     978           9 :     token.eType = TIDENT;
     979           9 :     token.cMathChar = '\0';
     980           9 :     token.nGroup = 0;
     981           9 :     token.nLevel = 5;
     982           9 :     token.aText = aString;
     983             : 
     984           9 :     SmTextNode* pText = new SmTextNode(token, FNT_VARIABLE);
     985             : 
     986             :     //Prepare the new node
     987           9 :     pText->Prepare(pDocShell->GetFormat(), *pDocShell);
     988           9 :     pText->AdjustFontDesc();
     989             : 
     990           9 :     SmNodeList* pList = new SmNodeList();
     991           9 :     pList->push_front(pText);
     992           9 :     InsertNodes(pList);
     993             : 
     994           9 :     EndEdit();
     995           9 : }
     996             : 
     997           4 : void SmCursor::InsertElement(SmFormulaElement element){
     998           4 :     BeginEdit();
     999             : 
    1000           4 :     Delete();
    1001             : 
    1002             :     //Create new node
    1003           4 :     SmNode* pNewNode = NULL;
    1004           4 :     switch(element){
    1005             :         case BlankElement:
    1006             :         {
    1007           0 :             SmToken token;
    1008           0 :             token.nGroup = TGBLANK;
    1009           0 :             token.aText = "~";
    1010           0 :             pNewNode = new SmBlankNode(token);
    1011           0 :         }break;
    1012             :         case FactorialElement:
    1013             :         {
    1014           0 :             SmToken token(TFACT, MS_FACT, "fact", TGUNOPER, 5);
    1015           0 :             pNewNode = new SmMathSymbolNode(token);
    1016           0 :         }break;
    1017             :         case PlusElement:
    1018             :         {
    1019           4 :             SmToken token;
    1020           4 :             token.eType = TPLUS;
    1021           4 :             token.cMathChar = MS_PLUS;
    1022           4 :             token.nGroup = TGUNOPER | TGSUM;
    1023           4 :             token.nLevel = 5;
    1024           4 :             token.aText = "+";
    1025           4 :             pNewNode = new SmMathSymbolNode(token);
    1026           4 :         }break;
    1027             :         case MinusElement:
    1028             :         {
    1029           0 :             SmToken token;
    1030           0 :             token.eType = TMINUS;
    1031           0 :             token.cMathChar = MS_MINUS;
    1032           0 :             token.nGroup = TGUNOPER | TGSUM;
    1033           0 :             token.nLevel = 5;
    1034           0 :             token.aText = "-";
    1035           0 :             pNewNode = new SmMathSymbolNode(token);
    1036           0 :         }break;
    1037             :         case CDotElement:
    1038             :         {
    1039           0 :             SmToken token;
    1040           0 :             token.eType = TCDOT;
    1041           0 :             token.cMathChar = MS_CDOT;
    1042           0 :             token.nGroup = TGPRODUCT;
    1043           0 :             token.aText = "cdot";
    1044           0 :             pNewNode = new SmMathSymbolNode(token);
    1045           0 :         }break;
    1046             :         case EqualElement:
    1047             :         {
    1048           0 :             SmToken token;
    1049           0 :             token.eType = TASSIGN;
    1050           0 :             token.cMathChar = MS_ASSIGN;
    1051           0 :             token.nGroup = TGRELATION;
    1052           0 :             token.aText = "=";
    1053           0 :             pNewNode = new SmMathSymbolNode(token);
    1054           0 :         }break;
    1055             :         case LessThanElement:
    1056             :         {
    1057           0 :             SmToken token;
    1058           0 :             token.eType = TLT;
    1059           0 :             token.cMathChar = MS_LT;
    1060           0 :             token.nGroup = TGRELATION;
    1061           0 :             token.aText = "<";
    1062           0 :             pNewNode = new SmMathSymbolNode(token);
    1063           0 :         }break;
    1064             :         case GreaterThanElement:
    1065             :         {
    1066           0 :             SmToken token;
    1067           0 :             token.eType = TGT;
    1068           0 :             token.cMathChar = MS_GT;
    1069           0 :             token.nGroup = TGRELATION;
    1070           0 :             token.aText = ">";
    1071           0 :             pNewNode = new SmMathSymbolNode(token);
    1072           0 :         }break;
    1073             :         case PercentElement:
    1074             :         {
    1075           0 :             SmToken token;
    1076           0 :             token.eType = TTEXT;
    1077           0 :             token.cMathChar = MS_PERCENT;
    1078           0 :             token.nGroup = 0;
    1079           0 :             token.aText = "\"%\"";
    1080           0 :             pNewNode = new SmMathSymbolNode(token);
    1081           0 :         }break;
    1082             :         default:
    1083             :             SAL_WARN("starmath", "Element unknown!");
    1084             :     }
    1085             :     OSL_ENSURE(pNewNode != NULL, "No new node was created!");
    1086           4 :     if(!pNewNode)
    1087           4 :         return;
    1088             : 
    1089             :     //Prepare the new node
    1090           4 :     pNewNode->Prepare(pDocShell->GetFormat(), *pDocShell);
    1091             : 
    1092             :     //Insert new node
    1093           4 :     SmNodeList* pList = new SmNodeList();
    1094           4 :     pList->push_front(pNewNode);
    1095           4 :     InsertNodes(pList);
    1096             : 
    1097           4 :     EndEdit();
    1098             : }
    1099             : 
    1100           0 : void SmCursor::InsertSpecial(const OUString& _aString)
    1101             : {
    1102           0 :     BeginEdit();
    1103           0 :     Delete();
    1104             : 
    1105           0 :     OUString aString = comphelper::string::strip(_aString, ' ');
    1106             : 
    1107             :     //Create instance of special node
    1108           0 :     SmToken token;
    1109           0 :     token.eType = TSPECIAL;
    1110           0 :     token.cMathChar = '\0';
    1111           0 :     token.nGroup = 0;
    1112           0 :     token.nLevel = 5;
    1113           0 :     token.aText = aString;
    1114           0 :     SmSpecialNode* pSpecial = new SmSpecialNode(token);
    1115             : 
    1116             :     //Prepare the special node
    1117           0 :     pSpecial->Prepare(pDocShell->GetFormat(), *pDocShell);
    1118             : 
    1119             :     //Insert the node
    1120           0 :     SmNodeList* pList = new SmNodeList();
    1121           0 :     pList->push_front(pSpecial);
    1122           0 :     InsertNodes(pList);
    1123             : 
    1124           0 :     EndEdit();
    1125           0 : }
    1126             : 
    1127           0 : void SmCursor::InsertCommand(sal_uInt16 nCommand) {
    1128           0 :     switch(nCommand){
    1129             :         case RID_NEWLINE:
    1130           0 :             InsertRow();
    1131           0 :             break;
    1132             :         case RID_FROMX:
    1133           0 :             InsertLimit(CSUB, true);
    1134           0 :             break;
    1135             :         case RID_TOX:
    1136           0 :             InsertLimit(CSUP, true);
    1137           0 :             break;
    1138             :         case RID_FROMXTOY:
    1139           0 :             if(InsertLimit(CSUB, true))
    1140           0 :                 InsertLimit(CSUP, true);
    1141           0 :             break;
    1142             :         default:
    1143           0 :             InsertCommandText(SM_RESSTR(nCommand));
    1144           0 :             break;
    1145             :     }
    1146           0 : }
    1147             : 
    1148           0 : void SmCursor::InsertCommandText(const OUString& aCommandText) {
    1149             :     //Parse the sub expression
    1150           0 :     SmNode* pSubExpr = SmParser().ParseExpression(aCommandText);
    1151             : 
    1152             :     //Prepare the subtree
    1153           0 :     pSubExpr->Prepare(pDocShell->GetFormat(), *pDocShell);
    1154             : 
    1155             :     //Convert subtree to list
    1156           0 :     SmNodeList* pLineList = NodeToList(pSubExpr);
    1157             : 
    1158           0 :     BeginEdit();
    1159             : 
    1160             :     //Delete any selection
    1161           0 :     Delete();
    1162             : 
    1163             :     //Insert it
    1164           0 :     InsertNodes(pLineList);
    1165             : 
    1166           0 :     EndEdit();
    1167           0 : }
    1168             : 
    1169           0 : void SmCursor::Copy(){
    1170           0 :     if(!HasSelection())
    1171           0 :         return;
    1172             : 
    1173             :     //Find selected node
    1174           0 :     SmNode* pSNode = FindSelectedNode(pTree);
    1175             :     //Find visual line
    1176           0 :     SmNode* pLine = FindTopMostNodeInLine(pSNode, true);
    1177             : 
    1178             :     //Clone selected nodes
    1179             :     SmNodeList* pList;
    1180           0 :     if(IsLineCompositionNode(pLine))
    1181           0 :         pList = CloneLineToList((SmStructureNode*)pLine, true);
    1182             :     else{
    1183           0 :         pList = new SmNodeList();
    1184             :         //Special care to only clone selected text
    1185           0 :         if(pLine->GetType() == NTEXT) {
    1186           0 :             SmTextNode *pText = (SmTextNode*)pLine;
    1187           0 :             SmTextNode *pClone = new SmTextNode( pText->GetToken(), pText->GetFontDesc() );
    1188           0 :             int start  = pText->GetSelectionStart(),
    1189           0 :                 length = pText->GetSelectionEnd() - pText->GetSelectionStart();
    1190           0 :             pClone->ChangeText(pText->GetText().copy(start, length));
    1191           0 :             pClone->SetScaleMode(pText->GetScaleMode());
    1192           0 :             pList->push_front(pClone);
    1193             :         } else {
    1194           0 :             SmCloningVisitor aCloneFactory;
    1195           0 :             pList->push_front(aCloneFactory.Clone(pLine));
    1196             :         }
    1197             :     }
    1198             : 
    1199             :     //Set clipboard
    1200           0 :     if (pList->size() > 0)
    1201           0 :         SetClipboard(pList);
    1202             :     else
    1203           0 :         delete pList;
    1204             : }
    1205             : 
    1206           0 : void SmCursor::Paste() {
    1207           0 :     BeginEdit();
    1208           0 :     Delete();
    1209             : 
    1210           0 :     if(pClipboard && pClipboard->size() > 0)
    1211           0 :         InsertNodes(CloneList(pClipboard));
    1212             : 
    1213           0 :     EndEdit();
    1214           0 : }
    1215             : 
    1216           0 : SmNodeList* SmCursor::CloneList(SmNodeList* pList){
    1217           0 :     SmCloningVisitor aCloneFactory;
    1218           0 :     SmNodeList* pClones = new SmNodeList();
    1219             : 
    1220           0 :     SmNodeList::iterator it;
    1221           0 :     for(it = pList->begin(); it != pList->end(); ++it){
    1222           0 :         SmNode *pClone = aCloneFactory.Clone(*it);
    1223           0 :         pClones->push_back(pClone);
    1224             :     }
    1225             : 
    1226           0 :     return pClones;
    1227             : }
    1228             : 
    1229           4 : void SmCursor::SetClipboard(SmNodeList* pList){
    1230           4 :     if(pClipboard){
    1231             :         //Delete all nodes on the clipboard
    1232           0 :         SmNodeList::iterator it;
    1233           0 :         for(it = pClipboard->begin(); it != pClipboard->end(); ++it)
    1234           0 :             delete (*it);
    1235           0 :         delete pClipboard;
    1236             :     }
    1237           4 :     pClipboard = pList;
    1238           4 : }
    1239             : 
    1240          16 : SmNode* SmCursor::FindTopMostNodeInLine(SmNode* pSNode, bool MoveUpIfSelected){
    1241             :     //If we haven't got a subnode
    1242          16 :     if(!pSNode)
    1243           0 :         return NULL;
    1244             : 
    1245             :     //Move up parent until we find a node who's
    1246             :     //parent is NULL or isn't selected and not a type of:
    1247             :     //      SmExpressionNode
    1248             :     //      SmLineNode
    1249             :     //      SmBinHorNode
    1250             :     //      SmUnHorNode
    1251             :     //      SmAlignNode
    1252             :     //      SmFontNode
    1253          66 :     while(pSNode->GetParent() &&
    1254           6 :           ((MoveUpIfSelected &&
    1255          39 :             pSNode->GetParent()->IsSelected()) ||
    1256          33 :            IsLineCompositionNode(pSNode->GetParent())))
    1257          17 :         pSNode = pSNode->GetParent();
    1258             :     //Now we have the selection line node
    1259          16 :     return pSNode;
    1260             : }
    1261             : 
    1262           7 : SmNode* SmCursor::FindSelectedNode(SmNode* pNode){
    1263           7 :     SmNodeIterator it(pNode);
    1264          15 :     while(it.Next()){
    1265           7 :         if(it->IsSelected())
    1266           2 :             return it.Current();
    1267           5 :         SmNode* pRetVal = FindSelectedNode(it.Current());
    1268           5 :         if(pRetVal)
    1269           4 :             return pRetVal;
    1270             :     }
    1271           1 :     return NULL;
    1272             : }
    1273             : 
    1274          21 : SmNodeList* SmCursor::LineToList(SmStructureNode* pLine, SmNodeList* list){
    1275          21 :     SmNodeIterator it(pLine);
    1276          73 :     while(it.Next()){
    1277          31 :         switch(it->GetType()){
    1278             :             case NLINE:
    1279             :             case NUNHOR:
    1280             :             case NEXPRESSION:
    1281             :             case NBINHOR:
    1282             :             case NALIGN:
    1283             :             case NFONT:
    1284           7 :                 LineToList((SmStructureNode*)it.Current(), list);
    1285           7 :                 break;
    1286             :             case NERROR:
    1287           4 :                 delete it.Current();
    1288           4 :                 break;
    1289             :             default:
    1290          20 :                 list->push_back(it.Current());
    1291             :         }
    1292             :     }
    1293          21 :     SmNodeArray emptyArray(0);
    1294          21 :     pLine->SetSubNodes(emptyArray);
    1295          21 :     delete pLine;
    1296          21 :     return list;
    1297             : }
    1298             : 
    1299           0 : SmNodeList* SmCursor::CloneLineToList(SmStructureNode* pLine, bool bOnlyIfSelected, SmNodeList* pList){
    1300           0 :     SmCloningVisitor aCloneFactory;
    1301           0 :     SmNodeIterator it(pLine);
    1302           0 :     while(it.Next()){
    1303           0 :         if( IsLineCompositionNode( it.Current() ) )
    1304           0 :             CloneLineToList( (SmStructureNode*)it.Current(), bOnlyIfSelected, pList );
    1305           0 :         else if( (!bOnlyIfSelected || it->IsSelected()) && it->GetType() != NERROR ) {
    1306             :             //Only clone selected text from SmTextNode
    1307           0 :             if(it->GetType() == NTEXT) {
    1308           0 :                 SmTextNode *pText = (SmTextNode*)it.Current();
    1309           0 :                 SmTextNode *pClone = new SmTextNode( it->GetToken(), pText->GetFontDesc() );
    1310           0 :                 int start = pText->GetSelectionStart(),
    1311           0 :                     length = pText->GetSelectionEnd() - pText->GetSelectionStart();
    1312           0 :                 pClone->ChangeText(pText->GetText().copy(start, length));
    1313           0 :                 pClone->SetScaleMode(pText->GetScaleMode());
    1314           0 :                 pList->push_back(pClone);
    1315             :             } else
    1316           0 :                 pList->push_back(aCloneFactory.Clone(it.Current()));
    1317             :         }
    1318             :     }
    1319           0 :     return pList;
    1320             : }
    1321             : 
    1322          49 : bool SmCursor::IsLineCompositionNode(SmNode* pNode){
    1323          49 :     switch(pNode->GetType()){
    1324             :         case NLINE:
    1325             :         case NUNHOR:
    1326             :         case NEXPRESSION:
    1327             :         case NBINHOR:
    1328             :         case NALIGN:
    1329             :         case NFONT:
    1330          31 :             return true;
    1331             :         default:
    1332          18 :             return false;
    1333             :     }
    1334             : }
    1335             : 
    1336           0 : int SmCursor::CountSelectedNodes(SmNode* pNode){
    1337           0 :     int nCount = 0;
    1338           0 :     SmNodeIterator it(pNode);
    1339           0 :     while(it.Next()){
    1340           0 :         if(it->IsSelected() && !IsLineCompositionNode(it.Current()))
    1341           0 :             nCount++;
    1342           0 :         nCount += CountSelectedNodes(it.Current());
    1343             :     }
    1344           0 :     return nCount;
    1345             : }
    1346             : 
    1347           0 : bool SmCursor::HasComplexSelection(){
    1348           0 :     if(!HasSelection())
    1349           0 :         return false;
    1350           0 :     AnnotateSelection();
    1351             : 
    1352           0 :     return CountSelectedNodes(pTree) > 1;
    1353             : }
    1354             : 
    1355          16 : void SmCursor::FinishEdit(SmNodeList* pLineList,
    1356             :                           SmStructureNode* pParent,
    1357             :                           int nParentIndex,
    1358             :                           SmCaretPos PosAfterEdit,
    1359             :                           SmNode* pStartLine) {
    1360             :     //Store number of nodes in line for later
    1361          16 :     int entries = pLineList->size();
    1362             : 
    1363             :     //Parse list of nodes to a tree
    1364          16 :     SmNodeListParser parser;
    1365          16 :     SmNode* pLine = parser.Parse(pLineList);
    1366          16 :     delete pLineList;
    1367             : 
    1368             :     //Check if we're making the body of a subsup node bigger than one
    1369          35 :     if(pParent->GetType() == NSUBSUP &&
    1370          16 :        nParentIndex == 0 &&
    1371             :        entries > 1) {
    1372             :         //Wrap pLine in scalable round brackets
    1373           0 :         SmToken aTok(TLEFT, '\0', "left", 0, 5);
    1374           0 :         SmBraceNode *pBrace = new SmBraceNode(aTok);
    1375           0 :         pBrace->SetScaleMode(SCALE_HEIGHT);
    1376           0 :         SmNode *pLeft  = CreateBracket(RoundBrackets, true),
    1377           0 :                *pRight = CreateBracket(RoundBrackets, false);
    1378           0 :         SmBracebodyNode *pBody = new SmBracebodyNode(SmToken());
    1379           0 :         pBody->SetSubNodes(pLine, NULL);
    1380           0 :         pBrace->SetSubNodes(pLeft, pBody, pRight);
    1381           0 :         pBrace->Prepare(pDocShell->GetFormat(), *pDocShell);
    1382           0 :         pLine = pBrace;
    1383             :         //TODO: Consider the following alternative behavior:
    1384             :         //Consider the line: A + {B + C}^D lsub E
    1385             :         //Here pLineList is B, + and C and pParent is a subsup node with
    1386             :         //both RSUP and LSUB set. Imagine the user just inserted "B +" in
    1387             :         //the body of the subsup node...
    1388             :         //The most natural thing to do would be to make the line like this:
    1389             :         //A + B lsub E + C ^ D
    1390             :         //E.g. apply LSUB and LSUP to the first element in pLineList and RSUP
    1391             :         //and RSUB to the last eleent in pLineList. But how should this act
    1392             :         //for CSUP and CSUB ???
    1393             :         //For this reason and because brackets was faster to implement, this solution
    1394             :         //have been choosen. It might be worth working on the other solution later...
    1395             :     }
    1396             : 
    1397             :     //Set pStartLine if NULL
    1398          16 :     if(!pStartLine)
    1399          15 :         pStartLine = pLine;
    1400             : 
    1401             :     //Insert it back into the parent
    1402          16 :     pParent->SetSubNode(nParentIndex, pLine);
    1403             : 
    1404             :     //Rebuild graph of caret position
    1405          16 :     anchor = NULL;
    1406          16 :     position = NULL;
    1407          16 :     BuildGraph();
    1408          16 :     AnnotateSelection(); //Update selection annotation!
    1409             : 
    1410             :     //Set caret position
    1411          16 :     if(!SetCaretPosition(PosAfterEdit, true))
    1412           1 :         SetCaretPosition(SmCaretPos(pStartLine, 0), true);
    1413             : 
    1414             :     //End edit section
    1415          16 :     EndEdit();
    1416          16 : }
    1417             : 
    1418          29 : void SmCursor::BeginEdit(){
    1419          58 :     if(nEditSections++ > 0) return;
    1420             : 
    1421          16 :     bIsEnabledSetModifiedSmDocShell = pDocShell->IsEnableSetModified();
    1422          16 :     if( bIsEnabledSetModifiedSmDocShell )
    1423           0 :         pDocShell->EnableSetModified( false );
    1424             : }
    1425             : 
    1426          29 : void SmCursor::EndEdit(){
    1427          58 :     if(--nEditSections > 0) return;
    1428             : 
    1429          16 :     pDocShell->SetFormulaArranged(false);
    1430             :     //Okay, I don't know what this does... :)
    1431             :     //It's used in SmDocShell::SetText and with places where everything is modified.
    1432             :     //I think it does some magic, with sfx, but everything is totally undocumented so
    1433             :     //it's kinda hard to tell...
    1434          16 :     if ( bIsEnabledSetModifiedSmDocShell )
    1435           0 :         pDocShell->EnableSetModified( bIsEnabledSetModifiedSmDocShell );
    1436             :     //I think this notifies people around us that we've modified this document...
    1437          16 :     pDocShell->SetModified(true);
    1438             :     //I think SmDocShell uses this value when it sends an update graphics event
    1439             :     //Anyway comments elsewhere suggests it need to be updated...
    1440          16 :     pDocShell->nModifyCount++;
    1441             : 
    1442             :     //TODO: Consider copying the update accessability code from SmDocShell::SetText in here...
    1443             :     //This somehow updates the size of SmGraphicView if it is running in embedded mode
    1444          16 :     if( pDocShell->GetCreateMode() == SFX_CREATE_MODE_EMBEDDED )
    1445          16 :         pDocShell->OnDocumentPrinterChanged(0);
    1446             : 
    1447             :     //Request a replaint...
    1448          16 :     RequestRepaint();
    1449             : 
    1450             :     //Update the edit engine and text of the document
    1451          16 :     OUString formula;
    1452          16 :     SmNodeToTextVisitor(pTree, formula);
    1453             :     //pTree->CreateTextFromNode(formula);
    1454          16 :     pDocShell->aText = formula;
    1455          16 :     pDocShell->GetEditEngine().QuickInsertText( formula, ESelection( 0, 0, EE_PARA_ALL, EE_TEXTPOS_ALL ) );
    1456          16 :     pDocShell->GetEditEngine().QuickFormatDoc();
    1457             : }
    1458             : 
    1459          44 : void SmCursor::RequestRepaint(){
    1460          44 :     SmViewShell *pViewSh = SmGetActiveView();
    1461          44 :     if( pViewSh ) {
    1462           0 :         if ( SFX_CREATE_MODE_EMBEDDED == pDocShell->GetCreateMode() )
    1463           0 :             pDocShell->Repaint();
    1464             :         else
    1465           0 :             pViewSh->GetGraphicWindow().Invalidate();
    1466             :     }
    1467          44 : }
    1468             : 
    1469           0 : bool SmCursor::IsAtTailOfBracket(SmBracketType eBracketType, SmBraceNode** ppBraceNode) const {
    1470           0 :     const SmCaretPos pos = GetPosition();
    1471           0 :     if (!pos.IsValid()) {
    1472           0 :         return false;
    1473             :     }
    1474             : 
    1475           0 :     SmNode* pNode = pos.pSelectedNode;
    1476             : 
    1477           0 :     if (pNode->GetType() == NTEXT) {
    1478           0 :         SmTextNode* pTextNode = static_cast<SmTextNode*>(pNode);
    1479           0 :         if (pos.Index < pTextNode->GetText().getLength()) {
    1480             :             // The cursor is on a text node and at the middle of it.
    1481           0 :             return false;
    1482             :         }
    1483             :     } else {
    1484           0 :         if (pos.Index < 1) {
    1485           0 :             return false;
    1486             :         }
    1487             :     }
    1488             : 
    1489             :     while (true) {
    1490           0 :         SmStructureNode* pParentNode = pNode->GetParent();
    1491           0 :         if (!pParentNode) {
    1492             :             // There's no brace body node in the ancestors.
    1493           0 :             return false;
    1494             :         }
    1495             : 
    1496           0 :         sal_uInt16 index = pNode->FindIndex();
    1497           0 :         if (index + 1 != pParentNode->GetNumSubNodes()) {
    1498             :             // The cursor is not at the tail at one of ancestor nodes.
    1499           0 :             return false;
    1500             :         }
    1501             : 
    1502           0 :         pNode = pParentNode;
    1503           0 :         if (pNode->GetType() == NBRACEBODY) {
    1504             :             // Found the brace body node.
    1505           0 :             break;
    1506             :         }
    1507             :     }
    1508             : 
    1509           0 :     SmStructureNode* pBraceNodeTmp = pNode->GetParent();
    1510           0 :     if (!pBraceNodeTmp || pBraceNodeTmp->GetType() != NBRACE) {
    1511             :         // Brace node is invalid.
    1512           0 :         return false;
    1513             :     }
    1514             : 
    1515           0 :     SmBraceNode* pBraceNode = static_cast<SmBraceNode*>(pBraceNodeTmp);
    1516           0 :     SmMathSymbolNode* pClosingNode = pBraceNode->ClosingBrace();
    1517           0 :     if (!pClosingNode) {
    1518             :         // Couldn't get closing symbol node.
    1519           0 :         return false;
    1520             :     }
    1521             : 
    1522             :     // Check if the closing brace matches eBracketType.
    1523           0 :     SmTokenType eClosingTokenType = pClosingNode->GetToken().eType;
    1524           0 :     switch (eBracketType) {
    1525           0 :     case NoneBrackets:         if (eClosingTokenType != TNONE)      { return false; } break;
    1526           0 :     case RoundBrackets:        if (eClosingTokenType != TRPARENT)   { return false; } break;
    1527           0 :     case SquareBrackets:       if (eClosingTokenType != TRBRACKET)  { return false; } break;
    1528           0 :     case DoubleSquareBrackets: if (eClosingTokenType != TRDBRACKET) { return false; } break;
    1529           0 :     case LineBrackets:         if (eClosingTokenType != TRLINE)     { return false; } break;
    1530           0 :     case DoubleLineBrackets:   if (eClosingTokenType != TRDLINE)    { return false; } break;
    1531           0 :     case CurlyBrackets:        if (eClosingTokenType != TRBRACE)    { return false; } break;
    1532           0 :     case AngleBrackets:        if (eClosingTokenType != TRANGLE)    { return false; } break;
    1533           0 :     case CeilBrackets:         if (eClosingTokenType != TRCEIL)     { return false; } break;
    1534           0 :     case FloorBrackets:        if (eClosingTokenType != TRFLOOR)    { return false; } break;
    1535             :     default:
    1536           0 :         return false;
    1537             :     }
    1538             : 
    1539           0 :     if (ppBraceNode) {
    1540           0 :         *ppBraceNode = static_cast<SmBraceNode*>(pBraceNode);
    1541             :     }
    1542             : 
    1543           0 :     return true;
    1544             : }
    1545             : 
    1546           0 : void SmCursor::MoveAfterBracket(SmBraceNode* pBraceNode, bool bMoveAnchor)
    1547             : {
    1548           0 :     position->CaretPos.pSelectedNode = pBraceNode;
    1549           0 :     position->CaretPos.Index = 1;
    1550           0 :     if (bMoveAnchor) {
    1551           0 :         anchor->CaretPos.pSelectedNode = pBraceNode;
    1552           0 :         anchor->CaretPos.Index = 1;
    1553             :     }
    1554           0 :     RequestRepaint();
    1555           0 : }
    1556             : 
    1557             : 
    1558             : /////////////////////////////////////// SmNodeListParser
    1559             : 
    1560          19 : SmNode* SmNodeListParser::Parse(SmNodeList* list, bool bDeleteErrorNodes){
    1561          19 :     pList = list;
    1562          19 :     if(bDeleteErrorNodes){
    1563             :         //Delete error nodes
    1564          19 :         SmNodeList::iterator it = pList->begin();
    1565          73 :         while(it != pList->end()) {
    1566          35 :             if((*it)->GetType() == NERROR){
    1567             :                 //Delete and erase
    1568           0 :                 delete *it;
    1569           0 :                 it = pList->erase(it);
    1570             :             }else
    1571          35 :                 ++it;
    1572             :         }
    1573             :     }
    1574          19 :     SmNode* retval = Expression();
    1575          19 :     pList = NULL;
    1576          19 :     return retval;
    1577             : }
    1578             : 
    1579          19 : SmNode* SmNodeListParser::Expression(){
    1580          19 :     SmNodeArray NodeArray;
    1581             :     //Accept as many relations as there is
    1582          59 :     while(Terminal())
    1583          21 :         NodeArray.push_back(Relation());
    1584             : 
    1585             :     //Create SmExpressionNode, I hope SmToken() will do :)
    1586          19 :     SmStructureNode* pExpr = new SmExpressionNode(SmToken());
    1587          19 :     pExpr->SetSubNodes(NodeArray);
    1588          19 :     return pExpr;
    1589             : }
    1590             : 
    1591          21 : SmNode* SmNodeListParser::Relation(){
    1592             :     //Read a sum
    1593          21 :     SmNode* pLeft = Sum();
    1594             :     //While we have tokens and the next is a relation
    1595          42 :     while(Terminal() && IsRelationOperator(Terminal()->GetToken())){
    1596             :         //Take the operator
    1597           0 :         SmNode* pOper = Take();
    1598             :         //Find the right side of the relation
    1599           0 :         SmNode* pRight = Sum();
    1600             :         //Create new SmBinHorNode
    1601           0 :         SmStructureNode* pNewNode = new SmBinHorNode(SmToken());
    1602           0 :         pNewNode->SetSubNodes(pLeft, pOper, pRight);
    1603           0 :         pLeft = pNewNode;
    1604             :     }
    1605          21 :     return pLeft;
    1606             : }
    1607             : 
    1608          21 : SmNode* SmNodeListParser::Sum(){
    1609             :     //Read a product
    1610          21 :     SmNode* pLeft = Product();
    1611             :     //While we have tokens and the next is a sum
    1612          50 :     while(Terminal() && IsSumOperator(Terminal()->GetToken())){
    1613             :         //Take the operator
    1614           8 :         SmNode* pOper = Take();
    1615             :         //Find the right side of the sum
    1616           8 :         SmNode* pRight = Product();
    1617             :         //Create new SmBinHorNode
    1618           8 :         SmStructureNode* pNewNode = new SmBinHorNode(SmToken());
    1619           8 :         pNewNode->SetSubNodes(pLeft, pOper, pRight);
    1620           8 :         pLeft = pNewNode;
    1621             :     }
    1622          21 :     return pLeft;
    1623             : }
    1624             : 
    1625          29 : SmNode* SmNodeListParser::Product(){
    1626             :     //Read a Factor
    1627          29 :     SmNode* pLeft = Factor();
    1628             :     //While we have tokens and the next is a product
    1629          58 :     while(Terminal() && IsProductOperator(Terminal()->GetToken())){
    1630             :         //Take the operator
    1631           0 :         SmNode* pOper = Take();
    1632             :         //Find the right side of the operation
    1633           0 :         SmNode* pRight = Factor();
    1634             :         //Create new SmBinHorNode
    1635           0 :         SmStructureNode* pNewNode = new SmBinHorNode(SmToken());
    1636           0 :         pNewNode->SetSubNodes(pLeft, pOper, pRight);
    1637           0 :         pLeft = pNewNode;
    1638             :     }
    1639          29 :     return pLeft;
    1640             : }
    1641             : 
    1642          31 : SmNode* SmNodeListParser::Factor(){
    1643             :     //Read unary operations
    1644          31 :     if(!Terminal())
    1645           4 :         return Error();
    1646             :     //Take care of unary operators
    1647          27 :     else if(IsUnaryOperator(Terminal()->GetToken()))
    1648             :     {
    1649           2 :         SmStructureNode *pUnary = new SmUnHorNode(SmToken());
    1650           2 :         SmNode *pOper = Terminal(),
    1651             :                *pArg;
    1652             : 
    1653           2 :         if(Next())
    1654           2 :             pArg = Factor();
    1655             :         else
    1656           0 :             pArg = Error();
    1657             : 
    1658           2 :         pUnary->SetSubNodes(pOper, pArg);
    1659           2 :         return pUnary;
    1660             :     }
    1661          25 :     return Postfix();
    1662             : }
    1663             : 
    1664          25 : SmNode* SmNodeListParser::Postfix(){
    1665          25 :     if(!Terminal())
    1666           0 :         return Error();
    1667          25 :     SmNode *pArg = NULL;
    1668          25 :     if(IsPostfixOperator(Terminal()->GetToken()))
    1669           0 :         pArg = Error();
    1670          25 :     else if(IsOperator(Terminal()->GetToken()))
    1671           0 :         return Error();
    1672             :     else
    1673          25 :         pArg = Take();
    1674          50 :     while(Terminal() && IsPostfixOperator(Terminal()->GetToken())) {
    1675           0 :         SmStructureNode *pUnary = new SmUnHorNode(SmToken());
    1676           0 :         SmNode *pOper = Take();
    1677           0 :         pUnary->SetSubNodes(pArg, pOper);
    1678           0 :         pArg = pUnary;
    1679             :     }
    1680          25 :     return pArg;
    1681             : }
    1682             : 
    1683           4 : SmNode* SmNodeListParser::Error(){
    1684           4 :     return new SmErrorNode(PE_UNEXPECTED_TOKEN, SmToken());
    1685             : }
    1686             : 
    1687          27 : bool SmNodeListParser::IsOperator(const SmToken &token) {
    1688          54 :     return  IsRelationOperator(token) ||
    1689          54 :             IsSumOperator(token) ||
    1690          54 :             IsProductOperator(token) ||
    1691          81 :             IsUnaryOperator(token) ||
    1692          54 :             IsPostfixOperator(token);
    1693             : }
    1694             : 
    1695          30 : bool SmNodeListParser::IsRelationOperator(const SmToken &token) {
    1696          30 :     return token.nGroup & TGRELATION;
    1697             : }
    1698             : 
    1699          38 : bool SmNodeListParser::IsSumOperator(const SmToken &token) {
    1700          38 :     return token.nGroup & TGSUM;
    1701             : }
    1702             : 
    1703          38 : bool SmNodeListParser::IsProductOperator(const SmToken &token) {
    1704          46 :     return token.nGroup & TGPRODUCT &&
    1705          16 :            token.eType != TWIDESLASH &&
    1706          16 :            token.eType != TWIDEBACKSLASH &&
    1707          16 :            token.eType != TUNDERBRACE &&
    1708          54 :            token.eType != TOVERBRACE &&
    1709          46 :            token.eType != TOVER;
    1710             : }
    1711             : 
    1712          54 : bool SmNodeListParser::IsUnaryOperator(const SmToken &token) {
    1713          58 :     return  token.nGroup & TGUNOPER &&
    1714           4 :             (token.eType == TPLUS ||
    1715           2 :              token.eType == TMINUS ||
    1716           0 :              token.eType == TPLUSMINUS ||
    1717           0 :              token.eType == TMINUSPLUS ||
    1718           0 :              token.eType == TNEG ||
    1719          54 :              token.eType == TUOPER);
    1720             : }
    1721             : 
    1722          63 : bool SmNodeListParser::IsPostfixOperator(const SmToken &token) {
    1723          63 :     return token.eType == TFACT;
    1724          27 : }
    1725             : 
    1726             : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.10