// Attach non-breaking elements to the text or object on // their left. internal static bool MoveNaviBackward(TextNavigator nav) { TextSymbolType type; bool continueLoop = true; TextPosition position = nav.CreatePosition(); type = nav.GetSymbolType(LogicalDirection.Backward); while (continueLoop) { if (nav.CompareTo(nav.TextContainer.Start) == 0) { return(false); } switch (type) { case TextSymbolType.Character: case TextSymbolType.EmbeddedObject: continueLoop = false; break; case TextSymbolType.ElementStart: case TextSymbolType.ElementEnd: if (IsBreakingSymbol(nav, LogicalDirection.Backward)) { continueLoop = false; } break; } nav.MoveByDistance(-1); type = nav.GetSymbolType(LogicalDirection.Backward); } return(nav.CompareTo(position) < 0); }
/// <summary> /// Tries to find a selection boundary LogicalDirection.Forward of /// the current position. /// </summary> public bool MoveToNextWordBreak(TextNavigator navigator) { int index; // If the array of break positions hasn't been created and // populated yet, do that work now. if (null == breakPositions) { if (!BreakText(navigator, false, out breakPositions)) { return(false); } } index = breakPositions.BinarySearch(navigator); if (index < 0) { index = (~index); } else { index += 1; } if ((index > breakPositions.Count - 1) && (0 != navigator.TextContainer.End.CompareTo((TextPosition)breakPositions[breakPositions.Count - 1]))) { breakPositions = null; if (!BreakText(navigator, false, out breakPositions)) { return(false); } index = breakPositions.BinarySearch(navigator); if (index < 0) { index = (~index); } else { index += 1; } } if ((index < 0) || (index > breakPositions.Count - 1)) { return(false); } navigator.MoveToPosition((TextPosition)breakPositions[index]); return(true); }
internal bool BreakText(TextPosition start, TextPosition end, bool isSpelling, out ArrayList positionList) { string breakingString; ArrayList indexList; int currentIndex = 0; int lastIndex = 0; int i; TextNavigator nav = start.CreateNavigator(); positionList = null; // Convert input and execute main work. if (nav.CompareTo(nav.TextContainer.Start) != 0) { MoveNaviBackward(nav); MoveNaviForward(nav); } breakingString = GenerateText(nav, end); if (!BreakText(breakingString, isSpelling, out indexList)) { return(false); } // Convert from indices to TextPositions and return. positionList = new ArrayList(); if (0 == start.CompareTo(start.TextContainer.Start)) { positionList.Add(start); } foreach (Object item in indexList) { currentIndex = (int)item; for (i = 0; i < (currentIndex - lastIndex); ++i) { MoveNaviForward(nav); } positionList.Add(nav.CreatePosition()); lastIndex = currentIndex; } if (0 == end.CompareTo(end.TextContainer.End)) { // Add the end of the container as the last break. positionList.Add(end); } return(true); }
private static bool IsBreakingSymbol(TextNavigator navigator, LogicalDirection direction) { TextSymbolType type = navigator.GetSymbolType(direction); // (JCS) - This will need to be reworked after the Avalon team has // an API that can be called to determine if an Element should be // "breaking" or not. return((type == TextSymbolType.None) || (type == TextSymbolType.EmbeddedObject) || (((type == TextSymbolType.ElementStart) || (type == TextSymbolType.ElementEnd) ) && (navigator.GetElementType(direction).IsAssignableFrom(typeof(InlineElement))) ) ); }
internal static string GenerateText(TextPosition begin, TextPosition end) { StringBuilder output = new StringBuilder(); TextNavigator navigator = begin.CreateNavigator(); TextSymbolType type; char[] buffer = new char[1]; char ch; if (begin.TextContainer != end.TextContainer) { throw new ArgumentException(SR.Get(SRID.BeginEndTextContainerMismatch)); } navigator.MoveToPosition(begin); type = navigator.GetSymbolType(LogicalDirection.Forward); while (navigator < end) { switch (type) { case TextSymbolType.Character: navigator.GetText(LogicalDirection.Forward, 1, navigator.TextContainer.End, buffer, 0); ch = buffer[0]; output.Append(ch); break; case TextSymbolType.EmbeddedObject: ch = '\xF8FF'; // Private use Unicode. output.Append(ch); break; case TextSymbolType.ElementStart: case TextSymbolType.ElementEnd: if (IsBreakingSymbol(navigator, LogicalDirection.Forward)) { output.Append(" "); } break; } navigator.MoveByDistance(1); type = navigator.GetSymbolType(LogicalDirection.Forward); } return(output.ToString()); }
internal bool BreakText(TextPosition position, bool isSpelling, out ArrayList positionList) { TextNavigator start = position.CreateNavigator(); TextNavigator end = position.CreateNavigator(); TextNavigator checkLeft = position.CreateNavigator(); TextNavigator checkRight = position.CreateNavigator(); bool leftEdge = false; bool rightEdge = false; int distance = 32; int index; bool foundBreaks = false; positionList = null; while (!(leftEdge && rightEdge)) { if (!leftEdge) { if (start.TextContainer.Start.GetDistanceTo(start) > distance) { start.MoveByDistance(-(distance)); } else { start.MoveToPosition(start.TextContainer.Start); leftEdge = true; } MoveNaviForward(start); MoveNaviBackward(start); } if (!rightEdge) { if (end.GetDistanceTo(end.TextContainer.End) > distance) { end.MoveByDistance(distance); } else { end.MoveToPosition(end.TextContainer.End); rightEdge = true; } MoveNaviBackward(end); MoveNaviForward(end); } if (BreakText(start, end, isSpelling, out positionList)) { foundBreaks = true; } else { continue; } if (!leftEdge) { index = positionList.BinarySearch(checkLeft); if (index < 0) { index = (~index); } index -= 1; leftEdge = (index >= 0); } if (!rightEdge) { index = positionList.BinarySearch(checkRight); if (index < 0) { index = (~index); } else { index += 1; } rightEdge = (index < positionList.Count); } } return(foundBreaks); }
// moves an endpoint backward a certain number of units. // the endpoint is just an index into the text so it could represent either // the endpoint. private int MoveEndpointBackward(int index, TextUnit unit, int count, out int moved) { switch (unit) { case TextUnit.Character: { int limit = _provider.GetTextLength(); ValidateEndpoints(); int oneBasedIndex = index + 1; moved = Math.Max(count, -oneBasedIndex); index = index + moved; index = index < 0 ? 0 : index; } break; case TextUnit.Word: { string text = _provider.GetText(); ValidateEndpoints(); #if WCP_NLS_ENABLED // use the same word breaker as Avalon Text. WordBreaker breaker = new WordBreaker(); TextContainer container = new TextContainer(text); TextNavigator navigator = new TextNavigator(index, container); // move backward one word break for each count for (moved = 0; moved > count && index > 0; moved--) { if (!breaker.MoveToPreviousWordBreak(navigator)) { break; } } index = navigator.Position; #else for (moved = 0; moved > count && index > 0; moved--) { for (index--; !AtWordBoundary(text, index); index--) { ; } } #endif } break; case TextUnit.Line: { // Note count < 0. // Get 1-based line. int line = _provider.LineFromChar(index) + 1; int lineMax = _provider.GetLineCount(); // Truncate the count to the number of available lines. int actualCount = Math.Max(count, -line); moved = actualCount; if (actualCount == -line) { // We are moving by the maximum number of possible lines, // so we know the resulting index will be 0. index = 0; // If a line other than the first consists of only "\r\n", // you can move backwards past this line and the position changes, // hence this is counted. The first line is special, though: // if it is empty, and you move say from the second line back up // to the first, you cannot move further; however if the first line // is nonempty, you can move from the end of the first line to its // beginning! This latter move is counted, but if the first line // is empty, it is not counted. // Recalculate the value of "moved". // The first line is empty if it consists only of // a line separator sequence. bool firstLineEmpty = ((lineMax > 1 && _provider.LineIndex(1) == _lineSeparator.Length) || lineMax == 0); if (moved < 0 && firstLineEmpty) { ++moved; } } else // actualCount > -line { // Move the endpoint to the beginning of the following line, // then back by the line separator length to get to the end // of the previous line, since the Edit control has // no method to get the character index of the end // of a line directly. index = _provider.LineIndex(line + actualCount) - _lineSeparator.Length; } } break; case TextUnit.Paragraph: { // just like moving words but we look for paragraph boundaries instead of // word boundaries. string text = _provider.GetText(); ValidateEndpoints(); for (moved = 0; moved > count && index > 0; moved--) { for (index--; !AtParagraphBoundary(text, index); index--) { ; } } } break; case TextUnit.Format: case TextUnit.Page: case TextUnit.Document: { // since edit controls are plain text moving one uniform format unit will // take us all the way to the beginning of the document, just like // "pages" and document. // we'll move 1 format unit if we aren't already at the beginning of the // document. Otherwise, we won't move at all. moved = index > 0 ? -1 : 0; index = 0; } break; default: throw new System.ComponentModel.InvalidEnumArgumentException("unit", (int)unit, typeof(TextUnit)); } return(index); }
void ITextRangeProvider.ExpandToEnclosingUnit(TextUnit unit) { Misc.SetFocus(_provider._hwnd); switch (unit) { case TextUnit.Character: // if it is a degenerate range then expand it to be one character. // otherwise, leave it as it is. if (Start == End) { int moved; End = MoveEndpointForward(End, TextUnit.Character, 1, out moved); } break; case TextUnit.Word: { // this works same as paragraph except we look for word boundaries instead of paragraph boundaries. // get the text so we can figure out where the boundaries are string text = _provider.GetText(); ValidateEndpoints(); #if WCP_NLS_ENABLED // use the same word breaker that Avalon Text uses. WordBreaker breaker = new WordBreaker(); TextContainer container = new TextContainer(text); // if the starting point of the range is not already at a word break // then move it backwards to the nearest word break. TextNavigator startNavigator = new TextNavigator(Start, container); if (!breaker.IsAtWordBreak(startNavigator)) { breaker.MoveToPreviousWordBreak(startNavigator); Start = startNavigator.Position; } // if the range is degenerate or the ending point of the range is not already at a word break // then move it forwards to the nearest word break. TextNavigator endNavigator = new TextNavigator(End, container); if (Start == End || !breaker.IsAtWordBreak(endNavigator)) { breaker.MoveToNextWordBreak(endNavigator); End = endNavigator.Position; } #else // move start left until we reach a word boundary. for (; !AtWordBoundary(text, Start); Start--) { ; } // move end right until we reach word boundary (different from Start). End = Math.Min(Math.Max(End, Start + 1), text.Length); for (; !AtWordBoundary(text, End); End++) { ; } #endif } break; case TextUnit.Line: { if (_provider.GetLineCount() != 1) { int startLine = _provider.LineFromChar(Start); int endLine = _provider.LineFromChar(End); MoveTo(_provider.LineIndex(startLine), _provider.LineIndex(endLine + 1)); } else { MoveTo(0, _provider.GetTextLength()); } } break; case TextUnit.Paragraph: { // this works same as paragraph except we look for word boundaries instead of paragraph boundaries. // get the text so we can figure out where the boundaries are string text = _provider.GetText(); ValidateEndpoints(); // move start left until we reach a paragraph boundary. for (; !AtParagraphBoundary(text, Start); Start--) { ; } // move end right until we reach a paragraph boundary (different from Start). End = Math.Min(Math.Max(End, Start + 1), text.Length); for (; !AtParagraphBoundary(text, End); End++) { ; } } break; case TextUnit.Format: case TextUnit.Page: case TextUnit.Document: MoveTo(0, _provider.GetTextLength()); break; //break; default: throw new System.ComponentModel.InvalidEnumArgumentException("unit", (int)unit, typeof(TextUnit)); } }
// moves an endpoint forward a certain number of units. // the endpoint is just an index into the text so it could represent either // the endpoint. private int MoveEndpointForward(int index, TextUnit unit, int count, out int moved) { switch (unit) { case TextUnit.Character: { int limit = _provider.GetTextLength(); ValidateEndpoints(); moved = Math.Min(count, limit - index); index = index + moved; index = index > limit ? limit : index; } break; case TextUnit.Word: { string text = _provider.GetText(); ValidateEndpoints(); #if WCP_NLS_ENABLED // use the same word breaker as Avalon Text. WordBreaker breaker = new WordBreaker(); TextContainer container = new TextContainer(text); TextNavigator navigator = new TextNavigator(index, container); // move forward one word break for each count for (moved = 0; moved < count && index < text.Length; moved++) { if (!breaker.MoveToNextWordBreak(navigator)) { break; } } index = navigator.Position; #else for (moved = 0; moved < count && index < text.Length; moved++) { for (index++; !AtWordBoundary(text, index); index++) { ; } } #endif } break; case TextUnit.Line: { // figure out what line we are on. if we are in the middle of a line and // are moving left then we'll round up to the next line so that we move // to the beginning of the current line. int line = _provider.LineFromChar(index); // limit the number of lines moved to the number of lines available to move // Note lineMax is always >= 1. int lineMax = _provider.GetLineCount(); moved = Math.Min(count, lineMax - line - 1); if (moved > 0) { // move the endpoint to the beginning of the destination line. index = _provider.LineIndex(line + moved); } else if (moved == 0 && lineMax == 1) { // There is only one line so get the text length as endpoint index = _provider.GetTextLength(); moved = 1; } } break; case TextUnit.Paragraph: { // just like moving words but we look for paragraph boundaries instead of // word boundaries. string text = _provider.GetText(); ValidateEndpoints(); for (moved = 0; moved < count && index < text.Length; moved++) { for (index++; !AtParagraphBoundary(text, index); index++) { ; } } } break; case TextUnit.Format: case TextUnit.Page: case TextUnit.Document: { // since edit controls are plain text moving one uniform format unit will // take us all the way to the end of the document, just like // "pages" and document. int limit = _provider.GetTextLength(); ValidateEndpoints(); // we'll move 1 format unit if we aren't already at the end of the // document. Otherwise, we won't move at all. moved = index < limit ? 1 : 0; index = limit; } break; default: throw new System.ComponentModel.InvalidEnumArgumentException("unit", (int)unit, typeof(TextUnit)); } return(index); }