/* -*- 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/.
*/
void SmCaretDrawingVisitor::ProcessCaretLine(Point from, Point to)
{ if ( mbCaretVisible ) { //Set color
getDev().SetLineColor(COL_BLACK); //Draw vertical line
getDev().DrawLine(from, to);
}
}
void SmCaretDrawingVisitor::ProcessUnderline(Point from, Point to)
{ //Set color
getDev().SetLineColor(COL_BLACK); //Underline the line
getDev().DrawLine(from, to);
}
// since the width is always unscaled it corresponds to the _original_ // _unscaled_ font height to be used, we use that to calculate the // bar height. Thus it is independent of the arguments height. // ( see display of sqrt QQQ versus sqrt stack{Q#Q#Q#Q} )
tools::Long nBarHeight = pNode->GetWidth( ) * 7 / 100;
tools::Long nBarWidth = pNode->GetBodyWidth( ) + pNode->GetBorderWidth( );
Point aBarOffset( pNode->GetWidth( ), +pNode->GetBorderWidth( ) );
Point aBarPos( maPosition + aBarOffset );
void SmDrawingVisitor::DrawSpecialNode( SmSpecialNode* pNode )
{ //! since this chars might come from any font, that we may not have //! set to ALIGN_BASELINE yet, we do it now.
pNode->GetFont( ).SetAlignment( ALIGN_BASELINE );
for( auto pChild : *pNode )
{ if(!pChild) continue;
Point aOffset ( pChild->GetTopLeft( ) - pNode->GetTopLeft( ) );
maPosition = rPosition + aOffset;
pChild->Accept( this );
}
}
// SmSetSelectionVisitor
SmSetSelectionVisitor::SmSetSelectionVisitor( SmCaretPos startPos, SmCaretPos endPos, SmNode* pTree)
: maStartPos(startPos)
, maEndPos(endPos)
, mbSelecting(false)
{ //Assume that pTree is a SmTableNode
SAL_WARN_IF(pTree->GetType() != SmNodeType::Table, "starmath", "pTree should be a SmTableNode!"); //Visit root node, this is special as this node cannot be selected, but its children can! if(pTree->GetType() == SmNodeType::Table){ //Change state if maStartPos is in front of this node if( maStartPos.pSelectedNode == pTree && maStartPos.nIndex == 0 )
mbSelecting = !mbSelecting; //Change state if maEndPos is in front of this node if( maEndPos.pSelectedNode == pTree && maEndPos.nIndex == 0 )
mbSelecting = !mbSelecting;
SAL_WARN_IF(mbSelecting, "starmath", "Caret positions needed to set mbSelecting about, shouldn't be possible!");
//Visit lines for( auto pChild : *static_cast<SmStructureNode*>(pTree) )
{ if(!pChild) continue;
pChild->Accept( this ); //If we started a selection in this line and it haven't ended, we do that now! if(mbSelecting) {
mbSelecting = false;
SetSelectedOnAll(pChild); //Set maStartPos and maEndPos to invalid positions, this ensures that an unused //start or end (because we forced end above), doesn't start a new selection.
maStartPos = maEndPos = SmCaretPos();
}
} //Check if pTree isn't selected
SAL_WARN_IF(pTree->IsSelected(), "starmath", "pTree should never be selected!"); //Discard the selection if there's a bug (it's better than crashing) if(pTree->IsSelected())
SetSelectedOnAll(pTree, false);
}else//This shouldn't happen, but I don't see any reason to die if it does
pTree->Accept(this);
}
if(pSubTree->GetNumSubNodes() == 0) return; //Quick BFS to set all selections for( auto pChild : *static_cast<SmStructureNode*>(pSubTree) )
{ if(!pChild) continue;
SetSelectedOnAll( pChild, IsSelected );
}
}
void SmSetSelectionVisitor::DefaultVisit( SmNode* pNode ) { //Change state if maStartPos is in front of this node if( maStartPos.pSelectedNode == pNode && maStartPos.nIndex == 0 )
mbSelecting = !mbSelecting; //Change state if maEndPos is in front of this node if( maEndPos.pSelectedNode == pNode && maEndPos.nIndex == 0 )
mbSelecting = !mbSelecting;
//Cache current state bool WasSelecting = mbSelecting; bool ChangedState = false;
//Set selected
pNode->SetSelected( mbSelecting );
//Visit children if(pNode->GetNumSubNodes() > 0)
{ for( auto pChild : *static_cast<SmStructureNode*>(pNode) )
{ if(!pChild) continue;
pChild->Accept( this );
ChangedState = ( WasSelecting != mbSelecting ) || ChangedState;
}
}
//If state changed if( ChangedState )
{ //Select this node and all of its children //(Make exception for SmBracebodyNode) if( pNode->GetType() != SmNodeType::Bracebody ||
!pNode->GetParent() ||
pNode->GetParent()->GetType() != SmNodeType::Brace )
SetSelectedOnAll( pNode ); else
SetSelectedOnAll( pNode->GetParent() ); /* If the equation is: sqrt{2 + 4} + 5 * And the selection is: sqrt{2 + [4} +] 5 * Where [ denotes maStartPos and ] denotes maEndPos * Then the sqrt node should be selected, so that the * effective selection is: [sqrt{2 + 4} +] 5 * The same is the case if we swap maStartPos and maEndPos.
*/
}
//Change state if maStartPos is after this node if( maStartPos.pSelectedNode == pNode && maStartPos.nIndex == 1 )
{
mbSelecting = !mbSelecting;
} //Change state if maEndPos is after of this node if( maEndPos.pSelectedNode == pNode && maEndPos.nIndex == 1 )
{
mbSelecting = !mbSelecting;
}
}
void SmSetSelectionVisitor::VisitCompositionNode( SmStructureNode* pNode )
{ //Change state if maStartPos is in front of this node if( maStartPos.pSelectedNode == pNode && maStartPos.nIndex == 0 )
mbSelecting = !mbSelecting; //Change state if maEndPos is in front of this node if( maEndPos.pSelectedNode == pNode && maEndPos.nIndex == 0 )
mbSelecting = !mbSelecting;
//Cache current state bool WasSelecting = mbSelecting;
//Visit children for( auto pChild : *pNode )
{ if(!pChild) continue;
pChild->Accept( this );
}
//Set selected, if everything was selected
pNode->SetSelected( WasSelecting && mbSelecting );
//Change state if maStartPos is after this node if( maStartPos.pSelectedNode == pNode && maStartPos.nIndex == 1 )
mbSelecting = !mbSelecting; //Change state if maEndPos is after of this node if( maEndPos.pSelectedNode == pNode && maEndPos.nIndex == 1 )
mbSelecting = !mbSelecting;
}
SmCaretPosGraphBuildingVisitor::SmCaretPosGraphBuildingVisitor( SmNode* pRootNode )
: mpRightMost(nullptr)
, mpGraph(new SmCaretPosGraph)
{ //pRootNode should always be a table
SAL_WARN_IF( pRootNode->GetType( ) != SmNodeType::Table, "starmath", "pRootNode must be a table node"); //Handle the special case where SmNodeType::Table is used a rootnode if( pRootNode->GetType( ) == SmNodeType::Table ){ //Children are SmLineNodes //Or so I thought... Apparently, the children can be instances of SmExpression //especially if there's an error in the formula... So here we go, a simple work around. for( auto pChild : *static_cast<SmStructureNode*>(pRootNode) )
{ if(!pChild) continue;
mpRightMost = mpGraph->Add( SmCaretPos( pChild, 0 ) );
pChild->Accept( this );
}
}else
pRootNode->Accept(this);
}
void SmCaretPosGraphBuildingVisitor::Visit( SmLineNode* pNode ){ for( auto pChild : *pNode )
{ if(!pChild) continue;
pChild->Accept( this );
}
}
/** Build SmCaretPosGraph for SmTableNode * This method covers cases where SmTableNode is used in a binom or stack, * the special case where it is used as root node for the entire formula is * handled in the constructor.
*/ void SmCaretPosGraphBuildingVisitor::Visit( SmTableNode* pNode ){
SmCaretPosGraphEntry *left = mpRightMost,
*right = mpGraph->Add( SmCaretPos( pNode, 1) ); bool bIsFirst = true; for( auto pChild : *pNode )
{ if(!pChild) continue;
mpRightMost = mpGraph->Add( SmCaretPos( pChild, 0 ), left); if(bIsFirst)
left->SetRight(mpRightMost);
pChild->Accept( this );
mpRightMost->SetRight(right); if(bIsFirst)
right->SetLeft(mpRightMost);
bIsFirst = false;
}
mpRightMost = right;
}
/** Build SmCaretPosGraph for SmSubSupNode * * The child positions in a SubSupNode, where H is the body: * \code * CSUP * * LSUP H H RSUP * H H * HHHH * H H * LSUB H H RSUB * * CSUB * \endcode * * Graph over these, where "left" is before the SmSubSupNode and "right" is after: * \dot * digraph Graph{ * left -> H; * H -> right; * LSUP -> H; * LSUB -> H; * CSUP -> right; * CSUB -> right; * RSUP -> right; * RSUB -> right; * }; * \enddot *
*/ void SmCaretPosGraphBuildingVisitor::Visit( SmSubSupNode* pNode )
{
SmCaretPosGraphEntry *left,
*right,
*bodyLeft,
*bodyRight;
assert(mpRightMost);
left = mpRightMost;
//Create bodyLeft
SAL_WARN_IF( !pNode->GetBody(), "starmath", "SmSubSupNode Doesn't have a body!" );
bodyLeft = mpGraph->Add( SmCaretPos( pNode->GetBody( ), 0 ), left );
left->SetRight( bodyLeft ); //TODO: Don't make this if LSUP or LSUB are NULL ( not sure??? )
//Create right
right = mpGraph->Add( SmCaretPos( pNode, 1 ) );
//Visit the body, to get bodyRight
mpRightMost = bodyLeft;
pNode->GetBody( )->Accept( this );
bodyRight = mpRightMost;
bodyRight->SetRight( right );
right->SetLeft( bodyRight );
/** Build caret position for SmOperNode * * If first child is an SmSubSupNode we will ignore its * body, as this body is a SmMathSymbol, for SUM, INT or similar * that shouldn't be subject to modification. * If first child is not a SmSubSupNode, ignore it completely * as it is a SmMathSymbol. * * The child positions in a SmOperNode, where H is symbol, e.g. int, sum or similar: * \code * TO * * LSUP H H RSUP BBB BB BBB B B * H H B B B B B B B B * HHHH BBB B B B B B * H H B B B B B B B * LSUB H H RSUB BBB BB BBB B * * FROM * \endcode * Notice, CSUP, etc. are actually grandchildren, but inorder to ignore H, these are visited * from here. If they are present, that is if pOper is an instance of SmSubSupNode. * * Graph over these, where "left" is before the SmOperNode and "right" is after: * \dot * digraph Graph{ * left -> BODY; * BODY -> right; * LSUP -> BODY; * LSUB -> BODY; * TO -> BODY; * FROM -> BODY; * RSUP -> BODY; * RSUB -> BODY; * }; * \enddot
*/ void SmCaretPosGraphBuildingVisitor::Visit( SmOperNode* pNode )
{
SmNode *pOper = pNode->GetSubNode( 0 ),
*pBody = pNode->GetSubNode( 1 );
SmCaretPosGraphEntry *left = mpRightMost,
*bodyLeft,
*bodyRight,
*right; //Create body left
bodyLeft = mpGraph->Add( SmCaretPos( pBody, 0 ), left );
left->SetRight( bodyLeft );
//Visit body, get bodyRight
mpRightMost = bodyLeft;
pBody->Accept( this );
bodyRight = mpRightMost;
//Create right
right = mpGraph->Add( SmCaretPos( pNode, 1 ), bodyRight );
bodyRight->SetRight( right );
//Get subsup pNode if any
SmSubSupNode* pSubSup = pOper->GetType( ) == SmNodeType::SubSup ? static_cast<SmSubSupNode*>(pOper) : nullptr;
if( pSubSup ) {
SmNode* pChild; for (SmSubSup const nodeType : { LSUP, LSUB, CSUP, CSUB, RSUP, RSUB })
{
pChild = pSubSup->GetSubSup(nodeType); if( pChild )
{ //Create position in front of pChild
SmCaretPosGraphEntry *childLeft = mpGraph->Add( SmCaretPos( pChild, 0 ), left ); //Visit pChild
mpRightMost = childLeft;
pChild->Accept( this ); //Set right on mpRightMost from pChild
mpRightMost->SetRight( bodyLeft );
}
}
}
r = mpRightMost;
}
mpRightMost->SetRight( right ); if( ( pNode->GetNumRows() - 1U ) / 2 == i )
right->SetLeft( mpRightMost );
}
mpRightMost = right;
}
/** Build SmCaretPosGraph for SmTextNode * * Lines in an SmTextNode: * \code * A B C * \endcode * Where A B and C are characters in the text. * * Graph over these, where "left" is before the SmTextNode and "right" is after: * \dot * digraph Graph{ * left -> A; * A -> B * B -> right; * }; * \enddot * Notice that C and right is the same position here.
*/ void SmCaretPosGraphBuildingVisitor::Visit( SmTextNode* pNode )
{
SAL_WARN_IF( pNode->GetText().isEmpty(), "starmath", "Empty SmTextNode is bad" );
OUString& aText = pNode->GetText();
sal_Int32 i = 0; while (i < aText.getLength())
{
aText.iterateCodePoints(&i);
SmCaretPosGraphEntry* pRight = mpRightMost;
mpRightMost = mpGraph->Add( SmCaretPos( pNode, i ), pRight );
pRight->SetRight( mpRightMost );
}
}
/** Build SmCaretPosGraph for SmBinVerNode * * Lines in an SmBinVerNode: * \code * A * ----- * B * \endcode * * Graph over these, where "left" is before the SmBinVerNode and "right" is after: * \dot * digraph Graph{ * left -> A; * A -> right; * B -> right; * }; * \enddot
*/ void SmCaretPosGraphBuildingVisitor::Visit( SmBinVerNode* pNode )
{ //None if these children can be NULL, see SmBinVerNode::Arrange
SmNode *pNum = pNode->GetSubNode( 0 ),
*pDenom = pNode->GetSubNode( 2 );
//Create right
right = mpGraph->Add( SmCaretPos( pNode, 1 ) );
//Create bodyLeft
bodyLeft = mpGraph->Add( SmCaretPos( pBody, 0 ), left );
left->SetRight( bodyLeft );
mpRightMost = bodyLeft;
pBody->Accept( this );
mpRightMost->SetRight( right );
right->SetLeft( mpRightMost );
//Create script
scriptLeft = mpGraph->Add( SmCaretPos( pScript, 0 ), left );
mpRightMost = scriptLeft;
pScript->Accept( this );
mpRightMost->SetRight( right );
//Set return value
mpRightMost = right;
}
/** Build SmCaretPosGraph for SmBinDiagonalNode * * Lines in an SmBinDiagonalNode: * \code * A / * / * / B * \endcode * Where A and B are lines. * * Used in formulas such as "A wideslash B"
*/ void SmCaretPosGraphBuildingVisitor::Visit( SmBinDiagonalNode* pNode )
{
SmNode *A = pNode->GetSubNode( 0 ),
*B = pNode->GetSubNode( 1 );
SmCaretPosGraphEntry *left,
*leftA,
*rightA,
*leftB,
*right;
left = mpRightMost;
//Create right
right = mpGraph->Add( SmCaretPos( pNode, 1 ) );
//Create left A
leftA = mpGraph->Add( SmCaretPos( A, 0 ), left );
left->SetRight( leftA );
//Visit A
mpRightMost = leftA;
A->Accept( this );
rightA = mpRightMost;
//Create left B
leftB = mpGraph->Add( SmCaretPos( B, 0 ), rightA );
rightA->SetRight( leftB );
//Visit B
mpRightMost = leftB;
B->Accept( this );
mpRightMost->SetRight( right );
right->SetLeft( mpRightMost );
//Set return value
mpRightMost = right;
}
//Straight forward ( I think ) void SmCaretPosGraphBuildingVisitor::Visit( SmBinHorNode* pNode )
{ for( auto pChild : *pNode )
{ if(!pChild) continue;
pChild->Accept( this );
}
} void SmCaretPosGraphBuildingVisitor::Visit( SmUnHorNode* pNode )
{ // Unary operator node for( auto pChild : *pNode )
{ if(!pChild) continue;
pChild->Accept( this );
}
}
void SmCaretPosGraphBuildingVisitor::Visit( SmExpressionNode* pNode )
{ for( auto pChild : *pNode )
{ if(!pChild) continue;
pChild->Accept( this );
}
}
void SmCaretPosGraphBuildingVisitor::Visit( SmFontNode* pNode )
{ //Has only got one child, should act as an expression if possible for( auto pChild : *pNode )
{ if(!pChild) continue;
pChild->Accept( this );
}
}
/** Build SmCaretPosGraph for SmAlignNode * Acts as an SmExpressionNode, as it only has one child this okay
*/ void SmCaretPosGraphBuildingVisitor::Visit( SmAlignNode* pNode )
{ for( auto pChild : *pNode )
{ if(!pChild) continue;
pChild->Accept( this );
}
}
/** Build SmCaretPosGraph for SmRootNode * * Lines in an SmRootNode: * \code * _________ * A/ * \/ B * * \endcode * A: pExtra ( optional, can be NULL ), * B: pBody * * Graph over these, where "left" is before the SmRootNode and "right" is after: * \dot * digraph Graph{ * left -> B; * B -> right; * A -> B; * } * \enddot
*/ void SmCaretPosGraphBuildingVisitor::Visit( SmRootNode* pNode )
{
SmNode *pExtra = pNode->GetSubNode( 0 ), //Argument, NULL for sqrt, and SmTextNode if cubicroot
*pBody = pNode->GetSubNode( 2 ); //Body of the root
assert(pBody);
/** Build SmCaretPosGraph for SmPlaceNode * Consider this a single character.
*/ void SmCaretPosGraphBuildingVisitor::Visit( SmPlaceNode* pNode )
{
SmCaretPosGraphEntry* right = mpGraph->Add( SmCaretPos( pNode, 1 ), mpRightMost );
mpRightMost->SetRight( right );
mpRightMost = right;
}
/** SmErrorNode is context dependent metadata, it can't be selected * * @remarks There's no point in deleting, copying and/or moving an instance * of SmErrorNode as it may not exist in another context! Thus there are no * positions to select an SmErrorNode.
*/ void SmCaretPosGraphBuildingVisitor::Visit( SmErrorNode* )
{
}
/** Build SmCaretPosGraph for SmBlankNode * Consider this a single character, as it is only a blank space
*/ void SmCaretPosGraphBuildingVisitor::Visit( SmBlankNode* pNode )
{
SmCaretPosGraphEntry* right = mpGraph->Add( SmCaretPos( pNode, 1 ), mpRightMost );
mpRightMost->SetRight( right );
mpRightMost = right;
}
/** Build SmCaretPosGraph for SmBraceNode * * Lines in an SmBraceNode: * \code * | | * | B | * | | * \endcode * B: Body * * Graph over these, where "left" is before the SmBraceNode and "right" is after: * \dot * digraph Graph{ * left -> B; * B -> right; * } * \enddot
*/ void SmCaretPosGraphBuildingVisitor::Visit( SmBraceNode* pNode )
{
SmNode* pBody = pNode->Body();
pBody->Accept( this );
mpRightMost->SetRight( right );
right->SetLeft( mpRightMost );
mpRightMost = right;
}
/** Build SmCaretPosGraph for SmAttributeNode * * Lines in an SmAttributeNode: * \code * Attr * Body * \endcode * * There's a body and an attribute, the construction is used for "widehat A", where "A" is the body * and "^" is the attribute ( note GetScaleMode( ) on SmAttributeNode tells how the attribute should be * scaled ).
*/ void SmCaretPosGraphBuildingVisitor::Visit( SmAttributeNode* pNode )
{
SmNode *pAttr = pNode->Attribute(),
*pBody = pNode->Body();
assert(pAttr);
assert(pBody);
void SmCloningVisitor::CloneNodeAttr( SmNode const * pSource, SmNode* pTarget )
{
pTarget->SetScaleMode( pSource->GetScaleMode( ) ); //Other attributes are set when prepare or arrange is executed //and may depend on stuff not being cloned here.
}
void SmCloningVisitor::CloneKids( SmStructureNode* pSource, SmStructureNode* pTarget )
{ //Cache current result
SmNode* pCurrResult = mpResult;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.