diff options
author | Michael Stahl <mstahl@redhat.com> | 2012-01-28 21:26:09 +0100 |
---|---|---|
committer | Michael Stahl <mstahl@redhat.com> | 2012-01-28 21:26:09 +0100 |
commit | 7947d959db2a84860cdfd479d95ea5b30fdba051 (patch) | |
tree | 4cc2ec4d16ee75d338f85d966b089ca5a0240097 /patches/dev300/vEdit-13-August.diff | |
parent | 2786d0238b25d7bcc1408bb1a45a8f7f4bd1febd (diff) |
replace obsolete "master" branch with README that points at new repoHEADmaster-deletedmaster
Diffstat (limited to 'patches/dev300/vEdit-13-August.diff')
-rw-r--r-- | patches/dev300/vEdit-13-August.diff | 7896 |
1 files changed, 0 insertions, 7896 deletions
diff --git a/patches/dev300/vEdit-13-August.diff b/patches/dev300/vEdit-13-August.diff deleted file mode 100644 index 117a75742..000000000 --- a/patches/dev300/vEdit-13-August.diff +++ /dev/null @@ -1,7896 +0,0 @@ -diff --git starmath/inc/caret.hxx starmath/inc/caret.hxx -new file mode 100644 -index 0000000..b1ff954 ---- /dev/null -+++ starmath/inc/caret.hxx -@@ -0,0 +1,445 @@ -+#ifndef CARET_H -+#define CARET_H -+ -+#include "node.hxx" -+ -+/** Representation of caret position with an equantion */ -+struct SmCaretPos{ -+ SmCaretPos(SmNode* selectedNode = NULL, int iIndex = 0) { -+ pSelectedNode = selectedNode; -+ Index = iIndex; -+ } -+ /** Selected node */ -+ SmNode* pSelectedNode; -+ /** Index within the selected node -+ * -+ * 0: Position infront of a node -+ * 1: Position after a node or after first char in SmTextNode -+ * n: Position after n char in SmTextNode -+ * -+ * Notice how there's special cases for SmTextNode. -+ */ -+ //TODO: Special cases for SmBlankNode is needed -+ //TODO: Consider forgetting about the todo above... As it's really unpleasent. -+ int Index; -+ /** True, if this is a valid caret position */ -+ bool IsValid() { return pSelectedNode != NULL; } -+ bool operator!=(SmCaretPos pos) const { -+ return pos.pSelectedNode != pSelectedNode || Index != pos.Index; -+ } -+ bool operator==(SmCaretPos pos) const { -+ return pos.pSelectedNode == pSelectedNode && Index == pos.Index; -+ } -+ /** Get the caret position after pNode, regardless of pNode -+ * -+ * Gets the caret position following pNode, this is SmCaretPos(pNode, 1). -+ * Unless pNode is an instance of SmTextNode, then the index is the text length. -+ */ -+ static SmCaretPos GetPosAfter(SmNode* pNode) { -+ if(pNode && pNode->GetType() == NTEXT) -+ return SmCaretPos(pNode, ((SmTextNode*)pNode)->GetText().Len()); -+ return SmCaretPos(pNode, 1); -+ } -+}; -+ -+/** A line that represents a caret */ -+class SmCaretLine{ -+public: -+ SmCaretLine(long left = 0, long top = 0, long height = 0) { -+ _top = top; -+ _left = left; -+ _height = height; -+ } -+ long GetTop() const {return _top;} -+ long GetLeft() const {return _left;} -+ long GetHeight() const {return _height;} -+ long SquaredDistanceX(SmCaretLine line) const{ -+ return (GetLeft() - line.GetLeft()) * (GetLeft() - line.GetLeft()); -+ } -+ long SquaredDistanceX(Point pos) const{ -+ return (GetLeft() - pos.X()) * (GetLeft() - pos.X()); -+ } -+ long SquaredDistanceY(SmCaretLine line) const{ -+ long d = GetTop() - line.GetTop(); -+ if(d < 0) -+ d = (d * -1) - GetHeight(); -+ else -+ d = d - line.GetHeight(); -+ if(d < 0) -+ return 0; -+ return d * d; -+ } -+ long SquaredDistanceY(Point pos) const{ -+ long d = GetTop() - pos.Y(); -+ if(d < 0) -+ d = (d * -1) - GetHeight(); -+ if(d < 0) -+ return 0; -+ return d * d; -+ } -+private: -+ long _top; -+ long _left; -+ long _height; -+}; -+ -+/////////////////////////////// SmCaretPosGraph//////////////////////////////// -+ -+/** An entry in SmCaretPosGraph */ -+struct SmCaretPosGraphEntry{ -+ SmCaretPosGraphEntry(SmCaretPos pos = SmCaretPos(), -+ SmCaretPosGraphEntry* left = NULL, -+ SmCaretPosGraphEntry* right = NULL){ -+ CaretPos = pos; -+ Left = left; -+ Right = right; -+ } -+ /** Caret position */ -+ SmCaretPos CaretPos; -+ /** Entry to the left visually */ -+ SmCaretPosGraphEntry* Left; -+ /** Entry to the right visually */ -+ SmCaretPosGraphEntry* Right; -+ void SetRight(SmCaretPosGraphEntry* right){ -+ Right = right; -+ } -+ void SetLeft(SmCaretPosGraphEntry* left){ -+ Left = left; -+ } -+}; -+ -+/** Define SmCaretPosGraph to be less than one page 4096 */ -+#define SmCaretPosGraphSize 255 -+ -+class SmCaretPosGraph; -+ -+/** Iterator for SmCaretPosGraph */ -+class SmCaretPosGraphIterator{ -+public: -+ SmCaretPosGraphIterator(SmCaretPosGraph* graph){ -+ pGraph = graph; -+ nOffset = 0; -+ pEntry = NULL; -+ } -+ /** Get the next entry, NULL if none */ -+ SmCaretPosGraphEntry* Next(); -+ /** Get the current entry, NULL if none */ -+ SmCaretPosGraphEntry* Current(){ -+ return pEntry; -+ } -+ /** Get the current entry, NULL if none */ -+ SmCaretPosGraphEntry* operator->(){ -+ return pEntry; -+ } -+private: -+ /** Next entry to return */ -+ int nOffset; -+ /** Current graph */ -+ SmCaretPosGraph* pGraph; -+ /** Current entry */ -+ SmCaretPosGraphEntry* pEntry; -+}; -+ -+ -+/** A graph over all caret positions -+ * @remarks Graphs can only grow, entries cannot be removed! -+ */ -+class SmCaretPosGraph{ -+public: -+ SmCaretPosGraph(){ -+ pNext = NULL; -+ nOffset = 0; -+ } -+ ~SmCaretPosGraph(); -+ SmCaretPosGraphEntry* Add(SmCaretPosGraphEntry entry); -+ SmCaretPosGraphEntry* Add(SmCaretPos pos, -+ SmCaretPosGraphEntry* left = NULL, -+ SmCaretPosGraphEntry* right = NULL){ -+ j_assert(pos.Index >= 0, "Index shouldn't be -1!"); -+ return Add(SmCaretPosGraphEntry(pos, left, right)); -+ } -+ /** Get an iterator for this graph */ -+ SmCaretPosGraphIterator GetIterator(){ -+ return SmCaretPosGraphIterator(this); -+ } -+ friend class SmCaretPosGraphIterator; -+private: -+ /** Next graph, to be used when this graph is full */ -+ SmCaretPosGraph* pNext; -+ /** Next free entry in graph */ -+ int nOffset; -+ /** Entries in this graph segment */ -+ SmCaretPosGraphEntry Graph[SmCaretPosGraphSize]; -+}; -+ -+/** \page visual_formula_editing Visual Formula Editing -+ * A visual formula editor allows users to easily edit formulas without having to learn and -+ * use complicated commands. A visual formula editor is a WYSIWYG editor. For OpenOffice Math -+ * this essentially means that you can click on the formula image, to get a caret, which you -+ * can move with arrow keys, and use to modify the formula by entering text, clicking buttons -+ * or using shortcuts. -+ * -+ * \subsection formula_trees Formula Trees -+ * A formula in OpenOffice Math is a tree of nodes, take for instance the formula -+ * "A + {B cdot C} over D", it looks like this -+ * \f$ \mbox{A} + \frac{\mbox{B} \cdot \mbox{C}}{\mbox{D}} \f$. The tree for this formula -+ * looks like this: -+ * -+ * \dot -+ * digraph { -+ * labelloc = "t"; -+ * label= "Equation: \"A + {B cdot C} over D\""; -+ * size = "9,9"; -+ * n0 [label="SmTableNode (1)"]; -+ * n0 -> n1 [label="0"]; -+ * n1 [label="SmLineNode (2)"]; -+ * n1 -> n2 [label="0"]; -+ * n2 [label="SmExpressionNode (3)"]; -+ * n2 -> n3 [label="0"]; -+ * n3 [label="SmBinHorNode (4)"]; -+ * n3 -> n4 [label="0"]; -+ * n4 [label="SmTextNode: A (5)"]; -+ * n3 -> n5 [label="1"]; -+ * n5 [label="SmMathSymbolNode: (6)"]; -+ * n3 -> n6 [label="2"]; -+ * n6 [label="SmBinVerNode (7)"]; -+ * n6 -> n7 [label="0"]; -+ * n7 [label="SmExpressionNode (8)"]; -+ * n7 -> n8 [label="0"]; -+ * n8 [label="SmBinHorNode (9)"]; -+ * n8 -> n9 [label="0"]; -+ * n9 [label="SmTextNode: B (10)"]; -+ * n8 -> n10 [label="1"]; -+ * n10 [label="SmMathSymbolNode: ⋅ (11)"]; -+ * n8 -> n11 [label="2"]; -+ * n11 [label="SmTextNode: C (12)"]; -+ * n6 -> n12 [label="1"]; -+ * n12 [label="SmRectangleNode (13)"]; -+ * n6 -> n13 [label="2"]; -+ * n13 [label="SmTextNode: D (14)"]; -+ * } -+ * \enddot -+ * -+ * The vertices are nodes, their label says what kind of node and the number in parentheses is -+ * the identifier of the node (In practices a pointer is used instead of the id). The direction -+ * of the edges tells which node is parent and which is child. The label of the edges are the -+ * child node index number, given to SmNode::GetSubNode() of the parent to get the child node. -+ * -+ * -+ * \subsection visual_lines Visual Lines -+ * -+ * Inorder to do caret movement in visual lines, we need a definition of caret position and -+ * visual line. In a tree such as the above there are three visual lines. There's the outer most -+ * line, with entries such as -+ * \f$\mbox{A}\f$, \f$ + \f$ and \f$ \frac{\mbox{B} \cdot \mbox{C}}{\mbox{D}} \f$. Then there's -+ * the numerator line of the fraction it has entries \f$ \mbox{B} \f$, \f$ \cdot \f$ and \f$ \mbox{C} \f$. -+ * And last by not least there's the denominator line of the fraction it's only entry is \f$ \mbox{D} \f$. -+ * -+ * For visual editing it should be possible to place a caret on both sides of any line entry, -+ * consider a line entry a character or construction that in a line is treated as a character. -+ * Imagine the caret is placed to the right of the plus sign (id: 6), now if user presses -+ * backspace this should delete the plus sign (id: 6), and if the user presses delete this -+ * should delete the entire fraction (id: 7). This is because the caret is in the outer most -+ * line where the fraction is considered a line entry. -+ * -+ * However, inorder to prevent users from accidentally deleting large subtrees, just because -+ * they logically placed there caret a in the wrong line, require that complex constructions -+ * such as a fraction is selected before it is deleted. Thus in this case it wouldn't be -+ * deleted, but only selected and then deleted if the user hit delete again. Anyway, this is -+ * slightly off topic for now. -+ * -+ * Important about visual lines is that they don't always have an SmExpressionNode as root -+ * and the entries in a visual line is all the nodes of a subtree ordered left to right that -+ * isn't either an SmExpressionNode, SmBinHorNode or SmUnHorNode. -+ * -+ * -+ * \subsection caret_positions Caret Positions -+ * -+ * A caret position in OpenOffice Math is representated by an instance of SmCaretPos. -+ * That is a caret position is a node and an index related to this node. For most nodes the -+ * index 0, means caret is infront of this node, the index 1 means caret is after this node. -+ * For SmTextNode the index is the caret position after the specified number of characters, -+ * imagine an SmTextNode with the number 1337. The index 3 in such SmTextNode would mean a -+ * caret placed right before 7, e.g. "133|7". -+ * -+ * For SmExpressionNode, SmBinHorNode and SmUnHorNode the only legal index is 0, which means -+ * infront of the node. Actually the index 0 may only because for the first caret position -+ * in a visual line. From the example above, consider the following subtree that constitutes -+ * a visual line: -+ * -+ * \dot -+ * digraph { -+ * labelloc = "t"; -+ * label= "Subtree that constitutes a visual line"; -+ * size = "7,5"; -+ * n7 [label="SmExpressionNode (8)"]; -+ * n7 -> n8 [label="0"]; -+ * n8 [label="SmBinHorNode (9)"]; -+ * n8 -> n9 [label="0"]; -+ * n9 [label="SmTextNode: B (10)"]; -+ * n8 -> n10 [label="1"]; -+ * n10 [label="SmMathSymbolNode: ⋅ (11)"]; -+ * n8 -> n11 [label="2"]; -+ * n11 [label="SmTextNode: C (12)"]; -+ * } -+ * \enddot -+ * Here the caret positions are: -+ * -+ * <TABLE> -+ * <TR><TD><B>Caret position:</B></TD><TD><B>Example:</B></TD> -+ * </TR><TR> -+ * <TD>{id: 8, index: 0}</TD> -+ * <TD>\f$ \mid \mbox{C} \cdot \mbox{C} \f$</TD> -+ * </TR><TR> -+ * <TD>{id: 10, index: 1}</TD> -+ * <TD>\f$ \mbox{C} \mid \cdot \mbox{C} \f$</TD> -+ * </TR><TR> -+ * <TD>{id: 11, index: 1}</TD> -+ * <TD>\f$ \mbox{C} \cdot \mid \mbox{C} \f$</TD> -+ * </TR><TR> -+ * <TD>{id: 12, index: 1}</TD> -+ * <TD>\f$ \mbox{C} \cdot \mbox{C} \mid \f$</TD> -+ * </TR><TR> -+ * </TABLE> -+ * -+ * Where \f$ \mid \f$ is used to denote caret position. -+ * -+ * With these exceptions included in the definition the id and index: {id: 11, index: 0} does -+ * \b not constitute a caret position in the given context. Note the method -+ * SmCaretPos::IsValid() does not check if this invariant holds true, but code in SmCaret, -+ * SmSetSelectionVisitor and other places depends on this invariant to hold. -+ * -+ * -+ * \subsection caret_movement Caret Movement -+ * -+ * As the placement of caret positions depends very much on the context within which a node -+ * appears it is not trivial to find all caret positions and determine which follows which. -+ * In OpenOffice Math this is done by the SmCaretPosGraphBuildingVisitor. This visitor builds -+ * graph (an instnce of SmCaretPosGraph) over the caret positions. For details on how this -+ * graph is build, and how new methods should be implemented see SmCaretPosGraphBuildingVisitor. -+ * -+ * The result of the SmCaretPosGraphBuildingVisitor is a graph over the caret positions in a -+ * formula, representated by an instance of SmCaretPosGraph. Each entry (instances of SmCaretPosGraphEntry) -+ * has a pointer to the entry to the left and right of itself. This way we can easily find -+ * the caret position to a right or left of a given caret position. Note each caret position -+ * only appears once in this graph. -+ * -+ * When searching for a caret position after a left click on the formula this map is also used. -+ * We simply iterate over all entries, uses the SmCaretPos2LineVisitor to find a line for each -+ * caret position. Then the distance from the click to the line is computed and we choose the -+ * caret position closest to the click. -+ * -+ * For up and down movement, we also iterator over all caret positions and use SmCaretPos2LineVisitor -+ * to find a line for each caret position. Then we compute the distance from the current -+ * caret position to every other caret position and chooses the one closest that is either -+ * above or below the current caret position, depending on wether we're doing up or down movement. -+ * -+ * This result of this approach to caret movement is that we have logically predictable -+ * movement for left and right, whilst leftclick, up and down movement depends on the sizes -+ * and placement of all node and may be less logically predictable. This solution also means -+ * that we only have one complex visitor generating the graph, imagine the nightmare if we -+ * had a visitor for movement in each direction. -+ * -+ * Making up and down movement independent of node sizes and placement wouldn't necessarily -+ * be a good thing either. Consider the formula \f$ \frac{1+2+3+4+5}{6} \f$, if the caret is -+ * placed as displayed here: \f$ \frac{1+2+3+4+5}{6 \mid} \f$, up movement should move to right -+ * after "3": \f$ \frac{1+2+3|+4+5}{6} \f$. However, such a move depends on the sizes and placement -+ * of all nodes in the fraction. -+ * -+ * -+ * \subsubsection caretpos_graph_example Example of Caret Position Graph -+ * -+ * If we consider the formula -+ * \f$ \mbox{A} + \frac{\mbox{B} \cdot \mbox{C}}{\mbox{D}} \f$ from \ref formula_trees. -+ * It has the following caret positions: -+ * -+ * <TABLE> -+ * <TR> -+ * <TD><B>Caret position:</B></TD> -+ * <TD><B>Example:</B></TD> -+ * </TR><TR> -+ * <TD>{id: 3, index: 0}</TD> -+ * <TD>\f$ \mid\mbox{A} + \frac{\mbox{B} \cdot \mbox{C}}{\mbox{D}} \f$</TD> -+ * </TR><TR> -+ * <TD>{id: 5, index: 1}</TD> -+ * <TD>\f$ \mbox{A}\mid + \frac{\mbox{B} \cdot \mbox{C}}{\mbox{D}} \f$</TD> -+ * </TR><TR> -+ * <TD>{id: 6, index: 1}</TD> -+ * <TD>\f$ \mbox{A} + \mid \frac{\mbox{B} \cdot \mbox{C}}{\mbox{D}} \f$</TD> -+ * </TR><TR> -+ * <TD>{id: 8, index: 0}</TD> -+ * <TD>\f$ \mbox{A} + \frac{ \mid \mbox{B} \cdot \mbox{C}}{\mbox{D}} \f$</TD> -+ * </TR><TR> -+ * <TD>{id: 10, index: 1}</TD> -+ * <TD>\f$ \mbox{A} + \frac{\mbox{B} \mid \cdot \mbox{C}}{\mbox{D}} \f$</TD> -+ * </TR><TR> -+ * <TD>{id: 11, index: 1}</TD> -+ * <TD>\f$ \mbox{A} + \frac{\mbox{B} \cdot \mid \mbox{C}}{\mbox{D}} \f$</TD> -+ * </TR><TR> -+ * <TD>{id: 12, index: 1}</TD> -+ * <TD>\f$ \mbox{A} + \frac{\mbox{B} \cdot \mbox{C} \mid}{\mbox{D}} \f$</TD> -+ * </TR><TR> -+ * <TD>{id: 14, index: 0}</TD> -+ * <TD>\f$ \mbox{A} + \frac{\mbox{B} \cdot \mbox{C}}{\mid \mbox{D}} \f$</TD> -+ * </TR><TR> -+ * <TD>{id: 14, index: 1}</TD> -+ * <TD>\f$ \mbox{A} + \frac{\mbox{B} \cdot \mbox{C}}{\mbox{D} \mid} \f$</TD> -+ * </TR><TR> -+ * <TD>{id: 7, index: 1}</TD> -+ * <TD>\f$ \mbox{A} + \frac{\mbox{B} \cdot \mbox{C}}{\mbox{D}} \mid \f$</TD> -+ * </TR> -+ * </TABLE> -+ * -+ * Below is a directed graph over the caret postions and how you can move between them. -+ * \dot -+ * digraph { -+ * labelloc = "t"; -+ * label= "Caret Position Graph"; -+ * size = "4,6"; -+ * p0 [label = "{id: 3, index: 0}"]; -+ * p0 -> p1 [fontsize = 10.0, label = "right"]; -+ * p1 [label = "{id: 5, index: 1}"]; -+ * p1 -> p0 [fontsize = 10.0, label = "left"]; -+ * p1 -> p2 [fontsize = 10.0, label = "right"]; -+ * p2 [label = "{id: 6, index: 1}"]; -+ * p2 -> p1 [fontsize = 10.0, label = "left"]; -+ * p2 -> p3 [fontsize = 10.0, label = "right"]; -+ * p3 [label = "{id: 8, index: 0}"]; -+ * p3 -> p2 [fontsize = 10.0, label = "left"]; -+ * p3 -> p4 [fontsize = 10.0, label = "right"]; -+ * p4 [label = "{id: 10, index: 1}"]; -+ * p4 -> p3 [fontsize = 10.0, label = "left"]; -+ * p4 -> p5 [fontsize = 10.0, label = "right"]; -+ * p5 [label = "{id: 11, index: 1}"]; -+ * p5 -> p4 [fontsize = 10.0, label = "left"]; -+ * p5 -> p6 [fontsize = 10.0, label = "right"]; -+ * p6 [label = "{id: 12, index: 1}"]; -+ * p6 -> p5 [fontsize = 10.0, label = "left"]; -+ * p6 -> p9 [fontsize = 10.0, label = "right"]; -+ * p7 [label = "{id: 14, index: 0}"]; -+ * p7 -> p2 [fontsize = 10.0, label = "left"]; -+ * p7 -> p8 [fontsize = 10.0, label = "right"]; -+ * p8 [label = "{id: 14, index: 1}"]; -+ * p8 -> p7 [fontsize = 10.0, label = "left"]; -+ * p8 -> p9 [fontsize = 10.0, label = "right"]; -+ * p9 [label = "{id: 7, index: 1}"]; -+ * p9 -> p6 [fontsize = 10.0, label = "left"]; -+ * } -+ * \enddot -+ */ -+ -+/* TODO: Write documentation about the following keywords: -+ * -+ * Visual Selections: -+ * - Show images -+ * - Talk about how the visitor does this -+ * -+ * Modifying a Visual Line: -+ * - Find top most non-compo of the line (e.g. The subtree that constitutes a line) -+ * - Make the line into a list -+ * - Edit the list, add/remove/modify nodes -+ * - Parse the list back into a subtree -+ * - Insert the new subtree where the old was taken -+ */ -+ -+#endif /* CARET_H */ -diff --git starmath/inc/cursor.hxx starmath/inc/cursor.hxx -new file mode 100644 -index 0000000..ff865a8 ---- /dev/null -+++ starmath/inc/cursor.hxx -@@ -0,0 +1,404 @@ -+#ifndef SMCURSOR_H -+#define SMCURSOR_H -+ -+#include "node.hxx" -+#include "caret.hxx" -+ -+/** Factor to multiple the squared horizontical 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 -+}; -+ -+/** Bracket types that can be inserted */ -+enum SmBracketType { -+ /** None brackets, left command "none" */ -+ NoneBrackets, -+ /** Round brackets, left command "(" */ -+ RoundBrackets, -+ /**Square brackets, left command "[" */ -+ SquareBrackets, -+ /** Double square brackets, left command "ldbracket" */ -+ DoubleSquareBrackets, -+ /** Line brackets, left command "lline" */ -+ LineBrackets, -+ /** Double line brackets, left command "ldline" */ -+ DoubleLineBrackets, -+ /** Curly brackets, left command "lbrace" */ -+ CurlyBrackets, -+ /** Angle brackets, left command "langle" */ -+ AngleBrackets, -+ /** Ceiling brackets, left command "lceil" */ -+ CeilBrackets, -+ /** Floor brackets, left command "lfloor" */ -+ FloorBrackets -+}; -+ -+/** A list of nodes */ -+typedef std::list<SmNode*> SmNodeList; -+ -+class SmDocShell; -+ -+/** Formula cursor -+ * -+ * This class is used to represent a cursor in a formula, which can be used to manipulate -+ * an formula programmatically. -+ * @remarks This class is a very intimite friend of SmDocShell. -+ */ -+class SmCursor{ -+public: -+ SmCursor(SmNode* tree, SmDocShell* pShell){ -+ //Initialize members -+ pTree = tree; -+ anchor = NULL; -+ position = NULL; -+ pGraph = NULL; -+ pDocShell = pShell; -+ pClipboard = NULL; -+ nEditSections = 0; -+ //Build graph -+ BuildGraph(); -+ } -+ -+ ~SmCursor(){ -+ SetClipboard(); -+ if(pGraph) -+ delete pGraph; -+ pGraph = NULL; -+ } -+ -+ /** Gets the anchor */ -+ SmCaretPos GetAnchor(){ return anchor->CaretPos; } -+ -+ /** Get position */ -+ SmCaretPos GetPosition() { return position->CaretPos; } -+ -+ /** True, if the cursor has a selection */ -+ bool HasSelection() { return anchor != position; } -+ -+ /** Move the position of this cursor */ -+ void Move(OutputDevice* pDev, SmMovementDirection direction, bool bMoveAnchor = true); -+ -+ /** Move to the caret position closet to a given point */ -+ void MoveTo(OutputDevice* pDev, Point pos, bool bMoveAnchor = true); -+ -+ /** Delete the current selection or do nothing */ -+ void Delete(); -+ -+ /** Insert text at the current position */ -+ void InsertText(XubString aString); -+ -+ /** Insert an element into the formula */ -+ void InsertElement(SmFormulaElement element); -+ -+ /** Insert a command specified in commands.src*/ -+ void InsertCommand(USHORT nCommand); -+ -+ /** 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 spaning multiple lines -+ * or dependent on the context which position is placed in will not work! -+ */ -+ void InsertCommandText(String aCommandText); -+ -+ /** Insert a special node created from aString -+ * -+ * Used for handling insert request from the "catalog" dialog. -+ * The provided string should be formatet 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(XubString aString); -+ -+ /** Create sub-/super script -+ * -+ * If there's a selection, it will be move into the appropriate sub-/super scription -+ * of the node infront of it. If there's no node infront 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); -+ -+ /** Create a limit on an SmOperNode -+ * -+ * This this method only work if the caret is inside an SmOperNode, or to the right of one. -+ * Notice also that this method ignores any selection made. -+ * -+ * @param bMoveCaret If true that caret will be moved into the limit. -+ * -+ * @returns True, if the caret was in a context where this operation was possible. -+ */ -+ BOOL InsertLimit(SmSubSup eSubSup, BOOL bMoveCaret = TRUE); -+ -+ /** Insert a new row or newline -+ * -+ * Inserts a new row if position is in an 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(); -+ /** Cut the current selection */ -+ void Cut(){ -+ Copy(); -+ Delete(); -+ } -+ /** Paste the clipboard */ -+ void Paste(); -+ -+ /** 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 imidiately. -+ */ -+ 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); -+ -+private: -+ friend class SmDocShell; -+ -+ SmCaretPosGraphEntry *anchor, -+ *position; -+ /** Formula tree */ -+ SmNode* pTree; -+ /** Owner of the formula tree */ -+ SmDocShell* pDocShell; -+ /** Graph over caret position in the current tree */ -+ SmCaretPosGraph* pGraph; -+ /** Clipboard holder */ -+ SmNodeList* pClipboard; -+ -+ /** 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* 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 SmNodeList* LineToList(SmStructureNode* pLine, SmNodeList* pList = new SmNodeList()); -+ -+ /** Clone a visual line to a list -+ * -+ * Doesn't clone SmErrorNode's these are ignored, as they are context dependent metadata. -+ */ -+ static SmNodeList* CloneLineToList(SmStructureNode* pLine, -+ bool bOnlyIfSelected = false, -+ SmNodeList* pList = new SmNodeList()); -+ -+ /** Build pGraph over caret positions */ -+ void BuildGraph(); -+ -+ /** Insert new nodes in the tree after position */ -+ void InsertNodes(SmNodeList* pNewNodes); -+ -+ /** tries to set position to a specific SmCaretPos -+ * -+ * @returns false on failure to find the position in pGraph. -+ */ -+ bool SetCaretPosition(SmCaretPos pos, bool moveAnchor = false); -+ -+ /** Set selected on nodes of the tree */ -+ void AnnotateSelection(); -+ -+ /** Set the clipboard, and release current clipboard -+ * -+ * Call this method with NULL to reset the clipboard -+ * @remarks: This method takes ownership of pList. -+ */ -+ void SetClipboard(SmNodeList* pList = NULL); -+ -+ /** Clone list of nodes (creates a deep clone) */ -+ static SmNodeList* CloneList(SmNodeList* pList); -+ -+ /** Find an iterator pointing to the node in pLineList following aCaretPos -+ * -+ * If aCaretPos::pSelectedNode cannot be found it is assumed that it's infront 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, SmCaretPos aCaretPos); -+ -+ /** 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 it's 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 infront 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 = NULL); -+ -+ /** 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 nEditSections; -+ /** Holds data for BeginEdit() and EndEdit() */ -+ BOOL bIsEnabledSetModifiedSmDocShell; -+ /** Begin section where the tree will be modified */ -+ void BeginEdit(); -+ /** End section where the tree will be modified */ -+ void EndEdit(); -+ /** Request the formula is repainted */ -+ void RequestRepaint(); -+}; -+ -+/** Minimalistic recursive decent SmNodeList parser -+ * -+ * This parser is used to take a list of nodes that constitues 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 -+ * constitues 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 = NULL; -+ } -+ /** Parse a list of nodes to an expression -+ * -+ * If bDeleteErrorNodes is true, old error nodes will be deleted. -+ */ -+ SmNode* Parse(SmNodeList* list, bool bDeleteErrorNodes = true); -+ /** 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->size() > 0) -+ return pList->front(); -+ return NULL; -+ } -+ /** 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(); -+ SmNode* Error(); -+}; -+ -+ -+#endif /* SMCURSOR_H */ -diff --git starmath/inc/document.hxx starmath/inc/document.hxx -index 364eff2..1e7cd62 100644 ---- starmath/inc/document.hxx -+++ starmath/inc/document.hxx -@@ -47,6 +47,7 @@ class SmNode; - class SfxMenuBarManager; - class SfxPrinter; - class Printer; -+class SmCursor; - - #define HINT_DATACHANGED 1004 - -@@ -105,6 +106,7 @@ class SmDocShell : public SfxObjectShell, public SfxListener - { - friend class SmPrinterAccess; - friend class SmModel; -+ friend class SmCursor; - - String aText; - SmFormat aFormat; -@@ -122,6 +124,7 @@ class SmDocShell : public SfxObjectShell, public SfxListener - nBottomBorder; - USHORT nModifyCount; - BOOL bIsFormulaArranged; -+ SmCursor *pCursor; - - - -@@ -157,10 +160,15 @@ class SmDocShell : public SfxObjectShell, public SfxListener - OutputDevice* GetRefDev(); - - BOOL IsFormulaArranged() const { return bIsFormulaArranged; } -- void SetFormulaArranged(BOOL bVal) { bIsFormulaArranged = bVal; } -+ void SetFormulaArranged(BOOL bVal) { bIsFormulaArranged = bVal; } - - virtual BOOL ConvertFrom(SfxMedium &rMedium); - -+ /** Called whenever the formula is changed -+ * Deletes the current cursor -+ */ -+ void InvalidateCursor(); -+ - public: - TYPEINFO(); - SFX_DECL_INTERFACE(SFX_INTERFACE_SMA_START+1) -@@ -204,7 +212,7 @@ public: - EditEngine & GetEditEngine(); - SfxItemPool & GetEditEngineItemPool(); - -- void Draw(OutputDevice &rDev, Point &rPosition); -+ void DrawFormula(OutputDevice &rDev, Point &rPosition, BOOL bDrawSelection = FALSE); - Size GetSize(); - - void Repaint(); -@@ -218,6 +226,15 @@ public: - - virtual void SetVisArea (const Rectangle & rVisArea); - virtual void SetModified(BOOL bModified); -+ -+ /** Get a cursor for modifying this document -+ * @remarks Don't store this reference, a new cursor may be made... -+ */ -+ SmCursor& GetCursor(); -+ /** True, if cursor have previously been requested and thus -+ * has some sort of position. -+ */ -+ BOOL HasCursor() { return pCursor != NULL; } - }; - - -diff --git starmath/inc/edit.hxx starmath/inc/edit.hxx -index 595564f..56bafab 100644 ---- starmath/inc/edit.hxx -+++ starmath/inc/edit.hxx -@@ -68,15 +68,13 @@ class SmEditWindow : public Window, public DropTargetHelper - ScrollBar *pHScrollBar, - *pVScrollBar; - ScrollBarBox *pScrollBox; -- Timer aModifyTimer, -- aCursorMoveTimer; -+ Timer aModifyTimer; - ESelection aOldSelection; - - virtual void KeyInput(const KeyEvent& rKEvt); - virtual void Command(const CommandEvent& rCEvt); - DECL_LINK(MenuSelectHdl, Menu *); - DECL_LINK(ModifyTimerHdl, Timer *); -- DECL_LINK(CursorMoveTimerHdl, Timer *); - - virtual void DataChanged( const DataChangedEvent& ); - virtual void Resize(); -diff --git starmath/inc/node.hxx starmath/inc/node.hxx -index dabb503..7a19b6a 100644 ---- starmath/inc/node.hxx -+++ starmath/inc/node.hxx -@@ -31,6 +31,24 @@ - - - #include <vector> -+#include <fstream> -+#include <iostream> -+#include <stdio.h> -+ -+//My special assert macro -+//TODO: replace this with DBG_ASSERT when this patch moves to production, can be done using search/replace -+#define j_assert(cond, msg) do{ \ -+ if(!(cond)) \ -+ { \ -+ std::cerr<<"Failed assertion: "<<msg<<", at line "; \ -+ char* f = (char*)__FILE__; \ -+ f += strlen(f); \ -+ do f--; while(*f != '/'); \ -+ do f--; while(*f != '/'); \ -+ do f--; while(*f != '/'); \ -+ fprintf(stderr, "%d in %s\n", __LINE__, f + 1); \ -+ } \ -+ } while(false) - - #include "parse.hxx" - #include "types.hxx" -@@ -41,6 +59,7 @@ - #define ATTR_BOLD 0x0001 - #define ATTR_ITALIC 0x0002 - -+ - #define FNTSIZ_ABSOLUT 1 - #define FNTSIZ_PLUS 2 - #define FNTSIZ_MINUS 3 -@@ -59,6 +78,7 @@ - - extern SmFormat *pActiveFormat; - -+class SmVisitor; - class SmDocShell; - class SmNode; - class SmStructureNode; -@@ -97,6 +117,8 @@ class SmNode : public SmRect - nAttributes; - BOOL bIsPhantom, - bIsDebug; -+ -+ BOOL bIsSelected; - protected: - SmNode(SmNodeType eNodeType, const SmToken &rNodeToken); - -@@ -160,7 +182,6 @@ public: - #ifdef SM_RECT_DEBUG - using SmRect::Draw; - #endif -- virtual void Draw(OutputDevice &rDev, const Point &rPosition) const; - - virtual void GetAccessibleText( String &rText ) const; - sal_Int32 GetAccessibleIndex() const { return nAccIndex; } -@@ -180,12 +201,126 @@ public: - - const SmNode * FindTokenAt(USHORT nRow, USHORT nCol) const; - const SmNode * FindRectClosestTo(const Point &rPoint) const; --}; -+ -+ /** Accept a visitor -+ * Calls the method for this class on the visitor -+ */ -+ virtual void Accept(SmVisitor* pVisitor); -+ -+ /** True if this node is selected */ -+ BOOL IsSelected() const {return bIsSelected;} -+ void SetSelected(BOOL Selected = true) {bIsSelected = Selected;} -+ -+ /** The tree as dot graph for graphviz, usable for debugging -+ * Convert the output to a image using $ dot graph.gv -Tpng > graph.png -+ */ -+ inline void DumpAsDot(std::ostream &out, String* label = NULL) const{ -+ int id = 0; -+ DumpAsDot(out, label, -1, id, -1); -+ } - -+ /** Get the parent node of this node */ -+ SmStructureNode* GetParent(){ return aParentNode; } -+ /** Set the parent node */ -+ void SetParent(SmStructureNode* parent){ -+ aParentNode = parent; -+ } -+ -+ /** Get the index of a child node -+ * -+ * Returns -1, if pSubNode isn't a subnode of this. -+ */ -+ int IndexOfSubNode(SmNode* pSubNode){ -+ USHORT nSize = GetNumSubNodes(); -+ for(USHORT i = 0; i < nSize; i++) -+ if(pSubNode == GetSubNode(i)) -+ return i; -+ return -1; -+ } -+ /** Set the token for this node */ -+ void SetToken(SmToken& token){ -+ aNodeToken = token; -+ } -+protected: -+ /** Sets parent on children of this node */ -+ void ClaimPaternity(){ -+ SmNode* pNode; -+ USHORT nSize = GetNumSubNodes(); -+ for (USHORT i = 0; i < nSize; i++) -+ if (NULL != (pNode = GetSubNode(i))) -+ pNode->SetParent((SmStructureNode*)this); //Cast is valid if we have children -+ } -+private: -+ SmStructureNode* aParentNode; -+ void DumpAsDot(std::ostream &out, String* label, int number, int& id, int parent) const; -+}; - - //////////////////////////////////////////////////////////////////////////////// - -+/** A simple auxiliary iterator class for SmNode -+ * -+ * Example of iteration over children of pMyNode: -+ * \code -+ * //Node to iterate over: -+ * SmNode* pMyNode = 0;// A pointer from somewhere -+ * //The iterator: -+ * SmNodeIterator it(pMyNode); -+ * //The iteration: -+ * while(it.Next()) { -+ * it->SetSelected(true); -+ * } -+ * \endcode -+ */ -+class SmNodeIterator{ -+public: -+ SmNodeIterator(SmNode* node, bool bReverse = false){ -+ pNode = node; -+ nSize = pNode->GetNumSubNodes(); -+ nIndex = 0; -+ pChildNode = NULL; -+ bIsReverse = bReverse; -+ } -+ /** Get the subnode or NULL if none */ -+ SmNode* Next(){ -+ while(!bIsReverse && nIndex < nSize){ -+ if(NULL != (pChildNode = pNode->GetSubNode(nIndex++))) -+ return pChildNode; -+ } -+ while(bIsReverse && nSize > 0){ -+ if(NULL != (pChildNode = pNode->GetSubNode((nSize--)-1))) -+ return pChildNode; -+ } -+ pChildNode = NULL; -+ return NULL; -+ } -+ /** Get the current child node, NULL if none */ -+ SmNode* Current(){ -+ return pChildNode; -+ } -+ /** Get the current child node, NULL if none */ -+ SmNode* operator->(){ -+ return pChildNode; -+ } -+private: -+ /** Current child */ -+ SmNode* pChildNode; -+ /** Node whos children we're iterating over */ -+ SmNode* pNode; -+ /** Size of the node */ -+ USHORT nSize; -+ /** Current index in the node */ -+ USHORT nIndex; -+ /** Move reverse */ -+ bool bIsReverse; -+}; -+ -+//////////////////////////////////////////////////////////////////////////////// - -+/** Abstract baseclass for all composite node -+ * -+ * Subclasses of this class can have subnodes. Nodes that doesn't derivate from -+ * this class does not have subnodes. -+ */ - class SmStructureNode : public SmNode - { - SmNodeArray aSubNodes; -@@ -212,12 +347,29 @@ public: - virtual SmStructureNode & operator = ( const SmStructureNode &rNode ); - - virtual void GetAccessibleText( String &rText ) const; -+ -+ void SetSubNode(USHORT nIndex, SmNode* pNode){ -+ int size = aSubNodes.size(); -+ if(size <= nIndex){ -+ //Resize subnodes array -+ aSubNodes.resize(nIndex + 1); -+ //Set new slots to NULL -+ for(int i = size; i < nIndex+1; i++) -+ aSubNodes[i] = NULL; -+ } -+ aSubNodes[nIndex] = pNode; -+ ClaimPaternity(); -+ } - }; - - - //////////////////////////////////////////////////////////////////////////////// - -- -+/** Abstract base class for all visible node -+ * -+ * Nodes that doesn't derivate from this class doesn't draw anything, but their -+ * children. -+ */ - class SmVisibleNode : public SmNode - { - protected: -@@ -252,7 +404,10 @@ public: - - //////////////////////////////////////////////////////////////////////////////// - -- -+/** Draws a rectangle -+ * -+ * Used for drawing the line in the OVER and OVERSTRIKE commands. -+ */ - class SmRectangleNode : public SmGraphicNode - { - Size aToSize; -@@ -270,15 +425,19 @@ public: - #ifdef SM_RECT_DEBUG - using SmRect::Draw; - #endif -- virtual void Draw(OutputDevice &rDev, const Point &rPosition) const; -+ - - void CreateTextFromNode(String &rText); -+ void Accept(SmVisitor* pVisitor); - }; - - - //////////////////////////////////////////////////////////////////////////////// - -- -+/** Polygon line node -+ * -+ * Used to draw the slash of the WIDESLASH command by SmBinDiagonalNode. -+ */ - class SmPolyLineNode : public SmGraphicNode - { - Polygon aPoly; -@@ -289,6 +448,8 @@ public: - SmPolyLineNode(const SmToken &rNodeToken); - - long GetWidth() const { return nWidth; } -+ Size GetToSize() const { return aToSize; } -+ Polygon &GetPolygon() { return aPoly; } - - virtual void AdaptToX(const OutputDevice &rDev, ULONG nWidth); - virtual void AdaptToY(const OutputDevice &rDev, ULONG nHeight); -@@ -298,17 +459,29 @@ public: - #ifdef SM_RECT_DEBUG - using SmRect::Draw; - #endif -- virtual void Draw(OutputDevice &rDev, const Point &rPosition) const; -+ -+ void Accept(SmVisitor* pVisitor); - }; - - - //////////////////////////////////////////////////////////////////////////////// - -- -+/** Text node -+ * -+ * @remarks This class also serves as baseclass for all nodes that contains text. -+ */ - class SmTextNode : public SmVisibleNode - { - XubString aText; - USHORT nFontDesc; -+ /** Index within text where the selection starts -+ * @remarks Only valid if SmNode::IsSelected() is true -+ */ -+ xub_StrLen nSelectionStart; -+ /** Index within text where the selection ends -+ * @remarks Only valid if SmNode::IsSelected() is true -+ */ -+ xub_StrLen nSelectionEnd; - - protected: - SmTextNode(SmNodeType eNodeType, const SmToken &rNodeToken, USHORT nFontDescP) -@@ -328,6 +501,25 @@ public: - USHORT GetFontDesc() const { return nFontDesc; } - void SetText(const XubString &rText) { aText = rText; } - const XubString & GetText() const { return aText; } -+ /** Change the text of this node, including the underlying token */ -+ void ChangeText(const XubString &rText) { -+ aText = rText; -+ SmToken token = GetToken(); -+ token.aText = rText; -+ SetToken(token); -+ } -+ /** Index within GetText() where the selection starts -+ * @remarks Only valid of SmNode::IsSelected() is true -+ */ -+ xub_StrLen GetSelectionStart() const {return nSelectionStart;} -+ /** Index within GetText() where the selection end -+ * @remarks Only valid of SmNode::IsSelected() is true -+ */ -+ xub_StrLen GetSelectionEnd() const {return nSelectionEnd;} -+ /** Set the index within GetText() where the selection starts */ -+ void SetSelectionStart(xub_StrLen index) {nSelectionStart = index;} -+ /** Set the index within GetText() where the selection end */ -+ void SetSelectionEnd(xub_StrLen index) {nSelectionEnd = index;} - - virtual void Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell); - virtual void Arrange(const OutputDevice &rDev, const SmFormat &rFormat); -@@ -336,15 +528,22 @@ public: - #ifdef SM_RECT_DEBUG - using SmRect::Draw; - #endif -- virtual void Draw(OutputDevice &rDev, const Point &rPosition) const; -+ - - virtual void GetAccessibleText( String &rText ) const; -+ void Accept(SmVisitor* pVisitor); - }; - - - //////////////////////////////////////////////////////////////////////////////// - -- -+/** Special node for user defined characters -+ * -+ * Node used for pre- and user-defined characters from: -+ * officecfg/registry/data/org/openoffice/Office/Math.xcu -+ * -+ * This is just single characters, I think. -+ */ - class SmSpecialNode : public SmTextNode - { - protected: -@@ -363,13 +562,22 @@ public: - #ifdef SM_RECT_DEBUG - using SmRect::Draw; - #endif -- virtual void Draw(OutputDevice &rDev, const Point &rPosition) const; -+ -+ void Accept(SmVisitor* pVisitor); - }; - - - //////////////////////////////////////////////////////////////////////////////// - -- -+/** Glyph node for custom operators -+ * -+ * This node is used with commands: oper, uoper and boper. -+ * E.g. in "A boper op B", "op" will be an instance of SmGlyphSpecialNode. -+ * "boper" simply inteprets "op", the following token, as an binary operator. -+ * The command "uoper" interprets the following token as unary operator. -+ * For these commands an instance of SmGlyphSpecialNode is used for the -+ * operator token, following the command. -+ */ - class SmGlyphSpecialNode : public SmSpecialNode - { - public: -@@ -378,12 +586,16 @@ public: - {} - - virtual void Arrange(const OutputDevice &rDev, const SmFormat &rFormat); -+ void Accept(SmVisitor* pVisitor); - }; - - - //////////////////////////////////////////////////////////////////////////////// - -- -+/** Math symbol node -+ * -+ * Use for math symbols such as plus, minus and integrale in the INT command. -+ */ - class SmMathSymbolNode : public SmSpecialNode - { - protected: -@@ -404,12 +616,18 @@ public: - virtual void Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell); - virtual void Arrange(const OutputDevice &rDev, const SmFormat &rFormat); - void CreateTextFromNode(String &rText); -+ void Accept(SmVisitor* pVisitor); - }; - - - //////////////////////////////////////////////////////////////////////////////// - -- -+/** Root symbol node -+ * -+ * Root symbol node used by SmRootNode to create the root symbol, infront of -+ * the line with the line above. I don't think this node should be used for -+ * anything else. -+ */ - class SmRootSymbolNode : public SmMathSymbolNode - { - ULONG nBodyWidth; // width of body (argument) of root sign -@@ -419,19 +637,26 @@ public: - : SmMathSymbolNode(NROOTSYMBOL, rNodeToken) - {} - -+ ULONG GetBodyWidth() const {return nBodyWidth;}; - virtual void AdaptToX(const OutputDevice &rDev, ULONG nWidth); - virtual void AdaptToY(const OutputDevice &rDev, ULONG nHeight); - - #ifdef SM_RECT_DEBUG - using SmRect::Draw; - #endif -- virtual void Draw(OutputDevice &rDev, const Point &rPosition) const; -+ -+ void Accept(SmVisitor* pVisitor); - }; - - - //////////////////////////////////////////////////////////////////////////////// - -- -+/** Place node -+ * -+ * Used to create the <?> command, that denotes place where something can be -+ * written. -+ * It is drawn as a square with a shadow. -+ */ - class SmPlaceNode : public SmMathSymbolNode - { - public: -@@ -439,15 +664,21 @@ public: - : SmMathSymbolNode(NPLACE, rNodeToken) - { - } -+ SmPlaceNode() : SmMathSymbolNode(NPLACE, SmToken(TPLACE, MS_PLACE, "<?>")) {}; - - virtual void Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell); - virtual void Arrange(const OutputDevice &rDev, const SmFormat &rFormat); -+ void Accept(SmVisitor* pVisitor); - }; - - - //////////////////////////////////////////////////////////////////////////////// - -- -+/** Error node, for parsing errors -+ * -+ * This node is used for parsing errors and draws an questionmark turned upside -+ * down (inverted question mark). -+ */ - class SmErrorNode : public SmMathSymbolNode - { - public: -@@ -459,12 +690,19 @@ public: - - virtual void Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell); - virtual void Arrange(const OutputDevice &rDev, const SmFormat &rFormat); -+ void Accept(SmVisitor* pVisitor); - }; - - - //////////////////////////////////////////////////////////////////////////////// - -- -+/** Table node -+ * -+ * This is the root node for the formula tree. This node is also used for the -+ * STACK and BINOM commands. When used for root node, its -+ * children are instances of SmLineNode, and in some obscure cases the a child -+ * can be an instance of SmExpressionNode, mainly when errors occur. -+ */ - class SmTableNode : public SmStructureNode - { - public: -@@ -476,12 +714,17 @@ public: - virtual SmNode * GetLeftMost(); - - virtual void Arrange(const OutputDevice &rDev, const SmFormat &rFormat); -+ void Accept(SmVisitor* pVisitor); - }; - - - //////////////////////////////////////////////////////////////////////////////// - -- -+/** A line -+ * -+ * Used as child of SmTableNode when the SmTableNode is the root node of the -+ * formula tree. -+ */ - class SmLineNode : public SmStructureNode - { - protected: -@@ -496,12 +739,18 @@ public: - - virtual void Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell); - virtual void Arrange(const OutputDevice &rDev, const SmFormat &rFormat); -+ void Accept(SmVisitor* pVisitor); - }; - - - //////////////////////////////////////////////////////////////////////////////// - -- -+/** Expression node -+ * -+ * Used whenever you have an expression such as "A OVER {B + C}", here there is -+ * an expression node that allows "B + C" to be the denominator of the -+ * SmBinVerNode, that the OVER command creates. -+ */ - class SmExpressionNode : public SmLineNode - { - public: -@@ -511,12 +760,16 @@ public: - - virtual void Arrange(const OutputDevice &rDev, const SmFormat &rFormat); - void CreateTextFromNode(String &rText); -+ void Accept(SmVisitor* pVisitor); - }; - - - //////////////////////////////////////////////////////////////////////////////// - -- -+/** Unary horizontical node -+ * -+ * The same as SmBinHorNode except this is for unary operators. -+ */ - class SmUnHorNode : public SmStructureNode - { - public: -@@ -527,12 +780,23 @@ public: - } - - virtual void Arrange(const OutputDevice &rDev, const SmFormat &rFormat); -+ void Accept(SmVisitor* pVisitor); - }; - - - //////////////////////////////////////////////////////////////////////////////// - -- -+/** Root node -+ * -+ * Used for create square roots and other roots, example: -+ * \f$ \sqrt[\mbox{[Argument]}]{\mbox{[Body]}} \f$. -+ * -+ * Children:<BR> -+ * 0: Argument (optional)<BR> -+ * 1: Symbol (instance of SmRootSymbolNode)<BR> -+ * 2: Body<BR> -+ * Where argument is optinal and may be NULL. -+ */ - class SmRootNode : public SmStructureNode - { - protected: -@@ -549,12 +813,23 @@ public: - - virtual void Arrange(const OutputDevice &rDev, const SmFormat &rFormat); - void CreateTextFromNode(String &rText); -+ void Accept(SmVisitor* pVisitor); - }; - - - //////////////////////////////////////////////////////////////////////////////// - -- -+/** Binary horizontial node -+ * -+ * This node is used for binary operators. In a formula such as "A + B". -+ * -+ * Children:<BR> -+ * 0: Left operand<BR> -+ * 1: Binary operator<BR> -+ * 2: Right operand<BR> -+ * -+ * None of the children may be NULL. -+ */ - class SmBinHorNode : public SmStructureNode - { - public: -@@ -565,12 +840,24 @@ public: - } - - virtual void Arrange(const OutputDevice &rDev, const SmFormat &rFormat); -+ void Accept(SmVisitor* pVisitor); - }; - - - //////////////////////////////////////////////////////////////////////////////// - -- -+/** Binary horizontical node -+ * -+ * This node is used for creating the OVER command, consider the formula: -+ * "numerator OVER denominator", which looks like -+ * \f$ \frac{\mbox{numerator}}{\mbox{denominator}} \f$ -+ * -+ * Children:<BR> -+ * 0: Numerator<BR> -+ * 1: Line (instance of SmRectangleNode)<BR> -+ * 2: Denominator<BR> -+ * None of the children may be NULL. -+ */ - class SmBinVerNode : public SmStructureNode - { - public: -@@ -585,12 +872,22 @@ public: - - virtual void Arrange(const OutputDevice &rDev, const SmFormat &rFormat); - void CreateTextFromNode(String &rText); -+ void Accept(SmVisitor* pVisitor); - }; - - - //////////////////////////////////////////////////////////////////////////////// - -- -+/** Binary diagonal node -+ * -+ * Used for implementing the WIDESLASH command, example: "A WIDESLASH B". -+ * -+ * Children:<BR> -+ * 0: Left operand<BR> -+ * 1: right operand<BR> -+ * 2: Line (instance of SmPolyLineNode).<BR> -+ * None of the children may be NULL. -+ */ - class SmBinDiagonalNode : public SmStructureNode - { - BOOL bAscending; -@@ -605,35 +902,53 @@ public: - void SetAscending(BOOL bVal) { bAscending = bVal; } - - virtual void Arrange(const OutputDevice &rDev, const SmFormat &rFormat); -+ void Accept(SmVisitor* pVisitor); - }; - - - //////////////////////////////////////////////////////////////////////////////// - - --// enums used to index sub-/supscripts in the 'aSubNodes' array --// in 'SmSubSupNode' --// See graphic for positions at char: --// --// CSUP --// --// LSUP H H RSUP --// H H --// HHHH --// H H --// LSUB H H RSUB --// --// CSUB --// -+/** Enum used to index sub-/supscripts in the 'aSubNodes' array -+ * in 'SmSubSupNode' -+ * -+ * See graphic for positions at char: -+ * -+ * \code -+ * CSUP -+ * -+ * LSUP H H RSUP -+ * H H -+ * HHHH -+ * H H -+ * LSUB H H RSUB -+ * -+ * CSUB -+ * \endcode -+ */ - enum SmSubSup - { CSUB, CSUP, RSUB, RSUP, LSUB, LSUP - }; - --// numbers of entries in the above enum (that is: the number of possible --// sub-/supscripts) -+/** numbers of entries in the above enum (that is: the number of possible -+ * sub-/supscripts) -+ */ - #define SUBSUP_NUM_ENTRIES 6 - -- -+/** Super- and subscript node -+ * -+ * Used for creating super- and subscripts for commands such as: -+ * "^", "_", "lsup", "lsub", "csup" and "csub". -+ * Example: "A^2" which looks like: \f$ A^2 \f$ -+ * -+ * This node is also used for creating limits on SmOperNode, when -+ * "FROM" and "TO" commands are used with "INT", "SUM" or similar. -+ * -+ * Children of this node can be enumerated using the SmSubSup enum. -+ * Please note that children may be NULL, except for the body. -+ * It is recommended that you access children using GetBody() and -+ * GetSubSup(). -+ */ - class SmSubSupNode : public SmStructureNode - { - BOOL bUseLimits; -@@ -646,7 +961,9 @@ public: - bUseLimits = FALSE; - } - -+ /** Get body (Not NULL) */ - SmNode * GetBody() { return GetSubNode(0); } -+ /** Get body (Not NULL) */ - const SmNode * GetBody() const - { - return ((SmSubSupNode *) this)->GetBody(); -@@ -655,17 +972,39 @@ public: - void SetUseLimits(BOOL bVal) { bUseLimits = bVal; } - BOOL IsUseLimits() const { return bUseLimits; }; - -+ /** Get super- or subscript -+ * @remarks this method may return NULL. -+ */ - SmNode * GetSubSup(SmSubSup eSubSup) { return GetSubNode( sal::static_int_cast< USHORT >(1 + eSubSup) ); }; - -+ /** Set the body */ -+ void SetBody(SmNode* pBody) { SetSubNode(0, pBody); } -+ void SetSubSup(SmSubSup eSubSup, SmNode* pScript) { SetSubNode( 1 + eSubSup, pScript); } -+ - virtual void Arrange(const OutputDevice &rDev, const SmFormat &rFormat); - void CreateTextFromNode(String &rText); -+ void Accept(SmVisitor* pVisitor); - - }; - - - //////////////////////////////////////////////////////////////////////////////// - -- -+/** Node for brace construction -+ * -+ * Used for "lbrace [body] rbrace" and similar constructions. -+ * Should look like \f$ \{\mbox{[body]}\} \f$ -+ * -+ * Children:<BR> -+ * 0: Opening brace<BR> -+ * 1: Body (usually SmBracebodyNode)<BR> -+ * 2: Closing brace<BR> -+ * None of the children can be NULL. -+ * -+ * Note that child 1 (Body) is usually SmBracebodyNode, I don't know if it can -+ * be an SmExpressionNode, haven't seen the case. But didn't quite read parser.cxx -+ * enought to exclude this possibility. -+ */ - class SmBraceNode : public SmStructureNode - { - public: -@@ -677,12 +1016,21 @@ public: - - virtual void Arrange(const OutputDevice &rDev, const SmFormat &rFormat); - void CreateTextFromNode(String &rText); -+ void Accept(SmVisitor* pVisitor); - }; - - - //////////////////////////////////////////////////////////////////////////////// - -- -+/** Body of an SmBraceNode -+ * -+ * This usually only has one child an SmExpressionNode, however, it can also -+ * have other children. -+ * Consider the formula "lbrace [body1] mline [body2] rbrace", looks like: -+ * \f$ \{\mbox{[body1] | [body2]}\} \f$. -+ * In this case SmBracebodyNode will have three children, "[body1]", "|" and -+ * [body2]. -+ */ - class SmBracebodyNode : public SmStructureNode - { - long nBodyHeight; -@@ -692,6 +1040,7 @@ public: - - virtual void Arrange(const OutputDevice &rDev, const SmFormat &rFormat); - long GetBodyHeight() const { return nBodyHeight; } -+ void Accept(SmVisitor* pVisitor); - }; - - -@@ -704,13 +1053,25 @@ inline SmBracebodyNode::SmBracebodyNode(const SmToken &rNodeToken) : - - //////////////////////////////////////////////////////////////////////////////// - -- -+/** Node for vertical brace construction -+ * -+ * Used to implement commands "[body] underbrace [script]" and -+ * "[body] overbrace [script]". -+ * Underbrace should look like this \f$ \underbrace{\mbox{body}}_{\mbox{script}}\f$. -+ * -+ * Children:<BR> -+ * 0: body<BR> -+ * 1: brace<BR> -+ * 2: script<BR> -+ * (None of these children are optional, e.g. they must all be not NULL). -+ */ - class SmVerticalBraceNode : public SmStructureNode - { - public: - inline SmVerticalBraceNode(const SmToken &rNodeToken); - - virtual void Arrange(const OutputDevice &rDev, const SmFormat &rFormat); -+ void Accept(SmVisitor* pVisitor); - }; - - -@@ -724,6 +1085,18 @@ inline SmVerticalBraceNode::SmVerticalBraceNode(const SmToken &rNodeToken) : - //////////////////////////////////////////////////////////////////////////////// - - -+/** Operation Node -+ * -+ * Used for commands like SUM, INT and similar. -+ * -+ * Children:<BR> -+ * 0: Operation (instance of SmMathSymbolNode)<BR> -+ * 1: Body<BR> -+ * None of the children may be NULL. -+ * -+ * If there are boundaries on the operation the body will an instance of -+ * SmSubSupNode. -+ */ - class SmOperNode : public SmStructureNode - { - public: -@@ -742,12 +1115,14 @@ public: - long CalcSymbolHeight(const SmNode &rSymbol, const SmFormat &rFormat) const; - - virtual void Arrange(const OutputDevice &rDev, const SmFormat &rFormat); -+ void Accept(SmVisitor* pVisitor); - }; - - - //////////////////////////////////////////////////////////////////////////////// - -- -+/** Node used for alignment -+ */ - class SmAlignNode : public SmStructureNode - { - public: -@@ -756,12 +1131,22 @@ public: - {} - - virtual void Arrange(const OutputDevice &rDev, const SmFormat &rFormat); -+ void Accept(SmVisitor* pVisitor); - }; - - - //////////////////////////////////////////////////////////////////////////////// - -- -+/** Attribute node -+ * -+ * Used to give an attribute to another node. Used for commands such as: -+ * UNDERLINE, OVERLINE, OVERSTRIKE, WIDEVEC, WIDEHAT and WIDETILDE. -+ * -+ * Children:<BR> -+ * 0: Attribute<BR> -+ * 1: Body<BR> -+ * None of these may be NULL. -+ */ - class SmAttributNode : public SmStructureNode - { - public: -@@ -771,12 +1156,16 @@ public: - - virtual void Arrange(const OutputDevice &rDev, const SmFormat &rFormat); - void CreateTextFromNode(String &rText); -+ void Accept(SmVisitor* pVisitor); - }; - - - //////////////////////////////////////////////////////////////////////////////// - -- -+/** Font node -+ * -+ * Used to change the font of it's children. -+ */ - class SmFontNode : public SmStructureNode - { - USHORT nSizeType; -@@ -797,12 +1186,17 @@ public: - virtual void Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell); - virtual void Arrange(const OutputDevice &rDev, const SmFormat &rFormat); - void CreateTextFromNode(String &rText); -+ void Accept(SmVisitor* pVisitor); - }; - - - //////////////////////////////////////////////////////////////////////////////// - -- -+/** Matrix node -+ * -+ * Used to implement the MATRIX command, example: -+ * "matrix{ 1 # 2 ## 3 # 4}". -+ */ - class SmMatrixNode : public SmStructureNode - { - USHORT nNumRows, -@@ -824,12 +1218,16 @@ public: - - virtual void Arrange(const OutputDevice &rDev, const SmFormat &rFormat); - void CreateTextFromNode(String &rText); -+ void Accept(SmVisitor* pVisitor); - }; - - - //////////////////////////////////////////////////////////////////////////////// - -- -+/** Node for whitespace -+ * -+ * Used to implement the "~" command. This node is just a blank space. -+ */ - class SmBlankNode : public SmGraphicNode - { - USHORT nNum; -@@ -843,9 +1241,12 @@ public: - - void IncreaseBy(const SmToken &rToken); - void Clear() { nNum = 0; } -+ USHORT GetBlankNum() const { return nNum; } -+ void SetBlankNum(USHORT nNumber) { nNum = nNumber; } - - virtual void Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell); - virtual void Arrange(const OutputDevice &rDev, const SmFormat &rFormat); -+ void Accept(SmVisitor* pVisitor); - }; - - -diff --git starmath/inc/parse.hxx starmath/inc/parse.hxx -index 0211d97..97a7413 100644 ---- starmath/inc/parse.hxx -+++ starmath/inc/parse.hxx -@@ -117,7 +117,7 @@ struct SmToken - String aText; - // token info - SmTokenType eType; -- sal_Unicode cMathChar; -+ sal_Unicode cMathChar; - // parse-help info - ULONG nGroup; - USHORT nLevel; -@@ -126,6 +126,11 @@ struct SmToken - xub_StrLen nCol; - - SmToken(); -+ SmToken(SmTokenType eTokenType, -+ sal_Unicode cMath, -+ const sal_Char* pText, -+ ULONG nTokenGroup = 0, -+ USHORT nTokenLevel = 0); - }; - - -@@ -239,7 +244,10 @@ protected: - public: - SmParser(); - -+ /** Parse rBuffer to formula tree */ - SmNode *Parse(const String &rBuffer); -+ /** Parse rBuffer to formula subtree that constitutes an expression */ -+ SmNode *ParseExpression(const String &rBuffer); - - const String & GetText() const { return BufferString; }; - -diff --git starmath/inc/starmath.hrc starmath/inc/starmath.hrc -index 0f9aec4..cb3cbe5 100644 ---- starmath/inc/starmath.hrc -+++ starmath/inc/starmath.hrc -@@ -62,7 +62,9 @@ - #define SID_TEXT (SID_SMA_START + 100) - #define SID_GAPHIC_SM (SID_SMA_START + 101) - #define SID_FITINWINDOW (SID_SMA_START + 103) --#define SID_INSERTTEXT (SID_SMA_START + 104) -+/** Command for inserting a symbol specified by a string (Inserts an SmSpecialNode) */ -+#define SID_INSERTSYMBOL (SID_SMA_START + 104) -+/** Command for inserting a math construction specified in commands.src */ - #define SID_INSERTCOMMAND (SID_SMA_START + 105) - - #define SID_LOADSYMBOLS (SID_SMA_START + 107) -diff --git starmath/inc/view.hxx starmath/inc/view.hxx -index eab2444..45d3990 100644 ---- starmath/inc/view.hxx -+++ starmath/inc/view.hxx -@@ -50,7 +50,6 @@ class SmViewShell; - class SmGraphicWindow : public ScrollableWindow - { - Point aFormulaDrawPos; -- Rectangle aCursorRect; - - ::com::sun::star::uno::Reference< - ::com::sun::star::accessibility::XAccessible > xAccessible; -@@ -59,14 +58,9 @@ class SmGraphicWindow : public ScrollableWindow - SmViewShell *pViewShell; - USHORT nZoom; - short nModifyCount; -- BOOL bIsCursorVisible; - - protected: - void SetFormulaDrawPos(const Point &rPos) { aFormulaDrawPos = rPos; } -- void SetIsCursorVisible(BOOL bVis) { bIsCursorVisible = bVis; } -- using Window::SetCursor; -- void SetCursor(const SmNode *pNode); -- void SetCursor(const Rectangle &rRect); - - virtual void DataChanged( const DataChangedEvent& ); - virtual void Paint(const Rectangle&); -@@ -97,10 +91,6 @@ public: - using ScrollableWindow::SetTotalSize; - void SetTotalSize(); - -- BOOL IsCursorVisible() const { return bIsCursorVisible; } -- void ShowCursor(BOOL bShow); -- const SmNode * SetCursorPos(USHORT nRow, USHORT nCol); -- - void ApplyColorConfigValues( const svtools::ColorConfig &rColorCfg ); - - // for Accessibility -@@ -228,6 +218,11 @@ class SmViewShell: public SfxViewShell - DECL_LINK( DialogClosedHdl, sfx2::FileDialogHelper* ); - virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ); - -+ /** Used to determine whether insertions using SID_INSERTSYMBOL and SID_INSERTCOMMAND -+ * should be inserted into SmEditWindow or directly into the SmDocShell as done if the -+ * visual editor was last to have focus. -+ */ -+ BOOL bInsertIntoEditWindow; - protected: - - Size GetTextLineSize(OutputDevice& rDevice, -@@ -291,6 +286,16 @@ public: - - void Impl_Print( OutputDevice &rOutDev, const SmPrintSize ePrintSize, - Rectangle aOutRect, Point aZeroPoint ); -+ -+ /** Set bInsertIntoEditWindow so we know where to insert -+ * -+ * This method is called whenever SmGraphicWindow or SmEditWindow gets focus, -+ * so that when text is inserted from catalog or elsewhere we know whether to -+ * insert for the visual editor, or the text editor. -+ */ -+ void SetInsertIntoEditWindow(BOOL bEditWindowHadFocusLast = TRUE){ -+ bInsertIntoEditWindow = bEditWindowHadFocusLast; -+ } - }; - - #endif -diff --git starmath/inc/visitors.hxx starmath/inc/visitors.hxx -new file mode 100644 -index 0000000..5a61dd0 ---- /dev/null -+++ starmath/inc/visitors.hxx -@@ -0,0 +1,471 @@ -+#ifndef SMVISITORS_H -+#define SMVISITORS_H -+ -+#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( SmAttributNode* 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; -+}; -+ -+/** Simple visitor for testing SmVisitor */ -+class SmVisitorTest : public SmVisitor -+{ -+public: -+ void Visit( SmTableNode* pNode ); -+ void Visit( SmBraceNode* pNode ); -+ void Visit( SmBracebodyNode* pNode ); -+ void Visit( SmOperNode* pNode ); -+ void Visit( SmAlignNode* pNode ); -+ void Visit( SmAttributNode* pNode ); -+ void Visit( SmFontNode* pNode ); -+ void Visit( SmUnHorNode* pNode ); -+ void Visit( SmBinHorNode* pNode ); -+ void Visit( SmBinVerNode* pNode ); -+ void Visit( SmBinDiagonalNode* pNode ); -+ void Visit( SmSubSupNode* pNode ); -+ void Visit( SmMatrixNode* pNode ); -+ void Visit( SmPlaceNode* pNode ); -+ void Visit( SmTextNode* pNode ); -+ void Visit( SmSpecialNode* pNode ); -+ void Visit( SmGlyphSpecialNode* pNode ); -+ void Visit( SmMathSymbolNode* pNode ); -+ void Visit( SmBlankNode* pNode ); -+ void Visit( SmErrorNode* pNode ); -+ void Visit( SmLineNode* pNode ); -+ void Visit( SmExpressionNode* pNode ); -+ void Visit( SmPolyLineNode* pNode ); -+ void Visit( SmRootNode* pNode ); -+ void Visit( SmRootSymbolNode* pNode ); -+ void Visit( SmRectangleNode* pNode ); -+ void Visit( SmVerticalBraceNode* pNode ); -+private: -+ /** Auxiliary method for visiting the children of a pNode */ -+ void VisitChildren( SmNode* pNode ); -+}; -+ -+/////////////////////////////// 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 ); -+ void Visit( SmBraceNode* pNode ); -+ void Visit( SmBracebodyNode* pNode ); -+ void Visit( SmOperNode* pNode ); -+ void Visit( SmAlignNode* pNode ); -+ void Visit( SmAttributNode* pNode ); -+ void Visit( SmFontNode* pNode ); -+ void Visit( SmUnHorNode* pNode ); -+ void Visit( SmBinHorNode* pNode ); -+ void Visit( SmBinVerNode* pNode ); -+ void Visit( SmBinDiagonalNode* pNode ); -+ void Visit( SmSubSupNode* pNode ); -+ void Visit( SmMatrixNode* pNode ); -+ void Visit( SmPlaceNode* pNode ); -+ void Visit( SmTextNode* pNode ); -+ void Visit( SmSpecialNode* pNode ); -+ void Visit( SmGlyphSpecialNode* pNode ); -+ void Visit( SmMathSymbolNode* pNode ); -+ void Visit( SmBlankNode* pNode ); -+ void Visit( SmErrorNode* pNode ); -+ void Visit( SmLineNode* pNode ); -+ void Visit( SmExpressionNode* pNode ); -+ void Visit( SmPolyLineNode* pNode ); -+ void Visit( SmRootNode* pNode ); -+ void Visit( SmRootSymbolNode* pNode ); -+ void Visit( SmRectangleNode* pNode ); -+ void Visit( SmVerticalBraceNode* pNode ); -+protected: -+ /** Method invoked by Visit methods by default */ -+ virtual void DefaultVisit( SmNode* pNode ) = 0; -+}; -+ -+/////////////////////////////// SmCaretDrawingVisitor //////////////////////////////// -+ -+/** Visitor for drawing a caret position */ -+class SmCaretDrawingVisitor : public SmDefaultingVisitor -+{ -+public: -+ /** Given position and device this constructor will draw the caret */ -+ SmCaretDrawingVisitor( OutputDevice& rDevice, SmCaretPos position, Point offset ); -+ void Visit( SmTextNode* pNode ); -+private: -+ OutputDevice &rDev; -+ SmCaretPos pos; -+ /** Offset to draw from */ -+ Point Offset; -+protected: -+ /** Default method for drawing pNodes */ -+ void DefaultVisit( SmNode* pNode ); -+}; -+ -+/////////////////////////////// SmCaretPos2LineVisitor //////////////////////////////// -+ -+/** Visitor getting a line from a caret position */ -+class SmCaretPos2LineVisitor : public SmDefaultingVisitor -+{ -+public: -+ /** Given position and device this constructor will compute a line for the caret */ -+ SmCaretPos2LineVisitor( OutputDevice *pDevice, SmCaretPos position ) { -+ pDev = pDevice; -+ pos = position; -+ j_assert( position.IsValid( ), "Cannot draw invalid position!" ); -+ -+ pos.pSelectedNode->Accept( this ); -+ } -+ void Visit( SmTextNode* pNode ); -+ SmCaretLine GetResult( ){ -+ return line; -+ } -+private: -+ SmCaretLine line; -+ OutputDevice *pDev; -+ SmCaretPos pos; -+protected: -+ /** Default method for computing lines for pNodes */ -+ void DefaultVisit( SmNode* pNode ); -+}; -+ -+/////////////////////////////// DrawingVisitor //////////////////////////////// -+ -+/** Visitor for drawing SmNodes to OutputDevice */ -+class DrawingVisitor : public SmVisitor -+{ -+public: -+ /** Create an instance of DrawingVisitor, 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 -+ * @remarks This constructor will do the drawing, no need to anything more. -+ */ -+ DrawingVisitor( OutputDevice &rDevice, Point position, SmNode* pTree ) -+ : rDev( rDevice ) { -+ this->Position = position; -+ pTree->Accept( this ); -+ } -+ void Visit( SmTableNode* pNode ); -+ void Visit( SmBraceNode* pNode ); -+ void Visit( SmBracebodyNode* pNode ); -+ void Visit( SmOperNode* pNode ); -+ void Visit( SmAlignNode* pNode ); -+ void Visit( SmAttributNode* pNode ); -+ void Visit( SmFontNode* pNode ); -+ void Visit( SmUnHorNode* pNode ); -+ void Visit( SmBinHorNode* pNode ); -+ void Visit( SmBinVerNode* pNode ); -+ void Visit( SmBinDiagonalNode* pNode ); -+ void Visit( SmSubSupNode* pNode ); -+ void Visit( SmMatrixNode* pNode ); -+ void Visit( SmPlaceNode* pNode ); -+ void Visit( SmTextNode* pNode ); -+ void Visit( SmSpecialNode* pNode ); -+ void Visit( SmGlyphSpecialNode* pNode ); -+ void Visit( SmMathSymbolNode* pNode ); -+ void Visit( SmBlankNode* pNode ); -+ void Visit( SmErrorNode* pNode ); -+ void Visit( SmLineNode* pNode ); -+ void Visit( SmExpressionNode* pNode ); -+ void Visit( SmPolyLineNode* pNode ); -+ void Visit( SmRootNode* pNode ); -+ void Visit( SmRootSymbolNode* pNode ); -+ void Visit( SmRectangleNode* pNode ); -+ void Visit( SmVerticalBraceNode* pNode ); -+private: -+ /** Draw the children of a pNode -+ * This the default method, use by most pNodes -+ */ -+ void DrawChildren( SmNode* 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& rDev; -+ /** Position to draw on the rDev -+ * @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 Position; -+}; -+ -+/////////////////////////////// SetSelectionVisitor //////////////////////////////// -+ -+/** Set Selection Visitor -+ * Sets the IsSelected( ) property on all SmNodes of the tree -+ */ -+class SetSelectionVisitor : public SmDefaultingVisitor -+{ -+public: -+ SetSelectionVisitor( SmCaretPos startPos, -+ SmCaretPos endPos ){ -+ StartPos = startPos; -+ EndPos = endPos; -+ IsSelecting = false; -+ } -+ void Visit( SmBinHorNode* pNode ); -+ void Visit( SmUnHorNode* pNode ); -+ void Visit( SmFontNode* pNode ); -+ void Visit( SmTextNode* pNode ); -+ void Visit( SmExpressionNode* pNode ); -+ void Visit( SmBracebodyNode* pNode ); -+ void Visit( SmAlignNode* pNode ); -+ /** 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 seperately! -+ * Also note that pNodes such as SmBinVerNode cannot be selected, don't this method for -+ * it. -+ */ -+ void DefaultVisit( SmNode* pNode ); -+ void VisitCompositionNode( SmNode* pNode ); -+ /** Caret position where the selection starts */ -+ SmCaretPos StartPos; -+ /** Caret position where the selection ends */ -+ SmCaretPos EndPos; -+ /** The current state of this visitor -+ * This property changes when the visitor meets either StartPos -+ * or EndPos. This means that anything visited in between will be -+ * selected. -+ */ -+ BOOL IsSelecting; -+}; -+ -+ -+/////////////////////////////// SmCaretPosGraphBuildingVisitor //////////////////////////////// -+ -+ -+/** A visitor for building a SmCaretPosGraph */ -+class SmCaretPosGraphBuildingVisitor : public SmVisitor -+{ -+public: -+ SmCaretPosGraphBuildingVisitor( ){ -+ pRightMost = NULL; -+ pGraph = new SmCaretPosGraph( ); -+ } -+ /* Visit invariant: -+ * Each pNode, except SmExpressionNode, SmBinHorNode and a few others, constitues 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 in to calls and results out -+ * of calls. -+ * pRightMost : SmCaretPosGraphEntry* -+ * -+ * Prior to a Visit call: -+ * pRightMost: A pointer to right most position infront 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. -+ */ -+ void Visit( SmTableNode* pNode ); -+ void Visit( SmBraceNode* pNode ); -+ void Visit( SmBracebodyNode* pNode ); -+ void Visit( SmOperNode* pNode ); -+ void Visit( SmAlignNode* pNode ); -+ void Visit( SmAttributNode* pNode ); -+ void Visit( SmFontNode* pNode ); -+ void Visit( SmUnHorNode* pNode ); -+ void Visit( SmBinHorNode* pNode ); -+ void Visit( SmBinVerNode* pNode ); -+ void Visit( SmBinDiagonalNode* pNode ); -+ void Visit( SmSubSupNode* pNode ); -+ void Visit( SmMatrixNode* pNode ); -+ void Visit( SmPlaceNode* pNode ); -+ void Visit( SmTextNode* pNode ); -+ void Visit( SmSpecialNode* pNode ); -+ void Visit( SmGlyphSpecialNode* pNode ); -+ void Visit( SmMathSymbolNode* pNode ); -+ void Visit( SmBlankNode* pNode ); -+ void Visit( SmErrorNode* pNode ); -+ void Visit( SmLineNode* pNode ); -+ void Visit( SmExpressionNode* pNode ); -+ void Visit( SmPolyLineNode* pNode ); -+ void Visit( SmRootNode* pNode ); -+ void Visit( SmRootSymbolNode* pNode ); -+ void Visit( SmRectangleNode* pNode ); -+ void Visit( SmVerticalBraceNode* pNode ); -+ SmCaretPosGraph* Graph( ){ -+ return pGraph; -+ } -+private: -+ SmCaretPosGraphEntry* pRightMost; -+ SmCaretPosGraph* pGraph; -+}; -+ -+/////////////////////////////// SmCloningVisitor /////////////////////////////// -+ -+/** Visitor for cloning a pNode -+ * -+ * This visitor creates deep clones. -+ */ -+class SmCloningVisitor : public SmVisitor -+{ -+public: -+ SmCloningVisitor( ){ pResult = NULL; } -+ void Visit( SmTableNode* pNode ); -+ void Visit( SmBraceNode* pNode ); -+ void Visit( SmBracebodyNode* pNode ); -+ void Visit( SmOperNode* pNode ); -+ void Visit( SmAlignNode* pNode ); -+ void Visit( SmAttributNode* pNode ); -+ void Visit( SmFontNode* pNode ); -+ void Visit( SmUnHorNode* pNode ); -+ void Visit( SmBinHorNode* pNode ); -+ void Visit( SmBinVerNode* pNode ); -+ void Visit( SmBinDiagonalNode* pNode ); -+ void Visit( SmSubSupNode* pNode ); -+ void Visit( SmMatrixNode* pNode ); -+ void Visit( SmPlaceNode* pNode ); -+ void Visit( SmTextNode* pNode ); -+ void Visit( SmSpecialNode* pNode ); -+ void Visit( SmGlyphSpecialNode* pNode ); -+ void Visit( SmMathSymbolNode* pNode ); -+ void Visit( SmBlankNode* pNode ); -+ void Visit( SmErrorNode* pNode ); -+ void Visit( SmLineNode* pNode ); -+ void Visit( SmExpressionNode* pNode ); -+ void Visit( SmPolyLineNode* pNode ); -+ void Visit( SmRootNode* pNode ); -+ void Visit( SmRootSymbolNode* pNode ); -+ void Visit( SmRectangleNode* pNode ); -+ void Visit( SmVerticalBraceNode* pNode ); -+ /** Clone a pNode */ -+ SmNode* Clone( SmNode* pNode ); -+private: -+ SmNode* pResult; -+ /** Clone children of pSource and give them to pTarget */ -+ void CloneKids( SmStructureNode* pSource, SmStructureNode* pTarget ); -+ /** Clone attributes on a pNode */ -+ void CloneNodeAttr( SmNode* pSource, SmNode* pTarget ); -+}; -+ -+ -+/////////////////////////////// SmSelectionDrawingVisitor /////////////////////////////// -+ -+class SmSelectionDrawingVisitor : public SmDefaultingVisitor -+{ -+public: -+ /** Draws a selection on rDevice for the selection on pTree */ -+ SmSelectionDrawingVisitor( OutputDevice& rDevice, SmNode* pTree, Point Offset ); -+ void Visit( SmTextNode* pNode ); -+private: -+ /** Reference to drawing device */ -+ OutputDevice& rDev; -+ /** True if aSelectionArea have been initialized */ -+ BOOL bHasSelectionArea; -+ /** The current area that is selected */ -+ Rectangle aSelectionArea; -+ /** Extend the area that must be selected */ -+ void ExtendSelectionArea( Rectangle aArea ); -+ /** Default visiting method */ -+ void DefaultVisit( SmNode* pNode ); -+ /** Visit the children of a given pNode */ -+ void VisitChildren( SmNode* pNode ); -+}; -+ -+/////////////////////////////// SmNodeToTextVisitor /////////////////////////////// -+ -+/** Extract command text from pNodes */ -+class SmNodeToTextVisitor : public SmVisitor -+{ -+public: -+ SmNodeToTextVisitor( SmNode* pNode, String &rText ) -+ : rCmdText( rText ) { -+ pNode->Accept( this ); -+ } -+ void Visit( SmTableNode* pNode ); -+ void Visit( SmBraceNode* pNode ); -+ void Visit( SmBracebodyNode* pNode ); -+ void Visit( SmOperNode* pNode ); -+ void Visit( SmAlignNode* pNode ); -+ void Visit( SmAttributNode* pNode ); -+ void Visit( SmFontNode* pNode ); -+ void Visit( SmUnHorNode* pNode ); -+ void Visit( SmBinHorNode* pNode ); -+ void Visit( SmBinVerNode* pNode ); -+ void Visit( SmBinDiagonalNode* pNode ); -+ void Visit( SmSubSupNode* pNode ); -+ void Visit( SmMatrixNode* pNode ); -+ void Visit( SmPlaceNode* pNode ); -+ void Visit( SmTextNode* pNode ); -+ void Visit( SmSpecialNode* pNode ); -+ void Visit( SmGlyphSpecialNode* pNode ); -+ void Visit( SmMathSymbolNode* pNode ); -+ void Visit( SmBlankNode* pNode ); -+ void Visit( SmErrorNode* pNode ); -+ void Visit( SmLineNode* pNode ); -+ void Visit( SmExpressionNode* pNode ); -+ void Visit( SmPolyLineNode* pNode ); -+ void Visit( SmRootNode* pNode ); -+ void Visit( SmRootSymbolNode* pNode ); -+ void Visit( SmRectangleNode* pNode ); -+ void Visit( SmVerticalBraceNode* pNode ); -+private: -+ /** Extract text from a pNode that constitues a line */ -+ void LineToText( SmNode* pNode ) { -+ Separate( ); -+ if( pNode ) -+ pNode->Accept( this ); -+ Separate( ); -+ } -+ inline void Append( const sal_Char* pCharStr ) { -+ rCmdText.AppendAscii( pCharStr ); -+ } -+ inline void Append( const String &rText ) { -+ rCmdText.Append( rText ); -+ } -+ /** Append a blank for separation, if needed */ -+ inline void Separate( ){ -+ if( rCmdText.GetChar( rCmdText.Len( ) - 1 ) != ' ' ) -+ rCmdText.AppendAscii( RTL_CONSTASCII_STRINGPARAM( " " ) ); -+ } -+ /** Output text generated from the pNodes */ -+ String &rCmdText; -+}; -+ -+#endif /* SMVISITORS_H */ -diff --git starmath/sdi/smath.sdi starmath/sdi/smath.sdi -index a8fb305..8682f08 100644 ---- starmath/sdi/smath.sdi -+++ starmath/sdi/smath.sdi -@@ -378,7 +378,7 @@ SfxVoidItem InsertCommand SID_INSERTCOMMAND - ] - - //-------------------------------------------------------------------------- --SfxVoidItem InsertConfigName SID_INSERTTEXT -+SfxVoidItem InsertConfigName SID_INSERTSYMBOL - () - [ - /* flags: */ -diff --git starmath/sdi/smslots.sdi starmath/sdi/smslots.sdi -index b8494c6..518b8b1 100644 ---- starmath/sdi/smslots.sdi -+++ starmath/sdi/smslots.sdi -@@ -267,17 +267,17 @@ interface FormulaView : View - StateMethod = GetState ; - ] - //idlpp kein Menueeintrag , also keine Texte -- SID_INSERTTEXT //idlpp ole : no , status : no -+ SID_INSERTSYMBOL //idlpp ole : no , status : no - [ - ExecMethod = Execute ; - StateMethod = GetState ; - ] -- SID_INSERT_FORMULA //idlpp ole : no , status : no -- [ -- ExecMethod = Execute ; -- StateMethod = GetState ; -- Export = FALSE ; -- ] -+ SID_INSERT_FORMULA //idlpp ole : no , status : no -+ [ -+ ExecMethod = Execute ; -+ StateMethod = GetState ; -+ Export = FALSE ; -+ ] - //idlpp kein Menueeintrag , also keine Texte - SID_ATTR_ZOOM //idlpp ole : no , status : no - [ -diff --git starmath/source/caret.cxx starmath/source/caret.cxx -new file mode 100644 -index 0000000..bb312ef ---- /dev/null -+++ starmath/source/caret.cxx -@@ -0,0 +1,35 @@ -+#include "caret.hxx" -+ -+/////////////////////////////// SmCaretPosGraph //////////////////////////////// -+ -+SmCaretPosGraphEntry* SmCaretPosGraphIterator::Next(){ -+ if(nOffset >= pGraph->nOffset){ -+ if(pGraph->pNext){ -+ pGraph = pGraph->pNext; -+ nOffset = 0; -+ pEntry = Next(); -+ }else -+ pEntry = NULL; -+ }else -+ pEntry = pGraph->Graph + nOffset++; -+ return pEntry; -+} -+ -+SmCaretPosGraphEntry* SmCaretPosGraph::Add(SmCaretPosGraphEntry entry){ -+ if(nOffset >= SmCaretPosGraphSize){ -+ if(!pNext) -+ pNext = new SmCaretPosGraph(); -+ return pNext->Add(entry); -+ }else{ -+ Graph[nOffset] = entry; -+ return Graph + nOffset++; -+ } -+} -+ -+SmCaretPosGraph::~SmCaretPosGraph(){ -+ if(pNext) -+ delete pNext; -+ pNext = NULL; -+} -+ -+ -diff --git starmath/source/cursor.cxx starmath/source/cursor.cxx -new file mode 100644 -index 0000000..3c3cba6 ---- /dev/null -+++ starmath/source/cursor.cxx -@@ -0,0 +1,1615 @@ -+#include "cursor.hxx" -+#include "parse.hxx" -+#include "visitors.hxx" -+#include "document.hxx" -+#include "view.hxx" -+ -+void SmCursor::Move(OutputDevice* pDev, SmMovementDirection direction, bool bMoveAnchor){ -+ SmCaretPosGraphEntry* NewPos = NULL; -+ switch(direction){ -+ case MoveLeft: -+ { -+ //If position->Left is NULL, we want NewPos = NULL anyway... -+ NewPos = position->Left; -+ }break; -+ case MoveRight: -+ { -+ //If position->Right is NULL, we want NewPos = NULL anyway... -+ NewPos = position->Right; -+ }break; -+ case MoveUp: -+ //Implementation is practically identical to MoveDown, except for a single if statement -+ //so I've implemented them together and added a direction == MoveDown to the if statements. -+ case MoveDown: -+ { -+ SmCaretLine from_line = SmCaretPos2LineVisitor(pDev, position->CaretPos).GetResult(), -+ best_line, //Best approximated line found so far -+ curr_line; //Current line -+ long dbp_sq; //Distance squared to best line -+ SmCaretPosGraphIterator it = pGraph->GetIterator(); -+ while(it.Next()){ -+ //Reject it if it's the current position -+ if(it->CaretPos == position->CaretPos) continue; -+ //Compute caret line -+ curr_line = SmCaretPos2LineVisitor(pDev, it->CaretPos).GetResult(); -+ //Reject anything above if we're moving down -+ if(curr_line.GetTop() <= from_line.GetTop() && direction == MoveDown) continue; -+ //Reject anything below if we're moving up -+ if(curr_line.GetTop() + curr_line.GetHeight() >= from_line.GetTop() + from_line.GetHeight() -+ && direction == MoveUp) continue; -+ //Compare if it to what we have, if we have anything yet -+ if(NewPos){ -+ //Compute distance to current line squared, multiplied with a horizontial factor -+ long dp_sq = curr_line.SquaredDistanceX(from_line) * HORIZONTICAL_DISTANCE_FACTOR + -+ curr_line.SquaredDistanceY(from_line); -+ //Discard current line if best line is closer -+ if(dbp_sq <= dp_sq) continue; -+ } -+ //Take current line as the best -+ best_line = curr_line; -+ NewPos = it.Current(); -+ //Update distance to best line -+ dbp_sq = best_line.SquaredDistanceX(from_line) * HORIZONTICAL_DISTANCE_FACTOR + -+ best_line.SquaredDistanceY(from_line); -+ } -+ }break; -+ default: -+ j_assert(false, "Movement direction not supported!"); -+ } -+ if(NewPos){ -+ position = NewPos; -+ if(bMoveAnchor) -+ anchor = NewPos; -+ RequestRepaint(); -+ } -+} -+ -+void SmCursor::MoveTo(OutputDevice* pDev, Point pos, bool bMoveAnchor){ -+ SmCaretLine best_line, //Best line found so far, when iterating -+ curr_line; //Current line, when iterating -+ SmCaretPosGraphEntry* NewPos = NULL; -+ long dp_sq, //Distance to current line squared -+ dbp_sq; //Distance to best line squared -+ SmCaretPosGraphIterator it = pGraph->GetIterator(); -+ while(it.Next()){ -+ j_assert(it->CaretPos.IsValid(), "The caret position graph may not have invalid positions!"); -+ //Compute current line -+ curr_line = SmCaretPos2LineVisitor(pDev, it->CaretPos).GetResult(); -+ //If we have a position compare to it -+ if(NewPos){ -+ //Compute squared distance to current line -+ dp_sq = curr_line.SquaredDistanceX(pos) + curr_line.SquaredDistanceY(pos); -+ //If best line is closer, reject current line -+ if(dbp_sq <= dp_sq) continue; -+ } -+ //Accept current position as the best -+ best_line = curr_line; -+ NewPos = it.Current(); -+ //Update distance to best line -+ dbp_sq = best_line.SquaredDistanceX(pos) + best_line.SquaredDistanceY(pos); -+ } -+ if(NewPos){ -+ position = NewPos; -+ if(bMoveAnchor) -+ anchor = NewPos; -+ RequestRepaint(); -+ } -+} -+ -+void SmCursor::BuildGraph(){ -+ //Save the current anchor and position -+ SmCaretPos _anchor, _position; -+ //Release pGraph if allocated -+ if(pGraph){ -+ if(anchor) -+ _anchor = anchor->CaretPos; -+ if(position) -+ _position = position->CaretPos; -+ delete pGraph; -+ //Reset anchor and position as they point into an old graph -+ anchor = NULL; -+ position = NULL; -+ } -+ pGraph = NULL; -+ -+ //Build the new graph -+ SmCaretPosGraphBuildingVisitor builder; -+ pTree->Accept(&builder); -+ pGraph = builder.Graph(); -+ -+ //Restore anchor and position pointers -+ if(_anchor.IsValid() || _position.IsValid()){ -+ SmCaretPosGraphIterator it = pGraph->GetIterator(); -+ while(it.Next()){ -+ if(_anchor == it->CaretPos) -+ anchor = it.Current(); -+ if(_position == it->CaretPos) -+ position = it.Current(); -+ } -+ } -+ //Set position and anchor to first caret position -+ SmCaretPosGraphIterator it = pGraph->GetIterator(); -+ if(!position) -+ position = it.Next(); -+ if(!anchor) -+ anchor = position; -+ -+ j_assert(position->CaretPos.IsValid(), "Position must be valid"); -+ j_assert(anchor->CaretPos.IsValid(), "Anchor must be valid"); -+} -+ -+bool SmCursor::SetCaretPosition(SmCaretPos pos, bool moveAnchor){ -+ SmCaretPosGraphIterator it = pGraph->GetIterator(); -+ while(it.Next()){ -+ if(it->CaretPos == pos){ -+ position = it.Current(); -+ if(moveAnchor) -+ anchor = it.Current(); -+ return true; -+ } -+ } -+ return false; -+} -+ -+void SmCursor::AnnotateSelection(){ -+ //TODO: Manage a state, reset it upon modification and optimize this call -+ SetSelectionVisitor SSV(anchor->CaretPos, position->CaretPos); -+ pTree->Accept(&SSV); -+} -+ -+void SmCursor::Draw(OutputDevice& pDev, Point Offset){ -+ SmCaretDrawingVisitor(pDev, GetPosition(), Offset); -+} -+ -+void SmCursor::Delete(){ -+ //Return if we don't have a selection to delete -+ if(!HasSelection()) -+ return; -+ -+ //Enter edit setion -+ BeginEdit(); -+ -+ //Set selected on nodes -+ AnnotateSelection(); -+ -+ //Find an arbitrary selected node -+ SmNode* pSNode = FindSelectedNode(pTree); -+ j_assert(pSNode != NULL, "There must be a selection when HasSelection is true!"); -+ -+ //Find the topmost node of the line that holds the selection -+ SmNode* pLine = FindTopMostNodeInLine(pSNode, true); -+ -+ //Get the parent of the line -+ SmStructureNode* pLineParent = pLine->GetParent(); -+ //Find line offset in parent -+ int nLineOffset = pLineParent->IndexOfSubNode(pLine); -+ j_assert(nLineOffset != -1, "pLine must be a child of it's parent!"); -+ -+ //Position after delete -+ SmCaretPos PosAfterDelete; -+ -+ SmNodeList* pLineList; -+ if(IsLineCompositionNode(pLine)) -+ pLineList = LineToList((SmStructureNode*)pLine); -+ else { -+ pLineList = new SmNodeList(); -+ pLineList->push_back(pLine); -+ } -+ -+ //Take the selected nodes and delete them... -+ SmNodeList::iterator patchIt = TakeSelectedNodesFromList(pLineList); -+ -+ //Get teh position to set after delete -+ PosAfterDelete = PatchLineList(pLineList, patchIt); -+ -+ //Parse list of nodes to a tree -+ SmNodeListParser parser; -+ pLine = parser.Parse(pLineList); -+ delete pLineList; -+ -+ //Insert it back into the parent -+ pLineParent->SetSubNode(nLineOffset, pLine); -+ -+ //Rebuild graph of caret position -+ anchor = NULL; -+ position = NULL; -+ BuildGraph(); -+ AnnotateSelection(); //Update selection annotation! -+ -+ //Set caret position -+ if(!SetCaretPosition(PosAfterDelete, true)) -+ SetCaretPosition(SmCaretPos(pLine, 0), true); -+ -+ //End edit section -+ EndEdit(); -+} -+ -+void SmCursor::InsertNodes(SmNodeList* pNewNodes){ -+ if(pNewNodes->size() == 0){ -+ delete pNewNodes; -+ return; -+ } -+ -+ //Begin edit section -+ BeginEdit(); -+ -+ //Position after insert should be after pNewNode -+ SmCaretPos PosAfterInsert = SmCaretPos(pNewNodes->back(), 1); -+ -+ //Get the current position -+ const SmCaretPos pos = position->CaretPos; -+ -+ //Find top most of line that holds position -+ SmNode* pLine = FindTopMostNodeInLine(pos.pSelectedNode, false); -+ -+ //Find line parent and line index in parent -+ SmStructureNode* pLineParent = pLine->GetParent(); -+ int nParentIndex = pLineParent->IndexOfSubNode(pLine); -+ j_assert(nParentIndex != -1, "pLine must be a subnode of pLineParent!"); -+ -+ //Convert line to list -+ SmNodeList* pLineList; -+ if(IsLineCompositionNode(pLine)) -+ pLineList = LineToList((SmStructureNode*)pLine); -+ else { -+ pLineList = new SmNodeList(); -+ pLineList->push_front(pLine); -+ } -+ -+ //Find iterator for place to insert nodes -+ SmNodeList::iterator it = FindPositionInLineList(pLineList, pos); -+ -+ //Insert all new nodes -+ SmNodeList::iterator newIt, patchIt, insIt; -+ for(newIt = pNewNodes->begin(); newIt != pNewNodes->end(); newIt++){ -+ insIt = pLineList->insert(it, *newIt); -+ if(newIt == pNewNodes->begin()) -+ patchIt = insIt; -+ if((*newIt)->GetType() == NTEXT) -+ PosAfterInsert = SmCaretPos(*newIt, ((SmTextNode*)*newIt)->GetText().Len()); -+ else -+ PosAfterInsert = SmCaretPos(*newIt, 1); -+ } -+ //Patch the places we've changed stuff -+ PatchLineList(pLineList, patchIt); -+ PosAfterInsert = PatchLineList(pLineList, it); -+ //Release list, we've taken the nodes -+ delete pNewNodes; -+ pNewNodes = NULL; -+ -+ //Parse line -+ SmNodeListParser parser; -+ pLine = parser.Parse(pLineList); -+ delete pLineList; -+ -+ //Insert it back into the parent -+ pLineParent->SetSubNode(nParentIndex, pLine); -+ -+ //Rebuild graph of caret position -+ anchor = NULL; -+ position = NULL; -+ BuildGraph(); -+ AnnotateSelection(); //Update selection annotation! -+ -+ //Set caret position -+ if(!SetCaretPosition(PosAfterInsert, true)) -+ SetCaretPosition(SmCaretPos(pLine, 0), true); -+ -+ //End edit section -+ EndEdit(); -+} -+ -+SmNodeList::iterator SmCursor::FindPositionInLineList(SmNodeList* pLineList, SmCaretPos aCaretPos) { -+ //Find iterator for position -+ SmNodeList::iterator it; -+ for(it = pLineList->begin(); it != pLineList->end(); it++){ -+ if(*it == aCaretPos.pSelectedNode){ -+ if((*it)->GetType() == NTEXT){ -+ //Split textnode if needed -+ if(aCaretPos.Index > 0){ -+ SmTextNode* pText = (SmTextNode*)aCaretPos.pSelectedNode; -+ XubString str1 = pText->GetText().Copy(0, aCaretPos.Index); -+ XubString str2 = pText->GetText().Copy(aCaretPos.Index); -+ pText->ChangeText(str1); -+ ++it; -+ //Insert str2 as new text node -+ if(str2.Len() > 0){ -+ SmTextNode* pNewText = new SmTextNode(pText->GetToken(), pText->GetFontDesc()); -+ pNewText->ChangeText(str2); -+ it = pLineList->insert(it, pNewText); -+ } -+ } -+ }else -+ ++it; -+ //it now pointer to the node following pos, so pLineList->insert(it, ...) will insert correctly -+ return it; -+ -+ } -+ } -+ //If we didn't find pSelectedNode, it must be because the caret is infront of the line -+ return pLineList->begin(); -+} -+ -+SmCaretPos SmCursor::PatchLineList(SmNodeList* pLineList, SmNodeList::iterator aIter) { -+ //The nodes we should consider merging -+ SmNode *prev = NULL, -+ *next = NULL; -+ if(aIter != pLineList->end()) -+ next = *aIter; -+ if(aIter != pLineList->begin()) { -+ aIter--; -+ prev = *aIter; -+ aIter++; -+ } -+ -+ //Check if there's textnodes to merge -+ if(prev && next && prev->GetType() == NTEXT && next->GetType() == NTEXT){ -+ SmTextNode *pText = (SmTextNode*)prev, -+ *pOldN = (SmTextNode*)next; -+ SmCaretPos retval(pText, pText->GetText().Len()); -+ String newText; -+ newText += pText->GetText(); -+ newText += pOldN->GetText(); -+ pText->ChangeText(newText); -+ delete pOldN; -+ pLineList->erase(aIter); -+ return retval; -+ } -+ -+ //Check if there's a SmPlaceNode to remove: -+ if(prev && next && prev->GetType() == NPLACE && !SmNodeListParser::IsOperator(next->GetToken())){ -+ aIter--; -+ aIter = pLineList->erase(aIter); -+ delete prev; -+ //Return caret pos infront of aIter -+ if(aIter != pLineList->begin()) -+ aIter--; //Thus find node before aIter -+ if(aIter == pLineList->begin()) -+ return SmCaretPos(); -+ if((*aIter)->GetType() == NTEXT) -+ return SmCaretPos(*aIter, ((SmTextNode*)*aIter)->GetText().Len()); -+ return SmCaretPos(*aIter, 1); -+ } -+ if(prev && next && next->GetType() == NPLACE && !SmNodeListParser::IsOperator(prev->GetToken())){ -+ aIter = pLineList->erase(aIter); -+ delete next; -+ if(prev->GetType() == NTEXT) -+ return SmCaretPos(prev, ((SmTextNode*)prev)->GetText().Len()); -+ return SmCaretPos(prev, 1); -+ } -+ -+ //If we didn't do anything return -+ if(!prev) //return an invalid to indicate we're infront of line -+ return SmCaretPos(); -+ if(prev->GetType() == NTEXT) -+ return SmCaretPos(prev, ((SmTextNode*)prev)->GetText().Len()); -+ return SmCaretPos(prev, 1); -+} -+ -+SmNodeList::iterator SmCursor::TakeSelectedNodesFromList(SmNodeList *pLineList, -+ SmNodeList *pSelectedNodes) { -+ SmNodeList::iterator retval; -+ SmNodeList::iterator it = pLineList->begin(); -+ while(it != pLineList->end()){ -+ if((*it)->IsSelected()){ -+ //Split text nodes -+ if((*it)->GetType() == NTEXT) { -+ SmTextNode* pText = (SmTextNode*)*it; -+ String aText = pText->GetText(); -+ //Start and lengths of the segments, 2 is the selected segment -+ int start1 = 0, -+ start2 = pText->GetSelectionStart(), -+ start3 = pText->GetSelectionEnd(), -+ len1 = start2 - 0, -+ len2 = start3 - start2, -+ len3 = aText.Len() - start3; -+ SmToken aToken = pText->GetToken(); -+ USHORT eFontDesc = pText->GetFontDesc(); -+ //If we need make segment 1 -+ if(len1 > 0) { -+ String str = aText.Copy(start1, len1); -+ pText->ChangeText(str); -+ it++; -+ } else {//Remove it if not needed -+ it = pLineList->erase(it); -+ delete pText; -+ } -+ //Set retval to be right after the selection -+ retval = it; -+ //if we need make segment 3 -+ if(len3 > 0) { -+ String str = aText.Copy(start3, len3); -+ SmTextNode* pSeg3 = new SmTextNode(aToken, eFontDesc); -+ pSeg3->ChangeText(str); -+ retval = pLineList->insert(it, pSeg3); -+ } -+ //If we need to save the selected text -+ if(pSelectedNodes && len2 > 0) { -+ String str = aText.Copy(start2, len2); -+ SmTextNode* pSeg2 = new SmTextNode(aToken, eFontDesc); -+ pSeg2->ChangeText(str); -+ pSelectedNodes->push_back(pSeg2); -+ } -+ } else { //if it's not textnode -+ SmNode* pNode = *it; -+ retval = it = pLineList->erase(it); -+ if(pSelectedNodes) -+ pSelectedNodes->push_back(pNode); -+ else -+ delete pNode; -+ } -+ } else -+ it++; -+ } -+ return retval; -+} -+ -+void SmCursor::InsertSubSup(SmSubSup eSubSup) { -+ AnnotateSelection(); -+ -+ //Find line -+ SmNode *pLine; -+ if(HasSelection()) { -+ SmNode *pSNode = FindSelectedNode(pTree); -+ j_assert(pSNode != NULL, "There must be a selected node when HasSelection is true!"); -+ pLine = FindTopMostNodeInLine(pSNode, TRUE); -+ } else -+ pLine = FindTopMostNodeInLine(position->CaretPos.pSelectedNode, FALSE); -+ -+ //Find Parent and offset in parent -+ SmStructureNode *pLineParent = pLine->GetParent(); -+ int nParentIndex = pLineParent->IndexOfSubNode(pLine); -+ j_assert(nParentIndex != -1, "pLine must be a subnode of pLineParent!"); -+ -+ //TODO: Consider handling special cases where parent is an SmOperNode, -+ // Maybe this method should be able to add limits to an SmOperNode... -+ -+ //We begin modifying the tree here -+ BeginEdit(); -+ -+ //Convert line to list -+ SmNodeList* pLineList; -+ if(IsLineCompositionNode(pLine)) -+ pLineList = LineToList((SmStructureNode*)pLine); -+ else { -+ pLineList = new SmNodeList(); -+ pLineList->push_front(pLine); -+ } -+ -+ //Take the selection, and/or find iterator for current position -+ SmNodeList* pSelectedNodesList = new SmNodeList(); -+ SmNodeList::iterator it; -+ if(HasSelection()) -+ it = TakeSelectedNodesFromList(pLineList, pSelectedNodesList); -+ else -+ it = FindPositionInLineList(pLineList, position->CaretPos); -+ -+ //Find node that this should be applied to -+ SmNode* pSubject; -+ BOOL bPatchLine = pSelectedNodesList->size() > 0; //If the line should be patched later -+ if(it != pLineList->begin()) { -+ it--; -+ pSubject = *it; -+ it++; -+ } else { -+ //Create a new place node -+ pSubject = new SmPlaceNode(); -+ pSubject->Prepare(pDocShell->GetFormat(), *pDocShell); -+ it = pLineList->insert(it, pSubject); -+ it++; -+ bPatchLine = TRUE; //We've modified the line it should be patched later. -+ } -+ -+ //Wrap the subject in a SmSubSupNode -+ SmSubSupNode* pSubSup; -+ if(pSubject->GetType() != NSUBSUP){ -+ SmToken token; -+ token.nGroup = TGPOWER; -+ pSubSup = new SmSubSupNode(token); -+ pSubSup->SetBody(pSubject); -+ *(--it) = pSubSup; -+ it++; -+ }else -+ pSubSup = (SmSubSupNode*)pSubject; -+ //pSubject shouldn't be referenced anymore, pSubSup is the SmSubSupNode in pLineList we wish to edit. -+ //and it pointer to the element following pSubSup in pLineList. -+ pSubject = NULL; -+ -+ //Patch the line if we noted that was needed previously -+ if(bPatchLine) -+ PatchLineList(pLineList, it); -+ -+ //Convert existing, if any, sub-/superscript line to list -+ SmNode *pScriptLine = pSubSup->GetSubSup(eSubSup); -+ SmNodeList* pScriptLineList; -+ if(pScriptLine && IsLineCompositionNode(pScriptLine)) -+ pScriptLineList = LineToList((SmStructureNode*)pScriptLine); -+ else{ -+ pScriptLineList = new SmNodeList(); -+ if(pScriptLine) -+ pScriptLineList->push_front(pScriptLine); -+ } -+ -+ //Add selection to pScriptLineList -+ unsigned int nOldSize = pScriptLineList->size(); -+ pScriptLineList->insert(pScriptLineList->end(), pSelectedNodesList->begin(), pSelectedNodesList->end()); -+ delete pSelectedNodesList; -+ pSelectedNodesList = NULL; -+ -+ //Patch pScriptLineList if needed -+ if(0 < nOldSize && nOldSize < pScriptLineList->size()) { -+ SmNodeList::iterator iPatchPoint = pScriptLineList->begin(); -+ std::advance(iPatchPoint, nOldSize); -+ PatchLineList(pScriptLineList, iPatchPoint); -+ } -+ -+ //Find caret pos, that should be used after sub-/superscription. -+ SmCaretPos PosAfterScript; //Leave invalid for first position -+ if(pScriptLineList->size() > 0) -+ PosAfterScript = SmCaretPos::GetPosAfter(pScriptLineList->back()); -+ -+ //Parse pScriptLineList -+ pScriptLine = SmNodeListParser().Parse(pScriptLineList); -+ delete pScriptLineList; -+ pScriptLineList = NULL; -+ -+ //Insert pScriptLine back into the tree -+ pSubSup->SetSubSup(eSubSup, pScriptLine); -+ -+ //Parse pLineList -+ pLine = SmNodeListParser().Parse(pLineList); -+ delete pLineList; -+ pLineList = NULL; -+ -+ //Insert pLineList back into the tree -+ pLineParent->SetSubNode(nParentIndex, pLine); -+ -+ //Rebuild graph of caret positions -+ anchor = NULL; -+ position = NULL; -+ BuildGraph(); -+ AnnotateSelection(); -+ -+ //Set caret position -+ if(!SetCaretPosition(PosAfterScript, true)) -+ SetCaretPosition(SmCaretPos(pScriptLine, 0), true); -+ -+ EndEdit(); -+} -+ -+BOOL SmCursor::InsertLimit(SmSubSup eSubSup, BOOL bMoveCaret) { -+ //Find a subject to set limits on -+ SmOperNode *pSubject = NULL; -+ //Check if pSelectedNode might be a subject -+ if(position->CaretPos.pSelectedNode->GetType() == NOPER) -+ pSubject = (SmOperNode*)position->CaretPos.pSelectedNode; -+ else { -+ //If not, check if parent of the current line is a SmOperNode -+ SmNode *pLineNode = FindTopMostNodeInLine(position->CaretPos.pSelectedNode, FALSE); -+ if(pLineNode->GetParent() && pLineNode->GetParent()->GetType() == NOPER) -+ pSubject = (SmOperNode*)pLineNode->GetParent(); -+ } -+ -+ //Abort operation if we're not in the appropriate context -+ if(!pSubject) -+ return FALSE; -+ -+ BeginEdit(); -+ -+ //Find the sub sup node -+ SmSubSupNode *pSubSup = NULL; -+ //Check if there's already one there... -+ if(pSubject->GetSubNode(0)->GetType() == NSUBSUP) -+ pSubSup = (SmSubSupNode*)pSubject->GetSubNode(0); -+ else { //if not create a new SmSubSupNode -+ SmToken token; -+ token.nGroup = TGLIMIT; -+ pSubSup = new SmSubSupNode(token); -+ //Set it's body -+ pSubSup->SetBody(pSubject->GetSubNode(0)); -+ //Replace the operation of the SmOperNode -+ pSubject->SetSubNode(0, pSubSup); -+ } -+ -+ //Create the limit, if needed -+ SmCaretPos PosAfterLimit; -+ SmNode *pLine; -+ if(!pSubSup->GetSubSup(eSubSup)){ -+ pLine = new SmPlaceNode(); -+ pSubSup->SetSubSup(eSubSup, pLine); -+ PosAfterLimit = SmCaretPos(pLine, 1); -+ //If it's already there... let's move the caret -+ } else if(bMoveCaret){ -+ pLine = pSubSup->GetSubSup(eSubSup); -+ SmNodeList* pLineList; -+ if(IsLineCompositionNode(pLine)) -+ pLineList = LineToList((SmStructureNode*)pLine); -+ else { -+ pLineList = new SmNodeList(); -+ pLineList->push_front(pLine); -+ } -+ if(pLineList->size() > 0) -+ PosAfterLimit = SmCaretPos::GetPosAfter(pLineList->back()); -+ pLine = SmNodeListParser().Parse(pLineList); -+ delete pLineList; -+ pSubSup->SetSubSup(eSubSup, pLine); -+ } -+ -+ //Rebuild graph of caret positions -+ BuildGraph(); -+ AnnotateSelection(); -+ -+ //Set caret position -+ if(bMoveCaret) -+ if(!SetCaretPosition(PosAfterLimit, true)) -+ SetCaretPosition(SmCaretPos(pLine, 0), true); -+ -+ EndEdit(); -+ -+ return TRUE; -+} -+ -+void SmCursor::InsertBrackets(SmBracketType eBracketType) { -+ BeginEdit(); -+ -+ AnnotateSelection(); -+ -+ //Find line -+ SmNode *pLine; -+ if(HasSelection()) { -+ SmNode *pSNode = FindSelectedNode(pTree); -+ j_assert(pSNode != NULL, "There must be a selected node if HasSelection()"); -+ pLine = FindTopMostNodeInLine(pSNode, TRUE); -+ } else -+ pLine = FindTopMostNodeInLine(position->CaretPos.pSelectedNode, FALSE); -+ -+ //Find parent and offset in parent -+ SmStructureNode *pLineParent = pLine->GetParent(); -+ int nParentIndex = pLineParent->IndexOfSubNode(pLine); -+ j_assert( nParentIndex != -1, "pLine must be a subnode of pLineParent!"); -+ -+ //Convert line to list -+ SmNodeList *pLineList; -+ if(IsLineCompositionNode(pLine)) -+ pLineList = LineToList((SmStructureNode*)pLine); -+ else { -+ pLineList = new SmNodeList(); -+ pLineList->push_front(pLine); -+ } -+ -+ //Take the selection, and/or find iterator for current position -+ SmNodeList *pSelectedNodesList = new SmNodeList(); -+ SmNodeList::iterator it; -+ if(HasSelection()) -+ it = TakeSelectedNodesFromList(pLineList, pSelectedNodesList); -+ else -+ it = FindPositionInLineList(pLineList, position->CaretPos); -+ -+ //If there's no selected nodes, create a place node -+ if(pSelectedNodesList->size() == 0) -+ pSelectedNodesList->push_front(new SmPlaceNode()); -+ -+ //Parse body nodes -+ SmNode *pBodyNode = SmNodeListParser().Parse(pSelectedNodesList); -+ delete pSelectedNodesList; -+ -+ //Create SmBraceNode -+ SmToken aTok(TLEFT, '\0', "left", 0, 5); -+ SmBraceNode *pBrace = new SmBraceNode(aTok); -+ pBrace->SetScaleMode(SCALE_HEIGHT); -+ SmNode *pLeft = CreateBracket(eBracketType, true), -+ *pRight = CreateBracket(eBracketType, false); -+ SmBracebodyNode *pBody = new SmBracebodyNode(SmToken()); -+ pBody->SetSubNodes(pBodyNode, NULL); -+ pBrace->SetSubNodes(pLeft, pBody, pRight); -+ pBrace->Prepare(pDocShell->GetFormat(), *pDocShell); -+ -+ //Insert into line -+ pLineList->insert(it, pBrace); -+ //Patch line (I think this is good enough) -+ SmCaretPos PosAfterInsert = PatchLineList(pLineList, it); -+ -+ //Parse line -+ pLine = SmNodeListParser().Parse(pLineList); -+ delete pLineList; -+ -+ //Insert line back into tree -+ pLineParent->SetSubNode(nParentIndex, pLine); -+ -+ //Rebuild graph of caret positions -+ anchor = NULL; -+ position = NULL; -+ BuildGraph(); -+ AnnotateSelection(); -+ -+ //Set caret position -+ if(!SetCaretPosition(PosAfterInsert, true)) -+ SetCaretPosition(SmCaretPos(pLine, 0), true); -+ -+ EndEdit(); -+} -+ -+SmNode *SmCursor::CreateBracket(SmBracketType eBracketType, BOOL bIsLeft) { -+ SmToken aTok; -+ if(bIsLeft){ -+ switch(eBracketType){ -+ case NoneBrackets: -+ aTok = SmToken(TNONE, '\0', "none", TGLBRACES | TGRBRACES, 0); -+ break; -+ case RoundBrackets: -+ aTok = SmToken(TLPARENT, MS_LPARENT, "(", TGLBRACES, 5); -+ break; -+ case SquareBrackets: -+ aTok = SmToken(TLBRACKET, MS_LBRACKET, "[", TGLBRACES, 5); -+ break; -+ case DoubleSquareBrackets: -+ aTok = SmToken(TLDBRACKET, MS_LDBRACKET, "ldbracket", TGLBRACES, 5); -+ break; -+ case LineBrackets: -+ aTok = SmToken(TLLINE, MS_LINE, "lline", TGLBRACES, 5); -+ break; -+ case DoubleLineBrackets: -+ aTok = SmToken(TLDLINE, MS_DLINE, "ldline", TGLBRACES, 5); -+ break; -+ case CurlyBrackets: -+ aTok = SmToken(TLBRACE, MS_LBRACE, "lbrace", TGLBRACES, 5); -+ break; -+ case AngleBrackets: -+ aTok = SmToken(TLANGLE, MS_LANGLE, "langle", TGLBRACES, 5); -+ break; -+ case CeilBrackets: -+ aTok = SmToken(TLCEIL, MS_LCEIL, "lceil", TGLBRACES, 5); -+ break; -+ case FloorBrackets: -+ aTok = SmToken(TLFLOOR, MS_LFLOOR, "lfloor", TGLBRACES, 5); -+ break; -+ } -+ } else { -+ switch(eBracketType) { -+ case NoneBrackets: -+ aTok = SmToken(TNONE, '\0', "none", TGLBRACES | TGRBRACES, 0); -+ break; -+ case RoundBrackets: -+ aTok = SmToken(TRPARENT, MS_RPARENT, ")", TGRBRACES, 5); -+ break; -+ case SquareBrackets: -+ aTok = SmToken(TRBRACKET, MS_RBRACKET, "]", TGRBRACES, 5); -+ break; -+ case DoubleSquareBrackets: -+ aTok = SmToken(TRDBRACKET, MS_RDBRACKET, "rdbracket", TGRBRACES, 5); -+ break; -+ case LineBrackets: -+ aTok = SmToken(TRLINE, MS_LINE, "rline", TGRBRACES, 5); -+ break; -+ case DoubleLineBrackets: -+ aTok = SmToken(TRDLINE, MS_DLINE, "rdline", TGRBRACES, 5); -+ break; -+ case CurlyBrackets: -+ aTok = SmToken(TRBRACE, MS_RBRACE, "rbrace", TGRBRACES, 5); -+ break; -+ case AngleBrackets: -+ aTok = SmToken(TRANGLE, MS_RANGLE, "rangle", TGRBRACES, 5); -+ break; -+ case CeilBrackets: -+ aTok = SmToken(TRCEIL, MS_RCEIL, "rceil", TGRBRACES, 5); -+ break; -+ case FloorBrackets: -+ aTok = SmToken(TRFLOOR, MS_RFLOOR, "rfloor", TGRBRACES, 5); -+ break; -+ } -+ } -+ SmNode* pRetVal = new SmMathSymbolNode(aTok); -+ pRetVal->SetScaleMode(SCALE_HEIGHT); -+ return pRetVal; -+} -+ -+BOOL SmCursor::InsertRow() { -+ AnnotateSelection(); -+ -+ //Find line -+ SmNode *pLine; -+ if(HasSelection()) { -+ SmNode *pSNode = FindSelectedNode(pTree); -+ j_assert(pSNode != NULL, "There must be a selected node if HasSelection()"); -+ pLine = FindTopMostNodeInLine(pSNode, TRUE); -+ } else -+ pLine = FindTopMostNodeInLine(position->CaretPos.pSelectedNode, FALSE); -+ -+ //Find parent and offset in parent -+ SmStructureNode *pLineParent = pLine->GetParent(); -+ int nParentIndex = pLineParent->IndexOfSubNode(pLine); -+ j_assert( nParentIndex != -1, "pLine must be a subnode of pLineParent!"); -+ -+ //Discover the context of this command -+ SmTableNode *pTable = NULL; -+ SmMatrixNode *pMatrix = NULL; -+ int nTableIndex = nParentIndex; -+ if(pLineParent->GetType() == NTABLE) -+ pTable = (SmTableNode*)pLineParent; -+ //If it's warped in a SmLineNode, we can still insert a newline -+ else if(pLineParent->GetType() == NLINE && -+ pLineParent->GetParent() && -+ pLineParent->GetParent()->GetType() == NTABLE) { -+ //NOTE: This hack might give problems if we stop ignoring SmAlignNode -+ pTable = (SmTableNode*)pLineParent->GetParent(); -+ nTableIndex = pTable->IndexOfSubNode(pLineParent); -+ j_assert(nTableIndex != -1, "pLineParent must be a child of its parent!"); -+ } -+ if(pLineParent->GetType() == NMATRIX) -+ pMatrix = (SmMatrixNode*)pLineParent; -+ -+ //If we're not in a context that supports InsertRow, return FALSE -+ if(!pTable && !pMatrix) -+ return FALSE; -+ -+ //Now we start editing -+ BeginEdit(); -+ -+ //Convert line to list -+ SmNodeList *pLineList; -+ if(IsLineCompositionNode(pLine)) -+ pLineList = LineToList((SmStructureNode*)pLine); -+ else { -+ pLineList = new SmNodeList(); -+ pLineList->push_front(pLine); -+ } -+ -+ //Find position in line -+ SmNodeList::iterator it; -+ if(HasSelection()) { -+ //Take the selected nodes and delete them... -+ it = TakeSelectedNodesFromList(pLineList); -+ } else -+ it = FindPositionInLineList(pLineList, position->CaretPos); -+ -+ //New caret position after inserting the newline/row in whatever context -+ SmCaretPos PosAfterInsert; -+ -+ //If we're in the context of a table -+ if(pTable) { -+ SmNodeList *pNewLineList = new SmNodeList(); -+ //Move elements from pLineList to pNewLineList -+ pNewLineList->splice(pNewLineList->begin(), *pLineList, it, pLineList->end()); -+ //Make sure it is valid again -+ it = pLineList->end(); -+ if(it != pLineList->begin()) -+ it--; -+ if(pNewLineList->size() == 0) -+ pNewLineList->push_front(new SmPlaceNode()); -+ //Parse new line -+ SmNode *pNewLine = SmNodeListParser().Parse(pNewLineList); -+ delete pNewLineList; -+ //Get position before we wrap in SmLineNode -+ //NOTE: This should be done after, if SmLineNode ever becomes a line composition node -+ PosAfterInsert = SmCaretPos(pNewLine, 0); -+ //Wrap pNewLine in SmLineNode if needed -+ if(pLineParent->GetType() == NLINE) { -+ SmLineNode *pNewLineNode = new SmLineNode(SmToken(TNEWLINE, '\0', "newline")); -+ pNewLineNode->SetSubNodes(pNewLine, NULL); -+ pNewLine = pNewLineNode; -+ } -+ //Move other nodes if needed -+ for( int i = pTable->GetNumSubNodes(); i > nTableIndex + 1; i--) -+ pTable->SetSubNode(i, pTable->GetSubNode(i-1)); -+ //Insert new line -+ pTable->SetSubNode(nTableIndex + 1, pNewLine); -+ //Check if we need to change token type: -+ if(pTable->GetNumSubNodes() > 2 && pTable->GetToken().eType == TBINOM) { -+ SmToken tok = pTable->GetToken(); -+ tok.eType = TSTACK; -+ pTable->SetToken(tok); -+ } -+ } -+ //If we're in the context of a matrix -+ else if(pMatrix) { -+ //Find position after insert and patch the list -+ PosAfterInsert = PatchLineList(pLineList, it); -+ //Move other children -+ USHORT rows = pMatrix->GetNumRows(); -+ USHORT cols = pMatrix->GetNumCols(); -+ int nRowStart = (nParentIndex - nParentIndex % cols) + cols; -+ for( int i = pMatrix->GetNumSubNodes() + cols - 1; i >= nRowStart + cols; i--) -+ pMatrix->SetSubNode(i, pMatrix->GetSubNode(i - cols)); -+ for( int i = nRowStart; i < nRowStart + cols; i++) { -+ SmPlaceNode *pNewLine = new SmPlaceNode(); -+ if(i == nParentIndex + cols) -+ PosAfterInsert = SmCaretPos(pNewLine, 0); -+ pMatrix->SetSubNode(i, pNewLine); -+ } -+ pMatrix->SetRowCol(rows + 1, cols); -+ } else -+ j_assert(FALSE, "We must be either the context of a table or matrix!"); -+ -+ //Parse and put line back where it came from... -+ pLine = SmNodeListParser().Parse(pLineList); -+ delete pLineList; -+ pLineList = NULL; -+ pLineParent->SetSubNode(nParentIndex, pLine); -+ -+ //Rebuild graph of caret positions -+ anchor = NULL; -+ position = NULL; -+ BuildGraph(); -+ AnnotateSelection(); -+ -+ //Set caret position -+ if(!SetCaretPosition(PosAfterInsert, true)) -+ SetCaretPosition(SmCaretPos(pLine, 0), true); -+ -+ EndEdit(); -+ return TRUE; -+} -+ -+void SmCursor::InsertFraction() { -+ AnnotateSelection(); -+ -+ //Find line -+ SmNode *pLine; -+ if(HasSelection()) { -+ SmNode *pSNode = FindSelectedNode(pTree); -+ j_assert(pSNode != NULL, "There must be a selected node when HasSelection is true!"); -+ pLine = FindTopMostNodeInLine(pSNode, TRUE); -+ } else -+ pLine = FindTopMostNodeInLine(position->CaretPos.pSelectedNode, FALSE); -+ -+ //Find Parent and offset in parent -+ SmStructureNode *pLineParent = pLine->GetParent(); -+ int nParentIndex = pLineParent->IndexOfSubNode(pLine); -+ j_assert(nParentIndex != -1, "pLine must be a subnode of pLineParent!"); -+ -+ //We begin modifying the tree here -+ BeginEdit(); -+ -+ //Convert line to list -+ SmNodeList* pLineList; -+ if(IsLineCompositionNode(pLine)) -+ pLineList = LineToList((SmStructureNode*)pLine); -+ else { -+ pLineList = new SmNodeList(); -+ pLineList->push_front(pLine); -+ } -+ -+ //Take the selection, and/or find iterator for current position -+ SmNodeList* pSelectedNodesList = new SmNodeList(); -+ SmNodeList::iterator it; -+ if(HasSelection()) -+ it = TakeSelectedNodesFromList(pLineList, pSelectedNodesList); -+ else -+ it = FindPositionInLineList(pLineList, position->CaretPos); -+ -+ //Create pNum, and pDenom -+ if(pSelectedNodesList->size() == 0) -+ pSelectedNodesList->push_front(new SmPlaceNode()); -+ SmNode *pNum = SmNodeListParser().Parse(pSelectedNodesList), -+ *pDenom = new SmPlaceNode(); -+ delete pSelectedNodesList; -+ pSelectedNodesList = NULL; -+ -+ //Create new fraction -+ SmBinVerNode *pFrac = new SmBinVerNode(SmToken(TOVER, '\0', "over", TGPRODUCT, 0)); -+ SmNode *pRect = new SmRectangleNode(SmToken()); -+ pFrac->SetSubNodes(pNum, pRect, pDenom); -+ -+ //Insert in pLineList -+ SmNodeList::iterator patchIt = pLineList->insert(it, pFrac); -+ PatchLineList(pLineList, patchIt); -+ PatchLineList(pLineList, it); -+ -+ //Parse the line -+ pLine = SmNodeListParser().Parse(pLineList); -+ delete pLineList; -+ -+ //Insert pLine back into parent -+ pLineParent->SetSubNode(nParentIndex, pLine); -+ -+ //Rebuild graph of caret position -+ anchor = NULL; -+ position = NULL; -+ BuildGraph(); -+ AnnotateSelection(); //Update selection annotation! -+ -+ //Set caret position -+ if(!SetCaretPosition(SmCaretPos(pDenom, 1), true)) -+ SetCaretPosition(SmCaretPos(pLine, 0), true); -+ -+ EndEdit(); -+} -+ -+ -+void SmCursor::InsertText(XubString aString){ -+ BeginEdit(); -+ -+ Delete(); -+ -+ //TODO: Use other values than FNT_VARIABLE for numbers and functions -+ -+ SmToken token; -+ token.eType = TIDENT; -+ token.cMathChar = '\0'; -+ token.nGroup = 0; -+ token.nLevel = 5; -+ token.aText = aString; -+ -+ SmTextNode* pText = new SmTextNode(token, FNT_VARIABLE); -+ -+ //Prepare the new node -+ pText->Prepare(pDocShell->GetFormat(), *pDocShell); -+ -+ SmNodeList* pList = new SmNodeList(); -+ pList->push_front(pText); -+ InsertNodes(pList); -+ -+ EndEdit(); -+} -+ -+void SmCursor::InsertElement(SmFormulaElement element){ -+ BeginEdit(); -+ -+ Delete(); -+ -+ //Create new node -+ SmNode* pNewNode = NULL; -+ switch(element){ -+ case BlankElement: -+ { -+ SmToken token; -+ token.nGroup = TGBLANK; -+ token.aText.AssignAscii("~"); -+ pNewNode = new SmBlankNode(token); -+ }break; -+ case FactorialElement: -+ { -+ SmToken token(TFACT, MS_FACT, "fact", TGUNOPER, 5); -+ pNewNode = new SmMathSymbolNode(token); -+ }break; -+ case PlusElement: -+ { -+ SmToken token; -+ token.eType = TPLUS; -+ token.cMathChar = MS_PLUS; -+ token.nGroup = TGUNOPER | TGSUM; -+ token.nLevel = 5; -+ token.aText.AssignAscii("+"); -+ pNewNode = new SmMathSymbolNode(token); -+ }break; -+ case MinusElement: -+ { -+ SmToken token; -+ token.eType = TMINUS; -+ token.cMathChar = MS_MINUS; -+ token.nGroup = MS_PLUS; -+ token.nLevel = 5; -+ token.aText.AssignAscii("-"); -+ pNewNode = new SmMathSymbolNode(token); -+ }break; -+ case CDotElement: -+ { -+ SmToken token; -+ token.eType = TCDOT; -+ token.cMathChar = MS_CDOT; -+ token.nGroup = TGPRODUCT; -+ token.aText.AssignAscii("cdot"); -+ pNewNode = new SmMathSymbolNode(token); -+ }break; -+ case EqualElement: -+ { -+ SmToken token; -+ token.eType = TASSIGN; -+ token.cMathChar = MS_ASSIGN; -+ token.nGroup = TGRELATION; -+ token.aText.AssignAscii("="); -+ pNewNode = new SmMathSymbolNode(token); -+ }break; -+ case LessThanElement: -+ { -+ SmToken token; -+ token.eType = TLT; -+ token.cMathChar = MS_LT; -+ token.nGroup = TGRELATION; -+ token.aText.AssignAscii("<"); -+ pNewNode = new SmMathSymbolNode(token); -+ }break; -+ case GreaterThanElement: -+ { -+ SmToken token; -+ token.eType = TGT; -+ token.cMathChar = MS_GT; -+ token.nGroup = TGRELATION; -+ token.aText.AssignAscii(">"); -+ pNewNode = new SmMathSymbolNode(token); -+ }break; -+ default: -+ j_assert(false, "Element unknown!"); -+ } -+ j_assert(pNewNode != NULL, "No new node was created!"); -+ if(!pNewNode) -+ return; -+ -+ //Prepare the new node -+ pNewNode->Prepare(pDocShell->GetFormat(), *pDocShell); -+ -+ //Insert new node -+ SmNodeList* pList = new SmNodeList(); -+ pList->push_front(pNewNode); -+ InsertNodes(pList); -+ -+ EndEdit(); -+} -+ -+void SmCursor::InsertSpecial(XubString aString) { -+ BeginEdit(); -+ Delete(); -+ -+ aString.EraseLeadingAndTrailingChars(); -+ aString.EraseLeadingChars('%'); -+ -+ //Create instance of special node -+ SmToken token; -+ token.eType = TSPECIAL; -+ token.cMathChar = '\0'; -+ token.nGroup = 0; -+ token.nLevel = 5; -+ token.aText = aString; //Don't know if leading "%" should be removed -+ SmSpecialNode* pSpecial = new SmSpecialNode(token); -+ -+ //Prepare the special node -+ pSpecial->Prepare(pDocShell->GetFormat(), *pDocShell); -+ -+ //Insert the node -+ SmNodeList* pList = new SmNodeList(); -+ pList->push_front(pSpecial); -+ InsertNodes(pList); -+ -+ EndEdit(); -+} -+ -+void SmCursor::InsertCommand(USHORT nCommand) { -+ switch(nCommand){ -+ case RID_NEWLINE: -+ InsertRow(); -+ break; -+ case RID_FROMX: -+ InsertLimit(CSUB, TRUE); -+ break; -+ case RID_TOX: -+ InsertLimit(CSUP, TRUE); -+ break; -+ case RID_FROMXTOY: -+ if(InsertLimit(CSUB, FALSE)) -+ InsertLimit(CSUP, TRUE); -+ break; -+ default: -+ InsertCommandText(SmResId(nCommand)); -+ break; -+ } -+} -+ -+void SmCursor::InsertCommandText(XubString aCommandText) { -+ //Parse the the sub expression -+ SmNode* pSubExpr = SmParser().ParseExpression(aCommandText); -+ -+ //Prepare the subtree -+ pSubExpr->Prepare(pDocShell->GetFormat(), *pDocShell); -+ -+ //Convert subtree to list -+ SmNodeList* pLineList; -+ if(IsLineCompositionNode(pSubExpr)) -+ pLineList = LineToList((SmStructureNode*)pSubExpr); -+ else { -+ pLineList = new SmNodeList(); -+ pLineList->push_front(pSubExpr); -+ } -+ -+ BeginEdit(); -+ -+ //Delete any selection -+ Delete(); -+ -+ //Insert it -+ InsertNodes(pLineList); -+ -+ EndEdit(); -+} -+ -+void SmCursor::Copy(){ -+ if(!HasSelection()) -+ return; -+ -+ //Find selected node -+ SmNode* pSNode = FindSelectedNode(pTree); -+ //Find visual line -+ SmNode* pLine = FindTopMostNodeInLine(pSNode, true); -+ -+ //Clone selected nodes -+ SmNodeList* pList; -+ if(IsLineCompositionNode(pLine)) -+ pList = CloneLineToList((SmStructureNode*)pLine, true); -+ else{ -+ pList = new SmNodeList(); -+ //Special care to only clone selected text -+ if(pLine->GetType() == NTEXT) { -+ SmTextNode *pText = (SmTextNode*)pLine; -+ SmTextNode *pClone = new SmTextNode( pText->GetToken(), pText->GetFontDesc() ); -+ int start = pText->GetSelectionStart(), -+ length = pText->GetSelectionEnd() - pText->GetSelectionStart(); -+ pClone->ChangeText(pText->GetText().Copy(start, length)); -+ pClone->SetScaleMode(pText->GetScaleMode()); -+ pList->push_front(pClone); -+ } else { -+ SmCloningVisitor aCloneFactory; -+ pList->push_front(aCloneFactory.Clone(pLine)); -+ } -+ } -+ -+ //Set clipboard -+ if(pList->size() > 0) -+ SetClipboard(pList); -+} -+ -+void SmCursor::Paste() { -+ BeginEdit(); -+ Delete(); -+ -+ if(pClipboard && pClipboard->size() > 0) -+ InsertNodes(CloneList(pClipboard)); -+ -+ EndEdit(); -+} -+ -+SmNodeList* SmCursor::CloneList(SmNodeList* pList){ -+ SmCloningVisitor aCloneFactory; -+ SmNodeList* pClones = new SmNodeList(); -+ -+ SmNodeList::iterator it; -+ for(it = pList->begin(); it != pList->end(); it++){ -+ SmNode *pClone = aCloneFactory.Clone(*it); -+ pClones->push_back(pClone); -+ } -+ -+ return pClones; -+} -+ -+ -+void SmCursor::SetClipboard(SmNodeList* pList){ -+ if(pClipboard){ -+ //Delete all nodes on the clipboard -+ SmNodeList::iterator it; -+ for(it = pClipboard->begin(); it != pClipboard->end(); it++) -+ delete (*it); -+ delete pClipboard; -+ } -+ pClipboard = pList; -+} -+ -+SmNode* SmCursor::FindTopMostNodeInLine(SmNode* pSNode, bool MoveUpIfSelected){ -+ //If we haven't got a subnode -+ if(!pSNode) -+ return NULL; -+ -+ //Move up parent untill we find a node who's -+ //parent isn't selected and not a type of: -+ // SmExpressionNode -+ // SmBinHorNode -+ // SmUnHorNode -+ // SmAlignNode -+ // SmFontNode -+ while((MoveUpIfSelected && pSNode->GetParent()->IsSelected()) || -+ IsLineCompositionNode(pSNode->GetParent())){ -+ pSNode = pSNode->GetParent(); -+ j_assert(pSNode, "pSNode shouldn't be NULL, have we hit root node if so, this is bad!"); -+ if(!pSNode) //I've got to do something, nothing is probably the best solution :) -+ return NULL; -+ } -+ //Now we have the selection line node -+ return pSNode; -+} -+ -+SmNode* SmCursor::FindSelectedNode(SmNode* pNode){ -+ SmNodeIterator it(pNode); -+ while(it.Next()){ -+ if(it->IsSelected()) -+ return it.Current(); -+ SmNode* pRetVal = FindSelectedNode(it.Current()); -+ if(pRetVal) -+ return pRetVal; -+ } -+ return NULL; -+} -+ -+SmNodeList* SmCursor::LineToList(SmStructureNode* pLine, SmNodeList* list){ -+ SmNodeIterator it(pLine); -+ while(it.Next()){ -+ switch(it->GetType()){ -+ case NUNHOR: -+ case NEXPRESSION: -+ case NBINHOR: -+ case NALIGN: -+ case NFONT: -+ LineToList((SmStructureNode*)it.Current(), list); -+ break; -+ case NERROR: -+ delete it.Current(); -+ break; -+ default: -+ list->push_back(it.Current()); -+ } -+ } -+ SmNodeArray emptyArray(0); -+ pLine->SetSubNodes(emptyArray); -+ delete pLine; -+ return list; -+} -+ -+SmNodeList* SmCursor::CloneLineToList(SmStructureNode* pLine, bool bOnlyIfSelected, SmNodeList* pList){ -+ SmCloningVisitor aCloneFactory; -+ SmNodeIterator it(pLine); -+ while(it.Next()){ -+ if( IsLineCompositionNode( it.Current() ) ) -+ CloneLineToList( (SmStructureNode*)it.Current(), bOnlyIfSelected, pList ); -+ else if( (!bOnlyIfSelected || it->IsSelected()) && it->GetType() != NERROR ) { -+ //Only clone selected text from SmTextNode -+ if(it->GetType() == NTEXT) { -+ SmTextNode *pText = (SmTextNode*)it.Current(); -+ SmTextNode *pClone = new SmTextNode( it->GetToken(), pText->GetFontDesc() ); -+ int start = pText->GetSelectionStart(), -+ length = pText->GetSelectionEnd() - pText->GetSelectionStart(); -+ pClone->ChangeText(pText->GetText().Copy(start, length)); -+ pClone->SetScaleMode(pText->GetScaleMode()); -+ pList->push_back(pClone); -+ } else -+ pList->push_back(aCloneFactory.Clone(it.Current())); -+ } -+ } -+ return pList; -+} -+ -+bool SmCursor::IsLineCompositionNode(SmNode* pNode){ -+ switch(pNode->GetType()){ -+ case NUNHOR: -+ case NEXPRESSION: -+ case NBINHOR: -+ case NALIGN: -+ case NFONT: -+ return true; -+ default: -+ return false; -+ } -+ return false; -+} -+ -+int SmCursor::CountSelectedNodes(SmNode* pNode){ -+ int nCount = 0; -+ SmNodeIterator it(pNode); -+ while(it.Next()){ -+ if(it->IsSelected() && !IsLineCompositionNode(it.Current())) -+ nCount++; -+ nCount += CountSelectedNodes(it.Current()); -+ } -+ return nCount; -+} -+ -+bool SmCursor::HasComplexSelection(){ -+ if(!HasSelection()) -+ return false; -+ AnnotateSelection(); -+ -+ return CountSelectedNodes(pTree) > 1; -+} -+ -+void SmCursor::BeginEdit(){ -+ if(nEditSections++ > 0) return; -+ -+ bIsEnabledSetModifiedSmDocShell = pDocShell->IsEnableSetModified(); -+ if( bIsEnabledSetModifiedSmDocShell ) -+ pDocShell->EnableSetModified( FALSE ); -+} -+ -+void SmCursor::EndEdit(){ -+ if(--nEditSections > 0) return; -+ -+ pDocShell->SetFormulaArranged(FALSE); -+ //Okay, I don't know what this does... :) -+ //It's used in SmDocShell::SetText and with places where everything is modified. -+ //I think it does some magic, with sfx, but everything is totally undocumented so -+ //it's kinda hard to tell... -+ if ( bIsEnabledSetModifiedSmDocShell ) -+ pDocShell->EnableSetModified( bIsEnabledSetModifiedSmDocShell ); -+ //I think this notifies people around us that we've modified this document... -+ pDocShell->SetModified(TRUE); -+ //I think SmDocShell uses this value when it sends an update graphics event -+ //Anyway comments elsewhere suggests it need to be updated... -+ pDocShell->nModifyCount++; -+ -+ //TODO: Consider copying the update accessability code from SmDocShell::SetText in here... -+ //This somehow updates the size of SmGraphicView if it is running in embedded mode -+ if( pDocShell->GetCreateMode() == SFX_CREATE_MODE_EMBEDDED ) -+ pDocShell->OnDocumentPrinterChanged(0); -+ -+ //Request a replaint... -+ RequestRepaint(); -+ -+ //Update the edit engine and text of the document -+ String formula; -+ SmNodeToTextVisitor(pTree, formula); -+ //pTree->CreateTextFromNode(formula); -+ pDocShell->aText = formula; -+ pDocShell->GetEditEngine().SetText(formula); -+} -+ -+void SmCursor::RequestRepaint(){ -+ SmViewShell *pViewSh = SmGetActiveView(); -+ if( pViewSh ) { -+ if ( SFX_CREATE_MODE_EMBEDDED == pDocShell->GetCreateMode() ) -+ pDocShell->Repaint(); -+ else -+ pViewSh->GetGraphicWindow().Invalidate(); -+ } -+} -+ -+/////////////////////////////////////// SmNodeListParser /////////////////////////////////////// -+ -+SmNode* SmNodeListParser::Parse(SmNodeList* list, bool bDeleteErrorNodes){ -+ pList = list; -+ if(bDeleteErrorNodes){ -+ //Delete error nodes -+ SmNodeList::iterator it = pList->begin(); -+ while(it != pList->end()) { -+ if((*it)->GetType() == NERROR){ -+ //Delete and erase -+ delete *it; -+ it = pList->erase(it); -+ }else -+ it++; -+ } -+ } -+ SmNode* retval = Expression(); -+ pList = NULL; -+ return retval; -+} -+ -+SmNode* SmNodeListParser::Expression(){ -+ SmNodeArray NodeArray; -+ //Accept as many relations as there is -+ while(Terminal()) -+ NodeArray.push_back(Relation()); -+ -+ //Create SmExpressionNode, I hope SmToken() will do :) -+ SmStructureNode* pExpr = new SmExpressionNode(SmToken()); -+ pExpr->SetSubNodes(NodeArray); -+ return pExpr; -+} -+ -+SmNode* SmNodeListParser::Relation(){ -+ //Read a sum -+ SmNode* pLeft = Sum(); -+ //While we have tokens and the next is a relation -+ while(Terminal() && IsRelationOperator(Terminal()->GetToken())){ -+ //Take the operator -+ SmNode* pOper = Take(); -+ //Find the right side of the relation -+ SmNode* pRight = Sum(); -+ //Create new SmBinHorNode -+ SmStructureNode* pNewNode = new SmBinHorNode(SmToken()); -+ pNewNode->SetSubNodes(pLeft, pOper, pRight); -+ pLeft = pNewNode; -+ } -+ return pLeft; -+} -+ -+SmNode* SmNodeListParser::Sum(){ -+ //Read a product -+ SmNode* pLeft = Product(); -+ //While we have tokens and the next is a sum -+ while(Terminal() && IsSumOperator(Terminal()->GetToken())){ -+ //Take the operator -+ SmNode* pOper = Take(); -+ //Find the right side of the sum -+ SmNode* pRight = Product(); -+ //Create new SmBinHorNode -+ SmStructureNode* pNewNode = new SmBinHorNode(SmToken()); -+ pNewNode->SetSubNodes(pLeft, pOper, pRight); -+ pLeft = pNewNode; -+ } -+ return pLeft; -+} -+ -+SmNode* SmNodeListParser::Product(){ -+ //Read a Factor -+ SmNode* pLeft = Factor(); -+ //While we have tokens and the next is a product -+ while(Terminal() && IsProductOperator(Terminal()->GetToken())){ -+ //Take the operator -+ SmNode* pOper = Take(); -+ //Find the right side of the operation -+ SmNode* pRight = Factor(); -+ //Create new SmBinHorNode -+ SmStructureNode* pNewNode = new SmBinHorNode(SmToken()); -+ pNewNode->SetSubNodes(pLeft, pOper, pRight); -+ pLeft = pNewNode; -+ } -+ return pLeft; -+} -+ -+SmNode* SmNodeListParser::Factor(){ -+ //Read unary operations -+ if(!Terminal()) -+ return Error(); -+ //Take care of unary operators -+ else if(IsUnaryOperator(Terminal()->GetToken())) -+ { -+ SmStructureNode *pUnary = new SmUnHorNode(SmToken()); -+ SmNode *pOper = Terminal(), -+ *pArg; -+ -+ if(Next()) -+ pArg = Factor(); -+ else -+ pArg = Error(); -+ -+ pUnary->SetSubNodes(pOper, pArg); -+ return pUnary; -+ } -+ return Postfix(); -+} -+ -+SmNode* SmNodeListParser::Postfix(){ -+ if(!Terminal()) -+ return Error(); -+ SmNode *pArg = NULL; -+ if(IsPostfixOperator(Terminal()->GetToken())) -+ pArg = Error(); -+ else if(IsOperator(Terminal()->GetToken())) -+ return Error(); -+ else -+ pArg = Take(); -+ while(Terminal() && IsPostfixOperator(Terminal()->GetToken())) { -+ SmStructureNode *pUnary = new SmUnHorNode(SmToken()); -+ SmNode *pOper = Take(); -+ pUnary->SetSubNodes(pArg, pOper); -+ pArg = pUnary; -+ } -+ return pArg; -+} -+ -+SmNode* SmNodeListParser::Error(){ -+ return new SmErrorNode(PE_UNEXPECTED_TOKEN, SmToken()); -+} -+ -+BOOL SmNodeListParser::IsOperator(const SmToken &token) { -+ return IsRelationOperator(token) || -+ IsSumOperator(token) || -+ IsProductOperator(token) || -+ IsUnaryOperator(token) || -+ IsPostfixOperator(token); -+} -+ -+BOOL SmNodeListParser::IsRelationOperator(const SmToken &token) { -+ return token.nGroup & TGRELATION; -+} -+ -+BOOL SmNodeListParser::IsSumOperator(const SmToken &token) { -+ return token.nGroup & TGSUM; -+} -+ -+BOOL SmNodeListParser::IsProductOperator(const SmToken &token) { -+ return token.nGroup & TGPRODUCT && -+ token.eType != TWIDESLASH && -+ token.eType != TWIDEBACKSLASH && -+ token.eType != TUNDERBRACE && -+ token.eType != TOVERBRACE && -+ token.eType != TOVER; -+} -+ -+BOOL SmNodeListParser::IsUnaryOperator(const SmToken &token) { -+ return token.nGroup & TGUNOPER && -+ (token.eType == TPLUS || -+ token.eType == TMINUS || -+ token.eType == TPLUSMINUS || -+ token.eType == TMINUSPLUS || -+ token.eType == TNEG || -+ token.eType == TUOPER); -+} -+ -+BOOL SmNodeListParser::IsPostfixOperator(const SmToken &token) { -+ return token.eType == TFACT; -+} -diff --git starmath/source/dialog.cxx starmath/source/dialog.cxx -index f8b8f7e..82e386a 100644 ---- starmath/source/dialog.cxx -+++ starmath/source/dialog.cxx -@@ -1477,8 +1477,8 @@ IMPL_LINK( SmSymbolDialog, GetClickHdl, Button *, EMPTYARG pButton ) - aText += pSym->GetName(); - - rViewSh.GetViewFrame()->GetDispatcher()->Execute( -- SID_INSERTTEXT, SFX_CALLMODE_STANDARD, -- new SfxStringItem(SID_INSERTTEXT, aText), 0L); -+ SID_INSERTSYMBOL, SFX_CALLMODE_STANDARD, -+ new SfxStringItem(SID_INSERTSYMBOL, aText), 0L); - } - - return 0; -diff --git starmath/source/document.cxx starmath/source/document.cxx -index 259c26d..5985aa2 100644 ---- starmath/source/document.cxx -+++ starmath/source/document.cxx -@@ -94,7 +94,7 @@ - #include "mathtype.hxx" - #include "mathmlimport.hxx" - #include "mathmlexport.hxx" -- -+#include "cursor.hxx" - - - using namespace ::com::sun::star; -@@ -248,6 +248,7 @@ void SmDocShell::Parse() - pTree = aInterpreter.Parse(aText); - nModifyCount++; - SetFormulaArranged( FALSE ); -+ InvalidateCursor(); - } - - -@@ -434,9 +435,10 @@ SfxItemPool& SmDocShell::GetEditEngineItemPool() - DBG_ASSERT( pEditEngineItemPool, "EditEngineItemPool missing" ); - return *pEditEngineItemPool; - } -+//TODO: Move to the top of the file... -+#include "visitors.hxx" - -- --void SmDocShell::Draw(OutputDevice &rDev, Point &rPosition) -+void SmDocShell::DrawFormula(OutputDevice &rDev, Point &rPosition, BOOL bDrawSelection) - { - RTL_LOGFILE_CONTEXT( aLog, "starmath: SmDocShell::Draw" ); - -@@ -477,8 +479,16 @@ void SmDocShell::Draw(OutputDevice &rDev, Point &rPosition) - rDev.SetLayoutMode( TEXT_LAYOUT_BIDI_LTR ); - INT16 nDigitLang = rDev.GetDigitLanguage(); - rDev.SetDigitLanguage( LANGUAGE_ENGLISH ); -- // -- pTree->Draw(rDev, rPosition); -+ -+ //Set selection if any -+ if(pCursor && bDrawSelection){ -+ pCursor->AnnotateSelection(); -+ SmSelectionDrawingVisitor(rDev, pTree, rPosition); -+ } -+ -+ //Drawing using visitor -+ DrawingVisitor(rDev, rPosition, pTree); -+ - // - rDev.SetLayoutMode( nLayoutMode ); - rDev.SetDigitLanguage( nDigitLang ); -@@ -519,6 +529,18 @@ Size SmDocShell::GetSize() - return aRet; - } - -+void SmDocShell::InvalidateCursor(){ -+ if(pCursor) -+ delete pCursor; -+ pCursor = NULL; -+} -+ -+SmCursor& SmDocShell::GetCursor(){ -+ if(!pCursor) -+ pCursor = new SmCursor(pTree, this); -+ return *pCursor; -+} -+ - //////////////////////////////////////// - - SmPrinterAccess::SmPrinterAccess( SmDocShell &rDocShell ) -@@ -691,6 +713,7 @@ SmDocShell::SmDocShell(SfxObjectCreateMode eMode,const sal_Bool _bScriptSupport) - nModifyCount ( 0 ), - bIsFormulaArranged ( FALSE ) - { -+ pCursor = NULL; - RTL_LOGFILE_CONTEXT( aLog, "starmath: SmDocShell::SmDocShell" ); - - SetPool(&SFX_APP()->GetPool()); -@@ -720,6 +743,11 @@ SmDocShell::~SmDocShell() - EndListening(aFormat); - EndListening(*pp->GetConfig()); - -+ -+ if(pCursor) -+ delete pCursor; -+ pCursor = NULL; -+ - delete pEditEngine; - SfxItemPool::Free(pEditEngineItemPool); - delete pTree; -@@ -751,6 +779,7 @@ BOOL SmDocShell::ConvertFrom(SfxMedium &rMedium) - { - delete pTree; - pTree = 0; -+ InvalidateCursor(); - } - Reference<com::sun::star::frame::XModel> xModel(GetModel()); - SmXMLImportWrapper aEquation(xModel); -@@ -1313,7 +1342,7 @@ void SmDocShell::Draw(OutputDevice *pDevice, - - pDevice->IntersectClipRegion(GetVisArea()); - Point atmppoint; -- Draw(*pDevice, atmppoint); -+ DrawFormula(*pDevice, atmppoint); - } - - SfxItemPool& SmDocShell::GetPool() const -diff --git starmath/source/edit.cxx starmath/source/edit.cxx -index 1994271..62905b4 100644 ---- starmath/source/edit.cxx -+++ starmath/source/edit.cxx -@@ -122,9 +122,6 @@ SmEditWindow::SmEditWindow( SmCmdBoxWindow &rMyCmdBoxWin ) : - aModifyTimer.SetTimeout(2000); - aModifyTimer.Start(); - -- aCursorMoveTimer.SetTimeoutHdl(LINK(this, SmEditWindow, CursorMoveTimerHdl)); -- aCursorMoveTimer.SetTimeout(500); -- - // if not called explicitly the this edit window within the - // command window will just show an empty gray panel. - Show(); -@@ -133,7 +130,6 @@ SmEditWindow::SmEditWindow( SmCmdBoxWindow &rMyCmdBoxWin ) : - - SmEditWindow::~SmEditWindow() - { -- aCursorMoveTimer.Stop(); - aModifyTimer.Stop(); - - -@@ -257,35 +253,6 @@ IMPL_LINK( SmEditWindow, ModifyTimerHdl, Timer *, EMPTYARG /*pTimer*/ ) - return 0; - } - -- --IMPL_LINK(SmEditWindow, CursorMoveTimerHdl, Timer *, EMPTYARG /*pTimer*/) -- // every once in a while check cursor position (selection) of edit -- // window and if it has changed (try to) set the formula-cursor -- // according to that. --{ -- ESelection aNewSelection (GetSelection()); -- -- if (!aNewSelection.IsEqual(aOldSelection)) -- { SmViewShell *pView = rCmdBox.GetView(); -- -- if (pView) -- { -- // get row and column to look for -- USHORT nRow, nCol; -- SmGetLeftSelectionPart(aNewSelection, nRow, nCol); -- nRow++; -- nCol++; -- -- pView->GetGraphicWindow().SetCursorPos(nRow, nCol); -- -- aOldSelection = aNewSelection; -- } -- } -- -- return 0; --} -- -- - void SmEditWindow::Resize() - { - if (!pEditView) -@@ -319,8 +286,6 @@ void SmEditWindow::MouseButtonUp(const MouseEvent &rEvt) - else - Window::MouseButtonUp (rEvt); - -- // ggf FormulaCursor neu positionieren -- CursorMoveTimerHdl(&aCursorMoveTimer); - InvalidateSlots(); - } - -@@ -425,10 +390,6 @@ void SmEditWindow::KeyInput(const KeyEvent& rKEvt) - } - else - { -- // Timer neu starten, um den Handler (auch bei laengeren Eingaben) -- // moeglichst nur einmal am Ende aufzurufen. -- aCursorMoveTimer.Start(); -- - DBG_ASSERT( pEditView, "EditView missing (NULL pointer)" ); - if (!pEditView) - CreateEditView(); -@@ -631,7 +592,6 @@ void SmEditWindow::SetText(const XubString& rText) - //! Hier die Timer neu zu starten verhindert, dass die Handler fuer andere - //! (im Augenblick nicht mehr aktive) Math Tasks aufgerufen werden. - aModifyTimer.Start(); -- aCursorMoveTimer.Start(); - - pEditView->SetSelection(eSelection); - } -@@ -655,6 +615,10 @@ void SmEditWindow::GetFocus() - EditEngine *pEditEngine = GetEditEngine(); - if (pEditEngine) - pEditEngine->SetStatusEventHdl( LINK(this, SmEditWindow, EditStatusHdl) ); -+ -+ //Let SmViewShell know we got focus -+ if(GetView()) -+ GetView()->SetInsertIntoEditWindow(TRUE); - } - - -@@ -925,13 +889,6 @@ void SmEditWindow::Flush() - SID_TEXT, SFX_CALLMODE_STANDARD, - new SfxStringItem(SID_TEXT, GetText()), 0L); - } -- -- if (aCursorMoveTimer.IsActive()) -- { -- aCursorMoveTimer.Stop(); -- // ggf noch die (neue) FormulaCursor Position setzen -- CursorMoveTimerHdl(&aCursorMoveTimer); -- } - } - - -diff --git starmath/source/makefile.mk starmath/source/makefile.mk -index 912e6e6..da8046e 100644 ---- starmath/source/makefile.mk -+++ starmath/source/makefile.mk -@@ -63,6 +63,9 @@ SLO1FILES = \ - $(SLO)$/mathmlexport.obj \ - $(SLO)$/format.obj \ - $(SLO)$/node.obj \ -+ $(SLO)$/visitors.obj \ -+ $(SLO)$/caret.obj \ -+ $(SLO)$/cursor.obj \ - $(SLO)$/parse.obj \ - $(SLO)$/utility.obj \ - $(SLO)$/smdll.obj \ -diff --git starmath/source/node.cxx starmath/source/node.cxx -index b243ced..cd68767 100644 ---- starmath/source/node.cxx -+++ starmath/source/node.cxx -@@ -45,6 +45,7 @@ - #include "smmod.hxx" - #include <document.hxx> - #include <view.hxx> -+#include "visitors.hxx" - #ifndef _MATHTYPE_HXX - #include "mathtype.hxx" - #endif -@@ -144,6 +145,8 @@ SmNode::SmNode(SmNodeType eNodeType, const SmToken &rNodeToken) - eScaleMode = SCALE_NONE; - aNodeToken = rNodeToken; - nAccIndex = -1; -+ SetSelected(false); -+ aParentNode = NULL; - } - - -@@ -448,28 +451,6 @@ void SmNode::AdaptToY(const OutputDevice &/*rDev*/, ULONG /*nHeight*/) - } - - --void SmNode::Draw(OutputDevice &rDev, const Point &rPosition) const --{ -- if (IsPhantom()) -- return; -- -- const SmNode *pNode; -- USHORT nSize = GetNumSubNodes(); -- for (USHORT i = 0; i < nSize; i++) -- if (NULL != (pNode = GetSubNode(i))) -- { Point aOffset (pNode->GetTopLeft() - GetTopLeft()); -- pNode->Draw(rDev, rPosition + aOffset); -- } -- --#ifdef SM_RECT_DEBUG -- if (!IsDebug()) -- return; -- -- int nRFlags = SM_RECT_CORE | SM_RECT_ITALIC | SM_RECT_LINES | SM_RECT_MID; -- SmRect::Draw(rDev, rPosition, nRFlags); --#endif --} -- - const SmNode * SmNode::FindTokenAt(USHORT nRow, USHORT nCol) const - // returns (first) ** visible ** (sub)node with the tokens text at - // position 'nRow', 'nCol'. -@@ -570,6 +551,98 @@ const SmNode * SmNode::FindNodeWithAccessibleIndex(xub_StrLen nAccIdx) const - return pResult; - } - -+void SmNode::DumpAsDot(std::ostream &out, String* label, int number, int& id, int parent) const -+{ -+ //If this is the root start the file -+ if(number == -1){ -+ out<<"digraph {"<<std::endl; -+ if(label){ -+ out<<"labelloc = \"t\";"<<std::endl; -+ String eq(*label); -+ //CreateTextFromNode(eq); -+ eq.SearchAndReplaceAll(String::CreateFromAscii("\n"), String::CreateFromAscii(" ")); -+ eq.SearchAndReplaceAll(String::CreateFromAscii("\\"), String::CreateFromAscii("\\\\")); -+ eq.SearchAndReplaceAll(String::CreateFromAscii("\""), String::CreateFromAscii("\\\"")); -+ out<<"label= \"Equation: \\\""; -+ out<<ByteString( eq, RTL_TEXTENCODING_UTF8).GetBuffer(); -+ out<<"\\\"\";"<<std::endl; -+ } -+ } -+ -+ //Some how out<<(int)this; doesn't work... So we do this nasty workaround... -+ char strid[100]; -+ sprintf(strid, "%i", id); -+ -+ char strnr[100]; -+ sprintf(strnr, "%i", number); -+ -+ //Dump connection to this node -+ if( parent != -1 ){ -+ char pid[100]; -+ sprintf(pid, "%i", parent); -+ out<<"n"<<pid<<" -> n"<<strid<<" [label=\""<<strnr<<"\"];"<<std::endl; -+ //If doesn't have parent and isn't a rootnode: -+ } else if(number != -1) { -+ out<<"orphaned -> n"<<strid<<" [label=\""<<strnr<<"\"];"<<std::endl; -+ } -+ -+ //Dump this node -+ out<<"n"<< strid<<" [label=\""; -+ switch( GetType() ) { -+ case NTABLE: out<<"SmTableNode"; break; -+ case NBRACE: out<<"SmBraceNode"; break; -+ case NBRACEBODY: out<<"SmBracebodyNode"; break; -+ case NOPER: out<<"SmOperNode"; break; -+ case NALIGN: out<<"SmAlignNode"; break; -+ case NATTRIBUT: out<<"SmAttributNode"; break; -+ case NFONT: out<<"SmFontNode"; break; -+ case NUNHOR: out<<"SmUnHorNode"; break; -+ case NBINHOR: out<<"SmBinHorNode"; break; -+ case NBINVER: out<<"SmBinVerNode"; break; -+ case NBINDIAGONAL: out<<"SmBinDiagonalNode"; break; -+ case NSUBSUP: out<<"SmSubSupNode"; break; -+ case NMATRIX: out<<"SmMatrixNode"; break; -+ case NPLACE: out<<"SmPlaceNode"; break; -+ case NTEXT: -+ out<<"SmTextNode: "; -+ out<< ByteString( ((SmTextNode*)this)->GetText(), RTL_TEXTENCODING_UTF8).GetBuffer(); -+ break; -+ case NSPECIAL: out<<"SmSpecialNode"; break; -+ case NGLYPH_SPECIAL: out<<"SmGlyphSpecialNode"; break; -+ case NMATH: -+ out<<"SmMathSymbolNode: "; -+ out<< ByteString( ((SmMathSymbolNode*)this)->GetText(), RTL_TEXTENCODING_UTF8).GetBuffer(); -+ break; -+ case NBLANK: out<<"SmBlankNode"; break; -+ case NERROR: out<<"SmErrorNode"; break; -+ case NLINE: out<<"SmLineNode"; break; -+ case NEXPRESSION: out<<"SmExpressionNode"; break; -+ case NPOLYLINE: out<<"SmPolyLineNode"; break; -+ case NROOT: out<<"SmRootNode"; break; -+ case NROOTSYMBOL: out<<"SmRootSymbolNode"; break; -+ case NRECTANGLE: out<<"SmRectangleNode"; break; -+ case NVERTICAL_BRACE: out<<"SmVerticalBraceNode"; break; -+ default: -+ out<<"Unknown Node"; -+ } -+ out<<"\""; -+ if(IsSelected()) -+ out<<", style=dashed"; -+ out<<"];"<<std::endl; -+ -+ //Dump subnodes -+ int myid = id; -+ const SmNode *pNode; -+ USHORT nSize = GetNumSubNodes(); -+ for (USHORT i = 0; i < nSize; i++) -+ if (NULL != (pNode = GetSubNode(i))) -+ pNode->DumpAsDot(out, NULL, i, ++id, myid); -+ -+ //If this is the root end the file -+ if( number == -1 ) -+ out<<"}"<<std::endl; -+} -+ - /////////////////////////////////////////////////////////////////////////// - - SmStructureNode::SmStructureNode( const SmStructureNode &rNode ) : -@@ -587,6 +660,7 @@ SmStructureNode::SmStructureNode( const SmStructureNode &rNode ) : - SmNode *pNode = rNode.aSubNodes[i]; - aSubNodes[i] = pNode ? new SmNode( *pNode ) : 0; - } -+ ClaimPaternity(); - } - - -@@ -617,6 +691,8 @@ SmStructureNode & SmStructureNode::operator = ( const SmStructureNode &rNode ) - aSubNodes[i] = pNode ? new SmNode( *pNode ) : 0; - } - -+ ClaimPaternity(); -+ - return *this; - } - -@@ -631,12 +707,15 @@ void SmStructureNode::SetSubNodes(SmNode *pFirst, SmNode *pSecond, SmNode *pThir - aSubNodes[1] = pSecond; - if (pThird) - aSubNodes[2] = pThird; -+ -+ ClaimPaternity(); - } - - - void SmStructureNode::SetSubNodes(const SmNodeArray &rNodeArray) - { - aSubNodes = rNodeArray; -+ ClaimPaternity(); - } - - -@@ -2181,36 +2260,6 @@ void SmPolyLineNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat) - } - - --void SmPolyLineNode::Draw(OutputDevice &rDev, const Point &rPosition) const --{ -- if (IsPhantom()) -- return; -- -- long nBorderwidth = GetFont().GetBorderWidth(); -- -- LineInfo aInfo; -- aInfo.SetWidth(nWidth - 2 * nBorderwidth); -- -- Point aOffset (Point() - aPoly.GetBoundRect().TopLeft() -- + Point(nBorderwidth, nBorderwidth)), -- aPos (rPosition + aOffset); -- ((Polygon &) aPoly).Move(aPos.X(), aPos.Y()); -- -- SmTmpDevice aTmpDev ((OutputDevice &) rDev, FALSE); -- aTmpDev.SetLineColor( GetFont().GetColor() ); -- -- rDev.DrawPolyLine(aPoly, aInfo); -- --#ifdef SM_RECT_DEBUG -- if (!IsDebug()) -- return; -- -- int nRFlags = SM_RECT_CORE | SM_RECT_ITALIC | SM_RECT_LINES | SM_RECT_MID; -- SmRect::Draw(rDev, rPosition, nRFlags); --#endif --} -- -- - /**************************************************************************/ - - void SmRootSymbolNode::AdaptToX(const OutputDevice &/*rDev*/, ULONG nWidth) -@@ -2227,48 +2276,6 @@ void SmRootSymbolNode::AdaptToY(const OutputDevice &rDev, ULONG nHeight) - } - - --void SmRootSymbolNode::Draw(OutputDevice &rDev, const Point &rPosition) const --{ -- if (IsPhantom()) -- return; -- -- // draw root-sign itself -- SmMathSymbolNode::Draw(rDev, rPosition); -- -- SmTmpDevice aTmpDev( (OutputDevice &) rDev, TRUE ); -- aTmpDev.SetFillColor(GetFont().GetColor()); -- rDev.SetLineColor(); -- aTmpDev.SetFont( GetFont() ); -- -- // since the width is always unscaled it corresponds ot 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} ) -- long nBarHeight = GetWidth() * 7L / 100L; -- long nBarWidth = nBodyWidth + GetBorderWidth(); -- Point aBarOffset( GetWidth(), +GetBorderWidth() ); -- Point aBarPos( rPosition + aBarOffset ); -- -- Rectangle aBar(aBarPos, Size( nBarWidth, nBarHeight) ); -- //! avoid GROWING AND SHRINKING of drawn rectangle when constantly -- //! increasing zoomfactor. -- // This is done by shifting it's output-position to a point that -- // corresponds exactly to a pixel on the output device. -- Point aDrawPos( rDev.PixelToLogic(rDev.LogicToPixel(aBar.TopLeft())) ); -- //aDrawPos.X() = aBar.Left(); //! don't change X position -- aBar.SetPos( aDrawPos ); -- -- rDev.DrawRect( aBar ); -- --#ifdef SM_RECT_DEBUG -- if (!IsDebug()) -- return; -- -- int nRFlags = SM_RECT_CORE | SM_RECT_ITALIC | SM_RECT_LINES | SM_RECT_MID; -- SmRect::Draw(rDev, rPosition, nRFlags); --#endif --} -- - - /**************************************************************************/ - -@@ -2310,47 +2317,6 @@ void SmRectangleNode::Arrange(const OutputDevice &rDev, const SmFormat &/*rForma - } - - --void SmRectangleNode::Draw(OutputDevice &rDev, const Point &rPosition) const --{ -- if (IsPhantom()) -- return; -- -- SmTmpDevice aTmpDev ((OutputDevice &) rDev, FALSE); -- aTmpDev.SetFillColor(GetFont().GetColor()); -- rDev.SetLineColor(); -- aTmpDev.SetFont(GetFont()); -- -- ULONG nTmpBorderWidth = GetFont().GetBorderWidth(); -- -- // get rectangle and remove borderspace -- Rectangle aTmp (AsRectangle() + rPosition - GetTopLeft()); -- aTmp.Left() += nTmpBorderWidth; -- aTmp.Right() -= nTmpBorderWidth; -- aTmp.Top() += nTmpBorderWidth; -- aTmp.Bottom() -= nTmpBorderWidth; -- -- DBG_ASSERT(aTmp.GetHeight() > 0 && aTmp.GetWidth() > 0, -- "Sm: leeres Rechteck"); -- -- //! avoid GROWING AND SHRINKING of drawn rectangle when constantly -- //! increasing zoomfactor. -- // This is done by shifting it's output-position to a point that -- // corresponds exactly to a pixel on the output device. -- Point aPos (rDev.PixelToLogic(rDev.LogicToPixel(aTmp.TopLeft()))); -- aTmp.SetPos(aPos); -- -- rDev.DrawRect(aTmp); -- --#ifdef SM_RECT_DEBUG -- if (!IsDebug()) -- return; -- -- int nRFlags = SM_RECT_CORE | SM_RECT_ITALIC | SM_RECT_LINES | SM_RECT_MID; -- SmRect::Draw(rDev, rPosition, nRFlags); --#endif --} -- -- - /**************************************************************************/ - - -@@ -2445,29 +2411,6 @@ void SmTextNode::CreateTextFromNode(String &rText) - rText.Append(' '); - } - --void SmTextNode::Draw(OutputDevice &rDev, const Point& rPosition) const --{ -- if (IsPhantom() || aText.Len() == 0 || aText.GetChar(0) == xub_Unicode('\0')) -- return; -- -- SmTmpDevice aTmpDev ((OutputDevice &) rDev, FALSE); -- aTmpDev.SetFont(GetFont()); -- -- Point aPos (rPosition); -- aPos.Y() += GetBaselineOffset(); -- // auf Pixelkoordinaten runden -- aPos = rDev.PixelToLogic( rDev.LogicToPixel(aPos) ); -- -- rDev.DrawStretchText(aPos, GetWidth(), aText); -- --#ifdef SM_RECT_DEBUG -- if (!IsDebug()) -- return; -- -- int nRFlags = SM_RECT_CORE | SM_RECT_ITALIC | SM_RECT_LINES | SM_RECT_MID; -- SmRect::Draw(rDev, rPosition, nRFlags); --#endif --} - - void SmTextNode::GetAccessibleText( String &rText ) const - { -@@ -2804,7 +2747,7 @@ void SmSpecialNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell - - const SmSym *pSym; - SmModule *pp = SM_MOD1(); -- -+ - if (NULL != (pSym = pp->GetSymSetManager().GetSymbolByName(GetToken().aText))) - { - SetText( pSym->GetCharacter() ); -@@ -2844,17 +2787,6 @@ void SmSpecialNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat) - SmRect::operator = (SmRect(aTmpDev, &rFormat, GetText(), GetFont().GetBorderWidth())); - } - -- --void SmSpecialNode::Draw(OutputDevice &rDev, const Point& rPosition) const --{ -- //! since this chars might come from any font, that we may not have -- //! set to ALIGN_BASELINE yet, we do it now. -- ((SmSpecialNode *)this)->GetFont().SetAlign(ALIGN_BASELINE); -- -- SmTextNode::Draw(rDev, rPosition); --} -- -- - /**************************************************************************/ - - -@@ -2965,5 +2897,122 @@ void SmBlankNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat) - SetWidth(nSpace); - } - -+/**************************************************************************/ -+//Implementation of all accept methods for SmVisitor -+ -+void SmNode::Accept(SmVisitor*){ -+ //This method is only implemented to avoid making SmNode abstract because an -+ //obscure copy constructor is used... I can't find it's implementation, and -+ //don't want to figure out how to fix it... If you want to, just delete this -+ //method, making SmNode abstract, and see where you can an problem with that. -+ j_assert(false, "SmNode should not be visitable!"); -+} -+ -+void SmTableNode::Accept(SmVisitor* pVisitor) { -+ pVisitor->Visit(this); -+} -+ -+void SmBraceNode::Accept(SmVisitor* pVisitor) { -+ pVisitor->Visit(this); -+} -+ -+void SmBracebodyNode::Accept(SmVisitor* pVisitor) { -+ pVisitor->Visit(this); -+} -+ -+void SmOperNode::Accept(SmVisitor* pVisitor) { -+ pVisitor->Visit(this); -+} -+ -+void SmAlignNode::Accept(SmVisitor* pVisitor) { -+ pVisitor->Visit(this); -+} -+ -+void SmAttributNode::Accept(SmVisitor* pVisitor) { -+ pVisitor->Visit(this); -+} -+ -+void SmFontNode::Accept(SmVisitor* pVisitor) { -+ pVisitor->Visit(this); -+} -+ -+void SmUnHorNode::Accept(SmVisitor* pVisitor) { -+ pVisitor->Visit(this); -+} -+ -+void SmBinHorNode::Accept(SmVisitor* pVisitor) { -+ pVisitor->Visit(this); -+} - -+void SmBinVerNode::Accept(SmVisitor* pVisitor) { -+ pVisitor->Visit(this); -+} -+ -+void SmBinDiagonalNode::Accept(SmVisitor* pVisitor) { -+ pVisitor->Visit(this); -+} -+ -+void SmSubSupNode::Accept(SmVisitor* pVisitor) { -+ pVisitor->Visit(this); -+} -+ -+void SmMatrixNode::Accept(SmVisitor* pVisitor) { -+ pVisitor->Visit(this); -+} -+ -+void SmPlaceNode::Accept(SmVisitor* pVisitor) { -+ pVisitor->Visit(this); -+} -+ -+void SmTextNode::Accept(SmVisitor* pVisitor) { -+ pVisitor->Visit(this); -+} -+ -+void SmSpecialNode::Accept(SmVisitor* pVisitor) { -+ pVisitor->Visit(this); -+} -+ -+void SmGlyphSpecialNode::Accept(SmVisitor* pVisitor) { -+ pVisitor->Visit(this); -+} -+ -+void SmMathSymbolNode::Accept(SmVisitor* pVisitor) { -+ pVisitor->Visit(this); -+} -+ -+void SmBlankNode::Accept(SmVisitor* pVisitor) { -+ pVisitor->Visit(this); -+} -+ -+void SmErrorNode::Accept(SmVisitor* pVisitor) { -+ pVisitor->Visit(this); -+} -+ -+void SmLineNode::Accept(SmVisitor* pVisitor) { -+ pVisitor->Visit(this); -+} -+ -+void SmExpressionNode::Accept(SmVisitor* pVisitor) { -+ pVisitor->Visit(this); -+} -+ -+void SmPolyLineNode::Accept(SmVisitor* pVisitor) { -+ pVisitor->Visit(this); -+} -+ -+void SmRootNode::Accept(SmVisitor* pVisitor) { -+ pVisitor->Visit(this); -+} -+ -+void SmRootSymbolNode::Accept(SmVisitor* pVisitor) { -+ pVisitor->Visit(this); -+} -+ -+void SmRectangleNode::Accept(SmVisitor* pVisitor) { -+ pVisitor->Visit(this); -+} -+ -+void SmVerticalBraceNode::Accept(SmVisitor* pVisitor) { -+ pVisitor->Visit(this); -+} - -diff --git starmath/source/parse.cxx starmath/source/parse.cxx -index aab3502..0bda46c 100644 ---- starmath/source/parse.cxx -+++ starmath/source/parse.cxx -@@ -84,6 +84,19 @@ SmToken::SmToken() : - nGroup = nCol = nRow = nLevel = 0; - } - -+SmToken::SmToken(SmTokenType eTokenType, -+ sal_Unicode cMath, -+ const sal_Char* pText, -+ ULONG nTokenGroup, -+ USHORT nTokenLevel) { -+ eType = eTokenType; -+ cMathChar = cMath; -+ aText.AssignAscii(pText); -+ nGroup = nTokenGroup; -+ nLevel = nTokenLevel; -+ nCol = nRow = 0; -+} -+ - /////////////////////////////////////////////////////////////////////////// - - struct SmTokenTableEntry -@@ -1124,6 +1137,13 @@ void SmParser::Line() - ExpressionArray[n - 1] = NodeStack.Pop(); - } - -+ //If there's no expression, add an empty one. -+ //this is to avoid a formula tree without any caret -+ //positions, in visual formula editor. -+ if(ExpressionArray.size() == 0) -+ ExpressionArray.push_back(new SmExpressionNode(SmToken())); -+ -+ - SmStructureNode *pSNode = new SmLineNode(CurToken); - pSNode->SetSubNodes(ExpressionArray); - NodeStack.Push(pSNode); -@@ -1215,6 +1235,10 @@ void SmParser::Product() - - NextToken(); - -+ //Let the glyph node know it's a binary operation -+ CurToken.eType = TBOPER; -+ CurToken.nGroup = TGPRODUCT; -+ - GlyphSpecial(); - pOper = NodeStack.Pop(); - break; -@@ -1704,6 +1728,9 @@ void SmParser::UnOper() - - case TUOPER : - NextToken(); -+ //Let the glyph know what it is... -+ CurToken.eType = TUOPER; -+ CurToken.nGroup = TGUNOPER; - GlyphSpecial(); - pOper = NodeStack.Pop(); - break; -@@ -2203,7 +2230,11 @@ void SmParser::Stack() - - NextToken(); - -- SmStructureNode *pSNode = new SmTableNode(CurToken); -+ //We need to let the table node know it context -+ //it's used in SmNodeToTextVisitor -+ SmToken aTok = CurToken; -+ aTok.eType = TSTACK; -+ SmStructureNode *pSNode = new SmTableNode(aTok); - pSNode->SetSubNodes(ExpressionArray); - NodeStack.Push(pSNode); - } -@@ -2382,7 +2413,7 @@ SmNode *SmParser::Parse(const String &rBuffer) - { - BufferString = rBuffer; - BufferString.ConvertLineEnd( LINEEND_LF ); -- BufferIndex = -+ BufferIndex = 0; - nTokenIndex = 0; - Row = 1; - ColOff = 0; -@@ -2402,6 +2433,30 @@ SmNode *SmParser::Parse(const String &rBuffer) - return NodeStack.Pop(); - } - -+SmNode *SmParser::ParseExpression(const String &rBuffer) -+{ -+ BufferString = rBuffer; -+ BufferString.ConvertLineEnd( LINEEND_LF ); -+ BufferIndex = 0; -+ nTokenIndex = 0; -+ Row = 1; -+ ColOff = 0; -+ CurError = -1; -+ -+ for (USHORT i = 0; i < ErrDescList.Count(); i++) -+ delete ErrDescList.Remove(i); -+ -+ ErrDescList.Clear(); -+ -+ NodeStack.Clear(); -+ -+ SetLanguage( Application::GetSettings().GetUILanguage() ); -+ NextToken(); -+ Expression(); -+ -+ return NodeStack.Pop(); -+} -+ - - USHORT SmParser::AddError(SmParseError Type, SmNode *pNode) - { -diff --git starmath/source/view.cxx starmath/source/view.cxx -index f00036f..24e51a0 100644 ---- starmath/source/view.cxx -+++ starmath/source/view.cxx -@@ -65,6 +65,7 @@ - #include <vcl/menu.hxx> - #include <vcl/msgbox.hxx> - #include <vcl/wrkwin.hxx> -+#include <fstream> - - #include "view.hxx" - #include "config.hxx" -@@ -73,7 +74,7 @@ - #include "starmath.hrc" - #include "toolbox.hxx" - #include "mathmlimport.hxx" -- -+#include "cursor.hxx" - - #define MINWIDTH 200 - #define MINHEIGHT 200 -@@ -98,8 +99,7 @@ SmGraphicWindow::SmGraphicWindow(SmViewShell* pShell): - ScrollableWindow(&pShell->GetViewFrame()->GetWindow(), 0), - pAccessible(0), - pViewShell(pShell), -- nZoom(100), -- bIsCursorVisible(FALSE) -+ nZoom(100) - { - // docking windows are usually hidden (often already done in the - // resource) and will be shown by the sfx framework. -@@ -156,65 +156,32 @@ void SmGraphicWindow::MouseButtonDown(const MouseEvent& rMEvt) - { - ScrollableWindow::MouseButtonDown(rMEvt); - -+ GrabFocus(); -+ - // - // set formula-cursor and selection of edit window according to the - // position clicked at - // - DBG_ASSERT(rMEvt.GetClicks() > 0, "Sm : 0 clicks"); -- if ( rMEvt.IsLeft() && pViewShell->GetEditWindow() ) -+ if ( rMEvt.IsLeft() ) - { -- const SmNode *pTree = pViewShell->GetDoc()->GetFormulaTree(); -- //! kann NULL sein! ZB wenn bereits beim laden des Dokuments (bevor der -- //! Parser angeworfen wurde) ins Fenster geklickt wird. -- if (!pTree) -- return; -- - // get click position relativ to formula - Point aPos (PixelToLogic(rMEvt.GetPosPixel()) - - GetFormulaDrawPos()); - -+ const SmNode* pTree = pViewShell->GetDoc()->GetFormulaTree(); -+ - // if it was clicked inside the formula then get the appropriate node -- const SmNode *pNode = 0; - if (pTree->OrientedDist(aPos) <= 0) -- pNode = pTree->FindRectClosestTo(aPos); -- -- if (pNode) -- { SmEditWindow *pEdit = pViewShell->GetEditWindow(); -- const SmToken aToken (pNode->GetToken()); -- --#ifdef notnow -- // include introducing symbols of special char and text -- // (ie '%' and '"') -- USHORT nExtra = (aToken.eType == TSPECIAL || aToken.eType == TTEXT) ? 1 : 0; -- -- // set selection to the beginning of the token -- ESelection aSel (aToken.nRow - 1, aToken.nCol - 1 - nExtra); -- -- if (rMEvt.GetClicks() != 1) -- { // select whole token -- // for text include terminating symbol (ie '"') -- aSel.nEndPos += aToken.aText.Len() + nExtra -- + (aToken.eType == TTEXT ? 1 : 0); -- } --#endif -- // set selection to the beginning of the token -- ESelection aSel (aToken.nRow - 1, aToken.nCol - 1); -- -- if (rMEvt.GetClicks() != 1 || aToken.eType == TPLACE) -- aSel.nEndPos = aSel.nEndPos + sal::static_int_cast< USHORT >(aToken.aText.Len()); -- -- pEdit->SetSelection(aSel); -- SetCursor(pNode); -- -- // allow for immediate editing and -- //! implicitly synchronize the cursor position mark in this window -- pEdit->GrabFocus(); -- } -+ pViewShell->GetDoc()->GetCursor().MoveTo(this, aPos, !rMEvt.IsShift()); - } - } - - void SmGraphicWindow::GetFocus() - { -+ pViewShell->GetEditWindow()->Flush(); -+ //Let view shell know what insertions should be done in visual editor -+ pViewShell->SetInsertIntoEditWindow(FALSE); - /* - if (xAccessible.is()) - { -@@ -240,69 +207,6 @@ void SmGraphicWindow::LoseFocus() - } - } - --void SmGraphicWindow::ShowCursor(BOOL bShow) -- // shows or hides the formula-cursor depending on 'bShow' is TRUE or not --{ -- BOOL bInvert = bShow != IsCursorVisible(); -- -- if (bInvert) -- InvertTracking(aCursorRect, SHOWTRACK_SMALL | SHOWTRACK_WINDOW); -- -- SetIsCursorVisible(bShow); --} -- -- --void SmGraphicWindow::SetCursor(const SmNode *pNode) --{ -- const SmNode *pTree = pViewShell->GetDoc()->GetFormulaTree(); -- -- // get appropriate rectangle -- Point aOffset (pNode->GetTopLeft() - pTree->GetTopLeft()), -- aTLPos (GetFormulaDrawPos() + aOffset); -- aTLPos.X() -= pNode->GetItalicLeftSpace(); -- Size aSize (pNode->GetItalicSize()); -- Point aBRPos (aTLPos.X() + aSize.Width(), aTLPos.Y() + aSize.Height()); -- -- SetCursor(Rectangle(aTLPos, aSize)); --} -- --void SmGraphicWindow::SetCursor(const Rectangle &rRect) -- // sets cursor to new position (rectangle) 'rRect'. -- // The old cursor will be removed, and the new one will be shown if -- // that is activated in the ConfigItem --{ -- SmModule *pp = SM_MOD1(); -- -- if (IsCursorVisible()) -- ShowCursor(FALSE); // clean up remainings of old cursor -- aCursorRect = rRect; -- if (pp->GetConfig()->IsShowFormulaCursor()) -- ShowCursor(TRUE); // draw new cursor --} -- --const SmNode * SmGraphicWindow::SetCursorPos(USHORT nRow, USHORT nCol) -- // looks for a VISIBLE node in the formula tree with it's token at -- // (or around) the position 'nRow', 'nCol' in the edit window -- // (row and column numbering starts with 1 there!). -- // If there is such a node the formula-cursor is set to cover that nodes -- // rectangle. If not the formula-cursor will be hidden. -- // In any case the search result is being returned. --{ -- // find visible node with token at nRow, nCol -- const SmNode *pTree = pViewShell->GetDoc()->GetFormulaTree(), -- *pNode = 0; -- if (pTree) -- pNode = pTree->FindTokenAt(nRow, nCol); -- -- if (pNode) -- SetCursor(pNode); -- else -- ShowCursor(FALSE); -- -- return pNode; --} -- -- - void SmGraphicWindow::Paint(const Rectangle&) - { - DBG_ASSERT(pViewShell, "Sm : NULL pointer"); -@@ -310,25 +214,12 @@ void SmGraphicWindow::Paint(const Rectangle&) - SmDocShell &rDoc = *pViewShell->GetDoc(); - Point aPoint; - -- rDoc.Draw(*this, aPoint); //! modifies aPoint to be the topleft -+ rDoc.DrawFormula(*this, aPoint, TRUE); //! modifies aPoint to be the topleft - //! corner of the formula - SetFormulaDrawPos(aPoint); -- -- SetIsCursorVisible(FALSE); // (old) cursor must be drawn again -- -- const SmEditWindow *pEdit = pViewShell->GetEditWindow(); -- if (pEdit) -- { // get new position for formula-cursor (for possible altered formula) -- USHORT nRow, nCol; -- SmGetLeftSelectionPart(pEdit->GetSelection(), nRow, nCol); -- nRow++; -- nCol++; -- const SmNode *pFound = SetCursorPos(nRow, nCol); -- -- SmModule *pp = SM_MOD1(); -- if (pFound && pp->GetConfig()->IsShowFormulaCursor()) -- ShowCursor(TRUE); -- } -+ //Draw cursor if any... -+ if(pViewShell->GetDoc()->HasCursor()) -+ pViewShell->GetDoc()->GetCursor().Draw(*this, aPoint); - } - - -@@ -340,11 +231,111 @@ void SmGraphicWindow::SetTotalSize () - ScrollableWindow::SetTotalSize( aTmp ); - } - -- - void SmGraphicWindow::KeyInput(const KeyEvent& rKEvt) - { -- if (! (GetView() && GetView()->KeyInput(rKEvt)) ) -- ScrollableWindow::KeyInput(rKEvt); -+ USHORT nCode = rKEvt.GetKeyCode().GetCode(); -+ SmCursor& rCursor = pViewShell->GetDoc()->GetCursor(); -+ switch(nCode) -+ { -+ case KEY_LEFT: -+ { -+ rCursor.Move(this, MoveLeft, !rKEvt.GetKeyCode().IsShift()); -+ }break; -+ case KEY_RIGHT: -+ { -+ rCursor.Move(this, MoveRight, !rKEvt.GetKeyCode().IsShift()); -+ }break; -+ case KEY_UP: -+ { -+ rCursor.Move(this, MoveUp, !rKEvt.GetKeyCode().IsShift()); -+ }break; -+ case KEY_DOWN: -+ { -+ rCursor.Move(this, MoveDown, !rKEvt.GetKeyCode().IsShift()); -+ }break; -+ case KEY_RETURN: -+ { -+ if(!rKEvt.GetKeyCode().IsShift()) -+ rCursor.InsertRow(); -+ else { -+ SmNode *pTree = (SmNode*)pViewShell->GetDoc()->GetFormulaTree(); -+ std::fstream file("/tmp/smath-dump.gv", std::fstream::out); -+ String label(pViewShell->GetDoc()->GetText()); -+ pTree->DumpAsDot(file, &label); -+ file.close(); -+ } -+ }break; -+ case KEY_DELETE: -+ case KEY_BACKSPACE: -+ { -+ if(!rCursor.HasSelection()){ -+ rCursor.Move(this, nCode == KEY_DELETE ? MoveRight : MoveLeft, false); -+ if(rCursor.HasComplexSelection()) break; -+ } -+ rCursor.Delete(); -+ }break; -+ case KEY_ADD: -+ rCursor.InsertElement(PlusElement); -+ break; -+ case KEY_SUBTRACT: -+ if(rKEvt.GetKeyCode().IsShift()) -+ rCursor.InsertSubSup(RSUB); -+ else -+ rCursor.InsertElement(MinusElement); -+ break; -+ case KEY_MULTIPLY: -+ rCursor.InsertElement(CDotElement); -+ break; -+ case KEY_DIVIDE: -+ rCursor.InsertFraction(); -+ break; -+ case KEY_LESS: -+ rCursor.InsertElement(LessThanElement); -+ break; -+ case KEY_GREATER: -+ rCursor.InsertElement(GreaterThanElement); -+ break; -+ case KEY_EQUAL: -+ rCursor.InsertElement(EqualElement); -+ break; -+ case KEY_COPY: -+ rCursor.Copy(); -+ break; -+ case KEY_CUT: -+ rCursor.Cut(); -+ break; -+ case KEY_PASTE: -+ rCursor.Paste(); -+ break; -+ default: -+ { -+ sal_Unicode code = rKEvt.GetCharCode(); -+ if(code == ' ') { -+ rCursor.InsertElement(BlankElement); -+ }else if(code == 'c' && rKEvt.GetKeyCode().IsMod1()) { -+ rCursor.Copy(); -+ }else if(code == 'x' && rKEvt.GetKeyCode().IsMod1()) { -+ rCursor.Cut(); -+ }else if(code == 'v' && rKEvt.GetKeyCode().IsMod1()) { -+ rCursor.Paste(); -+ }else if(code == '^') { -+ rCursor.InsertSubSup(RSUP); -+ }else if(code == '(') { -+ rCursor.InsertBrackets(RoundBrackets); -+ }else if(code == '[') { -+ rCursor.InsertBrackets(SquareBrackets); -+ }else if(code == '{') { -+ rCursor.InsertBrackets(CurlyBrackets); -+ }else if(code == '!') { -+ rCursor.InsertElement(FactorialElement); -+ }else{ -+ if(code != 0){ -+ rCursor.InsertText(code); -+ }else if (! (GetView() && GetView()->KeyInput(rKEvt)) ) -+ ScrollableWindow::KeyInput(rKEvt); -+ } -+ } -+ } - } - - -@@ -1146,7 +1137,7 @@ void SmViewShell::Impl_Print( - - rOutDev.SetMapMode(OutputMapMode); - rOutDev.SetClipRegion(Region(aOutRect)); -- GetDoc()->Draw(rOutDev, aPos); -+ GetDoc()->DrawFormula(rOutDev, aPos, FALSE); - rOutDev.SetClipRegion(); - - rOutDev.Pop(); -@@ -1418,7 +1409,8 @@ void SmViewShell::Execute(SfxRequest& rReq) - bVal = !pp->GetConfig()->IsShowFormulaCursor(); - - pp->GetConfig()->SetShowFormulaCursor(bVal); -- GetGraphicWindow().ShowCursor(bVal); -+ //GetGraphicWindow().ShowCursor(bVal); -+ //TODO Consider disabling this option!!! - break; - } - case SID_DRAW: -@@ -1563,18 +1555,24 @@ void SmViewShell::Execute(SfxRequest& rReq) - const SfxInt16Item& rItem = - (const SfxInt16Item&)rReq.GetArgs()->Get(SID_INSERTCOMMAND); - -- if (pWin) -+ if (pWin && bInsertIntoEditWindow) - pWin->InsertCommand(rItem.GetValue()); -+ if (GetDoc() && !bInsertIntoEditWindow) { -+ GetDoc()->GetCursor().InsertCommand(rItem.GetValue()); -+ GetGraphicWindow().GrabFocus(); -+ } - break; - } - -- case SID_INSERTTEXT: -+ case SID_INSERTSYMBOL: - { - const SfxStringItem& rItem = -- (const SfxStringItem&)rReq.GetArgs()->Get(SID_INSERTTEXT); -+ (const SfxStringItem&)rReq.GetArgs()->Get(SID_INSERTSYMBOL); - -- if (pWin) -+ if (pWin && bInsertIntoEditWindow) - pWin->InsertText(rItem.GetValue()); -+ if(GetDoc() && !bInsertIntoEditWindow) -+ GetDoc()->GetCursor().InsertSpecial(rItem.GetValue()); - break; - } - -diff --git starmath/source/visitors.cxx starmath/source/visitors.cxx -new file mode 100644 -index 0000000..c7e886d ---- /dev/null -+++ starmath/source/visitors.cxx -@@ -0,0 +1,2466 @@ -+#include "visitors.hxx" -+#include "cursor.hxx" -+ -+///////////////////////////////////// SmVisitorTest ///////////////////////////////////// -+ -+void SmVisitorTest::Visit( SmTableNode* pNode ) -+{ -+ j_assert( pNode->GetType( ) == NTABLE, "the visitor-patterns isn't implemented correctly" ); -+ VisitChildren( pNode ); -+} -+ -+void SmVisitorTest::Visit( SmBraceNode* pNode ) -+{ -+ j_assert( pNode->GetType( ) == NBRACE, "the visitor-patterns isn't implemented correctly" ); -+ VisitChildren( pNode ); -+} -+ -+void SmVisitorTest::Visit( SmBracebodyNode* pNode ) -+{ -+ j_assert( pNode->GetType( ) == NBRACEBODY, "the visitor-patterns isn't implemented correctly" ); -+ VisitChildren( pNode ); -+} -+ -+void SmVisitorTest::Visit( SmOperNode* pNode ) -+{ -+ j_assert( pNode->GetType( ) == NOPER, "the visitor-patterns isn't implemented correctly" ); -+ VisitChildren( pNode ); -+} -+ -+void SmVisitorTest::Visit( SmAlignNode* pNode ) -+{ -+ j_assert( pNode->GetType( ) == NALIGN, "the visitor-patterns isn't implemented correctly" ); -+ VisitChildren( pNode ); -+} -+ -+void SmVisitorTest::Visit( SmAttributNode* pNode ) -+{ -+ j_assert( pNode->GetType( ) == NATTRIBUT, "the visitor-patterns isn't implemented correctly" ); -+ VisitChildren( pNode ); -+} -+ -+void SmVisitorTest::Visit( SmFontNode* pNode ) -+{ -+ j_assert( pNode->GetType( ) == NFONT, "the visitor-patterns isn't implemented correctly" ); -+ VisitChildren( pNode ); -+} -+ -+void SmVisitorTest::Visit( SmUnHorNode* pNode ) -+{ -+ j_assert( pNode->GetType( ) == NUNHOR, "the visitor-patterns isn't implemented correctly" ); -+ VisitChildren( pNode ); -+} -+ -+void SmVisitorTest::Visit( SmBinHorNode* pNode ) -+{ -+ j_assert( pNode->GetType( ) == NBINHOR, "the visitor-patterns isn't implemented correctly" ); -+ VisitChildren( pNode ); -+} -+ -+void SmVisitorTest::Visit( SmBinVerNode* pNode ) -+{ -+ j_assert( pNode->GetType( ) == NBINVER, "the visitor-patterns isn't implemented correctly" ); -+ VisitChildren( pNode ); -+} -+ -+void SmVisitorTest::Visit( SmBinDiagonalNode* pNode ) -+{ -+ j_assert( pNode->GetType( ) == NBINDIAGONAL, "the visitor-patterns isn't implemented correctly" ); -+ VisitChildren( pNode ); -+} -+ -+void SmVisitorTest::Visit( SmSubSupNode* pNode ) -+{ -+ j_assert( pNode->GetType( ) == NSUBSUP, "the visitor-patterns isn't implemented correctly" ); -+ VisitChildren( pNode ); -+} -+ -+void SmVisitorTest::Visit( SmMatrixNode* pNode ) -+{ -+ j_assert( pNode->GetType( ) == NMATRIX, "the visitor-patterns isn't implemented correctly" ); -+ VisitChildren( pNode ); -+} -+ -+void SmVisitorTest::Visit( SmPlaceNode* pNode ) -+{ -+ j_assert( pNode->GetType( ) == NPLACE, "the visitor-patterns isn't implemented correctly" ); -+ VisitChildren( pNode ); -+} -+ -+void SmVisitorTest::Visit( SmTextNode* pNode ) -+{ -+ j_assert( pNode->GetType( ) == NTEXT, "the visitor-patterns isn't implemented correctly" ); -+ VisitChildren( pNode ); -+} -+ -+void SmVisitorTest::Visit( SmSpecialNode* pNode ) -+{ -+ j_assert( pNode->GetType( ) == NSPECIAL, "the visitor-patterns isn't implemented correctly" ); -+ VisitChildren( pNode ); -+} -+ -+void SmVisitorTest::Visit( SmGlyphSpecialNode* pNode ) -+{ -+ j_assert( pNode->GetType( ) == NGLYPH_SPECIAL, "the visitor-patterns isn't implemented correctly" ); -+ VisitChildren( pNode ); -+} -+ -+void SmVisitorTest::Visit( SmMathSymbolNode* pNode ) -+{ -+ j_assert( pNode->GetType( ) == NMATH, "the visitor-patterns isn't implemented correctly" ); -+ VisitChildren( pNode ); -+} -+ -+void SmVisitorTest::Visit( SmBlankNode* pNode ) -+{ -+ j_assert( pNode->GetType( ) == NBLANK, "the visitor-patterns isn't implemented correctly" ); -+ VisitChildren( pNode ); -+} -+ -+void SmVisitorTest::Visit( SmErrorNode* pNode ) -+{ -+ j_assert( pNode->GetType( ) == NERROR, "the visitor-patterns isn't implemented correctly" ); -+ VisitChildren( pNode ); -+} -+ -+void SmVisitorTest::Visit( SmLineNode* pNode ) -+{ -+ j_assert( pNode->GetType( ) == NLINE, "the visitor-patterns isn't implemented correctly" ); -+ VisitChildren( pNode ); -+} -+ -+void SmVisitorTest::Visit( SmExpressionNode* pNode ) -+{ -+ j_assert( pNode->GetType( ) == NEXPRESSION, "the visitor-patterns isn't implemented correctly" ); -+ VisitChildren( pNode ); -+} -+ -+void SmVisitorTest::Visit( SmPolyLineNode* pNode ) -+{ -+ j_assert( pNode->GetType( ) == NPOLYLINE, "the visitor-patterns isn't implemented correctly" ); -+ VisitChildren( pNode ); -+} -+ -+void SmVisitorTest::Visit( SmRootNode* pNode ) -+{ -+ j_assert( pNode->GetType( ) == NROOT, "the visitor-patterns isn't implemented correctly" ); -+ VisitChildren( pNode ); -+} -+ -+void SmVisitorTest::Visit( SmRootSymbolNode* pNode ) -+{ -+ j_assert( pNode->GetType( ) == NROOTSYMBOL, "the visitor-patterns isn't implemented correctly" ); -+ VisitChildren( pNode ); -+} -+ -+void SmVisitorTest::Visit( SmRectangleNode* pNode ) -+{ -+ j_assert( pNode->GetType( ) == NRECTANGLE, "the visitor-patterns isn't implemented correctly" ); -+ VisitChildren( pNode ); -+} -+ -+void SmVisitorTest::Visit( SmVerticalBraceNode* pNode ) -+{ -+ j_assert( pNode->GetType( ) == NVERTICAL_BRACE, "the visitor-patterns isn't implemented correctly" ); -+ VisitChildren( pNode ); -+} -+ -+void SmVisitorTest::VisitChildren( SmNode* pNode ) -+{ -+ SmNodeIterator it( pNode ); -+ while( it.Next( ) ) -+ it->Accept( this ); -+} -+ -+/////////////////////////////// SmDefaultingVisitor //////////////////////////////// -+ -+void SmDefaultingVisitor::Visit( SmTableNode* pNode ) -+{ -+ DefaultVisit( pNode ); -+} -+ -+void SmDefaultingVisitor::Visit( SmBraceNode* pNode ) -+{ -+ DefaultVisit( pNode ); -+} -+ -+void SmDefaultingVisitor::Visit( SmBracebodyNode* pNode ) -+{ -+ DefaultVisit( pNode ); -+} -+ -+void SmDefaultingVisitor::Visit( SmOperNode* pNode ) -+{ -+ DefaultVisit( pNode ); -+} -+ -+void SmDefaultingVisitor::Visit( SmAlignNode* pNode ) -+{ -+ DefaultVisit( pNode ); -+} -+ -+void SmDefaultingVisitor::Visit( SmAttributNode* pNode ) -+{ -+ DefaultVisit( pNode ); -+} -+ -+void SmDefaultingVisitor::Visit( SmFontNode* pNode ) -+{ -+ DefaultVisit( pNode ); -+} -+ -+void SmDefaultingVisitor::Visit( SmUnHorNode* pNode ) -+{ -+ DefaultVisit( pNode ); -+} -+ -+void SmDefaultingVisitor::Visit( SmBinHorNode* pNode ) -+{ -+ DefaultVisit( pNode ); -+} -+ -+void SmDefaultingVisitor::Visit( SmBinVerNode* pNode ) -+{ -+ DefaultVisit( pNode ); -+} -+ -+void SmDefaultingVisitor::Visit( SmBinDiagonalNode* pNode ) -+{ -+ DefaultVisit( pNode ); -+} -+ -+void SmDefaultingVisitor::Visit( SmSubSupNode* pNode ) -+{ -+ DefaultVisit( pNode ); -+} -+ -+void SmDefaultingVisitor::Visit( SmMatrixNode* pNode ) -+{ -+ DefaultVisit( pNode ); -+} -+ -+void SmDefaultingVisitor::Visit( SmPlaceNode* pNode ) -+{ -+ DefaultVisit( pNode ); -+} -+ -+void SmDefaultingVisitor::Visit( SmTextNode* pNode ) -+{ -+ DefaultVisit( pNode ); -+} -+ -+void SmDefaultingVisitor::Visit( SmSpecialNode* pNode ) -+{ -+ DefaultVisit( pNode ); -+} -+ -+void SmDefaultingVisitor::Visit( SmGlyphSpecialNode* pNode ) -+{ -+ DefaultVisit( pNode ); -+} -+ -+void SmDefaultingVisitor::Visit( SmMathSymbolNode* pNode ) -+{ -+ DefaultVisit( pNode ); -+} -+ -+void SmDefaultingVisitor::Visit( SmBlankNode* pNode ) -+{ -+ DefaultVisit( pNode ); -+} -+ -+void SmDefaultingVisitor::Visit( SmErrorNode* pNode ) -+{ -+ DefaultVisit( pNode ); -+} -+ -+void SmDefaultingVisitor::Visit( SmLineNode* pNode ) -+{ -+ DefaultVisit( pNode ); -+} -+ -+void SmDefaultingVisitor::Visit( SmExpressionNode* pNode ) -+{ -+ DefaultVisit( pNode ); -+} -+ -+void SmDefaultingVisitor::Visit( SmPolyLineNode* pNode ) -+{ -+ DefaultVisit( pNode ); -+} -+ -+void SmDefaultingVisitor::Visit( SmRootNode* pNode ) -+{ -+ DefaultVisit( pNode ); -+} -+ -+void SmDefaultingVisitor::Visit( SmRootSymbolNode* pNode ) -+{ -+ DefaultVisit( pNode ); -+} -+ -+void SmDefaultingVisitor::Visit( SmRectangleNode* pNode ) -+{ -+ DefaultVisit( pNode ); -+} -+ -+void SmDefaultingVisitor::Visit( SmVerticalBraceNode* pNode ) -+{ -+ DefaultVisit( pNode ); -+} -+ -+ -+/////////////////////////////// SmCaretDrawingVisitor //////////////////////////////// -+ -+SmCaretDrawingVisitor::SmCaretDrawingVisitor( OutputDevice& rDevice, -+ SmCaretPos position, -+ Point offset ) -+ : rDev( rDevice ) -+{ -+ pos = position; -+ Offset = offset; -+ j_assert( position.IsValid( ), "Cannot draw invalid position!" ); -+ if( !position.IsValid( ) ) -+ return; -+ -+ //Save device state -+ rDev.Push( PUSH_FONT | PUSH_MAPMODE | PUSH_LINECOLOR | PUSH_FILLCOLOR | PUSH_TEXTCOLOR ); -+ -+ pos.pSelectedNode->Accept( this ); -+ //Restore device state -+ rDev.Pop( ); -+} -+ -+void SmCaretDrawingVisitor::Visit( SmTextNode* pNode ) -+{ -+ long i = pos.Index; -+ -+ rDev.SetFont( pNode->GetFont( ) ); -+ -+ //Find the line -+ SmNode* pLine = SmCursor::FindTopMostNodeInLine( pNode ); -+ -+ //Find coordinates -+ long left = pNode->GetLeft( ) + rDev.GetTextWidth( pNode->GetText( ), 0, i ) + Offset.X( ); -+ long top = pLine->GetTop( ) + Offset.Y( ); -+ long height = pLine->GetHeight( ); -+ -+ //Set color -+ rDev.SetLineColor( Color( COL_BLACK ) ); -+ -+ //Draw vertical line -+ Point p1( left, top ); -+ Point p2( left, top + height ); -+ rDev.DrawLine( p1, p2 ); -+} -+ -+void SmCaretDrawingVisitor::DefaultVisit( SmNode* pNode ) -+{ -+ rDev.SetLineColor( Color( COL_BLACK ) ); -+ -+ //Find the line -+ SmNode* pLine = SmCursor::FindTopMostNodeInLine( pNode ); -+ -+ //Find coordinates -+ long left = pNode->GetLeft( ) + Offset.X( ) + ( pos.Index == 1 ? pNode->GetWidth( ) : 0 ); -+ long top = pLine->GetTop( ) + Offset.Y( ); -+ long height = pLine->GetHeight( ); -+ -+ //Set color -+ rDev.SetLineColor( Color( COL_BLACK ) ); -+ -+ //Draw vertical line -+ Point p1( left, top ); -+ Point p2( left, top + height ); -+ rDev.DrawLine( p1, p2 ); -+} -+ -+/////////////////////////////// SmCaretPos2LineVisitor //////////////////////////////// -+ -+void SmCaretPos2LineVisitor::Visit( SmTextNode* pNode ) -+{ -+ //Save device state -+ pDev->Push( PUSH_FONT | PUSH_TEXTCOLOR ); -+ -+ long i = pos.Index; -+ -+ pDev->SetFont( pNode->GetFont( ) ); -+ -+ //Find coordinates -+ long left = pNode->GetLeft( ) + pDev->GetTextWidth( pNode->GetText( ), 0, i ); -+ long top = pNode->GetTop( ); -+ long height = pNode->GetHeight( ); -+ -+ line = SmCaretLine( left, top, height ); -+ -+ //Restore device state -+ pDev->Pop( ); -+} -+ -+void SmCaretPos2LineVisitor::DefaultVisit( SmNode* pNode ) -+{ -+ //Vertical line ( code from SmCaretDrawingVisitor ) -+ Point p1 = pNode->GetTopLeft( ); -+ if( pos.Index == 1 ) -+ p1.Move( pNode->GetWidth( ), 0 ); -+ -+ line = SmCaretLine( p1.X( ), p1.Y( ), pNode->GetHeight( ) ); -+} -+ -+/////////////////////////////// Nasty temporary device!!! //////////////////////////////// -+ -+ -+#include <tools/gen.hxx> -+#include <tools/fract.hxx> -+#include <rtl/math.hxx> -+#include <tools/color.hxx> -+#include <vcl/metric.hxx> -+#include <vcl/lineinfo.hxx> -+#include <vcl/outdev.hxx> -+#include <sfx2/module.hxx> -+#include "symbol.hxx" -+#include "smmod.hxx" -+ -+class SmTmpDevice2 -+{ -+ OutputDevice &rOutDev; -+ -+ // disallow use of copy-constructor and assignment-operator -+ SmTmpDevice2( const SmTmpDevice2 &rTmpDev ); -+ SmTmpDevice2 & operator = ( const SmTmpDevice2 &rTmpDev ); -+ -+ Color Impl_GetColor( const Color& rColor ); -+ -+public: -+ SmTmpDevice2( OutputDevice &rTheDev, BOOL bUseMap100th_mm ); -+ ~SmTmpDevice2( ) { rOutDev.Pop( ); } -+ -+ void SetFont( const Font &rNewFont ); -+ -+ void SetLineColor( const Color& rColor ) { rOutDev.SetLineColor( Impl_GetColor( rColor ) ); } -+ void SetFillColor( const Color& rColor ) { rOutDev.SetFillColor( Impl_GetColor( rColor ) ); } -+ void SetTextColor( const Color& rColor ) { rOutDev.SetTextColor( Impl_GetColor( rColor ) ); } -+ -+ operator OutputDevice & ( ) { return rOutDev; } -+}; -+ -+ -+SmTmpDevice2::SmTmpDevice2( OutputDevice &rTheDev, BOOL bUseMap100th_mm ) : -+ rOutDev( rTheDev ) -+{ -+ rOutDev.Push( PUSH_FONT | PUSH_MAPMODE | -+ PUSH_LINECOLOR | PUSH_FILLCOLOR | PUSH_TEXTCOLOR ); -+ if ( bUseMap100th_mm && MAP_100TH_MM != rOutDev.GetMapMode( ).GetMapUnit( ) ) -+ { -+ DBG_ERROR( "incorrect MapMode?" ); -+ rOutDev.SetMapMode( MAP_100TH_MM ); //Immer fuer 100% fomatieren -+ } -+} -+ -+ -+Color SmTmpDevice2::Impl_GetColor( const Color& rColor ) -+{ -+ ColorData nNewCol = rColor.GetColor( ); -+ if ( COL_AUTO == nNewCol ) -+ { -+ if ( OUTDEV_PRINTER == rOutDev.GetOutDevType( ) ) -+ nNewCol = COL_BLACK; -+ else -+ { -+ Color aBgCol( rOutDev.GetBackground( ).GetColor( ) ); -+ if ( OUTDEV_WINDOW == rOutDev.GetOutDevType( ) ) -+ aBgCol = ( ( Window & ) rOutDev ).GetDisplayBackground( ).GetColor( ); -+ -+ nNewCol = SM_MOD1( )->GetColorConfig( ).GetColorValue( svtools::FONTCOLOR ).nColor; -+ -+ Color aTmpColor( nNewCol ); -+ if ( aBgCol.IsDark( ) && aTmpColor.IsDark( ) ) -+ nNewCol = COL_WHITE; -+ else if ( aBgCol.IsBright( ) && aTmpColor.IsBright( ) ) -+ nNewCol = COL_BLACK; -+ } -+ } -+ return Color( nNewCol ); -+} -+ -+ -+void SmTmpDevice2::SetFont( const Font &rNewFont ) -+{ -+ rOutDev.SetFont( rNewFont ); -+ rOutDev.SetTextColor( Impl_GetColor( rNewFont.GetColor( ) ) ); -+} -+ -+/////////////////////////////// DrawingVisitor //////////////////////////////// -+ -+void DrawingVisitor::Visit( SmTableNode* pNode ) -+{ -+ DrawChildren( pNode ); -+} -+ -+void DrawingVisitor::Visit( SmBraceNode* pNode ) -+{ -+ DrawChildren( pNode ); -+} -+ -+void DrawingVisitor::Visit( SmBracebodyNode* pNode ) -+{ -+ DrawChildren( pNode ); -+} -+ -+void DrawingVisitor::Visit( SmOperNode* pNode ) -+{ -+ DrawChildren( pNode ); -+} -+ -+void DrawingVisitor::Visit( SmAlignNode* pNode ) -+{ -+ DrawChildren( pNode ); -+} -+ -+void DrawingVisitor::Visit( SmAttributNode* pNode ) -+{ -+ DrawChildren( pNode ); -+} -+ -+void DrawingVisitor::Visit( SmFontNode* pNode ) -+{ -+ DrawChildren( pNode ); -+} -+ -+void DrawingVisitor::Visit( SmUnHorNode* pNode ) -+{ -+ DrawChildren( pNode ); -+} -+ -+void DrawingVisitor::Visit( SmBinHorNode* pNode ) -+{ -+ DrawChildren( pNode ); -+} -+ -+void DrawingVisitor::Visit( SmBinVerNode* pNode ) -+{ -+ DrawChildren( pNode ); -+} -+ -+void DrawingVisitor::Visit( SmBinDiagonalNode* pNode ) -+{ -+ DrawChildren( pNode ); -+} -+ -+void DrawingVisitor::Visit( SmSubSupNode* pNode ) -+{ -+ DrawChildren( pNode ); -+} -+ -+void DrawingVisitor::Visit( SmMatrixNode* pNode ) -+{ -+ DrawChildren( pNode ); -+} -+ -+void DrawingVisitor::Visit( SmPlaceNode* pNode ) -+{ -+ DrawSpecialNode( pNode ); -+} -+ -+void DrawingVisitor::Visit( SmTextNode* pNode ) -+{ -+ DrawTextNode( pNode ); -+} -+ -+void DrawingVisitor::Visit( SmSpecialNode* pNode ) -+{ -+ DrawSpecialNode( pNode ); -+} -+ -+void DrawingVisitor::Visit( SmGlyphSpecialNode* pNode ) -+{ -+ DrawSpecialNode( pNode ); -+} -+ -+void DrawingVisitor::Visit( SmMathSymbolNode* pNode ) -+{ -+ DrawSpecialNode( pNode ); -+} -+ -+void DrawingVisitor::Visit( SmBlankNode* pNode ) -+{ -+ DrawChildren( pNode ); -+} -+ -+void DrawingVisitor::Visit( SmErrorNode* pNode ) -+{ -+ DrawSpecialNode( pNode ); -+} -+ -+void DrawingVisitor::Visit( SmLineNode* pNode ) -+{ -+ DrawChildren( pNode ); -+} -+ -+void DrawingVisitor::Visit( SmExpressionNode* pNode ) -+{ -+ DrawChildren( pNode ); -+} -+ -+void DrawingVisitor::Visit( SmRootNode* pNode ) -+{ -+ DrawChildren( pNode ); -+} -+ -+void DrawingVisitor::Visit( SmVerticalBraceNode* pNode ) -+{ -+ DrawChildren( pNode ); -+} -+ -+void DrawingVisitor::Visit( SmRootSymbolNode* pNode ) -+{ -+ if ( pNode->IsPhantom( ) ) -+ return; -+ -+ // draw root-sign itself -+ DrawSpecialNode( pNode ); -+ -+ -+ SmTmpDevice2 aTmpDev( ( OutputDevice & ) rDev, TRUE ); -+ aTmpDev.SetFillColor( pNode->GetFont( ).GetColor( ) ); -+ rDev.SetLineColor( ); -+ aTmpDev.SetFont( pNode->GetFont( ) ); -+ -+ // since the width is always unscaled it corresponds ot 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} ) -+ long nBarHeight = pNode->GetWidth( ) * 7L / 100L; -+ long nBarWidth = pNode->GetBodyWidth( ) + pNode->GetBorderWidth( ); -+ Point aBarOffset( pNode->GetWidth( ), +pNode->GetBorderWidth( ) ); -+ Point aBarPos( Position + aBarOffset ); -+ -+ Rectangle aBar( aBarPos, Size( nBarWidth, nBarHeight ) ); -+ //! avoid GROWING AND SHRINKING of drawn rectangle when constantly -+ //! increasing zoomfactor. -+ // This is done by shifting it's output-position to a point that -+ // corresponds exactly to a pixel on the output device. -+ Point aDrawPos( rDev.PixelToLogic( rDev.LogicToPixel( aBar.TopLeft( ) ) ) ); -+ //aDrawPos.X( ) = aBar.Left( ); //! don't change X position -+ aBar.SetPos( aDrawPos ); -+ -+ rDev.DrawRect( aBar ); -+ -+#ifdef SM_RECT_DEBUG -+ if ( !pNode->IsDebug( ) ) -+ return; -+ -+ int nRFlags = SM_RECT_CORE | SM_RECT_ITALIC | SM_RECT_LINES | SM_RECT_MID; -+ pNode->SmRect::Draw( rDev, Position, nRFlags ); -+#endif -+} -+ -+void DrawingVisitor::Visit( SmPolyLineNode* pNode ) -+{ -+ if ( pNode->IsPhantom( ) ) -+ return; -+ -+ long nBorderwidth = pNode->GetFont( ).GetBorderWidth( ); -+ -+ LineInfo aInfo; -+ aInfo.SetWidth( pNode->GetWidth( ) - 2 * nBorderwidth ); -+ -+ Point aOffset ( Point( ) - pNode->GetPolygon( ).GetBoundRect( ).TopLeft( ) -+ + Point( nBorderwidth, nBorderwidth ) ), -+ aPos ( Position + aOffset ); -+ pNode->GetPolygon( ).Move( aPos.X( ), aPos.Y( ) ); //Works because Polygon wraps a pointer -+ -+ SmTmpDevice2 aTmpDev ( ( OutputDevice & ) rDev, FALSE ); -+ aTmpDev.SetLineColor( pNode->GetFont( ).GetColor( ) ); -+ -+ rDev.DrawPolyLine( pNode->GetPolygon( ), aInfo ); -+ -+#ifdef SM_RECT_DEBUG -+ if ( !pNode->IsDebug( ) ) -+ return; -+ -+ int nRFlags = SM_RECT_CORE | SM_RECT_ITALIC | SM_RECT_LINES | SM_RECT_MID; -+ pNode->SmRect::Draw( rDev, Position, nRFlags ); -+#endif -+} -+ -+void DrawingVisitor::Visit( SmRectangleNode* pNode ) -+{ -+ if ( pNode->IsPhantom( ) ) -+ return; -+ -+ SmTmpDevice2 aTmpDev ( ( OutputDevice & ) rDev, FALSE ); -+ aTmpDev.SetFillColor( pNode->GetFont( ).GetColor( ) ); -+ rDev.SetLineColor( ); -+ aTmpDev.SetFont( pNode->GetFont( ) ); -+ -+ ULONG nTmpBorderWidth = pNode->GetFont( ).GetBorderWidth( ); -+ -+ // get rectangle and remove borderspace -+ Rectangle aTmp ( pNode->AsRectangle( ) + Position - pNode->GetTopLeft( ) ); -+ aTmp.Left( ) += nTmpBorderWidth; -+ aTmp.Right( ) -= nTmpBorderWidth; -+ aTmp.Top( ) += nTmpBorderWidth; -+ aTmp.Bottom( ) -= nTmpBorderWidth; -+ -+ DBG_ASSERT( aTmp.GetHeight( ) > 0 && aTmp.GetWidth( ) > 0, -+ "Sm: leeres Rechteck" ); -+ -+ //! avoid GROWING AND SHRINKING of drawn rectangle when constantly -+ //! increasing zoomfactor. -+ // This is done by shifting it's output-position to a point that -+ // corresponds exactly to a pixel on the output device. -+ Point aPos ( rDev.PixelToLogic( rDev.LogicToPixel( aTmp.TopLeft( ) ) ) ); -+ aTmp.SetPos( aPos ); -+ -+ rDev.DrawRect( aTmp ); -+ -+#ifdef SM_RECT_DEBUG -+ if ( !pNode->IsDebug( ) ) -+ return; -+ -+ int nRFlags = SM_RECT_CORE | SM_RECT_ITALIC | SM_RECT_LINES | SM_RECT_MID; -+ pNode->SmRect::Draw( rDev, rPosition, nRFlags ); -+#endif -+} -+ -+void DrawingVisitor::DrawTextNode( SmTextNode* pNode ) -+{ -+ if ( pNode->IsPhantom( ) || pNode->GetText( ).Len( ) == 0 || pNode->GetText( ).GetChar( 0 ) == xub_Unicode( '\0' ) ) -+ return; -+ -+ SmTmpDevice2 aTmpDev ( ( OutputDevice & ) rDev, FALSE ); -+ aTmpDev.SetFont( pNode->GetFont( ) ); -+ -+ Point aPos ( Position ); -+ aPos.Y( ) += pNode->GetBaselineOffset( ); -+ // auf Pixelkoordinaten runden -+ aPos = rDev.PixelToLogic( rDev.LogicToPixel( aPos ) ); -+ -+ rDev.DrawStretchText( aPos, pNode->GetWidth( ), pNode->GetText( ) ); -+ -+#ifdef SM_RECT_DEBUG -+ if ( !pNode->IsDebug( ) ) -+ return; -+ -+ int nRFlags = SM_RECT_CORE | SM_RECT_ITALIC | SM_RECT_LINES | SM_RECT_MID; -+ pNode->SmRect::Draw( rDev, Position, nRFlags ); -+#endif -+} -+ -+void DrawingVisitor::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( ).SetAlign( ALIGN_BASELINE ); -+ -+ DrawTextNode( pNode ); -+} -+ -+void DrawingVisitor::DrawChildren( SmNode* pNode ) -+{ -+ if ( pNode->IsPhantom( ) ) -+ return; -+ -+ Point rPosition = Position; -+ -+ SmNodeIterator it( pNode ); -+ while( it.Next( ) ) -+ { -+ Point aOffset ( it->GetTopLeft( ) - pNode->GetTopLeft( ) ); -+ Position = rPosition + aOffset; -+ it->Accept( this ); -+ } -+ -+#ifdef SM_RECT_DEBUG -+ if ( !pNode->IsDebug( ) ) -+ return; -+ -+ int nRFlags = SM_RECT_CORE | SM_RECT_ITALIC | SM_RECT_LINES | SM_RECT_MID; -+ pNode->SmRect::Draw( rDev, rPosition, nRFlags ); -+#endif -+} -+ -+/////////////////////////////// SetSelectionVisitor //////////////////////////////// -+ -+void SetSelectionVisitor::SetSelectedOnAll( SmNode* pSubTree, bool IsSelected ) -+{ -+ pSubTree->SetSelected( IsSelected ); -+ -+ //Quick BFS to set all selections -+ SmNodeIterator it( pSubTree ); -+ while( it.Next( ) ) -+ SetSelectedOnAll( it.Current( ), IsSelected ); -+} -+ -+void SetSelectionVisitor::DefaultVisit( SmNode* pNode ) -+{ -+ //Change state if StartPos is infront of this node -+ if( StartPos.pSelectedNode == pNode && StartPos.Index == 0 ) -+ IsSelecting = !IsSelecting; -+ //Change state if EndPos is infront of this node -+ if( EndPos.pSelectedNode == pNode && EndPos.Index == 0 ) -+ IsSelecting = !IsSelecting; -+ -+ //Cache current state -+ BOOL WasSelecting = IsSelecting; -+ BOOL ChangedState = FALSE; -+ -+ //Set selected -+ pNode->SetSelected( IsSelecting ); -+ -+ //Visit children -+ SmNodeIterator it( pNode ); -+ while( it.Next( ) ) -+ { -+ it->Accept( this ); -+ ChangedState = ( WasSelecting != IsSelecting ) || ChangedState; -+ } -+ -+ //If state changed -+ if( ChangedState ) -+ { -+ //Select this node and all of it's children -+ SetSelectedOnAll( pNode, true ); -+ /* If the equation is: sqrt{2 + 4} + 5 -+ * And the selection is: sqrt{2 + [4} +] 5 -+ * Where [ denotes StartPos and ] denotes EndPos -+ * 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 StartPos and EndPos. -+ */ -+ } -+ -+ //Change state if StartPos is after this node -+ if( StartPos.pSelectedNode == pNode && StartPos.Index == 1 ) -+ { -+ IsSelecting = !IsSelecting; -+ } -+ //Change state if EndPos is after of this node -+ if( EndPos.pSelectedNode == pNode && EndPos.Index == 1 ) -+ { -+ IsSelecting = !IsSelecting; -+ } -+} -+ -+void SetSelectionVisitor::VisitCompositionNode( SmNode* pNode ) -+{ -+ //Change state if StartPos is infront of this node -+ if( StartPos.pSelectedNode == pNode && StartPos.Index == 0 ) -+ IsSelecting = !IsSelecting; -+ //Change state if EndPos is infront of this node -+ if( EndPos.pSelectedNode == pNode && EndPos.Index == 0 ) -+ IsSelecting = !IsSelecting; -+ -+ //Cache current state -+ bool WasSelecting = IsSelecting; -+ -+ //Visit children -+ SmNodeIterator it( pNode ); -+ while( it.Next( ) ) -+ it->Accept( this ); -+ -+ //Set selected, if everything was selected -+ pNode->SetSelected( WasSelecting && IsSelecting ); -+ -+ //Change state if StartPos is after this node -+ if( StartPos.pSelectedNode == pNode && StartPos.Index == 1 ) -+ IsSelecting = !IsSelecting; -+ //Change state if EndPos is after of this node -+ if( EndPos.pSelectedNode == pNode && EndPos.Index == 1 ) -+ IsSelecting = !IsSelecting; -+} -+ -+void SetSelectionVisitor::Visit( SmTextNode* pNode ) -+{ -+ long i1 = -1, -+ i2 = -1; -+ if( StartPos.pSelectedNode == pNode ) -+ i1 = StartPos.Index; -+ if( EndPos.pSelectedNode == pNode ) -+ i2 = EndPos.Index; -+ -+ long start, end; -+ pNode->SetSelected( true ); -+ if( i1 != -1 && i2 != -1 ) { -+ start = i1 < i2 ? i1 : i2; //MIN -+ end = i1 > i2 ? i1 : i2; //MAX -+ } else if( IsSelecting && i1 != -1 ) { -+ start = 0; -+ end = i1; -+ IsSelecting = false; -+ } else if( IsSelecting && i2 != -1 ) { -+ start = 0; -+ end = i2; -+ IsSelecting = false; -+ } else if( !IsSelecting && i1 != -1 ) { -+ start = i1; -+ end = pNode->GetText( ).Len( ); -+ IsSelecting = true; -+ } else if( !IsSelecting && i2 != -1 ) { -+ start = i2; -+ end = pNode->GetText( ).Len( ); -+ IsSelecting = true; -+ } else if( IsSelecting ) { -+ start = 0; -+ end = pNode->GetText( ).Len( ); -+ } else { -+ pNode->SetSelected( false ); -+ start = 0; -+ end = 0; -+ } -+ pNode->SetSelected( start != end ); -+ pNode->SetSelectionStart( start ); -+ pNode->SetSelectionEnd( end ); -+} -+ -+void SetSelectionVisitor::Visit( SmExpressionNode* pNode ) -+{ -+ VisitCompositionNode( pNode ); -+} -+ -+void SetSelectionVisitor::Visit( SmBracebodyNode* pNode ) -+{ -+ VisitCompositionNode( pNode ); -+} -+ -+void SetSelectionVisitor::Visit( SmAlignNode* pNode ) -+{ -+ VisitCompositionNode( pNode ); -+} -+ -+void SetSelectionVisitor::Visit( SmBinHorNode* pNode ) -+{ -+ VisitCompositionNode( pNode ); -+} -+ -+void SetSelectionVisitor::Visit( SmUnHorNode* pNode ) -+{ -+ VisitCompositionNode( pNode ); -+} -+ -+void SetSelectionVisitor::Visit( SmFontNode* pNode ) -+{ -+ VisitCompositionNode( pNode ); -+} -+ -+ -+ -+/////////////////////////////// SmCaretPosGraphBuildingVisitor //////////////////////////////// -+ -+ -+//Needs special care: -+void SmCaretPosGraphBuildingVisitor::Visit( SmTableNode* pNode ) -+{ -+ //Children are SmLineNodes -+ //Or so I thought... Aparently, the children can be instances of SmExpression -+ //especially if there's a error in the formula... So he we go, a simple work around. -+ SmNodeIterator it( pNode ); -+ while( it.Next( ) ){ -+ //There's a special invariant between this method and the Visit( SmLineNode* ) -+ //Usually pRightMost may not be NULL, to avoid this pRightMost should here be -+ //set to a new SmCaretPos infront of it.Current( ), however, if it.Current( ) is -+ //an instance of SmLineNode we let SmLineNode create this position infront of -+ //the visual line. -+ //The argument for doing this is that we now don't have to worry about SmLineNode -+ //being a visual line composition node. Thus no need for yet another special case -+ //in SmCursor::IsLineCompositionNode and everywhere this method is used. -+ if( it->GetType( ) != NLINE ) -+ pRightMost = pGraph->Add( SmCaretPos( it.Current( ), 0 ) ); -+ it->Accept( this ); -+ } -+} -+void SmCaretPosGraphBuildingVisitor::Visit( SmLineNode* pNode ){ -+ pRightMost = NULL; -+ SmNodeIterator it( pNode ); -+ while( it.Next( ) ){ -+ if( !pRightMost ) -+ pRightMost = pGraph->Add( SmCaretPos( it.Current( ), 0 ) ); -+ it->Accept( this ); -+ } -+} -+ -+/** 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; -+ -+ left = pRightMost; -+ j_assert( pRightMost, "pRightMost shouldn't be NULL here!" ); -+ -+ //Create bodyLeft -+ j_assert( pNode->GetBody( ), "SmSubSupNode Doesn't have a body!" ); -+ bodyLeft = pGraph->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 = pGraph->Add( SmCaretPos( pNode, 1 ) ); -+ -+ //Visit the body, to get bodyRight -+ pRightMost = bodyLeft; -+ pNode->GetBody( )->Accept( this ); -+ bodyRight = pRightMost; -+ bodyRight->SetRight( right ); -+ right->SetLeft( bodyRight ); -+ -+ SmNode* pChild; -+ //If there's an LSUP -+ if( ( pChild = pNode->GetSubSup( LSUP ) ) ){ -+ SmCaretPosGraphEntry *cLeft; //Child left -+ cLeft = pGraph->Add( SmCaretPos( pChild, 0 ), left ); -+ -+ pRightMost = cLeft; -+ pChild->Accept( this ); -+ -+ pRightMost->SetRight( bodyLeft ); -+ } -+ //If there's an LSUB -+ if( ( pChild = pNode->GetSubSup( LSUB ) ) ){ -+ SmCaretPosGraphEntry *cLeft; //Child left -+ cLeft = pGraph->Add( SmCaretPos( pChild, 0 ), left ); -+ -+ pRightMost = cLeft; -+ pChild->Accept( this ); -+ -+ pRightMost->SetRight( bodyLeft ); -+ } -+ //If there's an CSUP -+ if( ( pChild = pNode->GetSubSup( CSUP ) ) ){ -+ SmCaretPosGraphEntry *cLeft; //Child left -+ cLeft = pGraph->Add( SmCaretPos( pChild, 0 ), left ); -+ -+ pRightMost = cLeft; -+ pChild->Accept( this ); -+ -+ pRightMost->SetRight( right ); -+ } -+ //If there's an CSUB -+ if( ( pChild = pNode->GetSubSup( CSUB ) ) ){ -+ SmCaretPosGraphEntry *cLeft; //Child left -+ cLeft = pGraph->Add( SmCaretPos( pChild, 0 ), left ); -+ -+ pRightMost = cLeft; -+ pChild->Accept( this ); -+ -+ pRightMost->SetRight( right ); -+ } -+ //If there's an RSUP -+ if( ( pChild = pNode->GetSubSup( RSUP ) ) ){ -+ SmCaretPosGraphEntry *cLeft; //Child left -+ cLeft = pGraph->Add( SmCaretPos( pChild, 0 ), bodyRight ); -+ -+ pRightMost = cLeft; -+ pChild->Accept( this ); -+ -+ pRightMost->SetRight( right ); -+ } -+ //If there's an RSUB -+ if( ( pChild = pNode->GetSubSup( RSUB ) ) ){ -+ SmCaretPosGraphEntry *cLeft; //Child left -+ cLeft = pGraph->Add( SmCaretPos( pChild, 0 ), bodyRight ); -+ -+ pRightMost = cLeft; -+ pChild->Accept( this ); -+ -+ pRightMost->SetRight( right ); -+ } -+ -+ //Set return parameters -+ pRightMost = right; -+} -+ -+/** Build caret position for SmOperNode -+ * -+ * If first child is an SmSubSupNode we will ignore it's -+ * 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 granchildren, 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 = pRightMost, -+ *bodyLeft, -+ *bodyRight, -+ *right; -+ //Create body left -+ bodyLeft = pGraph->Add( SmCaretPos( pBody, 0 ), left ); -+ left->SetRight( bodyLeft ); -+ -+ //Visit body, get bodyRight -+ pRightMost = bodyLeft; -+ pBody->Accept( this ); -+ bodyRight = pRightMost; -+ -+ //Create right -+ right = pGraph->Add( SmCaretPos( pNode, 1 ), bodyRight ); -+ bodyRight->SetRight( right ); -+ -+ //Get subsup pNode if any -+ SmSubSupNode* pSubSup = pOper->GetType( ) == NSUBSUP ? ( SmSubSupNode* )pOper : NULL; -+ -+ SmNode* pChild; -+ SmCaretPosGraphEntry *childLeft; -+ if( pSubSup && ( pChild = pSubSup->GetSubSup( LSUP ) ) ) { -+ //Create position infront of pChild -+ childLeft = pGraph->Add( SmCaretPos( pChild, 0 ), left ); -+ //Visit pChild -+ pRightMost = childLeft; -+ pChild->Accept( this ); -+ //Set right on pRightMost from pChild -+ pRightMost->SetRight( bodyLeft ); -+ } -+ if( pSubSup && ( pChild = pSubSup->GetSubSup( LSUB ) ) ) { -+ //Create position infront of pChild -+ childLeft = pGraph->Add( SmCaretPos( pChild, 0 ), left ); -+ //Visit pChild -+ pRightMost = childLeft; -+ pChild->Accept( this ); -+ //Set right on pRightMost from pChild -+ pRightMost->SetRight( bodyLeft ); -+ } -+ if( pSubSup && ( pChild = pSubSup->GetSubSup( CSUP ) ) ) {//TO -+ //Create position infront of pChild -+ childLeft = pGraph->Add( SmCaretPos( pChild, 0 ), left ); -+ //Visit pChild -+ pRightMost = childLeft; -+ pChild->Accept( this ); -+ //Set right on pRightMost from pChild -+ pRightMost->SetRight( bodyLeft ); -+ } -+ if( pSubSup && ( pChild = pSubSup->GetSubSup( CSUB ) ) ) { //FROM -+ //Create position infront of pChild -+ childLeft = pGraph->Add( SmCaretPos( pChild, 0 ), left ); -+ //Visit pChild -+ pRightMost = childLeft; -+ pChild->Accept( this ); -+ //Set right on pRightMost from pChild -+ pRightMost->SetRight( bodyLeft ); -+ } -+ if( pSubSup && ( pChild = pSubSup->GetSubSup( RSUP ) ) ) { -+ //Create position infront of pChild -+ childLeft = pGraph->Add( SmCaretPos( pChild, 0 ), left ); -+ //Visit pChild -+ pRightMost = childLeft; -+ pChild->Accept( this ); -+ //Set right on pRightMost from pChild -+ pRightMost->SetRight( bodyLeft ); -+ } -+ if( pSubSup && ( pChild = pSubSup->GetSubSup( RSUB ) ) ) { -+ //Create position infront of pChild -+ childLeft = pGraph->Add( SmCaretPos( pChild, 0 ), left ); -+ //Visit pChild -+ pRightMost = childLeft; -+ pChild->Accept( this ); -+ //Set right on pRightMost from pChild -+ pRightMost->SetRight( bodyLeft ); -+ } -+ -+ //Return right -+ pRightMost = right; -+} -+ -+void SmCaretPosGraphBuildingVisitor::Visit( SmMatrixNode* pNode ) -+{ -+ SmCaretPosGraphEntry *left = pRightMost, -+ *right = pGraph->Add( SmCaretPos( pNode, 1 ) ); -+ -+ for ( USHORT i = 0; i < pNode->GetNumRows( ); i++ ) { -+ SmCaretPosGraphEntry* r = left; -+ for ( USHORT j = 0; j < pNode->GetNumCols( ); j++ ){ -+ SmNode* pSubNode = pNode->GetSubNode( i * pNode->GetNumCols( ) + j ); -+ -+ pRightMost = pGraph->Add( SmCaretPos( pSubNode, 0 ), r ); -+ if( j != 0 || ( pNode->GetNumRows( ) - 1 ) / 2 == i ) -+ r->SetRight( pRightMost ); -+ -+ pSubNode->Accept( this ); -+ -+ r = pRightMost; -+ } -+ pRightMost->SetRight( right ); -+ if( ( pNode->GetNumRows( ) - 1 ) / 2 == i ) -+ right->SetLeft( pRightMost ); -+ } -+ -+ pRightMost = 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 ) -+{ -+ j_assert( pNode->GetText( ).Len( ) > 0, "Empty SmTextNode is bad" ); -+ -+ int size = pNode->GetText( ).Len( ); -+ for( int i = 1; i <= size; i++ ){ -+ SmCaretPosGraphEntry* pRight = pRightMost; -+ pRightMost = pGraph->Add( SmCaretPos( pNode, i ), pRight ); -+ pRight->SetRight( pRightMost ); -+ } -+} -+ -+/** 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 ); -+ -+ SmCaretPosGraphEntry *left, -+ *right, -+ *numLeft, -+ *denomLeft; -+ -+ //Set left -+ left = pRightMost; -+ j_assert( pRightMost, "There must be a position infront of this" ); -+ -+ //Create right -+ right = pGraph->Add( SmCaretPos( pNode, 1 ) ); -+ -+ //Create numLeft -+ numLeft = pGraph->Add( SmCaretPos( pNum, 0 ), left ); -+ left->SetRight( numLeft ); -+ -+ //Visit pNum -+ pRightMost = numLeft; -+ pNum->Accept( this ); -+ pRightMost->SetRight( right ); -+ right->SetLeft( pRightMost ); -+ -+ //Create denomLeft -+ denomLeft = pGraph->Add( SmCaretPos( pDenom, 0 ), left ); -+ -+ //Visit pDenom -+ pRightMost = denomLeft; -+ pDenom->Accept( this ); -+ pRightMost->SetRight( right ); -+ -+ //Set return parameter -+ pRightMost = right; -+} -+ -+/** Build SmCaretPosGraph for SmVerticalBraceNode -+ * -+ * Lines in an SmVerticalBraceNode: -+ * \code -+ * pScript -+ * ________ -+ * / \ -+ * pBody -+ * \endcode -+ * -+ */ -+void SmCaretPosGraphBuildingVisitor::Visit( SmVerticalBraceNode* pNode ) -+{ -+ SmNode *pBody = pNode->GetSubNode( 0 ), -+ *pScript = pNode->GetSubNode( 2 ); -+ //None of these children can be NULL -+ -+ SmCaretPosGraphEntry *left, -+ *bodyLeft, -+ *scriptLeft, -+ *right; -+ -+ left = pRightMost; -+ -+ //Create right -+ right = pGraph->Add( SmCaretPos( pNode, 1 ) ); -+ -+ //Create bodyLeft -+ bodyLeft = pGraph->Add( SmCaretPos( pBody, 0 ), left ); -+ left->SetRight( bodyLeft ); -+ pRightMost = bodyLeft; -+ pBody->Accept( this ); -+ pRightMost->SetRight( right ); -+ right->SetLeft( pRightMost ); -+ -+ //Create script -+ scriptLeft = pGraph->Add( SmCaretPos( pScript, 0 ), left ); -+ pRightMost = scriptLeft; -+ pScript->Accept( this ); -+ pRightMost->SetRight( right ); -+ -+ //Set return value -+ pRightMost = 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 = pRightMost; -+ -+ //Create right -+ right = pGraph->Add( SmCaretPos( pNode, 1 ) ); -+ -+ //Create left A -+ leftA = pGraph->Add( SmCaretPos( A, 0 ), left ); -+ left->SetRight( leftA ); -+ -+ //Visit A -+ pRightMost = leftA; -+ A->Accept( this ); -+ rightA = pRightMost; -+ -+ //Create left B -+ leftB = pGraph->Add( SmCaretPos( B, 0 ), rightA ); -+ rightA->SetRight( leftB ); -+ -+ //Visit B -+ pRightMost = leftB; -+ B->Accept( this ); -+ pRightMost->SetRight( right ); -+ right->SetLeft( pRightMost ); -+ -+ //Set return value -+ pRightMost = right; -+} -+ -+ -+//Straigt forward ( I think ) -+void SmCaretPosGraphBuildingVisitor::Visit( SmBinHorNode* pNode ) -+{ -+ SmNodeIterator it( pNode ); -+ while( it.Next( ) ) -+ it->Accept( this ); -+} -+void SmCaretPosGraphBuildingVisitor::Visit( SmUnHorNode* pNode ) -+{ -+ // Unary operator node -+ SmNodeIterator it( pNode ); -+ while( it.Next( ) ) -+ it->Accept( this ); -+ -+} -+ -+void SmCaretPosGraphBuildingVisitor::Visit( SmExpressionNode* pNode ) -+{ -+ SmNodeIterator it( pNode ); -+ while( it.Next( ) ) -+ it->Accept( this ); -+} -+ -+void SmCaretPosGraphBuildingVisitor::Visit( SmFontNode* pNode ) -+{ -+ //Has only got one child, should act as an expression if possible -+ SmNodeIterator it( pNode ); -+ while( it.Next( ) ) -+ it->Accept( this ); -+} -+ -+/** Build SmCaretPosGraph for SmBracebodyNode -+ * Acts as an SmExpressionNode -+ * -+ * Below is an example of a formula tree that has multiple children for SmBracebodyNode -+ * \dot -+ * digraph { -+ * labelloc = "t"; -+ * label= "Equation: \"lbrace i mline i in setZ rbrace\""; -+ * n0 [label="SmTableNode"]; -+ * n0 -> n1 [label="0"]; -+ * n1 [label="SmLineNode"]; -+ * n1 -> n2 [label="0"]; -+ * n2 [label="SmExpressionNode"]; -+ * n2 -> n3 [label="0"]; -+ * n3 [label="SmBraceNode"]; -+ * n3 -> n4 [label="0"]; -+ * n4 [label="SmMathSymbolNode: {"]; -+ * n3 -> n5 [label="1"]; -+ * n5 [label="SmBracebodyNode"]; -+ * n5 -> n6 [label="0"]; -+ * n6 [label="SmExpressionNode"]; -+ * n6 -> n7 [label="0"]; -+ * n7 [label="SmTextNode: i"]; -+ * n5 -> n8 [label="1"]; -+ * n8 [label="SmMathSymbolNode: ∣"]; -+ * n5 -> n9 [label="2"]; -+ * n9 [label="SmExpressionNode"]; -+ * n9 -> n10 [label="0"]; -+ * n10 [label="SmBinHorNode"]; -+ * n10 -> n11 [label="0"]; -+ * n11 [label="SmTextNode: i"]; -+ * n10 -> n12 [label="1"]; -+ * n12 [label="SmMathSymbolNode: ∈"]; -+ * n10 -> n13 [label="2"]; -+ * n13 [label="SmMathSymbolNode: ℤ"]; -+ * n3 -> n14 [label="2"]; -+ * n14 [label="SmMathSymbolNode: }"]; -+ * } -+ * \enddot -+ */ -+void SmCaretPosGraphBuildingVisitor::Visit( SmBracebodyNode* pNode ) -+{ -+ SmNodeIterator it( pNode ); -+ while( it.Next( ) ) -+ it->Accept( this ); -+} -+ -+/** Build SmCaretPosGraph for SmAlignNode -+ * Acts as an SmExpressionNode, as it only has one child this okay -+ */ -+void SmCaretPosGraphBuildingVisitor::Visit( SmAlignNode* pNode ) -+{ -+ SmNodeIterator it( pNode ); -+ while( it.Next( ) ) -+ it->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 -+ j_assert( pBody, "pBody cannot be NULL" ); -+ -+ SmCaretPosGraphEntry *left, -+ *right, -+ *bodyLeft, -+ *bodyRight; -+ -+ //Get left and save it -+ j_assert( pRightMost, "There must be a position infront of this" ); -+ left = pRightMost; -+ -+ //Create body left -+ bodyLeft = pGraph->Add( SmCaretPos( pBody, 0 ), left ); -+ left->SetRight( bodyLeft ); -+ -+ //Create right -+ right = pGraph->Add( SmCaretPos( pNode, 1 ) ); -+ -+ //Visit body -+ pRightMost = bodyLeft; -+ pBody->Accept( this ); -+ bodyRight = pRightMost; -+ bodyRight->SetRight( right ); -+ right->SetLeft( bodyRight ); -+ -+ //Visit pExtra -+ if( pExtra ){ -+ pRightMost = pGraph->Add( SmCaretPos( pExtra, 0 ), left ); -+ pExtra->Accept( this ); -+ pRightMost->SetRight( bodyLeft ); -+ } -+ -+ pRightMost = right; -+} -+ -+/** Build SmCaretPosGraph for SmPlaceNode -+ * Consider this a single character. -+ */ -+void SmCaretPosGraphBuildingVisitor::Visit( SmPlaceNode* pNode ) -+{ -+ SmCaretPosGraphEntry* right = pGraph->Add( SmCaretPos( pNode, 1 ), pRightMost ); -+ pRightMost->SetRight( right ); -+ pRightMost = 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 an other 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 = pGraph->Add( SmCaretPos( pNode, 1 ), pRightMost ); -+ pRightMost->SetRight( right ); -+ pRightMost = 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->GetSubNode( 1 ); -+ -+ SmCaretPosGraphEntry *left = pRightMost, -+ *right = pGraph->Add( SmCaretPos( pNode, 1 ) ); -+ -+ pRightMost = pGraph->Add( SmCaretPos( pBody, 0 ), left ); -+ left->SetRight( pRightMost ); -+ -+ pBody->Accept( this ); -+ pRightMost->SetRight( right ); -+ right->SetLeft( pRightMost ); -+ -+ pRightMost = right; -+} -+ -+/** Build SmCaretPosGraph for SmAttributNode -+ * -+ * Lines in an SmAttributNode: -+ * \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 SmAttributNode tells how the attribute should be -+ * scaled ). -+ */ -+void SmCaretPosGraphBuildingVisitor::Visit( SmAttributNode* pNode ) -+{ -+ SmNode *pAttr = pNode->GetSubNode( 0 ), -+ *pBody = pNode->GetSubNode( 1 ); -+ //None of the children can be NULL -+ -+ SmCaretPosGraphEntry *left = pRightMost, -+ *attrLeft, -+ *bodyLeft, -+ *bodyRight, -+ *right; -+ -+ //Creating bodyleft -+ bodyLeft = pGraph->Add( SmCaretPos( pBody, 0 ), left ); -+ left->SetRight( bodyLeft ); -+ -+ //Creating right -+ right = pGraph->Add( SmCaretPos( pNode, 1 ) ); -+ -+ //Visit the body -+ pRightMost = bodyLeft; -+ pBody->Accept( this ); -+ bodyRight = pRightMost; -+ bodyRight->SetRight( right ); -+ right->SetLeft( bodyRight ); -+ -+ //Create attrLeft -+ attrLeft = pGraph->Add( SmCaretPos( pAttr, 0 ), left ); -+ -+ //Visit attribute -+ pRightMost = attrLeft; -+ pAttr->Accept( this ); -+ pRightMost->SetRight( right ); -+ -+ //Set return value -+ pRightMost = right; -+} -+ -+//Consider these single symboles -+void SmCaretPosGraphBuildingVisitor::Visit( SmSpecialNode* pNode ) -+{ -+ SmCaretPosGraphEntry* right = pGraph->Add( SmCaretPos( pNode, 1 ), pRightMost ); -+ pRightMost->SetRight( right ); -+ pRightMost = right; -+} -+void SmCaretPosGraphBuildingVisitor::Visit( SmGlyphSpecialNode* pNode ) -+{ -+ SmCaretPosGraphEntry* right = pGraph->Add( SmCaretPos( pNode, 1 ), pRightMost ); -+ pRightMost->SetRight( right ); -+ pRightMost = right; -+} -+void SmCaretPosGraphBuildingVisitor::Visit( SmMathSymbolNode* pNode ) -+{ -+ SmCaretPosGraphEntry* right = pGraph->Add( SmCaretPos( pNode, 1 ), pRightMost ); -+ pRightMost->SetRight( right ); -+ pRightMost = right; -+} -+ -+void SmCaretPosGraphBuildingVisitor::Visit( SmRootSymbolNode* ) -+{ -+ //Do nothing -+} -+void SmCaretPosGraphBuildingVisitor::Visit( SmRectangleNode* ) -+{ -+ //Do nothing -+} -+void SmCaretPosGraphBuildingVisitor::Visit( SmPolyLineNode* ) -+{ -+ //Do nothing -+} -+ -+/////////////////////////////// SmCloningVisitor /////////////////////////////// -+ -+SmNode* SmCloningVisitor::Clone( SmNode* pNode ) -+{ -+ SmNode* pCurrResult = pResult; -+ pNode->Accept( this ); -+ SmNode* pClone = pResult; -+ pResult = pCurrResult; -+ return pClone; -+} -+ -+void SmCloningVisitor::CloneNodeAttr( SmNode* 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 = pResult; -+ -+ //Create array for holding clones -+ USHORT nSize = pSource->GetNumSubNodes( ); -+ SmNodeArray aNodes( nSize ); -+ -+ //Clone children -+ SmNode* pKid; -+ for( USHORT i = 0; i < nSize; i++ ){ -+ if( NULL != ( pKid = pSource->GetSubNode( i ) ) ) -+ pKid->Accept( this ); -+ else -+ pResult = NULL; -+ aNodes[i] = pResult; -+ } -+ -+ //Set subnodes of pTarget -+ pTarget->SetSubNodes( aNodes ); -+ -+ //Restore result as where prior to call -+ pResult = pCurrResult; -+} -+ -+void SmCloningVisitor::Visit( SmTableNode* pNode ) -+{ -+ SmTableNode* pClone = new SmTableNode( pNode->GetToken( ) ); -+ CloneNodeAttr( pNode, pClone ); -+ CloneKids( pNode, pClone ); -+ pResult = pClone; -+} -+ -+void SmCloningVisitor::Visit( SmBraceNode* pNode ) -+{ -+ SmBraceNode* pClone = new SmBraceNode( pNode->GetToken( ) ); -+ CloneNodeAttr( pNode, pClone ); -+ CloneKids( pNode, pClone ); -+ pResult = pClone; -+} -+ -+ -+void SmCloningVisitor::Visit( SmBracebodyNode* pNode ) -+{ -+ SmBracebodyNode* pClone = new SmBracebodyNode( pNode->GetToken( ) ); -+ CloneNodeAttr( pNode, pClone ); -+ CloneKids( pNode, pClone ); -+ pResult = pClone; -+} -+ -+void SmCloningVisitor::Visit( SmOperNode* pNode ) -+{ -+ SmOperNode* pClone = new SmOperNode( pNode->GetToken( ) ); -+ CloneNodeAttr( pNode, pClone ); -+ CloneKids( pNode, pClone ); -+ pResult = pClone; -+} -+ -+void SmCloningVisitor::Visit( SmAlignNode* pNode ) -+{ -+ SmAlignNode* pClone = new SmAlignNode( pNode->GetToken( ) ); -+ CloneNodeAttr( pNode, pClone ); -+ CloneKids( pNode, pClone ); -+ pResult = pClone; -+} -+ -+void SmCloningVisitor::Visit( SmAttributNode* pNode ) -+{ -+ SmAttributNode* pClone = new SmAttributNode( pNode->GetToken( ) ); -+ CloneNodeAttr( pNode, pClone ); -+ CloneKids( pNode, pClone ); -+ pResult = pClone; -+} -+ -+void SmCloningVisitor::Visit( SmFontNode* pNode ) -+{ -+ SmFontNode* pClone = new SmFontNode( pNode->GetToken( ) ); -+ pClone->SetSizeParameter( pNode->GetSizeParameter( ), pNode->GetSizeType( ) ); -+ CloneNodeAttr( pNode, pClone ); -+ CloneKids( pNode, pClone ); -+ pResult = pClone; -+} -+ -+void SmCloningVisitor::Visit( SmUnHorNode* pNode ) -+{ -+ SmUnHorNode* pClone = new SmUnHorNode( pNode->GetToken( ) ); -+ CloneNodeAttr( pNode, pClone ); -+ CloneKids( pNode, pClone ); -+ pResult = pClone; -+} -+ -+void SmCloningVisitor::Visit( SmBinHorNode* pNode ) -+{ -+ SmBinHorNode* pClone = new SmBinHorNode( pNode->GetToken( ) ); -+ CloneNodeAttr( pNode, pClone ); -+ CloneKids( pNode, pClone ); -+ pResult = pClone; -+} -+ -+void SmCloningVisitor::Visit( SmBinVerNode* pNode ) -+{ -+ SmBinVerNode* pClone = new SmBinVerNode( pNode->GetToken( ) ); -+ CloneNodeAttr( pNode, pClone ); -+ CloneKids( pNode, pClone ); -+ pResult = pClone; -+} -+ -+void SmCloningVisitor::Visit( SmBinDiagonalNode* pNode ) -+{ -+ SmBinDiagonalNode *pClone = new SmBinDiagonalNode( pNode->GetToken( ) ); -+ pClone->SetAscending( pNode->IsAscending( ) ); -+ CloneNodeAttr( pNode, pClone ); -+ CloneKids( pNode, pClone ); -+ pResult = pClone; -+} -+ -+void SmCloningVisitor::Visit( SmSubSupNode* pNode ) -+{ -+ SmSubSupNode *pClone = new SmSubSupNode( pNode->GetToken( ) ); -+ pClone->SetUseLimits( pNode->IsUseLimits( ) ); -+ CloneNodeAttr( pNode, pClone ); -+ CloneKids( pNode, pClone ); -+ pResult = pClone; -+} -+ -+void SmCloningVisitor::Visit( SmMatrixNode* pNode ) -+{ -+ SmMatrixNode *pClone = new SmMatrixNode( pNode->GetToken( ) ); -+ pClone->SetRowCol( pNode->GetNumRows( ), pNode->GetNumCols( ) ); -+ CloneNodeAttr( pNode, pClone ); -+ CloneKids( pNode, pClone ); -+ pResult = pClone; -+} -+ -+void SmCloningVisitor::Visit( SmPlaceNode* pNode ) -+{ -+ pResult = new SmPlaceNode( pNode->GetToken( ) ); -+ CloneNodeAttr( pNode, pResult ); -+} -+ -+void SmCloningVisitor::Visit( SmTextNode* pNode ) -+{ -+ SmTextNode* pClone = new SmTextNode( pNode->GetToken( ), pNode->GetFontDesc( ) ); -+ pClone->ChangeText( pNode->GetText( ) ); -+ CloneNodeAttr( pNode, pClone ); -+ pResult = pClone; -+} -+ -+void SmCloningVisitor::Visit( SmSpecialNode* pNode ) -+{ -+ pResult = new SmSpecialNode( pNode->GetToken( ) ); -+ CloneNodeAttr( pNode, pResult ); -+} -+ -+void SmCloningVisitor::Visit( SmGlyphSpecialNode* pNode ) -+{ -+ pResult = new SmGlyphSpecialNode( pNode->GetToken( ) ); -+ CloneNodeAttr( pNode, pResult ); -+} -+ -+void SmCloningVisitor::Visit( SmMathSymbolNode* pNode ) -+{ -+ pResult = new SmMathSymbolNode( pNode->GetToken( ) ); -+ CloneNodeAttr( pNode, pResult ); -+} -+ -+void SmCloningVisitor::Visit( SmBlankNode* pNode ) -+{ -+ SmBlankNode* pClone = new SmBlankNode( pNode->GetToken( ) ); -+ pClone->SetBlankNum( pNode->GetBlankNum( ) ); -+ pResult = pClone; -+ CloneNodeAttr( pNode, pResult ); -+} -+ -+void SmCloningVisitor::Visit( SmErrorNode* pNode ) -+{ -+ //PE_NONE is used the information have been discarded and isn't used -+ pResult = new SmErrorNode( PE_NONE, pNode->GetToken( ) ); -+ CloneNodeAttr( pNode, pResult ); -+} -+ -+void SmCloningVisitor::Visit( SmLineNode* pNode ) -+{ -+ SmLineNode* pClone = new SmLineNode( pNode->GetToken( ) ); -+ CloneNodeAttr( pNode, pClone ); -+ CloneKids( pNode, pClone ); -+ pResult = pClone; -+} -+ -+void SmCloningVisitor::Visit( SmExpressionNode* pNode ) -+{ -+ SmExpressionNode* pClone = new SmExpressionNode( pNode->GetToken( ) ); -+ CloneNodeAttr( pNode, pClone ); -+ CloneKids( pNode, pClone ); -+ pResult = pClone; -+} -+ -+void SmCloningVisitor::Visit( SmPolyLineNode* pNode ) -+{ -+ pResult = new SmPolyLineNode( pNode->GetToken( ) ); -+ CloneNodeAttr( pNode, pResult ); -+} -+ -+void SmCloningVisitor::Visit( SmRootNode* pNode ) -+{ -+ SmRootNode* pClone = new SmRootNode( pNode->GetToken( ) ); -+ CloneNodeAttr( pNode, pClone ); -+ CloneKids( pNode, pClone ); -+ pResult = pClone; -+} -+ -+void SmCloningVisitor::Visit( SmRootSymbolNode* pNode ) -+{ -+ pResult = new SmRootSymbolNode( pNode->GetToken( ) ); -+ CloneNodeAttr( pNode, pResult ); -+} -+ -+void SmCloningVisitor::Visit( SmRectangleNode* pNode ) -+{ -+ pResult = new SmRectangleNode( pNode->GetToken( ) ); -+ CloneNodeAttr( pNode, pResult ); -+} -+ -+void SmCloningVisitor::Visit( SmVerticalBraceNode* pNode ) -+{ -+ SmVerticalBraceNode* pClone = new SmVerticalBraceNode( pNode->GetToken( ) ); -+ CloneNodeAttr( pNode, pClone ); -+ CloneKids( pNode, pClone ); -+ pResult = pClone; -+} -+ -+/////////////////////////////// SmSelectionDrawingVisitor /////////////////////////////// -+ -+SmSelectionDrawingVisitor::SmSelectionDrawingVisitor( OutputDevice& rDevice, SmNode* pTree, Point Offset ) -+ : rDev( rDevice ) { -+ bHasSelectionArea = FALSE; -+ -+ //Visit everything -+ j_assert( pTree, "pTree can't be null!" ); -+ if( pTree ) -+ pTree->Accept( this ); -+ -+ //Draw selection if there's any -+ if( bHasSelectionArea ){ -+ aSelectionArea.Move( Offset.X( ), Offset.Y( ) ); -+ -+ //Save device state -+ rDev.Push( PUSH_LINECOLOR | PUSH_FILLCOLOR ); -+ //Change colors -+ rDev.SetLineColor( ); -+ rDev.SetFillColor( Color( COL_LIGHTGRAY ) ); -+ -+ //Draw rectangle -+ rDev.DrawRect( aSelectionArea ); -+ -+ //Restore device state -+ rDev.Pop( ); -+ } -+} -+ -+void SmSelectionDrawingVisitor::ExtendSelectionArea( Rectangle aArea ) -+{ -+ if ( ! bHasSelectionArea ) { -+ aSelectionArea = aArea; -+ bHasSelectionArea = true; -+ } else -+ aSelectionArea.Union( aArea ); -+} -+ -+void SmSelectionDrawingVisitor::DefaultVisit( SmNode* pNode ) -+{ -+ if( pNode->IsSelected( ) ) -+ ExtendSelectionArea( pNode->AsRectangle( ) ); -+ VisitChildren( pNode ); -+} -+ -+void SmSelectionDrawingVisitor::VisitChildren( SmNode* pNode ) -+{ -+ SmNodeIterator it( pNode ); -+ while( it.Next( ) ) -+ it->Accept( this ); -+} -+ -+void SmSelectionDrawingVisitor::Visit( SmTextNode* pNode ) -+{ -+ if( pNode->IsSelected( ) ){ -+ rDev.Push( PUSH_TEXTCOLOR | PUSH_FONT ); -+ -+ rDev.SetFont( pNode->GetFont( ) ); -+ Point Position = pNode->GetTopLeft( ); -+ long left = Position.getX( ) + rDev.GetTextWidth( pNode->GetText( ), 0, pNode->GetSelectionStart( ) ); -+ long right = Position.getX( ) + rDev.GetTextWidth( pNode->GetText( ), 0, pNode->GetSelectionEnd( ) ); -+ long top = Position.getY( ); -+ long bottom = top + pNode->GetHeight( ); -+ Rectangle rect( left, top, right, bottom ); -+ -+ ExtendSelectionArea( rect ); -+ -+ rDev.Pop( ); -+ } -+} -+ -+ -+/////////////////////////////// SmNodeToTextVisitor /////////////////////////////// -+ -+void SmNodeToTextVisitor::Visit( SmTableNode* pNode ) -+{ -+ if( pNode->GetToken( ).eType == TBINOM ) { -+ Append( "binom" ); -+ LineToText( pNode->GetSubNode( 0 ) ); -+ LineToText( pNode->GetSubNode( 1 ) ); -+ } else if( pNode->GetToken( ).eType == TSTACK ) { -+ Append( "stack{ " ); -+ SmNodeIterator it( pNode ); -+ it.Next( ); -+ while( true ) { -+ LineToText( it.Current( ) ); -+ if( it.Next( ) ) { -+ Separate( ); -+ Append( "## " ); -+ }else -+ break; -+ } -+ Separate( ); -+ Append( "}" ); -+ } else { //Assume it's a toplevel table, containing lines -+ SmNodeIterator it( pNode ); -+ it.Next( ); -+ while( true ) { -+ Separate( ); -+ it->Accept( this ); -+ if( it.Next( ) ) { -+ Separate( ); -+ Append( "newline" ); -+ }else -+ break; -+ } -+ } -+} -+ -+void SmNodeToTextVisitor::Visit( SmBraceNode* pNode ) -+{ -+ SmNode *pLeftBrace = pNode->GetSubNode( 0 ), -+ *pBody = pNode->GetSubNode( 1 ), -+ *pRightBrace = pNode->GetSubNode( 2 ); -+ //Handle special case where it's absolute function -+ if( pNode->GetToken( ).eType == TABS ) { -+ Append( "abs" ); -+ LineToText( pBody ); -+ } else { -+ if( pNode->GetScaleMode( ) == SCALE_HEIGHT ) -+ Append( "left " ); -+ pLeftBrace->Accept( this ); -+ Separate( ); -+ pBody->Accept( this ); -+ Separate( ); -+ if( pNode->GetScaleMode( ) == SCALE_HEIGHT ) -+ Append( "right " ); -+ pRightBrace->Accept( this ); -+ } -+} -+ -+void SmNodeToTextVisitor::Visit( SmBracebodyNode* pNode ) -+{ -+ SmNodeIterator it( pNode ); -+ while( it.Next( ) ){ -+ Separate( ); -+ it->Accept( this ); -+ } -+} -+ -+void SmNodeToTextVisitor::Visit( SmOperNode* pNode ) -+{ -+ Append( pNode->GetToken( ).aText ); -+ Separate( ); -+ if( pNode->GetToken( ).eType == TOPER ){ -+ //There's an SmGlyphSpecialNode if eType == TOPER -+ if( pNode->GetSubNode( 0 )->GetType( ) == NSUBSUP ) -+ Append( pNode->GetSubNode( 0 )->GetSubNode( 0 )->GetToken( ).aText ); -+ else -+ Append( pNode->GetSubNode( 0 )->GetToken( ).aText ); -+ } -+ if( pNode->GetSubNode( 0 )->GetType( ) == NSUBSUP ) { -+ SmSubSupNode *pSubSup = ( SmSubSupNode* )pNode->GetSubNode( 0 ); -+ SmNode* pChild; -+ if( ( pChild = pSubSup->GetSubSup( LSUP ) ) ) { -+ Separate( ); -+ Append( "lsup " ); -+ LineToText( pChild ); -+ } -+ if( ( pChild = pSubSup->GetSubSup( LSUB ) ) ) { -+ Separate( ); -+ Append( "lsub " ); -+ LineToText( pChild ); -+ } -+ if( ( pChild = pSubSup->GetSubSup( RSUP ) ) ) { -+ Separate( ); -+ Append( "rsup " ); -+ LineToText( pChild ); -+ } -+ if( ( pChild = pSubSup->GetSubSup( RSUB ) ) ) { -+ Separate( ); -+ Append( "rsub " ); -+ LineToText( pChild ); -+ } -+ if( ( pChild = pSubSup->GetSubSup( CSUP ) ) ) { -+ Separate( ); -+ Append( "csup " ); -+ LineToText( pChild ); -+ } -+ if( ( pChild = pSubSup->GetSubSup( CSUB ) ) ) { -+ Separate( ); -+ Append( "csub " ); -+ LineToText( pChild ); -+ } -+ } -+ LineToText( pNode->GetSubNode( 1 ) ); -+} -+ -+void SmNodeToTextVisitor::Visit( SmAlignNode* pNode ) -+{ -+ Append( pNode->GetToken( ).aText ); -+ LineToText( pNode->GetSubNode( 0 ) ); -+} -+ -+void SmNodeToTextVisitor::Visit( SmAttributNode* pNode ) -+{ -+ Append( pNode->GetToken( ).aText ); -+ LineToText( pNode->GetSubNode( 1 ) ); -+} -+ -+void SmNodeToTextVisitor::Visit( SmFontNode* pNode ) -+{ -+ switch ( pNode->GetToken( ).eType ) -+ { -+ case TBOLD: -+ Append( "bold " ); -+ break; -+ case TNBOLD: -+ Append( "nbold " ); -+ break; -+ case TITALIC: -+ Append( "italic " ); -+ break; -+ case TNITALIC: -+ Append( "nitalic " ); -+ break; -+ case TPHANTOM: -+ Append( "phantom " ); -+ break; -+ case TSIZE: -+ { -+ Append( "size " ); -+ switch ( pNode->GetSizeType( ) ) -+ { -+ case FNTSIZ_PLUS: -+ Append( "+" ); -+ break; -+ case FNTSIZ_MINUS: -+ Append( "-" ); -+ break; -+ case FNTSIZ_MULTIPLY: -+ Append( "*" ); -+ break; -+ case FNTSIZ_DIVIDE: -+ Append( "/" ); -+ break; -+ case FNTSIZ_ABSOLUT: -+ default: -+ break; -+ } -+ Append( String( ::rtl::math::doubleToUString( -+ static_cast<double>( pNode->GetSizeParameter( ) ), -+ rtl_math_StringFormat_Automatic, -+ rtl_math_DecimalPlaces_Max, '.', sal_True ) ) ); -+ Append( " " ); -+ } -+ break; -+ case TBLACK: -+ Append( "color black " ); -+ break; -+ case TWHITE: -+ Append( "color white " ); -+ break; -+ case TRED: -+ Append( "color red " ); -+ break; -+ case TGREEN: -+ Append( "color green " ); -+ break; -+ case TBLUE: -+ Append( "color blue " ); -+ break; -+ case TCYAN: -+ Append( "color cyan " ); -+ break; -+ case TMAGENTA: -+ Append( "color magenta " ); -+ break; -+ case TYELLOW: -+ Append( "color yellow " ); -+ break; -+ case TSANS: -+ Append( "font sans " ); -+ break; -+ case TSERIF: -+ Append( "font serif " ); -+ break; -+ case TFIXED: -+ Append( "font fixed " ); -+ break; -+ default: -+ break; -+ } -+ LineToText( pNode->GetSubNode( 1 ) ); -+} -+ -+void SmNodeToTextVisitor::Visit( SmUnHorNode* pNode ) -+{ -+ Append( "{" ); -+ SmNodeIterator it( pNode, pNode->GetSubNode( 1 )->GetToken( ).eType == TFACT ); -+ while( it.Next( ) ) { -+ Separate( ); -+ it->Accept( this ); -+ } -+ Append( " }" ); -+} -+ -+void SmNodeToTextVisitor::Visit( SmBinHorNode* pNode ) -+{ -+ Append( "{" ); -+ SmNode *pLeft = pNode->GetSubNode( 0 ), -+ *pOper = pNode->GetSubNode( 1 ), -+ *pRight = pNode->GetSubNode( 2 ); -+ Separate( ); -+ pLeft->Accept( this ); -+ Separate( ); -+ pOper->Accept( this ); -+ Separate( ); -+ pRight->Accept( this ); -+ Separate( ); -+ Append( "}" ); -+} -+ -+void SmNodeToTextVisitor::Visit( SmBinVerNode* pNode ) -+{ -+ SmNode *pNum = pNode->GetSubNode( 0 ), -+ *pDenom = pNode->GetSubNode( 2 ); -+ LineToText( pNum ); -+ Append( "over" ); -+ LineToText( pDenom ); -+} -+ -+void SmNodeToTextVisitor::Visit( SmBinDiagonalNode* pNode ) -+{ -+ SmNode *pLeftOperand = pNode->GetSubNode( 0 ), -+ *pRightOperand = pNode->GetSubNode( 1 ); -+ LineToText( pLeftOperand ); -+ Separate( ); -+ Append( "wideslash " ); -+ LineToText( pRightOperand ); -+} -+ -+void SmNodeToTextVisitor::Visit( SmSubSupNode* pNode ) -+{ -+ LineToText( pNode->GetBody( ) ); -+ SmNode *pChild; -+ if( ( pChild = pNode->GetSubSup( LSUP ) ) ) { -+ Separate( ); -+ Append( "lsup " ); -+ LineToText( pChild ); -+ } -+ if( ( pChild = pNode->GetSubSup( LSUB ) ) ) { -+ Separate( ); -+ Append( "lsub " ); -+ LineToText( pChild ); -+ } -+ if( ( pChild = pNode->GetSubSup( RSUP ) ) ) { -+ Separate( ); -+ Append( "rsup " ); -+ LineToText( pChild ); -+ } -+ if( ( pChild = pNode->GetSubSup( RSUB ) ) ) { -+ Separate( ); -+ Append( "rsub " ); -+ LineToText( pChild ); -+ } -+ if( ( pChild = pNode->GetSubSup( CSUP ) ) ) { -+ Separate( ); -+ Append( "csup " ); -+ LineToText( pChild ); -+ } -+ if( ( pChild = pNode->GetSubSup( CSUB ) ) ) { -+ Separate( ); -+ Append( "csub " ); -+ LineToText( pChild ); -+ } -+} -+ -+void SmNodeToTextVisitor::Visit( SmMatrixNode* pNode ) -+{ -+ Append( "matrix{" ); -+ for ( USHORT i = 0; i < pNode->GetNumRows( ); i++ ) { -+ for ( USHORT j = 0; j < pNode->GetNumCols( ); j++ ) { -+ SmNode* pSubNode = pNode->GetSubNode( i * pNode->GetNumCols( ) + j ); -+ Separate( ); -+ pSubNode->Accept( this ); -+ Separate( ); -+ if( j != pNode->GetNumCols( ) - 1 ) -+ Append( "#" ); -+ } -+ Separate( ); -+ if( i != pNode->GetNumRows( ) - 1 ) -+ Append( "##" ); -+ } -+ Append( "}" ); -+} -+ -+void SmNodeToTextVisitor::Visit( SmPlaceNode* ) -+{ -+ Append( "<?>" ); -+} -+ -+void SmNodeToTextVisitor::Visit( SmTextNode* pNode ) -+{ -+ //TODO: This method might need improvements, see SmTextNode::CreateTextFromNode -+ if( pNode->GetToken( ).eType == TTEXT ) -+ Append( "\"" ); -+ Append( pNode->GetText( ) ); -+ if( pNode->GetToken( ).eType == TTEXT ) -+ Append( "\"" ); -+} -+ -+void SmNodeToTextVisitor::Visit( SmSpecialNode* pNode ) -+{ -+ Append( "%" ); -+ Append( pNode->GetToken( ).aText ); -+} -+ -+void SmNodeToTextVisitor::Visit( SmGlyphSpecialNode* pNode ) -+{ -+ if( pNode->GetToken( ).eType == TBOPER ) -+ Append( "boper " ); -+ else -+ Append( "uoper " ); -+ Append( pNode->GetToken( ).aText ); -+} -+ -+void SmNodeToTextVisitor::Visit( SmMathSymbolNode* pNode ) -+{ -+ Append( pNode->GetToken( ).aText ); -+} -+ -+void SmNodeToTextVisitor::Visit( SmBlankNode* pNode ) -+{ -+ Append( pNode->GetToken( ).aText ); -+} -+ -+void SmNodeToTextVisitor::Visit( SmErrorNode* ) -+{ -+} -+ -+void SmNodeToTextVisitor::Visit( SmLineNode* pNode ) -+{ -+ SmNodeIterator it( pNode ); -+ while( it.Next( ) ){ -+ Separate( ); -+ it->Accept( this ); -+ } -+} -+ -+void SmNodeToTextVisitor::Visit( SmExpressionNode* pNode ) -+{ -+ Append( "{ " ); -+ SmNodeIterator it( pNode ); -+ while( it.Next( ) ) { -+ it->Accept( this ); -+ Separate( ); -+ } -+ Append( "}" ); -+} -+ -+void SmNodeToTextVisitor::Visit( SmPolyLineNode* ) -+{ -+} -+ -+void SmNodeToTextVisitor::Visit( SmRootNode* pNode ) -+{ -+ SmNode *pExtra = pNode->GetSubNode( 0 ), -+ *pBody = pNode->GetSubNode( 2 ); -+ if( pExtra ) { -+ Append( "nroot" ); -+ LineToText( pExtra ); -+ } else -+ Append( "sqrt" ); -+ LineToText( pBody ); -+} -+ -+void SmNodeToTextVisitor::Visit( SmRootSymbolNode* ) -+{ -+} -+ -+void SmNodeToTextVisitor::Visit( SmRectangleNode* ) -+{ -+} -+ -+void SmNodeToTextVisitor::Visit( SmVerticalBraceNode* pNode ) -+{ -+ SmNode *pBody = pNode->GetSubNode( 0 ), -+ *pScript = pNode->GetSubNode( 2 ); -+ LineToText( pBody ); -+ Append( pNode->GetToken( ).aText ); -+ LineToText( pScript ); -+} -+ |