//-------------------------------------------------------------------- // Highlight compositing //--------------------------------------------------------------------- private void _OnHighlightChanged(object sender, HighlightChangedEventArgs args) { Debug.Assert(sender != null); Debug.Assert(args != null); Debug.Assert(args.Ranges != null); #if DEBUG { Highlights highlights = this.Highlights; StaticTextPointer highlightTransitionPosition; StaticTextPointer highlightRangeStart; object selected; DocumentsTrace.FixedDocumentSequence.Highlights.Trace("===BeginNewHighlightRange==="); highlightTransitionPosition = ((ITextContainer)this).CreateStaticPointerAtOffset(0); while (true) { // Move to the next highlight start. if (!highlights.IsContentHighlighted(highlightTransitionPosition, LogicalDirection.Forward)) { highlightTransitionPosition = highlights.GetNextHighlightChangePosition(highlightTransitionPosition, LogicalDirection.Forward); // No more highlights? if (highlightTransitionPosition.IsNull) { break; } } // highlightTransitionPosition is at the start of a new highlight run. selected = highlights.GetHighlightValue(highlightTransitionPosition, LogicalDirection.Forward, typeof(TextSelection)); // Save the start position and find the end. highlightRangeStart = highlightTransitionPosition; highlightTransitionPosition = highlights.GetNextHighlightChangePosition(highlightTransitionPosition, LogicalDirection.Forward); Invariant.Assert(!highlightTransitionPosition.IsNull, "Highlight start not followed by highlight end!"); // Store the highlight. if (selected != DependencyProperty.UnsetValue) { DocumentsTrace.FixedDocumentSequence.Highlights.Trace(string.Format("HightlightRange {0}-{1}", highlightRangeStart.ToString(), highlightTransitionPosition.ToString())); if (highlightRangeStart.GetPointerContext(LogicalDirection.Forward) != TextPointerContext.Text) { DocumentsTrace.FixedDocumentSequence.Highlights.Trace("<HighlightNotOnText>"); } else { char[] sb = new char[256]; TextPointerBase.GetTextWithLimit(highlightRangeStart.CreateDynamicTextPointer(LogicalDirection.Forward), LogicalDirection.Forward, sb, 0, 256, highlightTransitionPosition.CreateDynamicTextPointer(LogicalDirection.Forward)); DocumentsTrace.FixedDocumentSequence.TextOM.Trace(string.Format("HightlightContent [{0}]", new String(sb))); } } } DocumentsTrace.FixedDocumentSequence.TextOM.Trace("===EndNewHighlightRange==="); } #endif Debug.Assert(args.Ranges.Count > 0 && ((TextSegment)args.Ranges[0]).Start.CompareTo(((TextSegment)args.Ranges[0]).End) < 0); // For each change range we received, we need to figure out // affected child TextContainer, and notify it with appropriate // ranges that are in the child's address space. // // We only fire one highlight change notification for any child // TextContainer even if there is multiple change ranges fall // into the same child TextContainer. // // We scan the ranges and the child TextContainer in the same loop, // moving forward two scanning pointers and at boundary of each // TextContainer, we fire a change notification. // int idxScan = 0; DocumentSequenceTextPointer tsScan = null; ChildDocumentBlock cdbScan = null; List <TextSegment> rangeArray = new List <TextSegment>(4); while (idxScan < args.Ranges.Count) { TextSegment ts = (TextSegment)args.Ranges[idxScan]; DocumentSequenceTextPointer tsEnd = (DocumentSequenceTextPointer)ts.End; ITextPointer tpChildStart, tpChildEnd; ChildDocumentBlock lastBlock; // If tsScan == null, we were done with previous range, // so we are going to set tsScan to begining of this range. // Otherwise the previous range was split so we will simply // start from what was left over from previous loop. if (tsScan == null) { tsScan = (DocumentSequenceTextPointer)ts.Start; } lastBlock = cdbScan; cdbScan = tsScan.ChildBlock; if (lastBlock != null && cdbScan != lastBlock && !(lastBlock.ChildContainer is NullTextContainer) && rangeArray.Count != 0) { // This range is in a different block, so take care of old blocks first lastBlock.ChildHighlightLayer.RaiseHighlightChangedEvent(new ReadOnlyCollection <TextSegment>(rangeArray)); rangeArray.Clear(); } tpChildStart = tsScan.ChildPointer; if (tsEnd.ChildBlock != cdbScan) { // If this range crosses blocks, we are done with current block tpChildEnd = tsScan.ChildPointer.TextContainer.End; if (tpChildStart.CompareTo(tpChildEnd) != 0) { rangeArray.Add(new TextSegment(tpChildStart, tpChildEnd)); } // Notify child container if (!(cdbScan.ChildContainer is NullTextContainer) && rangeArray.Count != 0) { cdbScan.ChildHighlightLayer.RaiseHighlightChangedEvent(new ReadOnlyCollection <TextSegment>(rangeArray)); } // Move on to next block; cdbScan = cdbScan.NextBlock; tsScan = new DocumentSequenceTextPointer(cdbScan, cdbScan.ChildContainer.Start); rangeArray.Clear(); } else { // Otherwise we need to go on to see if there is more ranges // fall withing the same block. Simply add this change range tpChildEnd = tsEnd.ChildPointer; if (tpChildStart.CompareTo(tpChildEnd) != 0) { rangeArray.Add(new TextSegment(tpChildStart, tpChildEnd)); } // Move on to next range idxScan++; tsScan = null; } } // Fire change notification for the last child block. if (rangeArray.Count > 0 && (!(cdbScan == null || cdbScan.ChildContainer is NullTextContainer))) { cdbScan.ChildHighlightLayer.RaiseHighlightChangedEvent(new ReadOnlyCollection <TextSegment>(rangeArray)); } }