Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / starmath / source / visitors.cxx
bloba15a62a4b04811776d204f54fa8a339e8ca3ce3d
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>
18 #include <starmathdatabase.hxx>
20 // SmDefaultingVisitor
22 void SmDefaultingVisitor::Visit( SmTableNode* pNode )
24 DefaultVisit( pNode );
27 void SmDefaultingVisitor::Visit( SmBraceNode* pNode )
29 DefaultVisit( pNode );
32 void SmDefaultingVisitor::Visit( SmBracebodyNode* pNode )
34 DefaultVisit( pNode );
37 void SmDefaultingVisitor::Visit( SmOperNode* pNode )
39 DefaultVisit( pNode );
42 void SmDefaultingVisitor::Visit( SmAlignNode* pNode )
44 DefaultVisit( pNode );
47 void SmDefaultingVisitor::Visit( SmAttributeNode* pNode )
49 DefaultVisit( pNode );
52 void SmDefaultingVisitor::Visit( SmFontNode* pNode )
54 DefaultVisit( pNode );
57 void SmDefaultingVisitor::Visit( SmUnHorNode* pNode )
59 DefaultVisit( pNode );
62 void SmDefaultingVisitor::Visit( SmBinHorNode* pNode )
64 DefaultVisit( pNode );
67 void SmDefaultingVisitor::Visit( SmBinVerNode* pNode )
69 DefaultVisit( pNode );
72 void SmDefaultingVisitor::Visit( SmBinDiagonalNode* pNode )
74 DefaultVisit( pNode );
77 void SmDefaultingVisitor::Visit( SmSubSupNode* pNode )
79 DefaultVisit( pNode );
82 void SmDefaultingVisitor::Visit( SmMatrixNode* pNode )
84 DefaultVisit( pNode );
87 void SmDefaultingVisitor::Visit( SmPlaceNode* pNode )
89 DefaultVisit( pNode );
92 void SmDefaultingVisitor::Visit( SmTextNode* pNode )
94 DefaultVisit( pNode );
97 void SmDefaultingVisitor::Visit( SmSpecialNode* pNode )
99 DefaultVisit( pNode );
102 void SmDefaultingVisitor::Visit( SmGlyphSpecialNode* pNode )
104 DefaultVisit( pNode );
107 void SmDefaultingVisitor::Visit( SmMathSymbolNode* pNode )
109 DefaultVisit( pNode );
112 void SmDefaultingVisitor::Visit( SmBlankNode* pNode )
114 DefaultVisit( pNode );
117 void SmDefaultingVisitor::Visit( SmErrorNode* pNode )
119 DefaultVisit( pNode );
122 void SmDefaultingVisitor::Visit( SmLineNode* pNode )
124 DefaultVisit( pNode );
127 void SmDefaultingVisitor::Visit( SmExpressionNode* pNode )
129 DefaultVisit( pNode );
132 void SmDefaultingVisitor::Visit( SmPolyLineNode* pNode )
134 DefaultVisit( pNode );
137 void SmDefaultingVisitor::Visit( SmRootNode* pNode )
139 DefaultVisit( pNode );
142 void SmDefaultingVisitor::Visit( SmRootSymbolNode* pNode )
144 DefaultVisit( pNode );
147 void SmDefaultingVisitor::Visit( SmRectangleNode* pNode )
149 DefaultVisit( pNode );
152 void SmDefaultingVisitor::Visit( SmVerticalBraceNode* pNode )
154 DefaultVisit( pNode );
157 // SmCaretLinesVisitor
159 SmCaretLinesVisitor::SmCaretLinesVisitor(OutputDevice& rDevice, SmCaretPos position, Point offset)
160 : mrDev(rDevice)
161 , maPos(position)
162 , maOffset(offset)
166 void SmCaretLinesVisitor::DoIt()
168 SAL_WARN_IF(!maPos.IsValid(), "starmath", "Cannot draw invalid position!");
169 if (!maPos.IsValid())
170 return;
172 //Save device state
173 mrDev.Push( vcl::PushFlags::FONT | vcl::PushFlags::MAPMODE | vcl::PushFlags::LINECOLOR | vcl::PushFlags::FILLCOLOR | vcl::PushFlags::TEXTCOLOR );
175 maPos.pSelectedNode->Accept( this );
176 //Restore device state
177 mrDev.Pop( );
180 void SmCaretLinesVisitor::Visit( SmTextNode* pNode )
182 tools::Long i = maPos.nIndex;
184 mrDev.SetFont( pNode->GetFont( ) );
186 //Find the line
187 SmNode* pLine = SmCursor::FindTopMostNodeInLine( pNode );
189 //Find coordinates
190 tools::Long left = pNode->GetLeft( ) + mrDev.GetTextWidth( pNode->GetText( ), 0, i ) + maOffset.X( );
191 tools::Long top = pLine->GetTop( ) + maOffset.Y( );
192 tools::Long height = pLine->GetHeight( );
193 tools::Long left_line = pLine->GetLeft( ) + maOffset.X( );
194 tools::Long right_line = pLine->GetRight( ) + maOffset.X( );
196 // Vertical line
197 ProcessCaretLine({ left, top }, { left, top + height });
199 // Underline
200 ProcessUnderline({ left_line, top + height }, { right_line, top + height });
203 void SmCaretLinesVisitor::DefaultVisit( SmNode* pNode )
205 //Find the line
206 SmNode* pLine = SmCursor::FindTopMostNodeInLine( pNode );
208 //Find coordinates
209 tools::Long left = pNode->GetLeft( ) + maOffset.X( ) + ( maPos.nIndex == 1 ? pNode->GetWidth( ) : 0 );
210 tools::Long top = pLine->GetTop( ) + maOffset.Y( );
211 tools::Long height = pLine->GetHeight( );
212 tools::Long left_line = pLine->GetLeft( ) + maOffset.X( );
213 tools::Long right_line = pLine->GetRight( ) + maOffset.X( );
215 // Vertical line
216 ProcessCaretLine({ left, top }, { left, top + height });
218 // Underline
219 ProcessUnderline({ left_line, top + height }, { right_line, top + height });
222 // SmCaretRectanglesVisitor
224 SmCaretRectanglesVisitor::SmCaretRectanglesVisitor(OutputDevice& rDevice, SmCaretPos position)
225 : SmCaretLinesVisitor(rDevice, position, {})
227 DoIt();
230 void SmCaretRectanglesVisitor::ProcessCaretLine(Point from, Point to) { maCaret = { from, to }; }
231 void SmCaretRectanglesVisitor::ProcessUnderline(Point /*from*/, Point /*to*/) {} // No underline
233 // SmCaretDrawingVisitor
235 SmCaretDrawingVisitor::SmCaretDrawingVisitor( OutputDevice& rDevice,
236 SmCaretPos position,
237 Point offset,
238 bool caretVisible )
239 : SmCaretLinesVisitor(rDevice, position, offset)
240 , mbCaretVisible( caretVisible )
242 DoIt();
245 void SmCaretDrawingVisitor::ProcessCaretLine(Point from, Point to)
247 if ( mbCaretVisible ) {
248 //Set color
249 getDev().SetLineColor(COL_BLACK);
250 //Draw vertical line
251 getDev().DrawLine(from, to);
255 void SmCaretDrawingVisitor::ProcessUnderline(Point from, Point to)
257 //Set color
258 getDev().SetLineColor(COL_BLACK);
259 //Underline the line
260 getDev().DrawLine(from, to);
263 // SmCaretPos2LineVisitor
265 void SmCaretPos2LineVisitor::Visit( SmTextNode* pNode )
267 //Save device state
268 mpDev->Push( vcl::PushFlags::FONT | vcl::PushFlags::TEXTCOLOR );
270 tools::Long i = maPos.nIndex;
272 mpDev->SetFont( pNode->GetFont( ) );
274 //Find coordinates
275 tools::Long left = pNode->GetLeft( ) + mpDev->GetTextWidth( pNode->GetText( ), 0, i );
276 tools::Long top = pNode->GetTop( );
277 tools::Long height = pNode->GetHeight( );
279 maLine = SmCaretLine( left, top, height );
281 //Restore device state
282 mpDev->Pop( );
285 void SmCaretPos2LineVisitor::DefaultVisit( SmNode* pNode )
287 //Vertical line ( code from SmCaretDrawingVisitor )
288 Point p1 = pNode->GetTopLeft( );
289 if( maPos.nIndex == 1 )
290 p1.Move( pNode->GetWidth( ), 0 );
292 maLine = SmCaretLine( p1.X( ), p1.Y( ), pNode->GetHeight( ) );
296 // SmDrawingVisitor
298 void SmDrawingVisitor::Visit( SmTableNode* pNode )
300 DrawChildren( pNode );
303 void SmDrawingVisitor::Visit( SmBraceNode* pNode )
305 DrawChildren( pNode );
308 void SmDrawingVisitor::Visit( SmBracebodyNode* pNode )
310 DrawChildren( pNode );
313 void SmDrawingVisitor::Visit( SmOperNode* pNode )
315 DrawChildren( pNode );
318 void SmDrawingVisitor::Visit( SmAlignNode* pNode )
320 DrawChildren( pNode );
323 void SmDrawingVisitor::Visit( SmAttributeNode* pNode )
325 DrawChildren( pNode );
328 void SmDrawingVisitor::Visit( SmFontNode* pNode )
330 DrawChildren( pNode );
333 void SmDrawingVisitor::Visit( SmUnHorNode* pNode )
335 DrawChildren( pNode );
338 void SmDrawingVisitor::Visit( SmBinHorNode* pNode )
340 DrawChildren( pNode );
343 void SmDrawingVisitor::Visit( SmBinVerNode* pNode )
345 DrawChildren( pNode );
348 void SmDrawingVisitor::Visit( SmBinDiagonalNode* pNode )
350 DrawChildren( pNode );
353 void SmDrawingVisitor::Visit( SmSubSupNode* pNode )
355 DrawChildren( pNode );
358 void SmDrawingVisitor::Visit( SmMatrixNode* pNode )
360 DrawChildren( pNode );
363 void SmDrawingVisitor::Visit( SmPlaceNode* pNode )
365 DrawSpecialNode( pNode );
368 void SmDrawingVisitor::Visit( SmTextNode* pNode )
370 DrawTextNode( pNode );
373 void SmDrawingVisitor::Visit( SmSpecialNode* pNode )
375 DrawSpecialNode( pNode );
378 void SmDrawingVisitor::Visit( SmGlyphSpecialNode* pNode )
380 DrawSpecialNode( pNode );
383 void SmDrawingVisitor::Visit( SmMathSymbolNode* pNode )
385 DrawSpecialNode( pNode );
388 void SmDrawingVisitor::Visit( SmBlankNode* )
392 void SmDrawingVisitor::Visit( SmErrorNode* pNode )
394 DrawSpecialNode( pNode );
397 void SmDrawingVisitor::Visit( SmLineNode* pNode )
399 DrawChildren( pNode );
402 void SmDrawingVisitor::Visit( SmExpressionNode* pNode )
404 DrawChildren( pNode );
407 void SmDrawingVisitor::Visit( SmRootNode* pNode )
409 DrawChildren( pNode );
412 void SmDrawingVisitor::Visit( SmVerticalBraceNode* pNode )
414 DrawChildren( pNode );
417 void SmDrawingVisitor::Visit( SmRootSymbolNode* pNode )
419 if ( pNode->IsPhantom( ) )
420 return;
422 // draw root-sign itself
423 DrawSpecialNode( pNode );
425 SmTmpDevice aTmpDev( mrDev, true );
426 aTmpDev.SetFillColor( pNode->GetFont( ).GetColor( ) );
427 mrDev.SetLineColor( );
428 aTmpDev.SetFont( pNode->GetFont( ) );
430 // since the width is always unscaled it corresponds to the _original_
431 // _unscaled_ font height to be used, we use that to calculate the
432 // bar height. Thus it is independent of the arguments height.
433 // ( see display of sqrt QQQ versus sqrt stack{Q#Q#Q#Q} )
434 tools::Long nBarHeight = pNode->GetWidth( ) * 7 / 100;
435 tools::Long nBarWidth = pNode->GetBodyWidth( ) + pNode->GetBorderWidth( );
436 Point aBarOffset( pNode->GetWidth( ), +pNode->GetBorderWidth( ) );
437 Point aBarPos( maPosition + aBarOffset );
439 tools::Rectangle aBar( aBarPos, Size( nBarWidth, nBarHeight ) );
440 //! avoid GROWING AND SHRINKING of drawn rectangle when constantly
441 //! increasing zoomfactor.
442 // This is done by shifting its output-position to a point that
443 // corresponds exactly to a pixel on the output device.
444 Point aDrawPos( mrDev.PixelToLogic( mrDev.LogicToPixel( aBar.TopLeft( ) ) ) );
445 aBar.SetPos( aDrawPos );
447 mrDev.DrawRect( aBar );
450 void SmDrawingVisitor::Visit( SmPolyLineNode* pNode )
452 if ( pNode->IsPhantom( ) )
453 return;
455 tools::Long nBorderwidth = pNode->GetFont( ).GetBorderWidth( );
457 LineInfo aInfo;
458 aInfo.SetWidth( pNode->GetWidth( ) - 2 * nBorderwidth );
460 Point aOffset ( Point( ) - pNode->GetPolygon( ).GetBoundRect( ).TopLeft( )
461 + Point( nBorderwidth, nBorderwidth ) ),
462 aPos ( maPosition + aOffset );
463 pNode->GetPolygon( ).Move( aPos.X( ), aPos.Y( ) ); //Works because Polygon wraps a pointer
465 SmTmpDevice aTmpDev ( mrDev, false );
466 aTmpDev.SetLineColor( pNode->GetFont( ).GetColor( ) );
468 mrDev.DrawPolyLine( pNode->GetPolygon( ), aInfo );
471 void SmDrawingVisitor::Visit( SmRectangleNode* pNode )
473 if ( pNode->IsPhantom( ) )
474 return;
476 SmTmpDevice aTmpDev ( mrDev, false );
477 aTmpDev.SetFillColor( pNode->GetFont( ).GetColor( ) );
478 mrDev.SetLineColor( );
479 aTmpDev.SetFont( pNode->GetFont( ) );
481 sal_uLong nTmpBorderWidth = pNode->GetFont( ).GetBorderWidth( );
483 // get rectangle and remove borderspace
484 tools::Rectangle aTmp ( pNode->AsRectangle( ) + maPosition - pNode->GetTopLeft( ) );
485 aTmp.AdjustLeft(nTmpBorderWidth );
486 aTmp.AdjustRight( -sal_Int32(nTmpBorderWidth) );
487 aTmp.AdjustTop(nTmpBorderWidth );
488 aTmp.AdjustBottom( -sal_Int32(nTmpBorderWidth) );
490 SAL_WARN_IF( aTmp.IsEmpty(), "starmath", "Empty rectangle" );
492 //! avoid GROWING AND SHRINKING of drawn rectangle when constantly
493 //! increasing zoomfactor.
494 // This is done by shifting its output-position to a point that
495 // corresponds exactly to a pixel on the output device.
496 Point aPos ( mrDev.PixelToLogic( mrDev.LogicToPixel( aTmp.TopLeft( ) ) ) );
497 aTmp.SetPos( aPos );
499 mrDev.DrawRect( aTmp );
502 void SmDrawingVisitor::DrawTextNode( SmTextNode* pNode )
504 if ( pNode->IsPhantom() || pNode->GetText().isEmpty() || pNode->GetText()[0] == '\0' )
505 return;
507 SmTmpDevice aTmpDev ( mrDev, false );
508 aTmpDev.SetFont( pNode->GetFont( ) );
510 Point aPos ( maPosition );
511 aPos.AdjustY(pNode->GetBaselineOffset( ) );
512 // round to pixel coordinate
513 aPos = mrDev.PixelToLogic( mrDev.LogicToPixel( aPos ) );
515 mrDev.DrawStretchText( aPos, pNode->GetWidth( ), pNode->GetText( ) );
518 void SmDrawingVisitor::DrawSpecialNode( SmSpecialNode* pNode )
520 //! since this chars might come from any font, that we may not have
521 //! set to ALIGN_BASELINE yet, we do it now.
522 pNode->GetFont( ).SetAlignment( ALIGN_BASELINE );
524 DrawTextNode( pNode );
527 void SmDrawingVisitor::DrawChildren( SmStructureNode* pNode )
529 if ( pNode->IsPhantom( ) )
530 return;
532 Point rPosition = maPosition;
534 for( auto pChild : *pNode )
536 if(!pChild)
537 continue;
538 Point aOffset ( pChild->GetTopLeft( ) - pNode->GetTopLeft( ) );
539 maPosition = rPosition + aOffset;
540 pChild->Accept( this );
544 // SmSetSelectionVisitor
546 SmSetSelectionVisitor::SmSetSelectionVisitor( SmCaretPos startPos, SmCaretPos endPos, SmNode* pTree)
547 : maStartPos(startPos)
548 , maEndPos(endPos)
549 , mbSelecting(false)
551 //Assume that pTree is a SmTableNode
552 SAL_WARN_IF(pTree->GetType() != SmNodeType::Table, "starmath", "pTree should be a SmTableNode!");
553 //Visit root node, this is special as this node cannot be selected, but its children can!
554 if(pTree->GetType() == SmNodeType::Table){
555 //Change state if maStartPos is in front of this node
556 if( maStartPos.pSelectedNode == pTree && maStartPos.nIndex == 0 )
557 mbSelecting = !mbSelecting;
558 //Change state if maEndPos is in front of this node
559 if( maEndPos.pSelectedNode == pTree && maEndPos.nIndex == 0 )
560 mbSelecting = !mbSelecting;
561 SAL_WARN_IF(mbSelecting, "starmath", "Caret positions needed to set mbSelecting about, shouldn't be possible!");
563 //Visit lines
564 for( auto pChild : *static_cast<SmStructureNode*>(pTree) )
566 if(!pChild)
567 continue;
568 pChild->Accept( this );
569 //If we started a selection in this line and it haven't ended, we do that now!
570 if(mbSelecting) {
571 mbSelecting = false;
572 SetSelectedOnAll(pChild);
573 //Set maStartPos and maEndPos to invalid positions, this ensures that an unused
574 //start or end (because we forced end above), doesn't start a new selection.
575 maStartPos = maEndPos = SmCaretPos();
578 //Check if pTree isn't selected
579 SAL_WARN_IF(pTree->IsSelected(), "starmath", "pTree should never be selected!");
580 //Discard the selection if there's a bug (it's better than crashing)
581 if(pTree->IsSelected())
582 SetSelectedOnAll(pTree, false);
583 }else //This shouldn't happen, but I don't see any reason to die if it does
584 pTree->Accept(this);
587 void SmSetSelectionVisitor::SetSelectedOnAll( SmNode* pSubTree, bool IsSelected ) {
588 pSubTree->SetSelected( IsSelected );
590 if(pSubTree->GetNumSubNodes() == 0)
591 return;
592 //Quick BFS to set all selections
593 for( auto pChild : *static_cast<SmStructureNode*>(pSubTree) )
595 if(!pChild)
596 continue;
597 SetSelectedOnAll( pChild, IsSelected );
601 void SmSetSelectionVisitor::DefaultVisit( SmNode* pNode ) {
602 //Change state if maStartPos is in front of this node
603 if( maStartPos.pSelectedNode == pNode && maStartPos.nIndex == 0 )
604 mbSelecting = !mbSelecting;
605 //Change state if maEndPos is in front of this node
606 if( maEndPos.pSelectedNode == pNode && maEndPos.nIndex == 0 )
607 mbSelecting = !mbSelecting;
609 //Cache current state
610 bool WasSelecting = mbSelecting;
611 bool ChangedState = false;
613 //Set selected
614 pNode->SetSelected( mbSelecting );
616 //Visit children
617 if(pNode->GetNumSubNodes() > 0)
619 for( auto pChild : *static_cast<SmStructureNode*>(pNode) )
621 if(!pChild)
622 continue;
623 pChild->Accept( this );
624 ChangedState = ( WasSelecting != mbSelecting ) || ChangedState;
628 //If state changed
629 if( ChangedState )
631 //Select this node and all of its children
632 //(Make exception for SmBracebodyNode)
633 if( pNode->GetType() != SmNodeType::Bracebody ||
634 !pNode->GetParent() ||
635 pNode->GetParent()->GetType() != SmNodeType::Brace )
636 SetSelectedOnAll( pNode );
637 else
638 SetSelectedOnAll( pNode->GetParent() );
639 /* If the equation is: sqrt{2 + 4} + 5
640 * And the selection is: sqrt{2 + [4} +] 5
641 * Where [ denotes maStartPos and ] denotes maEndPos
642 * Then the sqrt node should be selected, so that the
643 * effective selection is: [sqrt{2 + 4} +] 5
644 * The same is the case if we swap maStartPos and maEndPos.
648 //Change state if maStartPos is after this node
649 if( maStartPos.pSelectedNode == pNode && maStartPos.nIndex == 1 )
651 mbSelecting = !mbSelecting;
653 //Change state if maEndPos is after of this node
654 if( maEndPos.pSelectedNode == pNode && maEndPos.nIndex == 1 )
656 mbSelecting = !mbSelecting;
660 void SmSetSelectionVisitor::VisitCompositionNode( SmStructureNode* pNode )
662 //Change state if maStartPos is in front of this node
663 if( maStartPos.pSelectedNode == pNode && maStartPos.nIndex == 0 )
664 mbSelecting = !mbSelecting;
665 //Change state if maEndPos is in front of this node
666 if( maEndPos.pSelectedNode == pNode && maEndPos.nIndex == 0 )
667 mbSelecting = !mbSelecting;
669 //Cache current state
670 bool WasSelecting = mbSelecting;
672 //Visit children
673 for( auto pChild : *pNode )
675 if(!pChild)
676 continue;
677 pChild->Accept( this );
680 //Set selected, if everything was selected
681 pNode->SetSelected( WasSelecting && mbSelecting );
683 //Change state if maStartPos is after this node
684 if( maStartPos.pSelectedNode == pNode && maStartPos.nIndex == 1 )
685 mbSelecting = !mbSelecting;
686 //Change state if maEndPos is after of this node
687 if( maEndPos.pSelectedNode == pNode && maEndPos.nIndex == 1 )
688 mbSelecting = !mbSelecting;
691 void SmSetSelectionVisitor::Visit( SmTextNode* pNode ) {
692 tools::Long i1 = -1,
693 i2 = -1;
694 if( maStartPos.pSelectedNode == pNode )
695 i1 = maStartPos.nIndex;
696 if( maEndPos.pSelectedNode == pNode )
697 i2 = maEndPos.nIndex;
699 tools::Long start, end;
700 pNode->SetSelected(true);
701 if( i1 != -1 && i2 != -1 ) {
702 start = std::min(i1, i2);
703 end = std::max(i1, i2);
704 } else if( mbSelecting && i1 != -1 ) {
705 start = 0;
706 end = i1;
707 mbSelecting = false;
708 } else if( mbSelecting && i2 != -1 ) {
709 start = 0;
710 end = i2;
711 mbSelecting = false;
712 } else if( !mbSelecting && i1 != -1 ) {
713 start = i1;
714 end = pNode->GetText().getLength();
715 mbSelecting = true;
716 } else if( !mbSelecting && i2 != -1 ) {
717 start = i2;
718 end = pNode->GetText().getLength();
719 mbSelecting = true;
720 } else if( mbSelecting ) {
721 start = 0;
722 end = pNode->GetText().getLength();
723 } else {
724 pNode->SetSelected( false );
725 start = 0;
726 end = 0;
728 pNode->SetSelected( start != end );
729 pNode->SetSelectionStart( start );
730 pNode->SetSelectionEnd( end );
733 void SmSetSelectionVisitor::Visit( SmExpressionNode* pNode ) {
734 VisitCompositionNode( pNode );
737 void SmSetSelectionVisitor::Visit( SmLineNode* pNode ) {
738 VisitCompositionNode( pNode );
741 void SmSetSelectionVisitor::Visit( SmAlignNode* pNode ) {
742 VisitCompositionNode( pNode );
745 void SmSetSelectionVisitor::Visit( SmBinHorNode* pNode ) {
746 VisitCompositionNode( pNode );
749 void SmSetSelectionVisitor::Visit( SmUnHorNode* pNode ) {
750 VisitCompositionNode( pNode );
753 void SmSetSelectionVisitor::Visit( SmFontNode* pNode ) {
754 VisitCompositionNode( pNode );
757 // SmCaretPosGraphBuildingVisitor
759 SmCaretPosGraphBuildingVisitor::SmCaretPosGraphBuildingVisitor( SmNode* pRootNode )
760 : mpRightMost(nullptr)
761 , mpGraph(new SmCaretPosGraph)
763 //pRootNode should always be a table
764 SAL_WARN_IF( pRootNode->GetType( ) != SmNodeType::Table, "starmath", "pRootNode must be a table node");
765 //Handle the special case where SmNodeType::Table is used a rootnode
766 if( pRootNode->GetType( ) == SmNodeType::Table ){
767 //Children are SmLineNodes
768 //Or so I thought... Apparently, the children can be instances of SmExpression
769 //especially if there's an error in the formula... So here we go, a simple work around.
770 for( auto pChild : *static_cast<SmStructureNode*>(pRootNode) )
772 if(!pChild)
773 continue;
774 mpRightMost = mpGraph->Add( SmCaretPos( pChild, 0 ) );
775 pChild->Accept( this );
777 }else
778 pRootNode->Accept(this);
781 SmCaretPosGraphBuildingVisitor::~SmCaretPosGraphBuildingVisitor()
785 void SmCaretPosGraphBuildingVisitor::Visit( SmLineNode* pNode ){
786 for( auto pChild : *pNode )
788 if(!pChild)
789 continue;
790 pChild->Accept( this );
794 /** Build SmCaretPosGraph for SmTableNode
795 * This method covers cases where SmTableNode is used in a binom or stack,
796 * the special case where it is used as root node for the entire formula is
797 * handled in the constructor.
799 void SmCaretPosGraphBuildingVisitor::Visit( SmTableNode* pNode ){
800 SmCaretPosGraphEntry *left = mpRightMost,
801 *right = mpGraph->Add( SmCaretPos( pNode, 1) );
802 bool bIsFirst = true;
803 for( auto pChild : *pNode )
805 if(!pChild)
806 continue;
807 mpRightMost = mpGraph->Add( SmCaretPos( pChild, 0 ), left);
808 if(bIsFirst)
809 left->SetRight(mpRightMost);
810 pChild->Accept( this );
811 mpRightMost->SetRight(right);
812 if(bIsFirst)
813 right->SetLeft(mpRightMost);
814 bIsFirst = false;
816 mpRightMost = right;
819 /** Build SmCaretPosGraph for SmSubSupNode
821 * The child positions in a SubSupNode, where H is the body:
822 * \code
823 * CSUP
825 * LSUP H H RSUP
826 * H H
827 * HHHH
828 * H H
829 * LSUB H H RSUB
831 * CSUB
832 * \endcode
834 * Graph over these, where "left" is before the SmSubSupNode and "right" is after:
835 * \dot
836 * digraph Graph{
837 * left -> H;
838 * H -> right;
839 * LSUP -> H;
840 * LSUB -> H;
841 * CSUP -> right;
842 * CSUB -> right;
843 * RSUP -> right;
844 * RSUB -> right;
845 * };
846 * \enddot
849 void SmCaretPosGraphBuildingVisitor::Visit( SmSubSupNode* pNode )
851 SmCaretPosGraphEntry *left,
852 *right,
853 *bodyLeft,
854 *bodyRight;
856 assert(mpRightMost);
857 left = mpRightMost;
859 //Create bodyLeft
860 SAL_WARN_IF( !pNode->GetBody(), "starmath", "SmSubSupNode Doesn't have a body!" );
861 bodyLeft = mpGraph->Add( SmCaretPos( pNode->GetBody( ), 0 ), left );
862 left->SetRight( bodyLeft ); //TODO: Don't make this if LSUP or LSUB are NULL ( not sure??? )
864 //Create right
865 right = mpGraph->Add( SmCaretPos( pNode, 1 ) );
867 //Visit the body, to get bodyRight
868 mpRightMost = bodyLeft;
869 pNode->GetBody( )->Accept( this );
870 bodyRight = mpRightMost;
871 bodyRight->SetRight( right );
872 right->SetLeft( bodyRight );
874 SmNode* pChild;
875 for (SmSubSup const nodeType : { LSUP, LSUB, CSUP, CSUB, RSUP, RSUB })
877 pChild = pNode->GetSubSup(nodeType);
878 if( pChild )
880 SmCaretPosGraphEntry *cLeft; //Child left
881 cLeft = mpGraph->Add( SmCaretPos( pChild, 0 ), ((nodeType == RSUP) || (nodeType == RSUB))?bodyRight:left );
883 mpRightMost = cLeft;
884 pChild->Accept( this );
886 mpRightMost->SetRight( ((nodeType == LSUP) || (nodeType == LSUB))?bodyLeft:right );
890 //Set return parameters
891 mpRightMost = right;
894 /** Build caret position for SmOperNode
896 * If first child is an SmSubSupNode we will ignore its
897 * body, as this body is a SmMathSymbol, for SUM, INT or similar
898 * that shouldn't be subject to modification.
899 * If first child is not a SmSubSupNode, ignore it completely
900 * as it is a SmMathSymbol.
902 * The child positions in a SmOperNode, where H is symbol, e.g. int, sum or similar:
903 * \code
904 * TO
906 * LSUP H H RSUP BBB BB BBB B B
907 * H H B B B B B B B B
908 * HHHH BBB B B B B B
909 * H H B B B B B B B
910 * LSUB H H RSUB BBB BB BBB B
912 * FROM
913 * \endcode
914 * Notice, CSUP, etc. are actually grandchildren, but inorder to ignore H, these are visited
915 * from here. If they are present, that is if pOper is an instance of SmSubSupNode.
917 * Graph over these, where "left" is before the SmOperNode and "right" is after:
918 * \dot
919 * digraph Graph{
920 * left -> BODY;
921 * BODY -> right;
922 * LSUP -> BODY;
923 * LSUB -> BODY;
924 * TO -> BODY;
925 * FROM -> BODY;
926 * RSUP -> BODY;
927 * RSUB -> BODY;
928 * };
929 * \enddot
931 void SmCaretPosGraphBuildingVisitor::Visit( SmOperNode* pNode )
933 SmNode *pOper = pNode->GetSubNode( 0 ),
934 *pBody = pNode->GetSubNode( 1 );
936 SmCaretPosGraphEntry *left = mpRightMost,
937 *bodyLeft,
938 *bodyRight,
939 *right;
940 //Create body left
941 bodyLeft = mpGraph->Add( SmCaretPos( pBody, 0 ), left );
942 left->SetRight( bodyLeft );
944 //Visit body, get bodyRight
945 mpRightMost = bodyLeft;
946 pBody->Accept( this );
947 bodyRight = mpRightMost;
949 //Create right
950 right = mpGraph->Add( SmCaretPos( pNode, 1 ), bodyRight );
951 bodyRight->SetRight( right );
953 //Get subsup pNode if any
954 SmSubSupNode* pSubSup = pOper->GetType( ) == SmNodeType::SubSup ? static_cast<SmSubSupNode*>(pOper) : nullptr;
956 if( pSubSup ) {
957 SmNode* pChild;
958 for (SmSubSup const nodeType : { LSUP, LSUB, CSUP, CSUB, RSUP, RSUB })
960 pChild = pSubSup->GetSubSup(nodeType);
961 if( pChild )
963 //Create position in front of pChild
964 SmCaretPosGraphEntry *childLeft = mpGraph->Add( SmCaretPos( pChild, 0 ), left );
965 //Visit pChild
966 mpRightMost = childLeft;
967 pChild->Accept( this );
968 //Set right on mpRightMost from pChild
969 mpRightMost->SetRight( bodyLeft );
974 //Return right
975 mpRightMost = right;
978 void SmCaretPosGraphBuildingVisitor::Visit( SmMatrixNode* pNode )
980 SmCaretPosGraphEntry *left = mpRightMost,
981 *right = mpGraph->Add( SmCaretPos( pNode, 1 ) );
983 for (size_t i = 0; i < pNode->GetNumRows(); ++i)
985 SmCaretPosGraphEntry* r = left;
986 for (size_t j = 0; j < pNode->GetNumCols(); ++j)
988 SmNode* pSubNode = pNode->GetSubNode( i * pNode->GetNumCols( ) + j );
990 mpRightMost = mpGraph->Add( SmCaretPos( pSubNode, 0 ), r );
991 if( j != 0 || ( pNode->GetNumRows() - 1U ) / 2 == i )
992 r->SetRight( mpRightMost );
994 pSubNode->Accept( this );
996 r = mpRightMost;
998 mpRightMost->SetRight( right );
999 if( ( pNode->GetNumRows() - 1U ) / 2 == i )
1000 right->SetLeft( mpRightMost );
1003 mpRightMost = right;
1006 /** Build SmCaretPosGraph for SmTextNode
1008 * Lines in an SmTextNode:
1009 * \code
1010 * A B C
1011 * \endcode
1012 * Where A B and C are characters in the text.
1014 * Graph over these, where "left" is before the SmTextNode and "right" is after:
1015 * \dot
1016 * digraph Graph{
1017 * left -> A;
1018 * A -> B
1019 * B -> right;
1020 * };
1021 * \enddot
1022 * Notice that C and right is the same position here.
1024 void SmCaretPosGraphBuildingVisitor::Visit( SmTextNode* pNode )
1026 SAL_WARN_IF( pNode->GetText().isEmpty(), "starmath", "Empty SmTextNode is bad" );
1028 int size = pNode->GetText().getLength();
1029 for( int i = 1; i <= size; i++ ){
1030 SmCaretPosGraphEntry* pRight = mpRightMost;
1031 mpRightMost = mpGraph->Add( SmCaretPos( pNode, i ), pRight );
1032 pRight->SetRight( mpRightMost );
1036 /** Build SmCaretPosGraph for SmBinVerNode
1038 * Lines in an SmBinVerNode:
1039 * \code
1041 * -----
1043 * \endcode
1045 * Graph over these, where "left" is before the SmBinVerNode and "right" is after:
1046 * \dot
1047 * digraph Graph{
1048 * left -> A;
1049 * A -> right;
1050 * B -> right;
1051 * };
1052 * \enddot
1054 void SmCaretPosGraphBuildingVisitor::Visit( SmBinVerNode* pNode )
1056 //None if these children can be NULL, see SmBinVerNode::Arrange
1057 SmNode *pNum = pNode->GetSubNode( 0 ),
1058 *pDenom = pNode->GetSubNode( 2 );
1060 SmCaretPosGraphEntry *left,
1061 *right,
1062 *numLeft,
1063 *denomLeft;
1065 assert(mpRightMost);
1066 //Set left
1067 left = mpRightMost;
1069 //Create right
1070 right = mpGraph->Add( SmCaretPos( pNode, 1 ) );
1072 //Create numLeft
1073 numLeft = mpGraph->Add( SmCaretPos( pNum, 0 ), left );
1074 left->SetRight( numLeft );
1076 //Visit pNum
1077 mpRightMost = numLeft;
1078 pNum->Accept( this );
1079 mpRightMost->SetRight( right );
1080 right->SetLeft( mpRightMost );
1082 //Create denomLeft
1083 denomLeft = mpGraph->Add( SmCaretPos( pDenom, 0 ), left );
1085 //Visit pDenom
1086 mpRightMost = denomLeft;
1087 pDenom->Accept( this );
1088 mpRightMost->SetRight( right );
1090 //Set return parameter
1091 mpRightMost = right;
1094 /** Build SmCaretPosGraph for SmVerticalBraceNode
1096 * Lines in an SmVerticalBraceNode:
1097 * \code
1098 * pScript
1099 * ________
1100 * / \
1101 * pBody
1102 * \endcode
1105 void SmCaretPosGraphBuildingVisitor::Visit( SmVerticalBraceNode* pNode )
1107 SmNode *pBody = pNode->Body(),
1108 *pScript = pNode->Script();
1109 //None of these children can be NULL
1111 SmCaretPosGraphEntry *left,
1112 *bodyLeft,
1113 *scriptLeft,
1114 *right;
1116 left = mpRightMost;
1118 //Create right
1119 right = mpGraph->Add( SmCaretPos( pNode, 1 ) );
1121 //Create bodyLeft
1122 bodyLeft = mpGraph->Add( SmCaretPos( pBody, 0 ), left );
1123 left->SetRight( bodyLeft );
1124 mpRightMost = bodyLeft;
1125 pBody->Accept( this );
1126 mpRightMost->SetRight( right );
1127 right->SetLeft( mpRightMost );
1129 //Create script
1130 scriptLeft = mpGraph->Add( SmCaretPos( pScript, 0 ), left );
1131 mpRightMost = scriptLeft;
1132 pScript->Accept( this );
1133 mpRightMost->SetRight( right );
1135 //Set return value
1136 mpRightMost = right;
1139 /** Build SmCaretPosGraph for SmBinDiagonalNode
1141 * Lines in an SmBinDiagonalNode:
1142 * \code
1143 * A /
1145 * / B
1146 * \endcode
1147 * Where A and B are lines.
1149 * Used in formulas such as "A wideslash B"
1151 void SmCaretPosGraphBuildingVisitor::Visit( SmBinDiagonalNode* pNode )
1153 SmNode *A = pNode->GetSubNode( 0 ),
1154 *B = pNode->GetSubNode( 1 );
1156 SmCaretPosGraphEntry *left,
1157 *leftA,
1158 *rightA,
1159 *leftB,
1160 *right;
1161 left = mpRightMost;
1163 //Create right
1164 right = mpGraph->Add( SmCaretPos( pNode, 1 ) );
1166 //Create left A
1167 leftA = mpGraph->Add( SmCaretPos( A, 0 ), left );
1168 left->SetRight( leftA );
1170 //Visit A
1171 mpRightMost = leftA;
1172 A->Accept( this );
1173 rightA = mpRightMost;
1175 //Create left B
1176 leftB = mpGraph->Add( SmCaretPos( B, 0 ), rightA );
1177 rightA->SetRight( leftB );
1179 //Visit B
1180 mpRightMost = leftB;
1181 B->Accept( this );
1182 mpRightMost->SetRight( right );
1183 right->SetLeft( mpRightMost );
1185 //Set return value
1186 mpRightMost = right;
1189 //Straight forward ( I think )
1190 void SmCaretPosGraphBuildingVisitor::Visit( SmBinHorNode* pNode )
1192 for( auto pChild : *pNode )
1194 if(!pChild)
1195 continue;
1196 pChild->Accept( this );
1199 void SmCaretPosGraphBuildingVisitor::Visit( SmUnHorNode* pNode )
1201 // Unary operator node
1202 for( auto pChild : *pNode )
1204 if(!pChild)
1205 continue;
1206 pChild->Accept( this );
1210 void SmCaretPosGraphBuildingVisitor::Visit( SmExpressionNode* pNode )
1212 for( auto pChild : *pNode )
1214 if(!pChild)
1215 continue;
1216 pChild->Accept( this );
1220 void SmCaretPosGraphBuildingVisitor::Visit( SmFontNode* pNode )
1222 //Has only got one child, should act as an expression if possible
1223 for( auto pChild : *pNode )
1225 if(!pChild)
1226 continue;
1227 pChild->Accept( this );
1231 /** Build SmCaretPosGraph for SmBracebodyNode
1232 * Acts as an SmExpressionNode
1234 * Below is an example of a formula tree that has multiple children for SmBracebodyNode
1235 * \dot
1236 * digraph {
1237 * labelloc = "t";
1238 * label= "Equation: \"lbrace i mline i in setZ rbrace\"";
1239 * n0 [label="SmTableNode"];
1240 * n0 -> n1 [label="0"];
1241 * n1 [label="SmLineNode"];
1242 * n1 -> n2 [label="0"];
1243 * n2 [label="SmExpressionNode"];
1244 * n2 -> n3 [label="0"];
1245 * n3 [label="SmBraceNode"];
1246 * n3 -> n4 [label="0"];
1247 * n4 [label="SmMathSymbolNode: {"];
1248 * n3 -> n5 [label="1"];
1249 * n5 [label="SmBracebodyNode"];
1250 * n5 -> n6 [label="0"];
1251 * n6 [label="SmExpressionNode"];
1252 * n6 -> n7 [label="0"];
1253 * n7 [label="SmTextNode: i"];
1254 * n5 -> n8 [label="1"];
1255 * n8 [label="SmMathSymbolNode: &#124;"]; // Unicode "VERTICAL LINE"
1256 * n5 -> n9 [label="2"];
1257 * n9 [label="SmExpressionNode"];
1258 * n9 -> n10 [label="0"];
1259 * n10 [label="SmBinHorNode"];
1260 * n10 -> n11 [label="0"];
1261 * n11 [label="SmTextNode: i"];
1262 * n10 -> n12 [label="1"];
1263 * n12 [label="SmMathSymbolNode: &#8712;"]; // Unicode "ELEMENT OF"
1264 * n10 -> n13 [label="2"];
1265 * n13 [label="SmMathSymbolNode: &#8484;"]; // Unicode "DOUBLE-STRUCK CAPITAL Z"
1266 * n3 -> n14 [label="2"];
1267 * n14 [label="SmMathSymbolNode: }"];
1269 * \enddot
1271 void SmCaretPosGraphBuildingVisitor::Visit( SmBracebodyNode* pNode )
1273 for( auto pChild : *pNode )
1275 if(!pChild)
1276 continue;
1277 SmCaretPosGraphEntry* pStart = mpGraph->Add( SmCaretPos( pChild, 0), mpRightMost );
1278 mpRightMost->SetRight( pStart );
1279 mpRightMost = pStart;
1280 pChild->Accept( this );
1284 /** Build SmCaretPosGraph for SmAlignNode
1285 * Acts as an SmExpressionNode, as it only has one child this okay
1287 void SmCaretPosGraphBuildingVisitor::Visit( SmAlignNode* pNode )
1289 for( auto pChild : *pNode )
1291 if(!pChild)
1292 continue;
1293 pChild->Accept( this );
1297 /** Build SmCaretPosGraph for SmRootNode
1299 * Lines in an SmRootNode:
1300 * \code
1301 * _________
1302 * A/
1303 * \/ B
1305 * \endcode
1306 * A: pExtra ( optional, can be NULL ),
1307 * B: pBody
1309 * Graph over these, where "left" is before the SmRootNode and "right" is after:
1310 * \dot
1311 * digraph Graph{
1312 * left -> B;
1313 * B -> right;
1314 * A -> B;
1316 * \enddot
1318 void SmCaretPosGraphBuildingVisitor::Visit( SmRootNode* pNode )
1320 SmNode *pExtra = pNode->GetSubNode( 0 ), //Argument, NULL for sqrt, and SmTextNode if cubicroot
1321 *pBody = pNode->GetSubNode( 2 ); //Body of the root
1322 assert(pBody);
1324 SmCaretPosGraphEntry *left,
1325 *right,
1326 *bodyLeft,
1327 *bodyRight;
1329 //Get left and save it
1330 assert(mpRightMost);
1331 left = mpRightMost;
1333 //Create body left
1334 bodyLeft = mpGraph->Add( SmCaretPos( pBody, 0 ), left );
1335 left->SetRight( bodyLeft );
1337 //Create right
1338 right = mpGraph->Add( SmCaretPos( pNode, 1 ) );
1340 //Visit body
1341 mpRightMost = bodyLeft;
1342 pBody->Accept( this );
1343 bodyRight = mpRightMost;
1344 bodyRight->SetRight( right );
1345 right->SetLeft( bodyRight );
1347 //Visit pExtra
1348 if( pExtra ){
1349 mpRightMost = mpGraph->Add( SmCaretPos( pExtra, 0 ), left );
1350 pExtra->Accept( this );
1351 mpRightMost->SetRight( bodyLeft );
1354 mpRightMost = right;
1358 /** Build SmCaretPosGraph for SmPlaceNode
1359 * Consider this a single character.
1361 void SmCaretPosGraphBuildingVisitor::Visit( SmPlaceNode* pNode )
1363 SmCaretPosGraphEntry* right = mpGraph->Add( SmCaretPos( pNode, 1 ), mpRightMost );
1364 mpRightMost->SetRight( right );
1365 mpRightMost = right;
1368 /** SmErrorNode is context dependent metadata, it can't be selected
1370 * @remarks There's no point in deleting, copying and/or moving an instance
1371 * of SmErrorNode as it may not exist in another context! Thus there are no
1372 * positions to select an SmErrorNode.
1374 void SmCaretPosGraphBuildingVisitor::Visit( SmErrorNode* )
1378 /** Build SmCaretPosGraph for SmBlankNode
1379 * Consider this a single character, as it is only a blank space
1381 void SmCaretPosGraphBuildingVisitor::Visit( SmBlankNode* pNode )
1383 SmCaretPosGraphEntry* right = mpGraph->Add( SmCaretPos( pNode, 1 ), mpRightMost );
1384 mpRightMost->SetRight( right );
1385 mpRightMost = right;
1388 /** Build SmCaretPosGraph for SmBraceNode
1390 * Lines in an SmBraceNode:
1391 * \code
1392 * | |
1393 * | B |
1394 * | |
1395 * \endcode
1396 * B: Body
1398 * Graph over these, where "left" is before the SmBraceNode and "right" is after:
1399 * \dot
1400 * digraph Graph{
1401 * left -> B;
1402 * B -> right;
1404 * \enddot
1406 void SmCaretPosGraphBuildingVisitor::Visit( SmBraceNode* pNode )
1408 SmNode* pBody = pNode->Body();
1410 SmCaretPosGraphEntry *left = mpRightMost,
1411 *right = mpGraph->Add( SmCaretPos( pNode, 1 ) );
1413 if( pBody->GetType() != SmNodeType::Bracebody ) {
1414 mpRightMost = mpGraph->Add( SmCaretPos( pBody, 0 ), left );
1415 left->SetRight( mpRightMost );
1416 }else
1417 mpRightMost = left;
1419 pBody->Accept( this );
1420 mpRightMost->SetRight( right );
1421 right->SetLeft( mpRightMost );
1423 mpRightMost = right;
1426 /** Build SmCaretPosGraph for SmAttributeNode
1428 * Lines in an SmAttributeNode:
1429 * \code
1430 * Attr
1431 * Body
1432 * \endcode
1434 * There's a body and an attribute, the construction is used for "widehat A", where "A" is the body
1435 * and "^" is the attribute ( note GetScaleMode( ) on SmAttributeNode tells how the attribute should be
1436 * scaled ).
1438 void SmCaretPosGraphBuildingVisitor::Visit( SmAttributeNode* pNode )
1440 SmNode *pAttr = pNode->Attribute(),
1441 *pBody = pNode->Body();
1442 assert(pAttr);
1443 assert(pBody);
1445 SmCaretPosGraphEntry *left = mpRightMost,
1446 *attrLeft,
1447 *bodyLeft,
1448 *bodyRight,
1449 *right;
1451 //Creating bodyleft
1452 bodyLeft = mpGraph->Add( SmCaretPos( pBody, 0 ), left );
1453 left->SetRight( bodyLeft );
1455 //Creating right
1456 right = mpGraph->Add( SmCaretPos( pNode, 1 ) );
1458 //Visit the body
1459 mpRightMost = bodyLeft;
1460 pBody->Accept( this );
1461 bodyRight = mpRightMost;
1462 bodyRight->SetRight( right );
1463 right->SetLeft( bodyRight );
1465 //Create attrLeft
1466 attrLeft = mpGraph->Add( SmCaretPos( pAttr, 0 ), left );
1468 //Visit attribute
1469 mpRightMost = attrLeft;
1470 pAttr->Accept( this );
1471 mpRightMost->SetRight( right );
1473 //Set return value
1474 mpRightMost = right;
1477 //Consider these single symbols
1478 void SmCaretPosGraphBuildingVisitor::Visit( SmSpecialNode* pNode )
1480 SmCaretPosGraphEntry* right = mpGraph->Add( SmCaretPos( pNode, 1 ), mpRightMost );
1481 mpRightMost->SetRight( right );
1482 mpRightMost = right;
1484 void SmCaretPosGraphBuildingVisitor::Visit( SmGlyphSpecialNode* pNode )
1486 SmCaretPosGraphEntry* right = mpGraph->Add( SmCaretPos( pNode, 1 ), mpRightMost );
1487 mpRightMost->SetRight( right );
1488 mpRightMost = right;
1490 void SmCaretPosGraphBuildingVisitor::Visit( SmMathSymbolNode* pNode )
1492 SmCaretPosGraphEntry* right = mpGraph->Add( SmCaretPos( pNode, 1 ), mpRightMost );
1493 mpRightMost->SetRight( right );
1494 mpRightMost = right;
1497 void SmCaretPosGraphBuildingVisitor::Visit( SmRootSymbolNode* )
1499 //Do nothing
1502 void SmCaretPosGraphBuildingVisitor::Visit( SmRectangleNode* )
1504 //Do nothing
1506 void SmCaretPosGraphBuildingVisitor::Visit( SmPolyLineNode* )
1508 //Do nothing
1511 // SmCloningVisitor
1513 SmNode* SmCloningVisitor::Clone( SmNode* pNode )
1515 SmNode* pCurrResult = mpResult;
1516 pNode->Accept( this );
1517 SmNode* pClone = mpResult;
1518 mpResult = pCurrResult;
1519 return pClone;
1522 void SmCloningVisitor::CloneNodeAttr( SmNode const * pSource, SmNode* pTarget )
1524 pTarget->SetScaleMode( pSource->GetScaleMode( ) );
1525 //Other attributes are set when prepare or arrange is executed
1526 //and may depend on stuff not being cloned here.
1529 void SmCloningVisitor::CloneKids( SmStructureNode* pSource, SmStructureNode* pTarget )
1531 //Cache current result
1532 SmNode* pCurrResult = mpResult;
1534 //Create array for holding clones
1535 size_t nSize = pSource->GetNumSubNodes( );
1536 SmNodeArray aNodes( nSize );
1538 //Clone children
1539 for (size_t i = 0; i < nSize; ++i)
1541 SmNode* pKid;
1542 if( nullptr != ( pKid = pSource->GetSubNode( i ) ) )
1543 pKid->Accept( this );
1544 else
1545 mpResult = nullptr;
1546 aNodes[i] = mpResult;
1549 //Set subnodes of pTarget
1550 pTarget->SetSubNodes( std::move(aNodes) );
1552 //Restore result as where prior to call
1553 mpResult = pCurrResult;
1556 void SmCloningVisitor::Visit( SmTableNode* pNode )
1558 SmTableNode* pClone = new SmTableNode( pNode->GetToken( ) );
1559 pClone->SetSelection( pNode->GetSelection() );
1560 CloneNodeAttr( pNode, pClone );
1561 CloneKids( pNode, pClone );
1562 mpResult = pClone;
1565 void SmCloningVisitor::Visit( SmBraceNode* pNode )
1567 SmBraceNode* pClone = new SmBraceNode( pNode->GetToken( ) );
1568 pClone->SetSelection( pNode->GetSelection() );
1569 CloneNodeAttr( pNode, pClone );
1570 CloneKids( pNode, pClone );
1571 mpResult = pClone;
1574 void SmCloningVisitor::Visit( SmBracebodyNode* pNode )
1576 SmBracebodyNode* pClone = new SmBracebodyNode( pNode->GetToken( ) );
1577 pClone->SetSelection( pNode->GetSelection() );
1578 CloneNodeAttr( pNode, pClone );
1579 CloneKids( pNode, pClone );
1580 mpResult = pClone;
1583 void SmCloningVisitor::Visit( SmOperNode* pNode )
1585 SmOperNode* pClone = new SmOperNode( pNode->GetToken( ) );
1586 pClone->SetSelection( pNode->GetSelection() );
1587 CloneNodeAttr( pNode, pClone );
1588 CloneKids( pNode, pClone );
1589 mpResult = pClone;
1592 void SmCloningVisitor::Visit( SmAlignNode* pNode )
1594 SmAlignNode* pClone = new SmAlignNode( pNode->GetToken( ) );
1595 pClone->SetSelection( pNode->GetSelection() );
1596 CloneNodeAttr( pNode, pClone );
1597 CloneKids( pNode, pClone );
1598 mpResult = pClone;
1601 void SmCloningVisitor::Visit( SmAttributeNode* pNode )
1603 SmAttributeNode* pClone = new SmAttributeNode( pNode->GetToken( ) );
1604 pClone->SetSelection( pNode->GetSelection() );
1605 CloneNodeAttr( pNode, pClone );
1606 CloneKids( pNode, pClone );
1607 mpResult = pClone;
1610 void SmCloningVisitor::Visit( SmFontNode* pNode )
1612 SmFontNode* pClone = new SmFontNode( pNode->GetToken( ) );
1613 pClone->SetSelection( pNode->GetSelection() );
1614 pClone->SetSizeParameter( pNode->GetSizeParameter( ), pNode->GetSizeType( ) );
1615 CloneNodeAttr( pNode, pClone );
1616 CloneKids( pNode, pClone );
1617 mpResult = pClone;
1620 void SmCloningVisitor::Visit( SmUnHorNode* pNode )
1622 SmUnHorNode* pClone = new SmUnHorNode( pNode->GetToken( ) );
1623 pClone->SetSelection( pNode->GetSelection() );
1624 CloneNodeAttr( pNode, pClone );
1625 CloneKids( pNode, pClone );
1626 mpResult = pClone;
1629 void SmCloningVisitor::Visit( SmBinHorNode* pNode )
1631 SmBinHorNode* pClone = new SmBinHorNode( pNode->GetToken( ) );
1632 pClone->SetSelection( pNode->GetSelection() );
1633 CloneNodeAttr( pNode, pClone );
1634 CloneKids( pNode, pClone );
1635 mpResult = pClone;
1638 void SmCloningVisitor::Visit( SmBinVerNode* pNode )
1640 SmBinVerNode* pClone = new SmBinVerNode( pNode->GetToken( ) );
1641 pClone->SetSelection( pNode->GetSelection() );
1642 CloneNodeAttr( pNode, pClone );
1643 CloneKids( pNode, pClone );
1644 mpResult = pClone;
1647 void SmCloningVisitor::Visit( SmBinDiagonalNode* pNode )
1649 SmBinDiagonalNode *pClone = new SmBinDiagonalNode( pNode->GetToken( ) );
1650 pClone->SetSelection( pNode->GetSelection() );
1651 pClone->SetAscending( pNode->IsAscending( ) );
1652 CloneNodeAttr( pNode, pClone );
1653 CloneKids( pNode, pClone );
1654 mpResult = pClone;
1657 void SmCloningVisitor::Visit( SmSubSupNode* pNode )
1659 SmSubSupNode *pClone = new SmSubSupNode( pNode->GetToken( ) );
1660 pClone->SetSelection( pNode->GetSelection() );
1661 pClone->SetUseLimits( pNode->IsUseLimits( ) );
1662 CloneNodeAttr( pNode, pClone );
1663 CloneKids( pNode, pClone );
1664 mpResult = pClone;
1667 void SmCloningVisitor::Visit( SmMatrixNode* pNode )
1669 SmMatrixNode *pClone = new SmMatrixNode( pNode->GetToken( ) );
1670 pClone->SetSelection( pNode->GetSelection() );
1671 pClone->SetRowCol( pNode->GetNumRows( ), pNode->GetNumCols( ) );
1672 CloneNodeAttr( pNode, pClone );
1673 CloneKids( pNode, pClone );
1674 mpResult = pClone;
1677 void SmCloningVisitor::Visit( SmPlaceNode* pNode )
1679 mpResult = new SmPlaceNode( pNode->GetToken( ) );
1680 mpResult->SetSelection( pNode->GetSelection() );
1681 CloneNodeAttr( pNode, mpResult );
1684 void SmCloningVisitor::Visit( SmTextNode* pNode )
1686 SmTextNode* pClone = new SmTextNode( pNode->GetToken( ), pNode->GetFontDesc( ) );
1687 pClone->SetSelection( pNode->GetSelection() );
1688 pClone->ChangeText( pNode->GetText( ) );
1689 CloneNodeAttr( pNode, pClone );
1690 mpResult = pClone;
1693 void SmCloningVisitor::Visit( SmSpecialNode* pNode )
1695 mpResult = new SmSpecialNode( pNode->GetToken( ) );
1696 mpResult->SetSelection( pNode->GetSelection() );
1697 CloneNodeAttr( pNode, mpResult );
1700 void SmCloningVisitor::Visit( SmGlyphSpecialNode* pNode )
1702 mpResult = new SmGlyphSpecialNode( pNode->GetToken( ) );
1703 mpResult->SetSelection( pNode->GetSelection() );
1704 CloneNodeAttr( pNode, mpResult );
1707 void SmCloningVisitor::Visit( SmMathSymbolNode* pNode )
1709 mpResult = new SmMathSymbolNode( pNode->GetToken( ) );
1710 mpResult->SetSelection( pNode->GetSelection() );
1711 CloneNodeAttr( pNode, mpResult );
1714 void SmCloningVisitor::Visit( SmBlankNode* pNode )
1716 SmBlankNode* pClone = new SmBlankNode( pNode->GetToken( ) );
1717 pClone->SetSelection( pNode->GetSelection() );
1718 pClone->SetBlankNum( pNode->GetBlankNum( ) );
1719 mpResult = pClone;
1720 CloneNodeAttr( pNode, mpResult );
1723 void SmCloningVisitor::Visit( SmErrorNode* pNode )
1725 mpResult = new SmErrorNode( pNode->GetToken( ) );
1726 mpResult->SetSelection( pNode->GetSelection() );
1727 CloneNodeAttr( pNode, mpResult );
1730 void SmCloningVisitor::Visit( SmLineNode* pNode )
1732 SmLineNode* pClone = new SmLineNode( pNode->GetToken( ) );
1733 pClone->SetSelection( pNode->GetSelection() );
1734 CloneNodeAttr( pNode, pClone );
1735 CloneKids( pNode, pClone );
1736 mpResult = pClone;
1739 void SmCloningVisitor::Visit( SmExpressionNode* pNode )
1741 SmExpressionNode* pClone = new SmExpressionNode( pNode->GetToken( ) );
1742 pClone->SetSelection( pNode->GetSelection() );
1743 CloneNodeAttr( pNode, pClone );
1744 CloneKids( pNode, pClone );
1745 mpResult = pClone;
1748 void SmCloningVisitor::Visit( SmPolyLineNode* pNode )
1750 mpResult = new SmPolyLineNode( pNode->GetToken( ) );
1751 mpResult->SetSelection( pNode->GetSelection() );
1752 CloneNodeAttr( pNode, mpResult );
1755 void SmCloningVisitor::Visit( SmRootNode* pNode )
1757 SmRootNode* pClone = new SmRootNode( pNode->GetToken( ) );
1758 pClone->SetSelection( pNode->GetSelection() );
1759 CloneNodeAttr( pNode, pClone );
1760 CloneKids( pNode, pClone );
1761 mpResult = pClone;
1764 void SmCloningVisitor::Visit( SmRootSymbolNode* pNode )
1766 mpResult = new SmRootSymbolNode( pNode->GetToken( ) );
1767 mpResult->SetSelection( pNode->GetSelection() );
1768 CloneNodeAttr( pNode, mpResult );
1771 void SmCloningVisitor::Visit( SmRectangleNode* pNode )
1773 mpResult = new SmRectangleNode( pNode->GetToken( ) );
1774 mpResult->SetSelection( pNode->GetSelection() );
1775 CloneNodeAttr( pNode, mpResult );
1778 void SmCloningVisitor::Visit( SmVerticalBraceNode* pNode )
1780 SmVerticalBraceNode* pClone = new SmVerticalBraceNode( pNode->GetToken( ) );
1781 pClone->SetSelection( pNode->GetSelection() );
1782 CloneNodeAttr( pNode, pClone );
1783 CloneKids( pNode, pClone );
1784 mpResult = pClone;
1787 // SmSelectionDrawingVisitor
1789 SmSelectionDrawingVisitor::SmSelectionDrawingVisitor( OutputDevice& rDevice, SmNode* pTree, const Point& rOffset )
1790 : SmSelectionRectanglesVisitor( rDevice, pTree )
1792 //Draw selection if there's any
1793 if(GetSelection().IsEmpty()) return;
1795 tools::Rectangle aSelectionArea = GetSelection() + rOffset;
1797 //Save device state
1798 rDevice.Push( vcl::PushFlags::LINECOLOR | vcl::PushFlags::FILLCOLOR );
1799 //Change colors
1800 rDevice.SetLineColor( );
1801 rDevice.SetFillColor( COL_LIGHTGRAY );
1803 //Draw rectangle
1804 rDevice.DrawRect( aSelectionArea );
1806 //Restore device state
1807 rDevice.Pop( );
1810 // SmSelectionRectanglesVisitor
1812 SmSelectionRectanglesVisitor::SmSelectionRectanglesVisitor(OutputDevice& rDevice, SmNode* pTree)
1813 : mrDev(rDevice)
1815 // Visit everything
1816 SAL_WARN_IF(!pTree, "starmath", "pTree can't be null!");
1817 if (pTree)
1818 pTree->Accept(this);
1821 void SmSelectionRectanglesVisitor::DefaultVisit( SmNode* pNode )
1823 if( pNode->IsSelected( ) )
1824 ExtendSelectionArea( pNode->AsRectangle( ) );
1825 VisitChildren( pNode );
1828 void SmSelectionRectanglesVisitor::VisitChildren( SmNode* pNode )
1830 if(pNode->GetNumSubNodes() == 0)
1831 return;
1832 for( auto pChild : *static_cast<SmStructureNode*>(pNode) )
1834 if(!pChild)
1835 continue;
1836 pChild->Accept( this );
1840 void SmSelectionRectanglesVisitor::Visit( SmTextNode* pNode )
1842 if( !pNode->IsSelected())
1843 return;
1845 mrDev.Push( vcl::PushFlags::TEXTCOLOR | vcl::PushFlags::FONT );
1847 mrDev.SetFont( pNode->GetFont( ) );
1848 Point Position = pNode->GetTopLeft( );
1849 tools::Long left = Position.getX( ) + mrDev.GetTextWidth( pNode->GetText( ), 0, pNode->GetSelectionStart( ) );
1850 tools::Long right = Position.getX( ) + mrDev.GetTextWidth( pNode->GetText( ), 0, pNode->GetSelectionEnd( ) );
1851 tools::Long top = Position.getY( );
1852 tools::Long bottom = top + pNode->GetHeight( );
1853 tools::Rectangle rect( left, top, right, bottom );
1855 ExtendSelectionArea( rect );
1857 mrDev.Pop( );
1860 // SmNodeToTextVisitor
1862 SmNodeToTextVisitor::SmNodeToTextVisitor( SmNode* pNode, OUString &rText )
1864 pNode->Accept( this );
1865 maCmdText.stripEnd(' ');
1866 rText = maCmdText.makeStringAndClear();
1869 void SmNodeToTextVisitor::Visit( SmTableNode* pNode )
1871 if( pNode->GetToken( ).eType == TBINOM ) {
1872 Append(u"{ binom");
1873 LineToText( pNode->GetSubNode( 0 ) );
1874 LineToText( pNode->GetSubNode( 1 ) );
1875 Append(u"} ");
1876 } else if( pNode->GetToken( ).eType == TSTACK ) {
1877 Append(u"stack{ ");
1878 bool bFirst = true;
1879 for( auto pChild : *pNode )
1881 if(!pChild)
1882 continue;
1883 if(bFirst)
1884 bFirst = false;
1885 else
1887 Separate( );
1888 Append(u"# ");
1890 LineToText( pChild );
1892 Separate( );
1893 Append(u"}");
1894 } else { //Assume it's a toplevel table, containing lines
1895 bool bFirst = true;
1896 for( auto pChild : *pNode )
1898 if(!pChild)
1899 continue;
1900 if(bFirst)
1901 bFirst = false;
1902 else
1904 Separate( );
1905 Append(u"newline");
1907 Separate( );
1908 pChild->Accept( this );
1913 void SmNodeToTextVisitor::Visit( SmBraceNode* pNode )
1915 if ( pNode->GetToken().eType == TEVALUATE )
1917 SmNode *pBody = pNode->Body();
1918 Append(u"evaluate { ");
1919 pBody->Accept( this );
1920 Append(u"} ");
1922 else{
1923 SmNode *pLeftBrace = pNode->OpeningBrace(),
1924 *pBody = pNode->Body(),
1925 *pRightBrace = pNode->ClosingBrace();
1926 //Handle special case where it's absolute function
1927 if( pNode->GetToken( ).eType == TABS ) {
1928 Append(u"abs");
1929 LineToText( pBody );
1930 } else {
1931 if( pNode->GetScaleMode( ) == SmScaleMode::Height )
1932 Append(u"left ");
1933 pLeftBrace->Accept( this );
1934 Separate( );
1935 pBody->Accept( this );
1936 Separate( );
1937 if( pNode->GetScaleMode( ) == SmScaleMode::Height )
1938 Append(u"right ");
1939 pRightBrace->Accept( this );
1944 void SmNodeToTextVisitor::Visit( SmBracebodyNode* pNode )
1946 for( auto pChild : *pNode )
1948 if(!pChild)
1949 continue;
1950 Separate( );
1951 pChild->Accept( this );
1955 void SmNodeToTextVisitor::Visit( SmOperNode* pNode )
1957 Append( pNode->GetToken( ).aText );
1958 Separate( );
1959 if( pNode->GetToken( ).eType == TOPER ){
1960 //There's an SmGlyphSpecialNode if eType == TOPER
1961 if( pNode->GetSubNode( 0 )->GetType( ) == SmNodeType::SubSup )
1962 Append( pNode->GetSubNode( 0 )->GetSubNode( 0 )->GetToken( ).aText );
1963 else
1964 Append( pNode->GetSubNode( 0 )->GetToken( ).aText );
1966 if( pNode->GetSubNode( 0 )->GetType( ) == SmNodeType::SubSup ) {
1967 SmSubSupNode *pSubSup = static_cast<SmSubSupNode*>( pNode->GetSubNode( 0 ) );
1968 SmNode* pChild = pSubSup->GetSubSup( LSUP );
1969 if( pChild ) {
1970 Separate( );
1971 Append(u"lsup { ");
1972 LineToText( pChild );
1973 Append(u"} ");
1975 pChild = pSubSup->GetSubSup( LSUB );
1976 if( pChild ) {
1977 Separate( );
1978 Append(u"lsub { ");
1979 LineToText( pChild );
1980 Append(u"} ");
1982 pChild = pSubSup->GetSubSup( RSUP );
1983 if( pChild ) {
1984 Separate( );
1985 Append(u"^ { ");
1986 LineToText( pChild );
1987 Append(u"} ");
1989 pChild = pSubSup->GetSubSup( RSUB );
1990 if( pChild ) {
1991 Separate( );
1992 Append(u"_ { ");
1993 LineToText( pChild );
1994 Append(u"} ");
1996 pChild = pSubSup->GetSubSup( CSUP );
1997 if( pChild ) {
1998 Separate( );
1999 if (pSubSup->IsUseLimits())
2000 Append(u"to { ");
2001 else
2002 Append(u"csup { ");
2003 LineToText( pChild );
2004 Append(u"} ");
2006 pChild = pSubSup->GetSubSup( CSUB );
2007 if( pChild ) {
2008 Separate( );
2009 if (pSubSup->IsUseLimits())
2010 Append(u"from { ");
2011 else
2012 Append(u"csub { ");
2013 LineToText( pChild );
2014 Append(u"} ");
2017 LineToText( pNode->GetSubNode( 1 ) );
2020 void SmNodeToTextVisitor::Visit( SmAlignNode* pNode )
2022 Append( pNode->GetToken( ).aText );
2023 LineToText( pNode->GetSubNode( 0 ) );
2026 void SmNodeToTextVisitor::Visit( SmAttributeNode* pNode )
2028 Append( pNode->GetToken( ).aText );
2029 LineToText( pNode->Body() );
2032 void SmNodeToTextVisitor::Visit( SmFontNode* pNode )
2034 sal_uInt32 nc;
2035 sal_uInt8 nr, ng, nb;
2036 switch ( pNode->GetToken( ).eType )
2038 case TBOLD:
2039 Append(u"bold ");
2040 break;
2041 case TNBOLD:
2042 Append(u"nbold ");
2043 break;
2044 case TITALIC:
2045 Append(u"italic ");
2046 break;
2047 case TNITALIC:
2048 Append(u"nitalic ");
2049 break;
2050 case TPHANTOM:
2051 Append(u"phantom ");
2052 break;
2053 case TSIZE:
2055 Append(u"size ");
2056 switch ( pNode->GetSizeType( ) )
2058 case FontSizeType::PLUS:
2059 Append(u"+");
2060 break;
2061 case FontSizeType::MINUS:
2062 Append(u"-");
2063 break;
2064 case FontSizeType::MULTIPLY:
2065 Append(u"*");
2066 break;
2067 case FontSizeType::DIVIDE:
2068 Append(u"/");
2069 break;
2070 case FontSizeType::ABSOLUT:
2071 default:
2072 break;
2074 Append( ::rtl::math::doubleToUString(
2075 static_cast<double>( pNode->GetSizeParameter( ) ),
2076 rtl_math_StringFormat_Automatic,
2077 rtl_math_DecimalPlaces_Max, '.', true ) );
2078 Separate( );
2080 break;
2082 case TDVIPSNAMESCOL:
2083 Append(u"color dvip ");
2084 nc = pNode->GetToken().cMathChar.toUInt32(16);
2085 Append( starmathdatabase::Identify_Color_Parser( nc ).aIdent );
2086 break;
2087 case THTMLCOL:
2088 case TMATHMLCOL:
2089 case TICONICCOL:
2090 Append(u"color ");
2091 nc = pNode->GetToken().cMathChar.toUInt32(16);
2092 Append( starmathdatabase::Identify_Color_Parser( nc ).aIdent );
2093 break;
2094 case TRGB:
2095 nc = pNode->GetToken().cMathChar.toUInt32(16);
2096 Append(u"color rgb ");
2097 nb = nc % 256;
2098 nc /= 256;
2099 ng = nc % 256;
2100 nc /= 256;
2101 nr = nc % 256;
2102 Append(OUString::number(nr));
2103 Separate();
2104 Append(OUString::number(ng));
2105 Separate();
2106 Append(OUString::number(nb));
2107 Separate();
2108 break;
2109 case TRGBA:
2110 Append(u"color rgba ");
2111 nc = pNode->GetToken().cMathChar.toUInt32(16);
2112 nb = nc % 256;
2113 nc /= 256;
2114 ng = nc % 256;
2115 nc /= 256;
2116 nr = nc % 256;
2117 nc /= 256;
2118 Append(OUString::number(nr));
2119 Separate();
2120 Append(OUString::number(ng));
2121 Separate();
2122 Append(OUString::number(nb));
2123 Separate();
2124 Append(OUString::number(nc));
2125 Separate();
2126 break;
2127 case THEX:
2128 Append(u"color hex ");
2129 nc = pNode->GetToken().cMathChar.toUInt32(16);
2130 Append(OUString::number(nc,16));
2131 Separate();
2132 break;
2133 case TSANS:
2134 Append(u"font sans ");
2135 break;
2136 case TSERIF:
2137 Append(u"font serif ");
2138 break;
2139 case TFIXED:
2140 Append(u"font fixed ");
2141 break;
2142 default:
2143 break;
2145 LineToText( pNode->GetSubNode( 1 ) );
2148 void SmNodeToTextVisitor::Visit( SmUnHorNode* pNode )
2150 if(pNode->GetSubNode( 1 )->GetToken( ).eType == TFACT)
2152 // visit children in the reverse order
2153 for( auto it = pNode->rbegin(); it != pNode->rend(); ++it )
2155 auto pChild = *it;
2156 if(!pChild)
2157 continue;
2158 Separate( );
2159 pChild->Accept( this );
2162 else
2164 for( auto pChild : *pNode )
2166 if(!pChild)
2167 continue;
2168 Separate( );
2169 pChild->Accept( this );
2174 void SmNodeToTextVisitor::Visit( SmBinHorNode* pNode )
2176 const SmNode *pParent = pNode->GetParent();
2177 bool bBraceNeeded = pParent && pParent->GetType() == SmNodeType::Font;
2178 SmNode *pLeft = pNode->LeftOperand(),
2179 *pOper = pNode->Symbol(),
2180 *pRight = pNode->RightOperand();
2181 Separate( );
2182 if (bBraceNeeded)
2183 Append(u"{ ");
2184 pLeft->Accept( this );
2185 Separate( );
2186 pOper->Accept( this );
2187 Separate( );
2188 pRight->Accept( this );
2189 Separate( );
2190 if (bBraceNeeded)
2191 Append(u"} ");
2194 void SmNodeToTextVisitor::Visit( SmBinVerNode* pNode )
2196 if( pNode->GetToken().eType == TOVER ){
2197 SmNode *pNum = pNode->GetSubNode( 0 ),
2198 *pDenom = pNode->GetSubNode( 2 );
2199 Append(u"{ ");
2200 LineToText( pNum );
2201 Append(u"over");
2202 LineToText( pDenom );
2203 Append(u"} ");
2204 } else{
2205 SmNode *pNum = pNode->GetSubNode( 0 ),
2206 *pDenom = pNode->GetSubNode( 2 );
2207 Append(u"{ frac {");
2208 LineToText( pNum );
2209 Append(u"} {");
2210 LineToText( pDenom );
2211 Append(u"} }");
2215 void SmNodeToTextVisitor::Visit( SmBinDiagonalNode* pNode )
2217 SmNode *pLeftOperand = pNode->GetSubNode( 0 ),
2218 *pRightOperand = pNode->GetSubNode( 1 );
2219 Append(u"{ ");
2220 LineToText( pLeftOperand );
2221 Separate( );
2222 Append(u"wideslash ");
2223 LineToText( pRightOperand );
2224 Append(u"} ");
2227 void SmNodeToTextVisitor::Visit( SmSubSupNode* pNode )
2229 if( pNode->GetToken().eType == TEVALUATE )
2231 Append(u"evaluate { ");
2232 pNode->GetSubNode( 0 )->GetSubNode( 1 )->Accept(this);
2233 Append(u"} ");
2234 SmNode* pChild = pNode->GetSubSup( RSUP );
2235 if( pChild ) {
2236 Separate( );
2237 Append(u"to { ");
2238 LineToText( pChild );
2239 Append(u"} ");
2241 pChild = pNode->GetSubSup( RSUB );
2242 if( pChild ) {
2243 Separate( );
2244 Append(u"from { ");
2245 LineToText( pChild );
2246 Append(u"} ");
2249 else
2251 LineToText( pNode->GetBody( ) );
2252 SmNode *pChild = pNode->GetSubSup( LSUP );
2253 if( pChild ) {
2254 Separate( );
2255 Append(u"lsup ");
2256 LineToText( pChild );
2258 pChild = pNode->GetSubSup( LSUB );
2259 if( pChild ) {
2260 Separate( );
2261 Append(u"lsub ");
2262 LineToText( pChild );
2264 pChild = pNode->GetSubSup( RSUP );
2265 if( pChild ) {
2266 Separate( );
2267 Append(u"^ ");
2268 LineToText( pChild );
2270 pChild = pNode->GetSubSup( RSUB );
2271 if( pChild ) {
2272 Separate( );
2273 Append(u"_ ");
2274 LineToText( pChild );
2276 pChild = pNode->GetSubSup( CSUP );
2277 if( pChild ) {
2278 Separate( );
2279 if (pNode->IsUseLimits())
2280 Append(u"to ");
2281 else
2282 Append(u"csup ");
2283 LineToText( pChild );
2285 pChild = pNode->GetSubSup( CSUB );
2286 if( pChild ) {
2287 Separate( );
2288 if (pNode->IsUseLimits())
2289 Append(u"from ");
2290 else
2291 Append(u"csub ");
2292 LineToText( pChild );
2297 void SmNodeToTextVisitor::Visit( SmMatrixNode* pNode )
2299 Append(u"matrix{");
2300 for (size_t i = 0; i < pNode->GetNumRows(); ++i)
2302 for (size_t j = 0; j < pNode->GetNumCols( ); ++j)
2304 SmNode* pSubNode = pNode->GetSubNode( i * pNode->GetNumCols( ) + j );
2305 Separate( );
2306 if (pSubNode)
2307 pSubNode->Accept( this );
2308 Separate( );
2309 if (j != pNode->GetNumCols() - 1U)
2310 Append(u"#");
2312 Separate( );
2313 if (i != pNode->GetNumRows() - 1U)
2314 Append(u"##");
2316 Append(u"} ");
2319 void SmNodeToTextVisitor::Visit( SmPlaceNode* )
2321 Append(u"<?>");
2324 void SmNodeToTextVisitor::Visit( SmTextNode* pNode )
2326 SmTokenType type = pNode->GetToken( ).eType;
2327 switch(type){
2328 case TTEXT:
2329 Append(u"\"");
2330 Append( pNode->GetToken().aText );
2331 Append(u"\"");
2332 break;
2333 case TNUMBER:
2334 Append( pNode->GetToken().aText );
2335 break;
2336 case TIDENT:
2337 Append( pNode->GetToken().aText );
2338 break;
2339 case TFUNC:
2340 Append(u"func ");
2341 Append( pNode->GetToken().aText );
2342 break;
2343 case THEX:
2344 Append(u"hex ");
2345 Append( pNode->GetToken().aText );
2346 break;
2347 default:
2348 Append( pNode->GetToken().aText );
2350 Separate( );
2353 void SmNodeToTextVisitor::Visit( SmSpecialNode* pNode )
2355 SmTokenType type = pNode->GetToken().eType;
2356 switch(type){
2357 case TLIMSUP:
2358 Append(u"lim sup ");
2359 break;
2360 case TLIMINF:
2361 Append(u"lim inf ");
2362 break;
2363 default:
2364 Append( pNode->GetToken().aText );
2365 break;
2369 void SmNodeToTextVisitor::Visit( SmGlyphSpecialNode* pNode )
2371 if( pNode->GetToken( ).eType == TBOPER )
2372 Append(u"boper ");
2373 else
2374 Append(u"uoper ");
2375 Append( pNode->GetToken( ).aText );
2378 //TODO to improve this it is required to improve mathmlimport.
2379 void SmNodeToTextVisitor::Visit( SmMathSymbolNode* pNode )
2381 if ( ( pNode->GetToken().nGroup & TG::LBrace )
2382 || ( pNode->GetToken().nGroup & TG::RBrace )
2383 || ( pNode->GetToken().nGroup & TG::Sum )
2384 || ( pNode->GetToken().nGroup & TG::Product )
2385 || ( pNode->GetToken().nGroup & TG::Relation )
2386 || ( pNode->GetToken().nGroup & TG::UnOper )
2387 || ( pNode->GetToken().nGroup & TG::Oper )
2389 Append( pNode->GetToken().aText );
2390 return;
2392 sal_Unicode cChar = pNode->GetToken().cMathChar[0];
2393 Separate( );
2394 switch(cChar){
2395 case MS_NONE:
2396 Append(u"none");
2397 break;
2398 case '{':
2399 Append(u"{");
2400 break;
2401 case '}':
2402 Append(u"}");
2403 break;
2404 case MS_VERTLINE:
2405 Append(u"mline");
2406 break;
2407 case MS_TILDE:
2408 Append(u"\"~\"");
2409 break;
2410 case MS_RIGHTARROW:
2411 if( pNode->GetToken().eType == TTOWARD ) Append(u"toward");
2412 else Append(u"rightarrow");
2413 break;
2414 case MS_LEFTARROW:
2415 Append(u"leftarrow");
2416 break;
2417 case MS_UPARROW:
2418 Append(u"uparrow");
2419 break;
2420 case MS_DOWNARROW:
2421 Append(u"downarrow");
2422 break;
2423 case MS_LAMBDABAR:
2424 Append(u"lambdabar");
2425 break;
2426 case MS_DOTSLOW:
2427 Append(u"dotslow");
2428 break;
2429 case MS_SETC:
2430 Append(u"setC");
2431 break;
2432 case MS_HBAR:
2433 Append(u"hbar");
2434 break;
2435 case MS_IM:
2436 Append(u"Im");
2437 break;
2438 case MS_SETN:
2439 Append(u"setN");
2440 break;
2441 case MS_WP:
2442 Append(u"wp");
2443 break;
2444 case MS_LAPLACE:
2445 Append(u"laplace");
2446 break;
2447 case MS_SETQ:
2448 Append(u"setQ");
2449 break;
2450 case MS_RE:
2451 Append(u"Re");
2452 break;
2453 case MS_SETR:
2454 Append(u"setR");
2455 break;
2456 case MS_SETZ:
2457 Append(u"setZ");
2458 break;
2459 case MS_ALEPH:
2460 Append(u"aleph");
2461 break;
2462 case 0x0362:
2463 Append(u"widevec");
2464 break;
2465 case MS_DLARROW:
2466 Append(u"dlarrow");
2467 break;
2468 case MS_DRARROW:
2469 Append(u"drarrow");
2470 break;
2471 case MS_DLRARROW:
2472 Append(u"dlrarrow");
2473 break;
2474 case MS_FORALL:
2475 Append(u"forall");
2476 break;
2477 case MS_PARTIAL:
2478 Append(u"partial");
2479 break;
2480 case MS_EXISTS:
2481 Append(u"exists");
2482 break;
2483 case MS_NOTEXISTS:
2484 Append(u"notexists");
2485 break;
2486 case MS_EMPTYSET:
2487 Append(u"emptyset");
2488 break;
2489 case MS_NABLA:
2490 Append(u"nabla");
2491 break;
2492 case MS_BACKEPSILON:
2493 Append(u"backepsilon");
2494 break;
2495 case MS_CIRC:
2496 Append(u"circ");
2497 break;
2498 case MS_INFINITY:
2499 Append(u"infinity");
2500 break;
2501 case 0x22b2: // NORMAL SUBGROUP OF
2502 Append(OUStringChar(cChar));
2503 break;
2504 case 0x22b3: // CONTAINS AS NORMAL SUBGROUP
2505 Append(OUStringChar(cChar));
2506 break;
2507 case MS_ORTHO:
2508 Append(u"ortho");
2509 break;
2510 case MS_DOTSVERT:
2511 Append(u"dotsvert");
2512 break;
2513 case MS_DOTSAXIS:
2514 Append(u"dotsaxis");
2515 break;
2516 case MS_DOTSUP:
2517 Append(u"dotsup");
2518 break;
2519 case MS_DOTSDOWN:
2520 Append(u"dotsdown");
2521 break;
2522 case '^':
2523 Append(u"^");
2524 break;
2525 case 0xe091:
2526 Append(u"widehat");
2527 break;
2528 case 0xe096:
2529 Append(u"widetilde");
2530 break;
2531 case 0xe098:
2532 Append(u"widevec");
2533 break;
2534 case 0xeb01: //no space
2535 case 0xeb08: //normal space
2536 break;
2537 case 0xef04: //tiny space
2538 case 0xef05: //tiny space
2539 case 0xeb02: //small space
2540 case 0xeb04: //medium space
2541 Append(u"`");
2542 break;
2543 case 0xeb05: //large space
2544 Append(u"~");
2545 break;
2546 case 0x3a9:
2547 Append(u"%OMEGA");
2548 break;
2549 default:
2550 Append(OUStringChar(cChar));
2551 break;
2555 void SmNodeToTextVisitor::Visit( SmBlankNode* pNode )
2557 sal_uInt16 nNum = pNode->GetBlankNum();
2558 if (nNum <= 0)
2559 return;
2560 sal_uInt16 nWide = nNum / 4;
2561 sal_uInt16 nNarrow = nNum % 4;
2562 for (sal_uInt16 i = 0; i < nWide; i++)
2563 Append(u"~");
2564 for (sal_uInt16 i = 0; i < nNarrow; i++)
2565 Append(u"`");
2566 Append(u" ");
2569 void SmNodeToTextVisitor::Visit( SmErrorNode* )
2573 void SmNodeToTextVisitor::Visit( SmLineNode* pNode )
2575 for( auto pChild : *pNode )
2577 if(!pChild)
2578 continue;
2579 Separate( );
2580 pChild->Accept( this );
2584 void SmNodeToTextVisitor::Visit( SmExpressionNode* pNode )
2586 bool bracketsNeeded = pNode->GetNumSubNodes() != 1 || pNode->GetSubNode(0)->GetType() == SmNodeType::BinHor;
2587 if (!bracketsNeeded)
2589 const SmNode *pParent = pNode->GetParent();
2590 // nested subsups
2591 bracketsNeeded =
2592 pParent && pParent->GetType() == SmNodeType::SubSup &&
2593 pNode->GetNumSubNodes() == 1 &&
2594 pNode->GetSubNode(0)->GetType() == SmNodeType::SubSup;
2597 if (bracketsNeeded) {
2598 Append(u"{ ");
2600 for( auto pChild : *pNode )
2602 if(!pChild)
2603 continue;
2604 pChild->Accept( this );
2605 Separate( );
2607 if (bracketsNeeded) {
2608 Append(u"} ");
2612 void SmNodeToTextVisitor::Visit( SmPolyLineNode* )
2616 void SmNodeToTextVisitor::Visit( SmRootNode* pNode )
2618 SmNode *pExtra = pNode->GetSubNode( 0 ),
2619 *pBody = pNode->GetSubNode( 2 );
2620 if( pExtra ) {
2621 Append(u"nroot");
2622 LineToText( pExtra );
2623 } else
2624 Append(u"sqrt");
2625 LineToText( pBody );
2628 void SmNodeToTextVisitor::Visit( SmRootSymbolNode* )
2632 void SmNodeToTextVisitor::Visit( SmRectangleNode* )
2636 void SmNodeToTextVisitor::Visit( SmVerticalBraceNode* pNode )
2638 SmNode *pBody = pNode->Body(),
2639 *pScript = pNode->Script();
2640 LineToText( pBody );
2641 Append( pNode->GetToken( ).aText );
2642 LineToText( pScript );
2645 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */