summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonas Jensen <jopsen@gmail.com>2010-09-22 21:39:23 +0200
committerCédric Bosdonnat <cedricbosdo@openoffice.org>2010-09-22 21:41:33 +0200
commitd6b73c9d63b02142b9d48d9fed1573912ee3705e (patch)
tree56cb793afef6d4917bd50d37f70543e31575ee15
parenta0866751f996613466f7e10619c3c3f2ff54c38d (diff)
Pushed Jonas' patches AS IS
-rw-r--r--patches/dev300/symbols-20-august.diff238
-rw-r--r--patches/dev300/vEdit-13-August.diff7896
2 files changed, 8134 insertions, 0 deletions
diff --git a/patches/dev300/symbols-20-august.diff b/patches/dev300/symbols-20-august.diff
new file mode 100644
index 000000000..6d919944f
--- /dev/null
+++ b/patches/dev300/symbols-20-august.diff
@@ -0,0 +1,238 @@
+diff --git extras/source/truetype/symbol/OpenSymbol.sfd extras/source/truetype/symbol/OpenSymbol.sfd
+index e04c03f..d6ecc95 100644
+--- extras/source/truetype/symbol/OpenSymbol.sfd
++++ extras/source/truetype/symbol/OpenSymbol.sfd
+@@ -13,13 +13,14 @@ Descent: 410
+ LayerCount: 2
+ Layer: 0 1 "Back" 1
+ Layer: 1 1 "Fore" 0
++NeedsXUIDChange: 1
+ XUID: [1021 161 2043615882 15846768]
+ FSType: 8
+ OS2Version: 0
+ OS2_WeightWidthSlopeOnly: 0
+ OS2_UseTypoMetrics: 1
+ CreationTime: 1144938807
+-ModificationTime: 1238168413
++ModificationTime: 1281980608
+ PfmFamily: 81
+ TTFWeight: 400
+ TTFWidth: 5
+@@ -591,8 +592,8 @@ NameList: Adobe Glyph List
+ DisplaySize: -24
+ AntiAlias: 1
+ FitToEm: 1
+-WinInfo: 0 45 18
+-BeginChars: 65539 912
++WinInfo: 135 45 18
++BeginChars: 65539 914
+
+ StartChar: .notdef
+ Encoding: 65536 -1 0
+@@ -100162,5 +100163,88 @@ SplineSet
+ EndSplineSet
+ Validated: 1
+ EndChar
++
++StartChar: uni27FC
++Encoding: 10236 10236 912
++Width: 2048
++VWidth: 0
++Flags: W
++LayerCount: 2
++Fore
++SplineSet
++120.605 893.365 m 0,0,1
++ 130.745 894.649 130.745 894.649 139.122 886.971 c 128,-1,2
++ 147.499 879.293 147.499 879.293 147.099 869.08 c 2,3,-1
++ 147.099 657.135 l 1,4,-1
++ 1753.62 657.135 l 1,5,6
++ 1721.36 678.613 1721.36 678.613 1692.21 706.222 c 128,-1,7
++ 1663.06 733.83 1663.06 733.83 1644.5 757.892 c 128,-1,8
++ 1625.94 781.953 1625.94 781.953 1611.99 802.947 c 128,-1,9
++ 1598.04 823.94 1598.04 823.94 1591.94 836.207 c 2,10,-1
++ 1585.83 848.475 l 1,11,12
++ 1580.32 857.07 1580.32 857.07 1583.64 867.92 c 128,-1,13
++ 1586.96 878.77 1586.96 878.77 1596.33 882.81 c 128,-1,14
++ 1605.71 886.85 1605.71 886.85 1615.88 881.814 c 128,-1,15
++ 1626.05 876.778 1626.05 876.778 1628.51 866.872 c 1,16,17
++ 1631.31 860.546 1631.31 860.546 1636.84 849.729 c 128,-1,18
++ 1642.37 838.911 1642.37 838.911 1662.49 809.124 c 128,-1,19
++ 1682.61 779.338 1682.61 779.338 1706.09 753.681 c 128,-1,20
++ 1729.56 728.024 1729.56 728.024 1768.64 699.899 c 128,-1,21
++ 1807.71 671.774 1807.71 671.774 1850.76 656.4 c 1,22,23
++ 1865.45 654.098 1865.45 654.098 1865.48 633.585 c 0,24,25
++ 1865.49 624.275 1865.49 624.275 1861.81 618.755 c 128,-1,26
++ 1858.13 613.236 1858.13 613.236 1854.45 612.371 c 2,27,-1
++ 1850.76 611.507 l 1,28,29
++ 1807.71 596.133 1807.71 596.133 1768.99 568.798 c 128,-1,30
++ 1730.26 541.464 1730.26 541.464 1705.74 513.434 c 128,-1,31
++ 1681.21 485.405 1681.21 485.405 1662.84 459.572 c 128,-1,32
++ 1644.47 433.739 1644.47 433.739 1636.49 417.386 c 2,33,-1
++ 1628.51 401.033 l 1,34,35
++ 1626.05 391.127 1626.05 391.127 1615.88 386.09 c 128,-1,36
++ 1605.71 381.053 1605.71 381.053 1596.33 385.093 c 128,-1,37
++ 1586.96 389.134 1586.96 389.134 1583.64 399.985 c 128,-1,38
++ 1580.32 410.837 1580.32 410.837 1585.83 419.432 c 0,39,40
++ 1587.95 424.066 1587.95 424.066 1592.14 432.201 c 128,-1,41
++ 1596.33 440.337 1596.33 440.337 1611.48 464.117 c 128,-1,42
++ 1626.63 487.897 1626.63 487.897 1644.23 510.113 c 128,-1,43
++ 1661.82 532.328 1661.82 532.328 1690.99 560.441 c 128,-1,44
++ 1720.15 588.555 1720.15 588.555 1752.15 610.037 c 1,45,-1
++ 147.099 610.037 l 1,46,-1
++ 147.099 398.091 l 2,47,48
++ 147.231 388.76 147.231 388.76 140.056 381.483 c 128,-1,49
++ 132.882 374.206 132.882 374.206 123.55 374.206 c 128,-1,50
++ 114.217 374.206 114.217 374.206 107.042 381.483 c 128,-1,51
++ 99.8674 388.76 99.8674 388.76 100 398.091 c 2,52,-1
++ 100 629.171 l 2,53,54
++ 99.1577 633.586 99.1577 633.586 100 638.002 c 2,55,-1
++ 100 869.082 l 2,56,57
++ 99.6602 877.819 99.6602 877.819 105.795 885.049 c 128,-1,58
++ 111.93 892.279 111.93 892.279 120.605 893.365 c 0,0,1
++EndSplineSet
++EndChar
++
++StartChar: uni2312
++Encoding: 8978 8978 913
++Width: 760
++VWidth: 0
++Flags: W
++LayerCount: 2
++Fore
++SplineSet
++30.002 1123.25 m 1,0,1
++ 30.002 1218.06 30.002 1218.06 76.9414 1298.72 c 128,-1,2
++ 123.88 1379.38 123.88 1379.38 204.536 1426.31 c 128,-1,3
++ 285.192 1473.25 285.192 1473.25 380.001 1473.25 c 128,-1,4
++ 474.809 1473.25 474.809 1473.25 555.465 1426.31 c 128,-1,5
++ 636.121 1379.38 636.121 1379.38 683.061 1298.72 c 128,-1,6
++ 730 1218.06 730 1218.06 730 1123.25 c 1,7,-1
++ 643.486 1123.25 l 1,8,9
++ 643.486 1232.78 643.486 1232.78 566.506 1309.76 c 128,-1,10
++ 489.525 1386.74 489.525 1386.74 380.001 1386.74 c 128,-1,11
++ 270.477 1386.74 270.477 1386.74 193.496 1309.76 c 128,-1,12
++ 116.515 1232.78 116.515 1232.78 116.515 1123.25 c 1,13,-1
++ 30.002 1123.25 l 1,0,1
++EndSplineSet
++EndChar
+ EndChars
+ EndSplineFont
+diff --git extras/source/truetype/symbol/opens___.ttf extras/source/truetype/symbol/opens___.ttf
+index b4f169f..b7fb394 100644
+Binary files extras/source/truetype/symbol/opens___.ttf and extras/source/truetype/symbol/opens___.ttf differ
+diff --git officecfg/registry/data/org/openoffice/Office/Math.xcu officecfg/registry/data/org/openoffice/Office/Math.xcu
+index cf11e57..b185902 100644
+--- officecfg/registry/data/org/openoffice/Office/Math.xcu
++++ officecfg/registry/data/org/openoffice/Office/Math.xcu
+@@ -994,5 +994,19 @@
+ <value>Id1</value>
+ </prop>
+ </node>
++ <node oor:name="mapsto" oor:op="replace">
++ <prop oor:name="Char">
++ <value>10236</value>
++ </prop>
++ <prop oor:name="Set">
++ <value>Special</value>
++ </prop>
++ <prop oor:name="Predefined">
++ <value>true</value>
++ </prop>
++ <prop oor:name="FontFormatId">
++ <value>Id1</value>
++ </prop>
++ </node>
+ </node>
+ </oor:component-data>
+diff --git starmath/inc/parse.hxx starmath/inc/parse.hxx
+index 0211d97..b462abb 100644
+--- starmath/inc/parse.hxx
++++ starmath/inc/parse.hxx
+@@ -106,7 +106,7 @@ enum SmTokenType
+ TLEFTARROW, TRIGHTARROW, TUPARROW, TDOWNARROW, TDIVIDES,
+ TNDIBVIDES, TSETN, TSETZ, TSETQ, TSETR,
+ TSETC, TWIDEVEC, TWIDETILDE, TWIDEHAT, TWIDESLASH,
+- TWIDEBACKSLASH, TLDBRACKET, TRDBRACKET,
++ TWIDEBACKSLASH, TLDBRACKET, TRDBRACKET, TBOW,
+ TUNKNOWN, TDEBUG
+ };
+
+diff --git starmath/inc/types.hxx starmath/inc/types.hxx
+index b8cb4d0..4596877 100644
+--- starmath/inc/types.hxx
++++ starmath/inc/types.hxx
+@@ -165,6 +165,7 @@ enum MathSymbol
+ MS_FORALL = (sal_Unicode) 0x2200,
+
+ MS_HAT = (sal_Unicode) 0xE091,
++ MS_BOW = (sal_Unicode) 0x2312,
+ MS_CHECK = (sal_Unicode) 0xE092,
+ MS_BREVE = (sal_Unicode) 0xE093,
+ MS_ACUTE = (sal_Unicode) 0xE094,
+diff --git starmath/source/parse.cxx starmath/source/parse.cxx
+index aab3502..3fc2912 100644
+--- starmath/source/parse.cxx
++++ starmath/source/parse.cxx
+@@ -202,7 +202,7 @@ static const SmTokenTableEntry aTokenTable[] =
+ { "infinity" , TINFINITY, MS_INFINITY, TGSTANDALONE, 5},
+ { "infty" , TINFINITY, MS_INFINITY, TGSTANDALONE, 5},
+ { "int", TINT, MS_INT, TGOPER, 5},
+- { "intersection", TINTERSECT, MS_INTERSECT, TGPRODUCT, 0},
++ { "intersection", TINTERSECT, MS_INTERSECT, TGPRODUCT | TGOPER, 0},
+ { "ital", TITALIC, '\0', TGFONTATTR, 5},
+ { "italic", TITALIC, '\0', TGFONTATTR, 5},
+ { "lambdabar" , TLAMBDABAR, MS_LAMBDABAR, TGSTANDALONE, 5},
+@@ -312,13 +312,14 @@ static const SmTokenTableEntry aTokenTable[] =
+ { "transr", TTRANSR, MS_TRANSR, TGRELATION, 0},
+ { "underbrace", TUNDERBRACE, MS_UNDERBRACE, TGPRODUCT, 5},
+ { "underline", TUNDERLINE, '\0', TGATTRIBUT, 5},
+- { "union", TUNION, MS_UNION, TGSUM, 0},
++ { "union", TUNION, MS_UNION, TGSUM | TGOPER, 0},
+ { "uoper", TUOPER, '\0', TGUNOPER, 5},
+ { "uparrow" , TUPARROW, MS_UPARROW, TGSTANDALONE, 5},
+ { "vec", TVEC, MS_VEC, TGATTRIBUT, 5},
+ { "white", TWHITE, '\0', TGCOLOR, 0},
+ { "widebslash", TWIDEBACKSLASH, MS_BACKSLASH, TGPRODUCT, 0 },
+ { "widehat", TWIDEHAT, MS_HAT, TGATTRIBUT, 5},
++ { "bow", TBOW, MS_BOW, TGATTRIBUT, 5},
+ { "widetilde", TWIDETILDE, MS_TILDE, TGATTRIBUT, 5},
+ { "wideslash", TWIDESLASH, MS_SLASH, TGPRODUCT, 0 },
+ { "widevec", TWIDEVEC, MS_VEC, TGATTRIBUT, 5},
+@@ -1624,6 +1625,8 @@ void SmParser::Oper()
+
+ switch (eType)
+ {
++ case TINTERSECT:
++ case TUNION :
+ case TSUM :
+ case TPROD :
+ case TCOPROD :
+@@ -1781,6 +1784,7 @@ void SmParser::Attribut()
+
+ case TWIDEVEC :
+ case TWIDEHAT :
++ case TBOW :
+ case TWIDETILDE :
+ pAttr = new SmMathSymbolNode(CurToken);
+ eScaleMode = SCALE_WIDTH;
+diff --git starmath/source/symbol.src starmath/source/symbol.src
+index c4cab66..ed0fb9b 100644
+--- starmath/source/symbol.src
++++ starmath/source/symbol.src
+@@ -268,6 +268,7 @@ Resource RID_LOCALIZED_NAMES
+ < "perthousand" ; > ;
+ < "and" ; > ;
+ < "or" ; > ;
++ < "mapsto" ; > ;
+ };
+ };
+ StringArray RID_UI_SYMBOL_NAMES
+@@ -340,6 +341,7 @@ Resource RID_LOCALIZED_NAMES
+ < "perthousand" ; > ;
+ < "and" ; > ;
+ < "or" ; > ;
++ < "mapsto" ; > ;
+ };
+ };
+ };
diff --git a/patches/dev300/vEdit-13-August.diff b/patches/dev300/vEdit-13-August.diff
new file mode 100644
index 000000000..117a75742
--- /dev/null
+++ b/patches/dev300/vEdit-13-August.diff
@@ -0,0 +1,7896 @@
+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 );
++}
++