calc: on editing invalidation of view with different zoom is wrong
[LibreOffice.git] / starmath / source / visitors.cxx
blob6efe9936b94838c9a7303e1090cc79da7c4b2551
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 //If there's an LSUP
875 SmNode* pChild = pNode->GetSubSup( LSUP );
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( bodyLeft );
885 //If there's an LSUB
886 pChild = pNode->GetSubSup( LSUB );
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( bodyLeft );
896 //If there's a CSUP
897 pChild = pNode->GetSubSup( CSUP );
898 if( pChild ){
899 SmCaretPosGraphEntry *cLeft; //Child left
900 cLeft = mpGraph->Add( SmCaretPos( pChild, 0 ), left );
902 mpRightMost = cLeft;
903 pChild->Accept( this );
905 mpRightMost->SetRight( right );
907 //If there's a CSUB
908 pChild = pNode->GetSubSup( CSUB );
909 if( pChild ){
910 SmCaretPosGraphEntry *cLeft; //Child left
911 cLeft = mpGraph->Add( SmCaretPos( pChild, 0 ), left );
913 mpRightMost = cLeft;
914 pChild->Accept( this );
916 mpRightMost->SetRight( right );
918 //If there's an RSUP
919 pChild = pNode->GetSubSup( RSUP );
920 if( pChild ){
921 SmCaretPosGraphEntry *cLeft; //Child left
922 cLeft = mpGraph->Add( SmCaretPos( pChild, 0 ), bodyRight );
924 mpRightMost = cLeft;
925 pChild->Accept( this );
927 mpRightMost->SetRight( right );
929 //If there's an RSUB
930 pChild = pNode->GetSubSup( RSUB );
931 if( pChild ){
932 SmCaretPosGraphEntry *cLeft; //Child left
933 cLeft = mpGraph->Add( SmCaretPos( pChild, 0 ), bodyRight );
935 mpRightMost = cLeft;
936 pChild->Accept( this );
938 mpRightMost->SetRight( right );
941 //Set return parameters
942 mpRightMost = right;
945 /** Build caret position for SmOperNode
947 * If first child is an SmSubSupNode we will ignore its
948 * body, as this body is a SmMathSymbol, for SUM, INT or similar
949 * that shouldn't be subject to modification.
950 * If first child is not a SmSubSupNode, ignore it completely
951 * as it is a SmMathSymbol.
953 * The child positions in a SmOperNode, where H is symbol, e.g. int, sum or similar:
954 * \code
955 * TO
957 * LSUP H H RSUP BBB BB BBB B B
958 * H H B B B B B B B B
959 * HHHH BBB B B B B B
960 * H H B B B B B B B
961 * LSUB H H RSUB BBB BB BBB B
963 * FROM
964 * \endcode
965 * Notice, CSUP, etc. are actually grandchildren, but inorder to ignore H, these are visited
966 * from here. If they are present, that is if pOper is an instance of SmSubSupNode.
968 * Graph over these, where "left" is before the SmOperNode and "right" is after:
969 * \dot
970 * digraph Graph{
971 * left -> BODY;
972 * BODY -> right;
973 * LSUP -> BODY;
974 * LSUB -> BODY;
975 * TO -> BODY;
976 * FROM -> BODY;
977 * RSUP -> BODY;
978 * RSUB -> BODY;
979 * };
980 * \enddot
982 void SmCaretPosGraphBuildingVisitor::Visit( SmOperNode* pNode )
984 SmNode *pOper = pNode->GetSubNode( 0 ),
985 *pBody = pNode->GetSubNode( 1 );
987 SmCaretPosGraphEntry *left = mpRightMost,
988 *bodyLeft,
989 *bodyRight,
990 *right;
991 //Create body left
992 bodyLeft = mpGraph->Add( SmCaretPos( pBody, 0 ), left );
993 left->SetRight( bodyLeft );
995 //Visit body, get bodyRight
996 mpRightMost = bodyLeft;
997 pBody->Accept( this );
998 bodyRight = mpRightMost;
1000 //Create right
1001 right = mpGraph->Add( SmCaretPos( pNode, 1 ), bodyRight );
1002 bodyRight->SetRight( right );
1004 //Get subsup pNode if any
1005 SmSubSupNode* pSubSup = pOper->GetType( ) == SmNodeType::SubSup ? static_cast<SmSubSupNode*>(pOper) : nullptr;
1007 if( pSubSup ) {
1008 SmNode* pChild = pSubSup->GetSubSup( LSUP );
1009 if( pChild ) {
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( LSUB );
1020 if( pChild ) {
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( CSUP );
1031 if ( pChild ) {//TO
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( CSUB );
1042 if( pChild ) { //FROM
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 );
1052 pChild = pSubSup->GetSubSup( RSUP );
1053 if ( pChild ) {
1054 //Create position in front of pChild
1055 SmCaretPosGraphEntry *childLeft = mpGraph->Add( SmCaretPos( pChild, 0 ), left );
1056 //Visit pChild
1057 mpRightMost = childLeft;
1058 pChild->Accept( this );
1059 //Set right on mpRightMost from pChild
1060 mpRightMost->SetRight( bodyLeft );
1063 pChild = pSubSup->GetSubSup( RSUB );
1064 if ( pChild ) {
1065 //Create position in front of pChild
1066 SmCaretPosGraphEntry *childLeft = mpGraph->Add( SmCaretPos( pChild, 0 ), left );
1067 //Visit pChild
1068 mpRightMost = childLeft;
1069 pChild->Accept( this );
1070 //Set right on mpRightMost from pChild
1071 mpRightMost->SetRight( bodyLeft );
1075 //Return right
1076 mpRightMost = right;
1079 void SmCaretPosGraphBuildingVisitor::Visit( SmMatrixNode* pNode )
1081 SmCaretPosGraphEntry *left = mpRightMost,
1082 *right = mpGraph->Add( SmCaretPos( pNode, 1 ) );
1084 for (size_t i = 0; i < pNode->GetNumRows(); ++i)
1086 SmCaretPosGraphEntry* r = left;
1087 for (size_t j = 0; j < pNode->GetNumCols(); ++j)
1089 SmNode* pSubNode = pNode->GetSubNode( i * pNode->GetNumCols( ) + j );
1091 mpRightMost = mpGraph->Add( SmCaretPos( pSubNode, 0 ), r );
1092 if( j != 0 || ( pNode->GetNumRows() - 1U ) / 2 == i )
1093 r->SetRight( mpRightMost );
1095 pSubNode->Accept( this );
1097 r = mpRightMost;
1099 mpRightMost->SetRight( right );
1100 if( ( pNode->GetNumRows() - 1U ) / 2 == i )
1101 right->SetLeft( mpRightMost );
1104 mpRightMost = right;
1107 /** Build SmCaretPosGraph for SmTextNode
1109 * Lines in an SmTextNode:
1110 * \code
1111 * A B C
1112 * \endcode
1113 * Where A B and C are characters in the text.
1115 * Graph over these, where "left" is before the SmTextNode and "right" is after:
1116 * \dot
1117 * digraph Graph{
1118 * left -> A;
1119 * A -> B
1120 * B -> right;
1121 * };
1122 * \enddot
1123 * Notice that C and right is the same position here.
1125 void SmCaretPosGraphBuildingVisitor::Visit( SmTextNode* pNode )
1127 SAL_WARN_IF( pNode->GetText().isEmpty(), "starmath", "Empty SmTextNode is bad" );
1129 int size = pNode->GetText().getLength();
1130 for( int i = 1; i <= size; i++ ){
1131 SmCaretPosGraphEntry* pRight = mpRightMost;
1132 mpRightMost = mpGraph->Add( SmCaretPos( pNode, i ), pRight );
1133 pRight->SetRight( mpRightMost );
1137 /** Build SmCaretPosGraph for SmBinVerNode
1139 * Lines in an SmBinVerNode:
1140 * \code
1142 * -----
1144 * \endcode
1146 * Graph over these, where "left" is before the SmBinVerNode and "right" is after:
1147 * \dot
1148 * digraph Graph{
1149 * left -> A;
1150 * A -> right;
1151 * B -> right;
1152 * };
1153 * \enddot
1155 void SmCaretPosGraphBuildingVisitor::Visit( SmBinVerNode* pNode )
1157 //None if these children can be NULL, see SmBinVerNode::Arrange
1158 SmNode *pNum = pNode->GetSubNode( 0 ),
1159 *pDenom = pNode->GetSubNode( 2 );
1161 SmCaretPosGraphEntry *left,
1162 *right,
1163 *numLeft,
1164 *denomLeft;
1166 assert(mpRightMost);
1167 //Set left
1168 left = mpRightMost;
1170 //Create right
1171 right = mpGraph->Add( SmCaretPos( pNode, 1 ) );
1173 //Create numLeft
1174 numLeft = mpGraph->Add( SmCaretPos( pNum, 0 ), left );
1175 left->SetRight( numLeft );
1177 //Visit pNum
1178 mpRightMost = numLeft;
1179 pNum->Accept( this );
1180 mpRightMost->SetRight( right );
1181 right->SetLeft( mpRightMost );
1183 //Create denomLeft
1184 denomLeft = mpGraph->Add( SmCaretPos( pDenom, 0 ), left );
1186 //Visit pDenom
1187 mpRightMost = denomLeft;
1188 pDenom->Accept( this );
1189 mpRightMost->SetRight( right );
1191 //Set return parameter
1192 mpRightMost = right;
1195 /** Build SmCaretPosGraph for SmVerticalBraceNode
1197 * Lines in an SmVerticalBraceNode:
1198 * \code
1199 * pScript
1200 * ________
1201 * / \
1202 * pBody
1203 * \endcode
1206 void SmCaretPosGraphBuildingVisitor::Visit( SmVerticalBraceNode* pNode )
1208 SmNode *pBody = pNode->Body(),
1209 *pScript = pNode->Script();
1210 //None of these children can be NULL
1212 SmCaretPosGraphEntry *left,
1213 *bodyLeft,
1214 *scriptLeft,
1215 *right;
1217 left = mpRightMost;
1219 //Create right
1220 right = mpGraph->Add( SmCaretPos( pNode, 1 ) );
1222 //Create bodyLeft
1223 bodyLeft = mpGraph->Add( SmCaretPos( pBody, 0 ), left );
1224 left->SetRight( bodyLeft );
1225 mpRightMost = bodyLeft;
1226 pBody->Accept( this );
1227 mpRightMost->SetRight( right );
1228 right->SetLeft( mpRightMost );
1230 //Create script
1231 scriptLeft = mpGraph->Add( SmCaretPos( pScript, 0 ), left );
1232 mpRightMost = scriptLeft;
1233 pScript->Accept( this );
1234 mpRightMost->SetRight( right );
1236 //Set return value
1237 mpRightMost = right;
1240 /** Build SmCaretPosGraph for SmBinDiagonalNode
1242 * Lines in an SmBinDiagonalNode:
1243 * \code
1244 * A /
1246 * / B
1247 * \endcode
1248 * Where A and B are lines.
1250 * Used in formulas such as "A wideslash B"
1252 void SmCaretPosGraphBuildingVisitor::Visit( SmBinDiagonalNode* pNode )
1254 SmNode *A = pNode->GetSubNode( 0 ),
1255 *B = pNode->GetSubNode( 1 );
1257 SmCaretPosGraphEntry *left,
1258 *leftA,
1259 *rightA,
1260 *leftB,
1261 *right;
1262 left = mpRightMost;
1264 //Create right
1265 right = mpGraph->Add( SmCaretPos( pNode, 1 ) );
1267 //Create left A
1268 leftA = mpGraph->Add( SmCaretPos( A, 0 ), left );
1269 left->SetRight( leftA );
1271 //Visit A
1272 mpRightMost = leftA;
1273 A->Accept( this );
1274 rightA = mpRightMost;
1276 //Create left B
1277 leftB = mpGraph->Add( SmCaretPos( B, 0 ), rightA );
1278 rightA->SetRight( leftB );
1280 //Visit B
1281 mpRightMost = leftB;
1282 B->Accept( this );
1283 mpRightMost->SetRight( right );
1284 right->SetLeft( mpRightMost );
1286 //Set return value
1287 mpRightMost = right;
1290 //Straight forward ( I think )
1291 void SmCaretPosGraphBuildingVisitor::Visit( SmBinHorNode* pNode )
1293 for( auto pChild : *pNode )
1295 if(!pChild)
1296 continue;
1297 pChild->Accept( this );
1300 void SmCaretPosGraphBuildingVisitor::Visit( SmUnHorNode* pNode )
1302 // Unary operator node
1303 for( auto pChild : *pNode )
1305 if(!pChild)
1306 continue;
1307 pChild->Accept( this );
1311 void SmCaretPosGraphBuildingVisitor::Visit( SmExpressionNode* pNode )
1313 for( auto pChild : *pNode )
1315 if(!pChild)
1316 continue;
1317 pChild->Accept( this );
1321 void SmCaretPosGraphBuildingVisitor::Visit( SmFontNode* pNode )
1323 //Has only got one child, should act as an expression if possible
1324 for( auto pChild : *pNode )
1326 if(!pChild)
1327 continue;
1328 pChild->Accept( this );
1332 /** Build SmCaretPosGraph for SmBracebodyNode
1333 * Acts as an SmExpressionNode
1335 * Below is an example of a formula tree that has multiple children for SmBracebodyNode
1336 * \dot
1337 * digraph {
1338 * labelloc = "t";
1339 * label= "Equation: \"lbrace i mline i in setZ rbrace\"";
1340 * n0 [label="SmTableNode"];
1341 * n0 -> n1 [label="0"];
1342 * n1 [label="SmLineNode"];
1343 * n1 -> n2 [label="0"];
1344 * n2 [label="SmExpressionNode"];
1345 * n2 -> n3 [label="0"];
1346 * n3 [label="SmBraceNode"];
1347 * n3 -> n4 [label="0"];
1348 * n4 [label="SmMathSymbolNode: {"];
1349 * n3 -> n5 [label="1"];
1350 * n5 [label="SmBracebodyNode"];
1351 * n5 -> n6 [label="0"];
1352 * n6 [label="SmExpressionNode"];
1353 * n6 -> n7 [label="0"];
1354 * n7 [label="SmTextNode: i"];
1355 * n5 -> n8 [label="1"];
1356 * n8 [label="SmMathSymbolNode: &#124;"]; // Unicode "VERTICAL LINE"
1357 * n5 -> n9 [label="2"];
1358 * n9 [label="SmExpressionNode"];
1359 * n9 -> n10 [label="0"];
1360 * n10 [label="SmBinHorNode"];
1361 * n10 -> n11 [label="0"];
1362 * n11 [label="SmTextNode: i"];
1363 * n10 -> n12 [label="1"];
1364 * n12 [label="SmMathSymbolNode: &#8712;"]; // Unicode "ELEMENT OF"
1365 * n10 -> n13 [label="2"];
1366 * n13 [label="SmMathSymbolNode: &#8484;"]; // Unicode "DOUBLE-STRUCK CAPITAL Z"
1367 * n3 -> n14 [label="2"];
1368 * n14 [label="SmMathSymbolNode: }"];
1370 * \enddot
1372 void SmCaretPosGraphBuildingVisitor::Visit( SmBracebodyNode* pNode )
1374 for( auto pChild : *pNode )
1376 if(!pChild)
1377 continue;
1378 SmCaretPosGraphEntry* pStart = mpGraph->Add( SmCaretPos( pChild, 0), mpRightMost );
1379 mpRightMost->SetRight( pStart );
1380 mpRightMost = pStart;
1381 pChild->Accept( this );
1385 /** Build SmCaretPosGraph for SmAlignNode
1386 * Acts as an SmExpressionNode, as it only has one child this okay
1388 void SmCaretPosGraphBuildingVisitor::Visit( SmAlignNode* pNode )
1390 for( auto pChild : *pNode )
1392 if(!pChild)
1393 continue;
1394 pChild->Accept( this );
1398 /** Build SmCaretPosGraph for SmRootNode
1400 * Lines in an SmRootNode:
1401 * \code
1402 * _________
1403 * A/
1404 * \/ B
1406 * \endcode
1407 * A: pExtra ( optional, can be NULL ),
1408 * B: pBody
1410 * Graph over these, where "left" is before the SmRootNode and "right" is after:
1411 * \dot
1412 * digraph Graph{
1413 * left -> B;
1414 * B -> right;
1415 * A -> B;
1417 * \enddot
1419 void SmCaretPosGraphBuildingVisitor::Visit( SmRootNode* pNode )
1421 SmNode *pExtra = pNode->GetSubNode( 0 ), //Argument, NULL for sqrt, and SmTextNode if cubicroot
1422 *pBody = pNode->GetSubNode( 2 ); //Body of the root
1423 assert(pBody);
1425 SmCaretPosGraphEntry *left,
1426 *right,
1427 *bodyLeft,
1428 *bodyRight;
1430 //Get left and save it
1431 assert(mpRightMost);
1432 left = mpRightMost;
1434 //Create body left
1435 bodyLeft = mpGraph->Add( SmCaretPos( pBody, 0 ), left );
1436 left->SetRight( bodyLeft );
1438 //Create right
1439 right = mpGraph->Add( SmCaretPos( pNode, 1 ) );
1441 //Visit body
1442 mpRightMost = bodyLeft;
1443 pBody->Accept( this );
1444 bodyRight = mpRightMost;
1445 bodyRight->SetRight( right );
1446 right->SetLeft( bodyRight );
1448 //Visit pExtra
1449 if( pExtra ){
1450 mpRightMost = mpGraph->Add( SmCaretPos( pExtra, 0 ), left );
1451 pExtra->Accept( this );
1452 mpRightMost->SetRight( bodyLeft );
1455 mpRightMost = right;
1459 /** Build SmCaretPosGraph for SmPlaceNode
1460 * Consider this a single character.
1462 void SmCaretPosGraphBuildingVisitor::Visit( SmPlaceNode* pNode )
1464 SmCaretPosGraphEntry* right = mpGraph->Add( SmCaretPos( pNode, 1 ), mpRightMost );
1465 mpRightMost->SetRight( right );
1466 mpRightMost = right;
1469 /** SmErrorNode is context dependent metadata, it can't be selected
1471 * @remarks There's no point in deleting, copying and/or moving an instance
1472 * of SmErrorNode as it may not exist in another context! Thus there are no
1473 * positions to select an SmErrorNode.
1475 void SmCaretPosGraphBuildingVisitor::Visit( SmErrorNode* )
1479 /** Build SmCaretPosGraph for SmBlankNode
1480 * Consider this a single character, as it is only a blank space
1482 void SmCaretPosGraphBuildingVisitor::Visit( SmBlankNode* pNode )
1484 SmCaretPosGraphEntry* right = mpGraph->Add( SmCaretPos( pNode, 1 ), mpRightMost );
1485 mpRightMost->SetRight( right );
1486 mpRightMost = right;
1489 /** Build SmCaretPosGraph for SmBraceNode
1491 * Lines in an SmBraceNode:
1492 * \code
1493 * | |
1494 * | B |
1495 * | |
1496 * \endcode
1497 * B: Body
1499 * Graph over these, where "left" is before the SmBraceNode and "right" is after:
1500 * \dot
1501 * digraph Graph{
1502 * left -> B;
1503 * B -> right;
1505 * \enddot
1507 void SmCaretPosGraphBuildingVisitor::Visit( SmBraceNode* pNode )
1509 SmNode* pBody = pNode->Body();
1511 SmCaretPosGraphEntry *left = mpRightMost,
1512 *right = mpGraph->Add( SmCaretPos( pNode, 1 ) );
1514 if( pBody->GetType() != SmNodeType::Bracebody ) {
1515 mpRightMost = mpGraph->Add( SmCaretPos( pBody, 0 ), left );
1516 left->SetRight( mpRightMost );
1517 }else
1518 mpRightMost = left;
1520 pBody->Accept( this );
1521 mpRightMost->SetRight( right );
1522 right->SetLeft( mpRightMost );
1524 mpRightMost = right;
1527 /** Build SmCaretPosGraph for SmAttributeNode
1529 * Lines in an SmAttributeNode:
1530 * \code
1531 * Attr
1532 * Body
1533 * \endcode
1535 * There's a body and an attribute, the construction is used for "widehat A", where "A" is the body
1536 * and "^" is the attribute ( note GetScaleMode( ) on SmAttributeNode tells how the attribute should be
1537 * scaled ).
1539 void SmCaretPosGraphBuildingVisitor::Visit( SmAttributeNode* pNode )
1541 SmNode *pAttr = pNode->Attribute(),
1542 *pBody = pNode->Body();
1543 assert(pAttr);
1544 assert(pBody);
1546 SmCaretPosGraphEntry *left = mpRightMost,
1547 *attrLeft,
1548 *bodyLeft,
1549 *bodyRight,
1550 *right;
1552 //Creating bodyleft
1553 bodyLeft = mpGraph->Add( SmCaretPos( pBody, 0 ), left );
1554 left->SetRight( bodyLeft );
1556 //Creating right
1557 right = mpGraph->Add( SmCaretPos( pNode, 1 ) );
1559 //Visit the body
1560 mpRightMost = bodyLeft;
1561 pBody->Accept( this );
1562 bodyRight = mpRightMost;
1563 bodyRight->SetRight( right );
1564 right->SetLeft( bodyRight );
1566 //Create attrLeft
1567 attrLeft = mpGraph->Add( SmCaretPos( pAttr, 0 ), left );
1569 //Visit attribute
1570 mpRightMost = attrLeft;
1571 pAttr->Accept( this );
1572 mpRightMost->SetRight( right );
1574 //Set return value
1575 mpRightMost = right;
1578 //Consider these single symbols
1579 void SmCaretPosGraphBuildingVisitor::Visit( SmSpecialNode* pNode )
1581 SmCaretPosGraphEntry* right = mpGraph->Add( SmCaretPos( pNode, 1 ), mpRightMost );
1582 mpRightMost->SetRight( right );
1583 mpRightMost = right;
1585 void SmCaretPosGraphBuildingVisitor::Visit( SmGlyphSpecialNode* pNode )
1587 SmCaretPosGraphEntry* right = mpGraph->Add( SmCaretPos( pNode, 1 ), mpRightMost );
1588 mpRightMost->SetRight( right );
1589 mpRightMost = right;
1591 void SmCaretPosGraphBuildingVisitor::Visit( SmMathSymbolNode* pNode )
1593 SmCaretPosGraphEntry* right = mpGraph->Add( SmCaretPos( pNode, 1 ), mpRightMost );
1594 mpRightMost->SetRight( right );
1595 mpRightMost = right;
1598 void SmCaretPosGraphBuildingVisitor::Visit( SmRootSymbolNode* )
1600 //Do nothing
1603 void SmCaretPosGraphBuildingVisitor::Visit( SmRectangleNode* )
1605 //Do nothing
1607 void SmCaretPosGraphBuildingVisitor::Visit( SmPolyLineNode* )
1609 //Do nothing
1612 // SmCloningVisitor
1614 SmNode* SmCloningVisitor::Clone( SmNode* pNode )
1616 SmNode* pCurrResult = mpResult;
1617 pNode->Accept( this );
1618 SmNode* pClone = mpResult;
1619 mpResult = pCurrResult;
1620 return pClone;
1623 void SmCloningVisitor::CloneNodeAttr( SmNode const * pSource, SmNode* pTarget )
1625 pTarget->SetScaleMode( pSource->GetScaleMode( ) );
1626 //Other attributes are set when prepare or arrange is executed
1627 //and may depend on stuff not being cloned here.
1630 void SmCloningVisitor::CloneKids( SmStructureNode* pSource, SmStructureNode* pTarget )
1632 //Cache current result
1633 SmNode* pCurrResult = mpResult;
1635 //Create array for holding clones
1636 size_t nSize = pSource->GetNumSubNodes( );
1637 SmNodeArray aNodes( nSize );
1639 //Clone children
1640 for (size_t i = 0; i < nSize; ++i)
1642 SmNode* pKid;
1643 if( nullptr != ( pKid = pSource->GetSubNode( i ) ) )
1644 pKid->Accept( this );
1645 else
1646 mpResult = nullptr;
1647 aNodes[i] = mpResult;
1650 //Set subnodes of pTarget
1651 pTarget->SetSubNodes( std::move(aNodes) );
1653 //Restore result as where prior to call
1654 mpResult = pCurrResult;
1657 void SmCloningVisitor::Visit( SmTableNode* pNode )
1659 SmTableNode* pClone = new SmTableNode( pNode->GetToken( ) );
1660 pClone->SetSelection( pNode->GetSelection() );
1661 CloneNodeAttr( pNode, pClone );
1662 CloneKids( pNode, pClone );
1663 mpResult = pClone;
1666 void SmCloningVisitor::Visit( SmBraceNode* pNode )
1668 SmBraceNode* pClone = new SmBraceNode( pNode->GetToken( ) );
1669 pClone->SetSelection( pNode->GetSelection() );
1670 CloneNodeAttr( pNode, pClone );
1671 CloneKids( pNode, pClone );
1672 mpResult = pClone;
1675 void SmCloningVisitor::Visit( SmBracebodyNode* pNode )
1677 SmBracebodyNode* pClone = new SmBracebodyNode( pNode->GetToken( ) );
1678 pClone->SetSelection( pNode->GetSelection() );
1679 CloneNodeAttr( pNode, pClone );
1680 CloneKids( pNode, pClone );
1681 mpResult = pClone;
1684 void SmCloningVisitor::Visit( SmOperNode* pNode )
1686 SmOperNode* pClone = new SmOperNode( pNode->GetToken( ) );
1687 pClone->SetSelection( pNode->GetSelection() );
1688 CloneNodeAttr( pNode, pClone );
1689 CloneKids( pNode, pClone );
1690 mpResult = pClone;
1693 void SmCloningVisitor::Visit( SmAlignNode* pNode )
1695 SmAlignNode* pClone = new SmAlignNode( pNode->GetToken( ) );
1696 pClone->SetSelection( pNode->GetSelection() );
1697 CloneNodeAttr( pNode, pClone );
1698 CloneKids( pNode, pClone );
1699 mpResult = pClone;
1702 void SmCloningVisitor::Visit( SmAttributeNode* pNode )
1704 SmAttributeNode* pClone = new SmAttributeNode( pNode->GetToken( ) );
1705 pClone->SetSelection( pNode->GetSelection() );
1706 CloneNodeAttr( pNode, pClone );
1707 CloneKids( pNode, pClone );
1708 mpResult = pClone;
1711 void SmCloningVisitor::Visit( SmFontNode* pNode )
1713 SmFontNode* pClone = new SmFontNode( pNode->GetToken( ) );
1714 pClone->SetSelection( pNode->GetSelection() );
1715 pClone->SetSizeParameter( pNode->GetSizeParameter( ), pNode->GetSizeType( ) );
1716 CloneNodeAttr( pNode, pClone );
1717 CloneKids( pNode, pClone );
1718 mpResult = pClone;
1721 void SmCloningVisitor::Visit( SmUnHorNode* pNode )
1723 SmUnHorNode* pClone = new SmUnHorNode( pNode->GetToken( ) );
1724 pClone->SetSelection( pNode->GetSelection() );
1725 CloneNodeAttr( pNode, pClone );
1726 CloneKids( pNode, pClone );
1727 mpResult = pClone;
1730 void SmCloningVisitor::Visit( SmBinHorNode* pNode )
1732 SmBinHorNode* pClone = new SmBinHorNode( pNode->GetToken( ) );
1733 pClone->SetSelection( pNode->GetSelection() );
1734 CloneNodeAttr( pNode, pClone );
1735 CloneKids( pNode, pClone );
1736 mpResult = pClone;
1739 void SmCloningVisitor::Visit( SmBinVerNode* pNode )
1741 SmBinVerNode* pClone = new SmBinVerNode( pNode->GetToken( ) );
1742 pClone->SetSelection( pNode->GetSelection() );
1743 CloneNodeAttr( pNode, pClone );
1744 CloneKids( pNode, pClone );
1745 mpResult = pClone;
1748 void SmCloningVisitor::Visit( SmBinDiagonalNode* pNode )
1750 SmBinDiagonalNode *pClone = new SmBinDiagonalNode( pNode->GetToken( ) );
1751 pClone->SetSelection( pNode->GetSelection() );
1752 pClone->SetAscending( pNode->IsAscending( ) );
1753 CloneNodeAttr( pNode, pClone );
1754 CloneKids( pNode, pClone );
1755 mpResult = pClone;
1758 void SmCloningVisitor::Visit( SmSubSupNode* pNode )
1760 SmSubSupNode *pClone = new SmSubSupNode( pNode->GetToken( ) );
1761 pClone->SetSelection( pNode->GetSelection() );
1762 pClone->SetUseLimits( pNode->IsUseLimits( ) );
1763 CloneNodeAttr( pNode, pClone );
1764 CloneKids( pNode, pClone );
1765 mpResult = pClone;
1768 void SmCloningVisitor::Visit( SmMatrixNode* pNode )
1770 SmMatrixNode *pClone = new SmMatrixNode( pNode->GetToken( ) );
1771 pClone->SetSelection( pNode->GetSelection() );
1772 pClone->SetRowCol( pNode->GetNumRows( ), pNode->GetNumCols( ) );
1773 CloneNodeAttr( pNode, pClone );
1774 CloneKids( pNode, pClone );
1775 mpResult = pClone;
1778 void SmCloningVisitor::Visit( SmPlaceNode* pNode )
1780 mpResult = new SmPlaceNode( pNode->GetToken( ) );
1781 mpResult->SetSelection( pNode->GetSelection() );
1782 CloneNodeAttr( pNode, mpResult );
1785 void SmCloningVisitor::Visit( SmTextNode* pNode )
1787 SmTextNode* pClone = new SmTextNode( pNode->GetToken( ), pNode->GetFontDesc( ) );
1788 pClone->SetSelection( pNode->GetSelection() );
1789 pClone->ChangeText( pNode->GetText( ) );
1790 CloneNodeAttr( pNode, pClone );
1791 mpResult = pClone;
1794 void SmCloningVisitor::Visit( SmSpecialNode* pNode )
1796 mpResult = new SmSpecialNode( pNode->GetToken( ) );
1797 mpResult->SetSelection( pNode->GetSelection() );
1798 CloneNodeAttr( pNode, mpResult );
1801 void SmCloningVisitor::Visit( SmGlyphSpecialNode* pNode )
1803 mpResult = new SmGlyphSpecialNode( pNode->GetToken( ) );
1804 mpResult->SetSelection( pNode->GetSelection() );
1805 CloneNodeAttr( pNode, mpResult );
1808 void SmCloningVisitor::Visit( SmMathSymbolNode* pNode )
1810 mpResult = new SmMathSymbolNode( pNode->GetToken( ) );
1811 mpResult->SetSelection( pNode->GetSelection() );
1812 CloneNodeAttr( pNode, mpResult );
1815 void SmCloningVisitor::Visit( SmBlankNode* pNode )
1817 SmBlankNode* pClone = new SmBlankNode( pNode->GetToken( ) );
1818 pClone->SetSelection( pNode->GetSelection() );
1819 pClone->SetBlankNum( pNode->GetBlankNum( ) );
1820 mpResult = pClone;
1821 CloneNodeAttr( pNode, mpResult );
1824 void SmCloningVisitor::Visit( SmErrorNode* pNode )
1826 mpResult = new SmErrorNode( pNode->GetToken( ) );
1827 mpResult->SetSelection( pNode->GetSelection() );
1828 CloneNodeAttr( pNode, mpResult );
1831 void SmCloningVisitor::Visit( SmLineNode* pNode )
1833 SmLineNode* pClone = new SmLineNode( pNode->GetToken( ) );
1834 pClone->SetSelection( pNode->GetSelection() );
1835 CloneNodeAttr( pNode, pClone );
1836 CloneKids( pNode, pClone );
1837 mpResult = pClone;
1840 void SmCloningVisitor::Visit( SmExpressionNode* pNode )
1842 SmExpressionNode* pClone = new SmExpressionNode( pNode->GetToken( ) );
1843 pClone->SetSelection( pNode->GetSelection() );
1844 CloneNodeAttr( pNode, pClone );
1845 CloneKids( pNode, pClone );
1846 mpResult = pClone;
1849 void SmCloningVisitor::Visit( SmPolyLineNode* pNode )
1851 mpResult = new SmPolyLineNode( pNode->GetToken( ) );
1852 mpResult->SetSelection( pNode->GetSelection() );
1853 CloneNodeAttr( pNode, mpResult );
1856 void SmCloningVisitor::Visit( SmRootNode* pNode )
1858 SmRootNode* pClone = new SmRootNode( pNode->GetToken( ) );
1859 pClone->SetSelection( pNode->GetSelection() );
1860 CloneNodeAttr( pNode, pClone );
1861 CloneKids( pNode, pClone );
1862 mpResult = pClone;
1865 void SmCloningVisitor::Visit( SmRootSymbolNode* pNode )
1867 mpResult = new SmRootSymbolNode( pNode->GetToken( ) );
1868 mpResult->SetSelection( pNode->GetSelection() );
1869 CloneNodeAttr( pNode, mpResult );
1872 void SmCloningVisitor::Visit( SmRectangleNode* pNode )
1874 mpResult = new SmRectangleNode( pNode->GetToken( ) );
1875 mpResult->SetSelection( pNode->GetSelection() );
1876 CloneNodeAttr( pNode, mpResult );
1879 void SmCloningVisitor::Visit( SmVerticalBraceNode* pNode )
1881 SmVerticalBraceNode* pClone = new SmVerticalBraceNode( pNode->GetToken( ) );
1882 pClone->SetSelection( pNode->GetSelection() );
1883 CloneNodeAttr( pNode, pClone );
1884 CloneKids( pNode, pClone );
1885 mpResult = pClone;
1888 // SmSelectionDrawingVisitor
1890 SmSelectionDrawingVisitor::SmSelectionDrawingVisitor( OutputDevice& rDevice, SmNode* pTree, const Point& rOffset )
1891 : SmSelectionRectanglesVisitor( rDevice, pTree )
1893 //Draw selection if there's any
1894 if(GetSelection().IsEmpty()) return;
1896 tools::Rectangle aSelectionArea = GetSelection() + rOffset;
1898 //Save device state
1899 rDevice.Push( vcl::PushFlags::LINECOLOR | vcl::PushFlags::FILLCOLOR );
1900 //Change colors
1901 rDevice.SetLineColor( );
1902 rDevice.SetFillColor( COL_LIGHTGRAY );
1904 //Draw rectangle
1905 rDevice.DrawRect( aSelectionArea );
1907 //Restore device state
1908 rDevice.Pop( );
1911 // SmSelectionRectanglesVisitor
1913 SmSelectionRectanglesVisitor::SmSelectionRectanglesVisitor(OutputDevice& rDevice, SmNode* pTree)
1914 : mrDev(rDevice)
1916 // Visit everything
1917 SAL_WARN_IF(!pTree, "starmath", "pTree can't be null!");
1918 if (pTree)
1919 pTree->Accept(this);
1922 void SmSelectionRectanglesVisitor::DefaultVisit( SmNode* pNode )
1924 if( pNode->IsSelected( ) )
1925 ExtendSelectionArea( pNode->AsRectangle( ) );
1926 VisitChildren( pNode );
1929 void SmSelectionRectanglesVisitor::VisitChildren( SmNode* pNode )
1931 if(pNode->GetNumSubNodes() == 0)
1932 return;
1933 for( auto pChild : *static_cast<SmStructureNode*>(pNode) )
1935 if(!pChild)
1936 continue;
1937 pChild->Accept( this );
1941 void SmSelectionRectanglesVisitor::Visit( SmTextNode* pNode )
1943 if( !pNode->IsSelected())
1944 return;
1946 mrDev.Push( vcl::PushFlags::TEXTCOLOR | vcl::PushFlags::FONT );
1948 mrDev.SetFont( pNode->GetFont( ) );
1949 Point Position = pNode->GetTopLeft( );
1950 tools::Long left = Position.getX( ) + mrDev.GetTextWidth( pNode->GetText( ), 0, pNode->GetSelectionStart( ) );
1951 tools::Long right = Position.getX( ) + mrDev.GetTextWidth( pNode->GetText( ), 0, pNode->GetSelectionEnd( ) );
1952 tools::Long top = Position.getY( );
1953 tools::Long bottom = top + pNode->GetHeight( );
1954 tools::Rectangle rect( left, top, right, bottom );
1956 ExtendSelectionArea( rect );
1958 mrDev.Pop( );
1961 // SmNodeToTextVisitor
1963 SmNodeToTextVisitor::SmNodeToTextVisitor( SmNode* pNode, OUString &rText )
1965 pNode->Accept( this );
1966 maCmdText.stripEnd(' ');
1967 rText = maCmdText.makeStringAndClear();
1970 void SmNodeToTextVisitor::Visit( SmTableNode* pNode )
1972 if( pNode->GetToken( ).eType == TBINOM ) {
1973 Append(u"{ binom");
1974 LineToText( pNode->GetSubNode( 0 ) );
1975 LineToText( pNode->GetSubNode( 1 ) );
1976 Append(u"} ");
1977 } else if( pNode->GetToken( ).eType == TSTACK ) {
1978 Append(u"stack{ ");
1979 bool bFirst = true;
1980 for( auto pChild : *pNode )
1982 if(!pChild)
1983 continue;
1984 if(bFirst)
1985 bFirst = false;
1986 else
1988 Separate( );
1989 Append(u"# ");
1991 LineToText( pChild );
1993 Separate( );
1994 Append(u"}");
1995 } else { //Assume it's a toplevel table, containing lines
1996 bool bFirst = true;
1997 for( auto pChild : *pNode )
1999 if(!pChild)
2000 continue;
2001 if(bFirst)
2002 bFirst = false;
2003 else
2005 Separate( );
2006 Append(u"newline");
2008 Separate( );
2009 pChild->Accept( this );
2014 void SmNodeToTextVisitor::Visit( SmBraceNode* pNode )
2016 if ( pNode->GetToken().eType == TEVALUATE )
2018 SmNode *pBody = pNode->Body();
2019 Append(u"evaluate { ");
2020 pBody->Accept( this );
2021 Append(u"} ");
2023 else{
2024 SmNode *pLeftBrace = pNode->OpeningBrace(),
2025 *pBody = pNode->Body(),
2026 *pRightBrace = pNode->ClosingBrace();
2027 //Handle special case where it's absolute function
2028 if( pNode->GetToken( ).eType == TABS ) {
2029 Append(u"abs");
2030 LineToText( pBody );
2031 } else {
2032 if( pNode->GetScaleMode( ) == SmScaleMode::Height )
2033 Append(u"left ");
2034 pLeftBrace->Accept( this );
2035 Separate( );
2036 pBody->Accept( this );
2037 Separate( );
2038 if( pNode->GetScaleMode( ) == SmScaleMode::Height )
2039 Append(u"right ");
2040 pRightBrace->Accept( this );
2045 void SmNodeToTextVisitor::Visit( SmBracebodyNode* pNode )
2047 for( auto pChild : *pNode )
2049 if(!pChild)
2050 continue;
2051 Separate( );
2052 pChild->Accept( this );
2056 void SmNodeToTextVisitor::Visit( SmOperNode* pNode )
2058 Append( pNode->GetToken( ).aText );
2059 Separate( );
2060 if( pNode->GetToken( ).eType == TOPER ){
2061 //There's an SmGlyphSpecialNode if eType == TOPER
2062 if( pNode->GetSubNode( 0 )->GetType( ) == SmNodeType::SubSup )
2063 Append( pNode->GetSubNode( 0 )->GetSubNode( 0 )->GetToken( ).aText );
2064 else
2065 Append( pNode->GetSubNode( 0 )->GetToken( ).aText );
2067 if( pNode->GetSubNode( 0 )->GetType( ) == SmNodeType::SubSup ) {
2068 SmSubSupNode *pSubSup = static_cast<SmSubSupNode*>( pNode->GetSubNode( 0 ) );
2069 SmNode* pChild = pSubSup->GetSubSup( LSUP );
2070 if( pChild ) {
2071 Separate( );
2072 Append(u"lsup { ");
2073 LineToText( pChild );
2074 Append(u"} ");
2076 pChild = pSubSup->GetSubSup( LSUB );
2077 if( pChild ) {
2078 Separate( );
2079 Append(u"lsub { ");
2080 LineToText( pChild );
2081 Append(u"} ");
2083 pChild = pSubSup->GetSubSup( RSUP );
2084 if( pChild ) {
2085 Separate( );
2086 Append(u"^ { ");
2087 LineToText( pChild );
2088 Append(u"} ");
2090 pChild = pSubSup->GetSubSup( RSUB );
2091 if( pChild ) {
2092 Separate( );
2093 Append(u"_ { ");
2094 LineToText( pChild );
2095 Append(u"} ");
2097 pChild = pSubSup->GetSubSup( CSUP );
2098 if( pChild ) {
2099 Separate( );
2100 if (pSubSup->IsUseLimits())
2101 Append(u"to { ");
2102 else
2103 Append(u"csup { ");
2104 LineToText( pChild );
2105 Append(u"} ");
2107 pChild = pSubSup->GetSubSup( CSUB );
2108 if( pChild ) {
2109 Separate( );
2110 if (pSubSup->IsUseLimits())
2111 Append(u"from { ");
2112 else
2113 Append(u"csub { ");
2114 LineToText( pChild );
2115 Append(u"} ");
2118 LineToText( pNode->GetSubNode( 1 ) );
2121 void SmNodeToTextVisitor::Visit( SmAlignNode* pNode )
2123 Append( pNode->GetToken( ).aText );
2124 LineToText( pNode->GetSubNode( 0 ) );
2127 void SmNodeToTextVisitor::Visit( SmAttributeNode* pNode )
2129 Append( pNode->GetToken( ).aText );
2130 LineToText( pNode->Body() );
2133 void SmNodeToTextVisitor::Visit( SmFontNode* pNode )
2135 sal_uInt32 nc;
2136 sal_uInt8 nr, ng, nb;
2137 switch ( pNode->GetToken( ).eType )
2139 case TBOLD:
2140 Append(u"bold ");
2141 break;
2142 case TNBOLD:
2143 Append(u"nbold ");
2144 break;
2145 case TITALIC:
2146 Append(u"italic ");
2147 break;
2148 case TNITALIC:
2149 Append(u"nitalic ");
2150 break;
2151 case TPHANTOM:
2152 Append(u"phantom ");
2153 break;
2154 case TSIZE:
2156 Append(u"size ");
2157 switch ( pNode->GetSizeType( ) )
2159 case FontSizeType::PLUS:
2160 Append(u"+");
2161 break;
2162 case FontSizeType::MINUS:
2163 Append(u"-");
2164 break;
2165 case FontSizeType::MULTIPLY:
2166 Append(u"*");
2167 break;
2168 case FontSizeType::DIVIDE:
2169 Append(u"/");
2170 break;
2171 case FontSizeType::ABSOLUT:
2172 default:
2173 break;
2175 Append( ::rtl::math::doubleToUString(
2176 static_cast<double>( pNode->GetSizeParameter( ) ),
2177 rtl_math_StringFormat_Automatic,
2178 rtl_math_DecimalPlaces_Max, '.', true ) );
2179 Separate( );
2181 break;
2183 case TDVIPSNAMESCOL:
2184 Append(u"color dvip ");
2185 nc = pNode->GetToken().cMathChar.toUInt32(16);
2186 Append( starmathdatabase::Identify_Color_Parser( nc ).aIdent );
2187 break;
2188 case THTMLCOL:
2189 case TMATHMLCOL:
2190 case TICONICCOL:
2191 Append(u"color ");
2192 nc = pNode->GetToken().cMathChar.toUInt32(16);
2193 Append( starmathdatabase::Identify_Color_Parser( nc ).aIdent );
2194 break;
2195 case TRGB:
2196 nc = pNode->GetToken().cMathChar.toUInt32(16);
2197 Append(u"color rgb ");
2198 nb = nc % 256;
2199 nc /= 256;
2200 ng = nc % 256;
2201 nc /= 256;
2202 nr = nc % 256;
2203 Append(OUString::number(nr));
2204 Separate();
2205 Append(OUString::number(ng));
2206 Separate();
2207 Append(OUString::number(nb));
2208 Separate();
2209 break;
2210 case TRGBA:
2211 Append(u"color rgba ");
2212 nc = pNode->GetToken().cMathChar.toUInt32(16);
2213 nb = nc % 256;
2214 nc /= 256;
2215 ng = nc % 256;
2216 nc /= 256;
2217 nr = nc % 256;
2218 nc /= 256;
2219 Append(OUString::number(nr));
2220 Separate();
2221 Append(OUString::number(ng));
2222 Separate();
2223 Append(OUString::number(nb));
2224 Separate();
2225 Append(OUString::number(nc));
2226 Separate();
2227 break;
2228 case THEX:
2229 Append(u"color hex ");
2230 nc = pNode->GetToken().cMathChar.toUInt32(16);
2231 Append(OUString::number(nc,16));
2232 Separate();
2233 break;
2234 case TSANS:
2235 Append(u"font sans ");
2236 break;
2237 case TSERIF:
2238 Append(u"font serif ");
2239 break;
2240 case TFIXED:
2241 Append(u"font fixed ");
2242 break;
2243 default:
2244 break;
2246 LineToText( pNode->GetSubNode( 1 ) );
2249 void SmNodeToTextVisitor::Visit( SmUnHorNode* pNode )
2251 if(pNode->GetSubNode( 1 )->GetToken( ).eType == TFACT)
2253 // visit children in the reverse order
2254 for( auto it = pNode->rbegin(); it != pNode->rend(); ++it )
2256 auto pChild = *it;
2257 if(!pChild)
2258 continue;
2259 Separate( );
2260 pChild->Accept( this );
2263 else
2265 for( auto pChild : *pNode )
2267 if(!pChild)
2268 continue;
2269 Separate( );
2270 pChild->Accept( this );
2275 void SmNodeToTextVisitor::Visit( SmBinHorNode* pNode )
2277 const SmNode *pParent = pNode->GetParent();
2278 bool bBraceNeeded = pParent && pParent->GetType() == SmNodeType::Font;
2279 SmNode *pLeft = pNode->LeftOperand(),
2280 *pOper = pNode->Symbol(),
2281 *pRight = pNode->RightOperand();
2282 Separate( );
2283 if (bBraceNeeded)
2284 Append(u"{ ");
2285 pLeft->Accept( this );
2286 Separate( );
2287 pOper->Accept( this );
2288 Separate( );
2289 pRight->Accept( this );
2290 Separate( );
2291 if (bBraceNeeded)
2292 Append(u"} ");
2295 void SmNodeToTextVisitor::Visit( SmBinVerNode* pNode )
2297 if( pNode->GetToken().eType == TOVER ){
2298 SmNode *pNum = pNode->GetSubNode( 0 ),
2299 *pDenom = pNode->GetSubNode( 2 );
2300 Append(u"{ ");
2301 LineToText( pNum );
2302 Append(u"over");
2303 LineToText( pDenom );
2304 Append(u"} ");
2305 } else{
2306 SmNode *pNum = pNode->GetSubNode( 0 ),
2307 *pDenom = pNode->GetSubNode( 2 );
2308 Append(u"frac {");
2309 LineToText( pNum );
2310 Append(u"} {");
2311 LineToText( pDenom );
2312 Append(u"}");
2316 void SmNodeToTextVisitor::Visit( SmBinDiagonalNode* pNode )
2318 SmNode *pLeftOperand = pNode->GetSubNode( 0 ),
2319 *pRightOperand = pNode->GetSubNode( 1 );
2320 Append(u"{ ");
2321 LineToText( pLeftOperand );
2322 Separate( );
2323 Append(u"wideslash ");
2324 LineToText( pRightOperand );
2325 Append(u"} ");
2328 void SmNodeToTextVisitor::Visit( SmSubSupNode* pNode )
2330 if( pNode->GetToken().eType == TEVALUATE )
2332 Append(u"evaluate { ");
2333 pNode->GetSubNode( 0 )->GetSubNode( 1 )->Accept(this);
2334 Append(u"} ");
2335 SmNode* pChild = pNode->GetSubSup( RSUP );
2336 if( pChild ) {
2337 Separate( );
2338 Append(u"to { ");
2339 LineToText( pChild );
2340 Append(u"} ");
2342 pChild = pNode->GetSubSup( RSUB );
2343 if( pChild ) {
2344 Separate( );
2345 Append(u"from { ");
2346 LineToText( pChild );
2347 Append(u"} ");
2350 else
2352 LineToText( pNode->GetBody( ) );
2353 SmNode *pChild = pNode->GetSubSup( LSUP );
2354 if( pChild ) {
2355 Separate( );
2356 Append(u"lsup ");
2357 LineToText( pChild );
2359 pChild = pNode->GetSubSup( LSUB );
2360 if( pChild ) {
2361 Separate( );
2362 Append(u"lsub ");
2363 LineToText( pChild );
2365 pChild = pNode->GetSubSup( RSUP );
2366 if( pChild ) {
2367 Separate( );
2368 Append(u"^ ");
2369 LineToText( pChild );
2371 pChild = pNode->GetSubSup( RSUB );
2372 if( pChild ) {
2373 Separate( );
2374 Append(u"_ ");
2375 LineToText( pChild );
2377 pChild = pNode->GetSubSup( CSUP );
2378 if( pChild ) {
2379 Separate( );
2380 if (pNode->IsUseLimits())
2381 Append(u"to ");
2382 else
2383 Append(u"csup ");
2384 LineToText( pChild );
2386 pChild = pNode->GetSubSup( CSUB );
2387 if( pChild ) {
2388 Separate( );
2389 if (pNode->IsUseLimits())
2390 Append(u"from ");
2391 else
2392 Append(u"csub ");
2393 LineToText( pChild );
2398 void SmNodeToTextVisitor::Visit( SmMatrixNode* pNode )
2400 Append(u"matrix{");
2401 for (size_t i = 0; i < pNode->GetNumRows(); ++i)
2403 for (size_t j = 0; j < pNode->GetNumCols( ); ++j)
2405 SmNode* pSubNode = pNode->GetSubNode( i * pNode->GetNumCols( ) + j );
2406 Separate( );
2407 if (pSubNode)
2408 pSubNode->Accept( this );
2409 Separate( );
2410 if (j != pNode->GetNumCols() - 1U)
2411 Append(u"#");
2413 Separate( );
2414 if (i != pNode->GetNumRows() - 1U)
2415 Append(u"##");
2417 Append(u"} ");
2420 void SmNodeToTextVisitor::Visit( SmPlaceNode* )
2422 Append(u"<?>");
2425 void SmNodeToTextVisitor::Visit( SmTextNode* pNode )
2427 SmTokenType type = pNode->GetToken( ).eType;
2428 switch(type){
2429 case TTEXT:
2430 Append(u"\"");
2431 Append( pNode->GetToken().aText );
2432 Append(u"\"");
2433 break;
2434 case TNUMBER:
2435 Append( pNode->GetToken().aText );
2436 break;
2437 case TIDENT:
2438 Append( pNode->GetToken().aText );
2439 break;
2440 case TFUNC:
2441 Append(u"func ");
2442 Append( pNode->GetToken().aText );
2443 break;
2444 case THEX:
2445 Append(u"hex ");
2446 Append( pNode->GetToken().aText );
2447 break;
2448 default:
2449 Append( pNode->GetToken().aText );
2451 Separate( );
2454 void SmNodeToTextVisitor::Visit( SmSpecialNode* pNode )
2456 SmTokenType type = pNode->GetToken().eType;
2457 switch(type){
2458 case TLIMSUP:
2459 Append(u"lim sup ");
2460 break;
2461 case TLIMINF:
2462 Append(u"lim inf ");
2463 break;
2464 default:
2465 Append( pNode->GetToken().aText );
2466 break;
2470 void SmNodeToTextVisitor::Visit( SmGlyphSpecialNode* pNode )
2472 if( pNode->GetToken( ).eType == TBOPER )
2473 Append(u"boper ");
2474 else
2475 Append(u"uoper ");
2476 Append( pNode->GetToken( ).aText );
2479 //TODO to improve this it is required to improve mathmlimport.
2480 void SmNodeToTextVisitor::Visit( SmMathSymbolNode* pNode )
2482 if ( ( pNode->GetToken().nGroup & TG::LBrace )
2483 || ( pNode->GetToken().nGroup & TG::RBrace )
2484 || ( pNode->GetToken().nGroup & TG::Sum )
2485 || ( pNode->GetToken().nGroup & TG::Product )
2486 || ( pNode->GetToken().nGroup & TG::Relation )
2487 || ( pNode->GetToken().nGroup & TG::UnOper )
2488 || ( pNode->GetToken().nGroup & TG::Oper )
2490 Append( pNode->GetToken().aText );
2491 return;
2493 sal_Unicode cChar = pNode->GetToken().cMathChar[0];
2494 Separate( );
2495 switch(cChar){
2496 case MS_NONE:
2497 Append(u"none");
2498 break;
2499 case '{':
2500 Append(u"{");
2501 break;
2502 case '}':
2503 Append(u"}");
2504 break;
2505 case MS_VERTLINE:
2506 Append(u"mline");
2507 break;
2508 case MS_TILDE:
2509 Append(u"\"~\"");
2510 break;
2511 case MS_RIGHTARROW:
2512 if( pNode->GetToken().eType == TTOWARD ) Append(u"toward");
2513 else Append(u"rightarrow");
2514 break;
2515 case MS_LEFTARROW:
2516 Append(u"leftarrow");
2517 break;
2518 case MS_UPARROW:
2519 Append(u"uparrow");
2520 break;
2521 case MS_DOWNARROW:
2522 Append(u"downarrow");
2523 break;
2524 case MS_LAMBDABAR:
2525 Append(u"lambdabar");
2526 break;
2527 case MS_DOTSLOW:
2528 Append(u"dotslow");
2529 break;
2530 case MS_SETC:
2531 Append(u"setC");
2532 break;
2533 case MS_HBAR:
2534 Append(u"hbar");
2535 break;
2536 case MS_IM:
2537 Append(u"Im");
2538 break;
2539 case MS_SETN:
2540 Append(u"setN");
2541 break;
2542 case MS_WP:
2543 Append(u"wp");
2544 break;
2545 case MS_LAPLACE:
2546 Append(u"laplace");
2547 break;
2548 case MS_SETQ:
2549 Append(u"setQ");
2550 break;
2551 case MS_RE:
2552 Append(u"Re");
2553 break;
2554 case MS_SETR:
2555 Append(u"setR");
2556 break;
2557 case MS_SETZ:
2558 Append(u"setZ");
2559 break;
2560 case MS_ALEPH:
2561 Append(u"aleph");
2562 break;
2563 case 0x0362:
2564 Append(u"widevec");
2565 break;
2566 case MS_DLARROW:
2567 Append(u"dlarrow");
2568 break;
2569 case MS_DRARROW:
2570 Append(u"drarrow");
2571 break;
2572 case MS_DLRARROW:
2573 Append(u"dlrarrow");
2574 break;
2575 case MS_FORALL:
2576 Append(u"forall");
2577 break;
2578 case MS_PARTIAL:
2579 Append(u"partial");
2580 break;
2581 case MS_EXISTS:
2582 Append(u"exists");
2583 break;
2584 case MS_NOTEXISTS:
2585 Append(u"notexists");
2586 break;
2587 case MS_EMPTYSET:
2588 Append(u"emptyset");
2589 break;
2590 case MS_NABLA:
2591 Append(u"nabla");
2592 break;
2593 case MS_BACKEPSILON:
2594 Append(u"backepsilon");
2595 break;
2596 case MS_CIRC:
2597 Append(u"circ");
2598 break;
2599 case MS_INFINITY:
2600 Append(u"infinity");
2601 break;
2602 case 0x22b2: // NORMAL SUBGROUP OF
2603 Append(OUStringChar(cChar));
2604 break;
2605 case 0x22b3: // CONTAINS AS NORMAL SUBGROUP
2606 Append(OUStringChar(cChar));
2607 break;
2608 case MS_ORTHO:
2609 Append(u"ortho");
2610 break;
2611 case MS_DOTSVERT:
2612 Append(u"dotsvert");
2613 break;
2614 case MS_DOTSAXIS:
2615 Append(u"dotsaxis");
2616 break;
2617 case MS_DOTSUP:
2618 Append(u"dotsup");
2619 break;
2620 case MS_DOTSDOWN:
2621 Append(u"dotsdown");
2622 break;
2623 case '^':
2624 Append(u"^");
2625 break;
2626 case 0xe091:
2627 Append(u"widehat");
2628 break;
2629 case 0xe096:
2630 Append(u"widetilde");
2631 break;
2632 case 0xe098:
2633 Append(u"widevec");
2634 break;
2635 case 0xeb01: //no space
2636 case 0xeb08: //normal space
2637 break;
2638 case 0xef04: //tiny space
2639 case 0xef05: //tiny space
2640 case 0xeb02: //small space
2641 case 0xeb04: //medium space
2642 Append(u"`");
2643 break;
2644 case 0xeb05: //large space
2645 Append(u"~");
2646 break;
2647 case 0x3a9:
2648 Append(u"%OMEGA");
2649 break;
2650 default:
2651 Append(OUStringChar(cChar));
2652 break;
2656 void SmNodeToTextVisitor::Visit( SmBlankNode* pNode )
2658 sal_uInt16 nNum = pNode->GetBlankNum();
2659 if (nNum <= 0)
2660 return;
2661 sal_uInt16 nWide = nNum / 4;
2662 sal_uInt16 nNarrow = nNum % 4;
2663 for (sal_uInt16 i = 0; i < nWide; i++)
2664 Append(u"~");
2665 for (sal_uInt16 i = 0; i < nNarrow; i++)
2666 Append(u"`");
2667 Append(u" ");
2670 void SmNodeToTextVisitor::Visit( SmErrorNode* )
2674 void SmNodeToTextVisitor::Visit( SmLineNode* pNode )
2676 for( auto pChild : *pNode )
2678 if(!pChild)
2679 continue;
2680 Separate( );
2681 pChild->Accept( this );
2685 void SmNodeToTextVisitor::Visit( SmExpressionNode* pNode )
2687 bool bracketsNeeded = pNode->GetNumSubNodes() != 1 || pNode->GetSubNode(0)->GetType() == SmNodeType::BinHor;
2688 if (!bracketsNeeded)
2690 const SmNode *pParent = pNode->GetParent();
2691 // nested subsups
2692 bracketsNeeded =
2693 pParent && pParent->GetType() == SmNodeType::SubSup &&
2694 pNode->GetNumSubNodes() == 1 &&
2695 pNode->GetSubNode(0)->GetType() == SmNodeType::SubSup;
2698 if (bracketsNeeded) {
2699 Append(u"{ ");
2701 for( auto pChild : *pNode )
2703 if(!pChild)
2704 continue;
2705 pChild->Accept( this );
2706 Separate( );
2708 if (bracketsNeeded) {
2709 Append(u"} ");
2713 void SmNodeToTextVisitor::Visit( SmPolyLineNode* )
2717 void SmNodeToTextVisitor::Visit( SmRootNode* pNode )
2719 SmNode *pExtra = pNode->GetSubNode( 0 ),
2720 *pBody = pNode->GetSubNode( 2 );
2721 if( pExtra ) {
2722 Append(u"nroot");
2723 LineToText( pExtra );
2724 } else
2725 Append(u"sqrt");
2726 LineToText( pBody );
2729 void SmNodeToTextVisitor::Visit( SmRootSymbolNode* )
2733 void SmNodeToTextVisitor::Visit( SmRectangleNode* )
2737 void SmNodeToTextVisitor::Visit( SmVerticalBraceNode* pNode )
2739 SmNode *pBody = pNode->Body(),
2740 *pScript = pNode->Script();
2741 LineToText( pBody );
2742 Append( pNode->GetToken( ).aText );
2743 LineToText( pScript );
2746 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */