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

Generated by: LCOV version 1.10