Code:
/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / Framework / MS / Internal / Text / ComplexLine.cs / 1305600 / ComplexLine.cs
//---------------------------------------------------------------------------- // // Copyright (C) Microsoft Corporation. All rights reserved. // // File: ComplexLine.cs // // Description: Text line formatter. // // History: // 09/10/2003 : [....] - created. // //--------------------------------------------------------------------------- using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Media; using System.Windows.Media.TextFormatting; using MS.Internal.Documents; using MS.Internal.PtsHost; namespace MS.Internal.Text { // --------------------------------------------------------------------- // Text line formatter. // --------------------------------------------------------------------- internal sealed class ComplexLine : Line { // ------------------------------------------------------------------ // // TextSource Implementation // // ----------------------------------------------------------------- #region TextSource Implementation // ------------------------------------------------------------------ // Get a text run at specified text source position. // ------------------------------------------------------------------ public override TextRun GetTextRun(int dcp) { TextRun run = null; StaticTextPointer position = _owner.TextContainer.CreateStaticPointerAtOffset(dcp); switch (position.GetPointerContext(LogicalDirection.Forward)) { case TextPointerContext.Text: run = HandleText(position); break; case TextPointerContext.ElementStart: run = HandleElementStartEdge(position); break; case TextPointerContext.ElementEnd: run = HandleElementEndEdge(position); break; case TextPointerContext.EmbeddedElement: run = HandleInlineObject(position, dcp); break; case TextPointerContext.None: run = new TextEndOfParagraph(_syntheticCharacterLength); break; } Debug.Assert(run != null, "TextRun has not been created."); Debug.Assert(run.Length > 0, "TextRun has to have positive length."); return run; } // ----------------------------------------------------------------- // Get text immediately before specified text source position. // ------------------------------------------------------------------ public override TextSpanGetPrecedingText(int dcp) { // Parameter validation Debug.Assert(dcp >= 0); int nonTextLength = 0; CharacterBufferRange precedingText = CharacterBufferRange.Empty; CultureInfo culture = null; if (dcp > 0) { // Create TextPointer at dcp ITextPointer position = _owner.TextContainer.CreatePointerAtOffset(dcp, LogicalDirection.Backward); // Move backward until we find a position at the end of a text run, or reach start of TextContainer while (position.GetPointerContext(LogicalDirection.Backward) != TextPointerContext.Text && position.CompareTo(_owner.TextContainer.Start) != 0) { position.MoveByOffset(-1); nonTextLength++; } // Return text in run. If it is at start of TextContainer this will return an empty string string precedingTextString = position.GetTextInRun(LogicalDirection.Backward); precedingText = new CharacterBufferRange(precedingTextString, 0, precedingTextString.Length); StaticTextPointer pointer = position.CreateStaticPointer(); DependencyObject element = (pointer.Parent != null) ? pointer.Parent : _owner; culture = DynamicPropertyReader.GetCultureInfo(element); } return new TextSpan ( nonTextLength + precedingText.Length, new CultureSpecificCharacterBufferRange(culture, precedingText) ); } /// /// TextFormatter to map a text source character index to a text effect character index /// /// text source character index ///the text effect index corresponding to the text effect character index public override int GetTextEffectCharacterIndexFromTextSourceCharacterIndex( int textSourceCharacterIndex ) { return textSourceCharacterIndex; } #endregion TextSource Implementation //------------------------------------------------------------------- // // Internal Methods // //------------------------------------------------------------------- #region Internal Methods // ----------------------------------------------------------------- // Constructor. // // owner - owner of the line. // ------------------------------------------------------------------ internal ComplexLine(System.Windows.Controls.TextBlock owner) : base(owner) { } // ----------------------------------------------------------------- // Arrange content of formatted line. // // vc - Visual collection of the parent. // lineOffset - Offset of the line. // ------------------------------------------------------------------ internal override void Arrange(VisualCollection vc, Vector lineOffset) { // Arrange inline objects int runDcp = _dcp; IList> runs = _line.GetTextRunSpans(); Debug.Assert(runs != null, "Cannot retrieve runs collection."); // Calculate offset shift due to trailing spaces double adjustedXOffset = lineOffset.X + CalculateXOffsetShift(); foreach (TextSpan textSpan in runs) { TextRun run = textSpan.Value; if (run is InlineObject) { InlineObject inlineObject = run as InlineObject; // Disconnect visual from its old parent, if necessary. Visual currentParent = VisualTreeHelper.GetParent(inlineObject.Element) as Visual; if (currentParent != null) { ContainerVisual parent = currentParent as ContainerVisual; Invariant.Assert(parent != null, "parent should always derives from ContainerVisual"); parent.Children.Remove(inlineObject.Element); } // Get position of inline object withing the text line. FlowDirection flowDirection; Rect rect = GetBoundsFromPosition(runDcp, inlineObject.Length, out flowDirection); Debug.Assert(DoubleUtil.GreaterThanOrClose(rect.Width, 0), "Negative inline object's width."); ContainerVisual proxyVisual = new ContainerVisual(); if (inlineObject.Element is FrameworkElement) { FlowDirection parentFlowDirection = _owner.FlowDirection; // Check parent's FlowDirection to determine if mirroring is needed DependencyObject parent = ((FrameworkElement)inlineObject.Element).Parent; if(parent != null) { parentFlowDirection = (FlowDirection)parent.GetValue(FrameworkElement.FlowDirectionProperty); } PtsHelper.UpdateMirroringTransform(_owner.FlowDirection, parentFlowDirection, proxyVisual, rect.Width); } vc.Add(proxyVisual); if (_owner.UseLayoutRounding) { // If using layout rounding, check whether rounding needs to compensate for high DPI proxyVisual.Offset = new Vector(UIElement.RoundLayoutValue(lineOffset.X + rect.Left, FrameworkElement.DpiScaleX), UIElement.RoundLayoutValue(lineOffset.Y + rect.Top, FrameworkElement.DpiScaleY)); } else { proxyVisual.Offset = new Vector(lineOffset.X + rect.Left, lineOffset.Y + rect.Top); } proxyVisual.Children.Add(inlineObject.Element); // Combine text line offset (relative to the Text control) with inline object // offset (relative to the line) and set transorm on the visual. Trailing spaces // shift is not added here because it is returned by GetBoundsFromPosition inlineObject.Element.Arrange(new Rect(inlineObject.Element.DesiredSize)); } // Do not use TextRun.Length, because it gives total length of the run. // So, if the run is broken between lines, it gives incorrect value. // Use length of the TextSpan instead, which gives the correct length here. runDcp += textSpan.Length; } } // ------------------------------------------------------------------ // Find out if there are any inline objects. // ----------------------------------------------------------------- internal override bool HasInlineObjects() { bool hasInlineObjects = false; IList > runs = _line.GetTextRunSpans(); Debug.Assert(runs != null, "Cannot retrieve runs collection."); foreach (TextSpan textSpan in runs) { if (textSpan.Value is InlineObject) { hasInlineObjects = true; break; } } return hasInlineObjects; } // ------------------------------------------------------------------ // Hit tests to the correct ContentElement within the line. // // offset - offset within the line. // // Returns: ContentElement which has been hit. // ----------------------------------------------------------------- internal override IInputElement InputHitTest(double offset) { TextContainer tree; DependencyObject element; CharacterHit charHit; TextPointer position; TextPointerContext type = TextPointerContext.None; element = null; // We can only support hittesting text elements in a TextContainer. // If the TextContainer is not a TextContainer, return null which higher up the stack // will be converted into a reference to the control itself. tree = _owner.TextContainer as TextContainer; // Adjusted offset for shift due to trailing spaces rendering double delta = CalculateXOffsetShift(); if (tree != null) { if (_line.HasOverflowed && _owner.ParagraphProperties.TextTrimming != TextTrimming.None) { // We should not shift offset in this case Invariant.Assert(DoubleUtil.AreClose(delta, 0)); System.Windows.Media.TextFormatting.TextLine line = _line.Collapse(GetCollapsingProps(_wrappingWidth, _owner.ParagraphProperties)); Invariant.Assert(line.HasCollapsed, "Line has not been collapsed"); // Get TextPointer from specified distance. charHit = line.GetCharacterHitFromDistance(offset); } else { charHit = _line.GetCharacterHitFromDistance(offset - delta); } position = new TextPointer(_owner.ContentStart, CalcPositionOffset(charHit), LogicalDirection.Forward); if (position != null) { if (charHit.TrailingLength == 0) { // Start of character. Look forward type = position.GetPointerContext(LogicalDirection.Forward); } else { // End of character. Look backward type = position.GetPointerContext(LogicalDirection.Backward); } // Get element only for Text & Start/End element, for all other positions // return null (it means that the line owner has been hit). if (type == TextPointerContext.Text || type == TextPointerContext.ElementEnd) { element = position.Parent as TextElement; } else if (type == TextPointerContext.ElementStart) { element = position.GetAdjacentElementFromOuterPosition(LogicalDirection.Forward); } } } return element as IInputElement; } #endregion Internal Methods //------------------------------------------------------------------- // // Private Methods // //------------------------------------------------------------------- #region Private Methods // ------------------------------------------------------------------ // Fetch the next run at text position. // // position - current position in the text array // ----------------------------------------------------------------- private TextRun HandleText(StaticTextPointer position) { DependencyObject element; StaticTextPointer endOfRunPosition; Debug.Assert(position.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.Text, "TextPointer does not point to characters."); if (position.Parent != null) { element = position.Parent; } else { element = _owner; } // Extract the aggregated properties into something that the textrun can use. // TextRunProperties textProps = new TextProperties(element, position, false /* inline objects */, true /* get background */); // Calculate the end of the run by finding either: // a) the next intersection of highlight ranges, or // b) the natural end of this textrun endOfRunPosition = _owner.Highlights.GetNextPropertyChangePosition(position, LogicalDirection.Forward); // Clamp the text run at an arbitrary limit, so we don't make // an unbounded allocation. if (position.GetOffsetToPosition(endOfRunPosition) > 4096) { endOfRunPosition = position.CreatePointer(4096); } // Get character buffer for the text run. char[] textBuffer = new char[position.GetOffsetToPosition(endOfRunPosition)]; // Copy characters from text run into buffer. Note the actual number of characters copied, // which may be different than the buffer's length. Buffer length only specifies the maximum // number of characters int charactersCopied = position.GetTextInRun(LogicalDirection.Forward, textBuffer, 0, textBuffer.Length); // Create text run, using characters copied as length return new TextCharacters(textBuffer, 0, charactersCopied, textProps); } // ------------------------------------------------------------------ // Fetch the next run at element open edge position. // // position - current position in the text array // ------------------------------------------------------------------ private TextRun HandleElementStartEdge(StaticTextPointer position) { Debug.Assert(position.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.ElementStart, "TextPointer does not point to element start edge."); // TextRun run = null; TextElement element = (TextElement)position.GetAdjacentElement(LogicalDirection.Forward); Debug.Assert(element != null, "Cannot use ITextContainer that does not provide TextElement instances."); if (element is LineBreak) { run = new TextEndOfLine(_elementEdgeCharacterLength * 2); } else if (element.IsEmpty) { // Empty TextElement should affect line metrics. // TextFormatter does not support this feature right now, so as workaround // TextRun with ZERO WIDTH SPACE is used. TextRunProperties textProps = new TextProperties(element, position, false /* inline objects */, true /* get background */); char[] textBuffer = new char[_elementEdgeCharacterLength * 2]; textBuffer[0] = (char)0x200B; textBuffer[1] = (char)0x200B; run = new TextCharacters(textBuffer, 0, textBuffer.Length, textProps); } else { Inline inline = element as Inline; if (inline == null) { run = new TextHidden(_elementEdgeCharacterLength); } else { DependencyObject parent = inline.Parent; FlowDirection inlineFlowDirection = inline.FlowDirection; FlowDirection parentFlowDirection = inlineFlowDirection; if(parent != null) { parentFlowDirection = (FlowDirection)parent.GetValue(FrameworkElement.FlowDirectionProperty); } TextDecorationCollection inlineTextDecorations = DynamicPropertyReader.GetTextDecorations(inline); if (inlineFlowDirection != parentFlowDirection) { // Inline's flow direction is different from its parent. Need to create new TextSpanModifier with flow direction if (inlineTextDecorations == null || inlineTextDecorations.Count == 0) { run = new TextSpanModifier( _elementEdgeCharacterLength, null, null, inlineFlowDirection ); } else { run = new TextSpanModifier( _elementEdgeCharacterLength, inlineTextDecorations, inline.Foreground, inlineFlowDirection ); } } else { if (inlineTextDecorations == null || inlineTextDecorations.Count == 0) { run = new TextHidden(_elementEdgeCharacterLength); } else { run = new TextSpanModifier( _elementEdgeCharacterLength, inlineTextDecorations, inline.Foreground ); } } } } return run; } // ----------------------------------------------------------------- // Fetch the next run at element close edge position. // // position - current position in the text array // ------------------------------------------------------------------ private TextRun HandleElementEndEdge(StaticTextPointer position) { Debug.Assert(position.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.ElementEnd, "TextPointer does not point to element end edge."); TextRun run = null; TextElement element = (TextElement)position.GetAdjacentElement(LogicalDirection.Forward); Debug.Assert(element != null, "Element should be here."); Inline inline = element as Inline; if (inline == null) { run = new TextHidden(_elementEdgeCharacterLength); } else { DependencyObject parent = inline.Parent; FlowDirection parentFlowDirection = inline.FlowDirection; if(parent != null) { parentFlowDirection = (FlowDirection)parent.GetValue(FrameworkElement.FlowDirectionProperty); } if (inline.FlowDirection != parentFlowDirection) { run = new TextEndOfSegment(_elementEdgeCharacterLength); } else { TextDecorationCollection textDecorations = DynamicPropertyReader.GetTextDecorations(inline); if (textDecorations == null || textDecorations.Count == 0) { // (2) End of inline element, hide CloseEdge character and continue run = new TextHidden(_elementEdgeCharacterLength); } else { run = new TextEndOfSegment(_elementEdgeCharacterLength); } } } return run; } // ----------------------------------------------------------------- // Fetch the next run at UIElment position. // // position - current position in the text array // dcp - current position in the text array // ----------------------------------------------------------------- private TextRun HandleInlineObject(StaticTextPointer position, int dcp) { Debug.Assert(position.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.EmbeddedElement, "TextPointer does not point to embedded object."); TextRun run = null; DependencyObject element = position.GetAdjacentElement(LogicalDirection.Forward) as DependencyObject; if (element is UIElement) { // TextRunProperties textProps = new TextProperties(element, position, true /* inline objects */, true /* get background */); // Create object run. run = new InlineObject(dcp, TextContainerHelper.EmbeddedObjectLength, (UIElement)element, textProps, _owner); } else { // If the embedded object is of an unknown type (not UIElement), // treat it as element edge. run = HandleElementEndEdge(position); } return run; } /// /// Calculates the offset for the corresponding TextPointer from a CharacterHit /// ////// This is necessary to ensure that we don't try to create a position at an offset greater than TextContainer's symbol count. /// This may happen when a line is collapsed with ellipsis and we hit-test at the trailing edge of ellipsis, the trailing length /// returned for the CharacterHit is the length of all collapsed characters, including the synthetic EOP. If we try to /// create a position at this trailing length we can exceed TextContainer's symbol count. /// private int CalcPositionOffset(CharacterHit charHit) { int offset = charHit.FirstCharacterIndex + charHit.TrailingLength; if (this.EndOfParagraph) { offset = Math.Min(_dcp + this.Length, offset); } return offset; } #endregion Private methods //------------------------------------------------------------------- // // Private Fields // //-------------------------------------------------------------------- #region Private Fields // ----------------------------------------------------------------- // Element edge character length. // ------------------------------------------------------------------ private static int _elementEdgeCharacterLength = 1; #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
- EFAssociationProvider.cs
- BrushMappingModeValidation.cs
- TextPenaltyModule.cs
- SchemaNotation.cs
- PartialCachingAttribute.cs
- GPStream.cs
- HostingEnvironment.cs
- InProcStateClientManager.cs
- FileDialogCustomPlaces.cs
- SchemaImporterExtensionsSection.cs
- SecUtil.cs
- BooleanConverter.cs
- StrongNameIdentityPermission.cs
- SuppressMessageAttribute.cs
- CanonicalFontFamilyReference.cs
- X509Certificate2.cs
- ADConnectionHelper.cs
- CharConverter.cs
- WindowsIdentity.cs
- InvariantComparer.cs
- SiteMembershipCondition.cs
- SchemaType.cs
- FileDataSourceCache.cs
- CodeVariableReferenceExpression.cs
- ToolStripItemCollection.cs
- Quad.cs
- DbException.cs
- IdentifierCollection.cs
- HtmlEmptyTagControlBuilder.cs
- GridView.cs
- CategoryAttribute.cs
- PropertyGroupDescription.cs
- GregorianCalendar.cs
- HtmlControl.cs
- Quaternion.cs
- SelectionRange.cs
- SharedPersonalizationStateInfo.cs
- ButtonFlatAdapter.cs
- _Connection.cs
- OutOfMemoryException.cs
- GridViewColumnHeader.cs
- XmlILModule.cs
- TextAction.cs
- FileUpload.cs
- DocumentOrderQuery.cs
- CodeCompiler.cs
- ChannelDispatcherCollection.cs
- XmlTextWriter.cs
- EmptyStringExpandableObjectConverter.cs
- ViewStateModeByIdAttribute.cs
- xmlNames.cs
- Intellisense.cs
- TcpAppDomainProtocolHandler.cs
- XmlAnyElementAttribute.cs
- SmtpReplyReader.cs
- ReferentialConstraint.cs
- StackOverflowException.cs
- MetadataAssemblyHelper.cs
- UndoManager.cs
- DetailsViewInsertEventArgs.cs
- AssemblyResourceLoader.cs
- EtwTrace.cs
- SqlPersonalizationProvider.cs
- IgnoreFileBuildProvider.cs
- BitmapEffectInput.cs
- InheritanceUI.cs
- SmtpSpecifiedPickupDirectoryElement.cs
- autovalidator.cs
- ComplexBindingPropertiesAttribute.cs
- DataGridViewAutoSizeModeEventArgs.cs
- GridViewColumn.cs
- ResolvedKeyFrameEntry.cs
- XmlEntityReference.cs
- PropertyGeneratedEventArgs.cs
- DocumentAutomationPeer.cs
- TripleDESCryptoServiceProvider.cs
- DataContractSerializerOperationGenerator.cs
- GZipDecoder.cs
- Crc32.cs
- XmlSequenceWriter.cs
- HttpContextWrapper.cs
- AnnotationResourceCollection.cs
- ImageListStreamer.cs
- TCPClient.cs
- BaseUriHelper.cs
- FaultContractAttribute.cs
- URLIdentityPermission.cs
- CombinedHttpChannel.cs
- GridViewColumn.cs
- ContentPresenter.cs
- PathSegment.cs
- KeyGestureConverter.cs
- TreeViewCancelEvent.cs
- TypedTableBaseExtensions.cs
- ResourcesBuildProvider.cs
- EventManager.cs
- XsltFunctions.cs
- DataGridViewColumnHeaderCell.cs
- ZipIOExtraFieldPaddingElement.cs
- FrameworkContentElementAutomationPeer.cs