diff options
author | Jonas Jensen <jopsen@gmail.com> | 2010-09-22 21:39:23 +0200 |
---|---|---|
committer | Cédric Bosdonnat <cedricbosdo@openoffice.org> | 2010-09-22 21:41:33 +0200 |
commit | d6b73c9d63b02142b9d48d9fed1573912ee3705e (patch) | |
tree | 56cb793afef6d4917bd50d37f70543e31575ee15 | |
parent | a0866751f996613466f7e10619c3c3f2ff54c38d (diff) |
Pushed Jonas' patches AS IS
-rw-r--r-- | patches/dev300/symbols-20-august.diff | 238 | ||||
-rw-r--r-- | patches/dev300/vEdit-13-August.diff | 7896 |
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 ); ++} ++ |