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

Generated by: LCOV version 1.11