File: src\Framework\MS\Internal\Documents\ParagraphResult.cs
Project: wpf\PresentationFramework.csproj (PresentationFramework)
//-------------------------------------------------------------------------
//
// <copyright file="ParagraphResult.cs" company="Microsoft">
//    Copyright (C) Microsoft Corporation.  All rights reserved.
// </copyright>
// 
// Description: The ParagraphResult class provides access to layout-calculated 
//              information for a paragraph. 
//
// History:  
//  05/20/2003 : Microsoft - Moving from Avalon branch.
//  06/25/2004 : Microsoft - Performance work.
//
//---------------------------------------------------------------------------
 
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Windows;
using System.Windows.Documents;
using System.Windows.Media;
using MS.Internal.PtsHost;
using MS.Internal.Text;
 
namespace MS.Internal.Documents 
{
    /// <summary>
    /// The ParagraphResult class provides access to layout-calculated 
    /// information for a paragraph.
    /// </summary>
    internal abstract class ParagraphResult
    {
        //-------------------------------------------------------------------
        //
        //  Constructors
        //
        //-------------------------------------------------------------------
 
        #region Constructors
 
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="paraClient">Object representing a paragraph.</param>
        internal ParagraphResult(BaseParaClient paraClient)
        {
            _paraClient = paraClient;
            _layoutBox = _paraClient.Rect.FromTextDpi();
            _element = paraClient.Paragraph.Element;
        }
 
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="paraClient">Object representing a paragraph.</param>
        /// <param name="layoutBox">Layout box for paragraph.</param>
        /// <param name="element">Element associated with this paragraph result.</param>
        internal ParagraphResult(BaseParaClient paraClient, Rect layoutBox, DependencyObject element) : this(paraClient)
        {
            _layoutBox = layoutBox;
            _element = element;
        }
 
        #endregion Constructors
 
        //-------------------------------------------------------------------
        //
        //  Internal Methods
        //
        //-------------------------------------------------------------------
 
        #region Internal Methods
 
        /// <summary>
        /// Returns whether the position is contained in this paragraph.
        /// </summary>
        /// <param name="position">A position to test.</param>
        /// <param name="strict">Apply strict validation rules.</param>
        /// <returns>
        /// True if column contains specified text position. 
        /// Otherwise returns false.
        /// </returns>
        internal virtual bool Contains(ITextPointer position, bool strict)
        {
            EnsureTextContentRange();
            return _contentRange.Contains(position, strict);
        }
 
        #endregion Internal Methods
 
        //-------------------------------------------------------------------
        //
        //  Internal Properties
        //
        //-------------------------------------------------------------------
 
        #region Internal Properties
 
        /// <summary>
        /// Represents the beginning of the paragraph’s contents.
        /// </summary>
        internal ITextPointer StartPosition
        {
            get
            {
                EnsureTextContentRange();
                return _contentRange.StartPosition;
            }
        }
 
        /// <summary>
        /// Represents the end of the paragraph’s contents.
        /// </summary>
        internal ITextPointer EndPosition
        {
            get
            {
                EnsureTextContentRange();
                return _contentRange.EndPosition;
            }
        }
 
        /// <summary>
        /// The bounding rectangle of the paragraph; this is relative 
        /// to the parent bounding box.
        /// </summary>
        internal Rect LayoutBox { get { return _layoutBox;  } }
 
        /// <summary>
        /// Element associated with the paragraph.
        /// </summary>
        internal DependencyObject Element { get { return _element;  } }
 
        /// <summary>
        /// In derived classes, will return the _hasTextContent member whose value is set if the paragraph
        /// has text content.
        /// </summary>
        internal virtual bool HasTextContent
        {
            get
            {
                return false;
            }
        }
 
        #endregion Internal Properties
 
        //-------------------------------------------------------------------
        //
        //  Private Methods
        //
        //-------------------------------------------------------------------
 
        #region Private Methods
 
        /// <summary>
        /// Retrive TextContentRange if necessary.
        /// </summary>
        private void EnsureTextContentRange()
        {
            if (_contentRange == null)
            {
                _contentRange = _paraClient.GetTextContentRange();
                Invariant.Assert(_contentRange != null);
            }
        }
 
        #endregion Private Methods
 
        //-------------------------------------------------------------------
        //
        //  Private Fields
        //
        //-------------------------------------------------------------------
 
        #region Private Fields
 
        /// <summary>
        /// Object representing a paragraph.
        /// </summary>
        protected readonly BaseParaClient _paraClient;
 
        /// <summary>
        /// Layout rectangle of the paragraph.
        /// </summary>
        protected readonly Rect _layoutBox;
 
        /// <summary>
        /// Element paragraph is associated with
        /// </summary>
        protected readonly DependencyObject _element;
 
        /// <summary>
        /// TextContentRanges representing the column's contents.
        /// </summary>
        private TextContentRange _contentRange;
 
        /// <summary>
        /// True if the paragraph or any nested paragraphs has text content
        /// </summary>
        protected bool _hasTextContent;
 
        #endregion Private Fields
    }
 
    /// <summary>
    /// The ParagraphResult for a paragraph which only contains other 
    /// paragraphs.
    /// </summary>
    internal sealed class ContainerParagraphResult : ParagraphResult
    {
        //-------------------------------------------------------------------
        //
        //  Constructors
        //
        //-------------------------------------------------------------------
 
        #region Constructors
 
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="paraClient">Object representing a paragraph.</param>
        internal ContainerParagraphResult(ContainerParaClient paraClient)
            : base(paraClient)
        {
        }
 
        #endregion Constructors
 
 
        //-------------------------------------------------------------------
        //
        //  Internal Methods
        //
        //-------------------------------------------------------------------
 
        #region Internal Methods
 
        /// <summary>
        /// Returns tight bounding geometry for the given text range.
        /// </summary>
        /// <param name="startPosition">Start position of the range.</param>
        /// <param name="endPosition">End position of the range.</param>
        /// <param name="visibleRect">Visible clipping area</param>
        /// <returns>Geometry object containing tight bound.</returns>
        internal Geometry GetTightBoundingGeometryFromTextPositions(ITextPointer startPosition, ITextPointer endPosition, Rect visibleRect)
        {
            return (((ContainerParaClient)_paraClient).GetTightBoundingGeometryFromTextPositions(startPosition, endPosition, visibleRect));
        }
 
        #endregion Internal Methods
 
        //-------------------------------------------------------------------
        //
        //  Internal Properties
        //
        //-------------------------------------------------------------------
 
        #region Internal Properties
 
        /// <summary>
        /// Collection of paragraphs in this paragraph.
        /// </summary>
        internal ReadOnlyCollection<ParagraphResult> Paragraphs
        {
            get
            {
                if (_paragraphs == null)
                {
                    // While getting children paragraph results, each paragraph is queried for text content
                    _paragraphs = ((ContainerParaClient)_paraClient).GetChildrenParagraphResults(out _hasTextContent);
                }
                Invariant.Assert(_paragraphs != null, "Paragraph collection is empty");
                return _paragraphs;
            }
        }
 
        /// <summary>
        /// True if the paragraph has text content, i.e. not just figures and floaters. For a container paragraph, this is
        /// true if any of its children paras has text content
        /// </summary>
        internal override bool HasTextContent
        {
            get
            {
                if (_paragraphs == null)
                {
                    // Getting Paragraphs collection queries each paragraph for text content and sets the value
                    ReadOnlyCollection<ParagraphResult> paragraphs = Paragraphs;
                }
                return _hasTextContent;
            }
        }
 
        #endregion Internal Properties
 
        //-------------------------------------------------------------------
        //
        //  Private Fields
        //
        //-------------------------------------------------------------------
 
        #region Private Fields
 
        /// <summary>
        /// The collection of ParagraphResults for for nested paragraphs.
        /// </summary>
        private ReadOnlyCollection<ParagraphResult> _paragraphs;
 
        #endregion Private Fields
    }
 
    /// <summary>
    /// The ParagraphResult for a paragraph which contains Text, Figures, 
    /// and Floaters (no nested paragraphs).
    /// </summary>
    internal sealed class TextParagraphResult : ParagraphResult
    {
        //-------------------------------------------------------------------
        //
        //  Constructors
        //
        //-------------------------------------------------------------------
 
        #region Constructors
 
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="paraClient">Object representing a paragraph.</param>
        internal TextParagraphResult(TextParaClient paraClient)
            : base(paraClient)
        {
        }
 
        #endregion Constructors
 
        //-------------------------------------------------------------------
        //
        //  Internal Methods
        //
        //-------------------------------------------------------------------
 
        #region Internal Methods
 
        /// <summary>
        /// Retrieves the height and offset, in pixels, of the edge of 
        /// the object/character represented by position.
        /// </summary>
        /// <param name="position">Position of an object/character.</param>
        /// <returns>
        /// The height, in pixels, of the edge of the object/character 
        /// represented by position.
        /// </returns>
        internal Rect GetRectangleFromTextPosition(ITextPointer position)
        {
            return ((TextParaClient)_paraClient).GetRectangleFromTextPosition(position);
        }
 
        /// <summary>
        /// Returns tight bounding geometry for the given text range.
        /// </summary>
        /// <param name="startPosition">Start position of the range.</param>
        /// <param name="endPosition">End position of the range.</param>
        /// <param name="paragraphTopSpace">Paragraph's top space.</param>
        /// <param name="visibleRect">Visible clipping rectangle</param>
        /// <returns>Geometry object containing tight bound.</returns>
        internal Geometry GetTightBoundingGeometryFromTextPositions(ITextPointer startPosition, ITextPointer endPosition, double paragraphTopSpace, Rect visibleRect)
        {
            return ((TextParaClient)_paraClient).GetTightBoundingGeometryFromTextPositions(startPosition, endPosition, paragraphTopSpace, visibleRect);
        }
 
        /// <summary>
        /// Returns true if caret is at unit boundary
        /// </summary>
        /// <param name="position">Position of an object/character.</param>
        internal bool IsAtCaretUnitBoundary(ITextPointer position)
        {
            return ((TextParaClient)_paraClient).IsAtCaretUnitBoundary(position);
        }
 
        /// <summary>
        /// Retrieves next caret unit position
        /// </summary>
        /// <param name="position">Position of an object/character.</param>
        /// <param name="direction">
        /// LogicalDirection in which we seek the next caret unit position
        /// </param>
        internal ITextPointer GetNextCaretUnitPosition(ITextPointer position, LogicalDirection direction)
        {
            return ((TextParaClient)_paraClient).GetNextCaretUnitPosition(position, direction);
        }
 
        /// <summary>
        /// Retrieves caret unit position after backspace
        /// </summary>
        /// <param name="position">Position of an object/character.</param>
        internal ITextPointer GetBackspaceCaretUnitPosition(ITextPointer position)
        {
            return ((TextParaClient)_paraClient).GetBackspaceCaretUnitPosition(position);
        }
 
        /// <summary>
        /// Retrieves collection of GlyphRuns from a range of text.
        /// </summary>
        /// <param name="glyphRuns">
        /// Preallocated collection of GlyphRuns. 
        /// May already contain runs and new runs need to be appended.
        /// </param>
        /// <param name="start">The beginning of the range.</param>
        /// <param name="end">The end of the range.</param>
        internal void GetGlyphRuns(List<GlyphRun> glyphRuns, ITextPointer start, ITextPointer end)
        {
            ((TextParaClient)_paraClient).GetGlyphRuns(glyphRuns, start, end);
        }
 
        /// <summary>
        /// Returns whether the position is contained in this paragraph.
        /// </summary>
        /// <param name="position">A position to test.</param>
        /// <param name="strict">Apply strict validation rules.</param>
        /// <returns>
        /// True if column contains specified text position. 
        /// Otherwise returns false.
        /// </returns>
        internal override bool Contains(ITextPointer position, bool strict)
        {
            bool contains = base.Contains(position, strict);
            // If the last line of text paragraph does not have content (example: some text<LineBreak /><Para />),
            // position after LineBreak with Forward direction belongs to this text paragraph and indicates
            // the last line of it. 
            // Because of that, always treat position after th last character of text paragraph as a valid
            // position for it. It is safe to do it because it is impossible to get 2 adjacent text paragraphs.
            if (!contains && strict)
            {
                contains = (position.CompareTo(this.EndPosition) == 0);
            }
            return contains;
        }
 
        #endregion Internal Methods
 
        //-------------------------------------------------------------------
        //
        //  Internal Properties
        //
        //-------------------------------------------------------------------
 
        #region Internal Properties
 
        /// <summary>
        /// Collection of lines in this paragraph.
        /// </summary>
        internal ReadOnlyCollection<LineResult> Lines
        {
            get
            {
                if (_lines == null)
                {
                    _lines = ((TextParaClient)_paraClient).GetLineResults();
                }
                Invariant.Assert(_lines != null, "Lines collection is null");
                return _lines;
            }
        }
 
        /// <summary>
        /// Collection of floating UIElements in this paragraph.
        /// </summary>
        internal ReadOnlyCollection<ParagraphResult> Floaters
        {
            get
            {
                if (_floaters == null)
                {
                    _floaters = ((TextParaClient)_paraClient).GetFloaters();
                }
                return _floaters;
            }
        }
 
        /// <summary>
        /// Collection of figure UIElements in this paragraph.
        /// </summary>
        internal ReadOnlyCollection<ParagraphResult> Figures
        {
            get
            {
                if (_figures == null)
                {
                    _figures = ((TextParaClient)_paraClient).GetFigures();
                }
                return _figures;
            }
        }
 
        /// <summary>
        /// True if the paragraph has some text content, not only attached objects. A paragraph with no lines has no text content.
        /// A paragraph with just one line containing figures and/or floaters does *not* have text content. We will ignore the end of paragraph character
        /// in this case and not hit test the lines in the paragraph. However, an empty paragraph with no figures and floaters does have text content.
        /// We treat the EOP character as being on a line by itself. This is needed for editing.
        /// </summary>
        internal override bool HasTextContent
        {
            get
            {
                return (Lines.Count > 0 && !ContainsOnlyFloatingElements);
            }
        }
 
        #endregion Internal Properties
 
        //-------------------------------------------------------------------
        //
        //  Private Properties
        //
        //-------------------------------------------------------------------
 
        #region Private Properties
 
        /// <summary>
        /// True if the paragraph contains only figures or floaters with no text.
        /// </summary>
        /// <remarks>
        /// An empty paragraph with no floating elements and just EOP character will return false.
        /// </remarks>
        private bool ContainsOnlyFloatingElements
        {
            get
            {
                bool floatingElementsOnly = false;
                TextParagraph textParagraph = _paraClient.Paragraph as TextParagraph;
                Invariant.Assert(textParagraph != null);
                if (textParagraph.HasFiguresOrFloaters())
                {
                    if (Lines.Count == 0)
                    {
                        // No lines, only attached objects
                        floatingElementsOnly = true;
                    }
                    else if (Lines.Count == 1)
                    {
                        // If this line has no content, paragraph contains only attached objects
                        int lastDcpAttachedObjectBeforeLine = textParagraph.GetLastDcpAttachedObjectBeforeLine(0);
                        if (lastDcpAttachedObjectBeforeLine + textParagraph.ParagraphStartCharacterPosition == textParagraph.ParagraphEndCharacterPosition)
                        {
                            floatingElementsOnly = true;
                        }
                    }                    
                }
                return floatingElementsOnly;
            }
        }
 
        #endregion Private Properties
 
        //-------------------------------------------------------------------
        //
        //  Private Fields
        //
        //-------------------------------------------------------------------
 
        #region Private Fields
 
        /// <summary>
        /// The collection of LineResults for for the paragraph's lines.
        /// </summary>
        private ReadOnlyCollection<LineResult> _lines;
 
        /// <summary>
        /// The collection of floating objects.
        /// </summary>
        private ReadOnlyCollection<ParagraphResult> _floaters;
 
        /// <summary>
        /// The collection of figures.
        /// </summary>
        private ReadOnlyCollection<ParagraphResult> _figures;
 
        #endregion Private Fields
    }
 
    /// <summary>
    /// The ParagraphResult for a table paragraph.
    /// </summary>
    internal sealed class TableParagraphResult : ParagraphResult
    {
        //-------------------------------------------------------------------
        //
        //  Constructors
        //
        //-------------------------------------------------------------------
 
        #region Constructors
 
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="paraClient">Object representing a paragraph.</param>
        internal TableParagraphResult(BaseParaClient paraClient)
            : base(paraClient)
        {
        }
 
        #endregion Constructors
 
        //-------------------------------------------------------------------
        //
        //  Internal Methods
        //
        //-------------------------------------------------------------------
 
        #region Internal Methods
 
        /// <summary>
        /// Returns the paragraphs for the appropriate cell, given a point
        /// </summary>
        /// <param name="point">Point to check for cell.</param>
        /// <param name="snapToText">Whether to snap to text</param>
        /// <returns>
        /// Array of paragraph results
        /// </returns>
        internal ReadOnlyCollection<ParagraphResult> GetParagraphsFromPoint(Point point, bool snapToText)
        {
            return ((TableParaClient)_paraClient).GetParagraphsFromPoint(point, snapToText);
        }
 
        /// <summary>
        /// Returns the paragraphs for the appropriate cell, given a text position
        /// </summary>
        /// <param name="position">Position to check for cell.</param>
        /// <returns>
        /// Array of paragraph results
        /// </returns>
        internal ReadOnlyCollection<ParagraphResult> GetParagraphsFromPosition(ITextPointer position)
        {
            return ((TableParaClient)_paraClient).GetParagraphsFromPosition(position);
        }
 
        /// <summary>
        /// Returns tight bounding geometry for the given text range.
        /// </summary>
        /// <param name="startPosition">Start position of the range.</param>
        /// <param name="endPosition">End position of the range.</param>
        /// <param name="visibleRect">Visible clipping rectangle.</param>
        /// <returns>Geometry object containing tight bound.</returns>
        internal Geometry GetTightBoundingGeometryFromTextPositions(ITextPointer startPosition, ITextPointer endPosition, Rect visibleRect)
        {
            return ((TableParaClient)_paraClient).GetTightBoundingGeometryFromTextPositions(startPosition, endPosition, visibleRect);
        }
 
        /// <summary>
        /// Returns the para client for a cell, given a position
        /// </summary>
        /// <param name="position">Position to check for cell.</param>
        /// <returns>
        /// Cell para client
        /// </returns>
        internal CellParaClient GetCellParaClientFromPosition(ITextPointer position)
        {
            return ((TableParaClient)_paraClient).GetCellParaClientFromPosition(position);
        }
 
        /// <summary>
        /// Returns an appropriate found cell
        /// </summary>
        /// <param name="suggestedX">Suggested X position for cell to find.</param>
        /// <param name="rowGroupIndex">RowGroupIndex to be above.</param>
        /// <param name="rowIndex">RowIndex to be above.</param>
        /// <returns>
        /// Cell Para Client of cell
        /// </returns>
        internal CellParaClient GetCellAbove(double suggestedX, int rowGroupIndex, int rowIndex)
        {
            return ((TableParaClient)_paraClient).GetCellAbove(suggestedX, rowGroupIndex, rowIndex);
        }
 
        /// <summary>
        /// Returns an appropriate found cell
        /// </summary>
        /// <param name="suggestedX">Suggested X position for cell to find.</param>
        /// <param name="rowGroupIndex">RowGroupIndex to be below.</param>
        /// <param name="rowIndex">RowIndex to be below.</param>
        /// <returns>
        /// Cell Para Client of cell
        /// </returns>
        internal CellParaClient GetCellBelow(double suggestedX, int rowGroupIndex, int rowIndex)
        {
            return ((TableParaClient)_paraClient).GetCellBelow(suggestedX, rowGroupIndex, rowIndex);
        }
 
        /// <summary>
        /// Returns a cellinfo structure for a given point
        /// </summary>
        /// <param name="point">Point to check for cell.</param>
        /// <returns>
        /// CellInfo class
        /// </returns>
        internal CellInfo GetCellInfoFromPoint(Point point)
        {
            return ((TableParaClient)_paraClient).GetCellInfoFromPoint(point);
        }
 
        /// <summary>
        /// Returns a rect corresponding to a row end position.
        /// </summary>
        /// <param name="position">Position to check against.</param>
        /// <returns>
        /// Rect, area
        /// </returns>
        internal Rect GetRectangleFromRowEndPosition(ITextPointer position)
        {
            return ((TableParaClient)_paraClient).GetRectangleFromRowEndPosition(position);
        }
 
 
        /// <summary>
        /// Collection of paragraphs in this paragraph.
        /// </summary>
        internal ReadOnlyCollection<ParagraphResult> Paragraphs
        {
            get
            {
                if (_paragraphs == null)
                {
                    // While getting children paras, query each one for text content and use the result to set _hasTextContent
                    _paragraphs = ((TableParaClient)_paraClient).GetChildrenParagraphResults(out _hasTextContent);
                }
                Invariant.Assert(_paragraphs != null, "Paragraph collection is empty");
                return _paragraphs;
            }
        }
 
        /// <summary>
        /// True if the paragraph has text content and not only figures/floaters. For Table paragraph this is true if any of its
        /// children paras has text content
        /// </summary>
        internal override bool HasTextContent
        {
            get
            {
                if (_paragraphs == null)
                {
                    // Getting Paragraphs collection sets the value of _hasTextContent by checking each child for text content
                    ReadOnlyCollection<ParagraphResult> paragraphs = Paragraphs;
                }
                return _hasTextContent;
            }
        }
 
        #endregion Internal Methods
 
        //-------------------------------------------------------------------
        //
        //  Private Fields
        //
        //-------------------------------------------------------------------
 
        #region Private Fields
 
        /// <summary>
        /// The collection of ParagraphResults for for nested paragraphs.
        /// </summary>
        private ReadOnlyCollection<ParagraphResult> _paragraphs;
 
        #endregion Private Fields
 
    }
 
    /// <summary>
    /// The ParagraphResult for a table row.
    /// </summary>
    internal sealed class RowParagraphResult : ParagraphResult
    {
        //-------------------------------------------------------------------
        //
        //  Constructors
        //
        //-------------------------------------------------------------------
 
        #region Constructors
 
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="paraClient">Object representing a paragraph.</param>
        /// <param name="index">Index of row in paragraph.</param>
        /// <param name="rowRect">Rectangle of row - as rendered.</param>
        /// <param name="rowParagraph">Actual paragraph result is bound to.</param>
        internal RowParagraphResult(BaseParaClient paraClient, int index, Rect rowRect, RowParagraph rowParagraph)
            : base(paraClient, rowRect, rowParagraph.Element)
        {
            _index = index;
        }
 
        #endregion Constructors
 
        //-------------------------------------------------------------------
        //
        //  Internal Methods
        //
        //-------------------------------------------------------------------
 
        #region Internal Properties
 
        /// <summary>
        /// Collection of paragraphs in this paragraph.
        /// </summary>
        internal ReadOnlyCollection<ParagraphResult> CellParagraphs
        {
            get
            {
                if (_cells == null)
                {
                    // Check each cell for text content when getting cell paragraph results
                    _cells = ((TableParaClient)_paraClient).GetChildrenParagraphResultsForRow(_index, out _hasTextContent);
                }
                Invariant.Assert(_cells != null, "Paragraph collection is empty");
                return _cells;
            }
        }
 
        /// <summary>
        /// True if the table row has text content, i.e. if any of the table cells in the row has text content
        /// </summary>
        internal override bool HasTextContent
        {
            get
            {
                if (_cells == null)
                {
                    // Getting cell paragraph results queries each one for text content
                    ReadOnlyCollection<ParagraphResult> cells = CellParagraphs;
                }
                return _hasTextContent;
            }
        }
 
        #endregion Internal Properties
 
        //-------------------------------------------------------------------
        //
        //  Private Fields
        //
        //-------------------------------------------------------------------
 
        #region Private Fields
 
        /// <summary>
        /// The collection of ParagraphResults for for nested paragraphs.
        /// </summary>
        private ReadOnlyCollection<ParagraphResult> _cells;
 
        /// <summary>
        /// Index of this row paragraph in tableparaclient's row array.
        /// </summary>
        int _index;
 
        #endregion Private Fields
 
    }
 
    /// <summary>
    /// The ParagraphResult for a subpage paragraph.
    /// </summary>
    internal sealed class SubpageParagraphResult : ParagraphResult
    {
        //-------------------------------------------------------------------
        //
        //  Constructors
        //
        //-------------------------------------------------------------------
 
        #region Constructors
 
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="paraClient">Object representing a paragraph.</param>
        internal SubpageParagraphResult(BaseParaClient paraClient)
            : base(paraClient)
        {
        }
 
        #endregion Constructors
 
        //-------------------------------------------------------------------
        //
        //  Internal Properties
        //
        //-------------------------------------------------------------------
 
        #region Internal Properties
 
        /// <summary>
        /// Collection of ColumnResults for each line in the paragraph.
        /// </summary>
        internal ReadOnlyCollection<ColumnResult> Columns
        {
            get
            {
                if (_columns == null)
                {
                    // Check subpage columns for text content
                    _columns = ((SubpageParaClient)_paraClient).GetColumnResults(out _hasTextContent);
                    Invariant.Assert(_columns != null, "Columns collection is null");
                }
                return _columns;
            }
        }
 
        /// <summary>
        /// True if subpage has text content, i.e. if any of its columns has text content. Checking columns for text content will
        /// recursively check each column's paragraph collections
        /// </summary>
        internal override bool HasTextContent
        {
            get
            {
                if (_columns == null)
                {
                    ReadOnlyCollection<ColumnResult> columns = Columns;
                }
                return _hasTextContent;
            }
        }
 
        /// <summary>
        /// Collection of ParagraphResults for floating elements
        /// </summary>
        internal ReadOnlyCollection<ParagraphResult> FloatingElements
        {
            get
            {
                if (_floatingElements == null)
                {
                    _floatingElements = ((SubpageParaClient)_paraClient).FloatingElementResults;
                    Invariant.Assert(_floatingElements != null, "Floating elements collection is null");
                }
                return _floatingElements;
            }
        }
 
        /// <summary>
        /// Offset for contained content - New PTS coordinate system (0, 0)
        /// </summary>
        internal Vector ContentOffset 
        { 
            get 
            { 
                MbpInfo mbp = MbpInfo.FromElement(_paraClient.Paragraph.Element, _paraClient.Paragraph.StructuralCache.TextFormatterHost.PixelsPerDip);
                return new Vector(LayoutBox.X + TextDpi.FromTextDpi(mbp.BPLeft), LayoutBox.Y + TextDpi.FromTextDpi(mbp.BPTop));
            }
        } 
 
        #endregion Internal Properties
 
        //-------------------------------------------------------------------
        //
        //  Private Fields
        //
        //-------------------------------------------------------------------
 
        #region Private Fields
 
        /// <summary>
        /// The collection of ParagraphResults for for nested paragraphs.
        /// </summary>
        private ReadOnlyCollection<ColumnResult> _columns;
 
        /// <summary>
        /// The collection of ParagraphResults for floating elements.
        /// </summary>
        private ReadOnlyCollection<ParagraphResult> _floatingElements;
 
        #endregion Private Fields
    }
 
    /// <summary>
    /// The ParagraphResult for a figure paragraph.
    /// </summary>
    internal sealed class FigureParagraphResult : ParagraphResult
    {
        //-------------------------------------------------------------------
        //
        //  Constructors
        //
        //-------------------------------------------------------------------
 
        #region Constructors
 
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="paraClient">Object representing a paragraph.</param>
        internal FigureParagraphResult(BaseParaClient paraClient)
            : base(paraClient)
        {
        }
 
        #endregion Constructors
 
        //-------------------------------------------------------------------
        //
        //  Internal Properties
        //
        //-------------------------------------------------------------------
 
        #region Internal Properties
 
        /// <summary>
        /// Collection of ColumnResults for each line in the paragraph.
        /// </summary>
        internal ReadOnlyCollection<ColumnResult> Columns
        {
            get
            {
                if (_columns == null)
                {
                    // Check figure's columns for text content
                    _columns = ((FigureParaClient)_paraClient).GetColumnResults(out _hasTextContent);
                    Invariant.Assert(_columns != null, "Columns collection is null");
                }
                return _columns;
            }
        }
 
        /// <summary>
        /// True if the figure has text content, i.e. if its columns collection has text content.
        /// </summary>
        internal override bool HasTextContent
        {
            get
            {
                if (_columns == null)
                {
                    ReadOnlyCollection<ColumnResult> columns = Columns;
                }
                return _hasTextContent;
            }
        }
 
        /// <summary>
        /// Collection of ParagraphResults for floating elements
        /// </summary>
        internal ReadOnlyCollection<ParagraphResult> FloatingElements
        {
            get
            {
                if (_floatingElements == null)
                {
                    _floatingElements = ((FigureParaClient)_paraClient).FloatingElementResults;
                    Invariant.Assert(_floatingElements != null, "Floating elements collection is null");
                }
                return _floatingElements;
            }
        }
 
        /// <summary>
        /// Offset for contained content - New PTS coordinate system (0, 0)
        /// </summary>
        internal Vector ContentOffset
        {
            get
            {
                MbpInfo mbp = MbpInfo.FromElement(_paraClient.Paragraph.Element, _paraClient.Paragraph.StructuralCache.TextFormatterHost.PixelsPerDip);
                return new Vector(LayoutBox.X + TextDpi.FromTextDpi(mbp.BPLeft), LayoutBox.Y + TextDpi.FromTextDpi(mbp.BPTop));
            }
        } 
 
        #endregion Internal Properties
 
        //-------------------------------------------------------------------
        //
        //  Internal Methods
        //
        //-------------------------------------------------------------------
 
        #region Internal Methods
 
        /// <summary>
        /// Returns tight bounding geometry for the given text range.
        /// </summary>
        /// <param name="startPosition">Start position of the range.</param>
        /// <param name="endPosition">End position of the range.</param>
        /// <param name="visibleRect">Visible clipping area</param>
        /// <param name="success"> True if range starts in this [ara</param>
        /// <returns>Geometry object containing tight bound.</returns>
        internal Geometry GetTightBoundingGeometryFromTextPositions(ITextPointer startPosition, ITextPointer endPosition, Rect visibleRect, out bool success)
        {
            success = false;
            if (this.Contains(startPosition, true))
            {
                // Selection starts inside this floater and must end inside it
                success = true;
                ITextPointer endPositionInThisPara = endPosition.CompareTo(this.EndPosition) < 0 ? endPosition : this.EndPosition;
                // Pass paragraph results and floating elements to para client so it doesn't have to generate them
                return (((FigureParaClient)_paraClient).GetTightBoundingGeometryFromTextPositions(Columns, FloatingElements, startPosition, endPositionInThisPara, visibleRect));
            }
            return null;
        }
 
        #endregion Internal Methods
 
        //-------------------------------------------------------------------
        //
        //  Private Fields
        //
        //-------------------------------------------------------------------
 
        #region Private Fields
 
        /// <summary>
        /// The collection of ColumnResults for nested columns.
        /// </summary>
        private ReadOnlyCollection<ColumnResult> _columns;
 
        /// <summary>
        /// The collection of ParagraphResults for floating elements.
        /// </summary>
        private ReadOnlyCollection<ParagraphResult> _floatingElements;
 
 
        #endregion Private Fields
    }
 
    /// <summary>
    /// The ParagraphResult for a floater base paragraph.
    /// </summary>
    internal abstract class FloaterBaseParagraphResult : ParagraphResult
    {
        //-------------------------------------------------------------------
        //
        //  Constructors
        //
        //-------------------------------------------------------------------
 
        #region Constructors
 
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="paraClient">Object representing a paragraph.</param>
        internal FloaterBaseParagraphResult(BaseParaClient paraClient)
            : base(paraClient)
        {
        }
 
        #endregion Constructors
    }
 
    /// <summary>
    /// The ParagraphResult for a floater paragraph.
    /// </summary>
    internal sealed class FloaterParagraphResult : FloaterBaseParagraphResult
    {
        //-------------------------------------------------------------------
        //
        //  Constructors
        //
        //-------------------------------------------------------------------
 
        #region Constructors
 
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="paraClient">Object representing a paragraph.</param>
        internal FloaterParagraphResult(BaseParaClient paraClient)
            : base(paraClient)
        {
        }
 
        #endregion Constructors
 
        //-------------------------------------------------------------------
        //
        //  Internal Properties
        //
        //-------------------------------------------------------------------
 
        #region Internal Properties
 
        /// <summary>
        /// Collection of ColumnResults for each line in the paragraph.
        /// </summary>
        internal ReadOnlyCollection<ColumnResult> Columns
        {
            get
            {
                if (_columns == null)
                {
                    // Query floater's columns for text content
                    _columns = ((FloaterParaClient)_paraClient).GetColumnResults(out _hasTextContent);
                    Invariant.Assert(_columns != null, "Columns collection is null");
                }
                return _columns;
            }
        }
 
        /// <summary>
        /// True if the floater has text content, i.e. if its columns collection has text content.
        /// </summary>
        internal override bool HasTextContent
        {
            get
            {
                if (_columns == null)
                {
                    ReadOnlyCollection<ColumnResult> columns = Columns;
                }
                return _hasTextContent;
            }
        }
 
        /// <summary>
        /// Collection of ParagraphResults for floating elements
        /// </summary>
        internal ReadOnlyCollection<ParagraphResult> FloatingElements
        {
            get
            {
                if (_floatingElements == null)
                {
                    _floatingElements = ((FloaterParaClient)_paraClient).FloatingElementResults;
                    Invariant.Assert(_floatingElements != null, "Floating elements collection is null");
                }
                return _floatingElements;
            }
        }
 
        /// <summary>
        /// Offset for contained content - New PTS coordinate system (0, 0)
        /// </summary>
        internal Vector ContentOffset
        {
            get
            {
                MbpInfo mbp = MbpInfo.FromElement(_paraClient.Paragraph.Element, _paraClient.Paragraph.StructuralCache.TextFormatterHost.PixelsPerDip);
                return new Vector(LayoutBox.X + TextDpi.FromTextDpi(mbp.BPLeft), LayoutBox.Y + TextDpi.FromTextDpi(mbp.BPTop));
            }
        }
 
        #endregion Internal Properties
 
        //-------------------------------------------------------------------
        //
        //  Internal Methods
        //
        //-------------------------------------------------------------------
 
        #region Internal Methods
 
        /// <summary>
        /// Returns tight bounding geometry for the given text range.
        /// </summary>
        /// <param name="startPosition">Start position of the range.</param>
        /// <param name="endPosition">End position of the range.</param>
        /// <param name="visibleRect">Visible clipping area</param>
        /// <param name="success">True if the range starts in this floater paragraph</param>
        /// <returns>Geometry object containing tight bound.</returns>
        internal Geometry GetTightBoundingGeometryFromTextPositions(ITextPointer startPosition, ITextPointer endPosition, Rect visibleRect, out bool success)
        {
            success = false;
            if (this.Contains(startPosition, true))
            {
                // Selection starts inside this floater and must end inside it
                success = true;
                ITextPointer endPositionInThisPara = endPosition.CompareTo(this.EndPosition) < 0 ? endPosition : this.EndPosition;
                return (((FloaterParaClient)_paraClient).GetTightBoundingGeometryFromTextPositions(Columns, FloatingElements, startPosition, endPositionInThisPara, visibleRect));
            }
            return null;
        }
 
        #endregion Internal Methods
 
 
        //-------------------------------------------------------------------
        //
        //  Private Fields
        //
        //-------------------------------------------------------------------
 
        #region Private Fields
 
        /// <summary>
        /// The collection of ColumnResults for nested columns.
        /// </summary>
        private ReadOnlyCollection<ColumnResult> _columns;
 
        /// <summary>
        /// The collection of ParagraphResults for floating elements.
        /// </summary>
        private ReadOnlyCollection<ParagraphResult> _floatingElements;
 
        #endregion Private Fields
    }
 
    /// <summary>
    /// The ParagraphResult for a UIElement paragraph.
    /// </summary>
    internal sealed class UIElementParagraphResult : FloaterBaseParagraphResult
    {
        //-------------------------------------------------------------------
        //
        //  Constructors
        //
        //-------------------------------------------------------------------
 
        #region Constructors
 
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="paraClient">Object representing a paragraph.</param>
        internal UIElementParagraphResult(BaseParaClient paraClient)
            : base(paraClient)
        {
        }
 
        #endregion Constructors
 
        //-------------------------------------------------------------------
        //
        //  Internal Properties
        //
        //-------------------------------------------------------------------
 
        #region Internal Properties
 
        /// <summary>
        /// True if the BUIC has text content
        /// </summary>
        internal override bool HasTextContent
        {
            get
            {
                // We always treat BlockUIContainer as a 'line' from ContentStart to ContentEnd. So HasTextContent is always true
                return true;
            }
        }
 
        /// <summary>
        /// Returns tight bounding geometry for the given text range.
        /// </summary>
        internal Geometry GetTightBoundingGeometryFromTextPositions(ITextPointer startPosition, ITextPointer endPosition)
        {
            return (((UIElementParaClient)_paraClient).GetTightBoundingGeometryFromTextPositions(startPosition, endPosition));
        }
 
 
        #endregion Internal Properties
    }
}