// Finds a node/edge pair matching a given char offset in the tree. // If the pair matches a character within a text node, the text node is split. internal void GetNodeAndEdgeAtCharOffset(int charOffset, out TextTreeNode node, out ElementEdge edge) { int nodeCharOffset; int siblingTreeCharOffset; bool checkZeroWidthNode; // Offset zero/SymbolCount-1 are before/after the root node, which // is an illegal position -- you can't add or remove content there // and it's never exposed publicly. Invariant.Assert(charOffset >= 0 && charOffset <= this.IMECharCount, "Bogus char offset!"); if (this.IMECharCount == 0) { node = null; edge = ElementEdge.BeforeStart; return; } // If this flag is set true on exit, we need to consider the case // where we've found a "zero-width" (SymbolCount == 0) text node. // Zero width nodes needs special handling, since they are logically // part of a following or preceding node. checkZeroWidthNode = false; // Find the node. node = _rootNode; nodeCharOffset = 0; // Each iteration walks through one tree. while (true) { int leftEdgeCharCount = 0; TextTreeTextElementNode textElementNode = node as TextTreeTextElementNode; if (textElementNode != null) { leftEdgeCharCount = textElementNode.IMELeftEdgeCharCount; if (leftEdgeCharCount > 0) { if (charOffset == nodeCharOffset) { edge = ElementEdge.BeforeStart; break; } if (charOffset == nodeCharOffset + leftEdgeCharCount) { edge = ElementEdge.AfterStart; break; } } } else if (node is TextTreeTextNode || node is TextTreeObjectNode) { if (charOffset == nodeCharOffset) { edge = ElementEdge.BeforeStart; checkZeroWidthNode = true; break; } if (charOffset == nodeCharOffset + node.IMECharCount) { edge = ElementEdge.AfterEnd; checkZeroWidthNode = true; break; } } // No child node? That means we're inside a TextTreeTextNode. if (node.ContainedNode == null) { Invariant.Assert(node is TextTreeTextNode); // Need to split the TextTreeTextNode. // Here we want a character buried inside a single node, split // the node open.... node = ((TextTreeTextNode)node).Split(charOffset - nodeCharOffset, ElementEdge.AfterEnd); edge = ElementEdge.BeforeStart; break; } // Need to look into one of the child nodes. node = (TextTreeNode)node.ContainedNode; nodeCharOffset += leftEdgeCharCount; // Skip over the parent element start edge. // Walk down the sibling tree. node = (TextTreeNode)node.GetSiblingAtCharOffset(charOffset - nodeCharOffset, out siblingTreeCharOffset); nodeCharOffset += siblingTreeCharOffset; } // If we're on a zero-width TextTreeTextNode we need some special handling. if (checkZeroWidthNode) { node = (TextTreeNode)AdjustForZeroWidthNode(node, edge); } }