/// <summary> /// Create LingTreeNode objects based on XML tree description /// </summary> /// <param name="xmlNode">current xml node in the description</param> /// <param name="Node">parent LingTreeNode</param> /// <param name="iLevel">level (or depth) of a node within the tree</param> /// <param name="iIndex">unique index for nodes in the tree</param> protected void ProcessXmlTreeNode(XmlNode xmlNode, LingTreeNode Node, int iLevel, ref int iIndex) { Node = BeginASubTree(Node, iLevel++, iIndex++); if (m_Root == null) { m_Root = Node; } XmlNode attr; attr = xmlNode.SelectSingleNode("@type"); if (attr != null) { switch (attr.InnerText) { case "lex": Node.Type = LingTreeNode.NodeType.Lex; break; case "gloss": Node.Type = LingTreeNode.NodeType.Gloss; break; default: Node.Type = LingTreeNode.NodeType.NonTerminal; break; } } attr = xmlNode.SelectSingleNode("@id"); if (attr != null) { Node.Id = attr.InnerText; } attr = xmlNode.SelectSingleNode("@triangleover"); if (attr != null) { if (attr.InnerText == "yes") { Node.Triangle = true; } } attr = xmlNode.SelectSingleNode("@omitlineover"); if (attr != null) { if (attr.InnerText == "yes") { Node.OmitLine = true; } } ProcessNodeContentFromXml(xmlNode, Node); XmlNodeList daughterNodes = xmlNode.SelectNodes("node"); foreach (XmlNode daughterNode in daughterNodes) { ProcessXmlTreeNode(daughterNode, Node, iLevel, ref iIndex); } Node = EndASubTree(Node); iLevel--; // decrement level }
public LingTreeTree(int iInitialXCoord, int iInitialYCoord, int iHorizontalGap, int iVerticalGap, int iLexGlossGapAdjustment, Font fntGlossFont, Font fntLexFont, Font fntNTFont, Color clrGlossColor, Color clrLexColor, Color clrNTColor, Color clrLines, double dLineWidth, string sVersion) { m_aiMaxLevelHeights = new int[kMaxLevels]; InitialXCoord = iInitialXCoord; InitialYCoord = iInitialYCoord; HorizontalGap = iHorizontalGap; VerticalGap = iVerticalGap; LexGlossGapAdjustment = iLexGlossGapAdjustment; GlossFont = fntGlossFont; LexFont = fntLexFont; NTFont = fntNTFont; GlossColor = clrGlossColor; LexColor = clrLexColor; NTColor = clrNTColor; LinesColor = clrLines; LineWidth = dLineWidth; Version = sVersion; m_Root = null; this.Paint += new PaintEventHandler(OnPaint); this.MouseUp += new MouseEventHandler(LingTreeTree_MouseUp); this.m_LingTreeNodeClickedEvent = new LingTreeNodeClickedEventHandler(OnNodeClicked); }
private void ProcessNodeContentFromXml(XmlNode xmlNode, LingTreeNode Node) { XmlNode xmlTemp = xmlNode.SelectSingleNode("label"); if (xmlTemp != null) { Node.Content = xmlTemp.InnerXml; } AppendXScript(xmlNode, Node, "superscript"); AppendXScript(xmlNode, Node, "subscript"); }
public void ParseXmlTreeDescription(XmlNode treeDescriptionTopNode) { if (m_Root != null) { m_Root = null; // remove any existing tree } Controls.Clear(); // clear all extant (node) controls // process top node and all daughters int iIndex = 1; ProcessXmlTreeNode(treeDescriptionTopNode, null, 0, ref iIndex); }
public void SVGCreate(LingTreeTree Tree, StringBuilder sb, string sLineColor, double dLineWidth) { LingTreeNode Node; Font font = GetTextFont(Tree); Color clr = GetTextColor(Tree); // Draw this node if (Tree.ShowFlatView) { // adjust lex and gloss Y coordinates if (Type == NodeType.Lex) { YCoord = Tree.LexBottomYCoord; YUpperMid = Tree.LexBottomYUpperMid; } if (Type == NodeType.Gloss) { YCoord = Tree.GlossBottomYCoord; } } string sClrName = LingTreeNode.SVGConvertColorToSVGColorString(clr); SVGDrawString(Content, sb, font, sClrName); // Draw daughter nodes Node = Daughter; while (Node != null) { Node.SVGCreate(Tree, sb, sLineColor, dLineWidth); if (Type == NodeType.NonTerminal) { if (!Node.Triangle) { if (!Node.OmitLine) { SVGDrawLine(sb, sLineColor, dLineWidth, Node.XMid, Node.YUpperMid, XMid, YLowerMid); } } else { #if DoTrace Console.WriteLine("\tDrawing triangle: Node = {0},\tXCoord = {1}", Node.Content, Node.XCoord); #endif int ix = Node.XCoord + m_iTriangleOffset; Point[] apt = { new Point(ix, Node.YCoord), new Point(XMid, YLowerMid), new Point(ix + (Node.Width - 2 * m_iTriangleOffset), Node.YCoord), new Point(ix, Node.YCoord) }; SVGDrawLines(sb, sLineColor, dLineWidth, apt); } } Node = Node.Sister; } }
void AdjustXValues(LingTreeTree tree, int iAdjust) { // adjust this node XCoord += iAdjust; XMid += iAdjust; tree.HorizontalOffset = tree.HorizontalOffset + iAdjust; // adjust any daughter nodes LingTreeNode NextDaughter = Daughter; while (NextDaughter != null) { NextDaughter.AdjustXValues(tree, iAdjust); NextDaughter = NextDaughter.Sister; } }
/////////////////////////////////////////////////////////////////////////////// // NAME // EndASubTree // ARGUMENTS // pNode - node which is ending // DESCRIPTION // Finish processing a node during the parsing of a tree description // RETURN VALUE // // LingTreeNode EndASubTree(LingTreeNode Node) { // cleanup content if (Node.Content == null) { Node.Content = ""; } else { Node.Content = Node.Content.Trim(); } #if !Orig if (!Contains(Node)) { this.Controls.Add(Node); } #endif return(Node.Mother); }
public string CreateSVG(Graphics grfx, string sLineColor) { if (m_Root == null) { return(""); } CalculateCoordinates(grfx); // string sFrontMatter = @"<?xml version='1.0' standalone='no'?><!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' //'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'>"; StringBuilder sb = new StringBuilder(); sb.Append("<?xml version='1.0' standalone='no'?>\r\n"); string sSVGElement = "<svg width='{0}mm' height='{1}mm' version='1.1' xmlns='http://www.w3.org/2000/svg' contentScriptType='text/javascript'>\r\n"; sb.AppendFormat(sSVGElement, LingTreeNode.SVGConvert(XSize), LingTreeNode.SVGConvert(YSize)); sb.Append("<script id=\"clientEventHandlersJS\">\r\nfunction OnClickLingTreeNode(node){}\r\n</script>\r\n"); m_Root.SVGCreate(this, sb, sLineColor, LineWidth); sb.Append("</svg>"); return(sb.ToString()); }
private void AppendXScript(XmlNode xmlNode, LingTreeNode Node, string sType) { XmlNode element = xmlNode.SelectSingleNode(sType); if (element != null) { string sText; XmlNode attr = xmlNode.SelectSingleNode(sType + "/@italic"); switch (sType) { case "superscript": if (attr != null && attr.InnerText == "yes") { sText = "/^"; } else { sText = "/S"; } break; case "subscript": if (attr != null && attr.InnerText == "yes") { sText = "/_"; } else { sText = "/s"; } break; default: sText = ""; break; } Node.Content = Node.Content + sText + element.InnerXml; } }
NodeType m_Type; // node type (non-terminal, lex, gloss) #endregion Fields #region Constructors public LingTreeNode(int iLevel, int iIndex, string sContent, NodeType eNodeType, LingTreeNode ndeMother, LingTreeNode ndeDaughter, LingTreeNode ndeSister) { Level = iLevel; Index = iIndex; Content = sContent; Type = eNodeType; Mother = ndeMother; Daughter = ndeDaughter; Sister = ndeSister; Triangle = false; OmitLine = false; Height = 0; Width = 0; XCoord = 0; YCoord = 0; Id = ""; }
public LingTreeNode(int iLevel, int iIndex, string sContent, NodeType eNodeType, LingTreeNode ndeMother, LingTreeNode ndeDaughter, LingTreeNode ndeSister) { Level = iLevel; Index = iIndex; Content = sContent; Type = eNodeType; Mother = ndeMother; Daughter = ndeDaughter; Sister = ndeSister; Triangle = false; OmitLine = false; Height = 0; Width = 0; XCoord = 0; YCoord = 0; Id = ""; }
/////////////////////////////////////////////////////////////////////////////// // NAME // BeginASubTree // ARGUMENTS // pMother - pointer to mother node of new sub-tree // iLevel - level (or depth) within the tree // iIndex - index number of node in the tree // DESCRIPTION // Create a new node which is the root of a subtree during the parsing of a tree description // RETURN VALUE // Pointer to newly created node // LingTreeNode BeginASubTree(LingTreeNode Mother, int iLevel, int iIndex) { LingTreeNode Node = new LingTreeNode(iLevel, iIndex, null, LingTreeNode.NodeType.NonTerminal, Mother, null, null); if (Mother != null) { LingTreeNode Sister = Mother.Daughter; if (Sister == null) { Mother.Daughter = Node; // new node is the daughter } else { // there's a daughter already while (Sister.Sister != null) { Sister = Sister.Sister; // skip to rightmost sister } Sister.Sister = Node; } } return(Node); }
/////////////////////////////////////////////////////////////////////////////// // NAME // ParseTreeDescription // ARGUMENTS // none // DESCRIPTION // create tree nodes based on the tree description // RETURN VALUE // none // public bool ParseTreeDescription() { const string sIllFormed = "Ill-formed tree description!\n"; LingTreeNode Node = null; int iLevel = 0; // level (or depth) of a node within the tree int iIndex = 1; // unique index for nodes in the tree bool bSeenFirstOpenParen = false; if (m_Root != null) { m_Root = null; // remove any existing tree } Controls.Clear(); // clear all extant (node) controls m_sDescription = m_sDescription.Trim(); for (int i = 0; i < m_sDescription.Length; i++) { switch (m_sDescription[i]) { case '(': if (bSeenFirstOpenParen && (iLevel == 0)) { // Ill-formed tree: final close has been reached, yet there is more string sMsg = sIllFormed ; int iPos2 = Math.Max(0,i); sMsg += "End of well-formed tree already reached. Start of a new tree discovered:\n "; sMsg += m_sDescription.Substring(0, iPos2); sMsg += " <ERROR DETECTED HERE> "; sMsg += m_sDescription.Substring(iPos2); sMsg += "\nRest of the description will be ignored"; throw(new LingTreeDescriptionException(sMsg)); } Node = BeginASubTree(Node, iLevel++, iIndex++); if (m_Root == null) m_Root = Node; bSeenFirstOpenParen = true; break; case ')': if (Node == null) { ThrowUnmatchedClosingParenException(sIllFormed, m_sDescription, i); return false; } Node = EndASubTree(Node); iLevel--; // decrement level break; case '\\': if (Node == null) { ThrowUnmatchedClosingParenException(sIllFormed, m_sDescription, i); return false; } if (m_sDescription[i+1] == ')' || // check for quoted parens m_sDescription[i+1] == '(') Node.Content = Node.Content + m_sDescription[++i]; #if Orig else if (m_sDescription[i+1] == 'L' && m_sDescription[i+2] == ' ') { Node.Type = LingTreeNode.NodeType.Lex; i += 2; } else if (m_sDescription[i+1] == 'G' && m_sDescription[i+2] == ' ') { Node.Type = LingTreeNode.NodeType.Gloss; i += 2; } else if (m_sDescription[i+1] == 'T' && m_sDescription[i+2] == ' ') { Node.Triangle = true; i += 2; } #else else if (m_sDescription[i+1] == 'L') { Node.Type = LingTreeNode.NodeType.Lex; i++; } else if (m_sDescription[i+1] == 'G') { Node.Type = LingTreeNode.NodeType.Gloss; i++; } else if (m_sDescription[i+1] == 'T') { Node.Triangle = true; i++; } else if (m_sDescription[i+1] == 'O') { Node.OmitLine = true; i++; } #endif break; default: if (Node == null) { ThrowUnmatchedClosingParenException(sIllFormed, m_sDescription, i); return false; } Node.Content = Node.Content + m_sDescription[i]; break; } } if (iLevel == 0) return true; else { string sMsg = sIllFormed; sMsg += iLevel; sMsg += " unmatched opening parenthes"; if (iLevel == 1) sMsg += "i"; else sMsg += "e"; sMsg += "s discovered at the end of the description."; throw(new LingTreeDescriptionException(sMsg)); } }
private void ProcessNodeContentFromXml(XmlNode xmlNode, LingTreeNode Node) { XmlNode xmlTemp = xmlNode.SelectSingleNode("label"); if (xmlTemp != null) Node.Content = xmlTemp.InnerXml; AppendXScript(xmlNode, Node, "superscript"); AppendXScript(xmlNode, Node, "subscript"); }
/////////////////////////////////////////////////////////////////////////////// // NAME // EndASubTree // ARGUMENTS // pNode - node which is ending // DESCRIPTION // Finish processing a node during the parsing of a tree description // RETURN VALUE // // LingTreeNode EndASubTree(LingTreeNode Node) { // cleanup content if (Node.Content == null) Node.Content = ""; else Node.Content = Node.Content.Trim(); #if !Orig if (!Contains(Node)) { this.Controls.Add(Node); } #endif return Node.Mother; }
/////////////////////////////////////////////////////////////////////////////// // NAME // BeginASubTree // ARGUMENTS // pMother - pointer to mother node of new sub-tree // iLevel - level (or depth) within the tree // iIndex - index number of node in the tree // DESCRIPTION // Create a new node which is the root of a subtree during the parsing of a tree description // RETURN VALUE // Pointer to newly created node // LingTreeNode BeginASubTree(LingTreeNode Mother, int iLevel, int iIndex) { LingTreeNode Node = new LingTreeNode(iLevel, iIndex, null, LingTreeNode.NodeType.NonTerminal, Mother, null, null); if (Mother != null) { LingTreeNode Sister = Mother.Daughter; if (Sister == null) Mother.Daughter = Node; // new node is the daughter else { // there's a daughter already while (Sister.Sister != null) Sister = Sister.Sister; // skip to rightmost sister Sister.Sister = Node; } } return Node; }
private void AppendXScript(XmlNode xmlNode, LingTreeNode Node, string sType) { XmlNode element = xmlNode.SelectSingleNode(sType); if (element != null) { string sText; XmlNode attr = xmlNode.SelectSingleNode(sType + "/@italic"); switch (sType) { case "superscript": if (attr != null && attr.InnerText == "yes") sText = "/^"; else sText = "/S"; break; case "subscript": if (attr != null && attr.InnerText == "yes") sText = "/_"; else sText = "/s"; break; default: sText = ""; break; } Node.Content = Node.Content + sText + element.InnerXml; } }
/// <summary> /// Create LingTreeNode objects based on XML tree description /// </summary> /// <param name="xmlNode">current xml node in the description</param> /// <param name="Node">parent LingTreeNode</param> /// <param name="iLevel">level (or depth) of a node within the tree</param> /// <param name="iIndex">unique index for nodes in the tree</param> protected void ProcessXmlTreeNode(XmlNode xmlNode, LingTreeNode Node, int iLevel, ref int iIndex) { Node = BeginASubTree(Node, iLevel++, iIndex++); if (m_Root == null) m_Root = Node; XmlNode attr; attr = xmlNode.SelectSingleNode("@type"); if (attr != null) { switch (attr.InnerText) { case "lex": Node.Type = LingTreeNode.NodeType.Lex; break; case "gloss": Node.Type = LingTreeNode.NodeType.Gloss; break; default: Node.Type = LingTreeNode.NodeType.NonTerminal; break; } } attr = xmlNode.SelectSingleNode("@id"); if (attr != null) { Node.Id = attr.InnerText; } attr = xmlNode.SelectSingleNode("@triangleover"); if (attr != null) { if (attr.InnerText == "yes") Node.Triangle = true; } attr = xmlNode.SelectSingleNode("@omitlineover"); if (attr != null) { if (attr.InnerText == "yes") Node.OmitLine = true; } ProcessNodeContentFromXml(xmlNode, Node); XmlNodeList daughterNodes = xmlNode.SelectNodes("node"); foreach (XmlNode daughterNode in daughterNodes) { ProcessXmlTreeNode(daughterNode, Node, iLevel, ref iIndex); } Node = EndASubTree(Node); iLevel--; // decrement level }
/////////////////////////////////////////////////////////////////////////////// // NAME // ParseTreeDescription // ARGUMENTS // none // DESCRIPTION // create tree nodes based on the tree description // RETURN VALUE // none // public bool ParseTreeDescription() { const string sIllFormed = "Ill-formed tree description!\n"; LingTreeNode Node = null; int iLevel = 0; // level (or depth) of a node within the tree int iIndex = 1; // unique index for nodes in the tree bool bSeenFirstOpenParen = false; if (m_Root != null) { m_Root = null; // remove any existing tree } Controls.Clear(); // clear all extant (node) controls m_sDescription = m_sDescription.Trim(); for (int i = 0; i < m_sDescription.Length; i++) { switch (m_sDescription[i]) { case '(': if (bSeenFirstOpenParen && (iLevel == 0)) { // Ill-formed tree: final close has been reached, yet there is more string sMsg = sIllFormed; int iPos2 = Math.Max(0, i); sMsg += "End of well-formed tree already reached. Start of a new tree discovered:\n "; sMsg += m_sDescription.Substring(0, iPos2); sMsg += " <ERROR DETECTED HERE> "; sMsg += m_sDescription.Substring(iPos2); sMsg += "\nRest of the description will be ignored"; throw(new LingTreeDescriptionException(sMsg)); } Node = BeginASubTree(Node, iLevel++, iIndex++); if (m_Root == null) { m_Root = Node; } bSeenFirstOpenParen = true; break; case ')': if (Node == null) { ThrowUnmatchedClosingParenException(sIllFormed, m_sDescription, i); return(false); } Node = EndASubTree(Node); iLevel--; // decrement level break; case '\\': if (Node == null) { ThrowUnmatchedClosingParenException(sIllFormed, m_sDescription, i); return(false); } if (m_sDescription[i + 1] == ')' || // check for quoted parens m_sDescription[i + 1] == '(') { Node.Content = Node.Content + m_sDescription[++i]; } #if Orig else if (m_sDescription[i + 1] == 'L' && m_sDescription[i + 2] == ' ') { Node.Type = LingTreeNode.NodeType.Lex; i += 2; } else if (m_sDescription[i + 1] == 'G' && m_sDescription[i + 2] == ' ') { Node.Type = LingTreeNode.NodeType.Gloss; i += 2; } else if (m_sDescription[i + 1] == 'T' && m_sDescription[i + 2] == ' ') { Node.Triangle = true; i += 2; } #else else if (m_sDescription[i + 1] == 'L') { Node.Type = LingTreeNode.NodeType.Lex; i++; } else if (m_sDescription[i + 1] == 'G') { Node.Type = LingTreeNode.NodeType.Gloss; i++; } else if (m_sDescription[i + 1] == 'T') { Node.Triangle = true; i++; } else if (m_sDescription[i + 1] == 'O') { Node.OmitLine = true; i++; } #endif break; default: if (Node == null) { ThrowUnmatchedClosingParenException(sIllFormed, m_sDescription, i); return(false); } Node.Content = Node.Content + m_sDescription[i]; break; } } if (iLevel == 0) { return(true); } else { string sMsg = sIllFormed; sMsg += iLevel; sMsg += " unmatched opening parenthes"; if (iLevel == 1) { sMsg += "i"; } else { sMsg += "e"; } sMsg += "s discovered at the end of the description."; throw(new LingTreeDescriptionException(sMsg)); } }
public LingTreeNodeClickedEventArgs(LingTreeNode node) { m_node = node; }
/////////////////////////////////////////////////////////////////////////////// // NAME // CalculateXCoordinate // ARGUMENTS // pTree - pointer to tree this node is in // pDC - pointer to the device context // iMaxColWidth - maximum width of the current column // DESCRIPTION // Determine the X-axis coordinate for this node // It assumes that the width of this and all other nodes have // already been established. // It also assumes that higher branching nodes are not wider than the // total width of their daughters (which may not always be true...) // RETURN VALUE // The X-axis coordinate at the mid-point of this node // public int CalculateXCoordinate(LingTreeTree Tree, Graphics grfx, int iMaxColWidth) { XMid = 0; // Determine the width for this node CalculateWidth(Tree, grfx); if (Daughter != null) { // is a non-terminal node LingTreeNode NextDaughter = Daughter.Sister; if (NextDaughter != null) { // node branches if (iMaxColWidth < m_iWidth) { iMaxColWidth = m_iWidth; // remember widest node in the column } } else { // only one daughter, so could be in a column if (iMaxColWidth < Width) { iMaxColWidth = Width; // remember widest node in the column } } int iLeftMost = Daughter.CalculateXCoordinate(Tree, grfx, iMaxColWidth); int iRightMost = iLeftMost; while (NextDaughter != null) { // calculate coordinates for other daughters iRightMost = NextDaughter.CalculateXCoordinate(Tree, grfx, iMaxColWidth); NextDaughter = NextDaughter.Sister; } // take mid point between first & last daughter XMid = (iLeftMost + iRightMost) / 2; if (iRightMost > iLeftMost) { if (m_iWidth > (iRightMost - iLeftMost)) { int iAdjust = (m_iWidth - (iRightMost - iLeftMost)) / 2; m_iXMid += iAdjust; NextDaughter = Daughter; while (NextDaughter != null) { NextDaughter.AdjustXValues(Tree, iAdjust); NextDaughter = NextDaughter.Sister; } } } } else { // is a terminal node if (iMaxColWidth < Width) { iMaxColWidth = Width; // this might be the widest node in the column } XMid = (int)Tree.HorizontalOffset + // Offset from last terminal node plus iMaxColWidth / 2; // half the width of this column Tree.HorizontalOffset = XMid + // have a new offset for next terminal node Tree.HorizontalGap + // gap between terminal nodes plus iMaxColWidth / 2; // half the width of the widest node in this column } XCoord = XMid - Width / 2; // adjust for width of this node int iEnd = XCoord + Width; if (Triangle) { iEnd += Tree.HorizontalGap / 2; } if (iEnd > Tree.XSize) { Tree.XSize = iEnd; // Keep track of total width for scrolling } #if DoTrace Console.WriteLine("{0}\tXSize = {1},\tWidth = {2},\tXCoord = {3},\tYCoord = {4}, \tXMid = {4}", Content, Tree.XSize, Width, XCoord, YCoord, XMid); #endif return(XMid); }