FlowPosition.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ DotNET / DotNET / 8.0 / untmp / WIN_WINDOWS / lh_tools_devdiv_wpf / Windows / wcp / Framework / System / Windows / Documents / FlowPosition.cs / 1 / FlowPosition.cs

                            //---------------------------------------------------------------------------- 
// 
//      Copyright (C) 2003 by Microsoft Corporation.  All rights reserved.
// 
// 
// Description:
//      FlowPosition represents a navigational position in a document's content flow. 
// 
// History:
//      02/10/2003 - [....] ([....]) - Created. 
//
//---------------------------------------------------------------------------

namespace System.Windows.Documents 
{
    using MS.Internal.Documents; 
    using System; 
    using System.Collections;
    using System.Diagnostics; 
    using System.Globalization;
    using System.Windows.Controls;

 

    //===================================================================== 
    ///  
    /// FlowPosition represents a navigational position in a document's content flow.
    ///  
    /// 
    /// A FlowPosition is represented by a FlowNode in the backing store and offset within
    /// the flow node (starts with 0, on the left side of the symbol). e.g.
    ///         <P>     H   A   L     </P> 
    ///        0          1 0   1   2   3  0          1
    ///  
    internal sealed class FlowPosition : IComparable 
    {
        //------------------------------------------------------------------- 
        //
        // Connstructors
        //
        //---------------------------------------------------------------------- 

        #region Constructors 
        internal FlowPosition(FixedTextContainer container, FlowNode node, int offset) 
        {
            Debug.Assert(!FlowNode.IsNull(node)); 
            _container  = container;
            _flowNode   = node;
            _offset     = offset;
        } 
        #endregion Constructors
 
        //------------------------------------------------------------------- 
        //
        // Public Methods 
        //
        //----------------------------------------------------------------------

        #region Public Methods 
        /// 
        /// Create a shallow copy of this objet 
        ///  
        /// A clone of this FlowPosition
        public object Clone() 
        {
            return new FlowPosition(_container, _flowNode, _offset);
        }
 

        // Compare two FixedTextPointer based on their flow order and offset 
        public int CompareTo(object o) 
        {
            if (o == null) 
            {
                throw new ArgumentNullException("o");
            }
 
            FlowPosition flow = o as FlowPosition;
            if (flow == null) 
            { 
                throw new ArgumentException(SR.Get(SRID.UnexpectedParameterType, o.GetType(), typeof(FlowPosition)), "o");
            } 

            return _OverlapAwareCompare(flow);
        }
 

        ///  
        /// Compute hash code. A flow position is predominantly identified 
        /// by its flow node and the offset.
        ///  
        /// int - hash code
        public override int GetHashCode()
        {
            return _flowNode.GetHashCode()^_offset.GetHashCode(); 
        }
 
 
#if DEBUG
        ///  
        /// Create a string representation of this object
        /// 
        /// string - A string representation of this object
        public override string ToString() 
        {
            return String.Format(CultureInfo.InvariantCulture, "FP[{0}+{1}]", _flowNode, _offset); 
        } 
#endif
        #endregion Public Methods 

        //--------------------------------------------------------------------
        //
        // Public Properties 
        //
        //--------------------------------------------------------------------- 
 
        //--------------------------------------------------------------------
        // 
        // Public Events
        //
        //---------------------------------------------------------------------
 
        //-------------------------------------------------------------------
        // 
        // Internal Methods 
        //
        //--------------------------------------------------------------------- 

        #region Internal Methods

        //-------------------------------------------------------------------- 
        // Text OM Helper
        //--------------------------------------------------------------------- 
        #region Text OM Helper 
        // Returns the count of symbols between pos1 and pos2
        internal int GetDistance(FlowPosition flow) 
        {
            Debug.Assert(flow != null);
            // if both clings to the same flow node, simply
            // compare the offset 
            if (_flowNode.Equals(flow._flowNode))
            { 
                return flow._offset - _offset; 
            }
 
            // otherwise scan to find out distance
            // Make sure scanning from low to high flow order
            int np = _OverlapAwareCompare(flow);
            FlowPosition flowScan, flowEnd; 
            if (np == -1)
            { 
                // scan forward 
                flowScan = (FlowPosition)this.Clone();
                flowEnd  = flow; 
            }
            else
            {
                // scan backward 
                flowScan = (FlowPosition)flow.Clone();
                flowEnd  = this; 
            } 

            // scan from low to high and accumulate counts 
            // devirtualize the node as it scans
            int distance = 0;
            while (!flowScan._IsSamePosition(flowEnd))
            { 
                if (flowScan._flowNode.Equals(flowEnd._flowNode))
                { 
                    distance += (flowEnd._offset - flowScan._offset); 
                    break;
                } 
                int scan = flowScan._vScan(LogicalDirection.Forward, -1);

                distance += scan;
            } 
            return np * (-1) * distance;
        } 
 

 
        // Returns TextPointerContext.None if query is Backward at TextContainer.Start or Forward at TetContainer.End
        internal TextPointerContext GetPointerContext(LogicalDirection dir)
        {
            Debug.Assert(dir == LogicalDirection.Forward || dir == LogicalDirection.Backward); 
            return _vGetSymbolType(dir);
        } 
 

        // returns remaining text length on dir 
        internal int GetTextRunLength(LogicalDirection dir)
        {
            Debug.Assert(GetPointerContext(dir) == TextPointerContext.Text);
            FlowPosition flow = GetClingPosition(dir); 

            if (dir == LogicalDirection.Forward) 
            { 
                return flow._NodeLength - flow._offset;
            } 
            else
            {
                return flow._offset;
            } 
        }
 
 
        // Get Text until end-of-run or maxLength/limit is hit.
        internal int GetTextInRun(LogicalDirection dir, int maxLength, char[] chars, int startIndex) 
        {
            Debug.Assert(GetPointerContext(dir) == TextPointerContext.Text);

            // make sure the position is clinged to text run 
            FlowPosition flow = GetClingPosition(dir);
 
            int runLength = flow._NodeLength; 
            int remainingLength;
            if (dir == LogicalDirection.Forward) 
            {
                remainingLength = runLength - flow._offset;
            }
            else 
            {
                remainingLength = flow._offset; 
            } 
            maxLength = Math.Min(maxLength, remainingLength);
 
            //
            //

 

 
            string text = _container.FixedTextBuilder.GetFlowText(flow._flowNode); 
            if (dir == LogicalDirection.Forward)
            { 
                Array.Copy(text.ToCharArray(flow._offset, maxLength), 0, chars, startIndex, maxLength);
            }
            else
            { 
                Array.Copy(text.ToCharArray(flow._offset - maxLength, maxLength), 0, chars, startIndex, maxLength);
            } 
            return maxLength; 
        }
 

        // Get Embedeed Object instance
        internal object GetAdjacentElement(LogicalDirection dir)
        { 
            FlowPosition flow = GetClingPosition(dir);
            FlowNodeType type = flow._flowNode.Type; 
            Debug.Assert(type == FlowNodeType.Object || type == FlowNodeType.Noop || type == FlowNodeType.Start || type == FlowNodeType.End); 

            if (type == FlowNodeType.Noop) 
            {
                return String.Empty;
            }
            else 
            {
                Object obj = ((FixedElement)flow._flowNode.Cookie).GetObject(); 
                Image image = obj as Image; 
                if (type == FlowNodeType.Object && image != null)
                { 
                    //Set width and height properties by looking at corresponding SOMImage
                    FixedSOMElement[] elements = flow._flowNode.FixedSOMElements;
                    if (elements != null && elements.Length > 0)
                    { 
                        FixedSOMImage somImage = elements[0] as FixedSOMImage;
                        if (somImage != null) 
                        { 
                            image.Width = somImage.BoundingRect.Width;
                            image.Height = somImage.BoundingRect.Height; 
                        }
                    }
                }
                return obj; 
            }
        } 
 
        // return FixedElement on the direction
        internal FixedElement GetElement(LogicalDirection dir) 
        {
            FlowPosition flow = GetClingPosition(dir);
            return (FixedElement)flow._flowNode.Cookie;
        } 

 
        // Immediate scoping element. If no element scops the position, 
        // returns the container element.
        internal FixedElement GetScopingElement() 
        {
            FlowPosition flowScan = (FlowPosition)this.Clone();
            int nestedElement = 0;
            TextPointerContext tst; 

            while (flowScan.FlowNode.Fp > 0 && !IsVirtual(_FixedFlowMap[flowScan.FlowNode.Fp - 1]) && // do not de-virtualize nodes 
                (tst = flowScan.GetPointerContext(LogicalDirection.Backward))!= TextPointerContext.None) 
            {
                if (tst == TextPointerContext.ElementStart) 
                {
                    if (nestedElement == 0)
                    {
                        FlowPosition flowEnd = flowScan.GetClingPosition(LogicalDirection.Backward); 
                        return (FixedElement)flowEnd._flowNode.Cookie;
                    } 
                    nestedElement--; 
                }
                else if (tst == TextPointerContext.ElementEnd) 
                {
                    nestedElement++;
                }
 
                flowScan.Move(LogicalDirection.Backward);
            } 
            return _container.ContainerElement; 
        }
 

        // return false if distance exceeds the size of the document
        internal bool Move(int distance)
        { 
            LogicalDirection dir = (distance >= 0 ? LogicalDirection.Forward : LogicalDirection.Backward);
            distance = Math.Abs(distance); 
            FlowNode startNode = _flowNode;  // save state 
            int startOffset = _offset;
            // keep moving until we hit distance 
            while (distance > 0)
            {
                int scan = _vScan(dir, distance);
                if (scan == 0) 
                {
                    //restore state and return false 
                    _flowNode = startNode; 
                    _offset = startOffset;
                    return false; 
                }
                distance -= scan;
            }
            return true; 
        }
 
 
        // Skip current symbol or run
        internal bool Move(LogicalDirection dir) 
        {
            if (_vScan(dir, -1) > 0)
            {
                return true; 
            }
 
            return false; 
        }
 
        // Move to next FlowPosition
        internal void MoveTo(FlowPosition flow)
        {
            _flowNode   = flow._flowNode; 
            _offset     = flow._offset;
        } 
 
        #endregion Text OM Helper
 

        //--------------------------------------------------------------------
        // Internal Methods
        //---------------------------------------------------------------------- 
        internal void AttachElement(FixedElement e)
        { 
            _flowNode.AttachElement(e); 
        }
 
        internal void GetFlowNode(LogicalDirection direction, out FlowNode flowNode, out int offsetStart)
        {
            FlowPosition fp = GetClingPosition(direction);
 
            offsetStart = fp._offset;
            flowNode = fp._flowNode; 
        } 

        // return FlowNode within this range 
        internal void GetFlowNodes(FlowPosition pEnd, out FlowNode[] flowNodes, out int offsetStart, out int offsetEnd)
        {
            Debug.Assert(this._OverlapAwareCompare(pEnd) < 0);
            flowNodes = null; 
            offsetStart = 0;
            offsetEnd = 0; 
 
            FlowPosition flowScan = GetClingPosition(LogicalDirection.Forward);
            offsetStart = flowScan._offset; 

            ArrayList ar = new ArrayList();
            int distance = GetDistance(pEnd);
            // keep moving until we hit distance 
            while (distance > 0)
            { 
                int scan = flowScan._vScan(LogicalDirection.Forward, distance); 

                distance -= scan; 
                if (flowScan.IsRun || flowScan.IsObject)
                {
                    ar.Add(flowScan._flowNode);
                    offsetEnd = flowScan._offset; 
                }
            } 
            flowNodes = (FlowNode [])ar.ToArray(typeof(FlowNode)); 
        }
 
        // A canonical position is one that clings to a FlowNode
        internal FlowPosition GetClingPosition(LogicalDirection dir)
        {
            FlowPosition flow = (FlowPosition)this.Clone(); 
            FlowNode fn;
 
            if (dir == LogicalDirection.Forward) 
            {
                if (_offset == _NodeLength) 
                {
                    // This position is at right side of a FlowNode
                    // look for next run
                    fn = _xGetNextFlowNode(); 
                    if (!FlowNode.IsNull(fn))
                    { 
                        flow._flowNode = fn; 
                        flow._offset = 0;
                    } 
#if DEBUG
                    else
                    {
                        DocumentsTrace.FixedTextOM.FlowPosition.Trace("GetClingPosition: Next FlowNode is null"); 
                    }
#endif 
                } 
            }
            else 
            {
                Debug.Assert(dir == LogicalDirection.Backward);
                if (_offset == 0)
                { 
                    // This position is at left side of a FlowNode
                    // look for previous run 
                    fn = _xGetPreviousFlowNode(); 
                    if (!FlowNode.IsNull(fn))
                    { 
                        flow._flowNode = fn;
                        flow._offset = flow._NodeLength;
                    }
#if DEBUG 
                    else
                    { 
                        DocumentsTrace.FixedTextOM.FlowPosition.Trace("GetClingPosition: Next FlowNode is null"); 
                    }
#endif 
                }
            }
            return flow;
        } 

 
        internal bool IsVirtual(FlowNode flowNode) 
        {
            return (flowNode.Type == FlowNodeType.Virtual); 
        }
        #endregion Internal Methods

 
        //-------------------------------------------------------------------
        // 
        // Internal Properties 
        //
        //---------------------------------------------------------------------- 

        #region Internal Properties
        internal FixedTextContainer TextContainer
        { 
            get
            { 
                return _container; 
            }
        } 

        internal bool IsBoundary
        {
            get 
            {
                return (_flowNode.Type == FlowNodeType.Boundary); 
            } 
        }
 
        internal bool IsStart
        {
            get
            { 
                return (_flowNode.Type == FlowNodeType.Start);
            } 
        } 

        internal bool IsEnd 
        {
            get
            {
                return (_flowNode.Type == FlowNodeType.End); 
            }
        } 
 
        // If th_Is position _Is symbol
        internal bool IsSymbol 
        {
            get
            {
                FlowNodeType t = _flowNode.Type; 
                return (t == FlowNodeType.Start || t == FlowNodeType.End || t == FlowNodeType.Object);
            } 
        } 

 
        // If the FlowNode has run length
        internal bool IsRun
        {
            get 
            {
                return (_flowNode.Type == FlowNodeType.Run); 
            } 
        }
 
        // If the FlowNode has run length
        internal bool IsObject
        {
            get 
            {
                return (_flowNode.Type == FlowNodeType.Object); 
            } 
        }
 
        internal FlowNode FlowNode
        {
            get
            { 
                return this._flowNode;
            } 
        } 

        #endregion Internal Properties 

        //-------------------------------------------------------------------
        //
        // Private Methods 
        //
        //--------------------------------------------------------------------- 
 
        #region Private Methods
        //------------------------------------------------------------------- 
        //  Helper functions that could result in de-virtualization
        //----------------------------------------------------------------------

        // scan one node forward, return characters/symbols passed 
        // limit < 0 means scan entire node.
        private int _vScan(LogicalDirection dir, int limit) 
        { 
            if (limit == 0)
            { 
                return 0;
            }

            FlowNode flowNode = _flowNode; 
            int scanned = 0;
            if (dir == LogicalDirection.Forward) 
            { 
                if (_offset == _NodeLength || flowNode.Type == FlowNodeType.Boundary)
                { 
                    // This position is at right side of a FlowNode
                    flowNode = _xGetNextFlowNode();
                    if (FlowNode.IsNull(flowNode))
                    { 
                        return scanned;
                    } 
                    _flowNode = flowNode; 
                    scanned = _NodeLength;
                } 
                else
                {
                    scanned = _NodeLength - _offset;
                } 

                _offset = _NodeLength; 
                if (limit > 0 && scanned > limit) 
                {
                    int back = scanned - limit; 
                    scanned  = limit;
                    _offset -= back;
                }
            } 
            else
            { 
                Debug.Assert(dir == LogicalDirection.Backward); 
                if (_offset == 0 || flowNode.Type == FlowNodeType.Boundary)
                { 
                    // This position is at left side of a FlowNode
                    // look for previous run
                    flowNode = _xGetPreviousFlowNode();
                    if (FlowNode.IsNull(flowNode)) 
                    {
                        return scanned; 
                    } 

                    _flowNode = flowNode; 
                    scanned = _NodeLength;
                }
                else
                { 
                    scanned = _offset;
                } 
 
                _offset = 0;
                if (limit > 0 && scanned > limit) 
                {
                    int back = scanned - limit;
                    scanned  = limit;
                    _offset += back; 
                }
            } 
            return scanned; 
        }
 

        private TextPointerContext _vGetSymbolType(LogicalDirection dir)
        {
            FlowNode flowNode = _flowNode; 
            if (dir == LogicalDirection.Forward)
            { 
                if (_offset == _NodeLength) 
                {
                    // This position is at right side of a FlowNode 
                    // look for next run
                    flowNode = _xGetNextFlowNode();
                }
 
                if (!FlowNode.IsNull(flowNode))
                { 
                    return _FlowNodeTypeToTextSymbol(flowNode.Type); 
                }
            } 
            else
            {
                Debug.Assert(dir == LogicalDirection.Backward);
                if (_offset == 0) 
                {
                    // This position is at left side of a FlowNode 
                    // look for previous run 
                    flowNode = _xGetPreviousFlowNode();
                } 

                if (!FlowNode.IsNull(flowNode))
                {
                    return _FlowNodeTypeToTextSymbol(flowNode.Type); 
                }
            } 
            return TextPointerContext.None; 
        }
 

        //-------------------------------------------------------------------
        // Helper functions that have raw access to backing store and provided
        // a non-virtualied view on top of virtualized content. 
        //----------------------------------------------------------------------
 
        // return null if no previous node 
        private FlowNode _xGetPreviousFlowNode()
        { 
            if (_flowNode.Fp > 1)
            {
                FlowNode flow = _FixedFlowMap[_flowNode.Fp - 1];
 
                // devirtualize the backing store
                if (IsVirtual(flow)) 
                { 
                    _FixedTextBuilder.EnsureTextOMForPage((int)flow.Cookie);
                    flow = _FixedFlowMap[_flowNode.Fp - 1]; 
                }

                // filter out boundary node
                if (flow.Type != FlowNodeType.Boundary) 
                {
                    return flow; 
                } 
            }
            return null; 
        }


        // return null if no next node 
        private FlowNode _xGetNextFlowNode()
        { 
            if (_flowNode.Fp < _FixedFlowMap.FlowCount - 1) 
            {
                FlowNode flow = _FixedFlowMap[_flowNode.Fp + 1]; 

                // devirtualize the backing store
                if (IsVirtual(flow))
                { 
                    _FixedTextBuilder.EnsureTextOMForPage((int)flow.Cookie);
                    flow = _FixedFlowMap[_flowNode.Fp + 1]; 
                } 

                // filter out boundary node 
                if (flow.Type != FlowNodeType.Boundary)
                {
                    return flow;
                } 
            }
 
            return null; 
        }
 

        //--------------------------------------------------------------------
        // Helper functions
        //--------------------------------------------------------------------- 

        // see if the two FlowPosition are indeed same position 
        private bool _IsSamePosition(FlowPosition flow) 
        {
            if (flow == null) 
            {
                return false;
            }
 
            return (_OverlapAwareCompare(flow) == 0);
        } 
 

        // intelligent compare routine that understands position overlap 
        private int _OverlapAwareCompare(FlowPosition flow)
        {
            Debug.Assert(flow != null);
            if ((object)this == (object)flow) 
            {
                return 0; 
            } 

            int comp = this._flowNode.CompareTo(flow._flowNode); 
            if (comp < 0)
            {
                // Check overlap positions
                // Positions are the same if they are at end of previous run or begin of next run 
                if (this._flowNode.Fp == flow._flowNode.Fp - 1 && this._offset == _NodeLength && flow._offset == 0)
                { 
                    return 0; 
                }
            } 
            else if (comp > 0)
            {
                // Check overlap positions
                // Positions are the same if they are at end of previous run or begin of next run 
                if (flow._flowNode.Fp == this._flowNode.Fp - 1 && flow._offset == flow._NodeLength && this._offset == 0)
                { 
                    return 0; 
                }
            } 
            else
            {
                // compare offset only
                Debug.Assert(this._flowNode.Equals(flow._flowNode)); 
                comp = this._offset.CompareTo(flow._offset);
            } 
            return comp; 
        }
 

        // Convert Flow Node Type to TextPointerContext
        private TextPointerContext _FlowNodeTypeToTextSymbol(FlowNodeType t)
        { 
            switch (t)
            { 
                case FlowNodeType.Start: 
                    return TextPointerContext.ElementStart;
 
                case FlowNodeType.End:
                    return TextPointerContext.ElementEnd;

                case FlowNodeType.Run: 
                    return TextPointerContext.Text;
 
                case FlowNodeType.Object: 
                case FlowNodeType.Noop:
                    return TextPointerContext.EmbeddedElement; 

                default:
                    return TextPointerContext.None;
            } 
        }
 
 
        #endregion Private Methods
 
        //--------------------------------------------------------------------
        //
        // Private Properties
        // 
        //---------------------------------------------------------------------
 
        #region Private Properties 
        // Get length of this node structure
        private int _NodeLength 
        {
            get
            {
                if (IsRun) 
                {
                    return (int)_flowNode.Cookie; 
                } 
                else
                { 
                    return 1;
                }
            }
        } 

 
        private FixedTextBuilder _FixedTextBuilder 
        {
            get 
            {
                return _container.FixedTextBuilder;
            }
        } 

        private FixedFlowMap _FixedFlowMap 
        { 
            get
            { 
                return _container.FixedTextBuilder.FixedFlowMap;
            }
        }
        #endregion Private Properties 

        //------------------------------------------------------------------- 
        // 
        // Private Fields
        // 
        //---------------------------------------------------------------------

        #region Private Fields
        private FixedTextContainer  _container;         // the container has the backing store for flow nodes 
        private FlowNode            _flowNode;          // flow node
        private int                 _offset;            // offset into flow 
        #endregion Private Fields 
    }
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.


                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK