Branch libreoffice-6-3
[LibreOffice.git] / starmath / source / cursor.cxx
blob538a4f26bf946cda141798e35679b43d7324569c
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
9 #include <memory>
10 #include <cursor.hxx>
11 #include <visitors.hxx>
12 #include <document.hxx>
13 #include <view.hxx>
14 #include <comphelper/string.hxx>
15 #include <editeng/editeng.hxx>
16 #include <osl/diagnose.h>
17 #include <cassert>
19 void SmCursor::Move(OutputDevice* pDev, SmMovementDirection direction, bool bMoveAnchor){
20 SmCaretPosGraphEntry* NewPos = nullptr;
21 switch(direction)
23 case MoveLeft:
24 if (mpPosition)
25 NewPos = mpPosition->Left;
26 OSL_ENSURE(NewPos, "NewPos shouldn't be NULL here!");
27 break;
28 case MoveRight:
29 if (mpPosition)
30 NewPos = mpPosition->Right;
31 OSL_ENSURE(NewPos, "NewPos shouldn't be NULL here!");
32 break;
33 case MoveUp:
34 //Implementation is practically identical to MoveDown, except for a single if statement
35 //so I've implemented them together and added a direction == MoveDown to the if statements.
36 case MoveDown:
37 if (mpPosition)
39 SmCaretLine from_line = SmCaretPos2LineVisitor(pDev, mpPosition->CaretPos).GetResult(),
40 best_line, //Best approximated line found so far
41 curr_line; //Current line
42 long dbp_sq = 0; //Distance squared to best line
43 for(auto &pEntry : *mpGraph)
45 //Reject it if it's the current position
46 if(pEntry->CaretPos == mpPosition->CaretPos) continue;
47 //Compute caret line
48 curr_line = SmCaretPos2LineVisitor(pDev, pEntry->CaretPos).GetResult();
49 //Reject anything above if we're moving down
50 if(curr_line.GetTop() <= from_line.GetTop() && direction == MoveDown) continue;
51 //Reject anything below if we're moving up
52 if(curr_line.GetTop() + curr_line.GetHeight() >= from_line.GetTop() + from_line.GetHeight()
53 && direction == MoveUp) continue;
54 //Compare if it to what we have, if we have anything yet
55 if(NewPos){
56 //Compute distance to current line squared, multiplied with a horizontal factor
57 long dp_sq = curr_line.SquaredDistanceX(from_line) * HORIZONTICAL_DISTANCE_FACTOR +
58 curr_line.SquaredDistanceY(from_line);
59 //Discard current line if best line is closer
60 if(dbp_sq <= dp_sq) continue;
62 //Take current line as the best
63 best_line = curr_line;
64 NewPos = pEntry.get();
65 //Update distance to best line
66 dbp_sq = best_line.SquaredDistanceX(from_line) * HORIZONTICAL_DISTANCE_FACTOR +
67 best_line.SquaredDistanceY(from_line);
70 break;
71 default:
72 assert(false);
74 if(NewPos){
75 mpPosition = NewPos;
76 if(bMoveAnchor)
77 mpAnchor = NewPos;
78 RequestRepaint();
82 void SmCursor::MoveTo(OutputDevice* pDev, const Point& pos, bool bMoveAnchor)
84 SmCaretPosGraphEntry* NewPos = nullptr;
85 long dp_sq = 0, //Distance to current line squared
86 dbp_sq = 1; //Distance to best line squared
87 for(auto &pEntry : *mpGraph)
89 OSL_ENSURE(pEntry->CaretPos.IsValid(), "The caret position graph may not have invalid positions!");
90 //Compute current line
91 SmCaretLine curr_line = SmCaretPos2LineVisitor(pDev, pEntry->CaretPos).GetResult();
92 //Compute squared distance to current line
93 dp_sq = curr_line.SquaredDistanceX(pos) + curr_line.SquaredDistanceY(pos);
94 //If we have a position compare to it
95 if(NewPos){
96 //If best line is closer, reject current line
97 if(dbp_sq <= dp_sq) continue;
99 //Accept current position as the best
100 NewPos = pEntry.get();
101 //Update distance to best line
102 dbp_sq = dp_sq;
104 if(NewPos){
105 mpPosition = NewPos;
106 if(bMoveAnchor)
107 mpAnchor = NewPos;
108 RequestRepaint();
112 void SmCursor::BuildGraph(){
113 //Save the current anchor and position
114 SmCaretPos _anchor, _position;
115 //Release mpGraph if allocated
116 if(mpGraph){
117 if(mpAnchor)
118 _anchor = mpAnchor->CaretPos;
119 if(mpPosition)
120 _position = mpPosition->CaretPos;
121 mpGraph.reset();
122 //Reset anchor and position as they point into an old graph
123 mpAnchor = nullptr;
124 mpPosition = nullptr;
127 //Build the new graph
128 mpGraph.reset(SmCaretPosGraphBuildingVisitor(mpTree).takeGraph());
130 //Restore anchor and position pointers
131 if(_anchor.IsValid() || _position.IsValid()){
132 for(auto &pEntry : *mpGraph)
134 if(_anchor == pEntry->CaretPos)
135 mpAnchor = pEntry.get();
136 if(_position == pEntry->CaretPos)
137 mpPosition = pEntry.get();
140 //Set position and anchor to first caret position
141 auto it = mpGraph->begin();
142 assert(it != mpGraph->end());
143 if(!mpPosition)
144 mpPosition = it->get();
145 if(!mpAnchor)
146 mpAnchor = mpPosition;
148 assert(mpPosition);
149 assert(mpAnchor);
150 OSL_ENSURE(mpPosition->CaretPos.IsValid(), "Position must be valid");
151 OSL_ENSURE(mpAnchor->CaretPos.IsValid(), "Anchor must be valid");
154 bool SmCursor::SetCaretPosition(SmCaretPos pos){
155 for(auto &pEntry : *mpGraph)
157 if(pEntry->CaretPos == pos)
159 mpPosition = pEntry.get();
160 mpAnchor = pEntry.get();
161 return true;
164 return false;
167 void SmCursor::AnnotateSelection(){
168 //TODO: Manage a state, reset it upon modification and optimize this call
169 SmSetSelectionVisitor(mpAnchor->CaretPos, mpPosition->CaretPos, mpTree);
172 void SmCursor::Draw(OutputDevice& pDev, Point Offset, bool isCaretVisible){
173 SmCaretDrawingVisitor(pDev, GetPosition(), Offset, isCaretVisible);
176 void SmCursor::DeletePrev(OutputDevice* pDev){
177 //Delete only a selection if there's a selection
178 if(HasSelection()){
179 Delete();
180 return;
183 SmNode* pLine = FindTopMostNodeInLine(mpPosition->CaretPos.pSelectedNode);
184 SmStructureNode* pLineParent = pLine->GetParent();
185 int nLineOffsetIdx = pLineParent->IndexOfSubNode(pLine);
186 assert(nLineOffsetIdx >= 0);
188 //If we're in front of a node who's parent is a TABLE
189 if (pLineParent->GetType() == SmNodeType::Table && mpPosition->CaretPos.nIndex == 0 && nLineOffsetIdx > 0)
191 size_t nLineOffset = nLineOffsetIdx;
192 //Now we can merge with nLineOffset - 1
193 BeginEdit();
194 //Line to merge things into, so we can delete pLine
195 SmNode* pMergeLine = pLineParent->GetSubNode(nLineOffset-1);
196 OSL_ENSURE(pMergeLine, "pMergeLine cannot be NULL!");
197 SmCaretPos PosAfterDelete;
198 //Convert first line to list
199 std::unique_ptr<SmNodeList> pLineList(new SmNodeList);
200 NodeToList(pMergeLine, *pLineList);
201 if(!pLineList->empty()){
202 //Find iterator to patch
203 SmNodeList::iterator patchPoint = pLineList->end();
204 --patchPoint;
205 //Convert second line to list
206 NodeToList(pLine, *pLineList);
207 //Patch the line list
208 ++patchPoint;
209 PosAfterDelete = PatchLineList(pLineList.get(), patchPoint);
210 //Parse the line
211 pLine = SmNodeListParser().Parse(pLineList.get());
213 pLineList.reset();
214 pLineParent->SetSubNode(nLineOffset-1, pLine);
215 //Delete the removed line slot
216 SmNodeArray lines(pLineParent->GetNumSubNodes()-1);
217 for (size_t i = 0; i < pLineParent->GetNumSubNodes(); ++i)
219 if(i < nLineOffset)
220 lines[i] = pLineParent->GetSubNode(i);
221 else if(i > nLineOffset)
222 lines[i-1] = pLineParent->GetSubNode(i);
224 pLineParent->SetSubNodes(std::move(lines));
225 //Rebuild graph
226 mpAnchor = nullptr;
227 mpPosition = nullptr;
228 BuildGraph();
229 AnnotateSelection();
230 //Set caret position
231 if(!SetCaretPosition(PosAfterDelete))
232 SetCaretPosition(SmCaretPos(pLine, 0));
233 //Finish editing
234 EndEdit();
236 //TODO: If we're in an empty (sub/super/*) script
237 /*}else if(pLineParent->GetType() == SmNodeType::SubSup &&
238 nLineOffset != 0 &&
239 pLine->GetType() == SmNodeType::Expression &&
240 pLine->GetNumSubNodes() == 0){
241 //There's a (sub/super) script we can delete
242 //Consider selecting the entire script if GetNumSubNodes() != 0 or pLine->GetType() != SmNodeType::Expression
243 //TODO: Handle case where we delete a limit
246 //Else move select, and delete if not complex
247 }else{
248 Move(pDev, MoveLeft, false);
249 if(!HasComplexSelection())
250 Delete();
254 void SmCursor::Delete(){
255 //Return if we don't have a selection to delete
256 if(!HasSelection())
257 return;
259 //Enter edit section
260 BeginEdit();
262 //Set selected on nodes
263 AnnotateSelection();
265 //Find an arbitrary selected node
266 SmNode* pSNode = FindSelectedNode(mpTree);
267 assert(pSNode);
269 //Find the topmost node of the line that holds the selection
270 SmNode* pLine = FindTopMostNodeInLine(pSNode, true);
271 OSL_ENSURE(pLine != mpTree, "Shouldn't be able to select the entire tree");
273 //Get the parent of the line
274 SmStructureNode* pLineParent = pLine->GetParent();
275 //Find line offset in parent
276 int nLineOffset = pLineParent->IndexOfSubNode(pLine);
277 assert(nLineOffset >= 0);
279 //Position after delete
280 SmCaretPos PosAfterDelete;
282 std::unique_ptr<SmNodeList> pLineList(new SmNodeList);
283 NodeToList(pLine, *pLineList);
285 //Take the selected nodes and delete them...
286 SmNodeList::iterator patchIt = TakeSelectedNodesFromList(pLineList.get());
288 //Get the position to set after delete
289 PosAfterDelete = PatchLineList(pLineList.get(), patchIt);
291 //Finish editing
292 FinishEdit(std::move(pLineList), pLineParent, nLineOffset, PosAfterDelete);
295 void SmCursor::InsertNodes(std::unique_ptr<SmNodeList> pNewNodes){
296 if(pNewNodes->empty()){
297 return;
300 //Begin edit section
301 BeginEdit();
303 //Get the current position
304 const SmCaretPos pos = mpPosition->CaretPos;
306 //Find top most of line that holds position
307 SmNode* pLine = FindTopMostNodeInLine(pos.pSelectedNode);
309 //Find line parent and line index in parent
310 SmStructureNode* pLineParent = pLine->GetParent();
311 int nParentIndex = pLineParent->IndexOfSubNode(pLine);
312 assert(nParentIndex >= 0);
314 //Convert line to list
315 std::unique_ptr<SmNodeList> pLineList(new SmNodeList);
316 NodeToList(pLine, *pLineList);
318 //Find iterator for place to insert nodes
319 SmNodeList::iterator it = FindPositionInLineList(pLineList.get(), pos);
321 //Insert all new nodes
322 SmNodeList::iterator newIt,
323 patchIt = it, // (pointless default value, fixes compiler warnings)
324 insIt;
325 for(newIt = pNewNodes->begin(); newIt != pNewNodes->end(); ++newIt){
326 insIt = pLineList->insert(it, *newIt);
327 if(newIt == pNewNodes->begin())
328 patchIt = insIt;
330 //Patch the places we've changed stuff
331 PatchLineList(pLineList.get(), patchIt);
332 SmCaretPos PosAfterInsert = PatchLineList(pLineList.get(), it);
333 //Release list, we've taken the nodes
334 pNewNodes.reset();
336 //Finish editing
337 FinishEdit(std::move(pLineList), pLineParent, nParentIndex, PosAfterInsert);
340 SmNodeList::iterator SmCursor::FindPositionInLineList(SmNodeList* pLineList,
341 const SmCaretPos& rCaretPos)
343 //Find iterator for position
344 SmNodeList::iterator it = std::find(pLineList->begin(), pLineList->end(), rCaretPos.pSelectedNode);
345 if (it != pLineList->end())
347 if((*it)->GetType() == SmNodeType::Text)
349 //Split textnode if needed
350 if(rCaretPos.nIndex > 0)
352 SmTextNode* pText = static_cast<SmTextNode*>(rCaretPos.pSelectedNode);
353 if (rCaretPos.nIndex == pText->GetText().getLength())
354 return ++it;
355 OUString str1 = pText->GetText().copy(0, rCaretPos.nIndex);
356 OUString str2 = pText->GetText().copy(rCaretPos.nIndex);
357 pText->ChangeText(str1);
358 ++it;
359 //Insert str2 as new text node
360 assert(!str2.isEmpty());
361 SmTextNode* pNewText = new SmTextNode(pText->GetToken(), pText->GetFontDesc());
362 pNewText->ChangeText(str2);
363 it = pLineList->insert(it, pNewText);
365 }else
366 ++it;
367 //it now pointer to the node following pos, so pLineList->insert(it, ...) will insert correctly
368 return it;
370 //If we didn't find pSelectedNode, it must be because the caret is in front of the line
371 return pLineList->begin();
374 SmCaretPos SmCursor::PatchLineList(SmNodeList* pLineList, SmNodeList::iterator aIter) {
375 //The nodes we should consider merging
376 SmNode *prev = nullptr,
377 *next = nullptr;
378 if(aIter != pLineList->end())
379 next = *aIter;
380 if(aIter != pLineList->begin()) {
381 --aIter;
382 prev = *aIter;
383 ++aIter;
386 //Check if there's textnodes to merge
387 if( prev &&
388 next &&
389 prev->GetType() == SmNodeType::Text &&
390 next->GetType() == SmNodeType::Text &&
391 ( prev->GetToken().eType != TNUMBER ||
392 next->GetToken().eType == TNUMBER) ){
393 SmTextNode *pText = static_cast<SmTextNode*>(prev),
394 *pOldN = static_cast<SmTextNode*>(next);
395 SmCaretPos retval(pText, pText->GetText().getLength());
396 OUString newText;
397 newText += pText->GetText();
398 newText += pOldN->GetText();
399 pText->ChangeText(newText);
400 delete pOldN;
401 pLineList->erase(aIter);
402 return retval;
405 //Check if there's a SmPlaceNode to remove:
406 if(prev && next && prev->GetType() == SmNodeType::Place && !SmNodeListParser::IsOperator(next->GetToken())){
407 --aIter;
408 aIter = pLineList->erase(aIter);
409 delete prev;
410 //Return caret pos in front of aIter
411 if(aIter != pLineList->begin())
412 --aIter; //Thus find node before aIter
413 if(aIter == pLineList->begin())
414 return SmCaretPos();
415 return SmCaretPos::GetPosAfter(*aIter);
417 if(prev && next && next->GetType() == SmNodeType::Place && !SmNodeListParser::IsOperator(prev->GetToken())){
418 aIter = pLineList->erase(aIter);
419 delete next;
420 return SmCaretPos::GetPosAfter(prev);
423 //If we didn't do anything return
424 if(!prev) //return an invalid to indicate we're in front of line
425 return SmCaretPos();
426 return SmCaretPos::GetPosAfter(prev);
429 SmNodeList::iterator SmCursor::TakeSelectedNodesFromList(SmNodeList *pLineList,
430 SmNodeList *pSelectedNodes) {
431 SmNodeList::iterator retval;
432 SmNodeList::iterator it = pLineList->begin();
433 while(it != pLineList->end()){
434 if((*it)->IsSelected()){
435 //Split text nodes
436 if((*it)->GetType() == SmNodeType::Text) {
437 SmTextNode* pText = static_cast<SmTextNode*>(*it);
438 OUString aText = pText->GetText();
439 //Start and lengths of the segments, 2 is the selected segment
440 int start2 = pText->GetSelectionStart(),
441 start3 = pText->GetSelectionEnd(),
442 len1 = start2 - 0,
443 len2 = start3 - start2,
444 len3 = aText.getLength() - start3;
445 SmToken aToken = pText->GetToken();
446 sal_uInt16 eFontDesc = pText->GetFontDesc();
447 //If we need make segment 1
448 if(len1 > 0) {
449 OUString str = aText.copy(0, len1);
450 pText->ChangeText(str);
451 ++it;
452 } else {//Remove it if not needed
453 it = pLineList->erase(it);
454 delete pText;
456 //Set retval to be right after the selection
457 retval = it;
458 //if we need make segment 3
459 if(len3 > 0) {
460 OUString str = aText.copy(start3, len3);
461 SmTextNode* pSeg3 = new SmTextNode(aToken, eFontDesc);
462 pSeg3->ChangeText(str);
463 retval = pLineList->insert(it, pSeg3);
465 //If we need to save the selected text
466 if(pSelectedNodes && len2 > 0) {
467 OUString str = aText.copy(start2, len2);
468 SmTextNode* pSeg2 = new SmTextNode(aToken, eFontDesc);
469 pSeg2->ChangeText(str);
470 pSelectedNodes->push_back(pSeg2);
472 } else { //if it's not textnode
473 SmNode* pNode = *it;
474 retval = it = pLineList->erase(it);
475 if(pSelectedNodes)
476 pSelectedNodes->push_back(pNode);
477 else
478 delete pNode;
480 } else
481 ++it;
483 return retval;
486 void SmCursor::InsertSubSup(SmSubSup eSubSup) {
487 AnnotateSelection();
489 //Find line
490 SmNode *pLine;
491 if(HasSelection()) {
492 SmNode *pSNode = FindSelectedNode(mpTree);
493 assert(pSNode);
494 pLine = FindTopMostNodeInLine(pSNode, true);
495 } else
496 pLine = FindTopMostNodeInLine(mpPosition->CaretPos.pSelectedNode);
498 //Find Parent and offset in parent
499 SmStructureNode *pLineParent = pLine->GetParent();
500 int nParentIndex = pLineParent->IndexOfSubNode(pLine);
501 assert(nParentIndex >= 0);
503 //TODO: Consider handling special cases where parent is an SmOperNode,
504 // Maybe this method should be able to add limits to an SmOperNode...
506 //We begin modifying the tree here
507 BeginEdit();
509 //Convert line to list
510 std::unique_ptr<SmNodeList> pLineList(new SmNodeList);
511 NodeToList(pLine, *pLineList);
513 //Take the selection, and/or find iterator for current position
514 std::unique_ptr<SmNodeList> pSelectedNodesList(new SmNodeList);
515 SmNodeList::iterator it;
516 if(HasSelection())
517 it = TakeSelectedNodesFromList(pLineList.get(), pSelectedNodesList.get());
518 else
519 it = FindPositionInLineList(pLineList.get(), mpPosition->CaretPos);
521 //Find node that this should be applied to
522 SmNode* pSubject;
523 bool bPatchLine = !pSelectedNodesList->empty(); //If the line should be patched later
524 if(it != pLineList->begin()) {
525 --it;
526 pSubject = *it;
527 ++it;
528 } else {
529 //Create a new place node
530 pSubject = new SmPlaceNode();
531 pSubject->Prepare(mpDocShell->GetFormat(), *mpDocShell, 0);
532 it = pLineList->insert(it, pSubject);
533 ++it;
534 bPatchLine = true; //We've modified the line it should be patched later.
537 //Wrap the subject in a SmSubSupNode
538 SmSubSupNode* pSubSup;
539 if(pSubject->GetType() != SmNodeType::SubSup){
540 SmToken token;
541 token.nGroup = TG::Power;
542 pSubSup = new SmSubSupNode(token);
543 pSubSup->SetBody(pSubject);
544 *(--it) = pSubSup;
545 ++it;
546 }else
547 pSubSup = static_cast<SmSubSupNode*>(pSubject);
548 //pSubject shouldn't be referenced anymore, pSubSup is the SmSubSupNode in pLineList we wish to edit.
549 //and it pointer to the element following pSubSup in pLineList.
550 pSubject = nullptr;
552 //Patch the line if we noted that was needed previously
553 if(bPatchLine)
554 PatchLineList(pLineList.get(), it);
556 //Convert existing, if any, sub-/superscript line to list
557 SmNode *pScriptLine = pSubSup->GetSubSup(eSubSup);
558 std::unique_ptr<SmNodeList> pScriptLineList(new SmNodeList);
559 NodeToList(pScriptLine, *pScriptLineList);
561 //Add selection to pScriptLineList
562 unsigned int nOldSize = pScriptLineList->size();
563 pScriptLineList->insert(pScriptLineList->end(), pSelectedNodesList->begin(), pSelectedNodesList->end());
564 pSelectedNodesList.reset();
566 //Patch pScriptLineList if needed
567 if(0 < nOldSize && nOldSize < pScriptLineList->size()) {
568 SmNodeList::iterator iPatchPoint = pScriptLineList->begin();
569 std::advance(iPatchPoint, nOldSize);
570 PatchLineList(pScriptLineList.get(), iPatchPoint);
573 //Find caret pos, that should be used after sub-/superscription.
574 SmCaretPos PosAfterScript; //Leave invalid for first position
575 if (!pScriptLineList->empty())
576 PosAfterScript = SmCaretPos::GetPosAfter(pScriptLineList->back());
578 //Parse pScriptLineList
579 pScriptLine = SmNodeListParser().Parse(pScriptLineList.get());
580 pScriptLineList.reset();
582 //Insert pScriptLine back into the tree
583 pSubSup->SetSubSup(eSubSup, pScriptLine);
585 //Finish editing
586 FinishEdit(std::move(pLineList), pLineParent, nParentIndex, PosAfterScript, pScriptLine);
589 void SmCursor::InsertBrackets(SmBracketType eBracketType) {
590 BeginEdit();
592 AnnotateSelection();
594 //Find line
595 SmNode *pLine;
596 if(HasSelection()) {
597 SmNode *pSNode = FindSelectedNode(mpTree);
598 assert(pSNode);
599 pLine = FindTopMostNodeInLine(pSNode, true);
600 } else
601 pLine = FindTopMostNodeInLine(mpPosition->CaretPos.pSelectedNode);
603 //Find parent and offset in parent
604 SmStructureNode *pLineParent = pLine->GetParent();
605 int nParentIndex = pLineParent->IndexOfSubNode(pLine);
606 assert(nParentIndex >= 0);
608 //Convert line to list
609 std::unique_ptr<SmNodeList> pLineList(new SmNodeList);
610 NodeToList(pLine, *pLineList);
612 //Take the selection, and/or find iterator for current position
613 std::unique_ptr<SmNodeList> pSelectedNodesList(new SmNodeList);
614 SmNodeList::iterator it;
615 if(HasSelection())
616 it = TakeSelectedNodesFromList(pLineList.get(), pSelectedNodesList.get());
617 else
618 it = FindPositionInLineList(pLineList.get(), mpPosition->CaretPos);
620 //If there's no selected nodes, create a place node
621 std::unique_ptr<SmNode> pBodyNode;
622 SmCaretPos PosAfterInsert;
623 if(pSelectedNodesList->empty()) {
624 pBodyNode.reset(new SmPlaceNode());
625 PosAfterInsert = SmCaretPos(pBodyNode.get(), 1);
626 } else
627 pBodyNode.reset(SmNodeListParser().Parse(pSelectedNodesList.get()));
629 pSelectedNodesList.reset();
631 //Create SmBraceNode
632 SmToken aTok(TLEFT, '\0', "left", TG::NONE, 5);
633 SmBraceNode *pBrace = new SmBraceNode(aTok);
634 pBrace->SetScaleMode(SmScaleMode::Height);
635 std::unique_ptr<SmNode> pLeft( CreateBracket(eBracketType, true) ),
636 pRight( CreateBracket(eBracketType, false) );
637 std::unique_ptr<SmBracebodyNode> pBody(new SmBracebodyNode(SmToken()));
638 pBody->SetSubNodes(std::move(pBodyNode), nullptr);
639 pBrace->SetSubNodes(std::move(pLeft), std::move(pBody), std::move(pRight));
640 pBrace->Prepare(mpDocShell->GetFormat(), *mpDocShell, 0);
642 //Insert into line
643 pLineList->insert(it, pBrace);
644 //Patch line (I think this is good enough)
645 SmCaretPos aAfter = PatchLineList(pLineList.get(), it);
646 if( !PosAfterInsert.IsValid() )
647 PosAfterInsert = aAfter;
649 //Finish editing
650 FinishEdit(std::move(pLineList), pLineParent, nParentIndex, PosAfterInsert);
653 SmNode *SmCursor::CreateBracket(SmBracketType eBracketType, bool bIsLeft) {
654 SmToken aTok;
655 if(bIsLeft){
656 switch(eBracketType){
657 case SmBracketType::Round:
658 aTok = SmToken(TLPARENT, MS_LPARENT, "(", TG::LBrace, 5);
659 break;
660 case SmBracketType::Square:
661 aTok = SmToken(TLBRACKET, MS_LBRACKET, "[", TG::LBrace, 5);
662 break;
663 case SmBracketType::Curly:
664 aTok = SmToken(TLBRACE, MS_LBRACE, "lbrace", TG::LBrace, 5);
665 break;
667 } else {
668 switch(eBracketType) {
669 case SmBracketType::Round:
670 aTok = SmToken(TRPARENT, MS_RPARENT, ")", TG::RBrace, 5);
671 break;
672 case SmBracketType::Square:
673 aTok = SmToken(TRBRACKET, MS_RBRACKET, "]", TG::RBrace, 5);
674 break;
675 case SmBracketType::Curly:
676 aTok = SmToken(TRBRACE, MS_RBRACE, "rbrace", TG::RBrace, 5);
677 break;
680 SmNode* pRetVal = new SmMathSymbolNode(aTok);
681 pRetVal->SetScaleMode(SmScaleMode::Height);
682 return pRetVal;
685 bool SmCursor::InsertRow() {
686 AnnotateSelection();
688 //Find line
689 SmNode *pLine;
690 if(HasSelection()) {
691 SmNode *pSNode = FindSelectedNode(mpTree);
692 assert(pSNode);
693 pLine = FindTopMostNodeInLine(pSNode, true);
694 } else
695 pLine = FindTopMostNodeInLine(mpPosition->CaretPos.pSelectedNode);
697 //Find parent and offset in parent
698 SmStructureNode *pLineParent = pLine->GetParent();
699 int nParentIndex = pLineParent->IndexOfSubNode(pLine);
700 assert(nParentIndex >= 0);
702 //Discover the context of this command
703 SmTableNode *pTable = nullptr;
704 SmMatrixNode *pMatrix = nullptr;
705 int nTableIndex = nParentIndex;
706 if(pLineParent->GetType() == SmNodeType::Table)
707 pTable = static_cast<SmTableNode*>(pLineParent);
708 //If it's wrapped in a SmLineNode, we can still insert a newline
709 else if(pLineParent->GetType() == SmNodeType::Line &&
710 pLineParent->GetParent() &&
711 pLineParent->GetParent()->GetType() == SmNodeType::Table) {
712 //NOTE: This hack might give problems if we stop ignoring SmAlignNode
713 pTable = static_cast<SmTableNode*>(pLineParent->GetParent());
714 nTableIndex = pTable->IndexOfSubNode(pLineParent);
715 assert(nTableIndex >= 0);
717 if(pLineParent->GetType() == SmNodeType::Matrix)
718 pMatrix = static_cast<SmMatrixNode*>(pLineParent);
720 //If we're not in a context that supports InsertRow, return sal_False
721 if(!pTable && !pMatrix)
722 return false;
724 //Now we start editing
725 BeginEdit();
727 //Convert line to list
728 std::unique_ptr<SmNodeList> pLineList(new SmNodeList);
729 NodeToList(pLine, *pLineList);
731 //Find position in line
732 SmNodeList::iterator it;
733 if(HasSelection()) {
734 //Take the selected nodes and delete them...
735 it = TakeSelectedNodesFromList(pLineList.get());
736 } else
737 it = FindPositionInLineList(pLineList.get(), mpPosition->CaretPos);
739 //New caret position after inserting the newline/row in whatever context
740 SmCaretPos PosAfterInsert;
742 //If we're in the context of a table
743 if(pTable) {
744 std::unique_ptr<SmNodeList> pNewLineList(new SmNodeList);
745 //Move elements from pLineList to pNewLineList
746 pNewLineList->splice(pNewLineList->begin(), *pLineList, it, pLineList->end());
747 //Make sure it is valid again
748 it = pLineList->end();
749 if(it != pLineList->begin())
750 --it;
751 if(pNewLineList->empty())
752 pNewLineList->push_front(new SmPlaceNode());
753 //Parse new line
754 std::unique_ptr<SmNode> pNewLine(SmNodeListParser().Parse(pNewLineList.get()));
755 pNewLineList.reset();
756 //Wrap pNewLine in SmLineNode if needed
757 if(pLineParent->GetType() == SmNodeType::Line) {
758 std::unique_ptr<SmLineNode> pNewLineNode(new SmLineNode(SmToken(TNEWLINE, '\0', "newline")));
759 pNewLineNode->SetSubNodes(std::move(pNewLine), nullptr);
760 pNewLine = std::move(pNewLineNode);
762 //Get position
763 PosAfterInsert = SmCaretPos(pNewLine.get(), 0);
764 //Move other nodes if needed
765 for( int i = pTable->GetNumSubNodes(); i > nTableIndex + 1; i--)
766 pTable->SetSubNode(i, pTable->GetSubNode(i-1));
768 //Insert new line
769 pTable->SetSubNode(nTableIndex + 1, pNewLine.get());
771 //Check if we need to change token type:
772 if(pTable->GetNumSubNodes() > 2 && pTable->GetToken().eType == TBINOM) {
773 SmToken tok = pTable->GetToken();
774 tok.eType = TSTACK;
775 pTable->SetToken(tok);
778 //If we're in the context of a matrix
779 else {
780 //Find position after insert and patch the list
781 PosAfterInsert = PatchLineList(pLineList.get(), it);
782 //Move other children
783 sal_uInt16 rows = pMatrix->GetNumRows();
784 sal_uInt16 cols = pMatrix->GetNumCols();
785 int nRowStart = (nParentIndex - nParentIndex % cols) + cols;
786 for( int i = pMatrix->GetNumSubNodes() + cols - 1; i >= nRowStart + cols; i--)
787 pMatrix->SetSubNode(i, pMatrix->GetSubNode(i - cols));
788 for( int i = nRowStart; i < nRowStart + cols; i++) {
789 SmPlaceNode *pNewLine = new SmPlaceNode();
790 if(i == nParentIndex + cols)
791 PosAfterInsert = SmCaretPos(pNewLine, 0);
792 pMatrix->SetSubNode(i, pNewLine);
794 pMatrix->SetRowCol(rows + 1, cols);
797 //Finish editing
798 FinishEdit(std::move(pLineList), pLineParent, nParentIndex, PosAfterInsert);
799 //FinishEdit is actually used to handle situations where parent is an instance of
800 //SmSubSupNode. In this case parent should always be a table or matrix, however, for
801 //code reuse we just use FinishEdit() here too.
802 return true;
805 void SmCursor::InsertFraction() {
806 AnnotateSelection();
808 //Find line
809 SmNode *pLine;
810 if(HasSelection()) {
811 SmNode *pSNode = FindSelectedNode(mpTree);
812 assert(pSNode);
813 pLine = FindTopMostNodeInLine(pSNode, true);
814 } else
815 pLine = FindTopMostNodeInLine(mpPosition->CaretPos.pSelectedNode);
817 //Find Parent and offset in parent
818 SmStructureNode *pLineParent = pLine->GetParent();
819 int nParentIndex = pLineParent->IndexOfSubNode(pLine);
820 assert(nParentIndex >= 0);
822 //We begin modifying the tree here
823 BeginEdit();
825 //Convert line to list
826 std::unique_ptr<SmNodeList> pLineList(new SmNodeList);
827 NodeToList(pLine, *pLineList);
829 //Take the selection, and/or find iterator for current position
830 std::unique_ptr<SmNodeList> pSelectedNodesList(new SmNodeList);
831 SmNodeList::iterator it;
832 if(HasSelection())
833 it = TakeSelectedNodesFromList(pLineList.get(), pSelectedNodesList.get());
834 else
835 it = FindPositionInLineList(pLineList.get(), mpPosition->CaretPos);
837 //Create pNum, and pDenom
838 bool bEmptyFraction = pSelectedNodesList->empty();
839 std::unique_ptr<SmNode> pNum( bEmptyFraction
840 ? new SmPlaceNode()
841 : SmNodeListParser().Parse(pSelectedNodesList.get()) );
842 std::unique_ptr<SmNode> pDenom(new SmPlaceNode());
843 pSelectedNodesList.reset();
845 //Create new fraction
846 SmBinVerNode *pFrac = new SmBinVerNode(SmToken(TOVER, '\0', "over", TG::Product, 0));
847 std::unique_ptr<SmNode> pRect(new SmRectangleNode(SmToken()));
848 pFrac->SetSubNodes(std::move(pNum), std::move(pRect), std::move(pDenom));
850 //Insert in pLineList
851 SmNodeList::iterator patchIt = pLineList->insert(it, pFrac);
852 PatchLineList(pLineList.get(), patchIt);
853 PatchLineList(pLineList.get(), it);
855 //Finish editing
856 SmNode *pSelectedNode = bEmptyFraction ? pFrac->GetSubNode(0) : pFrac->GetSubNode(2);
857 FinishEdit(std::move(pLineList), pLineParent, nParentIndex, SmCaretPos(pSelectedNode, 1));
860 void SmCursor::InsertText(const OUString& aString)
862 BeginEdit();
864 Delete();
866 SmToken token;
867 token.eType = TIDENT;
868 token.cMathChar = '\0';
869 token.nGroup = TG::NONE;
870 token.nLevel = 5;
871 token.aText = aString;
873 SmTextNode* pText = new SmTextNode(token, FNT_VARIABLE);
874 pText->SetText(aString);
875 pText->AdjustFontDesc();
876 pText->Prepare(mpDocShell->GetFormat(), *mpDocShell, 0);
878 std::unique_ptr<SmNodeList> pList(new SmNodeList);
879 pList->push_front(pText);
880 InsertNodes(std::move(pList));
882 EndEdit();
885 void SmCursor::InsertElement(SmFormulaElement element){
886 BeginEdit();
888 Delete();
890 //Create new node
891 SmNode* pNewNode = nullptr;
892 switch(element){
893 case BlankElement:
895 SmToken token;
896 token.eType = TBLANK;
897 token.nGroup = TG::Blank;
898 token.aText = "~";
899 SmBlankNode* pBlankNode = new SmBlankNode(token);
900 pBlankNode->IncreaseBy(token);
901 pNewNode = pBlankNode;
902 }break;
903 case FactorialElement:
905 SmToken token(TFACT, MS_FACT, "fact", TG::UnOper, 5);
906 pNewNode = new SmMathSymbolNode(token);
907 }break;
908 case PlusElement:
910 SmToken token;
911 token.eType = TPLUS;
912 token.cMathChar = MS_PLUS;
913 token.nGroup = TG::UnOper | TG::Sum;
914 token.nLevel = 5;
915 token.aText = "+";
916 pNewNode = new SmMathSymbolNode(token);
917 }break;
918 case MinusElement:
920 SmToken token;
921 token.eType = TMINUS;
922 token.cMathChar = MS_MINUS;
923 token.nGroup = TG::UnOper | TG::Sum;
924 token.nLevel = 5;
925 token.aText = "-";
926 pNewNode = new SmMathSymbolNode(token);
927 }break;
928 case CDotElement:
930 SmToken token;
931 token.eType = TCDOT;
932 token.cMathChar = MS_CDOT;
933 token.nGroup = TG::Product;
934 token.aText = "cdot";
935 pNewNode = new SmMathSymbolNode(token);
936 }break;
937 case EqualElement:
939 SmToken token;
940 token.eType = TASSIGN;
941 token.cMathChar = MS_ASSIGN;
942 token.nGroup = TG::Relation;
943 token.aText = "=";
944 pNewNode = new SmMathSymbolNode(token);
945 }break;
946 case LessThanElement:
948 SmToken token;
949 token.eType = TLT;
950 token.cMathChar = MS_LT;
951 token.nGroup = TG::Relation;
952 token.aText = "<";
953 pNewNode = new SmMathSymbolNode(token);
954 }break;
955 case GreaterThanElement:
957 SmToken token;
958 token.eType = TGT;
959 token.cMathChar = MS_GT;
960 token.nGroup = TG::Relation;
961 token.aText = ">";
962 pNewNode = new SmMathSymbolNode(token);
963 }break;
964 case PercentElement:
966 SmToken token;
967 token.eType = TTEXT;
968 token.cMathChar = MS_PERCENT;
969 token.nGroup = TG::NONE;
970 token.aText = "\"%\"";
971 pNewNode = new SmMathSymbolNode(token);
972 }break;
974 assert(pNewNode);
976 //Prepare the new node
977 pNewNode->Prepare(mpDocShell->GetFormat(), *mpDocShell, 0);
979 //Insert new node
980 std::unique_ptr<SmNodeList> pList(new SmNodeList);
981 pList->push_front(pNewNode);
982 InsertNodes(std::move(pList));
984 EndEdit();
987 void SmCursor::InsertSpecial(const OUString& _aString)
989 BeginEdit();
990 Delete();
992 OUString aString = comphelper::string::strip(_aString, ' ');
994 //Create instance of special node
995 SmToken token;
996 token.eType = TSPECIAL;
997 token.cMathChar = '\0';
998 token.nGroup = TG::NONE;
999 token.nLevel = 5;
1000 token.aText = aString;
1001 SmSpecialNode* pSpecial = new SmSpecialNode(token);
1003 //Prepare the special node
1004 pSpecial->Prepare(mpDocShell->GetFormat(), *mpDocShell, 0);
1006 //Insert the node
1007 std::unique_ptr<SmNodeList> pList(new SmNodeList);
1008 pList->push_front(pSpecial);
1009 InsertNodes(std::move(pList));
1011 EndEdit();
1014 void SmCursor::InsertCommandText(const OUString& aCommandText) {
1015 //Parse the sub expression
1016 auto xSubExpr = SmParser().ParseExpression(aCommandText);
1018 //Prepare the subtree
1019 xSubExpr->Prepare(mpDocShell->GetFormat(), *mpDocShell, 0);
1021 //Convert subtree to list
1022 SmNode* pSubExpr = xSubExpr.release();
1023 std::unique_ptr<SmNodeList> pLineList(new SmNodeList);
1024 NodeToList(pSubExpr, *pLineList);
1026 BeginEdit();
1028 //Delete any selection
1029 Delete();
1031 //Insert it
1032 InsertNodes(std::move(pLineList));
1034 EndEdit();
1037 void SmCursor::Copy(){
1038 if(!HasSelection())
1039 return;
1041 AnnotateSelection();
1042 //Find selected node
1043 SmNode* pSNode = FindSelectedNode(mpTree);
1044 assert(pSNode);
1045 //Find visual line
1046 SmNode* pLine = FindTopMostNodeInLine(pSNode, true);
1047 assert(pLine);
1049 //Clone selected nodes
1050 SmClipboard aClipboard;
1051 if(IsLineCompositionNode(pLine))
1052 CloneLineToClipboard(static_cast<SmStructureNode*>(pLine), &aClipboard);
1053 else{
1054 //Special care to only clone selected text
1055 if(pLine->GetType() == SmNodeType::Text) {
1056 SmTextNode *pText = static_cast<SmTextNode*>(pLine);
1057 std::unique_ptr<SmTextNode> pClone(new SmTextNode( pText->GetToken(), pText->GetFontDesc() ));
1058 int start = pText->GetSelectionStart(),
1059 length = pText->GetSelectionEnd() - pText->GetSelectionStart();
1060 pClone->ChangeText(pText->GetText().copy(start, length));
1061 pClone->SetScaleMode(pText->GetScaleMode());
1062 aClipboard.push_front(std::move(pClone));
1063 } else {
1064 SmCloningVisitor aCloneFactory;
1065 aClipboard.push_front(std::unique_ptr<SmNode>(aCloneFactory.Clone(pLine)));
1069 //Set clipboard
1070 if (!aClipboard.empty())
1071 maClipboard = std::move(aClipboard);
1074 void SmCursor::Paste() {
1075 BeginEdit();
1076 Delete();
1078 if (!maClipboard.empty())
1079 InsertNodes(CloneList(maClipboard));
1081 EndEdit();
1084 std::unique_ptr<SmNodeList> SmCursor::CloneList(SmClipboard &rClipboard){
1085 SmCloningVisitor aCloneFactory;
1086 std::unique_ptr<SmNodeList> pClones(new SmNodeList);
1088 for(auto &xNode : rClipboard){
1089 SmNode *pClone = aCloneFactory.Clone(xNode.get());
1090 pClones->push_back(pClone);
1093 return pClones;
1096 SmNode* SmCursor::FindTopMostNodeInLine(SmNode* pSNode, bool MoveUpIfSelected){
1097 assert(pSNode);
1098 //Move up parent until we find a node who's
1099 //parent is NULL or isn't selected and not a type of:
1100 // SmExpressionNode
1101 // SmLineNode
1102 // SmBinHorNode
1103 // SmUnHorNode
1104 // SmAlignNode
1105 // SmFontNode
1106 while(pSNode->GetParent() &&
1107 ((MoveUpIfSelected &&
1108 pSNode->GetParent()->IsSelected()) ||
1109 IsLineCompositionNode(pSNode->GetParent())))
1110 pSNode = pSNode->GetParent();
1111 //Now we have the selection line node
1112 return pSNode;
1115 SmNode* SmCursor::FindSelectedNode(SmNode* pNode){
1116 if(pNode->GetNumSubNodes() == 0)
1117 return nullptr;
1118 for(auto pChild : *static_cast<SmStructureNode*>(pNode))
1120 if(!pChild)
1121 continue;
1122 if(pChild->IsSelected())
1123 return pChild;
1124 SmNode* pRetVal = FindSelectedNode(pChild);
1125 if(pRetVal)
1126 return pRetVal;
1128 return nullptr;
1131 void SmCursor::LineToList(SmStructureNode* pLine, SmNodeList& list){
1132 for(auto pChild : *pLine)
1134 if (!pChild)
1135 continue;
1136 switch(pChild->GetType()){
1137 case SmNodeType::Line:
1138 case SmNodeType::UnHor:
1139 case SmNodeType::Expression:
1140 case SmNodeType::BinHor:
1141 case SmNodeType::Align:
1142 case SmNodeType::Font:
1143 LineToList(static_cast<SmStructureNode*>(pChild), list);
1144 break;
1145 case SmNodeType::Error:
1146 delete pChild;
1147 break;
1148 default:
1149 list.push_back(pChild);
1152 pLine->ClearSubNodes();
1153 delete pLine;
1156 void SmCursor::CloneLineToClipboard(SmStructureNode* pLine, SmClipboard* pClipboard){
1157 SmCloningVisitor aCloneFactory;
1158 for(auto pChild : *pLine)
1160 if (!pChild)
1161 continue;
1162 if( IsLineCompositionNode( pChild ) )
1163 CloneLineToClipboard( static_cast<SmStructureNode*>(pChild), pClipboard );
1164 else if( pChild->IsSelected() && pChild->GetType() != SmNodeType::Error ) {
1165 //Only clone selected text from SmTextNode
1166 if(pChild->GetType() == SmNodeType::Text) {
1167 SmTextNode *pText = static_cast<SmTextNode*>(pChild);
1168 std::unique_ptr<SmTextNode> pClone(new SmTextNode( pChild->GetToken(), pText->GetFontDesc() ));
1169 int start = pText->GetSelectionStart(),
1170 length = pText->GetSelectionEnd() - pText->GetSelectionStart();
1171 pClone->ChangeText(pText->GetText().copy(start, length));
1172 pClone->SetScaleMode(pText->GetScaleMode());
1173 pClipboard->push_back(std::move(pClone));
1174 } else
1175 pClipboard->push_back(std::unique_ptr<SmNode>(aCloneFactory.Clone(pChild)));
1180 bool SmCursor::IsLineCompositionNode(SmNode const * pNode){
1181 switch(pNode->GetType()){
1182 case SmNodeType::Line:
1183 case SmNodeType::UnHor:
1184 case SmNodeType::Expression:
1185 case SmNodeType::BinHor:
1186 case SmNodeType::Align:
1187 case SmNodeType::Font:
1188 return true;
1189 default:
1190 return false;
1194 int SmCursor::CountSelectedNodes(SmNode* pNode){
1195 if(pNode->GetNumSubNodes() == 0)
1196 return 0;
1197 int nCount = 0;
1198 for(auto pChild : *static_cast<SmStructureNode*>(pNode))
1200 if (!pChild)
1201 continue;
1202 if(pChild->IsSelected() && !IsLineCompositionNode(pChild))
1203 nCount++;
1204 nCount += CountSelectedNodes(pChild);
1206 return nCount;
1209 bool SmCursor::HasComplexSelection(){
1210 if(!HasSelection())
1211 return false;
1212 AnnotateSelection();
1214 return CountSelectedNodes(mpTree) > 1;
1217 void SmCursor::FinishEdit(std::unique_ptr<SmNodeList> pLineList,
1218 SmStructureNode* pParent,
1219 int nParentIndex,
1220 SmCaretPos PosAfterEdit,
1221 SmNode* pStartLine) {
1222 //Store number of nodes in line for later
1223 int entries = pLineList->size();
1225 //Parse list of nodes to a tree
1226 SmNodeListParser parser;
1227 std::unique_ptr<SmNode> pLine(parser.Parse(pLineList.get()));
1228 pLineList.reset();
1230 //Check if we're making the body of a subsup node bigger than one
1231 if(pParent->GetType() == SmNodeType::SubSup &&
1232 nParentIndex == 0 &&
1233 entries > 1) {
1234 //Wrap pLine in scalable round brackets
1235 SmToken aTok(TLEFT, '\0', "left", TG::NONE, 5);
1236 std::unique_ptr<SmBraceNode> pBrace(new SmBraceNode(aTok));
1237 pBrace->SetScaleMode(SmScaleMode::Height);
1238 std::unique_ptr<SmNode> pLeft( CreateBracket(SmBracketType::Round, true) ),
1239 pRight( CreateBracket(SmBracketType::Round, false) );
1240 std::unique_ptr<SmBracebodyNode> pBody(new SmBracebodyNode(SmToken()));
1241 pBody->SetSubNodes(std::move(pLine), nullptr);
1242 pBrace->SetSubNodes(std::move(pLeft), std::move(pBody), std::move(pRight));
1243 pBrace->Prepare(mpDocShell->GetFormat(), *mpDocShell, 0);
1244 pLine = std::move(pBrace);
1245 //TODO: Consider the following alternative behavior:
1246 //Consider the line: A + {B + C}^D lsub E
1247 //Here pLineList is B, + and C and pParent is a subsup node with
1248 //both RSUP and LSUB set. Imagine the user just inserted "B +" in
1249 //the body of the subsup node...
1250 //The most natural thing to do would be to make the line like this:
1251 //A + B lsub E + C ^ D
1252 //E.g. apply LSUB and LSUP to the first element in pLineList and RSUP
1253 //and RSUB to the last element in pLineList. But how should this act
1254 //for CSUP and CSUB ???
1255 //For this reason and because brackets was faster to implement, this solution
1256 //have been chosen. It might be worth working on the other solution later...
1259 //Set pStartLine if NULL
1260 if(!pStartLine)
1261 pStartLine = pLine.get();
1263 //Insert it back into the parent
1264 pParent->SetSubNode(nParentIndex, pLine.release());
1266 //Rebuild graph of caret position
1267 mpAnchor = nullptr;
1268 mpPosition = nullptr;
1269 BuildGraph();
1270 AnnotateSelection(); //Update selection annotation!
1272 //Set caret position
1273 if(!SetCaretPosition(PosAfterEdit))
1274 SetCaretPosition(SmCaretPos(pStartLine, 0));
1276 //End edit section
1277 EndEdit();
1280 void SmCursor::BeginEdit(){
1281 if(mnEditSections++ > 0) return;
1283 mbIsEnabledSetModifiedSmDocShell = mpDocShell->IsEnableSetModified();
1284 if( mbIsEnabledSetModifiedSmDocShell )
1285 mpDocShell->EnableSetModified( false );
1288 void SmCursor::EndEdit(){
1289 if(--mnEditSections > 0) return;
1291 mpDocShell->SetFormulaArranged(false);
1292 //Okay, I don't know what this does... :)
1293 //It's used in SmDocShell::SetText and with places where everything is modified.
1294 //I think it does some magic, with sfx, but everything is totally undocumented so
1295 //it's kinda hard to tell...
1296 if ( mbIsEnabledSetModifiedSmDocShell )
1297 mpDocShell->EnableSetModified( mbIsEnabledSetModifiedSmDocShell );
1298 //I think this notifies people around us that we've modified this document...
1299 mpDocShell->SetModified();
1300 //I think SmDocShell uses this value when it sends an update graphics event
1301 //Anyway comments elsewhere suggests it need to be updated...
1302 mpDocShell->mnModifyCount++;
1304 //TODO: Consider copying the update accessibility code from SmDocShell::SetText in here...
1305 //This somehow updates the size of SmGraphicView if it is running in embedded mode
1306 if( mpDocShell->GetCreateMode() == SfxObjectCreateMode::EMBEDDED )
1307 mpDocShell->OnDocumentPrinterChanged(nullptr);
1309 //Request a repaint...
1310 RequestRepaint();
1312 //Update the edit engine and text of the document
1313 OUString formula;
1314 SmNodeToTextVisitor(mpTree, formula);
1315 //mpTree->CreateTextFromNode(formula);
1316 mpDocShell->maText = formula;
1317 mpDocShell->GetEditEngine().QuickInsertText( formula, ESelection( 0, 0, EE_PARA_ALL, EE_TEXTPOS_ALL ) );
1318 mpDocShell->GetEditEngine().QuickFormatDoc();
1321 void SmCursor::RequestRepaint(){
1322 SmViewShell *pViewSh = SmGetActiveView();
1323 if( pViewSh ) {
1324 if ( SfxObjectCreateMode::EMBEDDED == mpDocShell->GetCreateMode() )
1325 mpDocShell->Repaint();
1326 else
1327 pViewSh->GetGraphicWindow().Invalidate();
1331 bool SmCursor::IsAtTailOfBracket(SmBracketType eBracketType, SmBraceNode** ppBraceNode) const {
1332 const SmCaretPos pos = GetPosition();
1333 if (!pos.IsValid()) {
1334 return false;
1337 SmNode* pNode = pos.pSelectedNode;
1339 if (pNode->GetType() == SmNodeType::Text) {
1340 SmTextNode* pTextNode = static_cast<SmTextNode*>(pNode);
1341 if (pos.nIndex < pTextNode->GetText().getLength()) {
1342 // The cursor is on a text node and at the middle of it.
1343 return false;
1345 } else {
1346 if (pos.nIndex < 1) {
1347 return false;
1351 while (true) {
1352 SmStructureNode* pParentNode = pNode->GetParent();
1353 if (!pParentNode) {
1354 // There's no brace body node in the ancestors.
1355 return false;
1358 int index = pParentNode->IndexOfSubNode(pNode);
1359 assert(index >= 0);
1360 if (static_cast<size_t>(index + 1) != pParentNode->GetNumSubNodes()) {
1361 // The cursor is not at the tail at one of ancestor nodes.
1362 return false;
1365 pNode = pParentNode;
1366 if (pNode->GetType() == SmNodeType::Bracebody) {
1367 // Found the brace body node.
1368 break;
1372 SmStructureNode* pBraceNodeTmp = pNode->GetParent();
1373 if (!pBraceNodeTmp || pBraceNodeTmp->GetType() != SmNodeType::Brace) {
1374 // Brace node is invalid.
1375 return false;
1378 SmBraceNode* pBraceNode = static_cast<SmBraceNode*>(pBraceNodeTmp);
1379 SmMathSymbolNode* pClosingNode = pBraceNode->ClosingBrace();
1380 if (!pClosingNode) {
1381 // Couldn't get closing symbol node.
1382 return false;
1385 // Check if the closing brace matches eBracketType.
1386 SmTokenType eClosingTokenType = pClosingNode->GetToken().eType;
1387 switch (eBracketType) {
1388 case SmBracketType::Round: if (eClosingTokenType != TRPARENT) { return false; } break;
1389 case SmBracketType::Square: if (eClosingTokenType != TRBRACKET) { return false; } break;
1390 case SmBracketType::Curly: if (eClosingTokenType != TRBRACE) { return false; } break;
1391 default:
1392 return false;
1395 if (ppBraceNode) {
1396 *ppBraceNode = pBraceNode;
1399 return true;
1402 void SmCursor::MoveAfterBracket(SmBraceNode* pBraceNode)
1404 mpPosition->CaretPos.pSelectedNode = pBraceNode;
1405 mpPosition->CaretPos.nIndex = 1;
1406 mpAnchor->CaretPos.pSelectedNode = pBraceNode;
1407 mpAnchor->CaretPos.nIndex = 1;
1408 RequestRepaint();
1412 /////////////////////////////////////// SmNodeListParser
1414 SmNode* SmNodeListParser::Parse(SmNodeList* list){
1415 pList = list;
1416 //Delete error nodes
1417 SmNodeList::iterator it = pList->begin();
1418 while(it != pList->end()) {
1419 if((*it)->GetType() == SmNodeType::Error){
1420 //Delete and erase
1421 delete *it;
1422 it = pList->erase(it);
1423 }else
1424 ++it;
1426 SmNode* retval = Expression();
1427 pList = nullptr;
1428 return retval;
1431 SmNode* SmNodeListParser::Expression(){
1432 SmNodeArray NodeArray;
1433 //Accept as many relations as there is
1434 while(Terminal())
1435 NodeArray.push_back(Relation());
1437 //Create SmExpressionNode, I hope SmToken() will do :)
1438 SmStructureNode* pExpr = new SmExpressionNode(SmToken());
1439 pExpr->SetSubNodes(std::move(NodeArray));
1440 return pExpr;
1443 SmNode* SmNodeListParser::Relation(){
1444 //Read a sum
1445 std::unique_ptr<SmNode> pLeft(Sum());
1446 //While we have tokens and the next is a relation
1447 while(Terminal() && IsRelationOperator(Terminal()->GetToken())){
1448 //Take the operator
1449 std::unique_ptr<SmNode> pOper(Take());
1450 //Find the right side of the relation
1451 std::unique_ptr<SmNode> pRight(Sum());
1452 //Create new SmBinHorNode
1453 std::unique_ptr<SmStructureNode> pNewNode(new SmBinHorNode(SmToken()));
1454 pNewNode->SetSubNodes(std::move(pLeft), std::move(pOper), std::move(pRight));
1455 pLeft = std::move(pNewNode);
1457 return pLeft.release();
1460 SmNode* SmNodeListParser::Sum(){
1461 //Read a product
1462 std::unique_ptr<SmNode> pLeft(Product());
1463 //While we have tokens and the next is a sum
1464 while(Terminal() && IsSumOperator(Terminal()->GetToken())){
1465 //Take the operator
1466 std::unique_ptr<SmNode> pOper(Take());
1467 //Find the right side of the sum
1468 std::unique_ptr<SmNode> pRight(Product());
1469 //Create new SmBinHorNode
1470 std::unique_ptr<SmStructureNode> pNewNode(new SmBinHorNode(SmToken()));
1471 pNewNode->SetSubNodes(std::move(pLeft), std::move(pOper), std::move(pRight));
1472 pLeft = std::move(pNewNode);
1474 return pLeft.release();
1477 SmNode* SmNodeListParser::Product(){
1478 //Read a Factor
1479 std::unique_ptr<SmNode> pLeft(Factor());
1480 //While we have tokens and the next is a product
1481 while(Terminal() && IsProductOperator(Terminal()->GetToken())){
1482 //Take the operator
1483 std::unique_ptr<SmNode> pOper(Take());
1484 //Find the right side of the operation
1485 std::unique_ptr<SmNode> pRight(Factor());
1486 //Create new SmBinHorNode
1487 std::unique_ptr<SmStructureNode> pNewNode(new SmBinHorNode(SmToken()));
1488 pNewNode->SetSubNodes(std::move(pLeft), std::move(pOper), std::move(pRight));
1489 pLeft = std::move(pNewNode);
1491 return pLeft.release();
1494 SmNode* SmNodeListParser::Factor(){
1495 //Read unary operations
1496 if(!Terminal())
1497 return Error();
1498 //Take care of unary operators
1499 else if(IsUnaryOperator(Terminal()->GetToken()))
1501 SmStructureNode *pUnary = new SmUnHorNode(SmToken());
1502 std::unique_ptr<SmNode> pOper(Terminal()),
1503 pArg;
1505 if(Next())
1506 pArg.reset(Factor());
1507 else
1508 pArg.reset(Error());
1510 pUnary->SetSubNodes(std::move(pOper), std::move(pArg));
1511 return pUnary;
1513 return Postfix();
1516 SmNode* SmNodeListParser::Postfix(){
1517 if(!Terminal())
1518 return Error();
1519 std::unique_ptr<SmNode> pArg;
1520 if(IsPostfixOperator(Terminal()->GetToken()))
1521 pArg.reset(Error());
1522 else if(IsOperator(Terminal()->GetToken()))
1523 return Error();
1524 else
1525 pArg.reset(Take());
1526 while(Terminal() && IsPostfixOperator(Terminal()->GetToken())) {
1527 std::unique_ptr<SmStructureNode> pUnary(new SmUnHorNode(SmToken()) );
1528 std::unique_ptr<SmNode> pOper(Take());
1529 pUnary->SetSubNodes(std::move(pArg), std::move(pOper));
1530 pArg = std::move(pUnary);
1532 return pArg.release();
1535 SmNode* SmNodeListParser::Error(){
1536 return new SmErrorNode(SmToken());
1539 bool SmNodeListParser::IsOperator(const SmToken &token) {
1540 return IsRelationOperator(token) ||
1541 IsSumOperator(token) ||
1542 IsProductOperator(token) ||
1543 IsUnaryOperator(token) ||
1544 IsPostfixOperator(token);
1547 bool SmNodeListParser::IsRelationOperator(const SmToken &token) {
1548 return bool(token.nGroup & TG::Relation);
1551 bool SmNodeListParser::IsSumOperator(const SmToken &token) {
1552 return bool(token.nGroup & TG::Sum);
1555 bool SmNodeListParser::IsProductOperator(const SmToken &token) {
1556 return token.nGroup & TG::Product &&
1557 token.eType != TWIDESLASH &&
1558 token.eType != TWIDEBACKSLASH &&
1559 token.eType != TUNDERBRACE &&
1560 token.eType != TOVERBRACE &&
1561 token.eType != TOVER;
1564 bool SmNodeListParser::IsUnaryOperator(const SmToken &token) {
1565 return token.nGroup & TG::UnOper &&
1566 (token.eType == TPLUS ||
1567 token.eType == TMINUS ||
1568 token.eType == TPLUSMINUS ||
1569 token.eType == TMINUSPLUS ||
1570 token.eType == TNEG ||
1571 token.eType == TUOPER);
1574 bool SmNodeListParser::IsPostfixOperator(const SmToken &token) {
1575 return token.eType == TFACT;
1578 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */