using System; using System.Windows; using System.Windows.Documents; using System.Collections.Generic; namespace DBToolControls { public static class WordBreaker { /// /// Returns a TextRange covering a word containing or following this TextPointer. /// /// /// If this TextPointer is within a word or at start of word, the containing word range is returned. /// If this TextPointer is between two words, the following word range is returned. /// If this TextPointer is at trailing word boundary, the following word range is returned. /// /// public static TextRange GetWordRange(TextPointer position) { TextRange wordRange = null; TextPointer wordStartPosition = null; TextPointer wordEndPosition = null; // Go forward first, to find word end position. wordEndPosition = GetPositionAtWordBoundary(position, /*wordBreakDirection*/LogicalDirection.Forward); if (wordEndPosition != null) { // Then travel backwards, to find word start position. wordStartPosition = GetPositionAtWordBoundary(wordEndPosition, /*wordBreakDirection*/LogicalDirection.Backward); } if (wordStartPosition != null && wordEndPosition != null) { wordRange = new TextRange(wordStartPosition, wordEndPosition); } return wordRange; } /// /// 1. When wordBreakDirection = Forward, returns a position at the end of the word, /// i.e. a position with a wordBreak character (space) following it. /// 2. When wordBreakDirection = Backward, returns a position at the start of the word, /// i.e. a position with a wordBreak character (space) preceeding it. /// 3. Returns null when there is no workbreak in the requested direction. /// /// private static TextPointer GetPositionAtWordBoundary(TextPointer position, LogicalDirection wordBreakDirection) { if (!position.IsAtInsertionPosition) { position = position.GetInsertionPosition(wordBreakDirection); } TextPointer navigator = position; while (navigator != null && !IsPositionNextToWordBreak(navigator, wordBreakDirection)) { navigator = navigator.GetNextInsertionPosition(wordBreakDirection); } return navigator; } // Helper for GetPositionAtWordBoundary. // Returns true when passed TextPointer is next to a wordBreak in requested direction. private static bool IsPositionNextToWordBreak(TextPointer position, LogicalDirection wordBreakDirection) { bool isAtWordBoundary = false; // Skip over any formatting. if (position.GetPointerContext(wordBreakDirection) != TextPointerContext.Text) { position = position.GetInsertionPosition(wordBreakDirection); } if (position.GetPointerContext(wordBreakDirection) == TextPointerContext.Text) { LogicalDirection oppositeDirection = (wordBreakDirection == LogicalDirection.Forward) ? LogicalDirection.Backward : LogicalDirection.Forward; char[] runBuffer = new char[1]; char[] oppositeRunBuffer = new char[1]; position.GetTextInRun(wordBreakDirection, runBuffer, /*startIndex*/0, /*count*/1); position.GetTextInRun(oppositeDirection, oppositeRunBuffer, /*startIndex*/0, /*count*/1); if (SyntaxProvider.Delimiters.Contains(runBuffer[0]) && !(SyntaxProvider.Delimiters.Contains(oppositeRunBuffer[0]))) { isAtWordBoundary = true; } } else { // If we're not adjacent to text then we always want to consider this position a "word break". // In practice, we're most likely next to an embedded object or a block boundary. isAtWordBoundary = true; } return isAtWordBoundary; } } }