Bump version to 21.06.18.1
[LibreOffice.git] / starmath / source / visitors.cxx
blobabae73e2e6f63ea51795775ddcdc3cb7aa0dde85
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 */
10 #include <rtl/math.hxx>
11 #include <sal/log.hxx>
12 #include <tools/gen.hxx>
13 #include <vcl/lineinfo.hxx>
14 #include <visitors.hxx>
15 #include "tmpdevice.hxx"
16 #include <cursor.hxx>
17 #include <cassert>
18 #include "mathtype.hxx"
19 #include <starmathdatabase.hxx>
21 // SmDefaultingVisitor
23 void SmDefaultingVisitor::Visit( SmTableNode* pNode )
25 DefaultVisit( pNode );
28 void SmDefaultingVisitor::Visit( SmBraceNode* pNode )
30 DefaultVisit( pNode );
33 void SmDefaultingVisitor::Visit( SmBracebodyNode* pNode )
35 DefaultVisit( pNode );
38 void SmDefaultingVisitor::Visit( SmOperNode* pNode )
40 DefaultVisit( pNode );
43 void SmDefaultingVisitor::Visit( SmAlignNode* pNode )
45 DefaultVisit( pNode );
48 void SmDefaultingVisitor::Visit( SmAttributNode* pNode )
50 DefaultVisit( pNode );
53 void SmDefaultingVisitor::Visit( SmFontNode* pNode )
55 DefaultVisit( pNode );
58 void SmDefaultingVisitor::Visit( SmUnHorNode* pNode )
60 DefaultVisit( pNode );
63 void SmDefaultingVisitor::Visit( SmBinHorNode* pNode )
65 DefaultVisit( pNode );
68 void SmDefaultingVisitor::Visit( SmBinVerNode* pNode )
70 DefaultVisit( pNode );
73 void SmDefaultingVisitor::Visit( SmBinDiagonalNode* pNode )
75 DefaultVisit( pNode );
78 void SmDefaultingVisitor::Visit( SmSubSupNode* pNode )
80 DefaultVisit( pNode );
83 void SmDefaultingVisitor::Visit( SmMatrixNode* pNode )
85 DefaultVisit( pNode );
88 void SmDefaultingVisitor::Visit( SmPlaceNode* pNode )
90 DefaultVisit( pNode );
93 void SmDefaultingVisitor::Visit( SmTextNode* pNode )
95 DefaultVisit( pNode );
98 void SmDefaultingVisitor::Visit( SmSpecialNode* pNode )
100 DefaultVisit( pNode );
103 void SmDefaultingVisitor::Visit( SmGlyphSpecialNode* pNode )
105 DefaultVisit( pNode );
108 void SmDefaultingVisitor::Visit( SmMathSymbolNode* pNode )
110 DefaultVisit( pNode );
113 void SmDefaultingVisitor::Visit( SmBlankNode* pNode )
115 DefaultVisit( pNode );
118 void SmDefaultingVisitor::Visit( SmErrorNode* pNode )
120 DefaultVisit( pNode );
123 void SmDefaultingVisitor::Visit( SmLineNode* pNode )
125 DefaultVisit( pNode );
128 void SmDefaultingVisitor::Visit( SmExpressionNode* pNode )
130 DefaultVisit( pNode );
133 void SmDefaultingVisitor::Visit( SmPolyLineNode* pNode )
135 DefaultVisit( pNode );
138 void SmDefaultingVisitor::Visit( SmRootNode* pNode )
140 DefaultVisit( pNode );
143 void SmDefaultingVisitor::Visit( SmRootSymbolNode* pNode )
145 DefaultVisit( pNode );
148 void SmDefaultingVisitor::Visit( SmRectangleNode* pNode )
150 DefaultVisit( pNode );
153 void SmDefaultingVisitor::Visit( SmVerticalBraceNode* pNode )
155 DefaultVisit( pNode );
158 // SmCaretDrawingVisitor
160 SmCaretDrawingVisitor::SmCaretDrawingVisitor( OutputDevice& rDevice,
161 SmCaretPos position,
162 Point offset,
163 bool caretVisible )
164 : mrDev( rDevice )
165 , maPos( position )
166 , maOffset( offset )
167 , mbCaretVisible( caretVisible )
169 SAL_WARN_IF( !position.IsValid(), "starmath", "Cannot draw invalid position!" );
170 if( !position.IsValid( ) )
171 return;
173 //Save device state
174 mrDev.Push( PushFlags::FONT | PushFlags::MAPMODE | PushFlags::LINECOLOR | PushFlags::FILLCOLOR | PushFlags::TEXTCOLOR );
176 maPos.pSelectedNode->Accept( this );
177 //Restore device state
178 mrDev.Pop( );
181 void SmCaretDrawingVisitor::Visit( SmTextNode* pNode )
183 tools::Long i = maPos.nIndex;
185 mrDev.SetFont( pNode->GetFont( ) );
187 //Find the line
188 SmNode* pLine = SmCursor::FindTopMostNodeInLine( pNode );
190 //Find coordinates
191 tools::Long left = pNode->GetLeft( ) + mrDev.GetTextWidth( pNode->GetText( ), 0, i ) + maOffset.X( );
192 tools::Long top = pLine->GetTop( ) + maOffset.Y( );
193 tools::Long height = pLine->GetHeight( );
194 tools::Long left_line = pLine->GetLeft( ) + maOffset.X( );
195 tools::Long right_line = pLine->GetRight( ) + maOffset.X( );
197 //Set color
198 mrDev.SetLineColor( COL_BLACK );
200 if ( mbCaretVisible ) {
201 //Draw vertical line
202 Point p1( left, top );
203 Point p2( left, top + height );
204 mrDev.DrawLine( p1, p2 );
207 //Underline the line
208 Point aLeft( left_line, top + height );
209 Point aRight( right_line, top + height );
210 mrDev.DrawLine( aLeft, aRight );
213 void SmCaretDrawingVisitor::DefaultVisit( SmNode* pNode )
215 //Find the line
216 SmNode* pLine = SmCursor::FindTopMostNodeInLine( pNode );
218 //Find coordinates
219 tools::Long left = pNode->GetLeft( ) + maOffset.X( ) + ( maPos.nIndex == 1 ? pNode->GetWidth( ) : 0 );
220 tools::Long top = pLine->GetTop( ) + maOffset.Y( );
221 tools::Long height = pLine->GetHeight( );
222 tools::Long left_line = pLine->GetLeft( ) + maOffset.X( );
223 tools::Long right_line = pLine->GetRight( ) + maOffset.X( );
225 //Set color
226 mrDev.SetLineColor( COL_BLACK );
228 if ( mbCaretVisible ) {
229 //Draw vertical line
230 Point p1( left, top );
231 Point p2( left, top + height );
232 mrDev.DrawLine( p1, p2 );
235 //Underline the line
236 Point aLeft( left_line, top + height );
237 Point aRight( right_line, top + height );
238 mrDev.DrawLine( aLeft, aRight );
241 // SmCaretPos2LineVisitor
243 void SmCaretPos2LineVisitor::Visit( SmTextNode* pNode )
245 //Save device state
246 mpDev->Push( PushFlags::FONT | PushFlags::TEXTCOLOR );
248 tools::Long i = maPos.nIndex;
250 mpDev->SetFont( pNode->GetFont( ) );
252 //Find coordinates
253 tools::Long left = pNode->GetLeft( ) + mpDev->GetTextWidth( pNode->GetText( ), 0, i );
254 tools::Long top = pNode->GetTop( );
255 tools::Long height = pNode->GetHeight( );
257 maLine = SmCaretLine( left, top, height );
259 //Restore device state
260 mpDev->Pop( );
263 void SmCaretPos2LineVisitor::DefaultVisit( SmNode* pNode )
265 //Vertical line ( code from SmCaretDrawingVisitor )
266 Point p1 = pNode->GetTopLeft( );
267 if( maPos.nIndex == 1 )
268 p1.Move( pNode->GetWidth( ), 0 );
270 maLine = SmCaretLine( p1.X( ), p1.Y( ), pNode->GetHeight( ) );
274 // SmDrawingVisitor
276 void SmDrawingVisitor::Visit( SmTableNode* pNode )
278 DrawChildren( pNode );
281 void SmDrawingVisitor::Visit( SmBraceNode* pNode )
283 DrawChildren( pNode );
286 void SmDrawingVisitor::Visit( SmBracebodyNode* pNode )
288 DrawChildren( pNode );
291 void SmDrawingVisitor::Visit( SmOperNode* pNode )
293 DrawChildren( pNode );
296 void SmDrawingVisitor::Visit( SmAlignNode* pNode )
298 DrawChildren( pNode );
301 void SmDrawingVisitor::Visit( SmAttributNode* pNode )
303 DrawChildren( pNode );
306 void SmDrawingVisitor::Visit( SmFontNode* pNode )
308 DrawChildren( pNode );
311 void SmDrawingVisitor::Visit( SmUnHorNode* pNode )
313 DrawChildren( pNode );
316 void SmDrawingVisitor::Visit( SmBinHorNode* pNode )
318 DrawChildren( pNode );
321 void SmDrawingVisitor::Visit( SmBinVerNode* pNode )
323 DrawChildren( pNode );
326 void SmDrawingVisitor::Visit( SmBinDiagonalNode* pNode )
328 DrawChildren( pNode );
331 void SmDrawingVisitor::Visit( SmSubSupNode* pNode )
333 DrawChildren( pNode );
336 void SmDrawingVisitor::Visit( SmMatrixNode* pNode )
338 DrawChildren( pNode );
341 void SmDrawingVisitor::Visit( SmPlaceNode* pNode )
343 DrawSpecialNode( pNode );
346 void SmDrawingVisitor::Visit( SmTextNode* pNode )
348 DrawTextNode( pNode );
351 void SmDrawingVisitor::Visit( SmSpecialNode* pNode )
353 DrawSpecialNode( pNode );
356 void SmDrawingVisitor::Visit( SmGlyphSpecialNode* pNode )
358 DrawSpecialNode( pNode );
361 void SmDrawingVisitor::Visit( SmMathSymbolNode* pNode )
363 DrawSpecialNode( pNode );
366 void SmDrawingVisitor::Visit( SmBlankNode* )
370 void SmDrawingVisitor::Visit( SmErrorNode* pNode )
372 DrawSpecialNode( pNode );
375 void SmDrawingVisitor::Visit( SmLineNode* pNode )
377 DrawChildren( pNode );
380 void SmDrawingVisitor::Visit( SmExpressionNode* pNode )
382 DrawChildren( pNode );
385 void SmDrawingVisitor::Visit( SmRootNode* pNode )
387 DrawChildren( pNode );
390 void SmDrawingVisitor::Visit( SmVerticalBraceNode* pNode )
392 DrawChildren( pNode );
395 void SmDrawingVisitor::Visit( SmRootSymbolNode* pNode )
397 if ( pNode->IsPhantom( ) )
398 return;
400 // draw root-sign itself
401 DrawSpecialNode( pNode );
403 SmTmpDevice aTmpDev( mrDev, true );
404 aTmpDev.SetFillColor( pNode->GetFont( ).GetColor( ) );
405 mrDev.SetLineColor( );
406 aTmpDev.SetFont( pNode->GetFont( ) );
408 // since the width is always unscaled it corresponds to the _original_
409 // _unscaled_ font height to be used, we use that to calculate the
410 // bar height. Thus it is independent of the arguments height.
411 // ( see display of sqrt QQQ versus sqrt stack{Q#Q#Q#Q} )
412 tools::Long nBarHeight = pNode->GetWidth( ) * 7 / 100;
413 tools::Long nBarWidth = pNode->GetBodyWidth( ) + pNode->GetBorderWidth( );
414 Point aBarOffset( pNode->GetWidth( ), +pNode->GetBorderWidth( ) );
415 Point aBarPos( maPosition + aBarOffset );
417 tools::Rectangle aBar( aBarPos, Size( nBarWidth, nBarHeight ) );
418 //! avoid GROWING AND SHRINKING of drawn rectangle when constantly
419 //! increasing zoomfactor.
420 // This is done by shifting its output-position to a point that
421 // corresponds exactly to a pixel on the output device.
422 Point aDrawPos( mrDev.PixelToLogic( mrDev.LogicToPixel( aBar.TopLeft( ) ) ) );
423 aBar.SetPos( aDrawPos );
425 mrDev.DrawRect( aBar );
428 void SmDrawingVisitor::Visit( SmPolyLineNode* pNode )
430 if ( pNode->IsPhantom( ) )
431 return;
433 tools::Long nBorderwidth = pNode->GetFont( ).GetBorderWidth( );
435 LineInfo aInfo;
436 aInfo.SetWidth( pNode->GetWidth( ) - 2 * nBorderwidth );
438 Point aOffset ( Point( ) - pNode->GetPolygon( ).GetBoundRect( ).TopLeft( )
439 + Point( nBorderwidth, nBorderwidth ) ),
440 aPos ( maPosition + aOffset );
441 pNode->GetPolygon( ).Move( aPos.X( ), aPos.Y( ) ); //Works because Polygon wraps a pointer
443 SmTmpDevice aTmpDev ( mrDev, false );
444 aTmpDev.SetLineColor( pNode->GetFont( ).GetColor( ) );
446 mrDev.DrawPolyLine( pNode->GetPolygon( ), aInfo );
449 void SmDrawingVisitor::Visit( SmRectangleNode* pNode )
451 if ( pNode->IsPhantom( ) )
452 return;
454 SmTmpDevice aTmpDev ( mrDev, false );
455 aTmpDev.SetFillColor( pNode->GetFont( ).GetColor( ) );
456 mrDev.SetLineColor( );
457 aTmpDev.SetFont( pNode->GetFont( ) );
459 sal_uLong nTmpBorderWidth = pNode->GetFont( ).GetBorderWidth( );
461 // get rectangle and remove borderspace
462 tools::Rectangle aTmp ( pNode->AsRectangle( ) + maPosition - pNode->GetTopLeft( ) );
463 aTmp.AdjustLeft(nTmpBorderWidth );
464 aTmp.AdjustRight( -sal_Int32(nTmpBorderWidth) );
465 aTmp.AdjustTop(nTmpBorderWidth );
466 aTmp.AdjustBottom( -sal_Int32(nTmpBorderWidth) );
468 SAL_WARN_IF( aTmp.IsEmpty(), "starmath", "Empty rectangle" );
470 //! avoid GROWING AND SHRINKING of drawn rectangle when constantly
471 //! increasing zoomfactor.
472 // This is done by shifting its output-position to a point that
473 // corresponds exactly to a pixel on the output device.
474 Point aPos ( mrDev.PixelToLogic( mrDev.LogicToPixel( aTmp.TopLeft( ) ) ) );
475 aTmp.SetPos( aPos );
477 mrDev.DrawRect( aTmp );
480 void SmDrawingVisitor::DrawTextNode( SmTextNode* pNode )
482 if ( pNode->IsPhantom() || pNode->GetText().isEmpty() || pNode->GetText()[0] == '\0' )
483 return;
485 SmTmpDevice aTmpDev ( mrDev, false );
486 aTmpDev.SetFont( pNode->GetFont( ) );
488 Point aPos ( maPosition );
489 aPos.AdjustY(pNode->GetBaselineOffset( ) );
490 // round to pixel coordinate
491 aPos = mrDev.PixelToLogic( mrDev.LogicToPixel( aPos ) );
493 mrDev.DrawStretchText( aPos, pNode->GetWidth( ), pNode->GetText( ) );
496 void SmDrawingVisitor::DrawSpecialNode( SmSpecialNode* pNode )
498 //! since this chars might come from any font, that we may not have
499 //! set to ALIGN_BASELINE yet, we do it now.
500 pNode->GetFont( ).SetAlignment( ALIGN_BASELINE );
502 DrawTextNode( pNode );
505 void SmDrawingVisitor::DrawChildren( SmStructureNode* pNode )
507 if ( pNode->IsPhantom( ) )
508 return;
510 Point rPosition = maPosition;
512 for( auto pChild : *pNode )
514 if(!pChild)
515 continue;
516 Point aOffset ( pChild->GetTopLeft( ) - pNode->GetTopLeft( ) );
517 maPosition = rPosition + aOffset;
518 pChild->Accept( this );
522 // SmSetSelectionVisitor
524 SmSetSelectionVisitor::SmSetSelectionVisitor( SmCaretPos startPos, SmCaretPos endPos, SmNode* pTree)
525 : maStartPos(startPos)
526 , maEndPos(endPos)
527 , mbSelecting(false)
529 //Assume that pTree is a SmTableNode
530 SAL_WARN_IF(pTree->GetType() != SmNodeType::Table, "starmath", "pTree should be a SmTableNode!");
531 //Visit root node, this is special as this node cannot be selected, but its children can!
532 if(pTree->GetType() == SmNodeType::Table){
533 //Change state if maStartPos is in front of this node
534 if( maStartPos.pSelectedNode == pTree && maStartPos.nIndex == 0 )
535 mbSelecting = !mbSelecting;
536 //Change state if maEndPos is in front of this node
537 if( maEndPos.pSelectedNode == pTree && maEndPos.nIndex == 0 )
538 mbSelecting = !mbSelecting;
539 SAL_WARN_IF(mbSelecting, "starmath", "Caret positions needed to set mbSelecting about, shouldn't be possible!");
541 //Visit lines
542 for( auto pChild : *static_cast<SmStructureNode*>(pTree) )
544 if(!pChild)
545 continue;
546 pChild->Accept( this );
547 //If we started a selection in this line and it haven't ended, we do that now!
548 if(mbSelecting) {
549 mbSelecting = false;
550 SetSelectedOnAll(pChild);
551 //Set maStartPos and maEndPos to invalid positions, this ensures that an unused
552 //start or end (because we forced end above), doesn't start a new selection.
553 maStartPos = maEndPos = SmCaretPos();
556 //Check if pTree isn't selected
557 SAL_WARN_IF(pTree->IsSelected(), "starmath", "pTree should never be selected!");
558 //Discard the selection if there's a bug (it's better than crashing)
559 if(pTree->IsSelected())
560 SetSelectedOnAll(pTree, false);
561 }else //This shouldn't happen, but I don't see any reason to die if it does
562 pTree->Accept(this);
565 void SmSetSelectionVisitor::SetSelectedOnAll( SmNode* pSubTree, bool IsSelected ) {
566 pSubTree->SetSelected( IsSelected );
568 if(pSubTree->GetNumSubNodes() == 0)
569 return;
570 //Quick BFS to set all selections
571 for( auto pChild : *static_cast<SmStructureNode*>(pSubTree) )
573 if(!pChild)
574 continue;
575 SetSelectedOnAll( pChild, IsSelected );
579 void SmSetSelectionVisitor::DefaultVisit( SmNode* pNode ) {
580 //Change state if maStartPos is in front of this node
581 if( maStartPos.pSelectedNode == pNode && maStartPos.nIndex == 0 )
582 mbSelecting = !mbSelecting;
583 //Change state if maEndPos is in front of this node
584 if( maEndPos.pSelectedNode == pNode && maEndPos.nIndex == 0 )
585 mbSelecting = !mbSelecting;
587 //Cache current state
588 bool WasSelecting = mbSelecting;
589 bool ChangedState = false;
591 //Set selected
592 pNode->SetSelected( mbSelecting );
594 //Visit children
595 if(pNode->GetNumSubNodes() > 0)
597 for( auto pChild : *static_cast<SmStructureNode*>(pNode) )
599 if(!pChild)
600 continue;
601 pChild->Accept( this );
602 ChangedState = ( WasSelecting != mbSelecting ) || ChangedState;
606 //If state changed
607 if( ChangedState )
609 //Select this node and all of its children
610 //(Make exception for SmBracebodyNode)
611 if( pNode->GetType() != SmNodeType::Bracebody ||
612 !pNode->GetParent() ||
613 pNode->GetParent()->GetType() != SmNodeType::Brace )
614 SetSelectedOnAll( pNode );
615 else
616 SetSelectedOnAll( pNode->GetParent() );
617 /* If the equation is: sqrt{2 + 4} + 5
618 * And the selection is: sqrt{2 + [4} +] 5
619 * Where [ denotes maStartPos and ] denotes maEndPos
620 * Then the sqrt node should be selected, so that the
621 * effective selection is: [sqrt{2 + 4} +] 5
622 * The same is the case if we swap maStartPos and maEndPos.
626 //Change state if maStartPos is after this node
627 if( maStartPos.pSelectedNode == pNode && maStartPos.nIndex == 1 )
629 mbSelecting = !mbSelecting;
631 //Change state if maEndPos is after of this node
632 if( maEndPos.pSelectedNode == pNode && maEndPos.nIndex == 1 )
634 mbSelecting = !mbSelecting;
638 void SmSetSelectionVisitor::VisitCompositionNode( SmStructureNode* pNode )
640 //Change state if maStartPos is in front of this node
641 if( maStartPos.pSelectedNode == pNode && maStartPos.nIndex == 0 )
642 mbSelecting = !mbSelecting;
643 //Change state if maEndPos is in front of this node
644 if( maEndPos.pSelectedNode == pNode && maEndPos.nIndex == 0 )
645 mbSelecting = !mbSelecting;
647 //Cache current state
648 bool WasSelecting = mbSelecting;
650 //Visit children
651 for( auto pChild : *pNode )
653 if(!pChild)
654 continue;
655 pChild->Accept( this );
658 //Set selected, if everything was selected
659 pNode->SetSelected( WasSelecting && mbSelecting );
661 //Change state if maStartPos is after this node
662 if( maStartPos.pSelectedNode == pNode && maStartPos.nIndex == 1 )
663 mbSelecting = !mbSelecting;
664 //Change state if maEndPos is after of this node
665 if( maEndPos.pSelectedNode == pNode && maEndPos.nIndex == 1 )
666 mbSelecting = !mbSelecting;
669 void SmSetSelectionVisitor::Visit( SmTextNode* pNode ) {
670 tools::Long i1 = -1,
671 i2 = -1;
672 if( maStartPos.pSelectedNode == pNode )
673 i1 = maStartPos.nIndex;
674 if( maEndPos.pSelectedNode == pNode )
675 i2 = maEndPos.nIndex;
677 tools::Long start, end;
678 pNode->SetSelected(true);
679 if( i1 != -1 && i2 != -1 ) {
680 start = std::min(i1, i2);
681 end = std::max(i1, i2);
682 } else if( mbSelecting && i1 != -1 ) {
683 start = 0;
684 end = i1;
685 mbSelecting = false;
686 } else if( mbSelecting && i2 != -1 ) {
687 start = 0;
688 end = i2;
689 mbSelecting = false;
690 } else if( !mbSelecting && i1 != -1 ) {
691 start = i1;
692 end = pNode->GetText().getLength();
693 mbSelecting = true;
694 } else if( !mbSelecting && i2 != -1 ) {
695 start = i2;
696 end = pNode->GetText().getLength();
697 mbSelecting = true;
698 } else if( mbSelecting ) {
699 start = 0;
700 end = pNode->GetText().getLength();
701 } else {
702 pNode->SetSelected( false );
703 start = 0;
704 end = 0;
706 pNode->SetSelected( start != end );
707 pNode->SetSelectionStart( start );
708 pNode->SetSelectionEnd( end );
711 void SmSetSelectionVisitor::Visit( SmExpressionNode* pNode ) {
712 VisitCompositionNode( pNode );
715 void SmSetSelectionVisitor::Visit( SmLineNode* pNode ) {
716 VisitCompositionNode( pNode );
719 void SmSetSelectionVisitor::Visit( SmAlignNode* pNode ) {
720 VisitCompositionNode( pNode );
723 void SmSetSelectionVisitor::Visit( SmBinHorNode* pNode ) {
724 VisitCompositionNode( pNode );
727 void SmSetSelectionVisitor::Visit( SmUnHorNode* pNode ) {
728 VisitCompositionNode( pNode );
731 void SmSetSelectionVisitor::Visit( SmFontNode* pNode ) {
732 VisitCompositionNode( pNode );
735 // SmCaretPosGraphBuildingVisitor
737 SmCaretPosGraphBuildingVisitor::SmCaretPosGraphBuildingVisitor( SmNode* pRootNode )
738 : mpRightMost(nullptr)
739 , mpGraph(new SmCaretPosGraph)
741 //pRootNode should always be a table
742 SAL_WARN_IF( pRootNode->GetType( ) != SmNodeType::Table, "starmath", "pRootNode must be a table node");
743 //Handle the special case where SmNodeType::Table is used a rootnode
744 if( pRootNode->GetType( ) == SmNodeType::Table ){
745 //Children are SmLineNodes
746 //Or so I thought... Apparently, the children can be instances of SmExpression
747 //especially if there's an error in the formula... So here we go, a simple work around.
748 for( auto pChild : *static_cast<SmStructureNode*>(pRootNode) )
750 if(!pChild)
751 continue;
752 mpRightMost = mpGraph->Add( SmCaretPos( pChild, 0 ) );
753 pChild->Accept( this );
755 }else
756 pRootNode->Accept(this);
759 SmCaretPosGraphBuildingVisitor::~SmCaretPosGraphBuildingVisitor()
763 void SmCaretPosGraphBuildingVisitor::Visit( SmLineNode* pNode ){
764 for( auto pChild : *pNode )
766 if(!pChild)
767 continue;
768 pChild->Accept( this );
772 /** Build SmCaretPosGraph for SmTableNode
773 * This method covers cases where SmTableNode is used in a binom or stack,
774 * the special case where it is used as root node for the entire formula is
775 * handled in the constructor.
777 void SmCaretPosGraphBuildingVisitor::Visit( SmTableNode* pNode ){
778 SmCaretPosGraphEntry *left = mpRightMost,
779 *right = mpGraph->Add( SmCaretPos( pNode, 1) );
780 bool bIsFirst = true;
781 for( auto pChild : *pNode )
783 if(!pChild)
784 continue;
785 mpRightMost = mpGraph->Add( SmCaretPos( pChild, 0 ), left);
786 if(bIsFirst)
787 left->SetRight(mpRightMost);
788 pChild->Accept( this );
789 mpRightMost->SetRight(right);
790 if(bIsFirst)
791 right->SetLeft(mpRightMost);
792 bIsFirst = false;
794 mpRightMost = right;
797 /** Build SmCaretPosGraph for SmSubSupNode
799 * The child positions in a SubSupNode, where H is the body:
800 * \code
801 * CSUP
803 * LSUP H H RSUP
804 * H H
805 * HHHH
806 * H H
807 * LSUB H H RSUB
809 * CSUB
810 * \endcode
812 * Graph over these, where "left" is before the SmSubSupNode and "right" is after:
813 * \dot
814 * digraph Graph{
815 * left -> H;
816 * H -> right;
817 * LSUP -> H;
818 * LSUB -> H;
819 * CSUP -> right;
820 * CSUB -> right;
821 * RSUP -> right;
822 * RSUB -> right;
823 * };
824 * \enddot
827 void SmCaretPosGraphBuildingVisitor::Visit( SmSubSupNode* pNode )
829 SmCaretPosGraphEntry *left,
830 *right,
831 *bodyLeft,
832 *bodyRight;
834 assert(mpRightMost);
835 left = mpRightMost;
837 //Create bodyLeft
838 SAL_WARN_IF( !pNode->GetBody(), "starmath", "SmSubSupNode Doesn't have a body!" );
839 bodyLeft = mpGraph->Add( SmCaretPos( pNode->GetBody( ), 0 ), left );
840 left->SetRight( bodyLeft ); //TODO: Don't make this if LSUP or LSUB are NULL ( not sure??? )
842 //Create right
843 right = mpGraph->Add( SmCaretPos( pNode, 1 ) );
845 //Visit the body, to get bodyRight
846 mpRightMost = bodyLeft;
847 pNode->GetBody( )->Accept( this );
848 bodyRight = mpRightMost;
849 bodyRight->SetRight( right );
850 right->SetLeft( bodyRight );
852 //If there's an LSUP
853 SmNode* pChild = pNode->GetSubSup( LSUP );
854 if( pChild ){
855 SmCaretPosGraphEntry *cLeft; //Child left
856 cLeft = mpGraph->Add( SmCaretPos( pChild, 0 ), left );
858 mpRightMost = cLeft;
859 pChild->Accept( this );
861 mpRightMost->SetRight( bodyLeft );
863 //If there's an LSUB
864 pChild = pNode->GetSubSup( LSUB );
865 if( pChild ){
866 SmCaretPosGraphEntry *cLeft; //Child left
867 cLeft = mpGraph->Add( SmCaretPos( pChild, 0 ), left );
869 mpRightMost = cLeft;
870 pChild->Accept( this );
872 mpRightMost->SetRight( bodyLeft );
874 //If there's a CSUP
875 pChild = pNode->GetSubSup( CSUP );
876 if( pChild ){
877 SmCaretPosGraphEntry *cLeft; //Child left
878 cLeft = mpGraph->Add( SmCaretPos( pChild, 0 ), left );
880 mpRightMost = cLeft;
881 pChild->Accept( this );
883 mpRightMost->SetRight( right );
885 //If there's a CSUB
886 pChild = pNode->GetSubSup( CSUB );
887 if( pChild ){
888 SmCaretPosGraphEntry *cLeft; //Child left
889 cLeft = mpGraph->Add( SmCaretPos( pChild, 0 ), left );
891 mpRightMost = cLeft;
892 pChild->Accept( this );
894 mpRightMost->SetRight( right );
896 //If there's an RSUP
897 pChild = pNode->GetSubSup( RSUP );
898 if( pChild ){
899 SmCaretPosGraphEntry *cLeft; //Child left
900 cLeft = mpGraph->Add( SmCaretPos( pChild, 0 ), bodyRight );
902 mpRightMost = cLeft;
903 pChild->Accept( this );
905 mpRightMost->SetRight( right );
907 //If there's an RSUB
908 pChild = pNode->GetSubSup( RSUB );
909 if( pChild ){
910 SmCaretPosGraphEntry *cLeft; //Child left
911 cLeft = mpGraph->Add( SmCaretPos( pChild, 0 ), bodyRight );
913 mpRightMost = cLeft;
914 pChild->Accept( this );
916 mpRightMost->SetRight( right );
919 //Set return parameters
920 mpRightMost = right;
923 /** Build caret position for SmOperNode
925 * If first child is an SmSubSupNode we will ignore its
926 * body, as this body is a SmMathSymbol, for SUM, INT or similar
927 * that shouldn't be subject to modification.
928 * If first child is not a SmSubSupNode, ignore it completely
929 * as it is a SmMathSymbol.
931 * The child positions in a SmOperNode, where H is symbol, e.g. int, sum or similar:
932 * \code
933 * TO
935 * LSUP H H RSUP BBB BB BBB B B
936 * H H B B B B B B B B
937 * HHHH BBB B B B B B
938 * H H B B B B B B B
939 * LSUB H H RSUB BBB BB BBB B
941 * FROM
942 * \endcode
943 * Notice, CSUP, etc. are actually grandchildren, but inorder to ignore H, these are visited
944 * from here. If they are present, that is if pOper is an instance of SmSubSupNode.
946 * Graph over these, where "left" is before the SmOperNode and "right" is after:
947 * \dot
948 * digraph Graph{
949 * left -> BODY;
950 * BODY -> right;
951 * LSUP -> BODY;
952 * LSUB -> BODY;
953 * TO -> BODY;
954 * FROM -> BODY;
955 * RSUP -> BODY;
956 * RSUB -> BODY;
957 * };
958 * \enddot
960 void SmCaretPosGraphBuildingVisitor::Visit( SmOperNode* pNode )
962 SmNode *pOper = pNode->GetSubNode( 0 ),
963 *pBody = pNode->GetSubNode( 1 );
965 SmCaretPosGraphEntry *left = mpRightMost,
966 *bodyLeft,
967 *bodyRight,
968 *right;
969 //Create body left
970 bodyLeft = mpGraph->Add( SmCaretPos( pBody, 0 ), left );
971 left->SetRight( bodyLeft );
973 //Visit body, get bodyRight
974 mpRightMost = bodyLeft;
975 pBody->Accept( this );
976 bodyRight = mpRightMost;
978 //Create right
979 right = mpGraph->Add( SmCaretPos( pNode, 1 ), bodyRight );
980 bodyRight->SetRight( right );
982 //Get subsup pNode if any
983 SmSubSupNode* pSubSup = pOper->GetType( ) == SmNodeType::SubSup ? static_cast<SmSubSupNode*>(pOper) : nullptr;
985 if( pSubSup ) {
986 SmNode* pChild = pSubSup->GetSubSup( LSUP );
987 if( pChild ) {
988 //Create position in front of pChild
989 SmCaretPosGraphEntry *childLeft = mpGraph->Add( SmCaretPos( pChild, 0 ), left );
990 //Visit pChild
991 mpRightMost = childLeft;
992 pChild->Accept( this );
993 //Set right on mpRightMost from pChild
994 mpRightMost->SetRight( bodyLeft );
997 pChild = pSubSup->GetSubSup( LSUB );
998 if( pChild ) {
999 //Create position in front of pChild
1000 SmCaretPosGraphEntry *childLeft = mpGraph->Add( SmCaretPos( pChild, 0 ), left );
1001 //Visit pChild
1002 mpRightMost = childLeft;
1003 pChild->Accept( this );
1004 //Set right on mpRightMost from pChild
1005 mpRightMost->SetRight( bodyLeft );
1008 pChild = pSubSup->GetSubSup( CSUP );
1009 if ( pChild ) {//TO
1010 //Create position in front of pChild
1011 SmCaretPosGraphEntry *childLeft = mpGraph->Add( SmCaretPos( pChild, 0 ), left );
1012 //Visit pChild
1013 mpRightMost = childLeft;
1014 pChild->Accept( this );
1015 //Set right on mpRightMost from pChild
1016 mpRightMost->SetRight( bodyLeft );
1019 pChild = pSubSup->GetSubSup( CSUB );
1020 if( pChild ) { //FROM
1021 //Create position in front of pChild
1022 SmCaretPosGraphEntry *childLeft = mpGraph->Add( SmCaretPos( pChild, 0 ), left );
1023 //Visit pChild
1024 mpRightMost = childLeft;
1025 pChild->Accept( this );
1026 //Set right on mpRightMost from pChild
1027 mpRightMost->SetRight( bodyLeft );
1030 pChild = pSubSup->GetSubSup( RSUP );
1031 if ( pChild ) {
1032 //Create position in front of pChild
1033 SmCaretPosGraphEntry *childLeft = mpGraph->Add( SmCaretPos( pChild, 0 ), left );
1034 //Visit pChild
1035 mpRightMost = childLeft;
1036 pChild->Accept( this );
1037 //Set right on mpRightMost from pChild
1038 mpRightMost->SetRight( bodyLeft );
1041 pChild = pSubSup->GetSubSup( RSUB );
1042 if ( pChild ) {
1043 //Create position in front of pChild
1044 SmCaretPosGraphEntry *childLeft = mpGraph->Add( SmCaretPos( pChild, 0 ), left );
1045 //Visit pChild
1046 mpRightMost = childLeft;
1047 pChild->Accept( this );
1048 //Set right on mpRightMost from pChild
1049 mpRightMost->SetRight( bodyLeft );
1053 //Return right
1054 mpRightMost = right;
1057 void SmCaretPosGraphBuildingVisitor::Visit( SmMatrixNode* pNode )
1059 SmCaretPosGraphEntry *left = mpRightMost,
1060 *right = mpGraph->Add( SmCaretPos( pNode, 1 ) );
1062 for (size_t i = 0; i < pNode->GetNumRows(); ++i)
1064 SmCaretPosGraphEntry* r = left;
1065 for (size_t j = 0; j < pNode->GetNumCols(); ++j)
1067 SmNode* pSubNode = pNode->GetSubNode( i * pNode->GetNumCols( ) + j );
1069 mpRightMost = mpGraph->Add( SmCaretPos( pSubNode, 0 ), r );
1070 if( j != 0 || ( pNode->GetNumRows() - 1U ) / 2 == i )
1071 r->SetRight( mpRightMost );
1073 pSubNode->Accept( this );
1075 r = mpRightMost;
1077 mpRightMost->SetRight( right );
1078 if( ( pNode->GetNumRows() - 1U ) / 2 == i )
1079 right->SetLeft( mpRightMost );
1082 mpRightMost = right;
1085 /** Build SmCaretPosGraph for SmTextNode
1087 * Lines in an SmTextNode:
1088 * \code
1089 * A B C
1090 * \endcode
1091 * Where A B and C are characters in the text.
1093 * Graph over these, where "left" is before the SmTextNode and "right" is after:
1094 * \dot
1095 * digraph Graph{
1096 * left -> A;
1097 * A -> B
1098 * B -> right;
1099 * };
1100 * \enddot
1101 * Notice that C and right is the same position here.
1103 void SmCaretPosGraphBuildingVisitor::Visit( SmTextNode* pNode )
1105 SAL_WARN_IF( pNode->GetText().isEmpty(), "starmath", "Empty SmTextNode is bad" );
1107 int size = pNode->GetText().getLength();
1108 for( int i = 1; i <= size; i++ ){
1109 SmCaretPosGraphEntry* pRight = mpRightMost;
1110 mpRightMost = mpGraph->Add( SmCaretPos( pNode, i ), pRight );
1111 pRight->SetRight( mpRightMost );
1115 /** Build SmCaretPosGraph for SmBinVerNode
1117 * Lines in an SmBinVerNode:
1118 * \code
1120 * -----
1122 * \endcode
1124 * Graph over these, where "left" is before the SmBinVerNode and "right" is after:
1125 * \dot
1126 * digraph Graph{
1127 * left -> A;
1128 * A -> right;
1129 * B -> right;
1130 * };
1131 * \enddot
1133 void SmCaretPosGraphBuildingVisitor::Visit( SmBinVerNode* pNode )
1135 //None if these children can be NULL, see SmBinVerNode::Arrange
1136 SmNode *pNum = pNode->GetSubNode( 0 ),
1137 *pDenom = pNode->GetSubNode( 2 );
1139 SmCaretPosGraphEntry *left,
1140 *right,
1141 *numLeft,
1142 *denomLeft;
1144 assert(mpRightMost);
1145 //Set left
1146 left = mpRightMost;
1148 //Create right
1149 right = mpGraph->Add( SmCaretPos( pNode, 1 ) );
1151 //Create numLeft
1152 numLeft = mpGraph->Add( SmCaretPos( pNum, 0 ), left );
1153 left->SetRight( numLeft );
1155 //Visit pNum
1156 mpRightMost = numLeft;
1157 pNum->Accept( this );
1158 mpRightMost->SetRight( right );
1159 right->SetLeft( mpRightMost );
1161 //Create denomLeft
1162 denomLeft = mpGraph->Add( SmCaretPos( pDenom, 0 ), left );
1164 //Visit pDenom
1165 mpRightMost = denomLeft;
1166 pDenom->Accept( this );
1167 mpRightMost->SetRight( right );
1169 //Set return parameter
1170 mpRightMost = right;
1173 /** Build SmCaretPosGraph for SmVerticalBraceNode
1175 * Lines in an SmVerticalBraceNode:
1176 * \code
1177 * pScript
1178 * ________
1179 * / \
1180 * pBody
1181 * \endcode
1184 void SmCaretPosGraphBuildingVisitor::Visit( SmVerticalBraceNode* pNode )
1186 SmNode *pBody = pNode->Body(),
1187 *pScript = pNode->Script();
1188 //None of these children can be NULL
1190 SmCaretPosGraphEntry *left,
1191 *bodyLeft,
1192 *scriptLeft,
1193 *right;
1195 left = mpRightMost;
1197 //Create right
1198 right = mpGraph->Add( SmCaretPos( pNode, 1 ) );
1200 //Create bodyLeft
1201 bodyLeft = mpGraph->Add( SmCaretPos( pBody, 0 ), left );
1202 left->SetRight( bodyLeft );
1203 mpRightMost = bodyLeft;
1204 pBody->Accept( this );
1205 mpRightMost->SetRight( right );
1206 right->SetLeft( mpRightMost );
1208 //Create script
1209 scriptLeft = mpGraph->Add( SmCaretPos( pScript, 0 ), left );
1210 mpRightMost = scriptLeft;
1211 pScript->Accept( this );
1212 mpRightMost->SetRight( right );
1214 //Set return value
1215 mpRightMost = right;
1218 /** Build SmCaretPosGraph for SmBinDiagonalNode
1220 * Lines in an SmBinDiagonalNode:
1221 * \code
1222 * A /
1224 * / B
1225 * \endcode
1226 * Where A and B are lines.
1228 * Used in formulas such as "A wideslash B"
1230 void SmCaretPosGraphBuildingVisitor::Visit( SmBinDiagonalNode* pNode )
1232 SmNode *A = pNode->GetSubNode( 0 ),
1233 *B = pNode->GetSubNode( 1 );
1235 SmCaretPosGraphEntry *left,
1236 *leftA,
1237 *rightA,
1238 *leftB,
1239 *right;
1240 left = mpRightMost;
1242 //Create right
1243 right = mpGraph->Add( SmCaretPos( pNode, 1 ) );
1245 //Create left A
1246 leftA = mpGraph->Add( SmCaretPos( A, 0 ), left );
1247 left->SetRight( leftA );
1249 //Visit A
1250 mpRightMost = leftA;
1251 A->Accept( this );
1252 rightA = mpRightMost;
1254 //Create left B
1255 leftB = mpGraph->Add( SmCaretPos( B, 0 ), rightA );
1256 rightA->SetRight( leftB );
1258 //Visit B
1259 mpRightMost = leftB;
1260 B->Accept( this );
1261 mpRightMost->SetRight( right );
1262 right->SetLeft( mpRightMost );
1264 //Set return value
1265 mpRightMost = right;
1268 //Straight forward ( I think )
1269 void SmCaretPosGraphBuildingVisitor::Visit( SmBinHorNode* pNode )
1271 for( auto pChild : *pNode )
1273 if(!pChild)
1274 continue;
1275 pChild->Accept( this );
1278 void SmCaretPosGraphBuildingVisitor::Visit( SmUnHorNode* pNode )
1280 // Unary operator node
1281 for( auto pChild : *pNode )
1283 if(!pChild)
1284 continue;
1285 pChild->Accept( this );
1289 void SmCaretPosGraphBuildingVisitor::Visit( SmExpressionNode* pNode )
1291 for( auto pChild : *pNode )
1293 if(!pChild)
1294 continue;
1295 pChild->Accept( this );
1299 void SmCaretPosGraphBuildingVisitor::Visit( SmFontNode* pNode )
1301 //Has only got one child, should act as an expression if possible
1302 for( auto pChild : *pNode )
1304 if(!pChild)
1305 continue;
1306 pChild->Accept( this );
1310 /** Build SmCaretPosGraph for SmBracebodyNode
1311 * Acts as an SmExpressionNode
1313 * Below is an example of a formula tree that has multiple children for SmBracebodyNode
1314 * \dot
1315 * digraph {
1316 * labelloc = "t";
1317 * label= "Equation: \"lbrace i mline i in setZ rbrace\"";
1318 * n0 [label="SmTableNode"];
1319 * n0 -> n1 [label="0"];
1320 * n1 [label="SmLineNode"];
1321 * n1 -> n2 [label="0"];
1322 * n2 [label="SmExpressionNode"];
1323 * n2 -> n3 [label="0"];
1324 * n3 [label="SmBraceNode"];
1325 * n3 -> n4 [label="0"];
1326 * n4 [label="SmMathSymbolNode: {"];
1327 * n3 -> n5 [label="1"];
1328 * n5 [label="SmBracebodyNode"];
1329 * n5 -> n6 [label="0"];
1330 * n6 [label="SmExpressionNode"];
1331 * n6 -> n7 [label="0"];
1332 * n7 [label="SmTextNode: i"];
1333 * n5 -> n8 [label="1"];
1334 * n8 [label="SmMathSymbolNode: &#124;"]; // Unicode "VERTICAL LINE"
1335 * n5 -> n9 [label="2"];
1336 * n9 [label="SmExpressionNode"];
1337 * n9 -> n10 [label="0"];
1338 * n10 [label="SmBinHorNode"];
1339 * n10 -> n11 [label="0"];
1340 * n11 [label="SmTextNode: i"];
1341 * n10 -> n12 [label="1"];
1342 * n12 [label="SmMathSymbolNode: &#8712;"]; // Unicode "ELEMENT OF"
1343 * n10 -> n13 [label="2"];
1344 * n13 [label="SmMathSymbolNode: &#8484;"]; // Unicode "DOUBLE-STRUCK CAPITAL Z"
1345 * n3 -> n14 [label="2"];
1346 * n14 [label="SmMathSymbolNode: }"];
1348 * \enddot
1350 void SmCaretPosGraphBuildingVisitor::Visit( SmBracebodyNode* pNode )
1352 for( auto pChild : *pNode )
1354 if(!pChild)
1355 continue;
1356 SmCaretPosGraphEntry* pStart = mpGraph->Add( SmCaretPos( pChild, 0), mpRightMost );
1357 mpRightMost->SetRight( pStart );
1358 mpRightMost = pStart;
1359 pChild->Accept( this );
1363 /** Build SmCaretPosGraph for SmAlignNode
1364 * Acts as an SmExpressionNode, as it only has one child this okay
1366 void SmCaretPosGraphBuildingVisitor::Visit( SmAlignNode* pNode )
1368 for( auto pChild : *pNode )
1370 if(!pChild)
1371 continue;
1372 pChild->Accept( this );
1376 /** Build SmCaretPosGraph for SmRootNode
1378 * Lines in an SmRootNode:
1379 * \code
1380 * _________
1381 * A/
1382 * \/ B
1384 * \endcode
1385 * A: pExtra ( optional, can be NULL ),
1386 * B: pBody
1388 * Graph over these, where "left" is before the SmRootNode and "right" is after:
1389 * \dot
1390 * digraph Graph{
1391 * left -> B;
1392 * B -> right;
1393 * A -> B;
1395 * \enddot
1397 void SmCaretPosGraphBuildingVisitor::Visit( SmRootNode* pNode )
1399 SmNode *pExtra = pNode->GetSubNode( 0 ), //Argument, NULL for sqrt, and SmTextNode if cubicroot
1400 *pBody = pNode->GetSubNode( 2 ); //Body of the root
1401 assert(pBody);
1403 SmCaretPosGraphEntry *left,
1404 *right,
1405 *bodyLeft,
1406 *bodyRight;
1408 //Get left and save it
1409 assert(mpRightMost);
1410 left = mpRightMost;
1412 //Create body left
1413 bodyLeft = mpGraph->Add( SmCaretPos( pBody, 0 ), left );
1414 left->SetRight( bodyLeft );
1416 //Create right
1417 right = mpGraph->Add( SmCaretPos( pNode, 1 ) );
1419 //Visit body
1420 mpRightMost = bodyLeft;
1421 pBody->Accept( this );
1422 bodyRight = mpRightMost;
1423 bodyRight->SetRight( right );
1424 right->SetLeft( bodyRight );
1426 //Visit pExtra
1427 if( pExtra ){
1428 mpRightMost = mpGraph->Add( SmCaretPos( pExtra, 0 ), left );
1429 pExtra->Accept( this );
1430 mpRightMost->SetRight( bodyLeft );
1433 mpRightMost = right;
1437 /** Build SmCaretPosGraph for SmPlaceNode
1438 * Consider this a single character.
1440 void SmCaretPosGraphBuildingVisitor::Visit( SmPlaceNode* pNode )
1442 SmCaretPosGraphEntry* right = mpGraph->Add( SmCaretPos( pNode, 1 ), mpRightMost );
1443 mpRightMost->SetRight( right );
1444 mpRightMost = right;
1447 /** SmErrorNode is context dependent metadata, it can't be selected
1449 * @remarks There's no point in deleting, copying and/or moving an instance
1450 * of SmErrorNode as it may not exist in another context! Thus there are no
1451 * positions to select an SmErrorNode.
1453 void SmCaretPosGraphBuildingVisitor::Visit( SmErrorNode* )
1457 /** Build SmCaretPosGraph for SmBlankNode
1458 * Consider this a single character, as it is only a blank space
1460 void SmCaretPosGraphBuildingVisitor::Visit( SmBlankNode* pNode )
1462 SmCaretPosGraphEntry* right = mpGraph->Add( SmCaretPos( pNode, 1 ), mpRightMost );
1463 mpRightMost->SetRight( right );
1464 mpRightMost = right;
1467 /** Build SmCaretPosGraph for SmBraceNode
1469 * Lines in an SmBraceNode:
1470 * \code
1471 * | |
1472 * | B |
1473 * | |
1474 * \endcode
1475 * B: Body
1477 * Graph over these, where "left" is before the SmBraceNode and "right" is after:
1478 * \dot
1479 * digraph Graph{
1480 * left -> B;
1481 * B -> right;
1483 * \enddot
1485 void SmCaretPosGraphBuildingVisitor::Visit( SmBraceNode* pNode )
1487 SmNode* pBody = pNode->Body();
1489 SmCaretPosGraphEntry *left = mpRightMost,
1490 *right = mpGraph->Add( SmCaretPos( pNode, 1 ) );
1492 if( pBody->GetType() != SmNodeType::Bracebody ) {
1493 mpRightMost = mpGraph->Add( SmCaretPos( pBody, 0 ), left );
1494 left->SetRight( mpRightMost );
1495 }else
1496 mpRightMost = left;
1498 pBody->Accept( this );
1499 mpRightMost->SetRight( right );
1500 right->SetLeft( mpRightMost );
1502 mpRightMost = right;
1505 /** Build SmCaretPosGraph for SmAttributNode
1507 * Lines in an SmAttributNode:
1508 * \code
1509 * Attr
1510 * Body
1511 * \endcode
1513 * There's a body and an attribute, the construction is used for "widehat A", where "A" is the body
1514 * and "^" is the attribute ( note GetScaleMode( ) on SmAttributNode tells how the attribute should be
1515 * scaled ).
1517 void SmCaretPosGraphBuildingVisitor::Visit( SmAttributNode* pNode )
1519 SmNode *pAttr = pNode->Attribute(),
1520 *pBody = pNode->Body();
1521 assert(pAttr);
1522 assert(pBody);
1524 SmCaretPosGraphEntry *left = mpRightMost,
1525 *attrLeft,
1526 *bodyLeft,
1527 *bodyRight,
1528 *right;
1530 //Creating bodyleft
1531 bodyLeft = mpGraph->Add( SmCaretPos( pBody, 0 ), left );
1532 left->SetRight( bodyLeft );
1534 //Creating right
1535 right = mpGraph->Add( SmCaretPos( pNode, 1 ) );
1537 //Visit the body
1538 mpRightMost = bodyLeft;
1539 pBody->Accept( this );
1540 bodyRight = mpRightMost;
1541 bodyRight->SetRight( right );
1542 right->SetLeft( bodyRight );
1544 //Create attrLeft
1545 attrLeft = mpGraph->Add( SmCaretPos( pAttr, 0 ), left );
1547 //Visit attribute
1548 mpRightMost = attrLeft;
1549 pAttr->Accept( this );
1550 mpRightMost->SetRight( right );
1552 //Set return value
1553 mpRightMost = right;
1556 //Consider these single symbols
1557 void SmCaretPosGraphBuildingVisitor::Visit( SmSpecialNode* pNode )
1559 SmCaretPosGraphEntry* right = mpGraph->Add( SmCaretPos( pNode, 1 ), mpRightMost );
1560 mpRightMost->SetRight( right );
1561 mpRightMost = right;
1563 void SmCaretPosGraphBuildingVisitor::Visit( SmGlyphSpecialNode* pNode )
1565 SmCaretPosGraphEntry* right = mpGraph->Add( SmCaretPos( pNode, 1 ), mpRightMost );
1566 mpRightMost->SetRight( right );
1567 mpRightMost = right;
1569 void SmCaretPosGraphBuildingVisitor::Visit( SmMathSymbolNode* pNode )
1571 SmCaretPosGraphEntry* right = mpGraph->Add( SmCaretPos( pNode, 1 ), mpRightMost );
1572 mpRightMost->SetRight( right );
1573 mpRightMost = right;
1576 void SmCaretPosGraphBuildingVisitor::Visit( SmRootSymbolNode* )
1578 //Do nothing
1581 void SmCaretPosGraphBuildingVisitor::Visit( SmRectangleNode* )
1583 //Do nothing
1585 void SmCaretPosGraphBuildingVisitor::Visit( SmPolyLineNode* )
1587 //Do nothing
1590 // SmCloningVisitor
1592 SmNode* SmCloningVisitor::Clone( SmNode* pNode )
1594 SmNode* pCurrResult = mpResult;
1595 pNode->Accept( this );
1596 SmNode* pClone = mpResult;
1597 mpResult = pCurrResult;
1598 return pClone;
1601 void SmCloningVisitor::CloneNodeAttr( SmNode const * pSource, SmNode* pTarget )
1603 pTarget->SetScaleMode( pSource->GetScaleMode( ) );
1604 //Other attributes are set when prepare or arrange is executed
1605 //and may depend on stuff not being cloned here.
1608 void SmCloningVisitor::CloneKids( SmStructureNode* pSource, SmStructureNode* pTarget )
1610 //Cache current result
1611 SmNode* pCurrResult = mpResult;
1613 //Create array for holding clones
1614 size_t nSize = pSource->GetNumSubNodes( );
1615 SmNodeArray aNodes( nSize );
1617 //Clone children
1618 for (size_t i = 0; i < nSize; ++i)
1620 SmNode* pKid;
1621 if( nullptr != ( pKid = pSource->GetSubNode( i ) ) )
1622 pKid->Accept( this );
1623 else
1624 mpResult = nullptr;
1625 aNodes[i] = mpResult;
1628 //Set subnodes of pTarget
1629 pTarget->SetSubNodes( std::move(aNodes) );
1631 //Restore result as where prior to call
1632 mpResult = pCurrResult;
1635 void SmCloningVisitor::Visit( SmTableNode* pNode )
1637 SmTableNode* pClone = new SmTableNode( pNode->GetToken( ) );
1638 CloneNodeAttr( pNode, pClone );
1639 CloneKids( pNode, pClone );
1640 mpResult = pClone;
1643 void SmCloningVisitor::Visit( SmBraceNode* pNode )
1645 SmBraceNode* pClone = new SmBraceNode( pNode->GetToken( ) );
1646 CloneNodeAttr( pNode, pClone );
1647 CloneKids( pNode, pClone );
1648 mpResult = pClone;
1651 void SmCloningVisitor::Visit( SmBracebodyNode* pNode )
1653 SmBracebodyNode* pClone = new SmBracebodyNode( pNode->GetToken( ) );
1654 CloneNodeAttr( pNode, pClone );
1655 CloneKids( pNode, pClone );
1656 mpResult = pClone;
1659 void SmCloningVisitor::Visit( SmOperNode* pNode )
1661 SmOperNode* pClone = new SmOperNode( pNode->GetToken( ) );
1662 CloneNodeAttr( pNode, pClone );
1663 CloneKids( pNode, pClone );
1664 mpResult = pClone;
1667 void SmCloningVisitor::Visit( SmAlignNode* pNode )
1669 SmAlignNode* pClone = new SmAlignNode( pNode->GetToken( ) );
1670 CloneNodeAttr( pNode, pClone );
1671 CloneKids( pNode, pClone );
1672 mpResult = pClone;
1675 void SmCloningVisitor::Visit( SmAttributNode* pNode )
1677 SmAttributNode* pClone = new SmAttributNode( pNode->GetToken( ) );
1678 CloneNodeAttr( pNode, pClone );
1679 CloneKids( pNode, pClone );
1680 mpResult = pClone;
1683 void SmCloningVisitor::Visit( SmFontNode* pNode )
1685 SmFontNode* pClone = new SmFontNode( pNode->GetToken( ) );
1686 pClone->SetSizeParameter( pNode->GetSizeParameter( ), pNode->GetSizeType( ) );
1687 CloneNodeAttr( pNode, pClone );
1688 CloneKids( pNode, pClone );
1689 mpResult = pClone;
1692 void SmCloningVisitor::Visit( SmUnHorNode* pNode )
1694 SmUnHorNode* pClone = new SmUnHorNode( pNode->GetToken( ) );
1695 CloneNodeAttr( pNode, pClone );
1696 CloneKids( pNode, pClone );
1697 mpResult = pClone;
1700 void SmCloningVisitor::Visit( SmBinHorNode* pNode )
1702 SmBinHorNode* pClone = new SmBinHorNode( pNode->GetToken( ) );
1703 CloneNodeAttr( pNode, pClone );
1704 CloneKids( pNode, pClone );
1705 mpResult = pClone;
1708 void SmCloningVisitor::Visit( SmBinVerNode* pNode )
1710 SmBinVerNode* pClone = new SmBinVerNode( pNode->GetToken( ) );
1711 CloneNodeAttr( pNode, pClone );
1712 CloneKids( pNode, pClone );
1713 mpResult = pClone;
1716 void SmCloningVisitor::Visit( SmBinDiagonalNode* pNode )
1718 SmBinDiagonalNode *pClone = new SmBinDiagonalNode( pNode->GetToken( ) );
1719 pClone->SetAscending( pNode->IsAscending( ) );
1720 CloneNodeAttr( pNode, pClone );
1721 CloneKids( pNode, pClone );
1722 mpResult = pClone;
1725 void SmCloningVisitor::Visit( SmSubSupNode* pNode )
1727 SmSubSupNode *pClone = new SmSubSupNode( pNode->GetToken( ) );
1728 pClone->SetUseLimits( pNode->IsUseLimits( ) );
1729 CloneNodeAttr( pNode, pClone );
1730 CloneKids( pNode, pClone );
1731 mpResult = pClone;
1734 void SmCloningVisitor::Visit( SmMatrixNode* pNode )
1736 SmMatrixNode *pClone = new SmMatrixNode( pNode->GetToken( ) );
1737 pClone->SetRowCol( pNode->GetNumRows( ), pNode->GetNumCols( ) );
1738 CloneNodeAttr( pNode, pClone );
1739 CloneKids( pNode, pClone );
1740 mpResult = pClone;
1743 void SmCloningVisitor::Visit( SmPlaceNode* pNode )
1745 mpResult = new SmPlaceNode( pNode->GetToken( ) );
1746 CloneNodeAttr( pNode, mpResult );
1749 void SmCloningVisitor::Visit( SmTextNode* pNode )
1751 SmTextNode* pClone = new SmTextNode( pNode->GetToken( ), pNode->GetFontDesc( ) );
1752 pClone->ChangeText( pNode->GetText( ) );
1753 CloneNodeAttr( pNode, pClone );
1754 mpResult = pClone;
1757 void SmCloningVisitor::Visit( SmSpecialNode* pNode )
1759 mpResult = new SmSpecialNode( pNode->GetToken( ) );
1760 CloneNodeAttr( pNode, mpResult );
1763 void SmCloningVisitor::Visit( SmGlyphSpecialNode* pNode )
1765 mpResult = new SmGlyphSpecialNode( pNode->GetToken( ) );
1766 CloneNodeAttr( pNode, mpResult );
1769 void SmCloningVisitor::Visit( SmMathSymbolNode* pNode )
1771 mpResult = new SmMathSymbolNode( pNode->GetToken( ) );
1772 CloneNodeAttr( pNode, mpResult );
1775 void SmCloningVisitor::Visit( SmBlankNode* pNode )
1777 SmBlankNode* pClone = new SmBlankNode( pNode->GetToken( ) );
1778 pClone->SetBlankNum( pNode->GetBlankNum( ) );
1779 mpResult = pClone;
1780 CloneNodeAttr( pNode, mpResult );
1783 void SmCloningVisitor::Visit( SmErrorNode* pNode )
1785 mpResult = new SmErrorNode( pNode->GetToken( ) );
1786 CloneNodeAttr( pNode, mpResult );
1789 void SmCloningVisitor::Visit( SmLineNode* pNode )
1791 SmLineNode* pClone = new SmLineNode( pNode->GetToken( ) );
1792 CloneNodeAttr( pNode, pClone );
1793 CloneKids( pNode, pClone );
1794 mpResult = pClone;
1797 void SmCloningVisitor::Visit( SmExpressionNode* pNode )
1799 SmExpressionNode* pClone = new SmExpressionNode( pNode->GetToken( ) );
1800 CloneNodeAttr( pNode, pClone );
1801 CloneKids( pNode, pClone );
1802 mpResult = pClone;
1805 void SmCloningVisitor::Visit( SmPolyLineNode* pNode )
1807 mpResult = new SmPolyLineNode( pNode->GetToken( ) );
1808 CloneNodeAttr( pNode, mpResult );
1811 void SmCloningVisitor::Visit( SmRootNode* pNode )
1813 SmRootNode* pClone = new SmRootNode( pNode->GetToken( ) );
1814 CloneNodeAttr( pNode, pClone );
1815 CloneKids( pNode, pClone );
1816 mpResult = pClone;
1819 void SmCloningVisitor::Visit( SmRootSymbolNode* pNode )
1821 mpResult = new SmRootSymbolNode( pNode->GetToken( ) );
1822 CloneNodeAttr( pNode, mpResult );
1825 void SmCloningVisitor::Visit( SmRectangleNode* pNode )
1827 mpResult = new SmRectangleNode( pNode->GetToken( ) );
1828 CloneNodeAttr( pNode, mpResult );
1831 void SmCloningVisitor::Visit( SmVerticalBraceNode* pNode )
1833 SmVerticalBraceNode* pClone = new SmVerticalBraceNode( pNode->GetToken( ) );
1834 CloneNodeAttr( pNode, pClone );
1835 CloneKids( pNode, pClone );
1836 mpResult = pClone;
1839 // SmSelectionDrawingVisitor
1841 SmSelectionDrawingVisitor::SmSelectionDrawingVisitor( OutputDevice& rDevice, SmNode* pTree, const Point& rOffset )
1842 : mrDev( rDevice )
1843 , mbHasSelectionArea( false )
1845 //Visit everything
1846 SAL_WARN_IF( !pTree, "starmath", "pTree can't be null!" );
1847 if( pTree )
1848 pTree->Accept( this );
1850 //Draw selection if there's any
1851 if( !mbHasSelectionArea ) return;
1853 maSelectionArea.Move( rOffset.X( ), rOffset.Y( ) );
1855 //Save device state
1856 mrDev.Push( PushFlags::LINECOLOR | PushFlags::FILLCOLOR );
1857 //Change colors
1858 mrDev.SetLineColor( );
1859 mrDev.SetFillColor( COL_LIGHTGRAY );
1861 //Draw rectangle
1862 mrDev.DrawRect( maSelectionArea );
1864 //Restore device state
1865 mrDev.Pop( );
1868 void SmSelectionDrawingVisitor::ExtendSelectionArea(const tools::Rectangle& rArea)
1870 if ( ! mbHasSelectionArea ) {
1871 maSelectionArea = rArea;
1872 mbHasSelectionArea = true;
1873 } else
1874 maSelectionArea.Union(rArea);
1877 void SmSelectionDrawingVisitor::DefaultVisit( SmNode* pNode )
1879 if( pNode->IsSelected( ) )
1880 ExtendSelectionArea( pNode->AsRectangle( ) );
1881 VisitChildren( pNode );
1884 void SmSelectionDrawingVisitor::VisitChildren( SmNode* pNode )
1886 if(pNode->GetNumSubNodes() == 0)
1887 return;
1888 for( auto pChild : *static_cast<SmStructureNode*>(pNode) )
1890 if(!pChild)
1891 continue;
1892 pChild->Accept( this );
1896 void SmSelectionDrawingVisitor::Visit( SmTextNode* pNode )
1898 if( !pNode->IsSelected())
1899 return;
1901 mrDev.Push( PushFlags::TEXTCOLOR | PushFlags::FONT );
1903 mrDev.SetFont( pNode->GetFont( ) );
1904 Point Position = pNode->GetTopLeft( );
1905 tools::Long left = Position.getX( ) + mrDev.GetTextWidth( pNode->GetText( ), 0, pNode->GetSelectionStart( ) );
1906 tools::Long right = Position.getX( ) + mrDev.GetTextWidth( pNode->GetText( ), 0, pNode->GetSelectionEnd( ) );
1907 tools::Long top = Position.getY( );
1908 tools::Long bottom = top + pNode->GetHeight( );
1909 tools::Rectangle rect( left, top, right, bottom );
1911 ExtendSelectionArea( rect );
1913 mrDev.Pop( );
1916 // SmNodeToTextVisitor
1918 SmNodeToTextVisitor::SmNodeToTextVisitor( SmNode* pNode, OUString &rText )
1920 pNode->Accept( this );
1921 maCmdText.stripEnd(' ');
1922 rText = maCmdText.makeStringAndClear();
1925 void SmNodeToTextVisitor::Visit( SmTableNode* pNode )
1927 if( pNode->GetToken( ).eType == TBINOM ) {
1928 Append( "{ binom" );
1929 LineToText( pNode->GetSubNode( 0 ) );
1930 LineToText( pNode->GetSubNode( 1 ) );
1931 Append("} ");
1932 } else if( pNode->GetToken( ).eType == TSTACK ) {
1933 Append( "stack{ " );
1934 bool bFirst = true;
1935 for( auto pChild : *pNode )
1937 if(!pChild)
1938 continue;
1939 if(bFirst)
1940 bFirst = false;
1941 else
1943 Separate( );
1944 Append( "# " );
1946 LineToText( pChild );
1948 Separate( );
1949 Append( "}" );
1950 } else { //Assume it's a toplevel table, containing lines
1951 bool bFirst = true;
1952 for( auto pChild : *pNode )
1954 if(!pChild)
1955 continue;
1956 if(bFirst)
1957 bFirst = false;
1958 else
1960 Separate( );
1961 Append( "newline" );
1963 Separate( );
1964 pChild->Accept( this );
1969 void SmNodeToTextVisitor::Visit( SmBraceNode* pNode )
1971 if ( pNode->GetToken().eType == TEVALUATE )
1973 SmNode *pBody = pNode->Body();
1974 Append( "evaluate { " );
1975 pBody->Accept( this );
1976 Append("} ");
1978 else{
1979 SmNode *pLeftBrace = pNode->OpeningBrace(),
1980 *pBody = pNode->Body(),
1981 *pRightBrace = pNode->ClosingBrace();
1982 //Handle special case where it's absolute function
1983 if( pNode->GetToken( ).eType == TABS ) {
1984 Append( "abs" );
1985 LineToText( pBody );
1986 } else {
1987 if( pNode->GetScaleMode( ) == SmScaleMode::Height )
1988 Append( "left " );
1989 pLeftBrace->Accept( this );
1990 Separate( );
1991 pBody->Accept( this );
1992 Separate( );
1993 if( pNode->GetScaleMode( ) == SmScaleMode::Height )
1994 Append( "right " );
1995 pRightBrace->Accept( this );
2000 void SmNodeToTextVisitor::Visit( SmBracebodyNode* pNode )
2002 for( auto pChild : *pNode )
2004 if(!pChild)
2005 continue;
2006 Separate( );
2007 pChild->Accept( this );
2011 void SmNodeToTextVisitor::Visit( SmOperNode* pNode )
2013 Append( pNode->GetToken( ).aText );
2014 Separate( );
2015 if( pNode->GetToken( ).eType == TOPER ){
2016 //There's an SmGlyphSpecialNode if eType == TOPER
2017 if( pNode->GetSubNode( 0 )->GetType( ) == SmNodeType::SubSup )
2018 Append( pNode->GetSubNode( 0 )->GetSubNode( 0 )->GetToken( ).aText );
2019 else
2020 Append( pNode->GetSubNode( 0 )->GetToken( ).aText );
2022 if( pNode->GetSubNode( 0 )->GetType( ) == SmNodeType::SubSup ) {
2023 SmSubSupNode *pSubSup = static_cast<SmSubSupNode*>( pNode->GetSubNode( 0 ) );
2024 SmNode* pChild = pSubSup->GetSubSup( LSUP );
2025 if( pChild ) {
2026 Separate( );
2027 Append( "lsup { " );
2028 LineToText( pChild );
2029 Append( "} " );
2031 pChild = pSubSup->GetSubSup( LSUB );
2032 if( pChild ) {
2033 Separate( );
2034 Append( "lsub { " );
2035 LineToText( pChild );
2036 Append( "} " );
2038 pChild = pSubSup->GetSubSup( RSUP );
2039 if( pChild ) {
2040 Separate( );
2041 Append( "^ { " );
2042 LineToText( pChild );
2043 Append( "} " );
2045 pChild = pSubSup->GetSubSup( RSUB );
2046 if( pChild ) {
2047 Separate( );
2048 Append( "_ { " );
2049 LineToText( pChild );
2050 Append( "} " );
2052 pChild = pSubSup->GetSubSup( CSUP );
2053 if( pChild ) {
2054 Separate( );
2055 if (pSubSup->IsUseLimits())
2056 Append( "to { " );
2057 else
2058 Append( "csup { " );
2059 LineToText( pChild );
2060 Append( "} " );
2062 pChild = pSubSup->GetSubSup( CSUB );
2063 if( pChild ) {
2064 Separate( );
2065 if (pSubSup->IsUseLimits())
2066 Append( "from { " );
2067 else
2068 Append( "csub { " );
2069 LineToText( pChild );
2070 Append( "} " );
2073 LineToText( pNode->GetSubNode( 1 ) );
2076 void SmNodeToTextVisitor::Visit( SmAlignNode* pNode )
2078 Append( pNode->GetToken( ).aText );
2079 LineToText( pNode->GetSubNode( 0 ) );
2082 void SmNodeToTextVisitor::Visit( SmAttributNode* pNode )
2084 Append( pNode->GetToken( ).aText );
2085 LineToText( pNode->Body() );
2088 void SmNodeToTextVisitor::Visit( SmFontNode* pNode )
2090 sal_uInt32 nc;
2091 sal_uInt8 nr, ng, nb;
2092 std::unique_ptr<SmColorTokenTableEntry> aSmColorTokenTableEntry;
2093 switch ( pNode->GetToken( ).eType )
2095 case TBOLD:
2096 Append( "bold " );
2097 break;
2098 case TNBOLD:
2099 Append( "nbold " );
2100 break;
2101 case TITALIC:
2102 Append( "italic " );
2103 break;
2104 case TNITALIC:
2105 Append( "nitalic " );
2106 break;
2107 case TPHANTOM:
2108 Append( "phantom " );
2109 break;
2110 case TSIZE:
2112 Append( "size " );
2113 switch ( pNode->GetSizeType( ) )
2115 case FontSizeType::PLUS:
2116 Append( "+" );
2117 break;
2118 case FontSizeType::MINUS:
2119 Append( "-" );
2120 break;
2121 case FontSizeType::MULTIPLY:
2122 Append( "*" );
2123 break;
2124 case FontSizeType::DIVIDE:
2125 Append( "/" );
2126 break;
2127 case FontSizeType::ABSOLUT:
2128 default:
2129 break;
2131 Append( ::rtl::math::doubleToUString(
2132 static_cast<double>( pNode->GetSizeParameter( ) ),
2133 rtl_math_StringFormat_Automatic,
2134 rtl_math_DecimalPlaces_Max, '.', true ) );
2135 Separate( );
2137 break;
2139 case TDVIPSNAMESCOL:
2140 Append( "color dvip " );
2141 nc = pNode->GetToken().aText.toUInt32(16);
2142 aSmColorTokenTableEntry = starmathdatabase::Identify_Color_Parser( nc );
2143 Append( aSmColorTokenTableEntry->pIdent );
2144 break;
2145 case THTMLCOL:
2146 case TMATHMLCOL:
2147 case TICONICCOL:
2148 Append( "color " );
2149 nc = pNode->GetToken().aText.toUInt32(16);
2150 aSmColorTokenTableEntry = starmathdatabase::Identify_Color_Parser( nc );
2151 Append( aSmColorTokenTableEntry->pIdent );
2152 break;
2153 case TRGB:
2154 nc = pNode->GetToken().aText.toUInt32(16);
2155 aSmColorTokenTableEntry = starmathdatabase::Identify_Color_Parser( nc );
2156 Append( "color rgb " );
2157 nb = nc % 256;
2158 nc /= 256;
2159 ng = nc % 256;
2160 nc /= 256;
2161 nr = nc % 256;
2162 Append(OUString::number(nr));
2163 Separate();
2164 Append(OUString::number(ng));
2165 Separate();
2166 Append(OUString::number(nb));
2167 Separate();
2168 break;
2169 case TRGBA:
2170 nc = pNode->GetToken().aText.toUInt32(16);
2171 aSmColorTokenTableEntry = starmathdatabase::Identify_Color_Parser( nc );
2172 Append( "color rgba " );
2173 nc = pNode->GetToken().aText.toUInt32(16);
2174 nb = nc % 256;
2175 nc /= 256;
2176 ng = nc % 256;
2177 nc /= 256;
2178 nr = nc % 256;
2179 nc /= 256;
2180 Append(OUString::number(nr));
2181 Separate();
2182 Append(OUString::number(ng));
2183 Separate();
2184 Append(OUString::number(nb));
2185 Separate();
2186 Append(OUString::number(nc));
2187 Separate();
2188 break;
2189 case THEX:
2190 nc = pNode->GetToken().aText.toUInt32(16);
2191 aSmColorTokenTableEntry = starmathdatabase::Identify_Color_Parser( nc );
2192 Append( "color hex " );
2193 nc = pNode->GetToken().aText.toUInt32(16);
2194 Append(OUString::number(nc,16));
2195 Separate();
2196 break;
2197 case TSANS:
2198 Append( "font sans " );
2199 break;
2200 case TSERIF:
2201 Append( "font serif " );
2202 break;
2203 case TFIXED:
2204 Append( "font fixed " );
2205 break;
2206 default:
2207 break;
2209 LineToText( pNode->GetSubNode( 1 ) );
2212 void SmNodeToTextVisitor::Visit( SmUnHorNode* pNode )
2214 if(pNode->GetSubNode( 1 )->GetToken( ).eType == TFACT)
2216 // visit children in the reverse order
2217 for( auto it = pNode->rbegin(); it != pNode->rend(); ++it )
2219 auto pChild = *it;
2220 if(!pChild)
2221 continue;
2222 Separate( );
2223 pChild->Accept( this );
2226 else
2228 for( auto pChild : *pNode )
2230 if(!pChild)
2231 continue;
2232 Separate( );
2233 pChild->Accept( this );
2238 void SmNodeToTextVisitor::Visit( SmBinHorNode* pNode )
2240 const SmNode *pParent = pNode->GetParent();
2241 bool bBraceNeeded = pParent && pParent->GetType() == SmNodeType::Font;
2242 SmNode *pLeft = pNode->LeftOperand(),
2243 *pOper = pNode->Symbol(),
2244 *pRight = pNode->RightOperand();
2245 Separate( );
2246 if (bBraceNeeded)
2247 Append( "{ " );
2248 pLeft->Accept( this );
2249 Separate( );
2250 pOper->Accept( this );
2251 Separate( );
2252 pRight->Accept( this );
2253 Separate( );
2254 if (bBraceNeeded)
2255 Append( "} " );
2258 void SmNodeToTextVisitor::Visit( SmBinVerNode* pNode )
2260 if( pNode->GetToken().eType == TOVER ){
2261 SmNode *pNum = pNode->GetSubNode( 0 ),
2262 *pDenom = pNode->GetSubNode( 2 );
2263 Append( "{ " );
2264 LineToText( pNum );
2265 Append( "over" );
2266 LineToText( pDenom );
2267 Append( "} " );
2268 } else{
2269 SmNode *pNum = pNode->GetSubNode( 0 ),
2270 *pDenom = pNode->GetSubNode( 2 );
2271 Append( "frac {" );
2272 LineToText( pNum );
2273 Append( "} {" );
2274 LineToText( pDenom );
2275 Append( "}" );
2279 void SmNodeToTextVisitor::Visit( SmBinDiagonalNode* pNode )
2281 SmNode *pLeftOperand = pNode->GetSubNode( 0 ),
2282 *pRightOperand = pNode->GetSubNode( 1 );
2283 Append( "{ " );
2284 LineToText( pLeftOperand );
2285 Separate( );
2286 Append( "wideslash " );
2287 LineToText( pRightOperand );
2288 Append( "} " );
2291 void SmNodeToTextVisitor::Visit( SmSubSupNode* pNode )
2293 if( pNode->GetToken().eType == TEVALUATE )
2295 Append("evaluate { ");
2296 pNode->GetSubNode( 0 )->GetSubNode( 1 )->Accept(this);
2297 Append("} ");
2298 SmNode* pChild = pNode->GetSubSup( RSUP );
2299 if( pChild ) {
2300 Separate( );
2301 Append( "to { " );
2302 LineToText( pChild );
2303 Append( "} " );
2305 pChild = pNode->GetSubSup( RSUB );
2306 if( pChild ) {
2307 Separate( );
2308 Append( "from { " );
2309 LineToText( pChild );
2310 Append( "} " );
2313 else
2315 LineToText( pNode->GetBody( ) );
2316 SmNode *pChild = pNode->GetSubSup( LSUP );
2317 if( pChild ) {
2318 Separate( );
2319 Append( "lsup " );
2320 LineToText( pChild );
2322 pChild = pNode->GetSubSup( LSUB );
2323 if( pChild ) {
2324 Separate( );
2325 Append( "lsub " );
2326 LineToText( pChild );
2328 pChild = pNode->GetSubSup( RSUP );
2329 if( pChild ) {
2330 Separate( );
2331 Append( "^ " );
2332 LineToText( pChild );
2334 pChild = pNode->GetSubSup( RSUB );
2335 if( pChild ) {
2336 Separate( );
2337 Append( "_ " );
2338 LineToText( pChild );
2340 pChild = pNode->GetSubSup( CSUP );
2341 if( pChild ) {
2342 Separate( );
2343 if (pNode->IsUseLimits())
2344 Append( "to " );
2345 else
2346 Append( "csup " );
2347 LineToText( pChild );
2349 pChild = pNode->GetSubSup( CSUB );
2350 if( pChild ) {
2351 Separate( );
2352 if (pNode->IsUseLimits())
2353 Append( "from " );
2354 else
2355 Append( "csub " );
2356 LineToText( pChild );
2361 void SmNodeToTextVisitor::Visit( SmMatrixNode* pNode )
2363 Append( "matrix{" );
2364 for (size_t i = 0; i < pNode->GetNumRows(); ++i)
2366 for (size_t j = 0; j < pNode->GetNumCols( ); ++j)
2368 SmNode* pSubNode = pNode->GetSubNode( i * pNode->GetNumCols( ) + j );
2369 Separate( );
2370 if (pSubNode)
2371 pSubNode->Accept( this );
2372 Separate( );
2373 if (j != pNode->GetNumCols() - 1U)
2374 Append( "#" );
2376 Separate( );
2377 if (i != pNode->GetNumRows() - 1U)
2378 Append( "##" );
2380 Append( "} " );
2383 void SmNodeToTextVisitor::Visit( SmPlaceNode* )
2385 Append( "<?>" );
2388 void SmNodeToTextVisitor::Visit( SmTextNode* pNode )
2390 SmTokenType type = pNode->GetToken( ).eType;
2391 switch(type){
2392 case TTEXT:
2393 Append( "\"" );
2394 Append( pNode->GetToken().aText );
2395 Append( "\"" );
2396 break;
2397 case TNUMBER:
2398 Append( pNode->GetToken().aText );
2399 break;
2400 case TIDENT:
2401 Append( pNode->GetToken().aText );
2402 break;
2403 case TFUNC:
2404 Append("func ");
2405 Append( pNode->GetToken().aText );
2406 break;
2407 case THEX:
2408 Append("hex ");
2409 Append( pNode->GetToken().aText );
2410 break;
2411 default:
2412 Append( pNode->GetToken().aText );
2414 Separate( );
2417 void SmNodeToTextVisitor::Visit( SmSpecialNode* pNode )
2419 SmTokenType type = pNode->GetToken().eType;
2420 switch(type){
2421 case TLIMSUP:
2422 Append("lim sup ");
2423 break;
2424 case TLIMINF:
2425 Append("lim inf ");
2426 break;
2427 default:
2428 Append( pNode->GetToken().aText );
2429 break;
2433 void SmNodeToTextVisitor::Visit( SmGlyphSpecialNode* pNode )
2435 if( pNode->GetToken( ).eType == TBOPER )
2436 Append( "boper " );
2437 else
2438 Append( "uoper " );
2439 Append( pNode->GetToken( ).aText );
2442 //TODO to improve this it is required to improve mathmlimport.
2443 void SmNodeToTextVisitor::Visit( SmMathSymbolNode* pNode )
2445 if ( ( pNode->GetToken().nGroup & TG::LBrace )
2446 || ( pNode->GetToken().nGroup & TG::RBrace )
2447 || ( pNode->GetToken().nGroup & TG::Sum )
2448 || ( pNode->GetToken().nGroup & TG::Product )
2449 || ( pNode->GetToken().nGroup & TG::Relation )
2450 || ( pNode->GetToken().nGroup & TG::UnOper )
2451 || ( pNode->GetToken().nGroup & TG::Oper )
2453 Append( pNode->GetToken().aText );
2454 return;
2456 sal_Unicode cChar = pNode->GetToken().cMathChar;
2457 Separate( );
2458 switch(cChar){
2459 case MS_NONE:
2460 Append("none");
2461 break;
2462 case '{':
2463 Append("{");
2464 break;
2465 case '}':
2466 Append("}");
2467 break;
2468 case MS_VERTLINE:
2469 Append("mline");
2470 break;
2471 case MS_TILDE:
2472 Append("\"~\"");
2473 break;
2474 case MS_RIGHTARROW:
2475 if( pNode->GetToken().eType == TTOWARD ) Append("toward");
2476 else Append("rightarrow");
2477 break;
2478 case MS_LEFTARROW:
2479 Append("leftarrow");
2480 break;
2481 case MS_UPARROW:
2482 Append("uparrow");
2483 break;
2484 case MS_DOWNARROW:
2485 Append("downarrow");
2486 break;
2487 case MS_LAMBDABAR:
2488 Append("lambdabar");
2489 break;
2490 case MS_DOTSLOW:
2491 Append("dotslow");
2492 break;
2493 case MS_SETC:
2494 Append("setC");
2495 break;
2496 case MS_HBAR:
2497 Append("hbar");
2498 break;
2499 case MS_IM:
2500 Append("Im");
2501 break;
2502 case MS_SETN:
2503 Append("setN");
2504 break;
2505 case MS_WP:
2506 Append("wp");
2507 break;
2508 case MS_LAPLACE:
2509 Append("laplace");
2510 break;
2511 case MS_SETQ:
2512 Append("setQ");
2513 break;
2514 case MS_RE:
2515 Append("Re");
2516 break;
2517 case MS_SETR:
2518 Append("setR");
2519 break;
2520 case MS_SETZ:
2521 Append("setZ");
2522 break;
2523 case MS_ALEPH:
2524 Append("aleph");
2525 break;
2526 case 0x0362:
2527 Append("widevec");
2528 break;
2529 case MS_DLARROW:
2530 Append("dlarrow");
2531 break;
2532 case MS_DRARROW:
2533 Append("drarrow");
2534 break;
2535 case MS_DLRARROW:
2536 Append("dlrarrow");
2537 break;
2538 case MS_FORALL:
2539 Append("forall");
2540 break;
2541 case MS_PARTIAL:
2542 Append("partial");
2543 break;
2544 case MS_EXISTS:
2545 Append("exists");
2546 break;
2547 case MS_NOTEXISTS:
2548 Append("notexists");
2549 break;
2550 case MS_EMPTYSET:
2551 Append("emptyset");
2552 break;
2553 case MS_NABLA:
2554 Append("nabla");
2555 break;
2556 case MS_BACKEPSILON:
2557 Append("backepsilon");
2558 break;
2559 case MS_CIRC:
2560 Append("circ");
2561 break;
2562 case MS_INFINITY:
2563 Append("infinity");
2564 break;
2565 case 0x22b2: // NORMAL SUBGROUP OF
2566 Append(OUStringChar(cChar));
2567 break;
2568 case 0x22b3: // CONTAINS AS NORMAL SUBGROUP
2569 Append(OUStringChar(cChar));
2570 break;
2571 case MS_ORTHO:
2572 Append("ortho");
2573 break;
2574 case MS_DOTSVERT:
2575 Append("dotsvert");
2576 break;
2577 case MS_DOTSAXIS:
2578 Append("dotsaxis");
2579 break;
2580 case MS_DOTSUP:
2581 Append("dotsup");
2582 break;
2583 case MS_DOTSDOWN:
2584 Append("dotsdown");
2585 break;
2586 case '^':
2587 Append("^");
2588 break;
2589 case 0xe091:
2590 Append("widehat");
2591 break;
2592 case 0xe096:
2593 Append("widetilde");
2594 break;
2595 case 0xe098:
2596 Append("widevec");
2597 break;
2598 case 0xeb01: //no space
2599 case 0xeb08: //normal space
2600 break;
2601 case 0xef04: //tiny space
2602 case 0xef05: //tiny space
2603 case 0xeb02: //small space
2604 case 0xeb04: //medium space
2605 Append("`");
2606 break;
2607 case 0xeb05: //large space
2608 Append("~");
2609 break;
2610 case 0x3a9:
2611 Append("%OMEGA");
2612 break;
2613 default:
2614 Append(OUStringChar(cChar));
2615 break;
2619 void SmNodeToTextVisitor::Visit( SmBlankNode* pNode )
2621 sal_uInt16 nNum = pNode->GetBlankNum();
2622 if (nNum <= 0)
2623 return;
2624 sal_uInt16 nWide = nNum / 4;
2625 sal_uInt16 nNarrow = nNum % 4;
2626 for (sal_uInt16 i = 0; i < nWide; i++)
2627 Append( "~" );
2628 for (sal_uInt16 i = 0; i < nNarrow; i++)
2629 Append( "`" );
2630 Append( " " );
2633 void SmNodeToTextVisitor::Visit( SmErrorNode* )
2637 void SmNodeToTextVisitor::Visit( SmLineNode* pNode )
2639 for( auto pChild : *pNode )
2641 if(!pChild)
2642 continue;
2643 Separate( );
2644 pChild->Accept( this );
2648 void SmNodeToTextVisitor::Visit( SmExpressionNode* pNode )
2650 bool bracketsNeeded = pNode->GetNumSubNodes() != 1 || pNode->GetSubNode(0)->GetType() == SmNodeType::BinHor;
2651 if (!bracketsNeeded)
2653 const SmNode *pParent = pNode->GetParent();
2654 // nested subsups
2655 bracketsNeeded =
2656 pParent && pParent->GetType() == SmNodeType::SubSup &&
2657 pNode->GetNumSubNodes() == 1 &&
2658 pNode->GetSubNode(0)->GetType() == SmNodeType::SubSup;
2661 if (bracketsNeeded) {
2662 Append( "{ " );
2664 for( auto pChild : *pNode )
2666 if(!pChild)
2667 continue;
2668 pChild->Accept( this );
2669 Separate( );
2671 if (bracketsNeeded) {
2672 Append( "} " );
2676 void SmNodeToTextVisitor::Visit( SmPolyLineNode* )
2680 void SmNodeToTextVisitor::Visit( SmRootNode* pNode )
2682 SmNode *pExtra = pNode->GetSubNode( 0 ),
2683 *pBody = pNode->GetSubNode( 2 );
2684 if( pExtra ) {
2685 Append( "nroot" );
2686 LineToText( pExtra );
2687 } else
2688 Append( "sqrt" );
2689 LineToText( pBody );
2692 void SmNodeToTextVisitor::Visit( SmRootSymbolNode* )
2696 void SmNodeToTextVisitor::Visit( SmRectangleNode* )
2700 void SmNodeToTextVisitor::Visit( SmVerticalBraceNode* pNode )
2702 SmNode *pBody = pNode->Body(),
2703 *pScript = pNode->Script();
2704 LineToText( pBody );
2705 Append( pNode->GetToken( ).aText );
2706 LineToText( pScript );
2709 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */