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: |"]; // 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: ∈"]; // Unicode "ELEMENT OF"
1265 * n10 -> n13 [label="2"];
1266 * n13 [label="SmMathSymbolNode: ℤ"]; // 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