|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
//
//
// Description:
// A SelectionProcessor subclass which produces locator parts that select
// all anchors that intersect with the text in an element's TextView.
//
using System;
using System.Windows;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Globalization;
using System.Windows.Annotations;
using System.Windows.Annotations.Storage;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Xml;
using MS.Utility;
using System.Windows.Media;
using System.Windows.Controls.Primitives;
using MS.Internal.Documents;
using MS.Internal.PtsHost;
namespace MS.Internal.Annotations.Anchoring
{
/// <summary>
/// </summary>
internal class TextViewSelectionProcessor : SelectionProcessor
{
//------------------------------------------------------
//
// Constructors
//
//------------------------------------------------------
#region Constructors
/// <summary>
/// Creates an instance of TextViewSelectionProcessor.
/// </summary>
public TextViewSelectionProcessor()
{
}
#endregion Constructors
//------------------------------------------------------
//
// Public Methods
//
//------------------------------------------------------
#region Public Methods
/// <summary>
/// Not implemented for this selection processor.
/// </summary>
/// <param name="selection1">selection to merge </param>
/// <param name="selection2">other selection to merge </param>
/// <param name="newSelection">always set to null because DocumentPageViews cannot be merged</param>
/// <returns>always returns false because DocumentPageViewss cannot be merged</returns>
public override bool MergeSelections(Object selection1, Object selection2, out Object newSelection)
{
newSelection = null;
return false;
}
/// <summary>
/// Simply returns the selection passed in which should be an element.
/// </summary>
/// <param name="selection">the selection to examine</param>
/// <returns>a list containing the selection</returns>
/// <exception cref="ArgumentNullException">selection is null</exception>
/// <exception cref="ArgumentException">selection is of wrong type</exception>
public override IList<DependencyObject> GetSelectedNodes(Object selection)
{
// Verify selection is a service provider that provides ITextView
VerifySelection(selection);
return new DependencyObject[] { (DependencyObject)selection };
}
/// <summary>
/// Simply returns the selection which should be an element.
/// </summary>
/// <param name="selection">the selection to examine</param>
/// <returns>the selection itself</returns>
/// <exception cref="ArgumentNullException">selection is null</exception>
/// <exception cref="ArgumentException">selection is of wrong type</exception>
public override UIElement GetParent(Object selection)
{
// Verify selection is a service provider that provides ITextView
VerifySelection(selection);
return (UIElement)selection;
}
/// <summary>
/// Gets the anchor point for the selection. This isn't a runtime selection
/// and therefore shouldn't be anchored to. This processor returns a point of
/// (Double.NaN, Double.NaN).
/// </summary>
/// <param name="selection">the selection to examine</param>
/// <exception cref="ArgumentNullException">selection is null</exception>
/// <exception cref="ArgumentException">selection is of wrong type</exception>
public override Point GetAnchorPoint(Object selection)
{
// Verify selection is a DocumentPageView
VerifySelection(selection);
// Returns the point (Nan,Nan)
return new Point(double.NaN, double.NaN);
}
/// <summary>
/// Creates one or more locator parts representing the portion
/// of 'startNode' spanned by 'selection'.
/// </summary>
/// <param name="selection">the selection that is being processed</param>
/// <param name="startNode">the node the locator parts should be in the
/// context of</param>
/// <returns>one or more locator parts representing the portion of 'startNode' spanned
/// by 'selection'</returns>
/// <exception cref="ArgumentNullException">startNode or selection is null</exception>
/// <exception cref="ArgumentException">selection is of the wrong type</exception>
public override IList<ContentLocatorPart>
GenerateLocatorParts(Object selection, DependencyObject startNode)
{
ArgumentNullException.ThrowIfNull(startNode);
List<ContentLocatorPart> res = null;
ITextView textView = VerifySelection(selection);
res = new List<ContentLocatorPart>(1);
int startOffset;
int endOffset;
if (textView != null && textView.IsValid)
{
GetTextViewTextRange(textView, out startOffset, out endOffset);
}
else
{
// This causes no content to be loaded
startOffset = -1;
endOffset = -1;
}
ContentLocatorPart part = new ContentLocatorPart(TextSelectionProcessor.CharacterRangeElementName);// DocumentPageViewLocatorPart();
part.NameValuePairs.Add(TextSelectionProcessor.CountAttribute, 1.ToString(NumberFormatInfo.InvariantInfo));
part.NameValuePairs.Add(TextSelectionProcessor.SegmentAttribute + 0.ToString(NumberFormatInfo.InvariantInfo), startOffset.ToString(NumberFormatInfo.InvariantInfo) + TextSelectionProcessor.Separator + endOffset.ToString(NumberFormatInfo.InvariantInfo));
part.NameValuePairs.Add(TextSelectionProcessor.IncludeOverlaps, Boolean.TrueString);
res.Add(part);
return res;
}
/// <summary>
/// This processor doesn't resolve ContentLocatorParts. It simply returns null.
/// </summary>
/// <param name="locatorPart">locator part specifying data to be spanned</param>
/// <param name="startNode">the node to be spanned by the created
/// selection</param>
/// <param name="attachmentLevel">always set to AttachmentLevel.Unresolved</param>
/// <returns>always returns null; this processor does not resolve ContentLocatorParts</returns>
/// <exception cref="ArgumentNullException">locatorPart or startNode are null</exception>
public override Object ResolveLocatorPart(ContentLocatorPart locatorPart, DependencyObject startNode, out AttachmentLevel attachmentLevel)
{
ArgumentNullException.ThrowIfNull(locatorPart);
ArgumentNullException.ThrowIfNull(startNode);
attachmentLevel = AttachmentLevel.Unresolved;
// ContentLocator Parts generated by this selection processor cannot be resolved
return null;
}
/// <summary>
/// Returns a list of XmlQualifiedNames representing the
/// the locator parts this processor can generate. This processor
/// does not resolve these ContentLocatorParts - only generates them.
/// </summary>
public override XmlQualifiedName[] GetLocatorPartTypes()
{
return (XmlQualifiedName[])LocatorPartTypeNames.Clone();
}
#endregion Public Methods
//------------------------------------------------------
//
// Public Operators
//
//------------------------------------------------------
//------------------------------------------------------
//
// Public Properties
//
//------------------------------------------------------
//------------------------------------------------------
//
// Public Events
//
//------------------------------------------------------
//------------------------------------------------------
//
// Internal Methods
//
//------------------------------------------------------
#region Internal Methods
/// <summary>
/// Returns the TextRange representing the visible contents of the ITextView.
/// Also returns the same information in terms of the offsets to the start and end.
/// </summary>
/// <param name="textView">ITextView to get visible contents of</param>
/// <param name="startOffset">offset into the document representing start of visible content</param>
/// <param name="endOffset">offset into the document representing end of visible content</param>
/// <returns>TextRange spanning all visible content</returns>
internal static TextRange GetTextViewTextRange(ITextView textView, out int startOffset, out int endOffset)
{
Debug.Assert(textView != null);
// These are the default in case we don't find any content in the DocumentPageView
startOffset = int.MinValue;
endOffset = 0;
TextRange textRange = null;
IList<TextSegment> segments = textView.TextSegments;
if (segments != null && segments.Count > 0)
{
ITextPointer start = segments[0].Start;
ITextPointer end = segments[segments.Count - 1].End;
startOffset = end.TextContainer.Start.GetOffsetToPosition(start);
endOffset = end.TextContainer.Start.GetOffsetToPosition(end);
textRange = new TextRange(start, end);
}
return textRange;
}
#endregion Internal Methods
//------------------------------------------------------
//
// Private Methods
//
//------------------------------------------------------
#region Private Methods
/// <summary>
/// Verify the selection is of the correct type.
/// </summary>
/// <param name="selection">selection to verify</param>
/// <returns>the selection cast to the necessary type</returns>
private ITextView VerifySelection(object selection)
{
ArgumentNullException.ThrowIfNull(selection);
IServiceProvider provider = selection as IServiceProvider;
if (provider == null)
throw new ArgumentException(SR.SelectionMustBeServiceProvider, "selection");
ITextView textView = provider.GetService(typeof(ITextView)) as ITextView;
return textView;
}
#endregion Private Methods
//------------------------------------------------------
//
// Private Fields
//
//------------------------------------------------------
#region Private Fields
// ContentLocator part types understood by this processor
private static readonly XmlQualifiedName[] LocatorPartTypeNames = Array.Empty<XmlQualifiedName>();
#endregion Private Fields
}
}
|