Code:
/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / wpf / src / Framework / System / Windows / Documents / CompositionAdorner.cs / 1 / CompositionAdorner.cs
//----------------------------------------------------------------------------
//
// File: CompositionAdorner.cs
//
// Copyright (C) Microsoft Corporation. All rights reserved.
//
// Description: Composition adorner to render the composition display attribute.
//
//---------------------------------------------------------------------------
namespace System.Windows.Documents
{
using System.Collections; // ArrayList
using System.Diagnostics;
using System.Windows.Media; // Brush, Transform
using System.Windows.Controls; // TextBox
using System.Windows.Threading; // Dispatcher
using MS.Win32; // TextServices
using MS.Internal; // Invariant
internal class CompositionAdorner : Adorner
{
//-----------------------------------------------------
//
// Constructors
//
//-----------------------------------------------------
#region Constructors
static CompositionAdorner()
{
// Provide a new default value for the composition adorner so that it is not hit-testable.
IsEnabledProperty.OverrideMetadata(typeof(CompositionAdorner), new FrameworkPropertyMetadata(false));
}
///
/// Creates new instance of CompositionAdorner.
///
///
/// TextView to which this CompositionAdorner is attached as adorner.
///
internal CompositionAdorner(ITextView textView) : this(textView, new ArrayList())
{
}
///
/// Creates new instance of CompositionAdorner.
///
///
/// TextView to which this CompositionAdorner is attached as adorner.
///
///
/// Attribute ranges
///
internal CompositionAdorner(ITextView textView, ArrayList attributeRanges)
: base(textView.RenderScope)
{
Debug.Assert(textView != null && textView.RenderScope != null);
// TextView to which this CompositionAdorner is attached as adorner and it will
// als be used for GetRectangleFromTextPosition/GetLineRange
_textView = textView;
// Create ArrayList for the composition attribute ranges and composition lines
_attributeRanges = attributeRanges;
}
#endregion Constructors
//------------------------------------------------------
//
// Public Methods
//
//-----------------------------------------------------
#region Public Methods
///
/// Add a transform so that the composition adorner gets positioned at the correct spot within the text being edited
///
///
/// The transform applied to the object the adorner adorns
///
///
/// Transform to apply to the adorner
///
public override GeneralTransform GetDesiredTransform(GeneralTransform transform)
{
TranslateTransform translation;
GeneralTransformGroup group = new GeneralTransformGroup();
// Get the matrix transform out, skip all non affine transforms
Transform t = transform.AffineTransform;
if (t == null)
{
t = Transform.Identity;
}
// Translate the adorner to (0, 0) point
translation = new TranslateTransform(-(t.Value.OffsetX), -(t.Value.OffsetY));
group.Children.Add(translation);
if (transform != null)
{
group.Children.Add(transform);
}
return group;
}
#endregion Public Methods
//------------------------------------------------------
//
// Protected Methods
//
//------------------------------------------------------
#region Protected Methods
///
/// Render override to render the composition adorner here.
///
protected override void OnRender(DrawingContext drawingContext)
{
// Get the matrix from AdornedElement to the visual parent to get the transformed
// start/end point
Visual parent2d = VisualTreeHelper.GetParent(this.AdornedElement) as Visual;
if (parent2d == null)
{
return;
}
GeneralTransform transform = AdornedElement.TransformToAncestor(parent2d);
if (transform == null)
{
return;
}
// Render the each of the composition string attribute from the attribute ranges.
for (int i = 0; i < _attributeRanges.Count; i++)
{
DoubleCollection dashArray;
// Get the composition attribute range from the attribute range lists
AttributeRange attributeRange = (AttributeRange)_attributeRanges[i];
// Skip the rendering composition lines if the composition line doesn't exist.
if (attributeRange.CompositionLines.Count == 0)
{
continue;
}
// Set the line bold and squiggle
bool lineBold = attributeRange.TextServicesDisplayAttribute.IsBoldLine ? true : false;
bool squiggle = false;
// Set the line height and cluse gap value that base on the ratio of text height
double height = attributeRange.Height;
double lineHeight = height * (lineBold ? BoldLineHeightRatio : NormalLineHeightRatio);
double clauseGap = height * ClauseGapRatio;
// Create Pen for drawing the composition lines with the specified line color
Pen pen = new Pen(new SolidColorBrush(attributeRange.TextServicesDisplayAttribute.LineColor), lineHeight);
// Set the pen style that based on IME's composition line style
switch (attributeRange.TextServicesDisplayAttribute.LineStyle)
{
case UnsafeNativeMethods.TF_DA_LINESTYLE.TF_LS_DOT:
// Add the dot length and specify the start/end line cap as the round
dashArray = new DoubleCollection();
dashArray.Add(DotLength);
dashArray.Add(DotLength);
pen.DashStyle = new DashStyle(dashArray, 0);
pen.DashCap = System.Windows.Media.PenLineCap.Round;
pen.StartLineCap = System.Windows.Media.PenLineCap.Round;
pen.EndLineCap = System.Windows.Media.PenLineCap.Round;
// Update the line height for the dot line. Dot line will be more thickness than
// other line to show it clearly.
lineHeight = height * (lineBold ? BoldDotLineHeightRatio : NormalDotLineHeightRatio);
break;
case UnsafeNativeMethods.TF_DA_LINESTYLE.TF_LS_DASH:
double dashLength = height * (lineBold ? BoldDashRatio : NormalDashRatio);
double dashGapLength = height * (lineBold ? BoldDashGapRatio : NormalDashGapRatio);
// Add the dash and dash gap legth
dashArray = new DoubleCollection();
dashArray.Add(dashLength);
dashArray.Add(dashGapLength);
pen.DashStyle = new DashStyle(dashArray, 0);
pen.DashCap = System.Windows.Media.PenLineCap.Round;
pen.StartLineCap = System.Windows.Media.PenLineCap.Round;
pen.EndLineCap = System.Windows.Media.PenLineCap.Round;
break;
case UnsafeNativeMethods.TF_DA_LINESTYLE.TF_LS_SOLID:
pen.StartLineCap = System.Windows.Media.PenLineCap.Round;
pen.EndLineCap = System.Windows.Media.PenLineCap.Round;
break;
case UnsafeNativeMethods.TF_DA_LINESTYLE.TF_LS_SQUIGGLE:
squiggle = true;
break;
}
double halfLineHeight = lineHeight / 2;
// Draw the each of the composition line
for (int j = 0; j < attributeRange.CompositionLines.Count; j++)
{
CompositionLine compositionLine = (CompositionLine)attributeRange.CompositionLines[j];
// Get the start/end point for composition adorner.
// Currently Text doesn't aware of the spaceroom for the drawing of the composition
// adorner(like as normal/bold dot/line/squggle), so we should draw the composition adorners
// to the closest area of the bottom text.
Point startPoint = new Point(compositionLine.Start.X + clauseGap, compositionLine.Start.Y - lineHeight / 4);
Point endPoint = new Point(compositionLine.End.X - clauseGap, compositionLine.End.Y - lineHeight / 4);
// Apply matrix to start/end point
//
transform.TryTransform(startPoint, out startPoint);
transform.TryTransform(endPoint, out endPoint);
if (squiggle)
{
// Draw the squiggle line with using of the PathFigure and DrawGemetry.
// We may revisit this logic to render the smooth squiggle line.
Point pathPoint = new Point(startPoint.X, startPoint.Y + halfLineHeight);
double squiggleGap = halfLineHeight;
PathFigure pathFigure = new PathFigure();
pathFigure.StartPoint = pathPoint;
int indexPoint = 0;
while (indexPoint < ((endPoint.X - startPoint.X) / (squiggleGap)))
{
if (indexPoint % 4 == 0 || indexPoint % 4 == 3)
{
pathPoint = new Point(pathPoint.X + squiggleGap, pathPoint.Y + halfLineHeight);
pathFigure.Segments.Add(new LineSegment(pathPoint, true));
}
else if (indexPoint % 4 == 1 || indexPoint % 4 == 2)
{
pathPoint = new Point(pathPoint.X + squiggleGap, pathPoint.Y - halfLineHeight);
pathFigure.Segments.Add(new LineSegment(pathPoint, true));
}
indexPoint++;
}
PathGeometry pathGeometry = new PathGeometry();
pathGeometry.Figures.Add(pathFigure);
// Draw the composition line with the squiggle
drawingContext.DrawGeometry(null, pen, pathGeometry);
}
else
{
drawingContext.DrawLine(pen, startPoint, endPoint);
}
}
}
}
#endregion Protected Events
//-----------------------------------------------------
//
// Internal Methods
//
//------------------------------------------------------
#region Internal Methods
///
/// Add the composition attribute range that will be rendered.
/// Used in TextServicesDisplayAttributePropertyRanges (for composition display attribute)
///
internal void AddAttributeRange(ITextPointer start, ITextPointer end, TextServicesDisplayAttribute textServiceDisplayAttribute)
{
// Set the range start/end point's logical direction
ITextPointer rangeStart = start.CreatePointer(LogicalDirection.Forward);
ITextPointer rangeEnd = end.CreatePointer(LogicalDirection.Backward);
// Add the composition attribute range
_attributeRanges.Add(new AttributeRange(_textView, rangeStart, rangeEnd, textServiceDisplayAttribute));
}
///
/// Invalidates the CompositionAdorner render.
/// Used in TextServicesDisplayAttributePropertyRanges (for composition display attribute)
///
internal void InvalidateAdorner()
{
for (int i = 0; i < _attributeRanges.Count; i++)
{
// Get the composition attribute range from the attribute range lists
AttributeRange attributeRange = (AttributeRange)_attributeRanges[i];
// Add the composition lines for rendering the composition lines
attributeRange.AddCompositionLines();
}
// Invalidate the CompositionAdorner to update the rendering.
AdornerLayer adornerLayer = VisualTreeHelper.GetParent(this) as AdornerLayer;
if (adornerLayer != null)
{
adornerLayer.Update(AdornedElement);
adornerLayer.InvalidateArrange();
}
}
///
/// Add CompositionAdorner to the scoping AdornerLayer.
///
internal void Initialize(ITextView textView)
{
Debug.Assert(_adornerLayer == null, "Attempt to overwrite existing AdornerLayer!");
_adornerLayer = AdornerLayer.GetAdornerLayer(textView.RenderScope);
if (_adornerLayer != null)
{
// Add the CompositionAdorner to the scoping of AdornerLayer
_adornerLayer.Add(this);
}
}
///
/// Remove this CompositionAdorner from its AdornerLayer.
///
internal void Uninitialize()
{
if (_adornerLayer != null)
{
// Remove CompositionAdorner form the socping of AdornerLayer
_adornerLayer.Remove(this);
_adornerLayer = null;
}
}
#endregion Internal methods
//-----------------------------------------------------
//
// Private Fields
//
//-----------------------------------------------------
#region Private Fields
// AdornerLayer holding this CompositionAdorner visual
private AdornerLayer _adornerLayer;
// TextView to which this CompositionAdorner is attached as adorner
private ITextView _textView;
// ArrayList for the composition attribute ranges
private readonly ArrayList _attributeRanges;
// Composition line's dot length
private const double DotLength = 1.2;
// Ratio of the composition line
private const double NormalLineHeightRatio = 0.06;
// Ratio of the bold composition line
private const double BoldLineHeightRatio = 0.08;
// Ratio of the composition line of dot
private const double NormalDotLineHeightRatio = 0.08;
// Ratio of the bold composition line of dot
private const double BoldDotLineHeightRatio = 0.10;
// Ratio of the composition line's dash
private const double NormalDashRatio = 0.27;
// Ratio of the bold composition line's dash
private const double BoldDashRatio = 0.39;
// Ratio of the composition line's clause gap
private const double ClauseGapRatio = 0.09;
// Ratio of the composition line's dash gap
private const double NormalDashGapRatio = 0.04;
// Ratio of the bold composition line's dash gap
private const double BoldDashGapRatio = 0.06;
#endregion Private Fields
//-----------------------------------------------------
//
// Private Class
//
//------------------------------------------------------
private class AttributeRange
{
//-----------------------------------------------------
//
// Constructors
//
//------------------------------------------------------
#region Constructors
internal AttributeRange(ITextView textView, ITextPointer start, ITextPointer end, TextServicesDisplayAttribute textServicesDisplayAttribute)
{
_textView = textView;
_startOffset = start.Offset;
_endOffset = end.Offset;
_textServicesDisplayAttribute = textServicesDisplayAttribute;
_compositionLines = new ArrayList(1);
}
#endregion Constructors
//------------------------------------------------------
//
// Internal Methods
//
//-----------------------------------------------------
#region Internal Methods
// Add the composition lines for rendering the composition lines.
internal void AddCompositionLines()
{
// Erase any current lines.
_compositionLines.Clear();
ITextPointer start = _textView.TextContainer.Start.CreatePointer(_startOffset, LogicalDirection.Forward);
ITextPointer end = _textView.TextContainer.Start.CreatePointer(_endOffset, LogicalDirection.Backward);
while (start.CompareTo(end) < 0 && start.GetPointerContext(LogicalDirection.Forward) != TextPointerContext.Text)
{
start.MoveToNextContextPosition(LogicalDirection.Forward);
}
Invariant.Assert(start.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.Text);
if (end.HasValidLayout)
{
// Get the rectangle for start/end position
_startRect = _textView.GetRectangleFromTextPosition(start);
_endRect = _textView.GetRectangleFromTextPosition(end);
// Check whether the composition line is single or multiple lines
if (_startRect.Top != _endRect.Top)
{
// Add the composition lines to be rendered for the composition string
AddMultipleCompositionLines(start, end);
}
else
{
// Set the start/end pointer to draw the line
Point startPoint = new Point(_startRect.Left, _startRect.Bottom);
Point endPoint = new Point(_endRect.Right, _endRect.Bottom);
// Add the composition line to be rendered
_compositionLines.Add(new CompositionLine(startPoint, endPoint));
}
}
}
#endregion Internal Methods
//------------------------------------------------------
//
// Internal Properties
//
//-----------------------------------------------------
#region Internal Properties
///
/// Height of the composition attribute range.
///
internal double Height
{
get
{
return _startRect.Bottom - _startRect.Top;
}
}
///
/// CompositionLines of the composition attribute range.
///
internal ArrayList CompositionLines
{
get
{
return _compositionLines;
}
}
///
/// Composition attribute information.
///
internal TextServicesDisplayAttribute TextServicesDisplayAttribute
{
get
{
return _textServicesDisplayAttribute;
}
}
#endregion Internal Properties
//-----------------------------------------------------
//
// Private Methods
//
//-----------------------------------------------------
#region Private Methods
private void AddMultipleCompositionLines(ITextPointer start, ITextPointer end)
{
// Initalize the start/end line pointer
ITextPointer startLinePointer = start;
ITextPointer endLinePointer = startLinePointer;
// Get all composition lines that includes the start/end pointer
while (endLinePointer.CompareTo(end) < 0)
{
TextSegment textSegment = _textView.GetLineRange(endLinePointer);
if (textSegment.IsNull)
{
// endLinePointer is not within the TextView's definition of a line.
// Skip ahead to text on the next iteration.
startLinePointer = endLinePointer;
}
else
{
Debug.Assert(start.CompareTo(startLinePointer) <= 0, "The start pointer is positioned after the composition start line pointer!");
if (startLinePointer.CompareTo(textSegment.Start) < 0)
{
// Update the start line pointer
startLinePointer = textSegment.Start;
}
if (endLinePointer.CompareTo(textSegment.End) < 0)
{
if (end.CompareTo(textSegment.End) < 0)
{
// Update the end line pointer
endLinePointer = end.CreatePointer();
}
else
{
// Update the end line pointer
endLinePointer = textSegment.End.CreatePointer(LogicalDirection.Backward);
}
}
else
{
Debug.Assert(endLinePointer.CompareTo(textSegment.End) == 0, "The end line pointer is positioned after the composition text range end pointer!");
}
// Get the rectangle for start/end position
Rect startRect = _textView.GetRectangleFromTextPosition(startLinePointer);
Rect endRect = _textView.GetRectangleFromTextPosition(endLinePointer);
// Set the start/end pointer to draw the line
Point startPoint = new Point(startRect.Left, startRect.Bottom);
Point endPoint = new Point(endRect.Right, endRect.Bottom);
// Add the composition line to be rendered
_compositionLines.Add(new CompositionLine(startPoint, endPoint));
startLinePointer = textSegment.End.CreatePointer(LogicalDirection.Forward);
}
// Move the start pointer to the next text line. startLinePointer must be a pointer to start
// text.
while ((startLinePointer.GetPointerContext(LogicalDirection.Forward) != TextPointerContext.None) &&
(startLinePointer.GetPointerContext(LogicalDirection.Forward) != TextPointerContext.Text))
{
startLinePointer.MoveToNextContextPosition(LogicalDirection.Forward);
}
endLinePointer = startLinePointer;
}
}
#endregion Private methods
//------------------------------------------------------
//
// Private Fields
//
//-----------------------------------------------------
#region Private Fields
// TextView to which this CompositionAdorner is attached as adorner
private ITextView _textView;
// Start rect of the composition attribute range
private Rect _startRect;
// End rect of the composition attribute range
private Rect _endRect;
// Start position offset of the composition attribute range
private readonly int _startOffset;
// End position offset of the composition attribute range
private readonly int _endOffset;
// Composition display attribute that is specified from IME
private readonly TextServicesDisplayAttribute _textServicesDisplayAttribute;
// ArrayList for the composition lines
private readonly ArrayList _compositionLines;
#endregion Private Fields
}
private class CompositionLine
{
//------------------------------------------------------
//
// Constructors
//
//------------------------------------------------------
#region Constructors
internal CompositionLine(Point start, Point end)
{
_start = start;
_end = end;
}
#endregion Constructors
//-----------------------------------------------------
//
// Internal Properties
//
//------------------------------------------------------
#region Internal Properties
///
/// Start point of the composition line draw
///
internal Point Start
{
get
{
return _start;
}
}
///
/// End point of the composition line draw
///
internal Point End
{
get
{
return _end;
}
}
#endregion Internal Properties
//-----------------------------------------------------
//
// Private Fields
//
//-----------------------------------------------------
#region Private Fields
// Start point of the composition line draw
private Point _start;
// End point of the composition line draw
private Point _end;
#endregion Private Fields
}
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------------------
//
// File: CompositionAdorner.cs
//
// Copyright (C) Microsoft Corporation. All rights reserved.
//
// Description: Composition adorner to render the composition display attribute.
//
//---------------------------------------------------------------------------
namespace System.Windows.Documents
{
using System.Collections; // ArrayList
using System.Diagnostics;
using System.Windows.Media; // Brush, Transform
using System.Windows.Controls; // TextBox
using System.Windows.Threading; // Dispatcher
using MS.Win32; // TextServices
using MS.Internal; // Invariant
internal class CompositionAdorner : Adorner
{
//-----------------------------------------------------
//
// Constructors
//
//-----------------------------------------------------
#region Constructors
static CompositionAdorner()
{
// Provide a new default value for the composition adorner so that it is not hit-testable.
IsEnabledProperty.OverrideMetadata(typeof(CompositionAdorner), new FrameworkPropertyMetadata(false));
}
///
/// Creates new instance of CompositionAdorner.
///
///
/// TextView to which this CompositionAdorner is attached as adorner.
///
internal CompositionAdorner(ITextView textView) : this(textView, new ArrayList())
{
}
///
/// Creates new instance of CompositionAdorner.
///
///
/// TextView to which this CompositionAdorner is attached as adorner.
///
///
/// Attribute ranges
///
internal CompositionAdorner(ITextView textView, ArrayList attributeRanges)
: base(textView.RenderScope)
{
Debug.Assert(textView != null && textView.RenderScope != null);
// TextView to which this CompositionAdorner is attached as adorner and it will
// als be used for GetRectangleFromTextPosition/GetLineRange
_textView = textView;
// Create ArrayList for the composition attribute ranges and composition lines
_attributeRanges = attributeRanges;
}
#endregion Constructors
//------------------------------------------------------
//
// Public Methods
//
//-----------------------------------------------------
#region Public Methods
///
/// Add a transform so that the composition adorner gets positioned at the correct spot within the text being edited
///
///
/// The transform applied to the object the adorner adorns
///
///
/// Transform to apply to the adorner
///
public override GeneralTransform GetDesiredTransform(GeneralTransform transform)
{
TranslateTransform translation;
GeneralTransformGroup group = new GeneralTransformGroup();
// Get the matrix transform out, skip all non affine transforms
Transform t = transform.AffineTransform;
if (t == null)
{
t = Transform.Identity;
}
// Translate the adorner to (0, 0) point
translation = new TranslateTransform(-(t.Value.OffsetX), -(t.Value.OffsetY));
group.Children.Add(translation);
if (transform != null)
{
group.Children.Add(transform);
}
return group;
}
#endregion Public Methods
//------------------------------------------------------
//
// Protected Methods
//
//------------------------------------------------------
#region Protected Methods
///
/// Render override to render the composition adorner here.
///
protected override void OnRender(DrawingContext drawingContext)
{
// Get the matrix from AdornedElement to the visual parent to get the transformed
// start/end point
Visual parent2d = VisualTreeHelper.GetParent(this.AdornedElement) as Visual;
if (parent2d == null)
{
return;
}
GeneralTransform transform = AdornedElement.TransformToAncestor(parent2d);
if (transform == null)
{
return;
}
// Render the each of the composition string attribute from the attribute ranges.
for (int i = 0; i < _attributeRanges.Count; i++)
{
DoubleCollection dashArray;
// Get the composition attribute range from the attribute range lists
AttributeRange attributeRange = (AttributeRange)_attributeRanges[i];
// Skip the rendering composition lines if the composition line doesn't exist.
if (attributeRange.CompositionLines.Count == 0)
{
continue;
}
// Set the line bold and squiggle
bool lineBold = attributeRange.TextServicesDisplayAttribute.IsBoldLine ? true : false;
bool squiggle = false;
// Set the line height and cluse gap value that base on the ratio of text height
double height = attributeRange.Height;
double lineHeight = height * (lineBold ? BoldLineHeightRatio : NormalLineHeightRatio);
double clauseGap = height * ClauseGapRatio;
// Create Pen for drawing the composition lines with the specified line color
Pen pen = new Pen(new SolidColorBrush(attributeRange.TextServicesDisplayAttribute.LineColor), lineHeight);
// Set the pen style that based on IME's composition line style
switch (attributeRange.TextServicesDisplayAttribute.LineStyle)
{
case UnsafeNativeMethods.TF_DA_LINESTYLE.TF_LS_DOT:
// Add the dot length and specify the start/end line cap as the round
dashArray = new DoubleCollection();
dashArray.Add(DotLength);
dashArray.Add(DotLength);
pen.DashStyle = new DashStyle(dashArray, 0);
pen.DashCap = System.Windows.Media.PenLineCap.Round;
pen.StartLineCap = System.Windows.Media.PenLineCap.Round;
pen.EndLineCap = System.Windows.Media.PenLineCap.Round;
// Update the line height for the dot line. Dot line will be more thickness than
// other line to show it clearly.
lineHeight = height * (lineBold ? BoldDotLineHeightRatio : NormalDotLineHeightRatio);
break;
case UnsafeNativeMethods.TF_DA_LINESTYLE.TF_LS_DASH:
double dashLength = height * (lineBold ? BoldDashRatio : NormalDashRatio);
double dashGapLength = height * (lineBold ? BoldDashGapRatio : NormalDashGapRatio);
// Add the dash and dash gap legth
dashArray = new DoubleCollection();
dashArray.Add(dashLength);
dashArray.Add(dashGapLength);
pen.DashStyle = new DashStyle(dashArray, 0);
pen.DashCap = System.Windows.Media.PenLineCap.Round;
pen.StartLineCap = System.Windows.Media.PenLineCap.Round;
pen.EndLineCap = System.Windows.Media.PenLineCap.Round;
break;
case UnsafeNativeMethods.TF_DA_LINESTYLE.TF_LS_SOLID:
pen.StartLineCap = System.Windows.Media.PenLineCap.Round;
pen.EndLineCap = System.Windows.Media.PenLineCap.Round;
break;
case UnsafeNativeMethods.TF_DA_LINESTYLE.TF_LS_SQUIGGLE:
squiggle = true;
break;
}
double halfLineHeight = lineHeight / 2;
// Draw the each of the composition line
for (int j = 0; j < attributeRange.CompositionLines.Count; j++)
{
CompositionLine compositionLine = (CompositionLine)attributeRange.CompositionLines[j];
// Get the start/end point for composition adorner.
// Currently Text doesn't aware of the spaceroom for the drawing of the composition
// adorner(like as normal/bold dot/line/squggle), so we should draw the composition adorners
// to the closest area of the bottom text.
Point startPoint = new Point(compositionLine.Start.X + clauseGap, compositionLine.Start.Y - lineHeight / 4);
Point endPoint = new Point(compositionLine.End.X - clauseGap, compositionLine.End.Y - lineHeight / 4);
// Apply matrix to start/end point
//
transform.TryTransform(startPoint, out startPoint);
transform.TryTransform(endPoint, out endPoint);
if (squiggle)
{
// Draw the squiggle line with using of the PathFigure and DrawGemetry.
// We may revisit this logic to render the smooth squiggle line.
Point pathPoint = new Point(startPoint.X, startPoint.Y + halfLineHeight);
double squiggleGap = halfLineHeight;
PathFigure pathFigure = new PathFigure();
pathFigure.StartPoint = pathPoint;
int indexPoint = 0;
while (indexPoint < ((endPoint.X - startPoint.X) / (squiggleGap)))
{
if (indexPoint % 4 == 0 || indexPoint % 4 == 3)
{
pathPoint = new Point(pathPoint.X + squiggleGap, pathPoint.Y + halfLineHeight);
pathFigure.Segments.Add(new LineSegment(pathPoint, true));
}
else if (indexPoint % 4 == 1 || indexPoint % 4 == 2)
{
pathPoint = new Point(pathPoint.X + squiggleGap, pathPoint.Y - halfLineHeight);
pathFigure.Segments.Add(new LineSegment(pathPoint, true));
}
indexPoint++;
}
PathGeometry pathGeometry = new PathGeometry();
pathGeometry.Figures.Add(pathFigure);
// Draw the composition line with the squiggle
drawingContext.DrawGeometry(null, pen, pathGeometry);
}
else
{
drawingContext.DrawLine(pen, startPoint, endPoint);
}
}
}
}
#endregion Protected Events
//-----------------------------------------------------
//
// Internal Methods
//
//------------------------------------------------------
#region Internal Methods
///
/// Add the composition attribute range that will be rendered.
/// Used in TextServicesDisplayAttributePropertyRanges (for composition display attribute)
///
internal void AddAttributeRange(ITextPointer start, ITextPointer end, TextServicesDisplayAttribute textServiceDisplayAttribute)
{
// Set the range start/end point's logical direction
ITextPointer rangeStart = start.CreatePointer(LogicalDirection.Forward);
ITextPointer rangeEnd = end.CreatePointer(LogicalDirection.Backward);
// Add the composition attribute range
_attributeRanges.Add(new AttributeRange(_textView, rangeStart, rangeEnd, textServiceDisplayAttribute));
}
///
/// Invalidates the CompositionAdorner render.
/// Used in TextServicesDisplayAttributePropertyRanges (for composition display attribute)
///
internal void InvalidateAdorner()
{
for (int i = 0; i < _attributeRanges.Count; i++)
{
// Get the composition attribute range from the attribute range lists
AttributeRange attributeRange = (AttributeRange)_attributeRanges[i];
// Add the composition lines for rendering the composition lines
attributeRange.AddCompositionLines();
}
// Invalidate the CompositionAdorner to update the rendering.
AdornerLayer adornerLayer = VisualTreeHelper.GetParent(this) as AdornerLayer;
if (adornerLayer != null)
{
adornerLayer.Update(AdornedElement);
adornerLayer.InvalidateArrange();
}
}
///
/// Add CompositionAdorner to the scoping AdornerLayer.
///
internal void Initialize(ITextView textView)
{
Debug.Assert(_adornerLayer == null, "Attempt to overwrite existing AdornerLayer!");
_adornerLayer = AdornerLayer.GetAdornerLayer(textView.RenderScope);
if (_adornerLayer != null)
{
// Add the CompositionAdorner to the scoping of AdornerLayer
_adornerLayer.Add(this);
}
}
///
/// Remove this CompositionAdorner from its AdornerLayer.
///
internal void Uninitialize()
{
if (_adornerLayer != null)
{
// Remove CompositionAdorner form the socping of AdornerLayer
_adornerLayer.Remove(this);
_adornerLayer = null;
}
}
#endregion Internal methods
//-----------------------------------------------------
//
// Private Fields
//
//-----------------------------------------------------
#region Private Fields
// AdornerLayer holding this CompositionAdorner visual
private AdornerLayer _adornerLayer;
// TextView to which this CompositionAdorner is attached as adorner
private ITextView _textView;
// ArrayList for the composition attribute ranges
private readonly ArrayList _attributeRanges;
// Composition line's dot length
private const double DotLength = 1.2;
// Ratio of the composition line
private const double NormalLineHeightRatio = 0.06;
// Ratio of the bold composition line
private const double BoldLineHeightRatio = 0.08;
// Ratio of the composition line of dot
private const double NormalDotLineHeightRatio = 0.08;
// Ratio of the bold composition line of dot
private const double BoldDotLineHeightRatio = 0.10;
// Ratio of the composition line's dash
private const double NormalDashRatio = 0.27;
// Ratio of the bold composition line's dash
private const double BoldDashRatio = 0.39;
// Ratio of the composition line's clause gap
private const double ClauseGapRatio = 0.09;
// Ratio of the composition line's dash gap
private const double NormalDashGapRatio = 0.04;
// Ratio of the bold composition line's dash gap
private const double BoldDashGapRatio = 0.06;
#endregion Private Fields
//-----------------------------------------------------
//
// Private Class
//
//------------------------------------------------------
private class AttributeRange
{
//-----------------------------------------------------
//
// Constructors
//
//------------------------------------------------------
#region Constructors
internal AttributeRange(ITextView textView, ITextPointer start, ITextPointer end, TextServicesDisplayAttribute textServicesDisplayAttribute)
{
_textView = textView;
_startOffset = start.Offset;
_endOffset = end.Offset;
_textServicesDisplayAttribute = textServicesDisplayAttribute;
_compositionLines = new ArrayList(1);
}
#endregion Constructors
//------------------------------------------------------
//
// Internal Methods
//
//-----------------------------------------------------
#region Internal Methods
// Add the composition lines for rendering the composition lines.
internal void AddCompositionLines()
{
// Erase any current lines.
_compositionLines.Clear();
ITextPointer start = _textView.TextContainer.Start.CreatePointer(_startOffset, LogicalDirection.Forward);
ITextPointer end = _textView.TextContainer.Start.CreatePointer(_endOffset, LogicalDirection.Backward);
while (start.CompareTo(end) < 0 && start.GetPointerContext(LogicalDirection.Forward) != TextPointerContext.Text)
{
start.MoveToNextContextPosition(LogicalDirection.Forward);
}
Invariant.Assert(start.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.Text);
if (end.HasValidLayout)
{
// Get the rectangle for start/end position
_startRect = _textView.GetRectangleFromTextPosition(start);
_endRect = _textView.GetRectangleFromTextPosition(end);
// Check whether the composition line is single or multiple lines
if (_startRect.Top != _endRect.Top)
{
// Add the composition lines to be rendered for the composition string
AddMultipleCompositionLines(start, end);
}
else
{
// Set the start/end pointer to draw the line
Point startPoint = new Point(_startRect.Left, _startRect.Bottom);
Point endPoint = new Point(_endRect.Right, _endRect.Bottom);
// Add the composition line to be rendered
_compositionLines.Add(new CompositionLine(startPoint, endPoint));
}
}
}
#endregion Internal Methods
//------------------------------------------------------
//
// Internal Properties
//
//-----------------------------------------------------
#region Internal Properties
///
/// Height of the composition attribute range.
///
internal double Height
{
get
{
return _startRect.Bottom - _startRect.Top;
}
}
///
/// CompositionLines of the composition attribute range.
///
internal ArrayList CompositionLines
{
get
{
return _compositionLines;
}
}
///
/// Composition attribute information.
///
internal TextServicesDisplayAttribute TextServicesDisplayAttribute
{
get
{
return _textServicesDisplayAttribute;
}
}
#endregion Internal Properties
//-----------------------------------------------------
//
// Private Methods
//
//-----------------------------------------------------
#region Private Methods
private void AddMultipleCompositionLines(ITextPointer start, ITextPointer end)
{
// Initalize the start/end line pointer
ITextPointer startLinePointer = start;
ITextPointer endLinePointer = startLinePointer;
// Get all composition lines that includes the start/end pointer
while (endLinePointer.CompareTo(end) < 0)
{
TextSegment textSegment = _textView.GetLineRange(endLinePointer);
if (textSegment.IsNull)
{
// endLinePointer is not within the TextView's definition of a line.
// Skip ahead to text on the next iteration.
startLinePointer = endLinePointer;
}
else
{
Debug.Assert(start.CompareTo(startLinePointer) <= 0, "The start pointer is positioned after the composition start line pointer!");
if (startLinePointer.CompareTo(textSegment.Start) < 0)
{
// Update the start line pointer
startLinePointer = textSegment.Start;
}
if (endLinePointer.CompareTo(textSegment.End) < 0)
{
if (end.CompareTo(textSegment.End) < 0)
{
// Update the end line pointer
endLinePointer = end.CreatePointer();
}
else
{
// Update the end line pointer
endLinePointer = textSegment.End.CreatePointer(LogicalDirection.Backward);
}
}
else
{
Debug.Assert(endLinePointer.CompareTo(textSegment.End) == 0, "The end line pointer is positioned after the composition text range end pointer!");
}
// Get the rectangle for start/end position
Rect startRect = _textView.GetRectangleFromTextPosition(startLinePointer);
Rect endRect = _textView.GetRectangleFromTextPosition(endLinePointer);
// Set the start/end pointer to draw the line
Point startPoint = new Point(startRect.Left, startRect.Bottom);
Point endPoint = new Point(endRect.Right, endRect.Bottom);
// Add the composition line to be rendered
_compositionLines.Add(new CompositionLine(startPoint, endPoint));
startLinePointer = textSegment.End.CreatePointer(LogicalDirection.Forward);
}
// Move the start pointer to the next text line. startLinePointer must be a pointer to start
// text.
while ((startLinePointer.GetPointerContext(LogicalDirection.Forward) != TextPointerContext.None) &&
(startLinePointer.GetPointerContext(LogicalDirection.Forward) != TextPointerContext.Text))
{
startLinePointer.MoveToNextContextPosition(LogicalDirection.Forward);
}
endLinePointer = startLinePointer;
}
}
#endregion Private methods
//------------------------------------------------------
//
// Private Fields
//
//-----------------------------------------------------
#region Private Fields
// TextView to which this CompositionAdorner is attached as adorner
private ITextView _textView;
// Start rect of the composition attribute range
private Rect _startRect;
// End rect of the composition attribute range
private Rect _endRect;
// Start position offset of the composition attribute range
private readonly int _startOffset;
// End position offset of the composition attribute range
private readonly int _endOffset;
// Composition display attribute that is specified from IME
private readonly TextServicesDisplayAttribute _textServicesDisplayAttribute;
// ArrayList for the composition lines
private readonly ArrayList _compositionLines;
#endregion Private Fields
}
private class CompositionLine
{
//------------------------------------------------------
//
// Constructors
//
//------------------------------------------------------
#region Constructors
internal CompositionLine(Point start, Point end)
{
_start = start;
_end = end;
}
#endregion Constructors
//-----------------------------------------------------
//
// Internal Properties
//
//------------------------------------------------------
#region Internal Properties
///
/// Start point of the composition line draw
///
internal Point Start
{
get
{
return _start;
}
}
///
/// End point of the composition line draw
///
internal Point End
{
get
{
return _end;
}
}
#endregion Internal Properties
//-----------------------------------------------------
//
// Private Fields
//
//-----------------------------------------------------
#region Private Fields
// Start point of the composition line draw
private Point _start;
// End point of the composition line draw
private Point _end;
#endregion Private Fields
}
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- CookieParameter.cs
- SamlSerializer.cs
- TcpStreams.cs
- SchemaTableColumn.cs
- HttpListenerRequest.cs
- DocumentApplicationJournalEntry.cs
- WebHeaderCollection.cs
- LoginNameDesigner.cs
- PropertyEmitter.cs
- XomlCompilerParameters.cs
- SqlTypesSchemaImporter.cs
- Utilities.cs
- FileDialogPermission.cs
- HtmlEncodedRawTextWriter.cs
- TableItemPattern.cs
- DataControlLinkButton.cs
- DebugView.cs
- ReservationCollection.cs
- CollectionBuilder.cs
- DataTableMapping.cs
- TrackBarRenderer.cs
- ScrollContentPresenter.cs
- NonVisualControlAttribute.cs
- ListBox.cs
- SpeechAudioFormatInfo.cs
- PathStreamGeometryContext.cs
- OrthographicCamera.cs
- WebBrowser.cs
- _Semaphore.cs
- FixedTextPointer.cs
- WsatAdminException.cs
- MetroSerializationManager.cs
- NullableLongMinMaxAggregationOperator.cs
- ScriptControlManager.cs
- SQlBooleanStorage.cs
- CompiledRegexRunnerFactory.cs
- HostingEnvironmentException.cs
- ObjectDataSourceEventArgs.cs
- VersionedStreamOwner.cs
- SemaphoreFullException.cs
- DocumentReference.cs
- InkPresenterAutomationPeer.cs
- TabRenderer.cs
- ZoneButton.cs
- MatchingStyle.cs
- SystemNetworkInterface.cs
- TabRenderer.cs
- UserInitiatedRoutedEventPermissionAttribute.cs
- AnimationLayer.cs
- Peer.cs
- ActiveDocumentEvent.cs
- WpfKnownTypeInvoker.cs
- EventListener.cs
- FamilyMapCollection.cs
- XPathParser.cs
- ToolStripItem.cs
- BaseCollection.cs
- MarkupCompilePass2.cs
- DataViewManager.cs
- RegexGroup.cs
- SoapWriter.cs
- FileSystemEventArgs.cs
- SelectionHighlightInfo.cs
- WorkflowInstanceUnhandledExceptionRecord.cs
- SamlNameIdentifierClaimResource.cs
- TypeListConverter.cs
- FolderLevelBuildProvider.cs
- Section.cs
- X509ScopedServiceCertificateElementCollection.cs
- TableCellsCollectionEditor.cs
- ConcurrentDictionary.cs
- XamlInterfaces.cs
- ListChangedEventArgs.cs
- Underline.cs
- LocalFileSettingsProvider.cs
- TemplateEditingVerb.cs
- SafeFileHandle.cs
- SQLInt16Storage.cs
- CompilerInfo.cs
- ModelUtilities.cs
- WindowsTokenRoleProvider.cs
- X509Certificate.cs
- TriState.cs
- SafeNativeMemoryHandle.cs
- DeviceContext.cs
- EnvironmentPermission.cs
- ProcessHostFactoryHelper.cs
- DoubleCollectionConverter.cs
- TextEndOfSegment.cs
- Vector3DAnimation.cs
- ProviderBase.cs
- InvokeProviderWrapper.cs
- ReachFixedPageSerializer.cs
- DbProviderFactoriesConfigurationHandler.cs
- BamlLocalizer.cs
- TextEditorCopyPaste.cs
- CompareValidator.cs
- HuffmanTree.cs
- LoginCancelEventArgs.cs
- FlowLayoutSettings.cs