xref: /core/starmath/source/visitors.cxx (revision 9e92a17c)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  */
9 
10 #include <rtl/math.hxx>
11 #include <sal/log.hxx>
12 #include <tools/gen.hxx>
13 #include <vcl/lineinfo.hxx>
14 #include <visitors.hxx>
15 #include "tmpdevice.hxx"
16 #include <cursor.hxx>
17 
18 #include <starmathdatabase.hxx>
19 
20 // SmDefaultingVisitor
21 
Visit(SmTableNode * pNode)22 void SmDefaultingVisitor::Visit( SmTableNode* pNode )
23 {
24     DefaultVisit( pNode );
25 }
26 
Visit(SmBraceNode * pNode)27 void SmDefaultingVisitor::Visit( SmBraceNode* pNode )
28 {
29     DefaultVisit( pNode );
30 }
31 
Visit(SmBracebodyNode * pNode)32 void SmDefaultingVisitor::Visit( SmBracebodyNode* pNode )
33 {
34     DefaultVisit( pNode );
35 }
36 
Visit(SmOperNode * pNode)37 void SmDefaultingVisitor::Visit( SmOperNode* pNode )
38 {
39     DefaultVisit( pNode );
40 }
41 
Visit(SmAlignNode * pNode)42 void SmDefaultingVisitor::Visit( SmAlignNode* pNode )
43 {
44     DefaultVisit( pNode );
45 }
46 
Visit(SmAttributeNode * pNode)47 void SmDefaultingVisitor::Visit( SmAttributeNode* pNode )
48 {
49     DefaultVisit( pNode );
50 }
51 
Visit(SmFontNode * pNode)52 void SmDefaultingVisitor::Visit( SmFontNode* pNode )
53 {
54     DefaultVisit( pNode );
55 }
56 
Visit(SmUnHorNode * pNode)57 void SmDefaultingVisitor::Visit( SmUnHorNode* pNode )
58 {
59     DefaultVisit( pNode );
60 }
61 
Visit(SmBinHorNode * pNode)62 void SmDefaultingVisitor::Visit( SmBinHorNode* pNode )
63 {
64     DefaultVisit( pNode );
65 }
66 
Visit(SmBinVerNode * pNode)67 void SmDefaultingVisitor::Visit( SmBinVerNode* pNode )
68 {
69     DefaultVisit( pNode );
70 }
71 
Visit(SmBinDiagonalNode * pNode)72 void SmDefaultingVisitor::Visit( SmBinDiagonalNode* pNode )
73 {
74     DefaultVisit( pNode );
75 }
76 
Visit(SmSubSupNode * pNode)77 void SmDefaultingVisitor::Visit( SmSubSupNode* pNode )
78 {
79     DefaultVisit( pNode );
80 }
81 
Visit(SmMatrixNode * pNode)82 void SmDefaultingVisitor::Visit( SmMatrixNode* pNode )
83 {
84     DefaultVisit( pNode );
85 }
86 
Visit(SmPlaceNode * pNode)87 void SmDefaultingVisitor::Visit( SmPlaceNode* pNode )
88 {
89     DefaultVisit( pNode );
90 }
91 
Visit(SmTextNode * pNode)92 void SmDefaultingVisitor::Visit( SmTextNode* pNode )
93 {
94     DefaultVisit( pNode );
95 }
96 
Visit(SmSpecialNode * pNode)97 void SmDefaultingVisitor::Visit( SmSpecialNode* pNode )
98 {
99     DefaultVisit( pNode );
100 }
101 
Visit(SmGlyphSpecialNode * pNode)102 void SmDefaultingVisitor::Visit( SmGlyphSpecialNode* pNode )
103 {
104     DefaultVisit( pNode );
105 }
106 
Visit(SmMathSymbolNode * pNode)107 void SmDefaultingVisitor::Visit( SmMathSymbolNode* pNode )
108 {
109     DefaultVisit( pNode );
110 }
111 
Visit(SmBlankNode * pNode)112 void SmDefaultingVisitor::Visit( SmBlankNode* pNode )
113 {
114     DefaultVisit( pNode );
115 }
116 
Visit(SmErrorNode * pNode)117 void SmDefaultingVisitor::Visit( SmErrorNode* pNode )
118 {
119     DefaultVisit( pNode );
120 }
121 
Visit(SmLineNode * pNode)122 void SmDefaultingVisitor::Visit( SmLineNode* pNode )
123 {
124     DefaultVisit( pNode );
125 }
126 
Visit(SmExpressionNode * pNode)127 void SmDefaultingVisitor::Visit( SmExpressionNode* pNode )
128 {
129     DefaultVisit( pNode );
130 }
131 
Visit(SmPolyLineNode * pNode)132 void SmDefaultingVisitor::Visit( SmPolyLineNode* pNode )
133 {
134     DefaultVisit( pNode );
135 }
136 
Visit(SmRootNode * pNode)137 void SmDefaultingVisitor::Visit( SmRootNode* pNode )
138 {
139     DefaultVisit( pNode );
140 }
141 
Visit(SmRootSymbolNode * pNode)142 void SmDefaultingVisitor::Visit( SmRootSymbolNode* pNode )
143 {
144     DefaultVisit( pNode );
145 }
146 
Visit(SmRectangleNode * pNode)147 void SmDefaultingVisitor::Visit( SmRectangleNode* pNode )
148 {
149     DefaultVisit( pNode );
150 }
151 
Visit(SmVerticalBraceNode * pNode)152 void SmDefaultingVisitor::Visit( SmVerticalBraceNode* pNode )
153 {
154     DefaultVisit( pNode );
155 }
156 
157 // SmCaretLinesVisitor
158 
SmCaretLinesVisitor(OutputDevice & rDevice,SmCaretPos position,Point offset)159 SmCaretLinesVisitor::SmCaretLinesVisitor(OutputDevice& rDevice, SmCaretPos position, Point offset)
160     : mrDev(rDevice)
161     , maPos(position)
162     , maOffset(offset)
163 {
164 }
165 
DoIt()166 void SmCaretLinesVisitor::DoIt()
167 {
168     SAL_WARN_IF(!maPos.IsValid(), "starmath", "Cannot draw invalid position!");
169     if (!maPos.IsValid())
170         return;
171 
172     //Save device state
173     mrDev.Push( vcl::PushFlags::FONT | vcl::PushFlags::MAPMODE | vcl::PushFlags::LINECOLOR | vcl::PushFlags::FILLCOLOR | vcl::PushFlags::TEXTCOLOR );
174 
175     maPos.pSelectedNode->Accept( this );
176     //Restore device state
177     mrDev.Pop( );
178 }
179 
Visit(SmTextNode * pNode)180 void SmCaretLinesVisitor::Visit( SmTextNode* pNode )
181 {
182     tools::Long i = maPos.nIndex;
183 
184     mrDev.SetFont( pNode->GetFont( ) );
185 
186     //Find the line
187     SmNode* pLine = SmCursor::FindTopMostNodeInLine( pNode );
188 
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( );
195 
196     // Vertical line
197     ProcessCaretLine({ left, top }, { left, top + height });
198 
199     // Underline
200     ProcessUnderline({ left_line, top + height }, { right_line, top + height });
201 }
202 
DefaultVisit(SmNode * pNode)203 void SmCaretLinesVisitor::DefaultVisit( SmNode* pNode )
204 {
205     //Find the line
206     SmNode* pLine = SmCursor::FindTopMostNodeInLine( pNode );
207 
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( );
214 
215     // Vertical line
216     ProcessCaretLine({ left, top }, { left, top + height });
217 
218     // Underline
219     ProcessUnderline({ left_line, top + height }, { right_line, top + height });
220 }
221 
222 // SmCaretRectanglesVisitor
223 
SmCaretRectanglesVisitor(OutputDevice & rDevice,SmCaretPos position)224 SmCaretRectanglesVisitor::SmCaretRectanglesVisitor(OutputDevice& rDevice, SmCaretPos position)
225     : SmCaretLinesVisitor(rDevice, position, {})
226 {
227     DoIt();
228 }
229 
ProcessCaretLine(Point from,Point to)230 void SmCaretRectanglesVisitor::ProcessCaretLine(Point from, Point to) { maCaret = { from, to }; }
ProcessUnderline(Point,Point)231 void SmCaretRectanglesVisitor::ProcessUnderline(Point /*from*/, Point /*to*/) {} // No underline
232 
233 // SmCaretDrawingVisitor
234 
SmCaretDrawingVisitor(OutputDevice & rDevice,SmCaretPos position,Point offset,bool caretVisible)235 SmCaretDrawingVisitor::SmCaretDrawingVisitor( OutputDevice& rDevice,
236                                              SmCaretPos position,
237                                              Point offset,
238                                              bool caretVisible )
239     : SmCaretLinesVisitor(rDevice, position, offset)
240     , mbCaretVisible( caretVisible )
241 {
242     DoIt();
243 }
244 
ProcessCaretLine(Point from,Point to)245 void SmCaretDrawingVisitor::ProcessCaretLine(Point from, Point to)
246 {
247     if ( mbCaretVisible ) {
248         //Set color
249         getDev().SetLineColor(COL_BLACK);
250         //Draw vertical line
251         getDev().DrawLine(from, to);
252     }
253 }
254 
ProcessUnderline(Point from,Point to)255 void SmCaretDrawingVisitor::ProcessUnderline(Point from, Point to)
256 {
257     //Set color
258     getDev().SetLineColor(COL_BLACK);
259     //Underline the line
260     getDev().DrawLine(from, to);
261 }
262 
263 // SmCaretPos2LineVisitor
264 
Visit(SmTextNode * pNode)265 void SmCaretPos2LineVisitor::Visit( SmTextNode* pNode )
266 {
267     //Save device state
268     mpDev->Push( vcl::PushFlags::FONT | vcl::PushFlags::TEXTCOLOR );
269 
270     tools::Long i = maPos.nIndex;
271 
272     mpDev->SetFont( pNode->GetFont( ) );
273 
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( );
278 
279     maLine = SmCaretLine( left, top, height );
280 
281     //Restore device state
282     mpDev->Pop( );
283 }
284 
DefaultVisit(SmNode * pNode)285 void SmCaretPos2LineVisitor::DefaultVisit( SmNode* pNode )
286 {
287     //Vertical line ( code from SmCaretDrawingVisitor )
288     Point p1 = pNode->GetTopLeft( );
289     if( maPos.nIndex == 1 )
290         p1.Move( pNode->GetWidth( ), 0 );
291 
292     maLine = SmCaretLine( p1.X( ), p1.Y( ), pNode->GetHeight( ) );
293 }
294 
295 
296 // SmDrawingVisitor
297 
Visit(SmTableNode * pNode)298 void SmDrawingVisitor::Visit( SmTableNode* pNode )
299 {
300     DrawChildren( pNode );
301 }
302 
Visit(SmBraceNode * pNode)303 void SmDrawingVisitor::Visit( SmBraceNode* pNode )
304 {
305     DrawChildren( pNode );
306 }
307 
Visit(SmBracebodyNode * pNode)308 void SmDrawingVisitor::Visit( SmBracebodyNode* pNode )
309 {
310     DrawChildren( pNode );
311 }
312 
Visit(SmOperNode * pNode)313 void SmDrawingVisitor::Visit( SmOperNode* pNode )
314 {
315     DrawChildren( pNode );
316 }
317 
Visit(SmAlignNode * pNode)318 void SmDrawingVisitor::Visit( SmAlignNode* pNode )
319 {
320     DrawChildren( pNode );
321 }
322 
Visit(SmAttributeNode * pNode)323 void SmDrawingVisitor::Visit( SmAttributeNode* pNode )
324 {
325     DrawChildren( pNode );
326 }
327 
Visit(SmFontNode * pNode)328 void SmDrawingVisitor::Visit( SmFontNode* pNode )
329 {
330     DrawChildren( pNode );
331 }
332 
Visit(SmUnHorNode * pNode)333 void SmDrawingVisitor::Visit( SmUnHorNode* pNode )
334 {
335     DrawChildren( pNode );
336 }
337 
Visit(SmBinHorNode * pNode)338 void SmDrawingVisitor::Visit( SmBinHorNode* pNode )
339 {
340     DrawChildren( pNode );
341 }
342 
Visit(SmBinVerNode * pNode)343 void SmDrawingVisitor::Visit( SmBinVerNode* pNode )
344 {
345     DrawChildren( pNode );
346 }
347 
Visit(SmBinDiagonalNode * pNode)348 void SmDrawingVisitor::Visit( SmBinDiagonalNode* pNode )
349 {
350     DrawChildren( pNode );
351 }
352 
Visit(SmSubSupNode * pNode)353 void SmDrawingVisitor::Visit( SmSubSupNode* pNode )
354 {
355     DrawChildren( pNode );
356 }
357 
Visit(SmMatrixNode * pNode)358 void SmDrawingVisitor::Visit( SmMatrixNode* pNode )
359 {
360     DrawChildren( pNode );
361 }
362 
Visit(SmPlaceNode * pNode)363 void SmDrawingVisitor::Visit( SmPlaceNode* pNode )
364 {
365     DrawSpecialNode( pNode );
366 }
367 
Visit(SmTextNode * pNode)368 void SmDrawingVisitor::Visit( SmTextNode* pNode )
369 {
370     DrawTextNode( pNode );
371 }
372 
Visit(SmSpecialNode * pNode)373 void SmDrawingVisitor::Visit( SmSpecialNode* pNode )
374 {
375     DrawSpecialNode( pNode );
376 }
377 
Visit(SmGlyphSpecialNode * pNode)378 void SmDrawingVisitor::Visit( SmGlyphSpecialNode* pNode )
379 {
380     DrawSpecialNode( pNode );
381 }
382 
Visit(SmMathSymbolNode * pNode)383 void SmDrawingVisitor::Visit( SmMathSymbolNode* pNode )
384 {
385     DrawSpecialNode( pNode );
386 }
387 
Visit(SmBlankNode *)388 void SmDrawingVisitor::Visit( SmBlankNode* )
389 {
390 }
391 
Visit(SmErrorNode * pNode)392 void SmDrawingVisitor::Visit( SmErrorNode* pNode )
393 {
394     DrawSpecialNode( pNode );
395 }
396 
Visit(SmLineNode * pNode)397 void SmDrawingVisitor::Visit( SmLineNode* pNode )
398 {
399     DrawChildren( pNode );
400 }
401 
Visit(SmExpressionNode * pNode)402 void SmDrawingVisitor::Visit( SmExpressionNode* pNode )
403 {
404     DrawChildren( pNode );
405 }
406 
Visit(SmRootNode * pNode)407 void SmDrawingVisitor::Visit( SmRootNode* pNode )
408 {
409     DrawChildren( pNode );
410 }
411 
Visit(SmVerticalBraceNode * pNode)412 void SmDrawingVisitor::Visit( SmVerticalBraceNode* pNode )
413 {
414     DrawChildren( pNode );
415 }
416 
Visit(SmRootSymbolNode * pNode)417 void SmDrawingVisitor::Visit( SmRootSymbolNode* pNode )
418 {
419     if ( pNode->IsPhantom( ) )
420         return;
421 
422     // draw root-sign itself
423     DrawSpecialNode( pNode );
424 
425     SmTmpDevice aTmpDev( mrDev, true );
426     aTmpDev.SetFillColor( pNode->GetFont( ).GetColor( ) );
427     mrDev.SetLineColor( );
428     aTmpDev.SetFont( pNode->GetFont( ) );
429 
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 );
438 
439     tools::Rectangle  aBar( aBarPos, Size( nBarWidth, nBarHeight ) );
440 
441     if (mrFormat.IsRightToLeft() && mrDev.GetOutDevType() != OUTDEV_WINDOW)
442         mrDev.ReMirror(aBar);
443 
444     mrDev.DrawRect( aBar );
445 }
446 
Visit(SmPolyLineNode * pNode)447 void SmDrawingVisitor::Visit( SmPolyLineNode* pNode )
448 {
449     if ( pNode->IsPhantom( ) )
450         return;
451 
452     tools::Long nBorderwidth = pNode->GetFont( ).GetBorderWidth( );
453 
454     LineInfo  aInfo;
455     aInfo.SetWidth( pNode->GetWidth( ) - 2 * nBorderwidth );
456 
457     Point aOffset ( Point( ) - pNode->GetPolygon( ).GetBoundRect( ).TopLeft( )
458                    + Point( nBorderwidth, nBorderwidth ) ),
459           aPos ( maPosition + aOffset );
460 
461     if (mrFormat.IsRightToLeft() && mrDev.GetOutDevType() != OUTDEV_WINDOW)
462         mrDev.ReMirror(aPos);
463 
464     pNode->GetPolygon( ).Move( aPos.X( ), aPos.Y( ) );    //Works because Polygon wraps a pointer
465 
466     SmTmpDevice aTmpDev ( mrDev, false );
467     aTmpDev.SetLineColor( pNode->GetFont( ).GetColor( ) );
468 
469     mrDev.DrawPolyLine( pNode->GetPolygon( ), aInfo );
470 }
471 
Visit(SmRectangleNode * pNode)472 void SmDrawingVisitor::Visit( SmRectangleNode* pNode )
473 {
474     if ( pNode->IsPhantom( ) )
475         return;
476 
477     SmTmpDevice aTmpDev ( mrDev, false );
478     aTmpDev.SetFillColor( pNode->GetFont( ).GetColor( ) );
479     mrDev.SetLineColor( );
480     aTmpDev.SetFont( pNode->GetFont( ) );
481 
482     sal_uLong  nTmpBorderWidth = pNode->GetFont( ).GetBorderWidth( );
483 
484     // get rectangle and remove borderspace
485     tools::Rectangle  aTmp ( pNode->AsRectangle( ) + maPosition - pNode->GetTopLeft( ) );
486     aTmp.AdjustLeft(nTmpBorderWidth );
487     aTmp.AdjustRight( -sal_Int32(nTmpBorderWidth) );
488     aTmp.AdjustTop(nTmpBorderWidth );
489     aTmp.AdjustBottom( -sal_Int32(nTmpBorderWidth) );
490 
491     SAL_WARN_IF( aTmp.IsEmpty(), "starmath", "Empty rectangle" );
492 
493     if (mrFormat.IsRightToLeft() && mrDev.GetOutDevType() != OUTDEV_WINDOW)
494         mrDev.ReMirror(aTmp);
495 
496     mrDev.DrawRect( aTmp );
497 }
498 
DrawTextNode(SmTextNode * pNode)499 void SmDrawingVisitor::DrawTextNode( SmTextNode* pNode )
500 {
501     if ( pNode->IsPhantom() || pNode->GetText().isEmpty() || pNode->GetText()[0] == '\0' )
502         return;
503 
504     SmTmpDevice aTmpDev ( mrDev, false );
505     aTmpDev.SetFont( pNode->GetFont( ) );
506 
507     Point  aPos ( maPosition );
508     aPos.AdjustY(pNode->GetBaselineOffset( ) );
509 
510     if (mrFormat.IsRightToLeft() && mrDev.GetOutDevType() != OUTDEV_WINDOW)
511         mrDev.ReMirror(aPos);
512 
513     mrDev.DrawStretchText( aPos, pNode->GetWidth( ), pNode->GetText( ) );
514 }
515 
DrawSpecialNode(SmSpecialNode * pNode)516 void SmDrawingVisitor::DrawSpecialNode( SmSpecialNode* pNode )
517 {
518     //! since this chars might come from any font, that we may not have
519     //! set to ALIGN_BASELINE yet, we do it now.
520     pNode->GetFont( ).SetAlignment( ALIGN_BASELINE );
521 
522     DrawTextNode( pNode );
523 }
524 
DrawChildren(SmStructureNode * pNode)525 void SmDrawingVisitor::DrawChildren( SmStructureNode* pNode )
526 {
527     if ( pNode->IsPhantom( ) )
528         return;
529 
530     Point rPosition = maPosition;
531 
532     for( auto pChild : *pNode )
533     {
534         if(!pChild)
535             continue;
536         Point  aOffset ( pChild->GetTopLeft( ) - pNode->GetTopLeft( ) );
537         maPosition = rPosition + aOffset;
538         pChild->Accept( this );
539     }
540 }
541 
542 // SmSetSelectionVisitor
543 
SmSetSelectionVisitor(SmCaretPos startPos,SmCaretPos endPos,SmNode * pTree)544 SmSetSelectionVisitor::SmSetSelectionVisitor( SmCaretPos startPos, SmCaretPos endPos, SmNode* pTree)
545     : maStartPos(startPos)
546     , maEndPos(endPos)
547     , mbSelecting(false)
548 {
549     //Assume that pTree is a SmTableNode
550     SAL_WARN_IF(pTree->GetType() != SmNodeType::Table, "starmath", "pTree should be a SmTableNode!");
551     //Visit root node, this is special as this node cannot be selected, but its children can!
552     if(pTree->GetType() == SmNodeType::Table){
553         //Change state if maStartPos is in front of this node
554         if( maStartPos.pSelectedNode == pTree && maStartPos.nIndex == 0 )
555             mbSelecting = !mbSelecting;
556         //Change state if maEndPos is in front of this node
557         if( maEndPos.pSelectedNode == pTree && maEndPos.nIndex == 0 )
558             mbSelecting = !mbSelecting;
559         SAL_WARN_IF(mbSelecting, "starmath", "Caret positions needed to set mbSelecting about, shouldn't be possible!");
560 
561         //Visit lines
562         for( auto pChild : *static_cast<SmStructureNode*>(pTree) )
563         {
564             if(!pChild)
565                 continue;
566             pChild->Accept( this );
567             //If we started a selection in this line and it haven't ended, we do that now!
568             if(mbSelecting) {
569                 mbSelecting = false;
570                 SetSelectedOnAll(pChild);
571                 //Set maStartPos and maEndPos to invalid positions, this ensures that an unused
572                 //start or end (because we forced end above), doesn't start a new selection.
573                 maStartPos = maEndPos = SmCaretPos();
574             }
575         }
576         //Check if pTree isn't selected
577         SAL_WARN_IF(pTree->IsSelected(), "starmath", "pTree should never be selected!");
578         //Discard the selection if there's a bug (it's better than crashing)
579         if(pTree->IsSelected())
580             SetSelectedOnAll(pTree, false);
581     }else //This shouldn't happen, but I don't see any reason to die if it does
582         pTree->Accept(this);
583 }
584 
SetSelectedOnAll(SmNode * pSubTree,bool IsSelected)585 void SmSetSelectionVisitor::SetSelectedOnAll( SmNode* pSubTree, bool IsSelected ) {
586     pSubTree->SetSelected( IsSelected );
587 
588     if(pSubTree->GetNumSubNodes() == 0)
589         return;
590     //Quick BFS to set all selections
591     for( auto pChild : *static_cast<SmStructureNode*>(pSubTree) )
592     {
593         if(!pChild)
594             continue;
595         SetSelectedOnAll( pChild, IsSelected );
596     }
597 }
598 
DefaultVisit(SmNode * pNode)599 void SmSetSelectionVisitor::DefaultVisit( SmNode* pNode ) {
600     //Change state if maStartPos is in front of this node
601     if( maStartPos.pSelectedNode == pNode && maStartPos.nIndex == 0 )
602         mbSelecting = !mbSelecting;
603     //Change state if maEndPos is in front of this node
604     if( maEndPos.pSelectedNode == pNode && maEndPos.nIndex == 0 )
605         mbSelecting = !mbSelecting;
606 
607     //Cache current state
608     bool WasSelecting = mbSelecting;
609     bool ChangedState = false;
610 
611     //Set selected
612     pNode->SetSelected( mbSelecting );
613 
614     //Visit children
615     if(pNode->GetNumSubNodes() > 0)
616     {
617         for( auto pChild : *static_cast<SmStructureNode*>(pNode) )
618         {
619             if(!pChild)
620                 continue;
621             pChild->Accept( this );
622             ChangedState = ( WasSelecting != mbSelecting ) || ChangedState;
623         }
624     }
625 
626     //If state changed
627     if( ChangedState )
628     {
629         //Select this node and all of its children
630         //(Make exception for SmBracebodyNode)
631         if( pNode->GetType() != SmNodeType::Bracebody ||
632             !pNode->GetParent() ||
633             pNode->GetParent()->GetType() != SmNodeType::Brace )
634             SetSelectedOnAll( pNode );
635         else
636             SetSelectedOnAll( pNode->GetParent() );
637         /* If the equation is:      sqrt{2 + 4} + 5
638          * And the selection is:    sqrt{2 + [4} +] 5
639          *      Where [ denotes maStartPos and ] denotes maEndPos
640          * Then the sqrt node should be selected, so that the
641          * effective selection is:  [sqrt{2 + 4} +] 5
642          * The same is the case if we swap maStartPos and maEndPos.
643          */
644     }
645 
646     //Change state if maStartPos is after this node
647     if( maStartPos.pSelectedNode == pNode && maStartPos.nIndex == 1 )
648     {
649         mbSelecting = !mbSelecting;
650     }
651     //Change state if maEndPos is after of this node
652     if( maEndPos.pSelectedNode == pNode && maEndPos.nIndex == 1 )
653     {
654         mbSelecting = !mbSelecting;
655     }
656 }
657 
VisitCompositionNode(SmStructureNode * pNode)658 void SmSetSelectionVisitor::VisitCompositionNode( SmStructureNode* pNode )
659 {
660     //Change state if maStartPos is in front of this node
661     if( maStartPos.pSelectedNode == pNode && maStartPos.nIndex == 0 )
662         mbSelecting = !mbSelecting;
663     //Change state if maEndPos is in front of this node
664     if( maEndPos.pSelectedNode == pNode && maEndPos.nIndex == 0 )
665         mbSelecting = !mbSelecting;
666 
667     //Cache current state
668     bool WasSelecting = mbSelecting;
669 
670     //Visit children
671     for( auto pChild : *pNode )
672     {
673         if(!pChild)
674             continue;
675         pChild->Accept( this );
676     }
677 
678     //Set selected, if everything was selected
679     pNode->SetSelected( WasSelecting && mbSelecting );
680 
681     //Change state if maStartPos is after this node
682     if( maStartPos.pSelectedNode == pNode && maStartPos.nIndex == 1 )
683         mbSelecting = !mbSelecting;
684     //Change state if maEndPos is after of this node
685     if( maEndPos.pSelectedNode == pNode && maEndPos.nIndex == 1 )
686         mbSelecting = !mbSelecting;
687 }
688 
Visit(SmTextNode * pNode)689 void SmSetSelectionVisitor::Visit( SmTextNode* pNode ) {
690     tools::Long    i1 = -1,
691             i2 = -1;
692     if( maStartPos.pSelectedNode == pNode )
693         i1 = maStartPos.nIndex;
694     if( maEndPos.pSelectedNode == pNode )
695         i2 = maEndPos.nIndex;
696 
697     tools::Long start, end;
698     pNode->SetSelected(true);
699     if( i1 != -1 && i2 != -1 ) {
700         start = std::min(i1, i2);
701         end   = std::max(i1, i2);
702     } else if( mbSelecting && i1 != -1 ) {
703         start = 0;
704         end = i1;
705         mbSelecting = false;
706     } else if( mbSelecting && i2 != -1 ) {
707         start = 0;
708         end = i2;
709         mbSelecting = false;
710     } else if( !mbSelecting && i1 != -1 ) {
711         start = i1;
712         end = pNode->GetText().getLength();
713         mbSelecting = true;
714     } else if( !mbSelecting && i2 != -1 ) {
715         start = i2;
716         end = pNode->GetText().getLength();
717         mbSelecting = true;
718     } else if( mbSelecting ) {
719         start = 0;
720         end = pNode->GetText().getLength();
721     } else {
722         pNode->SetSelected( false );
723         start = 0;
724         end = 0;
725     }
726     pNode->SetSelected( start != end );
727     pNode->SetSelectionStart( start );
728     pNode->SetSelectionEnd( end );
729 }
730 
Visit(SmExpressionNode * pNode)731 void SmSetSelectionVisitor::Visit( SmExpressionNode* pNode ) {
732     VisitCompositionNode( pNode );
733 }
734 
Visit(SmLineNode * pNode)735 void SmSetSelectionVisitor::Visit( SmLineNode* pNode ) {
736     VisitCompositionNode( pNode );
737 }
738 
Visit(SmAlignNode * pNode)739 void SmSetSelectionVisitor::Visit( SmAlignNode* pNode ) {
740     VisitCompositionNode( pNode );
741 }
742 
Visit(SmBinHorNode * pNode)743 void SmSetSelectionVisitor::Visit( SmBinHorNode* pNode ) {
744     VisitCompositionNode( pNode );
745 }
746 
Visit(SmUnHorNode * pNode)747 void SmSetSelectionVisitor::Visit( SmUnHorNode* pNode ) {
748     VisitCompositionNode( pNode );
749 }
750 
Visit(SmFontNode * pNode)751 void SmSetSelectionVisitor::Visit( SmFontNode* pNode ) {
752     VisitCompositionNode( pNode );
753 }
754 
755 // SmCaretPosGraphBuildingVisitor
756 
SmCaretPosGraphBuildingVisitor(SmNode * pRootNode)757 SmCaretPosGraphBuildingVisitor::SmCaretPosGraphBuildingVisitor( SmNode* pRootNode )
758     : mpRightMost(nullptr)
759     , mpGraph(new SmCaretPosGraph)
760 {
761     //pRootNode should always be a table
762     SAL_WARN_IF( pRootNode->GetType( ) != SmNodeType::Table, "starmath", "pRootNode must be a table node");
763     //Handle the special case where SmNodeType::Table is used a rootnode
764     if( pRootNode->GetType( ) == SmNodeType::Table ){
765         //Children are SmLineNodes
766         //Or so I thought... Apparently, the children can be instances of SmExpression
767         //especially if there's an error in the formula... So here we go, a simple work around.
768         for( auto pChild : *static_cast<SmStructureNode*>(pRootNode) )
769         {
770             if(!pChild)
771                 continue;
772             mpRightMost = mpGraph->Add( SmCaretPos( pChild, 0 ) );
773             pChild->Accept( this );
774         }
775     }else
776         pRootNode->Accept(this);
777 }
778 
~SmCaretPosGraphBuildingVisitor()779 SmCaretPosGraphBuildingVisitor::~SmCaretPosGraphBuildingVisitor()
780 {
781 }
782 
Visit(SmLineNode * pNode)783 void SmCaretPosGraphBuildingVisitor::Visit( SmLineNode* pNode ){
784     for( auto pChild : *pNode )
785     {
786         if(!pChild)
787             continue;
788         pChild->Accept( this );
789     }
790 }
791 
792 /** Build SmCaretPosGraph for SmTableNode
793  * This method covers cases where SmTableNode is used in a binom or stack,
794  * the special case where it is used as root node for the entire formula is
795  * handled in the constructor.
796  */
Visit(SmTableNode * pNode)797 void SmCaretPosGraphBuildingVisitor::Visit( SmTableNode* pNode ){
798     SmCaretPosGraphEntry *left  = mpRightMost,
799                          *right = mpGraph->Add( SmCaretPos( pNode, 1) );
800     bool bIsFirst = true;
801     for( auto pChild : *pNode )
802     {
803         if(!pChild)
804             continue;
805         mpRightMost = mpGraph->Add( SmCaretPos( pChild, 0 ), left);
806         if(bIsFirst)
807             left->SetRight(mpRightMost);
808         pChild->Accept( this );
809         mpRightMost->SetRight(right);
810         if(bIsFirst)
811             right->SetLeft(mpRightMost);
812         bIsFirst = false;
813     }
814     mpRightMost = right;
815 }
816 
817 /** Build SmCaretPosGraph for SmSubSupNode
818  *
819  * The child positions in a SubSupNode, where H is the body:
820  * \code
821  *      CSUP
822  *
823  * LSUP H  H RSUP
824  *      H  H
825  *      HHHH
826  *      H  H
827  * LSUB H  H RSUB
828  *
829  *      CSUB
830  * \endcode
831  *
832  * Graph over these, where "left" is before the SmSubSupNode and "right" is after:
833  * \dot
834  *  digraph Graph{
835  *      left -> H;
836  *      H -> right;
837  *      LSUP -> H;
838  *      LSUB -> H;
839  *      CSUP -> right;
840  *      CSUB -> right;
841  *      RSUP -> right;
842  *      RSUB -> right;
843  *  };
844  * \enddot
845  *
846  */
Visit(SmSubSupNode * pNode)847 void SmCaretPosGraphBuildingVisitor::Visit( SmSubSupNode* pNode )
848 {
849     SmCaretPosGraphEntry *left,
850                          *right,
851                          *bodyLeft,
852                          *bodyRight;
853 
854     assert(mpRightMost);
855     left = mpRightMost;
856 
857     //Create bodyLeft
858     SAL_WARN_IF( !pNode->GetBody(), "starmath", "SmSubSupNode Doesn't have a body!" );
859     bodyLeft = mpGraph->Add( SmCaretPos( pNode->GetBody( ), 0 ), left );
860     left->SetRight( bodyLeft ); //TODO: Don't make this if LSUP or LSUB are NULL ( not sure??? )
861 
862     //Create right
863     right = mpGraph->Add( SmCaretPos( pNode, 1 ) );
864 
865     //Visit the body, to get bodyRight
866     mpRightMost = bodyLeft;
867     pNode->GetBody( )->Accept( this );
868     bodyRight = mpRightMost;
869     bodyRight->SetRight( right );
870     right->SetLeft( bodyRight );
871 
872     SmNode* pChild;
873     for (SmSubSup const nodeType : { LSUP, LSUB, CSUP, CSUB, RSUP, RSUB })
874     {
875         pChild = pNode->GetSubSup(nodeType);
876         if( pChild )
877         {
878             SmCaretPosGraphEntry *cLeft; //Child left
879             cLeft = mpGraph->Add( SmCaretPos( pChild, 0 ), ((nodeType == RSUP) || (nodeType == RSUB))?bodyRight:left );
880 
881             mpRightMost = cLeft;
882             pChild->Accept( this );
883 
884             mpRightMost->SetRight( ((nodeType == LSUP) || (nodeType == LSUB))?bodyLeft:right );
885         }
886     }
887 
888     //Set return parameters
889     mpRightMost = right;
890 }
891 
892 /** Build caret position for SmOperNode
893  *
894  * If first child is an SmSubSupNode we will ignore its
895  * body, as this body is a SmMathSymbol, for SUM, INT or similar
896  * that shouldn't be subject to modification.
897  * If first child is not a SmSubSupNode, ignore it completely
898  * as it is a SmMathSymbol.
899  *
900  * The child positions in a SmOperNode, where H is symbol, e.g. int, sum or similar:
901  * \code
902  *       TO
903  *
904  * LSUP H  H RSUP    BBB    BB  BBB  B   B
905  *      H  H         B  B  B  B B  B  B B
906  *      HHHH         BBB   B  B B  B   B
907  *      H  H         B  B  B  B B  B   B
908  * LSUB H  H RSUB    BBB    BB  BBB    B
909  *
910  *      FROM
911  * \endcode
912  * Notice, CSUP, etc. are actually grandchildren, but inorder to ignore H, these are visited
913  * from here. If they are present, that is if pOper is an instance of SmSubSupNode.
914  *
915  * Graph over these, where "left" is before the SmOperNode and "right" is after:
916  * \dot
917  *  digraph Graph{
918  *      left -> BODY;
919  *      BODY -> right;
920  *      LSUP -> BODY;
921  *      LSUB -> BODY;
922  *      TO   -> BODY;
923  *      FROM -> BODY;
924  *      RSUP -> BODY;
925  *      RSUB -> BODY;
926  *  };
927  * \enddot
928  */
Visit(SmOperNode * pNode)929 void SmCaretPosGraphBuildingVisitor::Visit( SmOperNode* pNode )
930 {
931     SmNode *pOper = pNode->GetSubNode( 0 ),
932            *pBody = pNode->GetSubNode( 1 );
933 
934     SmCaretPosGraphEntry *left = mpRightMost,
935                          *bodyLeft,
936                          *bodyRight,
937                          *right;
938     //Create body left
939     bodyLeft = mpGraph->Add( SmCaretPos( pBody, 0 ), left );
940     left->SetRight( bodyLeft );
941 
942     //Visit body, get bodyRight
943     mpRightMost = bodyLeft;
944     pBody->Accept( this );
945     bodyRight = mpRightMost;
946 
947     //Create right
948     right = mpGraph->Add( SmCaretPos( pNode, 1 ), bodyRight );
949     bodyRight->SetRight( right );
950 
951     //Get subsup pNode if any
952     SmSubSupNode* pSubSup = pOper->GetType( ) == SmNodeType::SubSup ? static_cast<SmSubSupNode*>(pOper) : nullptr;
953 
954     if( pSubSup ) {
955         SmNode* pChild;
956         for (SmSubSup const nodeType : { LSUP, LSUB, CSUP, CSUB, RSUP, RSUB })
957         {
958             pChild = pSubSup->GetSubSup(nodeType);
959             if( pChild )
960             {
961             //Create position in front of pChild
962                 SmCaretPosGraphEntry *childLeft = mpGraph->Add( SmCaretPos( pChild, 0 ), left );
963                 //Visit pChild
964                 mpRightMost = childLeft;
965                 pChild->Accept( this );
966                 //Set right on mpRightMost from pChild
967                 mpRightMost->SetRight( bodyLeft );
968             }
969         }
970     }
971 
972     //Return right
973     mpRightMost = right;
974 }
975 
Visit(SmMatrixNode * pNode)976 void SmCaretPosGraphBuildingVisitor::Visit( SmMatrixNode* pNode )
977 {
978     SmCaretPosGraphEntry *left  = mpRightMost,
979                          *right = mpGraph->Add( SmCaretPos( pNode, 1 ) );
980 
981     for (size_t i = 0;  i < pNode->GetNumRows(); ++i)
982     {
983         SmCaretPosGraphEntry* r = left;
984         for (size_t j = 0;  j < pNode->GetNumCols(); ++j)
985         {
986             SmNode* pSubNode = pNode->GetSubNode( i * pNode->GetNumCols( ) + j );
987 
988             mpRightMost = mpGraph->Add( SmCaretPos( pSubNode, 0 ), r );
989             if( j != 0 || ( pNode->GetNumRows() - 1U ) / 2 == i )
990                 r->SetRight( mpRightMost );
991 
992             pSubNode->Accept( this );
993 
994             r = mpRightMost;
995         }
996         mpRightMost->SetRight( right );
997         if( ( pNode->GetNumRows() - 1U ) / 2 == i )
998             right->SetLeft( mpRightMost );
999     }
1000 
1001     mpRightMost = right;
1002 }
1003 
1004 /** Build SmCaretPosGraph for SmTextNode
1005  *
1006  * Lines in an SmTextNode:
1007  * \code
1008  * A B C
1009  * \endcode
1010  * Where A B and C are characters in the text.
1011  *
1012  * Graph over these, where "left" is before the SmTextNode and "right" is after:
1013  * \dot
1014  *  digraph Graph{
1015  *      left -> A;
1016  *      A -> B
1017  *      B -> right;
1018  *  };
1019  * \enddot
1020  * Notice that C and right is the same position here.
1021  */
Visit(SmTextNode * pNode)1022 void SmCaretPosGraphBuildingVisitor::Visit( SmTextNode* pNode )
1023 {
1024     SAL_WARN_IF( pNode->GetText().isEmpty(), "starmath", "Empty SmTextNode is bad" );
1025 
1026     OUString& aText = pNode->GetText();
1027     sal_Int32 i = 0;
1028     while (i < aText.getLength())
1029     {
1030         aText.iterateCodePoints(&i);
1031         SmCaretPosGraphEntry* pRight = mpRightMost;
1032         mpRightMost = mpGraph->Add( SmCaretPos( pNode, i ), pRight );
1033         pRight->SetRight( mpRightMost );
1034     }
1035 }
1036 
1037 /** Build SmCaretPosGraph for SmBinVerNode
1038  *
1039  * Lines in an SmBinVerNode:
1040  * \code
1041  *    A
1042  *  -----
1043  *    B
1044  * \endcode
1045  *
1046  * Graph over these, where "left" is before the SmBinVerNode and "right" is after:
1047  * \dot
1048  *  digraph Graph{
1049  *      left -> A;
1050  *      A -> right;
1051  *      B -> right;
1052  *  };
1053  * \enddot
1054  */
Visit(SmBinVerNode * pNode)1055 void SmCaretPosGraphBuildingVisitor::Visit( SmBinVerNode* pNode )
1056 {
1057     //None if these children can be NULL, see SmBinVerNode::Arrange
1058     SmNode  *pNum   = pNode->GetSubNode( 0 ),
1059             *pDenom = pNode->GetSubNode( 2 );
1060 
1061     SmCaretPosGraphEntry *left,
1062                          *right,
1063                          *numLeft,
1064                          *denomLeft;
1065 
1066     assert(mpRightMost);
1067     //Set left
1068     left = mpRightMost;
1069 
1070     //Create right
1071     right = mpGraph->Add( SmCaretPos( pNode, 1 ) );
1072 
1073     //Create numLeft
1074     numLeft = mpGraph->Add( SmCaretPos( pNum, 0 ), left );
1075     left->SetRight( numLeft );
1076 
1077     //Visit pNum
1078     mpRightMost = numLeft;
1079     pNum->Accept( this );
1080     mpRightMost->SetRight( right );
1081     right->SetLeft( mpRightMost );
1082 
1083     //Create denomLeft
1084     denomLeft = mpGraph->Add( SmCaretPos( pDenom, 0 ), left );
1085 
1086     //Visit pDenom
1087     mpRightMost = denomLeft;
1088     pDenom->Accept( this );
1089     mpRightMost->SetRight( right );
1090 
1091     //Set return parameter
1092     mpRightMost = right;
1093 }
1094 
1095 /** Build SmCaretPosGraph for SmVerticalBraceNode
1096  *
1097  * Lines in an SmVerticalBraceNode:
1098  * \code
1099  *   pScript
1100  *  ________
1101  * /        \
1102  *   pBody
1103  * \endcode
1104  *
1105  */
Visit(SmVerticalBraceNode * pNode)1106 void SmCaretPosGraphBuildingVisitor::Visit( SmVerticalBraceNode* pNode )
1107 {
1108     SmNode  *pBody   = pNode->Body(),
1109             *pScript = pNode->Script();
1110     //None of these children can be NULL
1111 
1112     SmCaretPosGraphEntry  *left,
1113                         *bodyLeft,
1114                         *scriptLeft,
1115                         *right;
1116 
1117     left = mpRightMost;
1118 
1119     //Create right
1120     right = mpGraph->Add( SmCaretPos( pNode, 1 ) );
1121 
1122     //Create bodyLeft
1123     bodyLeft = mpGraph->Add( SmCaretPos( pBody, 0 ), left );
1124     left->SetRight( bodyLeft );
1125     mpRightMost = bodyLeft;
1126     pBody->Accept( this );
1127     mpRightMost->SetRight( right );
1128     right->SetLeft( mpRightMost );
1129 
1130     //Create script
1131     scriptLeft = mpGraph->Add( SmCaretPos( pScript, 0 ), left );
1132     mpRightMost = scriptLeft;
1133     pScript->Accept( this );
1134     mpRightMost->SetRight( right );
1135 
1136     //Set return value
1137     mpRightMost = right;
1138 }
1139 
1140 /** Build SmCaretPosGraph for SmBinDiagonalNode
1141  *
1142  * Lines in an SmBinDiagonalNode:
1143  * \code
1144  *  A /
1145  *   /
1146  *  / B
1147  * \endcode
1148  * Where A and B are lines.
1149  *
1150  * Used in formulas such as "A wideslash B"
1151  */
Visit(SmBinDiagonalNode * pNode)1152 void SmCaretPosGraphBuildingVisitor::Visit( SmBinDiagonalNode* pNode )
1153 {
1154     SmNode  *A = pNode->GetSubNode( 0 ),
1155             *B = pNode->GetSubNode( 1 );
1156 
1157     SmCaretPosGraphEntry  *left,
1158                         *leftA,
1159                         *rightA,
1160                         *leftB,
1161                         *right;
1162     left = mpRightMost;
1163 
1164     //Create right
1165     right = mpGraph->Add( SmCaretPos( pNode, 1 ) );
1166 
1167     //Create left A
1168     leftA = mpGraph->Add( SmCaretPos( A, 0 ), left );
1169     left->SetRight( leftA );
1170 
1171     //Visit A
1172     mpRightMost = leftA;
1173     A->Accept( this );
1174     rightA = mpRightMost;
1175 
1176     //Create left B
1177     leftB = mpGraph->Add( SmCaretPos( B, 0 ), rightA );
1178     rightA->SetRight( leftB );
1179 
1180     //Visit B
1181     mpRightMost = leftB;
1182     B->Accept( this );
1183     mpRightMost->SetRight( right );
1184     right->SetLeft( mpRightMost );
1185 
1186     //Set return value
1187     mpRightMost = right;
1188 }
1189 
1190 //Straight forward ( I think )
Visit(SmBinHorNode * pNode)1191 void SmCaretPosGraphBuildingVisitor::Visit( SmBinHorNode* pNode )
1192 {
1193     for( auto pChild : *pNode )
1194     {
1195         if(!pChild)
1196             continue;
1197         pChild->Accept( this );
1198     }
1199 }
Visit(SmUnHorNode * pNode)1200 void SmCaretPosGraphBuildingVisitor::Visit( SmUnHorNode* pNode )
1201 {
1202     // Unary operator node
1203     for( auto pChild : *pNode )
1204     {
1205         if(!pChild)
1206             continue;
1207         pChild->Accept( this );
1208     }
1209 }
1210 
Visit(SmExpressionNode * pNode)1211 void SmCaretPosGraphBuildingVisitor::Visit( SmExpressionNode* pNode )
1212 {
1213     for( auto pChild : *pNode )
1214     {
1215         if(!pChild)
1216             continue;
1217         pChild->Accept( this );
1218     }
1219 }
1220 
Visit(SmFontNode * pNode)1221 void SmCaretPosGraphBuildingVisitor::Visit( SmFontNode* pNode )
1222 {
1223     //Has only got one child, should act as an expression if possible
1224     for( auto pChild : *pNode )
1225     {
1226         if(!pChild)
1227             continue;
1228         pChild->Accept( this );
1229     }
1230 }
1231 
1232 /** Build SmCaretPosGraph for SmBracebodyNode
1233  * Acts as an SmExpressionNode
1234  *
1235  * Below is an example of a formula tree that has multiple children for SmBracebodyNode
1236  * \dot
1237  * digraph {
1238  * labelloc = "t";
1239  * label= "Equation: \"lbrace i mline i in setZ rbrace\"";
1240  * n0 [label="SmTableNode"];
1241  * n0 -> n1 [label="0"];
1242  * n1 [label="SmLineNode"];
1243  * n1 -> n2 [label="0"];
1244  * n2 [label="SmExpressionNode"];
1245  * n2 -> n3 [label="0"];
1246  * n3 [label="SmBraceNode"];
1247  * n3 -> n4 [label="0"];
1248  * n4 [label="SmMathSymbolNode: {"];
1249  * n3 -> n5 [label="1"];
1250  * n5 [label="SmBracebodyNode"];
1251  * n5 -> n6 [label="0"];
1252  * n6 [label="SmExpressionNode"];
1253  * n6 -> n7 [label="0"];
1254  * n7 [label="SmTextNode: i"];
1255  * n5 -> n8 [label="1"];
1256  * n8 [label="SmMathSymbolNode: &#124;"]; // Unicode "VERTICAL LINE"
1257  * n5 -> n9 [label="2"];
1258  * n9 [label="SmExpressionNode"];
1259  * n9 -> n10 [label="0"];
1260  * n10 [label="SmBinHorNode"];
1261  * n10 -> n11 [label="0"];
1262  * n11 [label="SmTextNode: i"];
1263  * n10 -> n12 [label="1"];
1264  * n12 [label="SmMathSymbolNode: &#8712;"]; // Unicode "ELEMENT OF"
1265  * n10 -> n13 [label="2"];
1266  * n13 [label="SmMathSymbolNode: &#8484;"]; // Unicode "DOUBLE-STRUCK CAPITAL Z"
1267  * n3 -> n14 [label="2"];
1268  * n14 [label="SmMathSymbolNode: }"];
1269  * }
1270  * \enddot
1271  */
Visit(SmBracebodyNode * pNode)1272 void SmCaretPosGraphBuildingVisitor::Visit( SmBracebodyNode* pNode )
1273 {
1274     for( auto pChild : *pNode )
1275     {
1276         if(!pChild)
1277             continue;
1278         SmCaretPosGraphEntry* pStart = mpGraph->Add( SmCaretPos( pChild, 0), mpRightMost );
1279         mpRightMost->SetRight( pStart );
1280         mpRightMost = pStart;
1281         pChild->Accept( this );
1282     }
1283 }
1284 
1285 /** Build SmCaretPosGraph for SmAlignNode
1286  * Acts as an SmExpressionNode, as it only has one child this okay
1287  */
Visit(SmAlignNode * pNode)1288 void SmCaretPosGraphBuildingVisitor::Visit( SmAlignNode* pNode )
1289 {
1290     for( auto pChild : *pNode )
1291     {
1292         if(!pChild)
1293             continue;
1294         pChild->Accept( this );
1295     }
1296 }
1297 
1298 /** Build SmCaretPosGraph for SmRootNode
1299  *
1300  * Lines in an SmRootNode:
1301  * \code
1302  *    _________
1303  *  A/
1304  * \/    B
1305  *
1306  * \endcode
1307  * A: pExtra ( optional, can be NULL ),
1308  * B: pBody
1309  *
1310  * Graph over these, where "left" is before the SmRootNode and "right" is after:
1311  * \dot
1312  *  digraph Graph{
1313  *      left -> B;
1314  *      B -> right;
1315  *      A -> B;
1316  *  }
1317  * \enddot
1318  */
Visit(SmRootNode * pNode)1319 void SmCaretPosGraphBuildingVisitor::Visit( SmRootNode* pNode )
1320 {
1321     SmNode  *pExtra = pNode->GetSubNode( 0 ), //Argument, NULL for sqrt, and SmTextNode if cubicroot
1322             *pBody  = pNode->GetSubNode( 2 ); //Body of the root
1323     assert(pBody);
1324 
1325     SmCaretPosGraphEntry  *left,
1326                         *right,
1327                         *bodyLeft,
1328                         *bodyRight;
1329 
1330     //Get left and save it
1331     assert(mpRightMost);
1332     left = mpRightMost;
1333 
1334     //Create body left
1335     bodyLeft = mpGraph->Add( SmCaretPos( pBody, 0 ), left );
1336     left->SetRight( bodyLeft );
1337 
1338     //Create right
1339     right = mpGraph->Add( SmCaretPos( pNode, 1 ) );
1340 
1341     //Visit body
1342     mpRightMost = bodyLeft;
1343     pBody->Accept( this );
1344     bodyRight = mpRightMost;
1345     bodyRight->SetRight( right );
1346     right->SetLeft( bodyRight );
1347 
1348     //Visit pExtra
1349     if( pExtra ){
1350         mpRightMost = mpGraph->Add( SmCaretPos( pExtra, 0 ), left );
1351         pExtra->Accept( this );
1352         mpRightMost->SetRight( bodyLeft );
1353     }
1354 
1355     mpRightMost = right;
1356 }
1357 
1358 
1359 /** Build SmCaretPosGraph for SmPlaceNode
1360  * Consider this a single character.
1361  */
Visit(SmPlaceNode * pNode)1362 void SmCaretPosGraphBuildingVisitor::Visit( SmPlaceNode* pNode )
1363 {
1364     SmCaretPosGraphEntry* right = mpGraph->Add( SmCaretPos( pNode, 1 ), mpRightMost );
1365     mpRightMost->SetRight( right );
1366     mpRightMost = right;
1367 }
1368 
1369 /** SmErrorNode is context dependent metadata, it can't be selected
1370  *
1371  * @remarks There's no point in deleting, copying and/or moving an instance
1372  * of SmErrorNode as it may not exist in another context! Thus there are no
1373  * positions to select an SmErrorNode.
1374  */
Visit(SmErrorNode *)1375 void SmCaretPosGraphBuildingVisitor::Visit( SmErrorNode* )
1376 {
1377 }
1378 
1379 /** Build SmCaretPosGraph for SmBlankNode
1380  * Consider this a single character, as it is only a blank space
1381  */
Visit(SmBlankNode * pNode)1382 void SmCaretPosGraphBuildingVisitor::Visit( SmBlankNode* pNode )
1383 {
1384     SmCaretPosGraphEntry* right = mpGraph->Add( SmCaretPos( pNode, 1 ), mpRightMost );
1385     mpRightMost->SetRight( right );
1386     mpRightMost = right;
1387 }
1388 
1389 /** Build SmCaretPosGraph for SmBraceNode
1390  *
1391  * Lines in an SmBraceNode:
1392  * \code
1393  * |     |
1394  * |  B  |
1395  * |     |
1396  * \endcode
1397  * B: Body
1398  *
1399  * Graph over these, where "left" is before the SmBraceNode and "right" is after:
1400  * \dot
1401  *  digraph Graph{
1402  *      left -> B;
1403  *      B -> right;
1404  *  }
1405  * \enddot
1406  */
Visit(SmBraceNode * pNode)1407 void SmCaretPosGraphBuildingVisitor::Visit( SmBraceNode* pNode )
1408 {
1409     SmNode* pBody = pNode->Body();
1410 
1411     SmCaretPosGraphEntry  *left = mpRightMost,
1412                         *right = mpGraph->Add( SmCaretPos( pNode, 1 ) );
1413 
1414     if( pBody->GetType() != SmNodeType::Bracebody ) {
1415         mpRightMost = mpGraph->Add( SmCaretPos( pBody, 0 ), left );
1416         left->SetRight( mpRightMost );
1417     }else
1418         mpRightMost = left;
1419 
1420     pBody->Accept( this );
1421     mpRightMost->SetRight( right );
1422     right->SetLeft( mpRightMost );
1423 
1424     mpRightMost = right;
1425 }
1426 
1427 /** Build SmCaretPosGraph for SmAttributeNode
1428  *
1429  * Lines in an SmAttributeNode:
1430  * \code
1431  *   Attr
1432  *   Body
1433  * \endcode
1434  *
1435  * There's a body and an attribute, the construction is used for "widehat A", where "A" is the body
1436  * and "^" is the attribute ( note GetScaleMode( ) on SmAttributeNode tells how the attribute should be
1437  * scaled ).
1438  */
Visit(SmAttributeNode * pNode)1439 void SmCaretPosGraphBuildingVisitor::Visit( SmAttributeNode* pNode )
1440 {
1441     SmNode  *pAttr = pNode->Attribute(),
1442             *pBody = pNode->Body();
1443     assert(pAttr);
1444     assert(pBody);
1445 
1446     SmCaretPosGraphEntry  *left = mpRightMost,
1447                         *attrLeft,
1448                         *bodyLeft,
1449                         *bodyRight,
1450                         *right;
1451 
1452     //Creating bodyleft
1453     bodyLeft = mpGraph->Add( SmCaretPos( pBody, 0 ), left );
1454     left->SetRight( bodyLeft );
1455 
1456     //Creating right
1457     right = mpGraph->Add( SmCaretPos( pNode, 1 ) );
1458 
1459     //Visit the body
1460     mpRightMost = bodyLeft;
1461     pBody->Accept( this );
1462     bodyRight = mpRightMost;
1463     bodyRight->SetRight( right );
1464     right->SetLeft( bodyRight );
1465 
1466     //Create attrLeft
1467     attrLeft = mpGraph->Add( SmCaretPos( pAttr, 0 ), left );
1468 
1469     //Visit attribute
1470     mpRightMost = attrLeft;
1471     pAttr->Accept( this );
1472     mpRightMost->SetRight( right );
1473 
1474     //Set return value
1475     mpRightMost = right;
1476 }
1477 
1478 //Consider these single symbols
Visit(SmSpecialNode * pNode)1479 void SmCaretPosGraphBuildingVisitor::Visit( SmSpecialNode* pNode )
1480 {
1481     SmCaretPosGraphEntry* right = mpGraph->Add( SmCaretPos( pNode, 1 ), mpRightMost );
1482     mpRightMost->SetRight( right );
1483     mpRightMost = right;
1484 }
Visit(SmGlyphSpecialNode * pNode)1485 void SmCaretPosGraphBuildingVisitor::Visit( SmGlyphSpecialNode* pNode )
1486 {
1487     SmCaretPosGraphEntry* right = mpGraph->Add( SmCaretPos( pNode, 1 ), mpRightMost );
1488     mpRightMost->SetRight( right );
1489     mpRightMost = right;
1490 }
Visit(SmMathSymbolNode * pNode)1491 void SmCaretPosGraphBuildingVisitor::Visit( SmMathSymbolNode* pNode )
1492 {
1493     SmCaretPosGraphEntry* right = mpGraph->Add( SmCaretPos( pNode, 1 ), mpRightMost );
1494     mpRightMost->SetRight( right );
1495     mpRightMost = right;
1496 }
1497 
Visit(SmRootSymbolNode *)1498 void SmCaretPosGraphBuildingVisitor::Visit( SmRootSymbolNode* )
1499 {
1500     //Do nothing
1501 }
1502 
Visit(SmRectangleNode *)1503 void SmCaretPosGraphBuildingVisitor::Visit( SmRectangleNode* )
1504 {
1505     //Do nothing
1506 }
Visit(SmPolyLineNode *)1507 void SmCaretPosGraphBuildingVisitor::Visit( SmPolyLineNode* )
1508 {
1509     //Do nothing
1510 }
1511 
1512 // SmCloningVisitor
1513 
Clone(SmNode * pNode)1514 SmNode* SmCloningVisitor::Clone( SmNode* pNode )
1515 {
1516     SmNode* pCurrResult = mpResult;
1517     pNode->Accept( this );
1518     SmNode* pClone = mpResult;
1519     mpResult = pCurrResult;
1520     return pClone;
1521 }
1522 
CloneNodeAttr(SmNode const * pSource,SmNode * pTarget)1523 void SmCloningVisitor::CloneNodeAttr( SmNode const * pSource, SmNode* pTarget )
1524 {
1525     pTarget->SetScaleMode( pSource->GetScaleMode( ) );
1526     //Other attributes are set when prepare or arrange is executed
1527     //and may depend on stuff not being cloned here.
1528 }
1529 
CloneKids(SmStructureNode * pSource,SmStructureNode * pTarget)1530 void SmCloningVisitor::CloneKids( SmStructureNode* pSource, SmStructureNode* pTarget )
1531 {
1532     //Cache current result
1533     SmNode* pCurrResult = mpResult;
1534 
1535     //Create array for holding clones
1536     size_t nSize = pSource->GetNumSubNodes( );
1537     SmNodeArray aNodes( nSize );
1538 
1539     //Clone children
1540     for (size_t i = 0; i < nSize; ++i)
1541     {
1542         SmNode* pKid;
1543         if( nullptr != ( pKid = pSource->GetSubNode( i ) ) )
1544             pKid->Accept( this );
1545         else
1546             mpResult = nullptr;
1547         aNodes[i] = mpResult;
1548     }
1549 
1550     //Set subnodes of pTarget
1551     pTarget->SetSubNodes( std::move(aNodes) );
1552 
1553     //Restore result as where prior to call
1554     mpResult = pCurrResult;
1555 }
1556 
Visit(SmTableNode * pNode)1557 void SmCloningVisitor::Visit( SmTableNode* pNode )
1558 {
1559     SmTableNode* pClone = new SmTableNode( pNode->GetToken( ) );
1560     pClone->SetSelection( pNode->GetSelection() );
1561     CloneNodeAttr( pNode, pClone );
1562     CloneKids( pNode, pClone );
1563     mpResult = pClone;
1564 }
1565 
Visit(SmBraceNode * pNode)1566 void SmCloningVisitor::Visit( SmBraceNode* pNode )
1567 {
1568     SmBraceNode* pClone = new SmBraceNode( pNode->GetToken( ) );
1569     pClone->SetSelection( pNode->GetSelection() );
1570     CloneNodeAttr( pNode, pClone );
1571     CloneKids( pNode, pClone );
1572     mpResult = pClone;
1573 }
1574 
Visit(SmBracebodyNode * pNode)1575 void SmCloningVisitor::Visit( SmBracebodyNode* pNode )
1576 {
1577     SmBracebodyNode* pClone = new SmBracebodyNode( pNode->GetToken( ) );
1578     pClone->SetSelection( pNode->GetSelection() );
1579     CloneNodeAttr( pNode, pClone );
1580     CloneKids( pNode, pClone );
1581     mpResult = pClone;
1582 }
1583 
Visit(SmOperNode * pNode)1584 void SmCloningVisitor::Visit( SmOperNode* pNode )
1585 {
1586     SmOperNode* pClone = new SmOperNode( pNode->GetToken( ) );
1587     pClone->SetSelection( pNode->GetSelection() );
1588     CloneNodeAttr( pNode, pClone );
1589     CloneKids( pNode, pClone );
1590     mpResult = pClone;
1591 }
1592 
Visit(SmAlignNode * pNode)1593 void SmCloningVisitor::Visit( SmAlignNode* pNode )
1594 {
1595     SmAlignNode* pClone = new SmAlignNode( pNode->GetToken( ) );
1596     pClone->SetSelection( pNode->GetSelection() );
1597     CloneNodeAttr( pNode, pClone );
1598     CloneKids( pNode, pClone );
1599     mpResult = pClone;
1600 }
1601 
Visit(SmAttributeNode * pNode)1602 void SmCloningVisitor::Visit( SmAttributeNode* pNode )
1603 {
1604     SmAttributeNode* pClone = new SmAttributeNode( pNode->GetToken( ) );
1605     pClone->SetSelection( pNode->GetSelection() );
1606     CloneNodeAttr( pNode, pClone );
1607     CloneKids( pNode, pClone );
1608     mpResult = pClone;
1609 }
1610 
Visit(SmFontNode * pNode)1611 void SmCloningVisitor::Visit( SmFontNode* pNode )
1612 {
1613     SmFontNode* pClone = new SmFontNode( pNode->GetToken( ) );
1614     pClone->SetSelection( pNode->GetSelection() );
1615     pClone->SetSizeParameter( pNode->GetSizeParameter( ), pNode->GetSizeType( ) );
1616     CloneNodeAttr( pNode, pClone );
1617     CloneKids( pNode, pClone );
1618     mpResult = pClone;
1619 }
1620 
Visit(SmUnHorNode * pNode)1621 void SmCloningVisitor::Visit( SmUnHorNode* pNode )
1622 {
1623     SmUnHorNode* pClone = new SmUnHorNode( pNode->GetToken( ) );
1624     pClone->SetSelection( pNode->GetSelection() );
1625     CloneNodeAttr( pNode, pClone );
1626     CloneKids( pNode, pClone );
1627     mpResult = pClone;
1628 }
1629 
Visit(SmBinHorNode * pNode)1630 void SmCloningVisitor::Visit( SmBinHorNode* pNode )
1631 {
1632     SmBinHorNode* pClone = new SmBinHorNode( pNode->GetToken( ) );
1633     pClone->SetSelection( pNode->GetSelection() );
1634     CloneNodeAttr( pNode, pClone );
1635     CloneKids( pNode, pClone );
1636     mpResult = pClone;
1637 }
1638 
Visit(SmBinVerNode * pNode)1639 void SmCloningVisitor::Visit( SmBinVerNode* pNode )
1640 {
1641     SmBinVerNode* pClone = new SmBinVerNode( pNode->GetToken( ) );
1642     pClone->SetSelection( pNode->GetSelection() );
1643     CloneNodeAttr( pNode, pClone );
1644     CloneKids( pNode, pClone );
1645     mpResult = pClone;
1646 }
1647 
Visit(SmBinDiagonalNode * pNode)1648 void SmCloningVisitor::Visit( SmBinDiagonalNode* pNode )
1649 {
1650     SmBinDiagonalNode *pClone = new SmBinDiagonalNode( pNode->GetToken( ) );
1651     pClone->SetSelection( pNode->GetSelection() );
1652     pClone->SetAscending( pNode->IsAscending( ) );
1653     CloneNodeAttr( pNode, pClone );
1654     CloneKids( pNode, pClone );
1655     mpResult = pClone;
1656 }
1657 
Visit(SmSubSupNode * pNode)1658 void SmCloningVisitor::Visit( SmSubSupNode* pNode )
1659 {
1660     SmSubSupNode *pClone = new SmSubSupNode( pNode->GetToken( ) );
1661     pClone->SetSelection( pNode->GetSelection() );
1662     pClone->SetUseLimits( pNode->IsUseLimits( ) );
1663     CloneNodeAttr( pNode, pClone );
1664     CloneKids( pNode, pClone );
1665     mpResult = pClone;
1666 }
1667 
Visit(SmMatrixNode * pNode)1668 void SmCloningVisitor::Visit( SmMatrixNode* pNode )
1669 {
1670     SmMatrixNode *pClone = new SmMatrixNode( pNode->GetToken( ) );
1671     pClone->SetSelection( pNode->GetSelection() );
1672     pClone->SetRowCol( pNode->GetNumRows( ), pNode->GetNumCols( ) );
1673     CloneNodeAttr( pNode, pClone );
1674     CloneKids( pNode, pClone );
1675     mpResult = pClone;
1676 }
1677 
Visit(SmPlaceNode * pNode)1678 void SmCloningVisitor::Visit( SmPlaceNode* pNode )
1679 {
1680     mpResult = new SmPlaceNode( pNode->GetToken( ) );
1681     mpResult->SetSelection( pNode->GetSelection() );
1682     CloneNodeAttr( pNode, mpResult );
1683 }
1684 
Visit(SmTextNode * pNode)1685 void SmCloningVisitor::Visit( SmTextNode* pNode )
1686 {
1687     SmTextNode* pClone = new SmTextNode( pNode->GetToken( ), pNode->GetFontDesc( ) );
1688     pClone->SetSelection( pNode->GetSelection() );
1689     pClone->ChangeText( pNode->GetText( ) );
1690     CloneNodeAttr( pNode, pClone );
1691     mpResult = pClone;
1692 }
1693 
Visit(SmSpecialNode * pNode)1694 void SmCloningVisitor::Visit( SmSpecialNode* pNode )
1695 {
1696     mpResult = new SmSpecialNode( pNode->GetToken( ) );
1697     mpResult->SetSelection( pNode->GetSelection() );
1698     CloneNodeAttr( pNode, mpResult );
1699 }
1700 
Visit(SmGlyphSpecialNode * pNode)1701 void SmCloningVisitor::Visit( SmGlyphSpecialNode* pNode )
1702 {
1703     mpResult = new SmGlyphSpecialNode( pNode->GetToken( ) );
1704     mpResult->SetSelection( pNode->GetSelection() );
1705     CloneNodeAttr( pNode, mpResult );
1706 }
1707 
Visit(SmMathSymbolNode * pNode)1708 void SmCloningVisitor::Visit( SmMathSymbolNode* pNode )
1709 {
1710     mpResult = new SmMathSymbolNode( pNode->GetToken( ) );
1711     mpResult->SetSelection( pNode->GetSelection() );
1712     CloneNodeAttr( pNode, mpResult );
1713 }
1714 
Visit(SmBlankNode * pNode)1715 void SmCloningVisitor::Visit( SmBlankNode* pNode )
1716 {
1717     SmBlankNode* pClone = new SmBlankNode( pNode->GetToken( ) );
1718     pClone->SetSelection( pNode->GetSelection() );
1719     pClone->SetBlankNum( pNode->GetBlankNum( ) );
1720     mpResult = pClone;
1721     CloneNodeAttr( pNode, mpResult );
1722 }
1723 
Visit(SmErrorNode * pNode)1724 void SmCloningVisitor::Visit( SmErrorNode* pNode )
1725 {
1726     mpResult = new SmErrorNode( pNode->GetToken( ) );
1727     mpResult->SetSelection( pNode->GetSelection() );
1728     CloneNodeAttr( pNode, mpResult );
1729 }
1730 
Visit(SmLineNode * pNode)1731 void SmCloningVisitor::Visit( SmLineNode* pNode )
1732 {
1733     SmLineNode* pClone = new SmLineNode( pNode->GetToken( ) );
1734     pClone->SetSelection( pNode->GetSelection() );
1735     CloneNodeAttr( pNode, pClone );
1736     CloneKids( pNode, pClone );
1737     mpResult = pClone;
1738 }
1739 
Visit(SmExpressionNode * pNode)1740 void SmCloningVisitor::Visit( SmExpressionNode* pNode )
1741 {
1742     SmExpressionNode* pClone = new SmExpressionNode( pNode->GetToken( ) );
1743     pClone->SetSelection( pNode->GetSelection() );
1744     CloneNodeAttr( pNode, pClone );
1745     CloneKids( pNode, pClone );
1746     mpResult = pClone;
1747 }
1748 
Visit(SmPolyLineNode * pNode)1749 void SmCloningVisitor::Visit( SmPolyLineNode* pNode )
1750 {
1751     mpResult = new SmPolyLineNode( pNode->GetToken( ) );
1752     mpResult->SetSelection( pNode->GetSelection() );
1753     CloneNodeAttr( pNode, mpResult );
1754 }
1755 
Visit(SmRootNode * pNode)1756 void SmCloningVisitor::Visit( SmRootNode* pNode )
1757 {
1758     SmRootNode* pClone = new SmRootNode( pNode->GetToken( ) );
1759     pClone->SetSelection( pNode->GetSelection() );
1760     CloneNodeAttr( pNode, pClone );
1761     CloneKids( pNode, pClone );
1762     mpResult = pClone;
1763 }
1764 
Visit(SmRootSymbolNode * pNode)1765 void SmCloningVisitor::Visit( SmRootSymbolNode* pNode )
1766 {
1767     mpResult = new SmRootSymbolNode( pNode->GetToken( ) );
1768     mpResult->SetSelection( pNode->GetSelection() );
1769     CloneNodeAttr( pNode, mpResult );
1770 }
1771 
Visit(SmRectangleNode * pNode)1772 void SmCloningVisitor::Visit( SmRectangleNode* pNode )
1773 {
1774     mpResult = new SmRectangleNode( pNode->GetToken( ) );
1775     mpResult->SetSelection( pNode->GetSelection() );
1776     CloneNodeAttr( pNode, mpResult );
1777 }
1778 
Visit(SmVerticalBraceNode * pNode)1779 void SmCloningVisitor::Visit( SmVerticalBraceNode* pNode )
1780 {
1781     SmVerticalBraceNode* pClone = new SmVerticalBraceNode( pNode->GetToken( ) );
1782     pClone->SetSelection( pNode->GetSelection() );
1783     CloneNodeAttr( pNode, pClone );
1784     CloneKids( pNode, pClone );
1785     mpResult = pClone;
1786 }
1787 
1788 // SmSelectionDrawingVisitor
1789 
SmSelectionDrawingVisitor(OutputDevice & rDevice,SmNode * pTree,const Point & rOffset)1790 SmSelectionDrawingVisitor::SmSelectionDrawingVisitor( OutputDevice& rDevice, SmNode* pTree, const Point& rOffset )
1791     : SmSelectionRectanglesVisitor( rDevice, pTree )
1792 {
1793     //Draw selection if there's any
1794     if(GetSelection().IsEmpty())        return;
1795 
1796     tools::Rectangle aSelectionArea = GetSelection() + rOffset;
1797 
1798     //Save device state
1799     rDevice.Push( vcl::PushFlags::LINECOLOR | vcl::PushFlags::FILLCOLOR );
1800     //Change colors
1801     rDevice.SetLineColor( );
1802     rDevice.SetFillColor( COL_LIGHTGRAY );
1803 
1804     //Draw rectangle
1805     rDevice.DrawRect( aSelectionArea );
1806 
1807     //Restore device state
1808     rDevice.Pop( );
1809 }
1810 
1811 // SmSelectionRectanglesVisitor
1812 
SmSelectionRectanglesVisitor(OutputDevice & rDevice,SmNode * pTree)1813 SmSelectionRectanglesVisitor::SmSelectionRectanglesVisitor(OutputDevice& rDevice, SmNode* pTree)
1814     : mrDev(rDevice)
1815 {
1816     // Visit everything
1817     SAL_WARN_IF(!pTree, "starmath", "pTree can't be null!");
1818     if (pTree)
1819         pTree->Accept(this);
1820 }
1821 
DefaultVisit(SmNode * pNode)1822 void SmSelectionRectanglesVisitor::DefaultVisit( SmNode* pNode )
1823 {
1824     if( pNode->IsSelected( ) )
1825         ExtendSelectionArea( pNode->AsRectangle( ) );
1826     VisitChildren( pNode );
1827 }
1828 
VisitChildren(SmNode * pNode)1829 void SmSelectionRectanglesVisitor::VisitChildren( SmNode* pNode )
1830 {
1831     if(pNode->GetNumSubNodes() == 0)
1832         return;
1833     for( auto pChild : *static_cast<SmStructureNode*>(pNode) )
1834     {
1835         if(!pChild)
1836             continue;
1837         pChild->Accept( this );
1838     }
1839 }
1840 
Visit(SmTextNode * pNode)1841 void SmSelectionRectanglesVisitor::Visit( SmTextNode* pNode )
1842 {
1843     if( !pNode->IsSelected())
1844         return;
1845 
1846     mrDev.Push( vcl::PushFlags::TEXTCOLOR | vcl::PushFlags::FONT );
1847 
1848     mrDev.SetFont( pNode->GetFont( ) );
1849     Point Position = pNode->GetTopLeft( );
1850     tools::Long left   = Position.getX( ) + mrDev.GetTextWidth( pNode->GetText( ), 0, pNode->GetSelectionStart( ) );
1851     tools::Long right  = Position.getX( ) + mrDev.GetTextWidth( pNode->GetText( ), 0, pNode->GetSelectionEnd( ) );
1852     tools::Long top    = Position.getY( );
1853     tools::Long bottom = top + pNode->GetHeight( );
1854     tools::Rectangle rect( left, top, right, bottom );
1855 
1856     ExtendSelectionArea( rect );
1857 
1858     mrDev.Pop( );
1859 }
1860 
1861 // SmNodeToTextVisitor
1862 
SmNodeToTextVisitor(SmNode * pNode,OUString & rText)1863 SmNodeToTextVisitor::SmNodeToTextVisitor( SmNode* pNode, OUString &rText )
1864 {
1865     pNode->Accept( this );
1866     maCmdText.stripEnd(' ');
1867     rText = maCmdText.makeStringAndClear();
1868 }
1869 
Visit(SmTableNode * pNode)1870 void SmNodeToTextVisitor::Visit( SmTableNode* pNode )
1871 {
1872     if( pNode->GetToken( ).eType == TBINOM ) {
1873         Append(u"{ binom");
1874         LineToText( pNode->GetSubNode( 0 ) );
1875         LineToText( pNode->GetSubNode( 1 ) );
1876         Append(u"} ");
1877     } else if( pNode->GetToken( ).eType == TSTACK ) {
1878         Append(u"stack{ ");
1879         bool bFirst = true;
1880         for( auto pChild : *pNode )
1881         {
1882             if(!pChild)
1883                 continue;
1884             if(bFirst)
1885                 bFirst = false;
1886             else
1887             {
1888                 Separate( );
1889                 Append(u"# ");
1890             }
1891             LineToText( pChild );
1892         }
1893         Separate( );
1894         Append(u"}");
1895     } else { //Assume it's a toplevel table, containing lines
1896         bool bFirst = true;
1897         for( auto pChild : *pNode )
1898         {
1899             if(!pChild)
1900                 continue;
1901             if(bFirst)
1902                 bFirst = false;
1903             else
1904             {
1905                 Separate( );
1906                 Append(u"newline");
1907             }
1908             Separate( );
1909             pChild->Accept( this );
1910         }
1911     }
1912 }
1913 
Visit(SmBraceNode * pNode)1914 void SmNodeToTextVisitor::Visit( SmBraceNode* pNode )
1915 {
1916     if ( pNode->GetToken().eType == TEVALUATE )
1917     {
1918         SmNode *pBody = pNode->Body();
1919         Append(u"evaluate { ");
1920         pBody->Accept( this );
1921         Append(u"} ");
1922     }
1923     else{
1924         SmNode *pLeftBrace  = pNode->OpeningBrace(),
1925                *pBody       = pNode->Body(),
1926                *pRightBrace = pNode->ClosingBrace();
1927         //Handle special case where it's absolute function
1928         if( pNode->GetToken( ).eType == TABS ) {
1929             Append(u"abs");
1930             LineToText( pBody );
1931         } else {
1932             if( pNode->GetScaleMode( ) == SmScaleMode::Height )
1933                 Append(u"left ");
1934             pLeftBrace->Accept( this );
1935             Separate( );
1936             pBody->Accept( this );
1937             Separate( );
1938             if( pNode->GetScaleMode( ) == SmScaleMode::Height )
1939                 Append(u"right ");
1940             pRightBrace->Accept( this );
1941         }
1942     }
1943 }
1944 
Visit(SmBracebodyNode * pNode)1945 void SmNodeToTextVisitor::Visit( SmBracebodyNode* pNode )
1946 {
1947     for( auto pChild : *pNode )
1948     {
1949         if(!pChild)
1950             continue;
1951         Separate( );
1952         pChild->Accept( this );
1953     }
1954 }
1955 
Visit(SmOperNode * pNode)1956 void SmNodeToTextVisitor::Visit( SmOperNode* pNode )
1957 {
1958     Append( pNode->GetToken( ).aText );
1959     Separate( );
1960     if( pNode->GetToken( ).eType == TOPER ){
1961         //There's an SmGlyphSpecialNode if eType == TOPER
1962         if( pNode->GetSubNode( 0 )->GetType( ) == SmNodeType::SubSup )
1963             Append( pNode->GetSubNode( 0 )->GetSubNode( 0 )->GetToken( ).aText );
1964         else
1965             Append( pNode->GetSubNode( 0 )->GetToken( ).aText );
1966     }
1967     if( pNode->GetSubNode( 0 )->GetType( ) == SmNodeType::SubSup ) {
1968         SmSubSupNode *pSubSup = static_cast<SmSubSupNode*>( pNode->GetSubNode( 0 ) );
1969         SmNode* pChild = pSubSup->GetSubSup( LSUP );
1970         if( pChild ) {
1971             Separate( );
1972             Append(u"lsup { ");
1973             LineToText( pChild );
1974             Append(u"} ");
1975         }
1976         pChild = pSubSup->GetSubSup( LSUB );
1977         if( pChild ) {
1978             Separate( );
1979             Append(u"lsub { ");
1980             LineToText( pChild );
1981             Append(u"} ");
1982         }
1983         pChild = pSubSup->GetSubSup( RSUP );
1984         if( pChild ) {
1985             Separate( );
1986             Append(u"^ { ");
1987             LineToText( pChild );
1988             Append(u"} ");
1989         }
1990         pChild = pSubSup->GetSubSup( RSUB );
1991         if( pChild ) {
1992             Separate( );
1993             Append(u"_ { ");
1994             LineToText( pChild );
1995             Append(u"} ");
1996         }
1997         pChild = pSubSup->GetSubSup( CSUP );
1998         if( pChild ) {
1999             Separate( );
2000             if (pSubSup->IsUseLimits())
2001                 Append(u"to { ");
2002             else
2003                 Append(u"csup { ");
2004             LineToText( pChild );
2005             Append(u"} ");
2006         }
2007         pChild = pSubSup->GetSubSup( CSUB );
2008         if( pChild ) {
2009             Separate( );
2010             if (pSubSup->IsUseLimits())
2011                 Append(u"from { ");
2012             else
2013                 Append(u"csub { ");
2014             LineToText( pChild );
2015             Append(u"} ");
2016         }
2017     }
2018     LineToText( pNode->GetSubNode( 1 ) );
2019 }
2020 
Visit(SmAlignNode * pNode)2021 void SmNodeToTextVisitor::Visit( SmAlignNode* pNode )
2022 {
2023     Append( pNode->GetToken( ).aText );
2024     LineToText( pNode->GetSubNode( 0 ) );
2025 }
2026 
Visit(SmAttributeNode * pNode)2027 void SmNodeToTextVisitor::Visit( SmAttributeNode* pNode )
2028 {
2029     Append( pNode->GetToken( ).aText );
2030     LineToText( pNode->Body() );
2031 }
2032 
Visit(SmFontNode * pNode)2033 void SmNodeToTextVisitor::Visit( SmFontNode* pNode )
2034 {
2035     sal_uInt32 nc;
2036     sal_uInt8  nr, ng, nb;
2037     switch ( pNode->GetToken( ).eType )
2038     {
2039         case TBOLD:
2040             Append(u"bold ");
2041             break;
2042         case TNBOLD:
2043             Append(u"nbold ");
2044             break;
2045         case TITALIC:
2046             Append(u"italic ");
2047             break;
2048         case TNITALIC:
2049             Append(u"nitalic ");
2050             break;
2051         case TPHANTOM:
2052             Append(u"phantom ");
2053             break;
2054         case TSIZE:
2055             {
2056                 Append(u"size ");
2057                 switch ( pNode->GetSizeType( ) )
2058                 {
2059                     case FontSizeType::PLUS:
2060                         Append(u"+");
2061                         break;
2062                     case FontSizeType::MINUS:
2063                         Append(u"-");
2064                         break;
2065                     case FontSizeType::MULTIPLY:
2066                         Append(u"*");
2067                         break;
2068                     case FontSizeType::DIVIDE:
2069                         Append(u"/");
2070                         break;
2071                     case FontSizeType::ABSOLUT:
2072                     default:
2073                         break;
2074                 }
2075                 Append( ::rtl::math::doubleToUString(
2076                             static_cast<double>( pNode->GetSizeParameter( ) ),
2077                             rtl_math_StringFormat_Automatic,
2078                             rtl_math_DecimalPlaces_Max, '.', true ) );
2079                 Separate( );
2080             }
2081             break;
2082 
2083         case TDVIPSNAMESCOL:
2084             Append(u"color dvip ");
2085             nc = pNode->GetToken().cMathChar.toUInt32(16);
2086             Append( starmathdatabase::Identify_Color_Parser( nc ).aIdent );
2087             break;
2088         case THTMLCOL:
2089         case TMATHMLCOL:
2090         case TICONICCOL:
2091             Append(u"color ");
2092             nc = pNode->GetToken().cMathChar.toUInt32(16);
2093             Append( starmathdatabase::Identify_Color_Parser( nc ).aIdent );
2094             break;
2095         case TRGB:
2096             nc = pNode->GetToken().cMathChar.toUInt32(16);
2097             Append(u"color rgb ");
2098             nb = nc % 256;
2099             nc /= 256;
2100             ng = nc % 256;
2101             nc /= 256;
2102             nr = nc % 256;
2103             Append(OUString::number(nr));
2104             Separate();
2105             Append(OUString::number(ng));
2106             Separate();
2107             Append(OUString::number(nb));
2108             Separate();
2109             break;
2110         case TRGBA:
2111             Append(u"color rgba ");
2112             nc = pNode->GetToken().cMathChar.toUInt32(16);
2113             nb = nc % 256;
2114             nc /= 256;
2115             ng = nc % 256;
2116             nc /= 256;
2117             nr = nc % 256;
2118             nc /= 256;
2119             Append(OUString::number(nr));
2120             Separate();
2121             Append(OUString::number(ng));
2122             Separate();
2123             Append(OUString::number(nb));
2124             Separate();
2125             Append(OUString::number(nc));
2126             Separate();
2127             break;
2128         case THEX:
2129             Append(u"color hex ");
2130             nc = pNode->GetToken().cMathChar.toUInt32(16);
2131             Append(OUString::number(nc,16));
2132             Separate();
2133             break;
2134         case TSANS:
2135             Append(u"font sans ");
2136             break;
2137         case TSERIF:
2138             Append(u"font serif ");
2139             break;
2140         case TFIXED:
2141             Append(u"font fixed ");
2142             break;
2143         default:
2144             break;
2145     }
2146     LineToText( pNode->GetSubNode( 1 ) );
2147 }
2148 
Visit(SmUnHorNode * pNode)2149 void SmNodeToTextVisitor::Visit( SmUnHorNode* pNode )
2150 {
2151     if(pNode->GetSubNode( 1 )->GetToken( ).eType == TFACT)
2152     {
2153         // visit children in the reverse order
2154         for( auto it = pNode->rbegin(); it != pNode->rend(); ++it )
2155         {
2156             auto pChild = *it;
2157             if(!pChild)
2158                 continue;
2159             Separate( );
2160             pChild->Accept( this );
2161         }
2162     }
2163     else
2164     {
2165         for( auto pChild : *pNode )
2166         {
2167             if(!pChild)
2168                 continue;
2169             Separate( );
2170             pChild->Accept( this );
2171         }
2172     }
2173 }
2174 
Visit(SmBinHorNode * pNode)2175 void SmNodeToTextVisitor::Visit( SmBinHorNode* pNode )
2176 {
2177     const SmNode *pParent = pNode->GetParent();
2178     bool bBraceNeeded = pParent;
2179     SmNode *pLeft  = pNode->LeftOperand(),
2180            *pOper  = pNode->Symbol(),
2181            *pRight = pNode->RightOperand();
2182     Separate( );
2183     if (bBraceNeeded)
2184         Append(u"{ ");
2185     pLeft->Accept( this );
2186     Separate( );
2187     pOper->Accept( this );
2188     Separate( );
2189     pRight->Accept( this );
2190     Separate( );
2191     if (bBraceNeeded)
2192         Append(u"} ");
2193 }
2194 
Visit(SmBinVerNode * pNode)2195 void SmNodeToTextVisitor::Visit( SmBinVerNode* pNode )
2196 {
2197     if( pNode->GetToken().eType == TOVER ){
2198         SmNode *pNum    = pNode->GetSubNode( 0 ),
2199                *pDenom  = pNode->GetSubNode( 2 );
2200         Append(u"{ ");
2201         LineToText( pNum );
2202         Append(u"over");
2203         LineToText( pDenom );
2204         Append(u"} ");
2205     } else{
2206         SmNode *pNum    = pNode->GetSubNode( 0 ),
2207                *pDenom  = pNode->GetSubNode( 2 );
2208         Append(u"{ frac {");
2209         LineToText( pNum );
2210         Append(u"} {");
2211         LineToText( pDenom );
2212         Append(u"} }");
2213     }
2214 }
2215 
Visit(SmBinDiagonalNode * pNode)2216 void SmNodeToTextVisitor::Visit( SmBinDiagonalNode* pNode )
2217 {
2218     SmNode *pLeftOperand  = pNode->GetSubNode( 0 ),
2219            *pRightOperand = pNode->GetSubNode( 1 );
2220     Append(u"{ ");
2221     LineToText( pLeftOperand );
2222     Separate( );
2223     Append(u"wideslash ");
2224     LineToText( pRightOperand );
2225     Append(u"} ");
2226 }
2227 
Visit(SmSubSupNode * pNode)2228 void SmNodeToTextVisitor::Visit( SmSubSupNode* pNode )
2229 {
2230     if( pNode->GetToken().eType == TEVALUATE )
2231     {
2232         Append(u"evaluate { ");
2233         pNode->GetSubNode( 0 )->GetSubNode( 1 )->Accept(this);
2234         Append(u"} ");
2235         SmNode* pChild = pNode->GetSubSup( RSUP );
2236         if( pChild ) {
2237             Separate( );
2238             Append(u"to { ");
2239             LineToText( pChild );
2240             Append(u"} ");
2241         }
2242         pChild = pNode->GetSubSup( RSUB );
2243         if( pChild ) {
2244             Separate( );
2245             Append(u"from { ");
2246             LineToText( pChild );
2247             Append(u"} ");
2248         }
2249     }
2250     else
2251     {
2252         LineToText( pNode->GetBody( ) );
2253         SmNode *pChild = pNode->GetSubSup( LSUP );
2254         if( pChild ) {
2255             Separate( );
2256             Append(u"lsup ");
2257             LineToText( pChild );
2258         }
2259         pChild = pNode->GetSubSup( LSUB );
2260         if( pChild ) {
2261             Separate( );
2262             Append(u"lsub ");
2263             LineToText( pChild );
2264         }
2265         pChild = pNode->GetSubSup( RSUP );
2266         if( pChild ) {
2267             Separate( );
2268             Append(u"^ ");
2269             LineToText( pChild );
2270         }
2271         pChild = pNode->GetSubSup( RSUB );
2272         if( pChild ) {
2273             Separate( );
2274             Append(u"_ ");
2275             LineToText( pChild );
2276         }
2277         pChild = pNode->GetSubSup( CSUP );
2278         if( pChild ) {
2279             Separate( );
2280             if (pNode->IsUseLimits())
2281                 Append(u"to ");
2282             else
2283                 Append(u"csup ");
2284             LineToText( pChild );
2285         }
2286         pChild = pNode->GetSubSup( CSUB );
2287         if( pChild ) {
2288             Separate( );
2289             if (pNode->IsUseLimits())
2290                 Append(u"from ");
2291             else
2292                 Append(u"csub ");
2293             LineToText( pChild );
2294         }
2295     }
2296 }
2297 
Visit(SmMatrixNode * pNode)2298 void SmNodeToTextVisitor::Visit( SmMatrixNode* pNode )
2299 {
2300     Append(u"matrix{");
2301     for (size_t i = 0; i < pNode->GetNumRows(); ++i)
2302     {
2303         for (size_t j = 0; j < pNode->GetNumCols( ); ++j)
2304         {
2305             SmNode* pSubNode = pNode->GetSubNode( i * pNode->GetNumCols( ) + j );
2306             Separate( );
2307             if (pSubNode)
2308                 pSubNode->Accept( this );
2309             Separate( );
2310             if (j != pNode->GetNumCols() - 1U)
2311                 Append(u"#");
2312         }
2313         Separate( );
2314         if (i != pNode->GetNumRows() - 1U)
2315             Append(u"##");
2316     }
2317     Append(u"} ");
2318 }
2319 
Visit(SmPlaceNode *)2320 void SmNodeToTextVisitor::Visit( SmPlaceNode* )
2321 {
2322     Append(u"<?>");
2323 }
2324 
Visit(SmTextNode * pNode)2325 void SmNodeToTextVisitor::Visit( SmTextNode* pNode )
2326 {
2327     SmTokenType type = pNode->GetToken( ).eType;
2328     switch(type){
2329         case TTEXT:
2330             Append(u"\"");
2331             Append( pNode->GetToken().aText );
2332             Append(u"\"");
2333             break;
2334         case TNUMBER:
2335             Append( pNode->GetToken().aText );
2336             break;
2337         case TIDENT:
2338             Append( pNode->GetToken().aText );
2339             break;
2340         case TFUNC:
2341             Append(u"func ");
2342             Append( pNode->GetToken().aText );
2343             break;
2344         case THEX:
2345             Append(u"hex ");
2346             Append( pNode->GetToken().aText );
2347             break;
2348         default:
2349             Append( pNode->GetToken().aText );
2350     }
2351     Separate( );
2352 }
2353 
Visit(SmSpecialNode * pNode)2354 void SmNodeToTextVisitor::Visit( SmSpecialNode* pNode )
2355 {
2356     SmTokenType type = pNode->GetToken().eType;
2357     switch(type){
2358         case TLIMSUP:
2359             Append(u"lim sup ");
2360             break;
2361         case TLIMINF:
2362             Append(u"lim inf ");
2363             break;
2364         default:
2365             Append( pNode->GetToken().aText );
2366             break;
2367     }
2368 }
2369 
Visit(SmGlyphSpecialNode * pNode)2370 void SmNodeToTextVisitor::Visit( SmGlyphSpecialNode* pNode )
2371 {
2372     if( pNode->GetToken( ).eType == TBOPER )
2373         Append(u"boper ");
2374     else
2375         Append(u"uoper ");
2376     Append( pNode->GetToken( ).aText );
2377 }
2378 
2379 //TODO to improve this it is required to improve mathmlimport.
Visit(SmMathSymbolNode * pNode)2380 void SmNodeToTextVisitor::Visit( SmMathSymbolNode* pNode )
2381 {
2382     if (    ( pNode->GetToken().nGroup & TG::LBrace )
2383          || ( pNode->GetToken().nGroup & TG::RBrace )
2384          || ( pNode->GetToken().nGroup & TG::Sum )
2385          || ( pNode->GetToken().nGroup & TG::Product )
2386          || ( pNode->GetToken().nGroup & TG::Relation )
2387          || ( pNode->GetToken().nGroup & TG::UnOper )
2388          || ( pNode->GetToken().nGroup & TG::Oper )
2389     ) {
2390         Append( pNode->GetToken().aText );
2391         return;
2392     }
2393     sal_Unicode cChar = pNode->GetToken().cMathChar[0];
2394     Separate( );
2395     switch(cChar){
2396         case MS_NONE:
2397             Append(u"none");
2398             break;
2399         case '{':
2400             Append(u"{");
2401             break;
2402         case '}':
2403             Append(u"}");
2404             break;
2405         case MS_VERTLINE:
2406             Append(u"mline");
2407             break;
2408         case MS_TILDE:
2409             Append(u"\"~\"");
2410             break;
2411         case MS_RIGHTARROW:
2412             if( pNode->GetToken().eType == TTOWARD ) Append(u"toward");
2413             else Append(u"rightarrow");
2414             break;
2415         case MS_LEFTARROW:
2416             Append(u"leftarrow");
2417             break;
2418         case MS_UPARROW:
2419             Append(u"uparrow");
2420             break;
2421         case MS_DOWNARROW:
2422             Append(u"downarrow");
2423             break;
2424         case MS_LAMBDABAR:
2425             Append(u"lambdabar");
2426             break;
2427         case MS_DOTSLOW:
2428             Append(u"dotslow");
2429             break;
2430         case MS_SETC:
2431             Append(u"setC");
2432             break;
2433         case MS_HBAR:
2434             Append(u"hbar");
2435             break;
2436         case MS_IM:
2437             Append(u"Im");
2438             break;
2439         case MS_SETN:
2440             Append(u"setN");
2441             break;
2442         case MS_WP:
2443             Append(u"wp");
2444             break;
2445         case MS_LAPLACE:
2446             Append(u"laplace");
2447             break;
2448         case MS_SETQ:
2449             Append(u"setQ");
2450             break;
2451         case MS_RE:
2452             Append(u"Re");
2453             break;
2454         case MS_SETR:
2455             Append(u"setR");
2456             break;
2457         case MS_SETZ:
2458             Append(u"setZ");
2459             break;
2460         case MS_ALEPH:
2461             Append(u"aleph");
2462             break;
2463         case 0x0362:
2464             Append(u"widevec");
2465             break;
2466         case MS_DLARROW:
2467             Append(u"dlarrow");
2468             break;
2469         case MS_DRARROW:
2470             Append(u"drarrow");
2471             break;
2472         case MS_DLRARROW:
2473             Append(u"dlrarrow");
2474             break;
2475         case MS_FORALL:
2476             Append(u"forall");
2477             break;
2478         case MS_PARTIAL:
2479             Append(u"partial");
2480             break;
2481         case MS_EXISTS:
2482             Append(u"exists");
2483             break;
2484         case MS_NOTEXISTS:
2485             Append(u"notexists");
2486             break;
2487         case MS_EMPTYSET:
2488             Append(u"emptyset");
2489             break;
2490         case MS_NABLA:
2491             Append(u"nabla");
2492             break;
2493         case MS_BACKEPSILON:
2494             Append(u"backepsilon");
2495             break;
2496         case MS_CIRC:
2497             Append(u"circ");
2498             break;
2499         case MS_INFINITY:
2500             Append(u"infinity");
2501             break;
2502         case 0x22b2: // NORMAL SUBGROUP OF
2503             Append(OUStringChar(cChar));
2504             break;
2505         case 0x22b3: // CONTAINS AS NORMAL SUBGROUP
2506             Append(OUStringChar(cChar));
2507             break;
2508         case MS_ORTHO:
2509             Append(u"ortho");
2510             break;
2511         case MS_DOTSVERT:
2512             Append(u"dotsvert");
2513             break;
2514         case MS_DOTSAXIS:
2515             Append(u"dotsaxis");
2516             break;
2517         case MS_DOTSUP:
2518             Append(u"dotsup");
2519             break;
2520         case MS_DOTSDOWN:
2521             Append(u"dotsdown");
2522             break;
2523         case '^':
2524             Append(u"^");
2525             break;
2526         case 0xe091:
2527             Append(u"widehat");
2528             break;
2529         case 0xe096:
2530             Append(u"widetilde");
2531             break;
2532         case 0xe098:
2533             Append(u"widevec");
2534             break;
2535         case 0xeb01:    //no space
2536         case 0xeb08:    //normal space
2537             break;
2538         case 0xef04:    //tiny space
2539         case 0xef05:    //tiny space
2540         case 0xeb02:    //small space
2541         case 0xeb04:    //medium space
2542             Append(u"`");
2543             break;
2544         case 0xeb05:    //large space
2545             Append(u"~");
2546             break;
2547         case 0x3a9:
2548             Append(u"%OMEGA");
2549             break;
2550         default:
2551             Append(OUStringChar(cChar));
2552             break;
2553     }
2554 }
2555 
Visit(SmBlankNode * pNode)2556 void SmNodeToTextVisitor::Visit( SmBlankNode* pNode )
2557 {
2558     sal_uInt16 nNum = pNode->GetBlankNum();
2559     if (nNum <= 0)
2560         return;
2561     sal_uInt16 nWide = nNum / 4;
2562     sal_uInt16 nNarrow = nNum % 4;
2563     for (sal_uInt16 i = 0; i < nWide; i++)
2564         Append(u"~");
2565     for (sal_uInt16 i = 0; i < nNarrow; i++)
2566         Append(u"`");
2567     Append(u" ");
2568 }
2569 
Visit(SmErrorNode *)2570 void SmNodeToTextVisitor::Visit( SmErrorNode* )
2571 {
2572     // Add something for error nodes so that we can parse this back.
2573     Append(u"{}");
2574 }
2575 
Visit(SmLineNode * pNode)2576 void SmNodeToTextVisitor::Visit( SmLineNode* pNode )
2577 {
2578     for( auto pChild : *pNode )
2579     {
2580         if(!pChild)
2581             continue;
2582         Separate( );
2583         pChild->Accept( this );
2584     }
2585 }
2586 
Visit(SmExpressionNode * pNode)2587 void SmNodeToTextVisitor::Visit( SmExpressionNode* pNode )
2588 {
2589     bool bracketsNeeded = pNode->GetNumSubNodes() != 1;
2590     if (!bracketsNeeded)
2591     {
2592         const SmNode *pParent = pNode->GetParent();
2593         // nested subsups
2594         bracketsNeeded =
2595             pParent && pParent->GetType() == SmNodeType::SubSup &&
2596             pNode->GetNumSubNodes() == 1 &&
2597             pNode->GetSubNode(0)->GetType() == SmNodeType::SubSup;
2598     }
2599 
2600     if (bracketsNeeded) {
2601         Append(u"{ ");
2602     }
2603     for( auto pChild : *pNode )
2604     {
2605         if(!pChild)
2606             continue;
2607         pChild->Accept( this );
2608         Separate( );
2609     }
2610     if (bracketsNeeded) {
2611         Append(u"} ");
2612     }
2613 }
2614 
Visit(SmPolyLineNode *)2615 void SmNodeToTextVisitor::Visit( SmPolyLineNode* )
2616 {
2617 }
2618 
Visit(SmRootNode * pNode)2619 void SmNodeToTextVisitor::Visit( SmRootNode* pNode )
2620 {
2621     SmNode *pExtra   = pNode->GetSubNode( 0 ),
2622            *pBody    = pNode->GetSubNode( 2 );
2623     if( pExtra ) {
2624         Append(u"nroot");
2625         LineToText( pExtra );
2626     } else
2627         Append(u"sqrt");
2628     LineToText( pBody );
2629 }
2630 
Visit(SmRootSymbolNode *)2631 void SmNodeToTextVisitor::Visit( SmRootSymbolNode* )
2632 {
2633 }
2634 
Visit(SmRectangleNode *)2635 void SmNodeToTextVisitor::Visit( SmRectangleNode* )
2636 {
2637 }
2638 
Visit(SmVerticalBraceNode * pNode)2639 void SmNodeToTextVisitor::Visit( SmVerticalBraceNode* pNode )
2640 {
2641     SmNode *pBody   = pNode->Body(),
2642            *pScript = pNode->Script();
2643     LineToText( pBody );
2644     Append( pNode->GetToken( ).aText );
2645     LineToText( pScript );
2646 }
2647 
2648 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
2649