office-gobmx/starmath/inc/cursor.hxx
Khaled Hosny 3c24177104 tdf#88744: Use real clipboard when inline editing is enabled
Instead of using a fake clipboard that works only inside the same
formula.

Change-Id: Ia1e97028e1aafa15912bc9b4397d66afb0d23ec7
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/156671
Tested-by: Jenkins
Reviewed-by: خالد حسني <khaled@libreoffice.org>
2023-09-08 04:24:48 +02:00

428 lines
15 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/.
*/
#pragma once
#include "caret.hxx"
#include <list>
/** Factor to multiple the squared horizontal distance with
* Used for Up and Down movement.
*/
#define HORIZONTICAL_DISTANCE_FACTOR 10
/** Enum of direction for movement */
enum SmMovementDirection
{
MoveUp,
MoveDown,
MoveLeft,
MoveRight
};
/** Enum of elements that can inserted into a formula */
enum SmFormulaElement
{
BlankElement,
FactorialElement,
PlusElement,
MinusElement,
CDotElement,
EqualElement,
LessThanElement,
GreaterThanElement,
PercentElement
};
/** Bracket types that can be inserted */
enum class SmBracketType
{
/** Round brackets, left command "(" */
Round,
/**Square brackets, left command "[" */
Square,
/** Curly brackets, left command "lbrace" */
Curly,
};
/** A list of nodes */
typedef std::list<SmNode*> SmNodeList;
typedef std::list<std::unique_ptr<SmNode>> SmClipboard;
class SmDocShell;
/** Formula cursor
*
* This class is used to represent a cursor in a formula, which can be used to manipulate
* a formula programmatically.
* @remarks This class is a very intimate friend of SmDocShell.
*/
class SmCursor
{
public:
SmCursor(SmNode* tree, SmDocShell* pShell)
: mpAnchor(nullptr)
, mpPosition(nullptr)
, mpTree(tree)
, mpDocShell(pShell)
, mnEditSections(0)
, mbIsEnabledSetModifiedSmDocShell(false)
{
//Build graph
BuildGraph();
}
/** Get position */
const SmCaretPos& GetPosition() const { return mpPosition->CaretPos; }
/** True, if the cursor has a selection */
bool HasSelection() const { return mpAnchor != mpPosition; }
/** Move the position of this cursor */
void Move(OutputDevice* pDev, SmMovementDirection direction, bool bMoveAnchor = true);
/** Move to the caret position closest to a given point */
void MoveTo(OutputDevice* pDev, const Point& pos, bool bMoveAnchor);
/** Delete the current selection or do nothing */
void Delete();
/** Delete selection, previous element or merge lines
*
* This method implements the behaviour of backspace.
*/
void DeletePrev(OutputDevice* pDev);
/** Insert text at the current position */
void InsertText(const OUString& aString);
/** Insert an element into the formula */
void InsertElement(SmFormulaElement element);
/** Insert command text translated into line entries at position
*
* Note: This method uses the parser to translate a command text into a
* tree, then it copies line entries from this tree into the current tree.
* Will not work for commands such as newline or ##, if position is in a matrix.
* This will work for stuff like "A intersection B". But stuff spanning multiple lines
* or dependent on the context which position is placed in will not work!
*/
void InsertCommandText(const OUString& aCommandText);
/** Insert a special node created from aString
*
* Used for handling insert request from the "catalog" dialog.
* The provided string should be formatted as the desired command: %phi
* Note: this method ONLY supports commands defined in Math.xcu
*
* For more complex expressions use InsertCommandText, this method doesn't
* use SmParser, this means that it's faster, but not as strong.
*/
void InsertSpecial(std::u16string_view aString);
/** Create sub-/super script
*
* If there's a selection, it will be move into the appropriate sub-/super scription
* of the node in front of it. If there's no node in front of position (or the selection),
* a sub-/super scription of a new SmPlaceNode will be made.
*
* If there's is an existing subscription of the node, the caret will be moved into it,
* and any selection will replace it.
*/
void InsertSubSup(SmSubSup eSubSup);
/** Insert a new row or newline
*
* Inserts a new row if position is in a matrix or stack command.
* Otherwise a newline is inserted if we're in a toplevel line.
*
* @returns True, if a new row/line could be inserted.
*
* @remarks If the caret is placed in a subline of a command that doesn't support
* this operator the method returns FALSE, and doesn't do anything.
*/
bool InsertRow();
/** Insert a fraction, use selection as numerator */
void InsertFraction();
/** Create brackets around current selection, or new SmPlaceNode */
void InsertBrackets(SmBracketType eBracketType);
/** Copy the current selection */
void Copy(vcl::Window* pWindow = nullptr);
/** Cut the current selection */
void Cut(vcl::Window* pWindow = nullptr)
{
Copy(pWindow);
Delete();
}
/** Paste the clipboard */
void Paste(vcl::Window* pWindow = nullptr);
/** Returns true if more than one node is selected
*
* This method is used for implementing backspace and delete.
* If one of these causes a complex selection, e.g. a node with
* subnodes or similar, this should not be deleted immediately.
*/
bool HasComplexSelection();
/** Finds the topmost node in a visual line
*
* If MoveUpIfSelected is true, this will move up to the parent line
* if the parent of the current line is selected.
*/
static SmNode* FindTopMostNodeInLine(SmNode* pSNode, bool MoveUpIfSelected = false);
/** Draw the caret */
void Draw(OutputDevice& pDev, Point Offset, bool isCaretVisible);
tools::Rectangle GetCaretRectangle(OutputDevice& rOutDev) const;
tools::Rectangle GetSelectionRectangle(OutputDevice& rOutDev) const;
bool IsAtTailOfBracket(SmBracketType eBracketType) const;
private:
friend class SmDocShell;
SmCaretPosGraphEntry *mpAnchor, *mpPosition;
/** Formula tree */
SmNode* mpTree;
/** Owner of the formula tree */
SmDocShell* mpDocShell;
/** Graph over caret position in the current tree */
std::unique_ptr<SmCaretPosGraph> mpGraph;
/** Returns a node that is selected, if any could be found */
SmNode* FindSelectedNode(SmNode* pNode);
/** Is this one of the nodes used to compose a line
*
* These are SmExpression, SmBinHorNode, SmUnHorNode etc.
*/
static bool IsLineCompositionNode(SmNode const* pNode);
/** Count number of selected nodes, excluding line composition nodes
*
* Note this function doesn't count line composition nodes and it
* does count all subnodes as well as the owner nodes.
*
* Used by SmCursor::HasComplexSelection()
*/
int CountSelectedNodes(SmNode* pNode);
/** Convert a visual line to a list
*
* Note this method will delete all the nodes that will no longer be needed.
* that includes pLine!
* This method also deletes SmErrorNode's as they're just meta info in the line.
*/
static void LineToList(SmStructureNode* pLine, SmNodeList& rList);
/** Auxiliary function for calling LineToList on a node
*
* This method sets pNode = NULL and remove it from its parent.
* (Assuming it has a parent, and is a child of it).
*/
static void NodeToList(SmNode*& rpNode, SmNodeList& rList)
{
//Remove from parent and NULL rpNode
SmNode* pNode = rpNode;
if (rpNode && rpNode->GetParent())
{ //Don't remove this, correctness relies on it
int index = rpNode->GetParent()->IndexOfSubNode(rpNode);
assert(index >= 0);
rpNode->GetParent()->SetSubNode(index, nullptr);
}
rpNode = nullptr;
//Create line from node
if (pNode && IsLineCompositionNode(pNode))
{
LineToList(static_cast<SmStructureNode*>(pNode), rList);
return;
}
if (pNode)
rList.push_front(pNode);
}
/** Clone a visual line to a clipboard
*
* ... but the selected part only.
* Doesn't clone SmErrorNodes, which are ignored as they are context dependent metadata.
*/
static void CloneLineToClipboard(SmStructureNode* pLine, SmClipboard* pClipboard);
/** Build pGraph over caret positions */
void BuildGraph();
/** Insert new nodes in the tree after position */
void InsertNodes(std::unique_ptr<SmNodeList> pNewNodes);
/** tries to set position to a specific SmCaretPos
*
* @returns false on failure to find the position in pGraph.
*/
bool SetCaretPosition(SmCaretPos pos);
/** Set selected on nodes of the tree */
void AnnotateSelection() const;
/** Clone list of nodes in a clipboard (creates a deep clone) */
static std::unique_ptr<SmNodeList> CloneList(SmClipboard& rClipboard);
/** Find an iterator pointing to the node in pLineList following rCaretPos
*
* If rCaretPos.pSelectedNode cannot be found it is assumed that it's in front of pLineList,
* thus not an element in pLineList. In this case this method returns an iterator to the
* first element in pLineList.
*
* If the current position is inside an SmTextNode, this node will be split in two, for this
* reason you should beaware that iterators to elements in pLineList may be invalidated, and
* that you should call PatchLineList() with this iterator if no action is taken.
*/
static SmNodeList::iterator FindPositionInLineList(SmNodeList* pLineList,
const SmCaretPos& rCaretPos);
/** Patch a line list after modification, merge SmTextNode, remove SmPlaceNode etc.
*
* @param pLineList The line list to patch
* @param aIter Iterator pointing to the element that needs to be patched with its previous.
*
* When the list is patched text nodes before and after aIter will be merged.
* If there's an, in the context, inappropriate SmPlaceNode before or after aIter it will also be
* removed.
*
* @returns A caret position equivalent to one selecting the node before aIter, the method returns
* an invalid SmCaretPos to indicate placement in front of the line.
*/
static SmCaretPos PatchLineList(SmNodeList* pLineList, SmNodeList::iterator aIter);
/** Take selected nodes from a list
*
* Puts the selected nodes into pSelectedNodes, or if pSelectedNodes is NULL deletes
* the selected nodes.
* Note: If there's a selection inside an SmTextNode this node will be split, and it
* will not be merged when the selection have been taken. Use PatchLineList on the
* iterator returns to fix this.
*
* @returns An iterator pointing to the element following the selection taken.
*/
static SmNodeList::iterator TakeSelectedNodesFromList(SmNodeList* pLineList,
SmNodeList* pSelectedNodes = nullptr);
/** Create an instance of SmMathSymbolNode usable for brackets */
static SmNode* CreateBracket(SmBracketType eBracketType, bool bIsLeft);
/** The number of times BeginEdit have been called
* Used to allow nesting of BeginEdit() and EndEdit() sections
*/
int mnEditSections;
/** Holds data for BeginEdit() and EndEdit() */
bool mbIsEnabledSetModifiedSmDocShell;
/** Begin edit section where the tree will be modified */
void BeginEdit();
/** End edit section where the tree will be modified */
void EndEdit();
/** Finish editing
*
* Finishes editing by parsing pLineList and inserting back into pParent at nParentIndex.
* This method also rebuilds the graph, annotates the selection, sets caret position and
* Calls EndEdit.
*
* @remarks Please note that this method will delete pLineList, as the elements are taken.
*
* @param pLineList List the constitutes the edited line.
* @param pParent Parent to which the line should be inserted.
* @param nParentIndex Index in parent where the line should be inserted.
* @param PosAfterEdit Caret position to look for after rebuilding graph.
* @param pStartLine Line to take first position in, if PosAfterEdit cannot be found,
* leave it NULL for pLineList.
*/
void FinishEdit(std::unique_ptr<SmNodeList> pLineList, SmStructureNode* pParent,
int nParentIndex, SmCaretPos PosAfterEdit, SmNode* pStartLine = nullptr);
/** Request the formula is repainted */
void RequestRepaint();
};
/** Minimalistic recursive decent SmNodeList parser
*
* This parser is used to take a list of nodes that constitutes a line
* and parse them to a tree of SmBinHorNode, SmUnHorNode and SmExpression.
*
* Please note, this will not handle all kinds of nodes, only nodes that
* constitutes and entry in a line.
*
* Below is an EBNF representation of the grammar used for this parser:
* \code
* Expression -> Relation*
* Relation -> Sum [(=|<|>|...) Sum]*
* Sum -> Product [(+|-) Product]*
* Product -> Factor [(*|/) Factor]*
* Factor -> [+|-|-+|...]* Factor | Postfix
* Postfix -> node [!]*
* \endcode
*/
class SmNodeListParser
{
public:
/** Create an instance of SmNodeListParser */
SmNodeListParser() { pList = nullptr; }
/** Parse a list of nodes to an expression.
*
* Old error nodes will be deleted.
*/
SmNode* Parse(SmNodeList* list);
/** True, if the token is an operator */
static bool IsOperator(const SmToken& token);
/** True, if the token is a relation operator */
static bool IsRelationOperator(const SmToken& token);
/** True, if the token is a sum operator */
static bool IsSumOperator(const SmToken& token);
/** True, if the token is a product operator */
static bool IsProductOperator(const SmToken& token);
/** True, if the token is a unary operator */
static bool IsUnaryOperator(const SmToken& token);
/** True, if the token is a postfix operator */
static bool IsPostfixOperator(const SmToken& token);
private:
SmNodeList* pList;
/** Get the current terminal */
SmNode* Terminal()
{
if (!pList->empty())
return pList->front();
return nullptr;
}
/** Move to next terminal */
SmNode* Next()
{
pList->pop_front();
return Terminal();
}
/** Take the current terminal */
SmNode* Take()
{
SmNode* pRetVal = Terminal();
Next();
return pRetVal;
}
SmNode* Expression();
SmNode* Relation();
SmNode* Sum();
SmNode* Product();
SmNode* Factor();
SmNode* Postfix();
static SmNode* Error();
};
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */