office-gobmx/starmath/inc/visitors.hxx
Khaled Hosny f80c2c7075 tdf#134193: Fix inline editing with RTL direction
Change-Id: I65d3ed0d7c56839ce3674318144269719b043ba5
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/156261
Tested-by: Jenkins
Reviewed-by: خالد حسني <khaled@libreoffice.org>
2023-09-04 18:18:37 +02:00

538 lines
19 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
/** Visitors are an easy way to automating operations with nodes.
*
* The available visitors are:
* SmVisitor base class
* SmDefaultingVisitor default visitor
* SmDrawingVisitor draws formula
* SmCaretPosGraphBuildingVisitor position of the node inside starmath code
* SmCloningVisitor duplicate nodes
* SmNodeToTextVisitor create code from nodes
*
*/
#pragma once
#include <sal/config.h>
#include <sal/log.hxx>
#include "node.hxx"
#include "caret.hxx"
/** Base class for visitors that visits a tree of SmNodes
* @remarks all methods have been left abstract to ensure that implementers
* don't forget to implement one.
*/
class SmVisitor
{
public:
virtual void Visit( SmTableNode* pNode ) = 0;
virtual void Visit( SmBraceNode* pNode ) = 0;
virtual void Visit( SmBracebodyNode* pNode ) = 0;
virtual void Visit( SmOperNode* pNode ) = 0;
virtual void Visit( SmAlignNode* pNode ) = 0;
virtual void Visit( SmAttributeNode* pNode ) = 0;
virtual void Visit( SmFontNode* pNode ) = 0;
virtual void Visit( SmUnHorNode* pNode ) = 0;
virtual void Visit( SmBinHorNode* pNode ) = 0;
virtual void Visit( SmBinVerNode* pNode ) = 0;
virtual void Visit( SmBinDiagonalNode* pNode ) = 0;
virtual void Visit( SmSubSupNode* pNode ) = 0;
virtual void Visit( SmMatrixNode* pNode ) = 0;
virtual void Visit( SmPlaceNode* pNode ) = 0;
virtual void Visit( SmTextNode* pNode ) = 0;
virtual void Visit( SmSpecialNode* pNode ) = 0;
virtual void Visit( SmGlyphSpecialNode* pNode ) = 0;
virtual void Visit( SmMathSymbolNode* pNode ) = 0;
virtual void Visit( SmBlankNode* pNode ) = 0;
virtual void Visit( SmErrorNode* pNode ) = 0;
virtual void Visit( SmLineNode* pNode ) = 0;
virtual void Visit( SmExpressionNode* pNode ) = 0;
virtual void Visit( SmPolyLineNode* pNode ) = 0;
virtual void Visit( SmRootNode* pNode ) = 0;
virtual void Visit( SmRootSymbolNode* pNode ) = 0;
virtual void Visit( SmRectangleNode* pNode ) = 0;
virtual void Visit( SmVerticalBraceNode* pNode ) = 0;
protected:
~SmVisitor() {}
};
// SmDefaultingVisitor
/** Visitor that uses DefaultVisit for handling visits by default
*
* This abstract baseclass is useful for visitors where many methods share the same
* implementation.
*/
class SmDefaultingVisitor : public SmVisitor
{
public:
void Visit( SmTableNode* pNode ) override;
void Visit( SmBraceNode* pNode ) override;
void Visit( SmBracebodyNode* pNode ) override;
void Visit( SmOperNode* pNode ) override;
void Visit( SmAlignNode* pNode ) override;
void Visit( SmAttributeNode* pNode ) override;
void Visit( SmFontNode* pNode ) override;
void Visit( SmUnHorNode* pNode ) override;
void Visit( SmBinHorNode* pNode ) override;
void Visit( SmBinVerNode* pNode ) override;
void Visit( SmBinDiagonalNode* pNode ) override;
void Visit( SmSubSupNode* pNode ) override;
void Visit( SmMatrixNode* pNode ) override;
void Visit( SmPlaceNode* pNode ) override;
void Visit( SmTextNode* pNode ) override;
void Visit( SmSpecialNode* pNode ) override;
void Visit( SmGlyphSpecialNode* pNode ) override;
void Visit( SmMathSymbolNode* pNode ) override;
void Visit( SmBlankNode* pNode ) override;
void Visit( SmErrorNode* pNode ) override;
void Visit( SmLineNode* pNode ) override;
void Visit( SmExpressionNode* pNode ) override;
void Visit( SmPolyLineNode* pNode ) override;
void Visit( SmRootNode* pNode ) override;
void Visit( SmRootSymbolNode* pNode ) override;
void Visit( SmRectangleNode* pNode ) override;
void Visit( SmVerticalBraceNode* pNode ) override;
protected:
~SmDefaultingVisitor() {}
/** Method invoked by Visit methods by default */
virtual void DefaultVisit( SmNode* pNode ) = 0;
};
// SmCaretLinesVisitor: ancestor of caret rectangle enumeration and drawing visitors
class SmCaretLinesVisitor : public SmDefaultingVisitor
{
public:
SmCaretLinesVisitor(OutputDevice& rDevice, SmCaretPos position, Point offset);
virtual ~SmCaretLinesVisitor() = default;
void Visit(SmTextNode* pNode) override;
using SmDefaultingVisitor::Visit;
protected:
void DoIt();
OutputDevice& getDev() { return mrDev; }
virtual void ProcessCaretLine(Point from, Point to) = 0;
virtual void ProcessUnderline(Point from, Point to) = 0;
/** Default method for drawing pNodes */
void DefaultVisit(SmNode* pNode) override;
private:
OutputDevice& mrDev;
SmCaretPos maPos;
/** Offset to draw from */
Point maOffset;
};
// SmCaretRectanglesVisitor: obtains the set of rectangles to sent to lok
class SmCaretRectanglesVisitor final : public SmCaretLinesVisitor
{
public:
SmCaretRectanglesVisitor(OutputDevice& rDevice, SmCaretPos position);
const tools::Rectangle& getCaret() const { return maCaret; }
protected:
virtual void ProcessCaretLine(Point from, Point to) override;
virtual void ProcessUnderline(Point from, Point to) override;
private:
tools::Rectangle maCaret;
};
// SmCaretDrawingVisitor
/** Visitor for drawing a caret position */
class SmCaretDrawingVisitor final : public SmCaretLinesVisitor
{
public:
/** Given position and device this constructor will draw the caret */
SmCaretDrawingVisitor( OutputDevice& rDevice, SmCaretPos position, Point offset, bool caretVisible );
protected:
virtual void ProcessCaretLine(Point from, Point to) override;
virtual void ProcessUnderline(Point from, Point to) override;
private:
bool mbCaretVisible;
};
// SmCaretPos2LineVisitor
/** Visitor getting a line from a caret position */
class SmCaretPos2LineVisitor final : public SmDefaultingVisitor
{
public:
/** Given position and device this constructor will compute a line for the caret */
SmCaretPos2LineVisitor( OutputDevice *pDevice, SmCaretPos position )
: mpDev( pDevice )
, maPos( position )
{
SAL_WARN_IF( !position.IsValid(), "starmath", "Cannot draw invalid position!" );
maPos.pSelectedNode->Accept( this );
}
virtual ~SmCaretPos2LineVisitor() {}
void Visit( SmTextNode* pNode ) override;
using SmDefaultingVisitor::Visit;
const SmCaretLine& GetResult( ) const {
return maLine;
}
private:
SmCaretLine maLine;
VclPtr<OutputDevice> mpDev;
SmCaretPos maPos;
/** Default method for computing lines for pNodes */
void DefaultVisit( SmNode* pNode ) override;
};
// SmDrawingVisitor
/** Visitor for drawing SmNodes to OutputDevice */
class SmDrawingVisitor final : public SmVisitor
{
public:
/** Create an instance of SmDrawingVisitor, and use it to draw a formula
* @param rDevice Device to draw on
* @param position Offset on device to draw the formula
* @param pTree Formula tree to draw
* @param rFormat Formula formatting settings
* @remarks This constructor will do the drawing, no need to anything more.
*/
SmDrawingVisitor( OutputDevice &rDevice, Point position, SmNode* pTree, const SmFormat& rFormat )
: mrDev( rDevice )
, maPosition( position )
, mrFormat( rFormat )
{
if (mrFormat.IsRightToLeft() && mrDev.GetOutDevType() != OUTDEV_WINDOW)
mrDev.ReMirror(maPosition);
pTree->Accept( this );
}
virtual ~SmDrawingVisitor() {}
void Visit( SmTableNode* pNode ) override;
void Visit( SmBraceNode* pNode ) override;
void Visit( SmBracebodyNode* pNode ) override;
void Visit( SmOperNode* pNode ) override;
void Visit( SmAlignNode* pNode ) override;
void Visit( SmAttributeNode* pNode ) override;
void Visit( SmFontNode* pNode ) override;
void Visit( SmUnHorNode* pNode ) override;
void Visit( SmBinHorNode* pNode ) override;
void Visit( SmBinVerNode* pNode ) override;
void Visit( SmBinDiagonalNode* pNode ) override;
void Visit( SmSubSupNode* pNode ) override;
void Visit( SmMatrixNode* pNode ) override;
void Visit( SmPlaceNode* pNode ) override;
void Visit( SmTextNode* pNode ) override;
void Visit( SmSpecialNode* pNode ) override;
void Visit( SmGlyphSpecialNode* pNode ) override;
void Visit( SmMathSymbolNode* pNode ) override;
void Visit( SmBlankNode* pNode ) override;
void Visit( SmErrorNode* pNode ) override;
void Visit( SmLineNode* pNode ) override;
void Visit( SmExpressionNode* pNode ) override;
void Visit( SmPolyLineNode* pNode ) override;
void Visit( SmRootNode* pNode ) override;
void Visit( SmRootSymbolNode* pNode ) override;
void Visit( SmRectangleNode* pNode ) override;
void Visit( SmVerticalBraceNode* pNode ) override;
private:
/** Draw the children of a pNode
* This the default method, use by most pNodes
*/
void DrawChildren( SmStructureNode* pNode );
/** Draw an SmTextNode or a subclass of this */
void DrawTextNode( SmTextNode* pNode );
/** Draw an SmSpecialNode or a subclass of this */
void DrawSpecialNode( SmSpecialNode* pNode );
/** OutputDevice to draw on */
OutputDevice& mrDev;
/** Position to draw on the mrDev
* @remarks This variable is used to pass parameters in DrawChildren( ), this means
that after a call to DrawChildren( ) the contents of this method is undefined
so if needed cache it locally on the stack.
*/
Point maPosition;
const SmFormat& mrFormat;
};
// SmSetSelectionVisitor
/** Set Selection Visitor
* Sets the IsSelected( ) property on all SmNodes of the tree
*/
class SmSetSelectionVisitor final : public SmDefaultingVisitor
{
public:
SmSetSelectionVisitor( SmCaretPos startPos, SmCaretPos endPos, SmNode* pNode);
virtual ~SmSetSelectionVisitor() {}
void Visit( SmBinHorNode* pNode ) override;
void Visit( SmUnHorNode* pNode ) override;
void Visit( SmFontNode* pNode ) override;
void Visit( SmTextNode* pNode ) override;
void Visit( SmExpressionNode* pNode ) override;
void Visit( SmLineNode* pNode ) override;
void Visit( SmAlignNode* pNode ) override;
using SmDefaultingVisitor::Visit;
/** Set IsSelected on all pNodes of pSubTree */
static void SetSelectedOnAll( SmNode* pSubTree, bool IsSelected = true );
private:
/** Visit a selectable pNode
* Can be used to handle pNodes that can be selected, that doesn't have more SmCaretPos'
* than 0 and 1 inside them. SmTextNode should be handle separately!
* Also note that pNodes such as SmBinVerNode cannot be selected, don't this method for
* it.
*/
void DefaultVisit( SmNode* pNode ) override;
void VisitCompositionNode( SmStructureNode* pNode );
/** Caret position where the selection starts */
SmCaretPos maStartPos;
/** Caret position where the selection ends */
SmCaretPos maEndPos;
/** The current state of this visitor
* This property changes when the visitor meets either maStartPos
* or maEndPos. This means that anything visited in between will be
* selected.
*/
bool mbSelecting;
};
// SmCaretPosGraphBuildingVisitor
/** A visitor for building a SmCaretPosGraph
*
* Visit invariant:
* Each pNode, except SmExpressionNode, SmBinHorNode and a few others, constitutes an entry
* in a line. Consider the line entry "H", this entry creates one carat position, here
* denoted by | in "H|".
*
* Parameter variables:
* The following variables are used to transfer parameters into calls and results out
* of calls.
* pRightMost : SmCaretPosGraphEntry*
*
* Prior to a Visit call:
* pRightMost: A pointer to right most position in front of the current line entry.
*
* After a Visit call:
* pRightMost: A pointer to the right most position in the called line entry, if no there's
* no caret positions in called line entry don't change this variable.
*/
class SmCaretPosGraphBuildingVisitor final : public SmVisitor
{
public:
/** Builds a caret position graph for pRootNode */
explicit SmCaretPosGraphBuildingVisitor( SmNode* pRootNode );
virtual ~SmCaretPosGraphBuildingVisitor();
void Visit( SmTableNode* pNode ) override;
void Visit( SmBraceNode* pNode ) override;
void Visit( SmBracebodyNode* pNode ) override;
void Visit( SmOperNode* pNode ) override;
void Visit( SmAlignNode* pNode ) override;
void Visit( SmAttributeNode* pNode ) override;
void Visit( SmFontNode* pNode ) override;
void Visit( SmUnHorNode* pNode ) override;
void Visit( SmBinHorNode* pNode ) override;
void Visit( SmBinVerNode* pNode ) override;
void Visit( SmBinDiagonalNode* pNode ) override;
void Visit( SmSubSupNode* pNode ) override;
void Visit( SmMatrixNode* pNode ) override;
void Visit( SmPlaceNode* pNode ) override;
void Visit( SmTextNode* pNode ) override;
void Visit( SmSpecialNode* pNode ) override;
void Visit( SmGlyphSpecialNode* pNode ) override;
void Visit( SmMathSymbolNode* pNode ) override;
void Visit( SmBlankNode* pNode ) override;
void Visit( SmErrorNode* pNode ) override;
void Visit( SmLineNode* pNode ) override;
void Visit( SmExpressionNode* pNode ) override;
void Visit( SmPolyLineNode* pNode ) override;
void Visit( SmRootNode* pNode ) override;
void Visit( SmRootSymbolNode* pNode ) override;
void Visit( SmRectangleNode* pNode ) override;
void Visit( SmVerticalBraceNode* pNode ) override;
SmCaretPosGraph* takeGraph()
{
return mpGraph.release();
}
private:
SmCaretPosGraphEntry* mpRightMost;
std::unique_ptr<SmCaretPosGraph> mpGraph;
};
// SmCloningVisitor
/** Visitor for cloning a pNode
*
* This visitor creates deep clones.
*/
class SmCloningVisitor final : public SmVisitor
{
public:
SmCloningVisitor()
: mpResult(nullptr)
{}
virtual ~SmCloningVisitor() {}
void Visit( SmTableNode* pNode ) override;
void Visit( SmBraceNode* pNode ) override;
void Visit( SmBracebodyNode* pNode ) override;
void Visit( SmOperNode* pNode ) override;
void Visit( SmAlignNode* pNode ) override;
void Visit( SmAttributeNode* pNode ) override;
void Visit( SmFontNode* pNode ) override;
void Visit( SmUnHorNode* pNode ) override;
void Visit( SmBinHorNode* pNode ) override;
void Visit( SmBinVerNode* pNode ) override;
void Visit( SmBinDiagonalNode* pNode ) override;
void Visit( SmSubSupNode* pNode ) override;
void Visit( SmMatrixNode* pNode ) override;
void Visit( SmPlaceNode* pNode ) override;
void Visit( SmTextNode* pNode ) override;
void Visit( SmSpecialNode* pNode ) override;
void Visit( SmGlyphSpecialNode* pNode ) override;
void Visit( SmMathSymbolNode* pNode ) override;
void Visit( SmBlankNode* pNode ) override;
void Visit( SmErrorNode* pNode ) override;
void Visit( SmLineNode* pNode ) override;
void Visit( SmExpressionNode* pNode ) override;
void Visit( SmPolyLineNode* pNode ) override;
void Visit( SmRootNode* pNode ) override;
void Visit( SmRootSymbolNode* pNode ) override;
void Visit( SmRectangleNode* pNode ) override;
void Visit( SmVerticalBraceNode* pNode ) override;
/** Clone a pNode */
SmNode* Clone( SmNode* pNode );
private:
SmNode* mpResult;
/** Clone children of pSource and give them to pTarget */
void CloneKids( SmStructureNode* pSource, SmStructureNode* pTarget );
/** Clone attributes on a pNode */
static void CloneNodeAttr( SmNode const * pSource, SmNode* pTarget );
};
// SmSelectionRectanglesVisitor: collect selection
class SmSelectionRectanglesVisitor : public SmDefaultingVisitor
{
public:
SmSelectionRectanglesVisitor(OutputDevice& rDevice, SmNode* pTree);
virtual ~SmSelectionRectanglesVisitor() = default;
void Visit( SmTextNode* pNode ) override;
using SmDefaultingVisitor::Visit;
const tools::Rectangle& GetSelection() { return maSelectionArea; }
private:
/** Reference to drawing device */
OutputDevice& mrDev;
/** The current area that is selected */
tools::Rectangle maSelectionArea;
/** Extend the area that must be selected */
void ExtendSelectionArea(const tools::Rectangle& rArea) { maSelectionArea.Union(rArea); }
/** Default visiting method */
void DefaultVisit( SmNode* pNode ) override;
/** Visit the children of a given pNode */
void VisitChildren( SmNode* pNode );
};
// SmSelectionDrawingVisitor
class SmSelectionDrawingVisitor final : public SmSelectionRectanglesVisitor
{
public:
/** Draws a selection on rDevice for the selection on pTree */
SmSelectionDrawingVisitor( OutputDevice& rDevice, SmNode* pTree, const Point& rOffset );
};
// SmNodeToTextVisitor
/** Extract command text from pNodes */
class SmNodeToTextVisitor final : public SmVisitor
{
public:
SmNodeToTextVisitor( SmNode* pNode, OUString &rText );
virtual ~SmNodeToTextVisitor() {}
void Visit( SmTableNode* pNode ) override;
void Visit( SmBraceNode* pNode ) override;
void Visit( SmBracebodyNode* pNode ) override;
void Visit( SmOperNode* pNode ) override;
void Visit( SmAlignNode* pNode ) override;
void Visit( SmAttributeNode* pNode ) override;
void Visit( SmFontNode* pNode ) override;
void Visit( SmUnHorNode* pNode ) override;
void Visit( SmBinHorNode* pNode ) override;
void Visit( SmBinVerNode* pNode ) override;
void Visit( SmBinDiagonalNode* pNode ) override;
void Visit( SmSubSupNode* pNode ) override;
void Visit( SmMatrixNode* pNode ) override;
void Visit( SmPlaceNode* pNode ) override;
void Visit( SmTextNode* pNode ) override;
void Visit( SmSpecialNode* pNode ) override;
void Visit( SmGlyphSpecialNode* pNode ) override;
void Visit( SmMathSymbolNode* pNode ) override;
void Visit( SmBlankNode* pNode ) override;
void Visit( SmErrorNode* pNode ) override;
void Visit( SmLineNode* pNode ) override;
void Visit( SmExpressionNode* pNode ) override;
void Visit( SmPolyLineNode* pNode ) override;
void Visit( SmRootNode* pNode ) override;
void Visit( SmRootSymbolNode* pNode ) override;
void Visit( SmRectangleNode* pNode ) override;
void Visit( SmVerticalBraceNode* pNode ) override;
private:
/**
* Extract text from a pNode that constitutes a line.
* @param pNode
* @return
*/
void LineToText( SmNode* pNode ) {
Separate( );
if( pNode ) pNode->Accept( this );
Separate( );
}
/**
* Appends rText to the OUStringBuffer ( maCmdText ).
* @param rText
* @return
*/
void Append( std::u16string_view rText ) {
maCmdText.append( rText );
}
/**
* Append a blank for separation, if needed.
* It is needed if last char is not ' '.
* @return
*/
void Separate( ){
if( !maCmdText.isEmpty() && maCmdText[ maCmdText.getLength() - 1 ] != ' ' )
maCmdText.append(' ');
}
/** Output text generated from the pNodes */
OUStringBuffer maCmdText;
};
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */