Code:
/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / Framework / MS / Internal / PtsHost / TableParaClient.cs / 1305600 / TableParaClient.cs
//---------------------------------------------------------------------------- // //// Copyright (C) Microsoft Corporation. All rights reserved. // // // // Description: TableParaClient holds display related information. // // History: // 05/19/2003 : olego - Created // //--------------------------------------------------------------------------- using MS.Internal; using MS.Internal.PtsTable; using MS.Internal.Text; using MS.Internal.Documents; using System; using System.Diagnostics; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Security; using System.Windows; using System.Windows.Documents; using System.Windows.Media; using MS.Internal.PtsHost.UnsafeNativeMethods; namespace MS.Internal.PtsHost { ////// Table display data /// internal sealed class TableParaClient : BaseParaClient { //----------------------------------------------------- // // Constructors // //----------------------------------------------------- #region Constructors ////// Constructor /// /// Owner of the para client internal TableParaClient(TableParagraph paragraph) : base(paragraph) { } #endregion //------------------------------------------------------ // // Protected Methods // //----------------------------------------------------- #region Protected Methods ////// Arrange table paragraph by calling Arrange on all cells /// ////// Critical, because: /// a) calls Critical function QueryRowDetails. /// b) calls Critical function PTS.FsTransformRectangle /// Safe - as this can't be used to pass arbitrary parameters to the Critical /// function. All In parameters passed in are generated by safe methods. /// [SecurityCritical, SecurityTreatAsSafe] protected override void OnArrange() { Debug.Assert( Table != null && CalculatedColumns != null ); base.OnArrange(); _columnRect = Paragraph.StructuralCache.CurrentArrangeContext.ColumnRect; CalculatedColumn[] calculatedColumns = CalculatedColumns; PTS.FSTABLEROWDESCRIPTION[] arrayTableRowDesc; PTS.FSKUPDATE fskupdTable; PTS.FSRECT rect; if ( // table has no rows thus no cells to arrange !QueryTableDetails(out arrayTableRowDesc, out fskupdTable, out rect) // no changes requiring arrange in the table || fskupdTable == PTS.FSKUPDATE.fskupdNoChange || fskupdTable == PTS.FSKUPDATE.fskupdShifted ) { return; } // Rect may have moved from collision with figure or floater. _rect = rect; // Update chunk information UpdateChunkInfo(arrayTableRowDesc); MbpInfo mbp = MbpInfo.FromElement(TableParagraph.Element); if(ParentFlowDirection != PageFlowDirection) { PTS.FSRECT pageRect = _pageContext.PageRect; PTS.Validate(PTS.FsTransformRectangle(PTS.FlowDirectionToFswdir(ParentFlowDirection), ref pageRect, ref _rect, PTS.FlowDirectionToFswdir(PageFlowDirection), out _rect)); mbp.MirrorMargin(); } _rect.u += mbp.MarginLeft; _rect.du -= mbp.MarginLeft + mbp.MarginRight; int vrRowTop = GetTableOffsetFirstRowTop() + TextDpi.ToTextDpi(Table.InternalCellSpacing) / 2; for (int iR = 0; iR < arrayTableRowDesc.Length; ++iR) { PTS.FSKUPDATE fskupdRow; PTS.FSKUPDATE[] arrayUpdate; IntPtr[] arrayFsCell; PTS.FSTABLEKCELLMERGE[] arrayTableCellMerge; fskupdRow = (arrayTableRowDesc[iR].fsupdinf.fskupd != PTS.FSKUPDATE.fskupdInherited) ? arrayTableRowDesc[iR].fsupdinf.fskupd : fskupdTable; if (fskupdRow == PTS.FSKUPDATE.fskupdNoChange) { // no changes requiring arrange in the row vrRowTop += arrayTableRowDesc[iR].u.dvrRow; continue; } QueryRowDetails( arrayTableRowDesc[iR].pfstablerow, out arrayFsCell, out arrayUpdate, out arrayTableCellMerge); for (int iC = 0; iC < arrayFsCell.Length; ++iC) { if (arrayFsCell[iC] == IntPtr.Zero) { // paginated case - cell may be null continue; } if ( iR != 0 // paginated case - row spanned cells appearing in first row on the page must be accounted && ( arrayTableCellMerge[iC] == PTS.FSTABLEKCELLMERGE.fskcellmergeMiddle || arrayTableCellMerge[iC] == PTS.FSTABLEKCELLMERGE.fskcellmergeLast ) ) { // this cell has been accounted continue; } CellParaClient cellParaClient = (CellParaClient)(PtsContext.HandleToObject(arrayFsCell[iC])); double urCellOffset = calculatedColumns[cellParaClient.ColumnIndex].UrOffset; cellParaClient.Arrange(TextDpi.ToTextDpi(urCellOffset), vrRowTop, _rect, ThisFlowDirection, _pageContext); } vrRowTop += arrayTableRowDesc[iR].u.dvrRow; if (iR == 0 && IsFirstChunk) { // Remove extra spacing we reported to PTS in RowProperties for top row in table vrRowTop -= mbp.BPTop; } } } #endregion Protected Methods //------------------------------------------------------ // // Internal Methods // //------------------------------------------------------ #region Internal Methods ////// Validate table visual /// /// PTS update info ////// Critical - as this calls Critical function ValidateRowVisualComplex and /// ValidateRowVisualSimple. /// Safe - as this can't be used to pass arbitrary parameters to the Critical /// function. All In parameters passed in are generated by safe methods. /// [SecurityCritical, SecurityTreatAsSafe] internal override void ValidateVisual(PTS.FSKUPDATE fskupdInherited) { Invariant.Assert( fskupdInherited != PTS.FSKUPDATE.fskupdInherited ); Invariant.Assert( TableParagraph.Table != null && CalculatedColumns != null ); PTS.FSTABLEROWDESCRIPTION[] arrayTableRowDesc; PTS.FSKUPDATE fskupdTable; PTS.FSRECT rect; Table table = TableParagraph.Table; Visual.Clip = new RectangleGeometry(_columnRect.FromTextDpi()); if (!QueryTableDetails(out arrayTableRowDesc, out fskupdTable, out rect)) { // table has no rows thus no cell to validate _visual.Children.Clear(); return; } MbpInfo mbpInfo = MbpInfo.FromElement(TableParagraph.Element); if (ThisFlowDirection != PageFlowDirection) { mbpInfo.MirrorBP(); } if (fskupdTable == PTS.FSKUPDATE.fskupdInherited) { fskupdTable = fskupdInherited; } if (fskupdTable == PTS.FSKUPDATE.fskupdNoChange) { // no need to arrange because nothing changed return; } if (fskupdTable == PTS.FSKUPDATE.fskupdShifted) { fskupdTable = PTS.FSKUPDATE.fskupdNew; } VisualCollection rowVisualsCollection = _visual.Children; if (fskupdTable == PTS.FSKUPDATE.fskupdNew) { rowVisualsCollection.Clear(); } // Draw border and background info. Brush backgroundBrush = (Brush)Paragraph.Element.GetValue(TextElement.BackgroundProperty); using (DrawingContext dc = _visual.RenderOpen()) { Rect tableContentRect = GetTableContentRect(mbpInfo).FromTextDpi(); _visual.DrawBackgroundAndBorderIntoContext(dc, backgroundBrush, mbpInfo.BorderBrush, mbpInfo.Border, _rect.FromTextDpi(), IsFirstChunk, IsLastChunk); DrawColumnBackgrounds(dc, tableContentRect); DrawRowGroupBackgrounds(dc, arrayTableRowDesc, tableContentRect, mbpInfo); DrawRowBackgrounds(dc, arrayTableRowDesc, tableContentRect, mbpInfo); } TableRow rowPrevious = null; for (int iR = 0; iR < arrayTableRowDesc.Length; ++iR) { PTS.FSKUPDATE fskupdRow; RowParagraph rowParagraph; TableRow row; fskupdRow = (arrayTableRowDesc[iR].fsupdinf.fskupd != PTS.FSKUPDATE.fskupdInherited) ? arrayTableRowDesc[iR].fsupdinf.fskupd : fskupdTable; rowParagraph = (RowParagraph)(PtsContext.HandleToObject(arrayTableRowDesc[iR].fsnmRow)); row = rowParagraph.Row; // // STEP 1 SYNCHRONIZATION // --------------------------------------------------------- // synchronize rowVisualCollection. // for newly created rows visual is inserted; // otherwise for removed rows (if any) corresponding visuals are removed // if (fskupdRow == PTS.FSKUPDATE.fskupdNew) { RowVisual rowVisual = new RowVisual(row); rowVisualsCollection.Insert(iR, rowVisual); } else { SynchronizeRowVisualsCollection(rowVisualsCollection, iR, row); } Invariant.Assert(((RowVisual)rowVisualsCollection[iR]).Row == row); // // STEP 2 CELL VISUALS VALIDATION // ---------------------------------------------------------- // for new or changed rows go inside and validate cells // if ( fskupdRow == PTS.FSKUPDATE.fskupdNew || fskupdRow == PTS.FSKUPDATE.fskupdChangeInside ) { // paginated case - if first row of a given rowgroup for this para client has foreign cells, they need to // be rendered regardless of merge state if(rowParagraph.Row.HasForeignCells && (rowPrevious == null || rowPrevious.RowGroup != row.RowGroup)) { ValidateRowVisualComplex( (RowVisual)(rowVisualsCollection[iR]), arrayTableRowDesc[iR].pfstablerow, CalculatedColumns.Length, fskupdRow, CalculatedColumns); } else { ValidateRowVisualSimple( (RowVisual)(rowVisualsCollection[iR]), arrayTableRowDesc[iR].pfstablerow, fskupdRow, CalculatedColumns); } } rowPrevious = row; } // // STEP 4 if (rowVisualsCollection.Count > arrayTableRowDesc.Length) { rowVisualsCollection.RemoveRange( arrayTableRowDesc.Length, rowVisualsCollection.Count - arrayTableRowDesc.Length); } } /// ------------------ /// Updates viewport /// -------------------- ////// Critical - as this calls Critical functions QueryRowDetails. /// Safe - The arrayTableRowDesc parameter passed to other methods is generated securely in this function. /// [SecurityCritical, SecurityTreatAsSafe] internal override void UpdateViewport(ref PTS.FSRECT viewport) { Debug.Assert( TableParagraph.Table != null && CalculatedColumns != null ); PTS.FSTABLEROWDESCRIPTION[] arrayTableRowDesc; PTS.FSKUPDATE fskupdTable; PTS.FSRECT rectTable; if (QueryTableDetails(out arrayTableRowDesc, out fskupdTable, out rectTable)) { for (int iR = 0; iR < arrayTableRowDesc.Length; ++iR) { PTS.FSKUPDATE[] arrayUpdate; IntPtr[] arrayFsCell; PTS.FSTABLEKCELLMERGE[] arrayTableCellMerge; QueryRowDetails( arrayTableRowDesc[iR].pfstablerow, out arrayFsCell, out arrayUpdate, out arrayTableCellMerge); for (int iC = 0; iC < arrayFsCell.Length; ++iC) { if (arrayFsCell[iC] == IntPtr.Zero) { // paginated case - cell may be null continue; } CellParaClient cellParaClient = (CellParaClient)(PtsContext.HandleToObject(arrayFsCell[iC])); cellParaClient.UpdateViewport(ref viewport); } } } } ////// Performs hit testing. /// /// Point of interest. ///Element if any. ////// Critical - as this calls Critical function QueryRowDetails. /// Safe - as this can't be used to pass arbitrary parameters to the Critical /// function. All In parameters passed in are generated by safe methods. /// [SecurityCritical, SecurityTreatAsSafe] internal override IInputElement InputHitTest(PTS.FSPOINT pt) { Debug.Assert( TableParagraph.Table != null && CalculatedColumns != null ); IInputElement element = null; PTS.FSTABLEROWDESCRIPTION[] arrayTableRowDesc; PTS.FSKUPDATE fskupdTable; PTS.FSRECT rectTable; if (QueryTableDetails(out arrayTableRowDesc, out fskupdTable, out rectTable)) { // Start vrRowTop from 'true' v of table int vrRowTop = GetTableOffsetFirstRowTop() + rectTable.v; for (int iR = 0; iR < arrayTableRowDesc.Length; ++iR) { if (pt.v >= vrRowTop && pt.v <= (vrRowTop + arrayTableRowDesc[iR].u.dvrRow)) { PTS.FSKUPDATE[] arrayUpdate; IntPtr[] arrayFsCell; PTS.FSTABLEKCELLMERGE[] arrayTableCellMerge; QueryRowDetails( arrayTableRowDesc[iR].pfstablerow, out arrayFsCell, out arrayUpdate, out arrayTableCellMerge); for (int iC = 0; iC < arrayFsCell.Length; ++iC) { if (arrayFsCell[iC] == IntPtr.Zero) { // paginated case - cell may be null continue; } CellParaClient cellParaClient = (CellParaClient)(PtsContext.HandleToObject(arrayFsCell[iC])); PTS.FSRECT rect = cellParaClient.Rect; if (cellParaClient.Rect.Contains(pt)) { element = cellParaClient.InputHitTest(pt); break; } } break; } vrRowTop += arrayTableRowDesc[iR].u.dvrRow; } } if(element == null && _rect.Contains(pt)) { element = TableParagraph.Table; } return (element); } ////// Returns ArrayList of rectangles for element if found. /// /// /// ContentElement for which rectangles are to be found. /// /// /// int representing start offset of e /// /// /// int representing number of positions occupied by element. /// ////// Critical - as this calls Critical function QueryRowDetails. /// Safe - as this can't be used to pass arbitrary parameters to the Critical /// function. All In parameters passed in are generated by safe methods. /// [SecurityCritical, SecurityTreatAsSafe] internal override ListGetRectangles(ContentElement e, int start, int length) { Debug.Assert(TableParagraph.Table != null && CalculatedColumns != null); List rectangles = new List (); if (TableParagraph.Table == e) { // We have found the element. Return rectangles for this paragraph. GetRectanglesForParagraphElement(out rectangles); } else { // Search subpage for table cells PTS.FSTABLEROWDESCRIPTION[] arrayTableRowDesc; PTS.FSKUPDATE fskupdTable; PTS.FSRECT rectTable; rectangles = new List (); if (QueryTableDetails(out arrayTableRowDesc, out fskupdTable, out rectTable)) { for (int iR = 0; iR < arrayTableRowDesc.Length; ++iR) { PTS.FSKUPDATE[] arrayUpdate; IntPtr[] arrayFsCell; PTS.FSTABLEKCELLMERGE[] arrayTableCellMerge; QueryRowDetails( arrayTableRowDesc[iR].pfstablerow, out arrayFsCell, out arrayUpdate, out arrayTableCellMerge); for (int iC = 0; iC < arrayFsCell.Length; ++iC) { if (arrayFsCell[iC] == IntPtr.Zero) { // paginated case - cell may be null continue; } CellParaClient cellParaClient = (CellParaClient)(PtsContext.HandleToObject(arrayFsCell[iC])); if (start < cellParaClient.Paragraph.ParagraphEndCharacterPosition) { // Element start is within cell boundary rectangles = cellParaClient.GetRectangles(e, start, length); Invariant.Assert(rectangles != null); if (rectangles.Count != 0) { break; } } } if (rectangles.Count != 0) { break; } } } } Invariant.Assert(rectangles != null); return rectangles; } /// /// Paragraph results - verification /// internal override ParagraphResult CreateParagraphResult() { return new TableParagraphResult(this); } ////// Return TextContentRange for the content of the paragraph. /// ////// Critical - as this calls Critical function QueryRowDetails. /// Safe - as this can't be used to pass arbitrary parameters to the Critical /// function. All In parameters passed in are generated by safe methods. /// [SecurityCritical, SecurityTreatAsSafe] internal override TextContentRange GetTextContentRange() { TextContentRange range = null; // Search subpage for table cells PTS.FSTABLEROWDESCRIPTION[] arrayTableRowDesc; PTS.FSKUPDATE fskupdTable; PTS.FSRECT rectTable; if (QueryTableDetails(out arrayTableRowDesc, out fskupdTable, out rectTable)) { range = new TextContentRange(); UpdateChunkInfo(arrayTableRowDesc); TextElement elementOwner = this.Paragraph.Element as TextElement; // Add range for Table leading edge, if necessary if (_isFirstChunk) range.Merge(TextContainerHelper.GetTextContentRangeForTextElementEdge( elementOwner, ElementEdge.BeforeStart)); for (int iR = 0; iR < arrayTableRowDesc.Length; ++iR) { PTS.FSTABLEROWDETAILS tableRowDetails; TableRow row = ((RowParagraph)(PtsContext.HandleToObject(arrayTableRowDesc[iR].fsnmRow))).Row; PTS.Validate(PTS.FsQueryTableObjRowDetails( PtsContext.Context, arrayTableRowDesc[iR].pfstablerow, out tableRowDetails)); if (tableRowDetails.fskboundaryAbove != PTS.FSKTABLEROWBOUNDARY.fsktablerowboundaryBreak) { // Add range for TableRowGroup leading edge, if necessary if (row.Index == 0) range.Merge(TextContainerHelper.GetTextContentRangeForTextElementEdge( row.RowGroup, ElementEdge.BeforeStart)); range.Merge(TextContainerHelper.GetTextContentRangeForTextElementEdge( row, ElementEdge.BeforeStart)); } PTS.FSKUPDATE[] arrayUpdate; IntPtr[] arrayFsCell; PTS.FSTABLEKCELLMERGE[] arrayTableCellMerge; QueryRowDetails( arrayTableRowDesc[iR].pfstablerow, out arrayFsCell, out arrayUpdate, out arrayTableCellMerge); for (int iC = 0; iC < arrayFsCell.Length; ++iC) { if (arrayFsCell[iC] == IntPtr.Zero) { // paginated case - cell may be null continue; } CellParaClient cellParaClient = (CellParaClient)(PtsContext.HandleToObject(arrayFsCell[iC])); range.Merge(cellParaClient.GetTextContentRange()); } if (tableRowDetails.fskboundaryBelow != PTS.FSKTABLEROWBOUNDARY.fsktablerowboundaryBreak) { range.Merge(TextContainerHelper.GetTextContentRangeForTextElementEdge( row, ElementEdge.AfterEnd)); // Add range for TableRowGroup trailing edge, if necessary if (row.Index == row.RowGroup.Rows.Count - 1) range.Merge(TextContainerHelper.GetTextContentRangeForTextElementEdge( row.RowGroup, ElementEdge.AfterEnd)); } } // Add range for Table trailing edge, if necessary if (_isLastChunk) range.Merge(TextContainerHelper.GetTextContentRangeForTextElementEdge( elementOwner, ElementEdge.AfterEnd)); } if (range == null) { // if table has no rows or no cells range = TextContainerHelper.GetTextContentRangeForTextElement(TableParagraph.Table); } return range; } ////// Returns the paragraphs for the appropriate cell, given a point /// /// Point to check for cell. /// Whether to snap to text ////// CellParaClient found /// ////// Critical - as this calls Critical function QueryRowDetails. /// Safe - as this can't be used to pass arbitrary parameters to the Critical /// function. All In parameters passed in are generated by safe methods. /// [SecurityCritical, SecurityTreatAsSafe] internal CellParaClient GetCellParaClientFromPoint(Point point, bool snapToText) { Debug.Assert( TableParagraph.Table != null && CalculatedColumns != null ); PTS.FSTABLEROWDESCRIPTION[] arrayTableRowDesc; PTS.FSKUPDATE fskupdTable; PTS.FSRECT rectTable; int u = TextDpi.ToTextDpi(point.X); int v = TextDpi.ToTextDpi(point.Y); CellParaClient cpcFound = null; CellParaClient cpcClosest = null; int iClosestDistance = int.MaxValue; if (QueryTableDetails(out arrayTableRowDesc, out fskupdTable, out rectTable)) { for (int iR = 0; iR < arrayTableRowDesc.Length && cpcFound == null; ++iR) { PTS.FSKUPDATE[] arrayUpdate; IntPtr[] arrayFsCell; PTS.FSTABLEKCELLMERGE[] arrayTableCellMerge; QueryRowDetails( arrayTableRowDesc[iR].pfstablerow, out arrayFsCell, out arrayUpdate, out arrayTableCellMerge); for (int iC = 0; iC < arrayFsCell.Length && cpcFound == null; ++iC) { if (arrayFsCell[iC] == IntPtr.Zero) { // paginated case - cell may be null continue; } CellParaClient cpc = (CellParaClient)(PtsContext.HandleToObject(arrayFsCell[iC])); PTS.FSRECT rect = cpc.Rect; if (u >= rect.u && u <= (rect.u + rect.du) && v >= rect.v && v <= (rect.v + rect.dv)) { cpcFound = cpc; } else if(snapToText) { int du = Math.Min(Math.Abs(rect.u - u), Math.Abs(rect.u + rect.du - u)); int dv = Math.Min(Math.Abs(rect.v - v), Math.Abs(rect.v + rect.dv - v)); if( (du + dv) < iClosestDistance) { iClosestDistance = du + dv; cpcClosest = cpc; } } } } } if(snapToText && cpcFound == null) { cpcFound = cpcClosest; } return cpcFound; } ////// Returns the paragraphs for all rows in a table /// /// /// True if any child paragraph result has text content /// ////// Array of paragraph results /// ////// Critical - as this calls Critical function QueryRowDetails. /// Safe - as this can't be used to pass arbitrary parameters to the Critical /// function. All In parameters passed in are generated by safe methods. /// [SecurityCritical, SecurityTreatAsSafe] internal ReadOnlyCollectionGetChildrenParagraphResults(out bool hasTextContent) { MbpInfo mbpInfo = MbpInfo.FromElement(TableParagraph.Element); if (ThisFlowDirection != PageFlowDirection) { mbpInfo.MirrorBP(); } Rect tableContentRect = GetTableContentRect(mbpInfo).FromTextDpi(); double rowTop = tableContentRect.Y; Rect rowRect = tableContentRect; hasTextContent = false; List rowParagraphResults = new List (0); PTS.FSTABLEROWDESCRIPTION[] arrayTableRowDesc; PTS.FSKUPDATE fskupdTable; PTS.FSRECT rectTable; if (QueryTableDetails(out arrayTableRowDesc, out fskupdTable, out rectTable)) { for (int iR = 0; iR < arrayTableRowDesc.Length; ++iR) { RowParagraph rowParagraph = (RowParagraph)(PtsContext.HandleToObject(arrayTableRowDesc[iR].fsnmRow)); rowRect.Y = rowTop; rowRect.Height = GetActualRowHeight(arrayTableRowDesc, iR, mbpInfo); RowParagraphResult rowParagraphResult = new RowParagraphResult(this, iR, rowRect, rowParagraph); if (rowParagraphResult.HasTextContent) { hasTextContent = true; } rowParagraphResults.Add(rowParagraphResult); rowTop += rowRect.Height; // Adjust for top of next row } } return new ReadOnlyCollection (rowParagraphResults); } /// /// Returns the paragraphs for a given row /// /// Row index ////// Array of paragraph results /// /// /// True if any child paragraph has text content /// ////// Critical - as this calls Critical function QueryRowDetails. /// Safe - as this can't be used to pass arbitrary parameters to the Critical /// function. All In parameters passed in are generated by safe methods. /// [SecurityCritical, SecurityTreatAsSafe] internal ReadOnlyCollectionGetChildrenParagraphResultsForRow(int rowIndex, out bool hasTextContent) { PTS.FSTABLEROWDESCRIPTION[] arrayTableRowDesc; PTS.FSKUPDATE fskupdTable; PTS.FSRECT rectTable; List cellParagraphResults = new List (0); hasTextContent = false; if (QueryTableDetails(out arrayTableRowDesc, out fskupdTable, out rectTable)) { PTS.FSKUPDATE[] arrayUpdate; IntPtr[] arrayFsCell; PTS.FSTABLEKCELLMERGE[] arrayTableCellMerge; QueryRowDetails( arrayTableRowDesc[rowIndex].pfstablerow, out arrayFsCell, out arrayUpdate, out arrayTableCellMerge); for (int iC = 0; iC < arrayFsCell.Length; ++iC) { if(arrayFsCell[iC] != IntPtr.Zero && (rowIndex == 0 || (arrayTableCellMerge[iC] != PTS.FSTABLEKCELLMERGE.fskcellmergeMiddle && arrayTableCellMerge[iC] != PTS.FSTABLEKCELLMERGE.fskcellmergeLast) ) ) { CellParaClient cpc = (CellParaClient)(PtsContext.HandleToObject(arrayFsCell[iC])); ParagraphResult cellParagraphResult = cpc.CreateParagraphResult(); if (cellParagraphResult.HasTextContent) { hasTextContent = true; } cellParagraphResults.Add(cellParagraphResult); } } } return new ReadOnlyCollection (cellParagraphResults); } /// /// Returns the paragraphs for the appropriate cell, given a point /// /// Point to check for cell. /// Whether to snap to text ////// Array of paragraph results /// internal ReadOnlyCollectionGetParagraphsFromPoint(Point point, bool snapToText) { CellParaClient cpc = GetCellParaClientFromPoint(point, snapToText); List listResults = new List (0); if(cpc != null) { listResults.Add(cpc.CreateParagraphResult()); } return new ReadOnlyCollection (listResults); } /// /// Returns the paragraphs for the appropriate cell, given a point /// /// Position of cell. ////// Array of paragraph results /// internal ReadOnlyCollectionGetParagraphsFromPosition(ITextPointer position) { CellParaClient cpc = GetCellParaClientFromPosition(position); List listResults = new List (0); if(cpc != null) { listResults.Add(cpc.CreateParagraphResult()); } return new ReadOnlyCollection (listResults); } /// /// Returns tight bounding path geometry. /// [SecurityCritical, SecurityTreatAsSafe] internal Geometry GetTightBoundingGeometryFromTextPositions(ITextPointer startPosition, ITextPointer endPosition, Rect visibleRect) { Debug.Assert( TableParagraph.Table != null && CalculatedColumns != null ); Geometry geometry = null; PTS.FSTABLEROWDESCRIPTION[] arrayTableRowDesc; PTS.FSKUPDATE fskupdTable; PTS.FSRECT rectTable; if (QueryTableDetails(out arrayTableRowDesc, out fskupdTable, out rectTable)) { bool passedEndPosition = false; for (int iR = 0; iR < arrayTableRowDesc.Length && !passedEndPosition; ++iR) { PTS.FSKUPDATE[] arrayUpdate; IntPtr[] arrayFsCell; PTS.FSTABLEKCELLMERGE[] arrayTableCellMerge; QueryRowDetails( arrayTableRowDesc[iR].pfstablerow, out arrayFsCell, out arrayUpdate, out arrayTableCellMerge); for (int iC = 0; iC < arrayFsCell.Length; ++iC) { if (arrayFsCell[iC] == IntPtr.Zero) { // paginated case - cell may be null continue; } CellParaClient cpc = (CellParaClient)(PtsContext.HandleToObject(arrayFsCell[iC])); if (endPosition.CompareTo(cpc.Cell.ContentStart) <= 0) { // remember that at least one cell in this row starts after the range's end. // in this case it is safe to break after this whole row is processed. // Note: can not break right away because cells in arrayFsCell come not // necessarily in backing store (cp) order. passedEndPosition = true; } else { if (startPosition.CompareTo(cpc.Cell.ContentEnd) <= 0) { Geometry cellGeometry = cpc.GetTightBoundingGeometryFromTextPositions(startPosition, endPosition, visibleRect); CaretElement.AddGeometry(ref geometry, cellGeometry); } } } } } if (geometry != null) { geometry = Geometry.Combine(geometry, Visual.Clip, GeometryCombineMode.Intersect, null); } return geometry; } ////// Returns the para client for a cell, given a position /// /// Position to check for cell. ////// Cell para client /// ////// Critical - as this calls Critical function QueryRowDetails. /// Safe - as this can't be used to pass arbitrary parameters to the Critical /// function. All In parameters passed in are generated by safe methods. /// [SecurityCritical, SecurityTreatAsSafe] internal CellParaClient GetCellParaClientFromPosition(ITextPointer position) { Debug.Assert( TableParagraph.Table != null && CalculatedColumns != null ); PTS.FSTABLEROWDESCRIPTION[] arrayTableRowDesc; PTS.FSKUPDATE fskupdTable; PTS.FSRECT rectTable; if (QueryTableDetails(out arrayTableRowDesc, out fskupdTable, out rectTable)) { for (int iR = 0; iR < arrayTableRowDesc.Length; ++iR) { PTS.FSKUPDATE[] arrayUpdate; IntPtr[] arrayFsCell; PTS.FSTABLEKCELLMERGE[] arrayTableCellMerge; QueryRowDetails( arrayTableRowDesc[iR].pfstablerow, out arrayFsCell, out arrayUpdate, out arrayTableCellMerge); for (int iC = 0; iC < arrayFsCell.Length; ++iC) { if (arrayFsCell[iC] == IntPtr.Zero) { // paginated case - cell may be null continue; } CellParaClient cpc = (CellParaClient)(PtsContext.HandleToObject(arrayFsCell[iC])); if(position.CompareTo(cpc.Cell.ContentStart) >= 0 && position.CompareTo(cpc.Cell.ContentEnd) <= 0) { return cpc; } } } } return (null); } ////// Returns an appropriate found cell /// /// Suggested X position for cell to find. /// RowGroupIndex to be above. /// RowIndex to be above. ////// Cell Para Client of cell /// ////// Critical - as this calls Critical function QueryRowDetails. /// Safe - as this can't be used to pass arbitrary parameters to the Critical /// function. All In parameters passed in are generated by safe methods. /// [SecurityCritical, SecurityTreatAsSafe] internal CellParaClient GetCellAbove(double suggestedX, int rowGroupIndex, int rowIndex) { Debug.Assert( TableParagraph.Table != null && CalculatedColumns != null ); PTS.FSTABLEROWDESCRIPTION[] arrayTableRowDesc; PTS.FSKUPDATE fskupdTable; PTS.FSRECT rectTable; int suggestedU = TextDpi.ToTextDpi(suggestedX); if (QueryTableDetails(out arrayTableRowDesc, out fskupdTable, out rectTable)) { MbpInfo mbp = MbpInfo.FromElement(TableParagraph.Element); for (int iR = arrayTableRowDesc.Length - 1; iR >= 0; --iR) { PTS.FSKUPDATE[] arrayUpdate; IntPtr[] arrayFsCell; PTS.FSTABLEKCELLMERGE[] arrayTableCellMerge; QueryRowDetails( arrayTableRowDesc[iR].pfstablerow, out arrayFsCell, out arrayUpdate, out arrayTableCellMerge); CellParaClient cpcClosest = null; int iClosestDistance = int.MaxValue; for (int iC = arrayFsCell.Length - 1; iC >= 0; --iC) { if (arrayFsCell[iC] == IntPtr.Zero) { // paginated case - cell may be null continue; } CellParaClient cpc = (CellParaClient)(PtsContext.HandleToObject(arrayFsCell[iC])); // Check if bottom row of cell is less than row index for moving up. int cellBottomIndex = cpc.Cell.RowIndex + cpc.Cell.RowSpan - 1; if( (cellBottomIndex < rowIndex && cpc.Cell.RowGroupIndex == rowGroupIndex) || (cpc.Cell.RowGroupIndex < rowGroupIndex) ) { if(suggestedU >= cpc.Rect.u && suggestedU <= (cpc.Rect.u + cpc.Rect.du)) { return cpc; } // Else record it, and return it if we find nothing better for this row. int iDistance = Math.Abs((cpc.Rect.u + cpc.Rect.du / 2) - suggestedU); if(iDistance < iClosestDistance) { iClosestDistance = iDistance; cpcClosest = cpc; } } } if(cpcClosest != null) { return cpcClosest; } } } return (null); } ////// Returns an appropriate found cell /// /// Suggested X position for cell to find. /// RowGroupIndex to be above. /// RowIndex to be below. ////// Cell Para Client of cell /// ////// Critical - as this calls Critical function QueryRowDetails. /// Safe - as this can't be used to pass arbitrary parameters to the Critical /// function. All In parameters passed in are generated by safe methods. /// [SecurityCritical, SecurityTreatAsSafe] internal CellParaClient GetCellBelow(double suggestedX, int rowGroupIndex, int rowIndex) { Debug.Assert( TableParagraph.Table != null && CalculatedColumns != null ); PTS.FSTABLEROWDESCRIPTION[] arrayTableRowDesc; PTS.FSKUPDATE fskupdTable; PTS.FSRECT rectTable; int suggestedU = TextDpi.ToTextDpi(suggestedX); if (QueryTableDetails(out arrayTableRowDesc, out fskupdTable, out rectTable)) { for (int iR = 0; iR < arrayTableRowDesc.Length; ++iR) { PTS.FSKUPDATE[] arrayUpdate; IntPtr[] arrayFsCell; PTS.FSTABLEKCELLMERGE[] arrayTableCellMerge; QueryRowDetails( arrayTableRowDesc[iR].pfstablerow, out arrayFsCell, out arrayUpdate, out arrayTableCellMerge); CellParaClient cpcClosest = null; int iClosestDistance = int.MaxValue; for (int iC = arrayFsCell.Length - 1; iC >= 0; --iC) { if (arrayFsCell[iC] == IntPtr.Zero) { // paginated case - cell may be null continue; } CellParaClient cpc = (CellParaClient)(PtsContext.HandleToObject(arrayFsCell[iC])); if( (cpc.Cell.RowIndex > rowIndex && cpc.Cell.RowGroupIndex == rowGroupIndex) || (cpc.Cell.RowGroupIndex > rowGroupIndex) ) { if(suggestedU >= cpc.Rect.u && suggestedU <= (cpc.Rect.u + cpc.Rect.du)) { return cpc; } // Else record it and report it if we find nothing better in this row. int iDistance = Math.Abs((cpc.Rect.u + cpc.Rect.du / 2) - suggestedU); if(iDistance < iClosestDistance) { iClosestDistance = iDistance; cpcClosest = cpc; } } } if(cpcClosest != null) { return cpcClosest; } } } return (null); } ////// Returns a cellinfo structure for a given point /// /// Point to check for cell. ////// CellInfo class /// internal CellInfo GetCellInfoFromPoint(Point point) { CellParaClient cpc = GetCellParaClientFromPoint(point, true); if(cpc != null) { return new CellInfo(this, cpc); } return null; } ////// Returns a rect corresponding to a row end position. /// /// Position to check against. ////// Rect, area /// ////// Critical - as this uses PTS.FSTABLEROWDESCRIPTION, a structure with /// overlapping fields (to match a union in PTS) /// Safe - returned information is safe to disclose. /// [SecurityCritical, SecurityTreatAsSafe] internal Rect GetRectangleFromRowEndPosition(ITextPointer position) { Debug.Assert( TableParagraph.Table != null && CalculatedColumns != null ); PTS.FSTABLEROWDESCRIPTION[] arrayTableRowDesc; PTS.FSKUPDATE fskupdTable; PTS.FSRECT rectTable; if (QueryTableDetails(out arrayTableRowDesc, out fskupdTable, out rectTable)) { int vrCur = GetTableOffsetFirstRowTop() + rectTable.v; for (int iR = 0; iR < arrayTableRowDesc.Length; ++iR) { TableRow row = ((RowParagraph)(PtsContext.HandleToObject(arrayTableRowDesc[iR].fsnmRow))).Row; if(((TextPointer)position).CompareTo(row.ContentEnd) == 0) { return new Rect( TextDpi.FromTextDpi(rectTable.u + rectTable.du), TextDpi.FromTextDpi(vrCur), 1.0, TextDpi.FromTextDpi(arrayTableRowDesc[iR].u.dvrRow)); } vrCur += arrayTableRowDesc[iR].u.dvrRow; } } // Valid row end position should have been specified. Debug.Assert(false); return System.Windows.Rect.Empty; } ////// AutofitTable /// /// Direction of Track /// Available space /// Table width after autofit. It is the same for all rows ////// durAvailableSpace is invalid due to the unfinished table/footer work in /// TableSection.GetPageDimensions (see notes in that method for more info). /// internal void AutofitTable( uint fswdirTrack, // IN: direction of Track int durAvailableSpace, // IN: available space (?) out int durTableWidth) // OUT: Table width after autofit. It is the same for all rows :) { Debug.Assert(Table != null); double availableSpace = TextDpi.FromTextDpi(durAvailableSpace); double tableWidth; Autofit(availableSpace, out tableWidth); durTableWidth = TextDpi.ToTextDpi(tableWidth); } ////// UpdAutofitTable /// /// Direction of Track /// Available space /// Table width after autofit /// Column width changes ////// durAvailableSpace is invalid due to the unfinished table/footer work in /// TableSection.GetPageDimensions (see notes in that method for more info). /// internal void UpdAutofitTable( uint fswdirTrack, // IN: direction of Track int durAvailableSpace, // IN: available space (?) out int durTableWidth, // OUT: Table width after autofit. out int fNoChangeInCellWidths) // OUT: { Debug.Assert(Table != null); double availableSpace = TextDpi.FromTextDpi(durAvailableSpace); double tableWidth; fNoChangeInCellWidths = Autofit(availableSpace, out tableWidth); durTableWidth = TextDpi.ToTextDpi(tableWidth); } ////// Performs autofit calculations /// /// Available width for table /// Resulting table width ////// internal int Autofit(double availableWidth, out double tableWidth) { int ret = PTS.True; ValidateCalculatedColumns(); if (!DoubleUtil.AreClose(availableWidth, _previousAutofitWidth)) { ret = ValidateTableWidths(availableWidth, out tableWidth); } else { tableWidth = _previousTableWidth; } _previousAutofitWidth = availableWidth; _previousTableWidth = tableWidth; return ret; } #endregion Internal Methods //----------------------------------------------------- // // Internal Properties // //----------------------------------------------------- #region Internal Properties ///PTS.True when no changes in columns width detected, ///PTS.False otherwise ////// Table paragraph created the client /// internal TableParagraph TableParagraph { get { return (TableParagraph)_paragraph; } } ////// Table object proper /// internal Table Table { get { return TableParagraph.Table; } } ////// CurrentPage Desired Width /// internal double TableDesiredWidth { get { // Use current format context's page width to limit cell spacing double durRet = 0; CalculatedColumn[] cols = CalculatedColumns; for (int i = 0; i < cols.Length; i++) { durRet += cols[i].DurWidth + Table.InternalCellSpacing; } return durRet; } } ////// CalculatedColumns /// internal CalculatedColumn[] CalculatedColumns { get { return (_calculatedColumns); } } ////// Autofit width - what width we used when autofitting the table. /// internal double AutofitWidth { get { return _previousAutofitWidth; } } // ------------------------------------------------------------------ // Is this the first chunk of paginated content. // ----------------------------------------------------------------- internal override bool IsFirstChunk { get { return _isFirstChunk; } } private bool _isFirstChunk; // ------------------------------------------------------------------ // Is this the last chunk of paginated content. // ------------------------------------------------------------------ internal override bool IsLastChunk { get { return _isLastChunk; } } private bool _isLastChunk; #endregion Internal Properties //----------------------------------------------------- // // Private Methods // //------------------------------------------------------ #region Private Methods // ----------------------------------------------------------------- // Update information about first/last chunk. // ----------------------------------------------------------------- ////// Queries PTS and saves information about this paginated chunk /// /// Table Row descriptions ////// Critical - as this calls Critical function PTS.FsQueryTableObjRowDetails /// [SecurityCritical] private unsafe void UpdateChunkInfo(PTS.FSTABLEROWDESCRIPTION[] arrayTableRowDesc) { PTS.FSTABLEROWDETAILS tableRowDetails; RowParagraph rowParagraph = (RowParagraph)(PtsContext.HandleToObject(arrayTableRowDesc[0].fsnmRow)); TableRow row = rowParagraph.Row; PTS.Validate(PTS.FsQueryTableObjRowDetails( PtsContext.Context, arrayTableRowDesc[0].pfstablerow, out tableRowDetails)); _isFirstChunk = (tableRowDetails.fskboundaryAbove == PTS.FSKTABLEROWBOUNDARY.fsktablerowboundaryOuter) && (row.Index == 0) && Table.IsFirstNonEmptyRowGroup(row.RowGroup.Index); row = ((RowParagraph)(PtsContext.HandleToObject(arrayTableRowDesc[arrayTableRowDesc.Length - 1].fsnmRow))).Row; PTS.Validate(PTS.FsQueryTableObjRowDetails( PtsContext.Context, arrayTableRowDesc[arrayTableRowDesc.Length - 1].pfstablerow, out tableRowDetails)); _isLastChunk = (tableRowDetails.fskboundaryBelow == PTS.FSKTABLEROWBOUNDARY.fsktablerowboundaryOuter) && (row.Index == row.RowGroup.Rows.Count - 1) && Table.IsLastNonEmptyRowGroup(row.RowGroup.Index); } ////// Queries PTS and returns table detailed structure / layout information about rows. /// /// Storage for table rows information /// Result of update /// Rect of the table ////// true when table has content;false otherwise/// Critical - as this calls Critical functions PTS.FsQueryTableObjDetails, /// PTS.FsQueryTableObjTableProperDetails and PTS.FsQueryTableObjRowList. /// Safe - returned information is safe to disclose. /// [SecurityCritical, SecurityTreatAsSafe] private unsafe bool QueryTableDetails( out PTS.FSTABLEROWDESCRIPTION[] arrayTableRowDesc, out PTS.FSKUPDATE fskupdTable, out PTS.FSRECT rect) { PTS.FSTABLEOBJDETAILS tableObjDetails; PTS.FSTABLEDETAILS tableDetails; PTS.Validate(PTS.FsQueryTableObjDetails( PtsContext.Context, _paraHandle.Value, out tableObjDetails)); Debug.Assert(TableParagraph == (TableParagraph)(PtsContext.HandleToObject(tableObjDetails.fsnmTable))); fskupdTable = tableObjDetails.fskupdTableProper; rect = tableObjDetails.fsrcTableObj; PTS.Validate(PTS.FsQueryTableObjTableProperDetails( PtsContext.Context, tableObjDetails.pfstableProper, out tableDetails)); if (tableDetails.cRows == 0) { // table has no rows (thus no content) arrayTableRowDesc = null; return (false); } arrayTableRowDesc = new PTS.FSTABLEROWDESCRIPTION[tableDetails.cRows]; fixed (PTS.FSTABLEROWDESCRIPTION * rgTableRowDesc = arrayTableRowDesc) { int cRowsActual; PTS.Validate(PTS.FsQueryTableObjRowList( PtsContext.Context, tableObjDetails.pfstableProper, tableDetails.cRows, rgTableRowDesc, out cRowsActual)); Debug.Assert(tableDetails.cRows == cRowsActual); } return (true); } ////// Queries PTS and returns detailed structure / layout information about cells. /// /// Row to query /// Names of the cells /// Update state of the cells /// Merge state of the cells ////// Critical - as this calls Critical functions PTS.FsQueryTableObjRowDetails /// and PTS.FsQueryTableObjCellList. /// [SecurityCritical] private unsafe void QueryRowDetails( IntPtr pfstablerow, out IntPtr[] arrayFsCell, out PTS.FSKUPDATE[] arrayUpdate, out PTS.FSTABLEKCELLMERGE[] arrayTableCellMerge) { PTS.FSTABLEROWDETAILS tableRowDetails; PTS.Validate(PTS.FsQueryTableObjRowDetails( PtsContext.Context, pfstablerow, out tableRowDetails)); arrayUpdate = new PTS.FSKUPDATE[tableRowDetails.cCells]; arrayFsCell = new IntPtr[tableRowDetails.cCells]; arrayTableCellMerge = new PTS.FSTABLEKCELLMERGE[tableRowDetails.cCells]; if (tableRowDetails.cCells > 0) { fixed (PTS.FSKUPDATE * rgUpdate = arrayUpdate) { fixed (IntPtr * rgFsCell = arrayFsCell) { fixed (PTS.FSTABLEKCELLMERGE * rgTableCellMerge = arrayTableCellMerge) { int cCellsActual; PTS.Validate(PTS.FsQueryTableObjCellList( PtsContext.Context, pfstablerow, tableRowDetails.cCells, rgUpdate, rgFsCell, rgTableCellMerge, out cCellsActual)); Debug.Assert(tableRowDetails.cCells == cCellsActual); } } } } } ////// SynchronizeRowVisualsCollection. /// /// Collection to synchromize. /// Index to start syncronization from. /// Row that still alive. private void SynchronizeRowVisualsCollection( VisualCollection rowVisualsCollection, int firstIndex, TableRow row) { // quick check to see if collection is already synchronized if (((RowVisual)(rowVisualsCollection[firstIndex])).Row != row) { int lastIndex = firstIndex; int count = rowVisualsCollection.Count; while (++lastIndex < count) { RowVisual rowVisual = (RowVisual)(rowVisualsCollection[lastIndex]); if (rowVisual.Row == row) break; } rowVisualsCollection.RemoveRange(firstIndex, lastIndex - firstIndex); } } ////// SynchronizeCellVisualsCollection. /// /// Collection to synchromize. /// Index to start syncronization from. /// Visual that still alive. private void SynchronizeCellVisualsCollection( VisualCollection cellVisualsCollection, int firstIndex, Visual visual) { // quick check to see if collection is already synchronized if ((cellVisualsCollection[firstIndex]) != visual) { int lastIndex = firstIndex; int count = cellVisualsCollection.Count; while (++lastIndex < count) { if (cellVisualsCollection[lastIndex] == visual) break; } cellVisualsCollection.RemoveRange(firstIndex, lastIndex - firstIndex); } } ////// Validates the row's visual. /// /// Row Visual to validate. /// Row to query. /// Row's change state. /// Columns offsets information. ////// Critical - as this calls Critical function QueryRowDetails and passes /// pfstablerow directly. /// [SecurityCritical] private void ValidateRowVisualSimple( RowVisual rowVisual, IntPtr pfstablerow, PTS.FSKUPDATE fskupdRow, CalculatedColumn[] calculatedColumns) { PTS.FSKUPDATE[] arrayUpdate; IntPtr[] arrayFsCell; PTS.FSTABLEKCELLMERGE[] arrayTableCellMerge; VisualCollection cellVisualsCollection; int sourceIndex; QueryRowDetails( pfstablerow, out arrayFsCell, out arrayUpdate, out arrayTableCellMerge); cellVisualsCollection = rowVisual.Children; sourceIndex = 0; for (int iC = 0; iC < arrayFsCell.Length; ++iC) { CellParaClient cellParaClient; double urCellOffset; PTS.FSKUPDATE fskupdCell; if ( // paginated case - cell may be null arrayFsCell[iC] == IntPtr.Zero // exclude hanging cells || arrayTableCellMerge[iC] == PTS.FSTABLEKCELLMERGE.fskcellmergeMiddle || arrayTableCellMerge[iC] == PTS.FSTABLEKCELLMERGE.fskcellmergeLast ) { continue; } fskupdCell = (arrayUpdate[iC] != PTS.FSKUPDATE.fskupdInherited) ? arrayUpdate[iC] : fskupdRow; if (fskupdCell != PTS.FSKUPDATE.fskupdNoChange) { cellParaClient = (CellParaClient)(PtsContext.HandleToObject(arrayFsCell[iC])); urCellOffset = calculatedColumns[cellParaClient.ColumnIndex].UrOffset; cellParaClient.ValidateVisual(); if ( fskupdCell == PTS.FSKUPDATE.fskupdNew // PTS bug is a suspect here - this is a temp workaround: || VisualTreeHelper.GetParent(cellParaClient.Visual) == null ) { Visual currentParent = VisualTreeHelper.GetParent(cellParaClient.Visual) as Visual; if(currentParent != null) { ContainerVisual parent = currentParent as ContainerVisual; Invariant.Assert(parent != null, "parent should always derives from ContainerVisual"); parent.Children.Remove(cellParaClient.Visual); } cellVisualsCollection.Insert(sourceIndex, cellParaClient.Visual); } else { Debug.Assert( cellParaClient.Visual != null // If the check below fails, then PTS cheats by reporting "ChangInside" for // a cell that in fact was re-Formatted. && VisualTreeHelper.GetParent(cellParaClient.Visual) != null ); SynchronizeCellVisualsCollection(cellVisualsCollection, sourceIndex, cellParaClient.Visual); } } sourceIndex++; } if (cellVisualsCollection.Count > sourceIndex) { cellVisualsCollection.RemoveRange( sourceIndex, cellVisualsCollection.Count - sourceIndex); } #if DEBUGDEBUG for (int iC = 0, sourceIndex = 0; iC < arrayFsCell.Length; ++iC) { if ( arrayFsCell[iC] != IntPtr.Zero && arrayTableCellMerge[iC] != PTS.FSTABLEKCELLMERGE.fskcellmergeMiddle && arrayTableCellMerge[iC] != PTS.FSTABLEKCELLMERGE.fskcellmergeLast ) { CellParaClient cellParaClient = (CellParaClient)(PtsContext.HandleToObject(arrayFsCell[iC])); Debug.Assert(rowVisual.Children.IndexOf(cellParaClient.Visual) == sourceIndex); sourceIndex++; } } #endif // DEBUGDEBUG } ////// Validates the row's visual. /// /// Row Visual to validate. /// Row to query. /// Number of columns in the table. /// Row's change state. /// Columns offsets information. ////// /// ////// Critical - as this calls Critical function QueryRowDetails and passes it /// pfstablerow directly. /// [SecurityCritical] private void ValidateRowVisualComplex( RowVisual rowVisual, IntPtr pfstablerow, int tableColumnCount, PTS.FSKUPDATE fskupdRow, CalculatedColumn[] calculatedColumns) { PTS.FSKUPDATE[] arrayUpdate; IntPtr[] arrayFsCell; PTS.FSTABLEKCELLMERGE[] arrayTableCellMerge; CellParaClientEntry[] arrayCellParaClients; VisualCollection cellVisualsCollection; int sourceIndex; QueryRowDetails( pfstablerow, out arrayFsCell, out arrayUpdate, out arrayTableCellMerge); // arrayFsCell lists cells in order different from one we want to maintain in visual collection. // before going to update visual collection it is necessary to reorder ascending by cell's column index. // knowing the following facts: // * total number of columns the row holds (including row spanned cells from previous rows) less or equal to tableColumnCount; // * total number of cells (including row spanned cells from previous rows) is less or equal to tableColumnCount; // * cells do not overlap - no two cells have the same column index; // * cells' column indices fall into the range [0, tableColumnCount - 1]; // it is possible to write custom and optimized sorting routine: // * iterate through arrayFsCell; // * for each item record its CellParaClient value into arrayCellParaClients[CellParaClient.ColumnIndex]; // once complete, arrayCellParaClients will contain CellParaClients in correct order. some entries however will be null // due to potential column spanning of cells. arrayCellParaClients = new CellParaClientEntry[tableColumnCount]; for (int iC = 0; iC < arrayFsCell.Length; ++iC) { CellParaClient cellParaClient; PTS.FSKUPDATE fskupdCell; int columnIndex; if (arrayFsCell[iC] == IntPtr.Zero) { // paginated case - cell may be null continue; } fskupdCell = (arrayUpdate[iC] != PTS.FSKUPDATE.fskupdInherited) ? arrayUpdate[iC] : fskupdRow; cellParaClient = (CellParaClient)(PtsContext.HandleToObject(arrayFsCell[iC])); columnIndex = cellParaClient.ColumnIndex; arrayCellParaClients[columnIndex].cellParaClient = cellParaClient; arrayCellParaClients[columnIndex].fskupdCell = fskupdCell; } cellVisualsCollection = rowVisual.Children; sourceIndex = 0; for (int columnIndex = 0; columnIndex < arrayCellParaClients.Length; ++columnIndex) { CellParaClient cellParaClient; double urCellOffset; PTS.FSKUPDATE fskupdCell; cellParaClient = arrayCellParaClients[columnIndex].cellParaClient; if (cellParaClient == null) { // paginated case - cell may be null continue; } fskupdCell = arrayCellParaClients[columnIndex].fskupdCell; if (fskupdCell != PTS.FSKUPDATE.fskupdNoChange) { urCellOffset = calculatedColumns[columnIndex].UrOffset; cellParaClient.ValidateVisual(); if (fskupdCell == PTS.FSKUPDATE.fskupdNew) { cellVisualsCollection.Insert(sourceIndex, cellParaClient.Visual); } else { Debug.Assert( cellParaClient.Visual != null // If the check below fails, then PTS cheats by reporting "ChangInside" for // a cell that in fact was re-Formatted. && VisualTreeHelper.GetParent(cellParaClient.Visual) != null ); SynchronizeCellVisualsCollection(cellVisualsCollection, sourceIndex, cellParaClient.Visual); } } sourceIndex++; } if (cellVisualsCollection.Count > sourceIndex) { cellVisualsCollection.RemoveRange( sourceIndex, cellVisualsCollection.Count - sourceIndex); } #if DEBUGDEBUG for (int columnIndex = 0, sourceIndex = 0; columnIndex < arrayCellParaClients.Length; ++columnIndex) { CellParaClient cellParaClient = arrayCellParaClients[columnIndex].cellParaClient; if (cellParaClient != null) { Debug.Assert(rowVisual.Children.IndexOf(cellParaClient.Visual) == sourceIndex); sourceIndex++; } } #endif // DEBUGDEBUG } ////// Draws the backgrounds for all columns in the table /// /// Drawing Context. /// Content rect of table. private void DrawColumnBackgrounds(DrawingContext dc, Rect tableContentRect) { double columnStart = tableContentRect.X; Rect colRect = tableContentRect; Brush columnBackgroundBrush; if (ThisFlowDirection != PageFlowDirection) { for (int iC = CalculatedColumns.Length-1; iC >= 0; iC--) { columnBackgroundBrush = (iC < Table.Columns.Count) ? Table.Columns[iC].Background : null; colRect.Width = CalculatedColumns[iC].DurWidth + Table.InternalCellSpacing; if (columnBackgroundBrush != null) { colRect.X = columnStart; dc.DrawRectangle(columnBackgroundBrush, null, colRect); } columnStart += colRect.Width; } } else { for (int iC = 0; iC < CalculatedColumns.Length; iC++) { columnBackgroundBrush = (iC < Table.Columns.Count) ? Table.Columns[iC].Background : null; colRect.Width = CalculatedColumns[iC].DurWidth + Table.InternalCellSpacing; if (columnBackgroundBrush != null) { colRect.X = columnStart; dc.DrawRectangle(columnBackgroundBrush, null, colRect); } columnStart += colRect.Width; } } } ////// Calculates actual row height, subtracting what was previously reported as dvrBeforeTopRow and dvrAfterBottomRow /// private double GetActualRowHeight(PTS.FSTABLEROWDESCRIPTION[] arrayTableRowDesc, int rowIndex, MbpInfo mbpInfo) { int dvAdjustment = 0; if(IsFirstChunk && rowIndex == 0) { dvAdjustment = -mbpInfo.BPTop; } if(IsLastChunk && rowIndex == arrayTableRowDesc.Length - 1) { dvAdjustment = -mbpInfo.BPBottom; } return TextDpi.FromTextDpi(arrayTableRowDesc[rowIndex].u.dvrRow + dvAdjustment); } ////// Draws TableRowGroup backgrounds /// /// DrawingContext to draw in. /// Row descriptions. /// External rect for this chunk of table. /// Margin/Border/Padding info for table. ////// /// ////// Critical - as this uses unmanaged data structures with a union /// [SecurityCritical] private void DrawRowGroupBackgrounds(DrawingContext dc, PTS.FSTABLEROWDESCRIPTION[] arrayTableRowDesc, Rect tableContentRect, MbpInfo mbpInfo) { double rowGroupTop = tableContentRect.Y; double rowGroupHeight = 0; Rect rowRect = tableContentRect; Brush rowGroupBackgroundBrush; if(arrayTableRowDesc.Length > 0) { TableRow row = ((RowParagraph)(PtsContext.HandleToObject(arrayTableRowDesc[0].fsnmRow))).Row; TableRowGroup tableRowGroup = row.RowGroup; for (int iR = 0; iR < arrayTableRowDesc.Length; ++iR) { row = ((RowParagraph)(PtsContext.HandleToObject(arrayTableRowDesc[iR].fsnmRow))).Row; if (tableRowGroup != row.RowGroup) { rowGroupBackgroundBrush = (Brush)tableRowGroup.GetValue(TextElement.BackgroundProperty); if (rowGroupBackgroundBrush != null) { rowRect.Y = rowGroupTop; rowRect.Height = rowGroupHeight; dc.DrawRectangle(rowGroupBackgroundBrush, null, rowRect); } rowGroupTop += rowGroupHeight; tableRowGroup = row.RowGroup; rowGroupHeight = GetActualRowHeight(arrayTableRowDesc, iR, mbpInfo); } else { rowGroupHeight += GetActualRowHeight(arrayTableRowDesc, iR, mbpInfo); } } rowGroupBackgroundBrush = (Brush)tableRowGroup.GetValue(TextElement.BackgroundProperty); if (rowGroupBackgroundBrush != null) { rowRect.Y = rowGroupTop; rowRect.Height = rowGroupHeight; dc.DrawRectangle(rowGroupBackgroundBrush, null, rowRect); } } } ////// Draws TableRow backgrounds /// /// DrawingContext to draw in. /// Row descriptions. /// External rect for this chunk of table. /// Margin/Border/Padding info for table. ////// /// ////// Critical - as this uses unmanaged data structures with a union /// [SecurityCritical] private void DrawRowBackgrounds(DrawingContext dc, PTS.FSTABLEROWDESCRIPTION[] arrayTableRowDesc, Rect tableContentRect, MbpInfo mbpInfo) { double rowTop = tableContentRect.Y; Rect rowRect = tableContentRect; for (int iR = 0; iR < arrayTableRowDesc.Length; ++iR) { TableRow row = ((RowParagraph)(PtsContext.HandleToObject(arrayTableRowDesc[iR].fsnmRow))).Row; Brush rowBackgroundBrush = (Brush)row.GetValue(TextElement.BackgroundProperty); rowRect.Y = rowTop; rowRect.Height = GetActualRowHeight(arrayTableRowDesc, iR, mbpInfo); if (rowBackgroundBrush != null) { dc.DrawRectangle(rowBackgroundBrush, null, rowRect); } rowTop += rowRect.Height; // Adjust for top of next row } } ////// ValidateCalculatedColumns /// ////// Side effect: _durMinWidth and _durMaxWidth are also updated. /// private void ValidateCalculatedColumns() { double totalPadding; int columns = Table.ColumnCount; if (_calculatedColumns == null) { _calculatedColumns = new CalculatedColumn[columns]; } else if (_calculatedColumns.Length != columns) { CalculatedColumn[] newCalculatedColumns = new CalculatedColumn[columns]; Array.Copy( _calculatedColumns, newCalculatedColumns, Math.Min(_calculatedColumns.Length, columns)); _calculatedColumns = newCalculatedColumns; } if (_calculatedColumns.Length > 0) { int i = 0; while (i < _calculatedColumns.Length && i < Table.Columns.Count) { _calculatedColumns[i].UserWidth = Table.Columns[i].Width; i++; } while (i < _calculatedColumns.Length) { _calculatedColumns[i].UserWidth = TableColumn.DefaultWidth; i++; } } _durMinWidth = _durMaxWidth = 0; for (int i = 0; i < _calculatedColumns.Length; ++i) { switch (_calculatedColumns[i].UserWidth.GridUnitType) { case (GridUnitType.Auto): // _calculatedColumns[i].ValidateAuto(1.0, 10e5); break; case (GridUnitType.Star): _calculatedColumns[i].ValidateAuto(1.0, 10e5); break; case (GridUnitType.Pixel): _calculatedColumns[i].ValidateAuto( _calculatedColumns[i].UserWidth.Value, _calculatedColumns[i].UserWidth.Value); break; default: Debug.Assert(false, "Unsupported unit type"); break; } _durMinWidth += _calculatedColumns[i].DurMinWidth; _durMaxWidth += _calculatedColumns[i].DurMaxWidth; } // Use durAvailable as width limit for MBP, and MaxWidth as height limit since height values will not be used MbpInfo mbpInfo = MbpInfo.FromElement(Paragraph.Element); totalPadding = Table.InternalCellSpacing * Table.ColumnCount + mbpInfo.Margin.Left + mbpInfo.Border.Left + mbpInfo.Padding.Left + mbpInfo.Padding.Right + mbpInfo.Border.Right + mbpInfo.Margin.Right; _durMinWidth += totalPadding; _durMaxWidth += totalPadding; } ////// ValidateTableWidths /// /// Availble width /// Resulting table width ////// private int ValidateTableWidths(double durAvailableWidth, out double durTableWidth) { bool exactWidth = false; // assume that width should never be "exact" double durAbsoluteMin, durAbsoluteMax; // sum of min and max for all user absolute specified columns double durScalableMin; // sum if min for all user percent specified columns double durAutoMin, durAutoMax; // sum of min and max for all non-specified columns double mul, div, fP; double iPercent, iP; double durAbsoluteWidths; // calculated sum of widths for all absolute columns double durAbsoluteAndAutoWidths; // calculated sum of widths for all non-scalable columns double durAutoWidths; // calculated sum of widths for all non-specified columns bool fUseMax = false, fUseMin = false, fUseMaxMax = false; bool fUseUserMax = false, fUseUserMin = false, fUseUserMaxMax = false; bool fSubtract = false, fUserSubtract = false; double durTableUserWidth; double cellSpacing = Table.InternalCellSpacing; // Use durAvailable for MBP width limits and MaxWidth for height limits. Height values are not used in this calculation. MbpInfo mbpInfo = MbpInfo.FromElement(Paragraph.Element); double durTotalPadding = cellSpacing * Table.ColumnCount + TextDpi.FromTextDpi(mbpInfo.MBPLeft + mbpInfo.MBPRight); int ptsNoWidthChanges = PTS.True; // initialize output durTableWidth = 0; // // first calc sum of known percent, absolute and auto widths up // iPercent = 0; durAbsoluteMin = durAbsoluteMax = 0; durScalableMin = 0; durAutoMin = durAutoMax = 0; // // also keep track of the maxWidth/percent ratio necessary to // display the table with the columns at max width and the right // percent value // // Step 1 - Run through all of our calculated columns, and determine min and max widths (these widths do NOT include cell spacing) mul = 0; div = 1; for (int i = 0; i < _calculatedColumns.Length; ++i) { Debug.Assert(_calculatedColumns[i].UserWidth.GridUnitType == GridUnitType.Auto || _calculatedColumns[i].UserWidth.GridUnitType == GridUnitType.Star || _calculatedColumns[i].UserWidth.GridUnitType == GridUnitType.Pixel, "Unexpected GridUnitType"); if (_calculatedColumns[i].UserWidth.IsAuto) { Debug.Assert(0 <= _calculatedColumns[i].DurMinWidth && 0 <= _calculatedColumns[i].DurMaxWidth); durAutoMin += _calculatedColumns[i].DurMinWidth; durAutoMax += _calculatedColumns[i].DurMaxWidth; } else { if (_calculatedColumns[i].UserWidth.IsStar) { iP = _calculatedColumns[i].UserWidth.Value; if (iP < 0) { iP = 0; } // check for over 100% overflow if (iPercent + iP > 100) { iP = 100 - iPercent; iPercent = 100; _calculatedColumns[i].UserWidth = new GridLength(iP, GridUnitType.Star); } else { iPercent += iP; } if (iP == 0) { // non empty cell should get at least 1% iP = 1; } // remember maxWidth/percentage ratio if (_calculatedColumns[i].DurMaxWidth * div > iP * mul) { mul = _calculatedColumns[i].DurMaxWidth; div = iP; } durScalableMin += _calculatedColumns[i].DurMinWidth; } else { durAbsoluteMin += _calculatedColumns[i].DurMinWidth; durAbsoluteMax += _calculatedColumns[i].DurMaxWidth; } } } // iP is what left from the 100% // Step 2 - Calculate table user width - This width includes all padding and is a percentage of durAvailableWidth or _durMinWidth / _durMaxWidth, // all three values include padding. iP = 100 - iPercent; Debug.Assert(0 < iP || DoubleUtil.IsZero(iP)); if (exactWidth) { // have no choice - table's parent requires the width to be exact durTableUserWidth = durAvailableWidth; if (durTableUserWidth < _durMinWidth && DoubleUtil.AreClose(durTableUserWidth, _durMinWidth) == false) { durTableUserWidth = _durMinWidth; } } else if (0 < iPercent) // if user width is not given back calculate it from the maxWidth/percentage ratio { // check if the remaining user and normal columns are requiring bigger ratio if (0 < iP) { double tl = (durAbsoluteMax + durAutoMax) * div; double tr = iP * mul; // floating point (tl > tr) if (tl > tr && DoubleUtil.AreClose(tl, tr) == false) { mul = durAbsoluteMax + durAutoMax; div = iP; } } // // if there is percentage left or there are only percentage columns use the ratio // to back-calculate the table width // if ((0 < iP) || DoubleUtil.IsZero(durAbsoluteMax + durAutoMax)) { durTableUserWidth = mul * 100 / div + durTotalPadding; // floating point (durTableUserWidth > durAvailableWidth) if (durTableUserWidth > durAvailableWidth && DoubleUtil.AreClose(durTableUserWidth, durAvailableWidth) == false) { durTableUserWidth = durAvailableWidth; } } else { // otherwise use available width durTableUserWidth = durAvailableWidth; } // floating point (durTableUserWidth < _durMinWidth) if (durTableUserWidth < _durMinWidth && DoubleUtil.AreClose(durTableUserWidth, _durMinWidth) == false) { durTableUserWidth = _durMinWidth; } } else { // floating point (_durMaxWidth < durAvailableWidth) if (_durMaxWidth < durAvailableWidth && DoubleUtil.AreClose(_durMaxWidth, durAvailableWidth) == false) { // use max value if that smaller the parent size durTableUserWidth = _durMaxWidth; } // floating point (_durMinWidth > durAvailableWidth) else if (_durMinWidth > durAvailableWidth && DoubleUtil.AreClose(_durMinWidth, durAvailableWidth) == false) { // have to use min if that is bigger the parent durTableUserWidth = _durMinWidth; } else { // use parent between min and max durTableUserWidth = durAvailableWidth; } } // subtract padding width which contains border and cellspacing // floating point (durTableUserWidth >= durTotalPadding) // Step 3 - Remove padding - ( if (durTableUserWidth > durTotalPadding || DoubleUtil.AreClose(durTableUserWidth, durTotalPadding)) { durTableUserWidth -= durTotalPadding; } // floating point (0 < (durAutoMax + durAbsoluteMax)) // Step 4 - Calculate the space for 'auto' sized columns. if (0 < (durAutoMax + durAbsoluteMax) && DoubleUtil.IsZero(durAutoMax + durAbsoluteMax) == false) { // cache width remaining for normal and user columns over percent columns (durAbsoluteAndAutoWidths) durAbsoluteAndAutoWidths = iP * durTableUserWidth / 100; // floating point (durAbsoluteAndAutoWidths < (durAbsoluteMin + durAutoMin)) if (durAbsoluteAndAutoWidths < (durAbsoluteMin + durAutoMin) && DoubleUtil.AreClose(durAbsoluteAndAutoWidths, (durAbsoluteMin + durAutoMin)) == false) { durAbsoluteAndAutoWidths = durAbsoluteMin + durAutoMin; } // floating point (durAbsoluteAndAutoWidths > (durTableUserWidth - durScalableMin)) if (durAbsoluteAndAutoWidths > (durTableUserWidth - durScalableMin) && DoubleUtil.AreClose(durAbsoluteAndAutoWidths, (durTableUserWidth - durScalableMin)) == false) { durAbsoluteAndAutoWidths = durTableUserWidth - durScalableMin; } } else { // all widths is for percent columns durAbsoluteAndAutoWidths = 0; } // // distribute remaining width amongst normal and user columns // first try to use max width for user columns and normal columns // // floating point (0 < durAbsoluteMax) if (0 < durAbsoluteMax && DoubleUtil.IsZero(durAbsoluteMax) == false) { durAbsoluteWidths = durAbsoluteMax; if (durAbsoluteWidths > durAbsoluteAndAutoWidths && DoubleUtil.AreClose(durAbsoluteWidths, durAbsoluteAndAutoWidths) == false) { durAbsoluteWidths = durAbsoluteAndAutoWidths; } // floating point (0 < durAutoMax) if (0 < durAutoMax && DoubleUtil.IsZero(durAutoMax) == false) { durAutoWidths = durAutoMin; // floating point ((durAbsoluteWidths + durAutoWidths) <= durAbsoluteAndAutoWidths) if ((durAbsoluteWidths + durAutoWidths) < durAbsoluteAndAutoWidths || DoubleUtil.AreClose((durAbsoluteWidths + durAutoWidths), durAbsoluteAndAutoWidths)) { durAutoWidths = durAbsoluteAndAutoWidths - durAbsoluteWidths; } else { durAbsoluteWidths = durAbsoluteMin; // floating point ((durAbsoluteWidths + durAutoWidths) <= durAbsoluteAndAutoWidths) if ((durAbsoluteWidths + durAutoWidths) < durAbsoluteAndAutoWidths || DoubleUtil.AreClose((durAbsoluteWidths + durAutoWidths), durAbsoluteAndAutoWidths)) { durAbsoluteWidths = durAbsoluteAndAutoWidths - durAutoWidths; } } } else { durAutoWidths = 0; // floating point (durAbsoluteWidths < durAbsoluteAndAutoWidths) if (durAbsoluteWidths < durAbsoluteAndAutoWidths && DoubleUtil.AreClose(durAbsoluteWidths, durAbsoluteAndAutoWidths) == false) { durAbsoluteWidths = durAbsoluteAndAutoWidths; } } } else { durAbsoluteWidths = 0; // floating point (0 < durAutoMax) if (0 < durAutoMax && DoubleUtil.IsZero(durAutoMax) == false) { durAutoWidths = durAutoMin; // floating point (durAutoWidths < durAbsoluteAndAutoWidths) if (durAutoWidths < durAbsoluteAndAutoWidths && DoubleUtil.AreClose(durAutoWidths, durAbsoluteAndAutoWidths) == false) { durAutoWidths = durAbsoluteAndAutoWidths; } } else { durAutoWidths = 0; } } // floating point (durAutoWidths > durAutoMax) if (durAutoWidths > durAutoMax && DoubleUtil.AreClose(durAutoWidths, durAutoMax) == false) { fUseMaxMax = true; } // floating point (durAutoWidths == durAutoMax) else if (DoubleUtil.AreClose(durAutoWidths, durAutoMax)) { fUseMax = true; } // floating point (durAutoWidths == durAutoMin) else if (DoubleUtil.AreClose(durAutoWidths, durAutoMin)) { fUseMin = true; } // floating point (durAutoWidths < durAutoMax) else if (durAutoWidths < durAutoMax && DoubleUtil.AreClose(durAutoWidths, durAutoMax) == false) { fSubtract = true; } // floating point (durAbsoluteWidths > durAbsoluteMax) if (durAbsoluteWidths > durAbsoluteMax && DoubleUtil.AreClose(durAbsoluteWidths, durAbsoluteMax) == false) { fUseUserMaxMax = true; } // floating point (durAbsoluteWidths == durAbsoluteMax) else if (DoubleUtil.AreClose(durAbsoluteWidths, durAbsoluteMax)) { fUseUserMax = true; } // floating point (durAbsoluteWidths == durAbsoluteMin) else if (DoubleUtil.AreClose(durAbsoluteWidths, durAbsoluteMin)) { fUseUserMin = true; } // floating point (durAbsoluteWidths < durAbsoluteMax) else if (durAbsoluteWidths < durAbsoluteMax && DoubleUtil.AreClose(durAbsoluteWidths, durAbsoluteMax) == false) { fUserSubtract = true; } // calculate real percentage of percent columns in the table now using the final widths fP = (0 < durTableUserWidth) ? (100 * (durTableUserWidth - durAbsoluteWidths - durAutoWidths) / durTableUserWidth) : 0; // start with content area of table durTableWidth = TextDpi.FromTextDpi(mbpInfo.BPLeft); // // now go and calculate column widths by distributing the extra width over // the min width or subtracting the extra width from max // for (int i = 0; i < _calculatedColumns.Length; ++i) { if (_calculatedColumns[i].UserWidth.IsAuto) { // adjust normal column by adding to min or subtracting from max _calculatedColumns[i].DurWidth = fSubtract ? _calculatedColumns[i].DurMaxWidth - ((_calculatedColumns[i].DurMaxWidth - _calculatedColumns[i].DurMinWidth) * (durAutoMax - durAutoWidths) / (durAutoMax - durAutoMin)) : fUseMaxMax ? _calculatedColumns[i].DurMaxWidth + (_calculatedColumns[i].DurMaxWidth * (durAutoWidths - durAutoMax) / durAutoMax) : fUseMax ? _calculatedColumns[i].DurMaxWidth : fUseMin ? _calculatedColumns[i].DurMinWidth // floating point (0 < durAutoMax) : (0 < durAutoMax && DoubleUtil.IsZero(durAutoMax) == false) ? _calculatedColumns[i].DurMinWidth + (_calculatedColumns[i].DurMaxWidth * (durAutoWidths - durAutoMin) / durAutoMax) : 0; } else if (_calculatedColumns[i].UserWidth.IsStar) { // // if percent first calculate the width from the percent // durAbsoluteAndAutoWidths = (0 < iPercent) ? (durTableUserWidth * (fP * _calculatedColumns[i].UserWidth.Value / iPercent) / 100) : 0; // // make sure it is over the min width // durAbsoluteAndAutoWidths -= _calculatedColumns[i].DurMinWidth; // floating point (durAbsoluteAndAutoWidths < 0) if (durAbsoluteAndAutoWidths < 0 && DoubleUtil.IsZero(durAbsoluteAndAutoWidths) == false) { durAbsoluteAndAutoWidths = 0; } _calculatedColumns[i].DurWidth = (_calculatedColumns[i].DurMinWidth + durAbsoluteAndAutoWidths); } else { Debug.Assert(_calculatedColumns[i].UserWidth.IsAbsolute); // adjust user column by adding to min or subtracting from max _calculatedColumns[i].DurWidth = fUserSubtract // table needs to be (durAbsoluteMax - durAbsoluteWidths) shorter, so subtract width // from columns proportionally to (_calculatedColumns[i].DurMaxWidth - _calculatedColumns[i].DurMinWidth) ? _calculatedColumns[i].DurMaxWidth - ((_calculatedColumns[i].DurMaxWidth - _calculatedColumns[i].DurMinWidth) * (durAbsoluteMax - durAbsoluteWidths) / (durAbsoluteMax - durAbsoluteMin)) : fUseUserMaxMax ? _calculatedColumns[i].DurMaxWidth + (_calculatedColumns[i].DurMaxWidth * (durAbsoluteWidths - durAbsoluteMax) / durAbsoluteMax) : fUseUserMax ? _calculatedColumns[i].DurMaxWidth : fUseUserMin ? _calculatedColumns[i].DurMinWidth // floating point (0 < durAbsoluteMax) : (0 < durAbsoluteMax && DoubleUtil.IsZero(durAbsoluteMax) == false) ? _calculatedColumns[i].DurMinWidth + (_calculatedColumns[i].DurMaxWidth * (durAbsoluteWidths - durAbsoluteMin) / durAbsoluteMax) : 0; } Debug.Assert(_calculatedColumns[i].DurMinWidth <= _calculatedColumns[i].DurMaxWidth); // Cells themselves are 1/2 cell spacing inside column _calculatedColumns[i].UrOffset = durTableWidth + cellSpacing / 2.0; durTableWidth += _calculatedColumns[i].DurWidth + cellSpacing; // Advance to next column if (_calculatedColumns[i].PtsWidthChanged == PTS.True) { ptsNoWidthChanges = PTS.False; } } durTableWidth += mbpInfo.Margin.Left + TextDpi.FromTextDpi(mbpInfo.MBPRight); return (ptsNoWidthChanges); } ///PTS.True when no changes in columns width detected, ///PTS.False otherwise ////// Content rect - Rect from which rows/rowgroups/columns are calculated. /// private PTS.FSRECT GetTableContentRect(MbpInfo mbpInfo) { int calculatedBPTop = IsFirstChunk ? mbpInfo.BPTop : 0; int calculatedBPBottom = IsLastChunk ? mbpInfo.BPBottom : 0; return new PTS.FSRECT(_rect.u + mbpInfo.BPLeft, _rect.v + calculatedBPTop, Math.Max(_rect.du - (mbpInfo.BPRight + mbpInfo.BPLeft), 1), Math.Max(_rect.dv - calculatedBPBottom - calculatedBPTop, 1) ); } ////// Returns the offset from the table top to the first row of the table for the current chunk. /// private int GetTableOffsetFirstRowTop() { MbpInfo mbp = MbpInfo.FromElement(TableParagraph.Element); return IsFirstChunk ? mbp.BPTop : 0; } #endregion Private Methods //----------------------------------------------------- // // Private Classes / Structures // //------------------------------------------------------ #region Private Classes / Structures private struct CellParaClientEntry { internal CellParaClient cellParaClient; internal PTS.FSKUPDATE fskupdCell; } #endregion Private Classes / Structures //----------------------------------------------------- // // Private Data // //------------------------------------------------------ #region Private Data // Rectangle of column table para client is in. private PTS.FSRECT _columnRect; private CalculatedColumn[] _calculatedColumns; // layout related information per column private double _durMinWidth; // minimum width of the table private double _durMaxWidth; // maximum wudth of the table private double _previousAutofitWidth = 0.0; // Autofit width on previous layout pass. private double _previousTableWidth = 0.0; // Table width on previous layout pass. #endregion Private Data } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- SchemaMapping.cs
- StringValueConverter.cs
- JumpList.cs
- BrowserTree.cs
- ConnectivityStatus.cs
- ConfigurationLockCollection.cs
- objectquery_tresulttype.cs
- ComponentChangedEvent.cs
- PresentationAppDomainManager.cs
- GeneralTransform.cs
- MatchAttribute.cs
- NavigatingCancelEventArgs.cs
- WriteLineDesigner.xaml.cs
- UserControlCodeDomTreeGenerator.cs
- ObjectPropertyMapping.cs
- AdornerDecorator.cs
- ModelItem.cs
- ToggleButton.cs
- ObjectConverter.cs
- ArrayTypeMismatchException.cs
- StrongNameUtility.cs
- ZipIOFileItemStream.cs
- XmlUnspecifiedAttribute.cs
- RegexInterpreter.cs
- SspiNegotiationTokenAuthenticatorState.cs
- TypeConverterMarkupExtension.cs
- SafeCryptContextHandle.cs
- TranslateTransform.cs
- OverrideMode.cs
- UIElementIsland.cs
- Coordinator.cs
- FrameworkReadOnlyPropertyMetadata.cs
- TableProviderWrapper.cs
- IChannel.cs
- ProfessionalColors.cs
- ImageButton.cs
- TextChange.cs
- TaiwanLunisolarCalendar.cs
- SystemTcpStatistics.cs
- CapabilitiesAssignment.cs
- DocumentViewerBase.cs
- TextCollapsingProperties.cs
- Schema.cs
- SystemColors.cs
- StreamWriter.cs
- LoadRetryAsyncResult.cs
- IntSecurity.cs
- RequestCacheManager.cs
- EdgeProfileValidation.cs
- XMLUtil.cs
- BufferBuilder.cs
- DeferredReference.cs
- CallContext.cs
- DataGridViewCellValidatingEventArgs.cs
- GridViewDeleteEventArgs.cs
- PerfCounters.cs
- HTTPNotFoundHandler.cs
- SmiEventSink.cs
- CollectionEditorDialog.cs
- DataGridRowClipboardEventArgs.cs
- FixedSOMContainer.cs
- ReflectionServiceProvider.cs
- UnitySerializationHolder.cs
- QueryInterceptorAttribute.cs
- ObjectSet.cs
- SqlLiftIndependentRowExpressions.cs
- RoutingBehavior.cs
- Perspective.cs
- PageCache.cs
- EntityContainerEntitySetDefiningQuery.cs
- FlowNode.cs
- ClientType.cs
- StylusDevice.cs
- CollectionViewSource.cs
- ShaperBuffers.cs
- SectionInformation.cs
- RefreshEventArgs.cs
- LinkLabel.cs
- EntityContainerEntitySetDefiningQuery.cs
- HtmlInputPassword.cs
- SettingsPropertyValue.cs
- HtmlInputCheckBox.cs
- FormViewModeEventArgs.cs
- Peer.cs
- ProcessHostFactoryHelper.cs
- DLinqColumnProvider.cs
- EllipticalNodeOperations.cs
- IsolationInterop.cs
- HtmlTernaryTree.cs
- AutomationPeer.cs
- CollectionBuilder.cs
- ResourceAssociationSetEnd.cs
- ListViewItem.cs
- TextDecoration.cs
- ProviderException.cs
- HtmlButton.cs
- ProcessThreadCollection.cs
- Utils.cs
- DivideByZeroException.cs
- XsdDateTime.cs