PtsHelper.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / Framework / MS / Internal / PtsHost / PtsHelper.cs / 1305600 / PtsHelper.cs

                            //---------------------------------------------------------------------------- 
//
// Copyright (C) Microsoft Corporation.  All rights reserved.
//
// File: PtsHelper.cs 
//
// Description: Helper services to query PTS objects. 
// 
// History:
//  05/05/2003 : [....] - moving from Avalon branch. 
//
//---------------------------------------------------------------------------

using System; 
using System.Collections;
using System.Collections.Generic; 
using System.Diagnostics; 
using System.Security;
using System.Windows; 
using System.Windows.Media;
using System.Windows.Documents;
using MS.Internal.Text;
using MS.Internal.Documents; 

using MS.Internal.PtsHost.UnsafeNativeMethods; 
 
namespace MS.Internal.PtsHost
{ 
    // ---------------------------------------------------------------------
    // Helper services to query PTS objects.
    // ---------------------------------------------------------------------
    internal static class PtsHelper 
    {
        // ------------------------------------------------------------------ 
        // 
        // Visual Helpers
        // 
        // -----------------------------------------------------------------

        #region Visual Helpers
 
        // ------------------------------------------------------------------
        // Update mirroring transform. 
        // ------------------------------------------------------------------ 
        internal static void UpdateMirroringTransform(FlowDirection parentFD, FlowDirection childFD, ContainerVisual visualChild, double width)
        { 
            // Set mirroring transform if necessary, or clear it just in case it was set in the previous
            // format process.
            if (parentFD != childFD)
            { 
                MatrixTransform transform = new MatrixTransform(-1.0, 0.0, 0.0, 1.0, width, 0.0);
                visualChild.Transform = transform; 
                visualChild.SetValue(FrameworkElement.FlowDirectionProperty, childFD); 
            }
            else 
            {
                visualChild.Transform = null;
                visualChild.ClearValue(FrameworkElement.FlowDirectionProperty);
            } 
        }
 
        // ----------------------------------------------------------------- 
        // Clips visual children to a specified rect
        // ------------------------------------------------------------------ 
        internal static void ClipChildrenToRect(ContainerVisual visual, Rect rect)
        {
            VisualCollection visualChildren = visual.Children;
 
            for(int index = 0; index < visualChildren.Count; index++)
            { 
                ((ContainerVisual)visualChildren[index]).Clip = new RectangleGeometry(rect); 
            }
        } 

        // -----------------------------------------------------------------
        // Syncs a floating element para list with a visual collection
        // ----------------------------------------------------------------- 
        internal static void UpdateFloatingElementVisuals(ContainerVisual visual, List floatingElementList)
        { 
            VisualCollection visualChildren = visual.Children; 
            int visualIndex = 0;
 
            if(floatingElementList == null || floatingElementList.Count == 0)
            {
                visualChildren.Clear();
            } 
            else
            { 
                for(int index = 0; index < floatingElementList.Count; index++) 
                {
                    Visual paraVisual = floatingElementList[index].Visual; 

                    while(visualIndex < visualChildren.Count && visualChildren[visualIndex] != paraVisual)
                    {
                        visualChildren.RemoveAt(visualIndex); 
                    }
 
                    if(visualIndex == visualChildren.Count) 
                    {
                        visualChildren.Add(paraVisual); 
                    }

                    visualIndex++;
                } 

 
                if(visualChildren.Count > floatingElementList.Count) 
                {
                    visualChildren.RemoveRange(floatingElementList.Count, visualChildren.Count - floatingElementList.Count); 
                }
            }
        }
 
        #endregion Visual Helpers
 
        // ----------------------------------------------------------------- 
        //
        // Arrange Helpers 
        //
        // ------------------------------------------------------------------

        #region Arrange Helpers 

        //------------------------------------------------------------------- 
        // Arrange PTS track. 
        //--------------------------------------------------------------------
        ///  
        /// Critical:
        ///     a) calls Critical function PTS.FsQueryTrackDetails.
        ///     b) calls Critical functions: ParaListFromTrack and ArrangeParaList.
        ///  
        [SecurityCritical]
        internal static void ArrangeTrack( 
            PtsContext ptsContext, 
            ref PTS.FSTRACKDESCRIPTION trackDesc,
            uint fswdirTrack) 
        {
            // There is possibility to get empty track. (example: large figures)
            if (trackDesc.pfstrack != IntPtr.Zero)
            { 
                // Get track details
                PTS.FSTRACKDETAILS trackDetails; 
                PTS.Validate(PTS.FsQueryTrackDetails(ptsContext.Context, trackDesc.pfstrack, out trackDetails)); 

                // There is possibility to get empty track. 
                if (trackDetails.cParas != 0)
                {
                    // Get list of paragraphs
                    PTS.FSPARADESCRIPTION[] arrayParaDesc; 
                    ParaListFromTrack(ptsContext, trackDesc.pfstrack, ref trackDetails, out arrayParaDesc);
 
                    // Arrange paragraphs 
                    ArrangeParaList(ptsContext, trackDesc.fsrc, arrayParaDesc, fswdirTrack);
                } 
            }
        }

        // ------------------------------------------------------------------ 
        // Arrange para list.
        // ----------------------------------------------------------------- 
        ///  
        /// Critical, because:
        ///     a) calls Critical function BaseParaClient.Arrange. 
        ///     b) calls Critical function PTS.FsTransformRectangle
        /// 
        [SecurityCritical]
        internal static void ArrangeParaList( 
            PtsContext ptsContext,
            PTS.FSRECT rcTrackContent, 
            PTS.FSPARADESCRIPTION [] arrayParaDesc, 
            uint fswdirTrack)
        { 
            // For each paragraph, do following:
            // (1) Retrieve ParaClient object
            // (2) Arrange and update paragraph metrics
            int dvrPara = 0; 
            for (int index = 0; index < arrayParaDesc.Length; index++)
            { 
                // (1) Retrieve ParaClient object 
                BaseParaClient paraClient = ptsContext.HandleToObject(arrayParaDesc[index].pfsparaclient) as BaseParaClient;
                PTS.ValidateHandle(paraClient); 

                // Convert to appropriate page coordinates.
                if(index == 0)
                { 
                    uint fswdirPage = PTS.FlowDirectionToFswdir(paraClient.PageFlowDirection);
 
                    if(fswdirTrack != fswdirPage) 
                    {
                        PTS.FSRECT pageRect = paraClient.Paragraph.StructuralCache.CurrentArrangeContext.PageContext.PageRect; 
                        PTS.Validate(PTS.FsTransformRectangle(fswdirTrack, ref pageRect, ref rcTrackContent, fswdirPage, out rcTrackContent));
                    }
                }
 
                // (2) Arrange and update paragraph metrics
                int dvrTopSpace = arrayParaDesc[index].dvrTopSpace; 
                PTS.FSRECT rcPara = rcTrackContent; 
                rcPara.v += dvrPara + dvrTopSpace;
                rcPara.dv = arrayParaDesc[index].dvrUsed - dvrTopSpace; 

                paraClient.Arrange(arrayParaDesc[index].pfspara, rcPara, dvrTopSpace, fswdirTrack);
                dvrPara += arrayParaDesc[index].dvrUsed;
            } 
        }
 
        #endregion Arrange Helpers 

        // ------------------------------------------------------------------ 
        //
        // Update Visual Helpers
        //
        // ----------------------------------------------------------------- 

        #region Update Visual Helpers 
 
        //-------------------------------------------------------------------
        // Update PTS track (column) visuals. 
        //-------------------------------------------------------------------
        /// 
        /// Critical:
        ///     a) calls Critical function PTS.FsQueryTrackDetails. 
        ///     b) calls Critical function ParaListFromTrack.
        ///  
        [SecurityCritical] 
        internal static void UpdateTrackVisuals(
            PtsContext ptsContext, 
            VisualCollection visualCollection,
            PTS.FSKUPDATE fskupdInherited,
            ref PTS.FSTRACKDESCRIPTION trackDesc)
        { 
            PTS.FSKUPDATE fskupd = trackDesc.fsupdinf.fskupd;
            if (trackDesc.fsupdinf.fskupd == PTS.FSKUPDATE.fskupdInherited) 
            { 
                fskupd = fskupdInherited;
            } 

            // If there is no change, visual information is valid
            if (fskupd == PTS.FSKUPDATE.fskupdNoChange) { return; }
            ErrorHandler.Assert(fskupd != PTS.FSKUPDATE.fskupdShifted, ErrorHandler.UpdateShiftedNotValid); 

            bool emptyTrack = (trackDesc.pfstrack == IntPtr.Zero); 
            if (!emptyTrack) 
            {
                // Get track details 
                PTS.FSTRACKDETAILS trackDetails;
                PTS.Validate(PTS.FsQueryTrackDetails(ptsContext.Context, trackDesc.pfstrack, out trackDetails));

                emptyTrack = (trackDetails.cParas == 0); 
                if (!emptyTrack)
                { 
                    // Get list of paragraphs 
                    PTS.FSPARADESCRIPTION[] arrayParaDesc;
                    ParaListFromTrack(ptsContext, trackDesc.pfstrack, ref trackDetails, out arrayParaDesc); 

                    // Update visuals for list of paragraphs
                    UpdateParaListVisuals(ptsContext, visualCollection, fskupd, arrayParaDesc);
                } 
            }
 
            // There is possibility to get empty track. (example: large figures) 
            if (emptyTrack)
            { 
                // There is no content, remove all existing children visuals.
                visualCollection.Clear();
            }
        } 

        // ------------------------------------------------------------------ 
        // Update visuals for list of paragraphs. 
        // -----------------------------------------------------------------
        internal static void UpdateParaListVisuals( 
            PtsContext ptsContext,
            VisualCollection visualCollection,
            PTS.FSKUPDATE fskupdInherited,
            PTS.FSPARADESCRIPTION [] arrayParaDesc) 
        {
            // For each paragraph, do following: 
            // (1) Retrieve ParaClient object 
            // (3) Update visual, if necessary
            for (int index = 0; index < arrayParaDesc.Length; index++) 
            {
                // (1) Retrieve ParaClient object
                BaseParaClient paraClient = ptsContext.HandleToObject(arrayParaDesc[index].pfsparaclient) as BaseParaClient;
                PTS.ValidateHandle(paraClient); 

                // (2) Update visual, if necessary 
                PTS.FSKUPDATE fskupd = arrayParaDesc[index].fsupdinf.fskupd; 
                if (fskupd == PTS.FSKUPDATE.fskupdInherited)
                { 
                    fskupd = fskupdInherited;
                }
                if (fskupd == PTS.FSKUPDATE.fskupdNew)
                { 
                    // Disconnect visual from its old parent, if necessary.
                    Visual currentParent = VisualTreeHelper.GetParent(paraClient.Visual) as Visual; 
                    if(currentParent != null) 
                    {
                        ContainerVisual parent = currentParent as ContainerVisual; 
                        Invariant.Assert(parent != null, "parent should always derives from ContainerVisual");
                        parent.Children.Remove(paraClient.Visual);
                    }
 
                    // New paragraph - insert new visual node
                    visualCollection.Insert(index, paraClient.Visual); 
 
                    paraClient.ValidateVisual(fskupd);
                } 
                else
                {
                    // Remove visuals for non-existing paragraphs
                    while (visualCollection[index] != paraClient.Visual) 
                    {
                        visualCollection.RemoveAt(index); 
                        Invariant.Assert(index < visualCollection.Count); 
                    }
 
                    if(fskupd == PTS.FSKUPDATE.fskupdChangeInside || fskupd == PTS.FSKUPDATE.fskupdShifted)
                    {
                        paraClient.ValidateVisual(fskupd);
                    } 
                }
            } 
            // Remove obsolete visuals 
            if (arrayParaDesc.Length < visualCollection.Count)
            { 
                visualCollection.RemoveRange(arrayParaDesc.Length, visualCollection.Count - arrayParaDesc.Length);
            }
        }
 
        #endregion Update Visual Helpers
 
 
        #region Update Viewport Helpers
 
        //--------------------------------------------------------------------
        // Update viewport for track
        //--------------------------------------------------------------------
        ///  
        /// Critical:
        ///     a) calls Critical function PTS.FsQueryTrackDetails. 
        ///     b) calls Critical function ParaListFromTrack. 
        /// 
        [SecurityCritical] 
        internal static void UpdateViewportTrack(
            PtsContext ptsContext,
            ref PTS.FSTRACKDESCRIPTION trackDesc,
            ref PTS.FSRECT viewport) 
        {
            // There is possibility to get empty track. (example: large figures) 
            if (trackDesc.pfstrack != IntPtr.Zero) 
            {
                // Get track details 
                PTS.FSTRACKDETAILS trackDetails;
                PTS.Validate(PTS.FsQueryTrackDetails(ptsContext.Context, trackDesc.pfstrack, out trackDetails));

                // There is possibility to get empty track. 
                if (trackDetails.cParas != 0)
                { 
                    // Get list of paragraphs 
                    PTS.FSPARADESCRIPTION[] arrayParaDesc;
                    ParaListFromTrack(ptsContext, trackDesc.pfstrack, ref trackDetails, out arrayParaDesc); 

                    // Arrange paragraphs
                    UpdateViewportParaList(ptsContext, arrayParaDesc, ref viewport);
                } 
            }
        } 
 
        // -----------------------------------------------------------------
        // Update viewport for para list 
        // ------------------------------------------------------------------
        internal static void UpdateViewportParaList(
            PtsContext ptsContext,
            PTS.FSPARADESCRIPTION [] arrayParaDesc, 
            ref PTS.FSRECT viewport)
        { 
            for (int index = 0; index < arrayParaDesc.Length; index++) 
            {
                // (1) Retrieve ParaClient object 
                BaseParaClient paraClient = ptsContext.HandleToObject(arrayParaDesc[index].pfsparaclient) as BaseParaClient;
                PTS.ValidateHandle(paraClient);

                paraClient.UpdateViewport(ref viewport); 
            }
        } 
 
        #endregion Arrange Helpers
 
        // -----------------------------------------------------------------
        //
        // HitTest Helpers
        // 
        // -----------------------------------------------------------------
 
        #region HitTest Helpers 

        // ----------------------------------------------------------------- 
        // Hit tests to the correct IInputElement within the track that the
        // mouse is over.
        // ------------------------------------------------------------------
        ///  
        /// Critical:
        ///     a) calls Critical function PTS.FsQueryTrackDetails. 
        ///     b) calls Critical function ParaListFromTrack. 
        /// 
        [SecurityCritical] 
        internal static IInputElement InputHitTestTrack(
            PtsContext ptsContext,
            PTS.FSPOINT pt,
            ref PTS.FSTRACKDESCRIPTION trackDesc) 
        {
            // There is possibility to get empty track. (example: large figures) 
            if (trackDesc.pfstrack == IntPtr.Zero) { return null; } 

            IInputElement ie = null; 

            // Get track details
            PTS.FSTRACKDETAILS trackDetails;
            PTS.Validate(PTS.FsQueryTrackDetails(ptsContext.Context, trackDesc.pfstrack, out trackDetails)); 

            // There might be possibility to get empty track, skip the track 
            // in such case. 
            if (trackDetails.cParas != 0)
            { 
                // Get list of paragraphs
                PTS.FSPARADESCRIPTION[] arrayParaDesc;
                ParaListFromTrack(ptsContext, trackDesc.pfstrack, ref trackDetails, out arrayParaDesc);
 
                // Hittest list of paragraphs
                ie = InputHitTestParaList(ptsContext, pt, ref trackDesc.fsrc, arrayParaDesc); 
            } 

            return ie; 
        }

        // -----------------------------------------------------------------
        // Hit tests to the correct IInputElement within the list of 
        // paragraphs that the mouse is over.
        // ------------------------------------------------------------------ 
        internal static IInputElement InputHitTestParaList( 
            PtsContext ptsContext,
            PTS.FSPOINT pt, 
            ref PTS.FSRECT rcTrack,                     // track's rectangle
            PTS.FSPARADESCRIPTION [] arrayParaDesc)
        {
            IInputElement ie = null; 

            for (int index = 0; index < arrayParaDesc.Length && ie == null; index++) 
            { 
                BaseParaClient paraClient = ptsContext.HandleToObject(arrayParaDesc[index].pfsparaclient) as BaseParaClient;
                PTS.ValidateHandle(paraClient); 

                if(paraClient.Rect.Contains(pt))
                {
                    ie = paraClient.InputHitTest(pt); 
                }
            } 
            return ie; 
        }
 
        #endregion HitTest Helpers

        // ------------------------------------------------------------------
        // 
        // GetRectangles Helpers
        // 
        // ----------------------------------------------------------------- 

        #region GetRectangles Helpers 

        // ------------------------------------------------------------------
        // Returns ArrayList of rectangles for the ContentElement e within
        // the specified track. If e is not found or if track contains nothing, 
        // returns empty list
        // 
        //      start: int representing start offset of e 
        //      length: int representing number of positions occupied by e
        // ----------------------------------------------------------------- 
        /// 
        /// Critical:
        ///     a) calls Critical function PTS.FsQueryTrackDetails.
        ///     b) calls Critical function ParaListFromTrack. 
        /// 
        [SecurityCritical] 
        internal static List GetRectanglesInTrack( 
            PtsContext ptsContext,
            ContentElement e, 
            int start,
            int length,
            ref PTS.FSTRACKDESCRIPTION trackDesc)
        { 
            List rectangles = new List();
            // There is possibility to get empty track. (example: large figures) 
            if (trackDesc.pfstrack == IntPtr.Zero) 
            {
                // TRack is empty. Return empty list. 
                return rectangles;
            }

            // Get track details 
            PTS.FSTRACKDETAILS trackDetails;
            PTS.Validate(PTS.FsQueryTrackDetails(ptsContext.Context, trackDesc.pfstrack, out trackDetails)); 
 
            // There might be possibility to get empty track, skip the track
            // in such case. 
            if (trackDetails.cParas != 0)
            {
                // Get list of paragraphs
                PTS.FSPARADESCRIPTION[] arrayParaDesc; 
                ParaListFromTrack(ptsContext, trackDesc.pfstrack, ref trackDetails, out arrayParaDesc);
 
                // Check list of paragraphs for element 
                rectangles = GetRectanglesInParaList(ptsContext, e, start, length, arrayParaDesc);
            } 
            return rectangles;
        }

        // ----------------------------------------------------------------- 
        // Returns ArrayList of rectangles for the ContentElement e within the
        // list of paragraphs. If the element is not found, returns empty list. 
        //      start: int representing start offset of e 
        //      length: int representing number of positions occupied by e
        // ----------------------------------------------------------------- 
        internal static List GetRectanglesInParaList(
            PtsContext ptsContext,
            ContentElement e,
            int start, 
            int length,
            PTS.FSPARADESCRIPTION[] arrayParaDesc) 
        { 
            List rectangles = new List();
 
            for (int index = 0; index < arrayParaDesc.Length; index++)
            {
                BaseParaClient paraClient = ptsContext.HandleToObject(arrayParaDesc[index].pfsparaclient) as BaseParaClient;
                PTS.ValidateHandle(paraClient); 
                if (start < paraClient.Paragraph.ParagraphEndCharacterPosition)
                { 
                    // Element lies within the paraClient boundaries. 
                    rectangles = paraClient.GetRectangles(e, start, length);
 
                    // Rectangles collection should not be null for consistency
                    Invariant.Assert(rectangles != null);
                    if (rectangles.Count != 0)
                    { 
                        // Element cannot span more than one para client in the same track, so we stop
                        // if the element is found and the rectangles are calculated 
                        break; 
                    }
                } 
            }
            return rectangles;
        }
 
        // ------------------------------------------------------------------
        // Returns List of rectangles offset by x/y values. 
        // ----------------------------------------------------------------- 
        internal static List OffsetRectangleList(List rectangleList, double xOffset, double yOffset)
        { 
            List offsetRectangles = new List(rectangleList.Count);

            for(int index = 0; index < rectangleList.Count; index++)
            { 
                Rect rect = rectangleList[index];
 
                rect.X += xOffset; 
                rect.Y += yOffset;
 
                offsetRectangles.Add(rect);
            }

            return offsetRectangles; 
        }
 
        #endregion GetRectangles Helpers 

        // ------------------------------------------------------------------ 
        //
        // Query Helpers
        //
        // ------------------------------------------------------------------ 

        #region Query Helpers 
 
        // -----------------------------------------------------------------
        // Retrieve section list from page. 
        // ------------------------------------------------------------------
        /// 
        /// Critical, because:
        ///     a) calls Critical function PTS.FsQueryPageSectionList, 
        ///     b) it is unsafe method.
        ///  
        [SecurityCritical] 
        internal static unsafe void SectionListFromPage(
            PtsContext ptsContext, 
            IntPtr page,
            ref PTS.FSPAGEDETAILS pageDetails,
            out PTS.FSSECTIONDESCRIPTION [] arraySectionDesc)
        { 
            arraySectionDesc = new PTS.FSSECTIONDESCRIPTION [pageDetails.u.complex.cSections];
            int sectionCount; 
            fixed (PTS.FSSECTIONDESCRIPTION* rgSectionDesc = arraySectionDesc) 
            {
                PTS.Validate(PTS.FsQueryPageSectionList(ptsContext.Context, page, pageDetails.u.complex.cSections, 
                    rgSectionDesc, out sectionCount));
            }
            ErrorHandler.Assert(pageDetails.u.complex.cSections == sectionCount, ErrorHandler.PTSObjectsCountMismatch);
        } 

        // ----------------------------------------------------------------- 
        // Retrieve track (column) list from subpage. 
        // -----------------------------------------------------------------
        /// 
        /// Critical, because:
        ///     a) calls Critical function PTS.FsQuerySubpageBasicColumnList,
        ///     b) it is unsafe method.
        ///  
        [SecurityCritical]
        internal static unsafe void TrackListFromSubpage( 
            PtsContext ptsContext, 
            IntPtr subpage,
            ref PTS.FSSUBPAGEDETAILS subpageDetails, 
            out PTS.FSTRACKDESCRIPTION [] arrayTrackDesc)
        {
            arrayTrackDesc = new PTS.FSTRACKDESCRIPTION [subpageDetails.u.complex.cBasicColumns];
            int trackCount; 
            fixed (PTS.FSTRACKDESCRIPTION* rgTrackDesc = arrayTrackDesc)
            { 
                PTS.Validate(PTS.FsQuerySubpageBasicColumnList(ptsContext.Context, subpage, subpageDetails.u.complex.cBasicColumns, 
                    rgTrackDesc, out trackCount));
            } 
            ErrorHandler.Assert(subpageDetails.u.complex.cBasicColumns == trackCount, ErrorHandler.PTSObjectsCountMismatch);
        }

        // ----------------------------------------------------------------- 
        // Retrieve track (column) list from section.
        // ------------------------------------------------------------------ 
        ///  
        /// Critical, because:
        ///     a) calls Critical function PTS.FsQuerySectionBasicColumnList, 
        ///     b) it is unsafe method.
        /// 
        [SecurityCritical]
        internal static unsafe void TrackListFromSection( 
            PtsContext ptsContext,
            IntPtr section, 
            ref PTS.FSSECTIONDETAILS sectionDetails, 
            out PTS.FSTRACKDESCRIPTION [] arrayTrackDesc)
        { 
            //

            Debug.Assert(sectionDetails.u.withpagenotes.cSegmentDefinedColumnSpanAreas == 0);
            Debug.Assert(sectionDetails.u.withpagenotes.cHeightDefinedColumnSpanAreas == 0); 

            arrayTrackDesc = new PTS.FSTRACKDESCRIPTION[sectionDetails.u.withpagenotes.cBasicColumns]; 
 
            int trackCount;
            fixed (PTS.FSTRACKDESCRIPTION* rgTrackDesc = arrayTrackDesc) 
            {
                PTS.Validate(PTS.FsQuerySectionBasicColumnList(ptsContext.Context, section, sectionDetails.u.withpagenotes.cBasicColumns,
                    rgTrackDesc, out trackCount));
            } 
            ErrorHandler.Assert(sectionDetails.u.withpagenotes.cBasicColumns == trackCount, ErrorHandler.PTSObjectsCountMismatch);
        } 
 
        // -----------------------------------------------------------------
        // Retrieve paragraph list from the track. 
        // ------------------------------------------------------------------
        /// 
        /// Critical, because:
        ///     a) calls Critical function PTS.FsQueryTrackParaList, 
        ///     b) it is unsafe method.
        ///  
        [SecurityCritical] 
        internal static unsafe void ParaListFromTrack(
            PtsContext ptsContext, 
            IntPtr track,
            ref PTS.FSTRACKDETAILS trackDetails,
            out PTS.FSPARADESCRIPTION [] arrayParaDesc)
        { 
            arrayParaDesc = new PTS.FSPARADESCRIPTION [trackDetails.cParas];
            int paraCount; 
            fixed (PTS.FSPARADESCRIPTION* rgParaDesc = arrayParaDesc) 
            {
                PTS.Validate(PTS.FsQueryTrackParaList(ptsContext.Context, track, trackDetails.cParas, 
                    rgParaDesc, out paraCount));
            }
            ErrorHandler.Assert(trackDetails.cParas == paraCount, ErrorHandler.PTSObjectsCountMismatch);
        } 

        // ------------------------------------------------------------------ 
        // Retrieve paragraph list from the track. 
        // -----------------------------------------------------------------
        ///  
        /// Critical, because:
        ///     a) calls Critical function PTS.FsQuerySubtrackParaList,
        ///     b) it is unsafe method.
        ///  
        [SecurityCritical]
        internal static unsafe void ParaListFromSubtrack( 
            PtsContext ptsContext, 
            IntPtr subtrack,
            ref PTS.FSSUBTRACKDETAILS subtrackDetails, 
            out PTS.FSPARADESCRIPTION [] arrayParaDesc)
        {
            arrayParaDesc = new PTS.FSPARADESCRIPTION [subtrackDetails.cParas];
            int paraCount; 
            fixed (PTS.FSPARADESCRIPTION* rgParaDesc = arrayParaDesc)
            { 
                PTS.Validate(PTS.FsQuerySubtrackParaList(ptsContext.Context, subtrack, subtrackDetails.cParas, 
                    rgParaDesc, out paraCount));
            } 
            ErrorHandler.Assert(subtrackDetails.cParas == paraCount, ErrorHandler.PTSObjectsCountMismatch);
        }

        // ------------------------------------------------------------------ 
        // Retrieve simple line list from the full text paragraph.
        // ----------------------------------------------------------------- 
        ///  
        /// Critical, because:
        ///     a) calls Critical function PTS.FsQueryLineListSingle, 
        ///     b) it is unsafe method.
        /// 
        [SecurityCritical]
        internal static unsafe void LineListSimpleFromTextPara( 
            PtsContext ptsContext,
            IntPtr para, 
            ref PTS.FSTEXTDETAILSFULL textDetails, 
            out PTS.FSLINEDESCRIPTIONSINGLE [] arrayLineDesc)
        { 
            arrayLineDesc = new PTS.FSLINEDESCRIPTIONSINGLE [textDetails.cLines];
            int lineCount;
            fixed (PTS.FSLINEDESCRIPTIONSINGLE* rgLineDesc = arrayLineDesc)
            { 
                PTS.Validate(PTS.FsQueryLineListSingle(ptsContext.Context, para, textDetails.cLines,
                    rgLineDesc, out lineCount)); 
            } 
            ErrorHandler.Assert(textDetails.cLines == lineCount, ErrorHandler.PTSObjectsCountMismatch);
        } 

        // -----------------------------------------------------------------
        // Retrieve composite line list from the full text paragraph.
        // ----------------------------------------------------------------- 
        /// 
        /// Critical, because: 
        ///     a) calls Critical function PTS.FsQueryLineListComposite, 
        ///     b) it is unsafe method.
        ///  
        [SecurityCritical]
        internal static unsafe void LineListCompositeFromTextPara(
            PtsContext ptsContext,
            IntPtr para, 
            ref PTS.FSTEXTDETAILSFULL textDetails,
            out PTS.FSLINEDESCRIPTIONCOMPOSITE [] arrayLineDesc) 
        { 
            arrayLineDesc = new PTS.FSLINEDESCRIPTIONCOMPOSITE [textDetails.cLines];
            int lineCount; 
            fixed (PTS.FSLINEDESCRIPTIONCOMPOSITE* rgLineDesc = arrayLineDesc)
            {
                PTS.Validate(PTS.FsQueryLineListComposite(ptsContext.Context, para, textDetails.cLines,
                    rgLineDesc, out lineCount)); 
            }
            ErrorHandler.Assert(textDetails.cLines == lineCount, ErrorHandler.PTSObjectsCountMismatch); 
        } 

        // ------------------------------------------------------------------ 
        // Retrieve line elements list from the composite line.
        // -----------------------------------------------------------------
        /// 
        /// Critical, because: 
        ///     a) calls Critical function PTS.FsQueryLineCompositeElementList,
        ///     b) it is unsafe method. 
        ///  
        [SecurityCritical]
        internal static unsafe void LineElementListFromCompositeLine( 
            PtsContext ptsContext,
            ref PTS.FSLINEDESCRIPTIONCOMPOSITE lineDesc,
            out PTS.FSLINEELEMENT [] arrayLineElement)
        { 
            arrayLineElement = new PTS.FSLINEELEMENT [lineDesc.cElements];
            int lineElementCount; 
            fixed (PTS.FSLINEELEMENT* rgLineElement = arrayLineElement) 
            {
                PTS.Validate(PTS.FsQueryLineCompositeElementList(ptsContext.Context, lineDesc.pline, lineDesc.cElements, 
                    rgLineElement, out lineElementCount));
            }
            ErrorHandler.Assert(lineDesc.cElements == lineElementCount, ErrorHandler.PTSObjectsCountMismatch);
        } 

        // ------------------------------------------------------------------ 
        // Retrieve attached object list from the paragraph. 
        // ------------------------------------------------------------------
        ///  
        /// Critical, because:
        ///     a) calls Critical function PTS.FsQueryAttachedObjectList,
        ///     b) it is unsafe method.
        ///  
        [SecurityCritical]
        internal static unsafe void AttachedObjectListFromParagraph( 
            PtsContext ptsContext, 
            IntPtr para,
            int cAttachedObject, 
            out PTS.FSATTACHEDOBJECTDESCRIPTION [] arrayAttachedObjectDesc)
        {
            arrayAttachedObjectDesc = new PTS.FSATTACHEDOBJECTDESCRIPTION [cAttachedObject];
            int attachedObjectCount; 
            fixed (PTS.FSATTACHEDOBJECTDESCRIPTION* rgAttachedObjectDesc = arrayAttachedObjectDesc)
            { 
                PTS.Validate(PTS.FsQueryAttachedObjectList(ptsContext.Context, para, cAttachedObject, rgAttachedObjectDesc, out attachedObjectCount)); 
            }
            ErrorHandler.Assert(cAttachedObject == attachedObjectCount, ErrorHandler.PTSObjectsCountMismatch); 
        }

        #endregion Query Helpers
 
        // -----------------------------------------------------------------
        // 
        // Misc Helpers 
        //
        // ------------------------------------------------------------------ 

        #region Misc Helpers

        // ----------------------------------------------------------------- 
        // Retrieve TextContentRange from PTS track.
        // ----------------------------------------------------------------- 
        ///  
        /// Critical:
        ///     a) calls Critical function PTS.FsQueryTrackDetails. 
        ///     b) calls Critical function ParaListFromTrack.
        /// 
        [SecurityCritical]
        internal static TextContentRange TextContentRangeFromTrack( 
            PtsContext ptsContext,
            IntPtr pfstrack) 
        { 
            // Get track details
            PTS.FSTRACKDETAILS trackDetails; 
            PTS.Validate(PTS.FsQueryTrackDetails(ptsContext.Context, pfstrack, out trackDetails));

            // Combine ranges from all nested paragraphs.
            TextContentRange textContentRange = new TextContentRange(); 
            if (trackDetails.cParas != 0)
            { 
                PTS.FSPARADESCRIPTION[] arrayParaDesc; 
                PtsHelper.ParaListFromTrack(ptsContext, pfstrack, ref trackDetails, out arrayParaDesc);
 
                // Merge TextContentRanges for all paragraphs
                BaseParaClient paraClient;
                for (int i = 0; i < arrayParaDesc.Length; i++)
                { 
                    paraClient = ptsContext.HandleToObject(arrayParaDesc[i].pfsparaclient) as BaseParaClient;
                    PTS.ValidateHandle(paraClient); 
                    textContentRange.Merge(paraClient.GetTextContentRange()); 
                }
            } 
            return textContentRange;
        }

        // ----------------------------------------------------------------- 
        // Calculates a page margin adjustment to eliminate free space if column width is not flexible
        // ------------------------------------------------------------------ 
        internal static double CalculatePageMarginAdjustment(StructuralCache structuralCache, double pageMarginWidth) 
        {
            double pageMarginAdjustment = 0.0; 

            DependencyObject o = structuralCache.Section.Element;

            if(o is FlowDocument) 
            {
                ColumnPropertiesGroup columnProperties = new ColumnPropertiesGroup(o); 
 
                if(!columnProperties.IsColumnWidthFlexible)
                { 
                    double lineHeight = DynamicPropertyReader.GetLineHeightValue(o);
                    double pageFontSize = (double)structuralCache.PropertyOwner.GetValue(Block.FontSizeProperty);
                    FontFamily pageFontFamily = (FontFamily)structuralCache.PropertyOwner.GetValue(Block.FontFamilyProperty);
 
                    int ccol = PtsHelper.CalculateColumnCount(columnProperties, lineHeight, pageMarginWidth, pageFontSize, pageFontFamily, true);
 
                    double columnWidth; 
                    double freeSpace;
                    double gap; 

                    GetColumnMetrics(columnProperties, pageMarginWidth,
                                     pageFontSize, pageFontFamily, true, ccol,
                                     ref lineHeight, out columnWidth, out freeSpace, out gap); 

                    pageMarginAdjustment = freeSpace; 
                } 
            }
 
            return pageMarginAdjustment;
        }

 
        // -----------------------------------------------------------------
        // Calculate column count based on column properties. 
        // If column width is Auto column count is calculated by assuming 
        // ColumnWidth as 20*FontSize
        // ------------------------------------------------------------------ 
        internal static int CalculateColumnCount(
            ColumnPropertiesGroup columnProperties,
            double lineHeight,
            double pageWidth, 
            double pageFontSize,
            FontFamily pageFontFamily, 
            bool enableColumns) 
        {
            int columns = 1; 

            double gap;
            double rule = columnProperties.ColumnRuleWidth;
 
            if (enableColumns)
            { 
                if (columnProperties.ColumnGapAuto) 
                {
                    gap = 1 * lineHeight; 
                }
                else
                {
                    gap = columnProperties.ColumnGap; 
                }
 
                if (!columnProperties.ColumnWidthAuto) 
                {
                    // Column count is ignored in this case 
                    double column = columnProperties.ColumnWidth;
                    columns = (int)((pageWidth + gap) / (column + gap));
                }
                else 
                {
                    // Column width is assumed to be 20*FontSize 
                    double column = 20 * pageFontSize; 
                    columns = (int)((pageWidth + gap) / (column + gap));
                } 
            }
            return Math.Max(1, Math.Min(PTS.Restrictions.tscColumnRestriction-1, columns)); // at least 1 column is required
        }
 
        // ------------------------------------------------------------------
        // GetColumnMetrics 
        // ----------------------------------------------------------------- 
        internal static void GetColumnMetrics(ColumnPropertiesGroup columnProperties,
                                              double pageWidth, 
                                              double pageFontSize,
                                              FontFamily pageFontFamily,
                                              bool enableColumns,
                                              int cColumns, 
                                              ref double lineHeight,
                                              out double columnWidth, 
                                              out double freeSpace, 
                                              out double gapSpace)
        { 
            double rule = columnProperties.ColumnRuleWidth;
            if (!enableColumns)
            {
                Invariant.Assert(cColumns == 1); 
                columnWidth = pageWidth;
                gapSpace   = 0; 
                lineHeight = 0; 
                freeSpace  = 0;
            } 
            else
            {
                // For FlowDocument, calculate default column width
                if (columnProperties.ColumnWidthAuto) 
                {
                    columnWidth = 20 * pageFontSize; 
                } 
                else
                { 
                    columnWidth = columnProperties.ColumnWidth;
                }

                if (columnProperties.ColumnGapAuto) 
                {
                    gapSpace = 1 * lineHeight; 
                } 
                else
                { 
                    gapSpace = columnProperties.ColumnGap;
                }
            }
            columnWidth = Math.Max(1, Math.Min(columnWidth, pageWidth)); 
            freeSpace = pageWidth - (cColumns * columnWidth) - (cColumns - 1) * gapSpace;
            freeSpace = Math.Max(0, freeSpace); 
        } 

        // ------------------------------------------------------------------ 
        // Get columns info
        // -----------------------------------------------------------------
        /// 
        /// Critical, because it is unsafe method. 
        /// 
        [SecurityCritical] 
        internal static unsafe void GetColumnsInfo( 
            ColumnPropertiesGroup columnProperties,
            double lineHeight, 
            double pageWidth,
            double pageFontSize,
            FontFamily pageFontFamily,
            int cColumns, 
            PTS.FSCOLUMNINFO* pfscolinfo,
            bool enableColumns) 
        { 
            Debug.Assert(cColumns > 0, "At least one column is required.");
 
            double columnWidth;
            double freeSpace;
            double gap;
 
            double rule = columnProperties.ColumnRuleWidth;
 
            GetColumnMetrics(columnProperties, pageWidth, 
                                  pageFontSize, pageFontFamily, enableColumns, cColumns,
                                  ref lineHeight, out columnWidth, out freeSpace, out gap); 

            // Set columns information
            if (!columnProperties.IsColumnWidthFlexible)
            { 
                // All columns have the declared width
                // ColumnGap is flexible and is increased based on ColumnSpaceDistribution policy 
                // (ColumnGap is effectively min) 
                for (int i = 0; i < cColumns; i++)
                { 
                    // Today there is no way to change the default value of ColumnSpaceDistribution.
                    // If column widths are not flexible, always allocate unused space on the right side.
                    pfscolinfo[i].durBefore = TextDpi.ToTextDpi((i == 0) ? 0 : gap);
                    pfscolinfo[i].durWidth = TextDpi.ToTextDpi(columnWidth); 
                    // ColumnWidth has to be > 0 and SpaceBefore has to be >= 0
                    pfscolinfo[i].durBefore = Math.Max(0, pfscolinfo[i].durBefore); 
                    pfscolinfo[i].durWidth = Math.Max(1, pfscolinfo[i].durWidth); 
                }
            } 
            else
            {
                //  ColumnGap is honored
                //  ColumnWidth is effectively min, and space is distributed according to ColumnSpaceDistribution policy 
                for (int i = 0; i < cColumns; i++)
                { 
                    if (columnProperties.ColumnSpaceDistribution == ColumnSpaceDistribution.Right) 
                    {
                        pfscolinfo[i].durWidth = TextDpi.ToTextDpi((i == cColumns - 1) ? columnWidth + freeSpace : columnWidth); 
                    }
                    else if (columnProperties.ColumnSpaceDistribution == ColumnSpaceDistribution.Left)
                    {
                        pfscolinfo[i].durWidth = TextDpi.ToTextDpi((i == 0) ? columnWidth + freeSpace : columnWidth); 
                    }
                    else 
                    { 
                        pfscolinfo[i].durWidth = TextDpi.ToTextDpi(columnWidth + (freeSpace / cColumns));
                    } 

                    // If calculated column width is greater than the page width, set it to page width to
                    // avoid clipping
                    if (pfscolinfo[i].durWidth > TextDpi.ToTextDpi(pageWidth)) 
                    {
                        pfscolinfo[i].durWidth = TextDpi.ToTextDpi(pageWidth); 
                    } 

                    pfscolinfo[i].durBefore = TextDpi.ToTextDpi((i == 0) ? 0 : gap); 
                    // ColumnWidth has to be > 0 and SpaceBefore has to be >= 0
                    pfscolinfo[i].durBefore = Math.Max(0, pfscolinfo[i].durBefore);
                    pfscolinfo[i].durWidth = Math.Max(1, pfscolinfo[i].durWidth);
                } 
            }
        } 
 
        #endregion Misc Helpers
    } 
}


// 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