|
//---------------------------------------------------------------------------
//
// Copyright (C) Microsoft Corporation. All rights reserved.
//
// File: PtsPage.cs
//
// Description: Wrapper for PTS page.
//
// History:
// 05/05/2003 : Microsoft - moving from Avalon branch.
//
//---------------------------------------------------------------------------
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Security;
using System.Threading;
using System.Windows;
using System.Windows.Media;
using System.Windows.Documents;
using MS.Internal.Text;
using MS.Utility;
using System.Windows.Threading;
using MS.Internal.Documents;
using MS.Internal.PtsHost.UnsafeNativeMethods;
namespace MS.Internal.PtsHost
{
// ----------------------------------------------------------------------
// Wrapper for PTS page object.
// ----------------------------------------------------------------------
internal class PtsPage : IDisposable
{
//-------------------------------------------------------------------
//
// Constructors
//
//-------------------------------------------------------------------
#region Constructors
// ------------------------------------------------------------------
// Constructor.
//
// section - PTS section: root of the NameTable
// ------------------------------------------------------------------
internal PtsPage(Section section) : this()
{
_section = section;
}
// ------------------------------------------------------------------
// Constructor.
// ------------------------------------------------------------------
/// <SecurityNote>
/// Critical - as this calls Critical constructor for SecurityCriticalDataForSet.
/// Safe - as this just initializes it to IntPtr.Zero.
/// </SecurityNote>
[SecurityCritical, SecurityTreatAsSafe]
private PtsPage()
{
_ptsPage = new SecurityCriticalDataForSet<IntPtr>(IntPtr.Zero);
}
// ------------------------------------------------------------------
// Finalizer.
// ------------------------------------------------------------------
~PtsPage()
{
Dispose(false);
}
// ------------------------------------------------------------------
// Dispose unmanaged resources.
// ------------------------------------------------------------------
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
#endregion Constructors
//-------------------------------------------------------------------
//
// Internal Methods
//
//-------------------------------------------------------------------
#region Internal Methods
//-------------------------------------------------------------------
// Prepare for incremental update process. If update is not possible
// make sure that structural cache is in clean state.
//
// Following logic is used for to prepare for reformat/update:
// a) if _ptsPage == NULL, full format + clear NameTable(from DTRs) + clear DTRs
// b) if NameTable == NULL, full format + clear DTRs (covered by a))
// c) if ForceReformat, full format + clear DTRs + clear NameTable
// e) otherwise, update
//
// Allow incremental update if there is a page previously created
// and ForceReformat flag is not set.
// Before formatting needs to make sure that following is done:
// 1) If ForceReformat is true, clear entire NameTable.
// 2) If there is existing DTR and update is not possible,
// invalidate NameTable from the firts position
// stored in DTR list, then clear DTR list.
//
// Returns: 'true' if can do incremental update.
//-------------------------------------------------------------------
internal bool PrepareForBottomlessUpdate()
{
bool canUpdate = !IsEmpty;
if(!_section.CanUpdate)
{
// Main text segment is null for section, no update possible.
canUpdate = false;
}
else if (_section.StructuralCache != null)
{
// No update is possible when ForceReformat flag is set.
// Clear update information and clear entire NameTable.
if (_section.StructuralCache.ForceReformat)
{
canUpdate = false;
_section.StructuralCache.ClearUpdateInfo(true);
}
else if (_section.StructuralCache.DtrList != null)
{
// If there is DRT list and the page cannot be updated,
// invalidate entire NameTable starting from the position
// of the first DTR.
// Then clear update info.
if (!canUpdate)
{
_section.InvalidateStructure();
_section.StructuralCache.ClearUpdateInfo(false);
}
}
// else the NameTable is in a valid state.
}
return canUpdate;
}
//-------------------------------------------------------------------
// Prepare for incremental update process of a finite page. Finite
// page incremental is always done by PTS as full format with change
// delta exposed through queries.
// Allow incremental update if there is a page previously created.
// Before formatting needs to make sure that following is done:
// a) If ForceReformat is true, clear entire NameTable (reformat needs to
// start from the first page).
// b) If there is existing DTR, invalidate NameTable from the firts position
// stored in DTR list, then clear DTR list.
// NOTE: If ForceReformat is false and DTR list is null, it means that
// existing NameTable is in a valid state, and format can be done using
// cached portion of the NameTable.
//
// breakRecord - PageBreakRecord describing start position of the page.
//
// Returns: 'true' if can do incremental update.
//-------------------------------------------------------------------
internal bool PrepareForFiniteUpdate(PageBreakRecord breakRecord)
{
bool canUpdate = !IsEmpty;
#if DEBUG
Debug.Assert(!canUpdate || _section.CanUpdate);
#endif
if (_section.StructuralCache != null)
{
// No update is possible when ForceReformat flag is set.
// Clear update information and clear entire NameTable.
if (_section.StructuralCache.ForceReformat)
{
canUpdate = false;
Debug.Assert(breakRecord == null || !_section.StructuralCache.DestroyStructure, "Cannot format from dirty break record unless StructuralCache.DestroyStructure is not set.");
_section.InvalidateStructure();
// Update structural cache info. The DestroyStructureCache parameter is set to true if
// the name table is not preserved. If the name table is to be preserved, e.g. for highlight
// changed, we do not clear structure cache
_section.StructuralCache.ClearUpdateInfo(/*destroy structure cache:*/ _section.StructuralCache.DestroyStructure);
}
// If there is DRT list, invalidate entire NameTable starting from the
// position of the first DTR.
// Then clear update info, if incremental update is not possible.
else if (_section.StructuralCache.DtrList != null)
{
_section.InvalidateStructure();
if (!canUpdate)
{
_section.StructuralCache.ClearUpdateInfo(false);
}
}
// The NameTable is in a valid state, but cannot do incremental update, because
// there is no DTR stored anymore.
else
{
canUpdate = false;
_section.StructuralCache.ClearUpdateInfo(false);
}
}
return canUpdate;
}
//-------------------------------------------------------------------
// Hit tests to the correct IInputElement within the page that the
// mouse is over.
//
// p - Mouse coordinates relative to the page.
//
// Returns: IInputElement that has been hit.
//-------------------------------------------------------------------
internal IInputElement InputHitTest(Point p)
{
IInputElement ie = null;
if (!IsEmpty)
{
PTS.FSPOINT pt = TextDpi.ToTextPoint(p);
ie = InputHitTestPage(pt);
}
return ie;
}
//-------------------------------------------------------------------
// Returns rectangles for a specified element.
//
// e - ContentElement for which rectangles are to be returned
// start - int representing start offset of e
// length - int representing number of positions occupied by e
//
// Returns: ArrayList of rectangles. If element is not found or if
// there is nothing in this page, returns empty ArrayList
//-------------------------------------------------------------------
internal List<Rect> GetRectangles(ContentElement e, int start, int length)
{
List<Rect> rectangles = new List<Rect>();
if (!IsEmpty)
{
rectangles = GetRectanglesInPage(e, start, length);
}
return rectangles;
}
//-------------------------------------------------------------------
// Callback for background layout / update
//-------------------------------------------------------------------
private static DispatcherOperationCallback BackgroundUpdateCallback = new DispatcherOperationCallback(PtsPage.BackgroundFormatStatic);
//-------------------------------------------------------------------
// Static function, just proxies over to our real function
//-------------------------------------------------------------------
private static object BackgroundFormatStatic(object arg)
{
Invariant.Assert(arg is PtsPage);
((PtsPage)arg).BackgroundFormat();
return null;
}
//-------------------------------------------------------------------
// Does the work of background format - For now, this is simply an invalidate measure call.
// text.
//-------------------------------------------------------------------
private void BackgroundFormat()
{
FlowDocument formattingOwner = _section.StructuralCache.FormattingOwner;
if (formattingOwner.Formatter is FlowDocumentFormatter)
{
_section.StructuralCache.BackgroundFormatInfo.BackgroundFormat(formattingOwner.BottomlessFormatter, false /* ignoreThrottle */);
}
}
//-------------------------------------------------------------------
// Defers remaining text formatting to background - treated as if new text
//-------------------------------------------------------------------
private void DeferFormattingToBackground()
{
int cpLast = _section.StructuralCache.BackgroundFormatInfo.CPInterrupted;
int cpTextContainer = _section.StructuralCache.BackgroundFormatInfo.CchAllText;
DirtyTextRange dtr = new DirtyTextRange(cpLast, cpTextContainer - cpLast, cpTextContainer - cpLast);
_section.StructuralCache.AddDirtyTextRange(dtr);
_backgroundFormatOperation = Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, BackgroundUpdateCallback, this);
}
// ------------------------------------------------------------------
// Create bottomless page.
// ------------------------------------------------------------------
/// <SecurityNote>
/// Critical - as this calls Critical function FsCreatePageBottomless and also
/// calls Critical setter _ptsPage.Value.
/// Safe - as this doesn't take arbitrary parameters that can be set. PtsContext.Context
/// is Critical for set and _section.Handle is a handle to managed object that'll
/// be validated on callbacks.
/// </SecurityNote>
[SecurityCritical, SecurityTreatAsSafe]
internal void CreateBottomlessPage()
{
OnBeforeFormatPage(false, false);
if (TracePageFormatting.IsEnabled)
{
TracePageFormatting.Trace(
TraceEventType.Start,
TracePageFormatting.FormatPage,
PageContext,
PtsContext);
}
PTS.FSFMTRBL formattingResult;
IntPtr ptsPage;
int fserr = PTS.FsCreatePageBottomless(PtsContext.Context, _section.Handle, out formattingResult, out ptsPage);
if (fserr != PTS.fserrNone)
{
// Formatting failed and ptsPage may be set to a partially formatted page. Set value to IntPtr.Zero
_ptsPage.Value = IntPtr.Zero;
PTS.ValidateAndTrace(fserr, PtsContext);
}
else
{
// Formatting succeeded. Set page value
_ptsPage.Value = ptsPage;
}
if (TracePageFormatting.IsEnabled)
{
TracePageFormatting.Trace(
TraceEventType.Stop,
TracePageFormatting.FormatPage,
PageContext,
PtsContext);
}
OnAfterFormatPage(true, false);
if(formattingResult == PTS.FSFMTRBL.fmtrblInterrupted)
{
DeferFormattingToBackground();
}
}
// ------------------------------------------------------------------
// Update bottomless page.
// ------------------------------------------------------------------
/// <SecurityNote>
/// Critical - as this calls Critical function PTS.FsUpdateBottomlessPage.
/// Safe - as this function can't be used to pass arbitrary parameters and all
/// parameters passed in are Critical for set or opaque handles to PTS.
/// </SecurityNote>
[SecurityCritical, SecurityTreatAsSafe]
internal void UpdateBottomlessPage()
{
if (IsEmpty)
{
return;
}
OnBeforeFormatPage(false, true);
if (TracePageFormatting.IsEnabled)
{
TracePageFormatting.Trace(
TraceEventType.Start,
TracePageFormatting.FormatPage,
PageContext,
PtsContext);
}
PTS.FSFMTRBL formattingResult;
int fserr = PTS.FsUpdateBottomlessPage(PtsContext.Context, _ptsPage.Value, _section.Handle, out formattingResult);
if (fserr != PTS.fserrNone)
{
// Do inplace cleanup.
DestroyPage();
// Generic error handling.
PTS.ValidateAndTrace(fserr, PtsContext);
}
if (TracePageFormatting.IsEnabled)
{
TracePageFormatting.Trace(
TraceEventType.Stop,
TracePageFormatting.FormatPage,
PageContext,
PtsContext);
}
OnAfterFormatPage(true, true);
if(formattingResult == PTS.FSFMTRBL.fmtrblInterrupted)
{
DeferFormattingToBackground();
}
}
// ------------------------------------------------------------------
// Create finite page.
// ------------------------------------------------------------------
/// <SecurityNote>
/// Critical, because:
/// a) calls Critical functions PTS.FsCreateFinitePage,
/// b) calls SecurityCriticalDataForSet setter for PTS Page and BreakRecord.
/// Safe, because:
/// a) the parameters passed in are Critical for set
/// b) Critical objects (PTS Page and BreakRecord) are generated within the function.
/// </SecurityNote>
[SecurityCritical, SecurityTreatAsSafe]
internal void CreateFinitePage(PageBreakRecord breakRecord)
{
OnBeforeFormatPage(true, false);
if (TracePageFormatting.IsEnabled)
{
TracePageFormatting.Trace(
TraceEventType.Start,
TracePageFormatting.FormatPage,
PageContext,
PtsContext);
}
// Retrieve PTS break record
IntPtr brIn = (breakRecord != null) ? breakRecord.BreakRecord : IntPtr.Zero;
// Create finite page and update layout size information
PTS.FSFMTR formattingResult;
IntPtr brOut;
IntPtr ptsPage;
int fserr = PTS.FsCreatePageFinite(PtsContext.Context, brIn, _section.Handle, out formattingResult, out ptsPage, out brOut);
if (fserr != PTS.fserrNone)
{
// Formatting failed and ptsPage may be set to a partially formatted page. Set value to IntPtr.Zero
_ptsPage.Value = IntPtr.Zero;
brOut = IntPtr.Zero;
PTS.ValidateAndTrace(fserr, PtsContext);
}
else
{
_ptsPage.Value = ptsPage;
}
if (brOut != IntPtr.Zero)
{
StructuralCache structuralCache = _section.StructuralCache;
if (structuralCache != null)
{
_breakRecord = new PageBreakRecord(PtsContext, new SecurityCriticalDataForSet<IntPtr>(brOut), (breakRecord != null) ? breakRecord.PageNumber + 1 : 1);
}
}
if (TracePageFormatting.IsEnabled)
{
TracePageFormatting.Trace(
TraceEventType.Stop,
TracePageFormatting.FormatPage,
PageContext,
PtsContext);
}
OnAfterFormatPage(true, false);
}
// ------------------------------------------------------------------
// Update finite page.
// ------------------------------------------------------------------
/// <SecurityNote>
/// Critical, because:
/// a) calls Critical functions PTS.FsUpdateFinitePage,
/// b) calls SecurityCriticalDataForSet setter for PTS Page and BreakRecord.
/// Safe, because:
/// a) the parameters passed in are Critical for set
/// b) Critical objects (PTS Page and BreakRecord) are generated within the function.
/// </SecurityNote>
[SecurityCritical, SecurityTreatAsSafe]
internal void UpdateFinitePage(PageBreakRecord breakRecord)
{
if (IsEmpty)
{
return;
}
OnBeforeFormatPage(true, true);
if (TracePageFormatting.IsEnabled)
{
TracePageFormatting.Trace(
TraceEventType.Start,
TracePageFormatting.FormatPage,
PageContext,
PtsContext);
}
// Retrieve PTS break record
IntPtr brIn = (breakRecord != null) ? breakRecord.BreakRecord : IntPtr.Zero;
// Create finite page and update layout size information
PTS.FSFMTR formattingResult;
IntPtr brOut;
int fserr = PTS.FsUpdateFinitePage(PtsContext.Context, _ptsPage.Value, brIn,
_section.Handle, out formattingResult, out brOut);
if (fserr != PTS.fserrNone)
{
// Do inplace cleanup.
DestroyPage();
// Generic error handling.
PTS.ValidateAndTrace(fserr, PtsContext);
}
if (brOut != IntPtr.Zero)
{
StructuralCache structuralCache = _section.StructuralCache;
if (structuralCache != null)
{
_breakRecord = new PageBreakRecord(PtsContext, new SecurityCriticalDataForSet<IntPtr>(brOut), (breakRecord != null) ? breakRecord.PageNumber + 1 : 1);
}
}
if (TracePageFormatting.IsEnabled)
{
TracePageFormatting.Trace(
TraceEventType.Stop,
TracePageFormatting.FormatPage,
PageContext,
PtsContext);
}
OnAfterFormatPage(true, true);
}
//-------------------------------------------------------------------
// Arrange top level PTS page.
//-------------------------------------------------------------------
/// <SecurityNote>
/// Critical - as this calls Critical function PTS.FsQueryPageDetails and some
/// PtsHelper functions.
/// Safe - as this can't be be used to pass arbitrary parameters. All parameters passed
/// in are either Critical for set or are generated within the function.
/// </SecurityNote>
[SecurityCritical, SecurityTreatAsSafe]
internal void ArrangePage()
{
if (IsEmpty)
{
return;
}
_section.UpdateSegmentLastFormatPositions();
// Get page details
PTS.FSPAGEDETAILS pageDetails;
PTS.Validate(PTS.FsQueryPageDetails(PtsContext.Context, _ptsPage.Value, out pageDetails));
// Arrange page content. Page content may be simple or complex -
// depending of set of features used in the content of the page.
// (1) simple page (contains only one track)
// (2) complex page (contains header, page body (list of sections), footnotes and footer)
if (PTS.ToBoolean(pageDetails.fSimple))
{
// (1) simple page (contains only one track)
// Exceptions don't need to pop, as the top level arrange context will be nulled out if thrown.
_section.StructuralCache.CurrentArrangeContext.PushNewPageData(_pageContextOfThisPage, pageDetails.u.simple.trackdescr.fsrc, _finitePage);
PtsHelper.ArrangeTrack(PtsContext, ref pageDetails.u.simple.trackdescr, PTS.FlowDirectionToFswdir(_section.StructuralCache.PageFlowDirection));
_section.StructuralCache.CurrentArrangeContext.PopPageData();
}
else
{
// (2) complex page (contains header, page body (list of sections), footnotes and footer)
// NOTE: only page body (list of sections is currently supported).
//ErrorHandler.Assert(!PTS.ToBoolean(pageDetails.u.complex.fTopBottomHeaderFooter), ErrorHandler.NotSupportedHeadersFooters);
//ErrorHandler.Assert(!PTS.ToBoolean(pageDetails.u.complex.fJustified), ErrorHandler.NotSupportedVerticalJustify);
ErrorHandler.Assert(pageDetails.u.complex.cFootnoteColumns == 0, ErrorHandler.NotSupportedFootnotes);
// cSections == 0, means that page body content is empty.
if (pageDetails.u.complex.cSections != 0)
{
// Retrieve description for each section.
PTS.FSSECTIONDESCRIPTION[] arraySectionDesc;
PtsHelper.SectionListFromPage(PtsContext, _ptsPage.Value, ref pageDetails, out arraySectionDesc);
// Arrange each section
for (int index = 0; index < arraySectionDesc.Length; index++)
{
ArrangeSection(ref arraySectionDesc[index]);
}
}
}
}
//-------------------------------------------------------------------
// Update viewport top-level
//-------------------------------------------------------------------
/// <SecurityNote>
/// Critical - as this calls Critical function PTS.FsQueryPageDetails and some
/// PtsHelper functions.
/// Safe - as this can't be be used to pass arbitrary parameters. All parameters passed
/// in are either Critical for set or are generated within the function.
/// </SecurityNote>
[SecurityCritical, SecurityTreatAsSafe]
internal void UpdateViewport(ref PTS.FSRECT viewport)
{
if (!IsEmpty)
{
// Get page details
PTS.FSPAGEDETAILS pageDetails;
PTS.Validate(PTS.FsQueryPageDetails(PtsContext.Context, _ptsPage.Value, out pageDetails));
// Arrange page content. Page content may be simple or complex -
// depending of set of features used in the content of the page.
// (1) simple page (contains only one track)
// (2) complex page (contains header, page body (list of sections), footnotes and footer)
if (PTS.ToBoolean(pageDetails.fSimple))
{
// (1) simple page (contains only one track)
PtsHelper.UpdateViewportTrack(PtsContext, ref pageDetails.u.simple.trackdescr, ref viewport);
}
else
{
// (2) complex page (contains header, page body (list of sections), footnotes and footer)
// NOTE: only page body (list of sections is currently supported).
//ErrorHandler.Assert(!PTS.ToBoolean(pageDetails.u.complex.fTopBottomHeaderFooter), ErrorHandler.NotSupportedHeadersFooters);
//ErrorHandler.Assert(!PTS.ToBoolean(pageDetails.u.complex.fJustified), ErrorHandler.NotSupportedVerticalJustify);
ErrorHandler.Assert(pageDetails.u.complex.cFootnoteColumns == 0, ErrorHandler.NotSupportedFootnotes);
// cSections == 0, means that page body content is empty.
if (pageDetails.u.complex.cSections != 0)
{
// Retrieve description for each section.
PTS.FSSECTIONDESCRIPTION[] arraySectionDesc;
PtsHelper.SectionListFromPage(PtsContext, _ptsPage.Value, ref pageDetails, out arraySectionDesc);
// Arrange each section
for (int index = 0; index < arraySectionDesc.Length; index++)
{
UpdateViewportSection(ref arraySectionDesc[index], ref viewport);
}
}
}
}
}
// ------------------------------------------------------------------
// Clear update info.
// ------------------------------------------------------------------
/// <SecurityNote>
/// Critical - as this calls Critical function PTS.FsClearUpdateInfoInPage.
/// Safe - as this can't be used to pass arbitrary parameters and the parameters
/// passed in are Critical for set.
/// </SecurityNote>
[SecurityCritical, SecurityTreatAsSafe]
internal void ClearUpdateInfo()
{
if (!IsEmpty)
{
// Clear any incremental update state a----mulated during update process.
PTS.Validate(PTS.FsClearUpdateInfoInPage(PtsContext.Context, _ptsPage.Value), PtsContext);
}
}
// ------------------------------------------------------------------
// Get a visual representing the page's content.
// ------------------------------------------------------------------
internal ContainerVisual GetPageVisual()
{
if (_visual == null)
{
_visual = new ContainerVisual();
}
if (!IsEmpty)
{
UpdatePageVisuals(_calculatedSize);
}
else
{
_visual.Children.Clear();
}
return _visual;
}
#endregion Internal Methods
//-------------------------------------------------------------------
//
// Internal Properties
//
//-------------------------------------------------------------------
#region Internal Properties
//-------------------------------------------------------------------
// BreakRecord indicating break position of the page.
// 'null' if the page is bottomless or it is the last page
//-------------------------------------------------------------------
internal PageBreakRecord BreakRecord { get { return _breakRecord; } }
//-------------------------------------------------------------------
// Calculated size of the page.
//-------------------------------------------------------------------
internal Size CalculatedSize { get { return _calculatedSize; } }
//-------------------------------------------------------------------
// Content size of the page.
//-------------------------------------------------------------------
internal Size ContentSize { get { return _contentSize; } }
//-------------------------------------------------------------------
// Is it finite page or bottomless?
//-------------------------------------------------------------------
internal bool FinitePage { get { return _finitePage; } }
//-------------------------------------------------------------------
// Page context
//-------------------------------------------------------------------
internal PageContext PageContext { get { return _pageContextOfThisPage; } }
//-------------------------------------------------------------------
// Is during incremental update mode?
//-------------------------------------------------------------------
internal bool IncrementalUpdate { get { return _incrementalUpdate; } }
//-------------------------------------------------------------------
// PTS Host.
//-------------------------------------------------------------------
internal PtsContext PtsContext { get { return _section.PtsContext; } }
//-------------------------------------------------------------------
// Handle to PTS page.
//-------------------------------------------------------------------
internal IntPtr PageHandle { get { return _ptsPage.Value; } }
//-------------------------------------------------------------------
// Is being used in a plain text box?
//-------------------------------------------------------------------
internal bool UseSizingWorkaroundForTextBox
{
get { return _useSizingWorkaroundForTextBox; }
set { _useSizingWorkaroundForTextBox = value; }
}
#endregion Internal Properties
// ------------------------------------------------------------------
//
// Private Methods
//
// ------------------------------------------------------------------
#region Private Methods
// ------------------------------------------------------------------
// Dispose unmanaged resources.
// ------------------------------------------------------------------
/// <remarks>
/// Finalizer needs to follow rules below:
/// a) Your Finalize method must tolerate partially constructed instances.
/// b) Your Finalize method must consider the consequence of failure.
/// c) Your object is callable after Finalization.
/// d) Your object is callable during Finalization.
/// e) Your Finalizer could be called multiple times.
/// f) Your Finalizer runs in a delicate security context.
/// See: http://blogs.msdn.com/Microsoft/archive/2004/02/20/77460.aspx
/// </remarks>
/// <SecurityNote>
/// Critical, because sets value of Critical data for set.
/// Safe, because resets the value to IntPtr.Zero.
/// </SecurityNote>
[SecurityCritical, SecurityTreatAsSafe]
private void Dispose(bool disposing)
{
if (Interlocked.CompareExchange(ref _disposed, 1, 0) == 0)
{
// Destroy PTS page.
// According to following article the entire reachable graph from
// a finalizable object is promoted, and it is safe to access its
// members if they do not have their own finalizers.
// Hence it is OK to access _section during finalization.
// See: http://blogs.msdn.com/Microsoft/archive/2004/02/20/77460.aspx
if (!IsEmpty)
{
_section.PtsContext.OnPageDisposed(_ptsPage, disposing, true);
}
// Cleanup the state.
_ptsPage.Value = IntPtr.Zero;
_breakRecord = null;
_visual = null;
_backgroundFormatOperation = null;
}
}
// ------------------------------------------------------------------
// Inlitialize page state before formatting of the page.
// ------------------------------------------------------------------
private void OnBeforeFormatPage(bool finitePage, bool incremental)
{
// If not in incremental mode a new PTS page is created, hence there is
// a need to destroy existing one to avoid unmanaged resources leaking.
if (!incremental && !IsEmpty)
{
DestroyPage();
}
_incrementalUpdate = incremental;
_finitePage = finitePage;
_breakRecord = null;
_pageContextOfThisPage.PageRect = new PTS.FSRECT(new Rect(_section.StructuralCache.CurrentFormatContext.PageSize));
// Ensure we have no background work pending
if (_backgroundFormatOperation != null)
{
_backgroundFormatOperation.Abort();
}
if (!_finitePage)
{
_section.StructuralCache.BackgroundFormatInfo.UpdateBackgroundFormatInfo();
}
}
// ------------------------------------------------------------------
// Clear state and collect necessary data after formatting of the page.
// ------------------------------------------------------------------
/// <SecurityNote>
/// Critical - as this calls Critical function PtsContext.OnPageCreated.
/// Safe - as the parameter it passes it is Critical for set.
/// </SecurityNote>
[SecurityCritical, SecurityTreatAsSafe]
private void OnAfterFormatPage(bool setSize, bool incremental)
{
// Update page size if necessary
if (setSize)
{
PTS.FSRECT rect = GetRect();
PTS.FSBBOX bbox = GetBoundingBox();
// Workaround for PTS bug 860: get max of the page rect and
// bounding box of the page.
if (!FinitePage && PTS.ToBoolean(bbox.fDefined))
{
rect.dv = Math.Max(rect.dv, bbox.fsrc.dv);
}
// Set page size
_calculatedSize.Width = Math.Max(TextDpi.MinWidth, TextDpi.FromTextDpi(rect.du));
_calculatedSize.Height = Math.Max(TextDpi.MinWidth, TextDpi.FromTextDpi(rect.dv));
// Set content size
if (PTS.ToBoolean(bbox.fDefined))
{
_contentSize.Width = Math.Max(Math.Max(TextDpi.FromTextDpi(bbox.fsrc.du), TextDpi.MinWidth), _calculatedSize.Width);
_contentSize.Height = Math.Max(TextDpi.MinWidth, TextDpi.FromTextDpi(bbox.fsrc.dv));
// In bottomless pages, page size reported by PTS is
// actually content size (see PTS bug 860 for exceptions).
// Take PTS calculated value into account.
if (!FinitePage)
{
_contentSize.Height = Math.Max(_contentSize.Height, _calculatedSize.Height);
}
}
else
{
_contentSize = _calculatedSize;
}
}
if (!IsEmpty)
{
// If page has been just created, notify PtsContext about this fact.
// PtsContext keeps track of all created pages.
if (!incremental)
{
PtsContext.OnPageCreated(_ptsPage);
}
}
// Make sure that structural cache is in clean state after formatting
// is done.
if (_section.StructuralCache != null)
{
_section.StructuralCache.ClearUpdateInfo(false);
}
}
// ------------------------------------------------------------------
// Retrieve rectangle the page/subpage.
// ------------------------------------------------------------------
/// <SecurityNote>
/// Critical - as this calls Critical function PTS.FsQueryPageDetails.
/// Safe - as this can't be be used to pass arbitrary parameters. All parameters passed
/// in are either Critical for set or are generated within the function.
/// </SecurityNote>
[SecurityCritical, SecurityTreatAsSafe]
private PTS.FSRECT GetRect()
{
PTS.FSRECT rect;
// There are 3 cases when calculating page rect:
// (1) PTS page is not created - return empty rect.
// (2) PTS page - use page PTS APIs to get page rectangle.
// (3) PTS subpage - use subpage PTS APIs to get page rectangle.
if (IsEmpty)
{
// (1) PTS page is not created - empty rect.
rect = new PTS.FSRECT();
}
else
{
// (2) PTS page - use page PTS APIs to get page rectangle.
PTS.FSPAGEDETAILS pageDetails;
PTS.Validate(PTS.FsQueryPageDetails(PtsContext.Context, _ptsPage.Value, out pageDetails));
// There are 2 different types of PTS page and calculated rectangle depends on it:
// (a) simple page (contains only one track) - get rectanglefrom the track.
// (b) complex page (contains header, page body, footnotes and footer) - get bounding
// box of each segment and union them.
if (PTS.ToBoolean(pageDetails.fSimple))
{
// (a) simple page (contains only one track) - get rectanglefrom the track.
rect = pageDetails.u.simple.trackdescr.fsrc;
}
else
{
// (b) complex page (contains header, page body, footnotes and footer) - get rectangle
// of each segment and union them.
//ErrorHandler.Assert(!PTS.ToBoolean(pageDetails.u.complex.fTopBottomHeaderFooter) || this.PresenterCache.CurrentPresenter.IsBottomless, ErrorHandler.NotSupportedHeadersFooters);
//ErrorHandler.Assert(!PTS.ToBoolean(pageDetails.u.complex.fJustified) || this.PresenterCache.CurrentPresenter.IsBottomless, ErrorHandler.NotSupportedVerticalJustify);
ErrorHandler.Assert(pageDetails.u.complex.cFootnoteColumns == 0, ErrorHandler.NotSupportedFootnotes);
// Since header/footer and footnotes are not supported yet, use page body rectangle
rect = pageDetails.u.complex.fsrcPageBody;
}
}
return rect;
}
// ------------------------------------------------------------------
// Retrieve bounding box of the page/subpage
// ------------------------------------------------------------------
/// <SecurityNote>
/// Critical - as this calls Critical function PTS.FsQueryPageDetails.
/// Safe - as this can't be be used to pass arbitrary parameters. All parameters passed
/// in are either Critical for set or are generated within the function.
/// </SecurityNote>
[SecurityCritical, SecurityTreatAsSafe]
private PTS.FSBBOX GetBoundingBox()
{
PTS.FSBBOX bbox = new PTS.FSBBOX();
// There are 3 cases when calculating bounding box:
// (1) PTS page is not created - return empty rect.
// (2) PTS page - use page PTS APIs to get bounding box.
// (3) PTS subpage - use subpage PTS APIs to get bounding box.
if (IsEmpty)
{
// (1) PTS page is not created - bbox is not defined
}
else
{
// (2) PTS page - use page PTS APIs to get bounding box.
PTS.FSPAGEDETAILS pageDetails;
PTS.Validate(PTS.FsQueryPageDetails(PtsContext.Context, _ptsPage.Value, out pageDetails));
// There are 2 different types of PTS page and bounding box calculation depends on it:
// (a) simple page (contains only one track) - get bounding box from the track.
// (b) complex page (contains header, page body, footnotes and footer) - get bounding
// box of each segment and union them.
if (PTS.ToBoolean(pageDetails.fSimple))
{
// (a) simple page (contains only one track) - get bounding box from the track.
bbox = pageDetails.u.simple.trackdescr.fsbbox;
}
else
{
// (b) complex page (contains header, page body, footnotes and footer) - get bounding
// box of each segment and union them.
//ErrorHandler.Assert(!PTS.ToBoolean(pageDetails.u.complex.fTopBottomHeaderFooter), ErrorHandler.NotSupportedHeadersFooters);
//ErrorHandler.Assert(!PTS.ToBoolean(pageDetails.u.complex.fJustified), ErrorHandler.NotSupportedVerticalJustify);
ErrorHandler.Assert(pageDetails.u.complex.cFootnoteColumns == 0, ErrorHandler.NotSupportedFootnotes);
// Since header/footer and footnotes are not supported yet, use page body bounding box
bbox = pageDetails.u.complex.fsbboxPageBody;
}
}
return bbox;
}
//-------------------------------------------------------------------
// Arrange PTS section.
//-------------------------------------------------------------------
/// <SecurityNote>
/// Critical - as this calls Critical functions PTS.FsQuerySectionDetails and
/// some PtsHelper functions.
/// </SecurityNote>
[SecurityCritical]
private void ArrangeSection(ref PTS.FSSECTIONDESCRIPTION sectionDesc)
{
// Get section details
PTS.FSSECTIONDETAILS sectionDetails;
PTS.Validate(PTS.FsQuerySectionDetails(PtsContext.Context, sectionDesc.pfssection, out sectionDetails));
// There are 2 types of sections:
// (1) with page notes - footnotes in section treated as endnotes
// (2) with column notes - footnotes in section treated as column notes
if (PTS.ToBoolean(sectionDetails.fFootnotesAsPagenotes))
{
// (1) with page notes - footnotes in section treated as endnotes
ErrorHandler.Assert(sectionDetails.u.withpagenotes.cEndnoteColumns == 0, ErrorHandler.NotSupportedFootnotes);
//
Debug.Assert(sectionDetails.u.withpagenotes.cSegmentDefinedColumnSpanAreas == 0);
Debug.Assert(sectionDetails.u.withpagenotes.cHeightDefinedColumnSpanAreas == 0);
// cBasicColumns == 0, means that section content is empty.
// In such case there is nothing to render.
if (sectionDetails.u.withpagenotes.cBasicColumns != 0)
{
// Retrieve description for each column.
PTS.FSTRACKDESCRIPTION [] arrayColumnDesc;
PtsHelper.TrackListFromSection(PtsContext, sectionDesc.pfssection, ref sectionDetails, out arrayColumnDesc);
// Arrange each column
for (int index = 0; index < arrayColumnDesc.Length; index++)
{
// Exceptions don't need to pop, as the top level arrange context will be nulled out if thrown.
_section.StructuralCache.CurrentArrangeContext.PushNewPageData(_pageContextOfThisPage, arrayColumnDesc[index].fsrc, _finitePage);
PtsHelper.ArrangeTrack(PtsContext, ref arrayColumnDesc[index], sectionDetails.u.withpagenotes.fswdir);
_section.StructuralCache.CurrentArrangeContext.PopPageData();
}
}
}
else
{
// (2) with column notes - footnotes in section treated as column notes
ErrorHandler.Assert(false, ErrorHandler.NotSupportedCompositeColumns);
}
}
//-------------------------------------------------------------------
// Update the viewport for a section
//-------------------------------------------------------------------
/// <SecurityNote>
/// Critical - as this calls Critical function PTS.FsQuerySectionDetails and some
/// PtsHelper functions.
/// </SecurityNote>
[SecurityCritical]
private void UpdateViewportSection(ref PTS.FSSECTIONDESCRIPTION sectionDesc, ref PTS.FSRECT viewport)
{
// Get section details
PTS.FSSECTIONDETAILS sectionDetails;
PTS.Validate(PTS.FsQuerySectionDetails(PtsContext.Context, sectionDesc.pfssection, out sectionDetails));
// There are 2 types of sections:
// (1) with page notes - footnotes in section treated as endnotes
// (2) with column notes - footnotes in section treated as column notes
if (PTS.ToBoolean(sectionDetails.fFootnotesAsPagenotes))
{
// (1) with page notes - footnotes in section treated as endnotes
ErrorHandler.Assert(sectionDetails.u.withpagenotes.cEndnoteColumns == 0, ErrorHandler.NotSupportedFootnotes);
//
Debug.Assert(sectionDetails.u.withpagenotes.cSegmentDefinedColumnSpanAreas == 0);
Debug.Assert(sectionDetails.u.withpagenotes.cHeightDefinedColumnSpanAreas == 0);
// cBasicColumns == 0, means that section content is empty.
// In such case there is nothing to render.
if (sectionDetails.u.withpagenotes.cBasicColumns != 0)
{
// Retrieve description for each column.
PTS.FSTRACKDESCRIPTION [] arrayColumnDesc;
PtsHelper.TrackListFromSection(PtsContext, sectionDesc.pfssection, ref sectionDetails, out arrayColumnDesc);
// Arrange each column
for (int index = 0; index < arrayColumnDesc.Length; index++)
{
PtsHelper.UpdateViewportTrack(PtsContext, ref arrayColumnDesc[index], ref viewport);
}
}
}
else
{
// (2) with column notes - footnotes in section treated as column notes
ErrorHandler.Assert(false, ErrorHandler.NotSupportedCompositeColumns);
}
}
//-------------------------------------------------------------------
// Update PTS page visuals.
//-------------------------------------------------------------------
/// <SecurityNote>
/// Critical - as this calls Critical function PTS.FsQueryPageDetails and some
/// PtsHelper functions.
/// Safe - as this can't be be used to pass arbitrary parameters. All parameters passed
/// in are either Critical for set or are generated within the function.
/// </SecurityNote>
[SecurityCritical, SecurityTreatAsSafe]
private void UpdatePageVisuals(Size arrangeSize)
{
Invariant.Assert(!IsEmpty);
VisualCollection visualChildren;
// Get page details
PTS.FSPAGEDETAILS pageDetails;
PTS.Validate(PTS.FsQueryPageDetails(PtsContext.Context, _ptsPage.Value, out pageDetails));
// If there is no change, visual information is valid
if (pageDetails.fskupd == PTS.FSKUPDATE.fskupdNoChange) { return; }
ErrorHandler.Assert(pageDetails.fskupd != PTS.FSKUPDATE.fskupdShifted, ErrorHandler.UpdateShiftedNotValid);
ContainerVisual pageContentVisual;
ContainerVisual floatingElementsVisual;
if(_visual.Children.Count != 2)
{
_visual.Children.Clear();
_visual.Children.Add(new ContainerVisual());
_visual.Children.Add(new ContainerVisual());
}
pageContentVisual = (ContainerVisual)_visual.Children[0];
floatingElementsVisual = (ContainerVisual)_visual.Children[1];
// Page content may be simple or complex -
// depending of set of features used in the content of the page.
// (1) simple page (contains only one track)
// (2) complex page (contains header, page body (list of sections), footnotes and footer)
if (PTS.ToBoolean(pageDetails.fSimple))
{
// (1) simple page (contains only one track)
// Each track is represented as a ContainerVisual.
PTS.FSKUPDATE fskupd = pageDetails.u.simple.trackdescr.fsupdinf.fskupd;
if (fskupd == PTS.FSKUPDATE.fskupdInherited)
{
fskupd = pageDetails.fskupd;
}
visualChildren = pageContentVisual.Children;
if (fskupd == PTS.FSKUPDATE.fskupdNew)
{
visualChildren.Clear();
visualChildren.Add(new ContainerVisual());
}
// For complex page SectionVisual is added. So, when morphing
// complex subpage to simple one, remove SectionVisual.
else if (visualChildren.Count == 1 && visualChildren[0] is SectionVisual)
{
visualChildren.Clear();
visualChildren.Add(new ContainerVisual());
}
Debug.Assert(visualChildren.Count == 1 && visualChildren[0] is ContainerVisual);
ContainerVisual trackVisual = (ContainerVisual)visualChildren[0];
PtsHelper.UpdateTrackVisuals(PtsContext, trackVisual.Children, pageDetails.fskupd, ref pageDetails.u.simple.trackdescr);
}
else
{
// (2) complex page (contains header, page body (list of sections), footnotes and footer)
// NOTE: only page body (list of sections is currently supported).
//ErrorHandler.Assert(!PTS.ToBoolean(pageDetails.u.complex.fTopBottomHeaderFooter), ErrorHandler.NotSupportedHeadersFooters);
//ErrorHandler.Assert(!PTS.ToBoolean(pageDetails.u.complex.fJustified), ErrorHandler.NotSupportedVerticalJustify);
ErrorHandler.Assert(pageDetails.u.complex.cFootnoteColumns == 0, ErrorHandler.NotSupportedFootnotes);
// cSections == 0, means that page body content is empty.
bool emptyPage = (pageDetails.u.complex.cSections == 0);
if (!emptyPage)
{
// Retrieve description for each section.
PTS.FSSECTIONDESCRIPTION [] arraySectionDesc;
PtsHelper.SectionListFromPage(PtsContext, _ptsPage.Value, ref pageDetails, out arraySectionDesc);
emptyPage = (arraySectionDesc.Length == 0);
if (!emptyPage)
{
ErrorHandler.Assert(arraySectionDesc.Length == 1, ErrorHandler.NotSupportedMultiSection);
// For complex subpage SectionVisual is added. So, when morphing
// simple subpage to complex one, remove ParagraphVisual.
visualChildren = pageContentVisual.Children;
if (visualChildren.Count == 0)
{
visualChildren.Add(new SectionVisual());
}
else if (!(visualChildren[0] is SectionVisual))
{
visualChildren.Clear();
visualChildren.Add(new SectionVisual());
}
UpdateSectionVisuals((SectionVisual)visualChildren[0], pageDetails.fskupd, ref arraySectionDesc[0]);
}
}
if (emptyPage)
{
// There is no content, remove all existing visuals.
pageContentVisual.Children.Clear();
}
}
PtsHelper.UpdateFloatingElementVisuals(floatingElementsVisual, _pageContextOfThisPage.FloatingElementList);
}
//-------------------------------------------------------------------
// Update PTS section visuals.
//-------------------------------------------------------------------
/// <SecurityNote>
/// Critical - as it calls Critical function PTS.FsQuerySectionDetails and
/// some PtsHelper functions.
/// </SecurityNote>
[SecurityCritical]
private void UpdateSectionVisuals(
SectionVisual visual,
PTS.FSKUPDATE fskupdInherited,
ref PTS.FSSECTIONDESCRIPTION sectionDesc)
{
PTS.FSKUPDATE fskupd = sectionDesc.fsupdinf.fskupd;
if (fskupd == PTS.FSKUPDATE.fskupdInherited)
{
fskupd = fskupdInherited;
}
ErrorHandler.Assert(fskupd != PTS.FSKUPDATE.fskupdShifted, ErrorHandler.UpdateShiftedNotValid);
// If there is no change, visual information is valid
if (fskupd == PTS.FSKUPDATE.fskupdNoChange) { return; }
bool emptySection;
// Get section details
PTS.FSSECTIONDETAILS sectionDetails;
PTS.Validate(PTS.FsQuerySectionDetails(PtsContext.Context, sectionDesc.pfssection, out sectionDetails));
// There are 2 types of sections:
// (1) with page notes - footnotes in section treated as endnotes
// (2) with column notes - footnotes in section treated as column notes
if (PTS.ToBoolean(sectionDetails.fFootnotesAsPagenotes))
{
// (1) with page notes - footnotes in section treated as endnotes
ErrorHandler.Assert(sectionDetails.u.withpagenotes.cEndnoteColumns == 0, ErrorHandler.NotSupportedFootnotes);
//
Debug.Assert(sectionDetails.u.withpagenotes.cSegmentDefinedColumnSpanAreas == 0);
Debug.Assert(sectionDetails.u.withpagenotes.cHeightDefinedColumnSpanAreas == 0);
// cBasicColumns == 0, means that section content is empty.
emptySection = (sectionDetails.u.withpagenotes.cBasicColumns == 0);
if (!emptySection)
{
// Retrieve description for each column.
PTS.FSTRACKDESCRIPTION [] arrayColumnDesc;
PtsHelper.TrackListFromSection(PtsContext, sectionDesc.pfssection, ref sectionDetails, out arrayColumnDesc);
emptySection = (arrayColumnDesc.Length == 0);
if (!emptySection)
{
// Draw column rules.
ColumnPropertiesGroup columnProperties = new ColumnPropertiesGroup(_section.Element);
visual.DrawColumnRules(ref arrayColumnDesc, TextDpi.FromTextDpi(sectionDesc.fsrc.v), TextDpi.FromTextDpi(sectionDesc.fsrc.dv), columnProperties);
VisualCollection visualChildren = visual.Children;
if (fskupd == PTS.FSKUPDATE.fskupdNew)
{
visualChildren.Clear();
for (int index = 0; index < arrayColumnDesc.Length; index++)
{
visualChildren.Add(new ContainerVisual());
}
}
ErrorHandler.Assert(visualChildren.Count == arrayColumnDesc.Length, ErrorHandler.ColumnVisualCountMismatch);
for (int index = 0; index < arrayColumnDesc.Length; index++)
{
ContainerVisual trackVisual = (ContainerVisual)visualChildren[index];
PtsHelper.UpdateTrackVisuals(PtsContext, trackVisual.Children, fskupd, ref arrayColumnDesc[index]);
}
}
}
}
else
{
// (2) with column notes - footnotes in section treated as column notes
ErrorHandler.Assert(false, ErrorHandler.NotSupportedCompositeColumns);
emptySection = true;
}
if (emptySection)
{
// There is no content, remove all existing visuals.
visual.Children.Clear();
}
}
//-------------------------------------------------------------------
// Hit tests to the correct IInputElement within the page that the
// mouse is over.
//-------------------------------------------------------------------
/// <SecurityNote>
/// Critical - as this calls Critical function PTS.FsQueryPageDetails and some
/// PtsHelper functions.
/// Safe - as this can't be be used to pass arbitrary parameters. All parameters passed
/// in are either Critical for set or are generated within the function.
/// </SecurityNote>
[SecurityCritical, SecurityTreatAsSafe]
private IInputElement InputHitTestPage(PTS.FSPOINT pt)
{
IInputElement ie = null;
if(_pageContextOfThisPage.FloatingElementList != null)
{
for(int index = 0; index < _pageContextOfThisPage.FloatingElementList.Count && ie == null; index++)
{
BaseParaClient floatingElement = _pageContextOfThisPage.FloatingElementList[index];
ie = floatingElement.InputHitTest(pt);
}
}
if(ie == null)
{
// Get page details
PTS.FSPAGEDETAILS pageDetails;
PTS.Validate(PTS.FsQueryPageDetails(PtsContext.Context, _ptsPage.Value, out pageDetails));
// Hittest page content. Page content may be simple or complex -
// depending of set of features used in the content of the page.
// (1) simple page (contains only one track)
// (2) complex page (contains header, page body (list of sections), footnotes and footer)
if (PTS.ToBoolean(pageDetails.fSimple))
{
// (1) simple page (contains only one track)
if (pageDetails.u.simple.trackdescr.fsrc.Contains(pt))
{
ie = PtsHelper.InputHitTestTrack(PtsContext, pt, ref pageDetails.u.simple.trackdescr);
}
}
else
{
// (2) complex page (contains header, page body (list of sections), footnotes and footer)
// NOTE: only page body (list of sections is currently supported).
//ErrorHandler.Assert(!PTS.ToBoolean(pageDetails.u.complex.fTopBottomHeaderFooter), ErrorHandler.NotSupportedHeadersFooters);
//ErrorHandler.Assert(!PTS.ToBoolean(pageDetails.u.complex.fJustified), ErrorHandler.NotSupportedVerticalJustify);
ErrorHandler.Assert(pageDetails.u.complex.cFootnoteColumns == 0, ErrorHandler.NotSupportedFootnotes);
// cSections == 0, means that page body content is empty.
// In such case there is nothing to render.
if (pageDetails.u.complex.cSections != 0)
{
// Retrieve description for each section.
PTS.FSSECTIONDESCRIPTION [] arraySectionDesc;
PtsHelper.SectionListFromPage(PtsContext, _ptsPage.Value, ref pageDetails, out arraySectionDesc);
// Hittest each section
for (int index = 0; index < arraySectionDesc.Length && ie == null; index++)
{
if (arraySectionDesc[index].fsrc.Contains(pt))
{
ie = InputHitTestSection(pt, ref arraySectionDesc[index]);
}
}
}
}
}
return ie;
}
//-------------------------------------------------------------------
// Returns ArrayList of rectangles for the ContentElement e within
// the page. If element is not found, returns empty ArrayList
// start: int representing start offset of e
// length: int representing number of positions occupied by e
// ------------------------------------------------------------------
/// <SecurityNote>
/// Critical - as this calls Critical function PTS.FsQueryPageDetails and some
/// PtsHelper functions.
/// Safe - as this can't be be used to pass arbitrary parameters. All parameters passed
/// in are either Critical for set or are generated within the function.
/// </SecurityNote>
[SecurityCritical, SecurityTreatAsSafe]
private List<Rect> GetRectanglesInPage(ContentElement e, int start, int length)
{
// Rectangles to be returned
List<Rect> rectangles = new List<Rect>();
Invariant.Assert(!IsEmpty);
// Get page details
PTS.FSPAGEDETAILS pageDetails;
PTS.Validate(PTS.FsQueryPageDetails(PtsContext.Context, _ptsPage.Value, out pageDetails));
// Check for page content - if simple, contains only one track and we call the helper to
// find the element within that track. If complex, we must traverse sections within the content.
if (PTS.ToBoolean(pageDetails.fSimple))
{
// (1) simple page (contains only one track)
rectangles = PtsHelper.GetRectanglesInTrack(PtsContext, e, start, length, ref pageDetails.u.simple.trackdescr);
}
else
{
// (2) complex page (contains header, page body (list of sections), footnotes and footer)
// NOTE: only page body (list of sections is currently supported).
//ErrorHandler.Assert(!PTS.ToBoolean(pageDetails.u.complex.fTopBottomHeaderFooter), ErrorHandler.NotSupportedHeadersFooters);
//ErrorHandler.Assert(!PTS.ToBoolean(pageDetails.u.complex.fJustified), ErrorHandler.NotSupportedVerticalJustify);
ErrorHandler.Assert(pageDetails.u.complex.cFootnoteColumns == 0, ErrorHandler.NotSupportedFootnotes);
// cSections == 0, means that page body content is empty.
// In such case there is nothing to render.
if (pageDetails.u.complex.cSections != 0)
{
// Retrieve description for each section.
PTS.FSSECTIONDESCRIPTION[] arraySectionDesc;
PtsHelper.SectionListFromPage(PtsContext, _ptsPage.Value, ref pageDetails, out arraySectionDesc);
// Check each section for element
for (int index = 0; index < arraySectionDesc.Length; index++)
{
rectangles = GetRectanglesInSection(e, start, length, ref arraySectionDesc[index]);
// For consistency, helpers cannot return null for rectangles
Invariant.Assert(rectangles != null);
if (rectangles.Count != 0)
{
// Found element and rectangles. We will sotp here because the element has been
// found and it cannot span more than one section. So we do not need to search
// the rest of the sections
break;
}
}
}
else
{
// No complex content, return empty list
rectangles = new List<Rect>();
}
}
return rectangles;
}
//-------------------------------------------------------------------
// Hit tests to the correct IInputElement within the section that the
// mouse is over.
//-------------------------------------------------------------------
/// <SecurityNote>
/// Critical - as this calls Critical functions PTS.FsQuerySectionDetails and
/// some PtsHelper functions.
/// </SecurityNote>
[SecurityCritical]
private IInputElement InputHitTestSection(
PTS.FSPOINT pt,
ref PTS.FSSECTIONDESCRIPTION sectionDesc)
{
IInputElement ie = null;
// Get section details
PTS.FSSECTIONDETAILS sectionDetails;
PTS.Validate(PTS.FsQuerySectionDetails(PtsContext.Context, sectionDesc.pfssection, out sectionDetails));
// There are 2 types of sections:
// (1) with page notes - footnotes in section treated as endnotes
// (2) with column notes - footnotes in section treated as column notes
if (PTS.ToBoolean(sectionDetails.fFootnotesAsPagenotes))
{
// (1) with page notes - footnotes in section treated as endnotes
ErrorHandler.Assert(sectionDetails.u.withpagenotes.cEndnoteColumns == 0, ErrorHandler.NotSupportedFootnotes);
//
Debug.Assert(sectionDetails.u.withpagenotes.cSegmentDefinedColumnSpanAreas == 0);
Debug.Assert(sectionDetails.u.withpagenotes.cHeightDefinedColumnSpanAreas == 0);
// cBasicColumns == 0, means that section content is empty.
// In such case there is nothing to hit-test.
if (sectionDetails.u.withpagenotes.cBasicColumns != 0)
{
// Retrieve description for each column.
PTS.FSTRACKDESCRIPTION [] arrayColumnDesc;
PtsHelper.TrackListFromSection(PtsContext, sectionDesc.pfssection, ref sectionDetails, out arrayColumnDesc);
// Hittest each column
for (int index = 0; index < arrayColumnDesc.Length; index++)
{
if (arrayColumnDesc[index].fsrc.Contains(pt))
{
ie = PtsHelper.InputHitTestTrack(PtsContext, pt, ref arrayColumnDesc[index]);
break;
}
}
}
}
else
{
// (2) with column notes - footnotes in section treated as column notes
ErrorHandler.Assert(false, ErrorHandler.NotSupportedCompositeColumns);
}
return ie;
}
//-------------------------------------------------------------------
// Returns ArrayList of rectangles for the ContentElement e in the
// section if it is found in the section. Returns empty ArrayList
// if section does not contain e. e may span multiple tracks in which
// case we will have more than one rectangle
// start: int representing start offset of e
// length: int representing number of positions occupied by e
//-------------------------------------------------------------------
/// <SecurityNote>
/// Critical - as this calls Critical functions PTS.FsQuerySectionDetails
/// and some PtsHelper functions.
/// </SecurityNote>
[SecurityCritical]
private List<Rect> GetRectanglesInSection(
ContentElement e,
int start,
int length,
ref PTS.FSSECTIONDESCRIPTION sectionDesc)
{
// Get section details
PTS.FSSECTIONDETAILS sectionDetails;
PTS.Validate(PTS.FsQuerySectionDetails(PtsContext.Context, sectionDesc.pfssection, out sectionDetails));
// Declare ArrayList to be returned
List<Rect> rectangles = new List<Rect>();
// There are 2 types of sections:
// (1) with page notes - footnotes in section treated as endnotes
// (2) with column notes - footnotes in section treated as column notes
if (PTS.ToBoolean(sectionDetails.fFootnotesAsPagenotes))
{
// (1) with page notes - footnotes in section treated as endnotes
ErrorHandler.Assert(sectionDetails.u.withpagenotes.cEndnoteColumns == 0, ErrorHandler.NotSupportedFootnotes);
//
Debug.Assert(sectionDetails.u.withpagenotes.cSegmentDefinedColumnSpanAreas == 0);
Debug.Assert(sectionDetails.u.withpagenotes.cHeightDefinedColumnSpanAreas == 0);
// cBasicColumns == 0, means that section content is empty.
// In such case there is nothing to hit-test.
if (sectionDetails.u.withpagenotes.cBasicColumns != 0)
{
// Retrieve description for each column.
PTS.FSTRACKDESCRIPTION[] arrayColumnDesc;
PtsHelper.TrackListFromSection(PtsContext, sectionDesc.pfssection, ref sectionDetails, out arrayColumnDesc);
// Check each column for element or part of element - element may span multiple
// columns/tracks
for (int index = 0; index < arrayColumnDesc.Length; index++)
{
// See if any rectangles for the element are found in this track
List<Rect> trackRectangles = PtsHelper.GetRectanglesInTrack(PtsContext, e, start, length, ref arrayColumnDesc[index]);
// For consistency, rectangles collection is never null, only empty
Invariant.Assert(trackRectangles != null);
if (trackRectangles.Count != 0)
{
// Add rectangles found in this track to rectangles for element
rectangles.AddRange(trackRectangles);
}
}
}
}
else
{
// (2) with column notes - footnotes in section treated as column notes
ErrorHandler.Assert(false, ErrorHandler.NotSupportedCompositeColumns);
}
return rectangles;
}
// ------------------------------------------------------------------
// Destroy page - release unmanaged resources.
// ------------------------------------------------------------------
/// <SecurityNote>
/// Critical - as this calls Critical setter _ptsPage.Value.
/// Safe - as this just sets it to IntPtr.Zero which is safe.
/// </SecurityNote>
[SecurityCritical, SecurityTreatAsSafe]
private void DestroyPage()
{
if (_ptsPage.Value != IntPtr.Zero)
{
PtsContext.OnPageDisposed(_ptsPage, true, false);
_ptsPage.Value = IntPtr.Zero;
}
}
#endregion Private Methods
// ------------------------------------------------------------------
//
// Private Properties
//
// ------------------------------------------------------------------
#region Private Properties
private bool IsEmpty
{
get
{
return (_ptsPage.Value == IntPtr.Zero);
}
}
#endregion Private Properties
// ------------------------------------------------------------------
//
// Private Fields
//
// ------------------------------------------------------------------
#region Private Fields
// ------------------------------------------------------------------
// PTS section - root of the NameTable.
// ------------------------------------------------------------------
private readonly Section _section;
//-------------------------------------------------------------------
// BreakRecord indicating break position of the page.
//-------------------------------------------------------------------
private PageBreakRecord _breakRecord;
//-------------------------------------------------------------------
// Visual node representing content of the page.
//-------------------------------------------------------------------
private ContainerVisual _visual;
//-------------------------------------------------------------------
// Handle to pending background format operation
//-------------------------------------------------------------------
private DispatcherOperation _backgroundFormatOperation;
// ------------------------------------------------------------------
// Calculated size of the page.
// ------------------------------------------------------------------
private Size _calculatedSize = new Size();
// ------------------------------------------------------------------
// Content size of the page.
// ------------------------------------------------------------------
private Size _contentSize = new Size();
// ------------------------------------------------------------------
// Context the current page provides for its content.
// ------------------------------------------------------------------
private PageContext _pageContextOfThisPage = new PageContext();
// ------------------------------------------------------------------
// PTS page object.
// ------------------------------------------------------------------
/// <SecurityNote>
/// _ptsPage is passed directly Critical unmanaged PTS functions which
/// write to memory. So this should be Critical for set.
/// </SecurityNote>
private SecurityCriticalDataForSet<IntPtr> _ptsPage;
// ------------------------------------------------------------------
// Is it finite page?
// ------------------------------------------------------------------
private bool _finitePage;
//-------------------------------------------------------------------
// Is during incremental update mode?
//-------------------------------------------------------------------
private bool _incrementalUpdate;
//-------------------------------------------------------------------
// Is being used in a plain text box?
//-------------------------------------------------------------------
internal bool _useSizingWorkaroundForTextBox;
// ------------------------------------------------------------------
// Is object already disposed.
// ------------------------------------------------------------------
private int _disposed;
#endregion Private Fields
}
// ----------------------------------------------------------------------
// A page context object represents the current page or subpage being formatted.
// It allows for collection of floating elements, and provides sizing information for RTL mirroring.
// ----------------------------------------------------------------------
internal class PageContext
{
internal PTS.FSRECT PageRect { get { return _pageRect; } set { _pageRect = value; } }
internal List<BaseParaClient> FloatingElementList { get { return _floatingElementList; } }
internal void AddFloatingParaClient(BaseParaClient floatingElement)
{
if(_floatingElementList == null)
{
_floatingElementList = new List<BaseParaClient>();
}
if(!_floatingElementList.Contains(floatingElement))
{
_floatingElementList.Add(floatingElement);
}
}
internal void RemoveFloatingParaClient(BaseParaClient floatingElement)
{
if(_floatingElementList.Contains(floatingElement))
{
_floatingElementList.Remove(floatingElement);
}
if(_floatingElementList.Count == 0)
{
_floatingElementList = null;
}
}
// ------------------------------------------------------------------
// Floating element list
// ------------------------------------------------------------------
private List<BaseParaClient> _floatingElementList;
// ------------------------------------------------------------------
// Page rect
// ------------------------------------------------------------------
private PTS.FSRECT _pageRect;
}
}
|