Пример #1
0
        /// <summary>
        /// Given a range of pages, adds the pages to the existing row cache,
        /// adding new rows where necessary.
        /// </summary>
        /// <param name="startPage">The first page to add to the layout</param>
        /// <param name="count">The number of pages to add.</param>		
        private RowCacheChange AddPageRange(int startPage, int count)
        {
            if (!_isLayoutCompleted)
            {
                throw new InvalidOperationException(SR.Get(SRID.RowCacheCannotModifyNonExistentLayout));
            }

            int currentPage = startPage;
            int lastPage = startPage + count;

            int startRow = 0;
            int rowCount = 0;

            //First we check to see if startPage is such that we'd end up skipping
            //pages in the document -- that is, if the last page in our layout is currently
            //10 and start is 15, we need to fill in pages 11-14 as well.
            if (startPage > LastPageInCache + 1)
            {
                currentPage = LastPageInCache + 1;
            }            

            //Get the last row in the layout
            RowInfo lastRow = _rowCache[_rowCache.Count - 1];

            //Now we need to check to see if we can add any pages to this row 
            //without exceeding the current layout's pivot row width.

            //Get the size of the page to add
            Size pageSize = GetScaledPageSize(currentPage);

            //Get the pivot row
            RowInfo pivotRow = GetRow(_pivotRowIndex);

            bool lastRowUpdated = false;

            //Add new pages to the last row until we run out of pages or space.
            while (currentPage < lastPage && 
                lastRow.RowSize.Width + pageSize.Width <= pivotRow.RowSize.Width)
            {
                //Add the current page
                lastRow.AddPage(pageSize);
                currentPage++;

                //Get the size of the next page.
                pageSize = GetScaledPageSize(currentPage);

                //Note that we updated this row so we'll update the cache when we're done here.
                lastRowUpdated = true;
            }

            //If we actually made a change to the last row, then we need to update the row cache.
            if (lastRowUpdated)
            {
                startRow = _rowCache.Count - 1;
                //Update the last row
                UpdateRow(startRow, lastRow);
            }
            else
            {
                startRow = _rowCache.Count;
            }

            //Now we add more rows to the layout, if we have any pages left.
            while (currentPage < lastPage)
            {
                //Build a new row.
                RowInfo newRow = new RowInfo();              
                newRow.FirstPage = currentPage;              

                //Add pages until we either run out of pages or need to start a new row.               
                do
                {
                    //Get the size of the next page
                    pageSize = GetScaledPageSize(currentPage);

                    //Add it.
                    newRow.AddPage(pageSize);
                    currentPage++;

                } while (newRow.RowSize.Width + pageSize.Width <= pivotRow.RowSize.Width 
                    && currentPage < lastPage);

                //Add this new row to our cache
                AddRow(newRow);
                rowCount++;
            }

            return new RowCacheChange(startRow, rowCount);
        }
Пример #2
0
        /// <summary>
        /// Creates a "dynamic" row -- that is, given a maximum width for the row, it will fit as
        /// many pages on said row as possible.
        /// </summary>
        /// <param name="startPage">The first page to put on this row.</param>
        /// <param name="rowWidth">The requested width of this row.</param>
        /// <param name="createForward">Whether to create this row using the next N pages or the
        /// previous N.</param>
        /// <returns></returns>
        private RowInfo CreateDynamicRow(int startPage, double rowWidth, bool createForward)
        {
            if (startPage >= PageCache.PageCount)
            {
                throw new ArgumentOutOfRangeException("startPage");
            }            

            //Given a starting page for this row, and the specified
            //width for each row, figure out how many pages will fit on this row
            //and return the resulting RowInfo object.

            //Populate the struct with initial data.
            RowInfo newRow = new RowInfo();

            //Each row is guaranteed to have at least one page, even if it’s wider
            //than the allotted size, so we add it here.	        
            Size pageSize = GetScaledPageSize( startPage );
            newRow.AddPage(pageSize);

            //Keep adding pages until we either:
            // - run out of pages to add
            // - run out of space in the row
            for(;;)
            {                                
                if( createForward )
                {
                    //Grab the next page.
                    pageSize = GetScaledPageSize(startPage + newRow.PageCount);

                    //We’re out of pages, or out of space.
                    if (startPage + newRow.PageCount >= PageCache.PageCount ||
                        newRow.RowSize.Width + pageSize.Width > rowWidth )
                    {
                        break;
                    }
                }
                else
                {
                    //Grab the previous page.
                    pageSize = GetScaledPageSize( startPage - newRow.PageCount );                 

                    //We’re out of pages, or out of space.
                    if( startPage - newRow.PageCount < 0 ||
                        newRow.RowSize.Width + pageSize.Width > rowWidth )
                    {
                        break;
                    }
                }
                newRow.AddPage(pageSize);

                //If we've hit the hard upper limit for pages on a row then we're done with this row.
                if (newRow.PageCount == DocumentViewerConstants.MaximumMaxPagesAcross)
                {
                    break;
                }
            }

            if (!createForward)
            {
                newRow.FirstPage = startPage - (newRow.PageCount - 1);
            }
            else
            {
                newRow.FirstPage = startPage;
            }

            return newRow;
        }
Пример #3
0
        /// <summary>
        /// Creates a "fixed" row -- that is, a row with a specific number of columns on it.
        /// </summary>
        /// <param name="startPage">The first page to live on this row.</param>
        /// <param name="columns">The number of columns on this row.</param>
        /// <returns></returns>
        private RowInfo CreateFixedRow(int startPage, int columns)
        {
            if (startPage >= PageCache.PageCount)
            {
                throw new ArgumentOutOfRangeException("startPage");
            }

            if (columns < 1 )
            {
                throw new ArgumentOutOfRangeException("columns");
            }

            //Given a starting page for this row and the number of columns in the row
            //calculate the width & height and return the resulting RowInfo struct

            //Populate the struct with initial data
            RowInfo newRow = new RowInfo();
            newRow.FirstPage = startPage;            

            //Keep adding pages until we either:
            // - run out of pages to add
            // - add the appropriate number of pages            
            for (int i = startPage; i < startPage + columns; i++)
            {
                //We’re out of pages.
                if (i > PageCache.PageCount - 1)
                    break;

                //Get the size of the page
                Size pageSize = GetScaledPageSize(i);

                //Add this page to the row
                newRow.AddPage(pageSize);                
            }

            return newRow;
        }
Пример #4
0
        /// <summary>
        /// Updates the cache entry at the given index with the new RowInfo supplied.
        /// If the new row has a different height than the old one, we need to update
        /// the offsets of all the rows below this one and adjust the vertical extent appropriately.
        /// If it has a different width, we just need to update the horizontal extent appropriately.
        /// </summary>
        /// <param name="index">The index of the row to update</param>
        /// <param name="newRow">The new RowInfo to replace the old</param>
        private void UpdateRow(int index, RowInfo newRow)
        {
            if (!_isLayoutCompleted)
            {
                throw new InvalidOperationException(SR.Get(SRID.RowCacheCannotModifyNonExistentLayout));
            }            
            
            //Check for invalid indices.  If it's out of range then we just return.
            if (index > _rowCache.Count)
            {
                Debug.Assert(false, "Requested to update a non-existent row.");
                return;
            }

	        //Get the current entry.
	        RowInfo oldRowInfo = _rowCache[index];

            //Replace the old with the new.
            _rowCache[index] = newRow;

            //Compare the heights -- if they differ we need to update the rows beneath it.
            if (oldRowInfo.RowSize.Height != newRow.RowSize.Height)
            {
                //The new row has a different height, so we add the delta
                //between the old and the new to every row below this one.
                double delta = newRow.RowSize.Height - oldRowInfo.RowSize.Height;
                for(int i = index + 1; i < _rowCache.Count; i++)
                {
                    RowInfo row = _rowCache[i];
                    row.VerticalOffset += delta;
                    _rowCache[i] = row;
                }

                //Add the delta for this row to our vertical extent.
                _extentHeight += delta;				
            }

            //If the new row is wider than the current document's width, then we
            //can just update the document width now.
            if (newRow.RowSize.Width > _extentWidth)
            {
                _extentWidth = newRow.RowSize.Width;
            }            
	        //Otherwise, if the widths differ we need to recalculate the width 
            //of the document again.
            //(The logic is that this particular row could have defined the extent width, 
            //and now that it's changed size the extent may change as well.)
            else if (oldRowInfo.RowSize.Width != newRow.RowSize.Width)
            {
                //The width of this row has changed.
                //This means we need to recalculate the width of the entire document
                //by walking through each row.
                _extentWidth = 0;
                for (int i = 0; i < _rowCache.Count; i++)
                {
                    RowInfo row = _rowCache[i];
                    //Update the extent width.
                    _extentWidth = Math.Max(row.RowSize.Width, _extentWidth);                    
                }
            }          
        }
Пример #5
0
        /// <summary>
        /// Trims any pages (and rows) from the end of the layout, starting with the specified page.
        /// </summary>
        /// <param name="startPage">The first page to remove.</param>
        private RowCacheChange TrimPageRange(int startPage)
        {
            //First, we find the row the last page is on.
            int rowIndex = GetRowIndexForPageNumber(startPage);
            
            //Now we replace this row with a new row that has the deleted pages
            //removed, if there are pages on this row that aren't going to be deleted.
            RowInfo oldRow = GetRow(rowIndex);
            
            if (oldRow.FirstPage < startPage)
            {
                RowInfo updatedRow = new RowInfo();
                updatedRow.VerticalOffset = oldRow.VerticalOffset;
                updatedRow.FirstPage = oldRow.FirstPage;                              

                for (int i = oldRow.FirstPage; i < startPage; i++)
                {
                    //Get the page we're interested in.
                    Size pageSize = GetScaledPageSize(i);
                    //Add it.
                    updatedRow.AddPage(pageSize);                    
                }

                UpdateRow(rowIndex, updatedRow);

                //Increment the rowIndex, since we're going to keep this row.
                rowIndex++;
            }

            int removeCount = _rowCache.Count - rowIndex;

            //Now remove all the rows below this one.
            if(rowIndex < _rowCache.Count )
            {
                _rowCache.RemoveRange( rowIndex, removeCount);
            }

            //Update our extents
            _extentHeight = oldRow.VerticalOffset;

            return new RowCacheChange(rowIndex, removeCount);
        }
Пример #6
0
        /// <summary>
        /// Adds a new row onto the end of the layout and updates the current layout
        /// measurements.
        /// </summary>
        /// <param name="newRow">The new row to add to the layout</param>
        private void AddRow(RowInfo newRow)
        {
            //If this is the first row in the document we just put it at the beginning
            if (_rowCache.Count == 0)
            {
                newRow.VerticalOffset = 0.0;

                //The width of the document is just the width of the first row.
                _extentWidth = newRow.RowSize.Width;
            }
            else
            {
                //This is not the first row, so we put it at the end of the last row.
                RowInfo lastRow = _rowCache[_rowCache.Count - 1];

                //The new row needs to be positioned at the bottom of the last row
                newRow.VerticalOffset = lastRow.VerticalOffset + lastRow.RowSize.Height;

                //Update the document's width                
                _extentWidth = Math.Max(newRow.RowSize.Width, _extentWidth);
            }

            //Update the layout
            _extentHeight += newRow.RowSize.Height;
            
            //Add the row.
            _rowCache.Add(newRow);                       
        }
Пример #7
0
        /// <summary>
        /// Given a preexisting page range, updates the dimensions of the rows containing said 
        /// pages from the Page Cache.
        /// If any row's height changes as a result, all rows below have their offsets updated.
        /// </summary>		
        /// <param name="startPage">The first page changed</param>
        /// <param name="count">The number of pages changed</param>
        private RowCacheChange UpdatePageRange(int startPage, int count)
        {
            if (!_isLayoutCompleted)
            {
                throw new InvalidOperationException(SR.Get(SRID.RowCacheCannotModifyNonExistentLayout));
            }

            //Get the row that contains the first page
            int startRowIndex = GetRowIndexForPageNumber(startPage);
            int rowIndex = startRowIndex;

            int currentPage = startPage;

            //Recalculate the rows affected by the changed pages.
            while (currentPage < startPage + count && rowIndex < _rowCache.Count)
            {
                //Get the current row
                RowInfo currentRow = _rowCache[rowIndex];
                //Create a new row and copy pertinent data
                //from the old one.
                RowInfo updatedRow = new RowInfo();               
                updatedRow.VerticalOffset = currentRow.VerticalOffset;
                updatedRow.FirstPage = currentRow.FirstPage;

                //Now rebuild this row, thus recalculating the row's size
                //based on the new page sizes.
                for (int i = currentRow.FirstPage; i < currentRow.FirstPage + currentRow.PageCount; i++)
                {                    
                    //Get the updated page size and add it to our updated row
                    Size pageSize = GetScaledPageSize(i);
                    updatedRow.AddPage(pageSize);
                }				
                
                //Update the row layout with this new row.
                UpdateRow(rowIndex, updatedRow);

                //Move to the next group of pages.
                currentPage = updatedRow.FirstPage + updatedRow.PageCount;

                //Move to the next row.
                rowIndex++;
            }

            return new RowCacheChange(startRowIndex, rowIndex - startRowIndex);
        }
Пример #8
0
        /// <summary> 
        /// Helper function that calculates the Horizontal offset of the given page. 
        /// </summary>
        /// <param name="row">The row which the desired page lives on</param> 
        /// <param name="pageNumber">The page to find the offset for</param>
        /// <returns>The Horizontal offset of the page in the document.</returns>
        private double GetHorizontalOffsetForPage( RowInfo row, int pageNumber )
        { 
            if (row == null)
            { 
                throw new ArgumentNullException("row"); 
            }
 
            if (pageNumber < row.FirstPage ||
                pageNumber > row.FirstPage + row.PageCount)
            {
                throw new ArgumentOutOfRangeException("pageNumber"); 
            }
 
            //Rows are centered if the content has varying page sizes, 
            //Left-aligned otherwise.
            double horizontalOffset = _pageCache.DynamicPageSizes ? 
                Math.Max(0.0, (ExtentWidth - row.RowSize.Width) / 2.0) : 0.0;

            //Add the widths of the pages (and spacing) prior to this one on the row
            for (int i = row.FirstPage; i < pageNumber; i++) 
            {
                Size pageSize = _pageCache.GetPageSize(i); 
                horizontalOffset += pageSize.Width * Scale + HorizontalPageSpacing; 
            }
 
            return horizontalOffset;

        }
Пример #9
0
        private double CalculateScaleFactor(RowInfo pivotRow) 
        {
            //Determine the dimensions of this row minus any spacing between the pages. 
            //We use this as the baseline for our scale factor as page spacing does not scale. 
            double rowWidth;
 
            //If the page sizes vary, we use the width of the pivot row,
            //otherwise we use the overall width of the document (ExtentWidth).
            //(For uniform page sizes, we always use the width of the document, even
            //for the last row which may not have the same width as the rest of the document). 
            if (_pageCache.DynamicPageSizes)
            { 
                rowWidth = pivotRow.RowSize.Width - pivotRow.PageCount * HorizontalPageSpacing; 
            }
            else 
            {
                rowWidth = ExtentWidth - MaxPagesAcross * HorizontalPageSpacing;
            }
 
            double rowHeight = pivotRow.RowSize.Height - VerticalPageSpacing;
 
            //If we have row dimensions of zero or less, there's no reason to scale anything. 
            //So just return 1.0 to indicate no change.
            if (rowWidth <= 0.0 || rowHeight <= 0.0) 
            {
                return 1.0;
            }
 
            //The dimensions of our Viewport minus any spacing.  We use this as the baseline for our
            //scale factor as page spacing does not scale. 
            double compensatedViewportWidth; 

            if (_pageCache.DynamicPageSizes) 
            {
                compensatedViewportWidth = ViewportWidth - pivotRow.PageCount * HorizontalPageSpacing;
            }
            else 
            {
                compensatedViewportWidth = ViewportWidth - MaxPagesAcross * HorizontalPageSpacing; 
            } 

            double compensatedViewportHeight = ViewportHeight - VerticalPageSpacing; 

            //If we have no space to display pages, there's nothing to scale.
            //So just return 1.0 to indicate no change.
            if (compensatedViewportWidth <= 0.0 || 
                compensatedViewportHeight <= 0.0)
            { 
                return 1.0; 
            }
 
            double scaleFactor = 1.0;

            //Based on the previously determined ViewMode (set in SetColumns(), FitToWidth(), etc..
            //scale the pages appropriately. 
            switch (_documentLayout.ViewMode)
            { 
                case ViewMode.SetColumns: 
                    //We leave the scale factor as is -- this is not a "page-fit" mode.
                    break; 

                case ViewMode.FitColumns:
                    //Update the scale factor so that the pivot row is completely visible.
                    scaleFactor = Math.Min(compensatedViewportWidth / rowWidth, compensatedViewportHeight / rowHeight); 
                    break;
 
                case ViewMode.PageWidth: 
                    //Update the scale factor so that the pivot row is as wide as the viewport.
                    scaleFactor = compensatedViewportWidth / rowWidth; 
                    break;

                case ViewMode.PageHeight:
                    //Update the scale factor so that the pivot row is as tall as the viewport. 
                    scaleFactor = compensatedViewportHeight / rowHeight;
                    break; 
 
                case ViewMode.Thumbnails:
                    //Update the scale factor so that the _entire layout_ is completely visible.  As in previous 
                    //cases we must compensate for the fact that the spacing between pages does not scale.
                    //However, unlike in previous cases, we must exclude the space between all rows rather
                    //merely one space, so we must recalculate the compensated values.  Furthermore we must
                    //also compensate for the ExtentHeight as well since it includes the spaces. 
                    double thumbnailCompensatedExtentHeight = ExtentHeight - VerticalPageSpacing * _rowCache.RowCount;
                    double thumbnailCompensatedViewportHeight = ViewportHeight - VerticalPageSpacing * _rowCache.RowCount; 
                    //If we have no space to display pages, there's nothing to scale. 
                    //So just return 1.0 to indicate no change.
                    if (thumbnailCompensatedViewportHeight <= 0.0) 
                    {
                        scaleFactor = 1.0;
                    }
                    else 
                    {
                        scaleFactor = Math.Min(compensatedViewportWidth / rowWidth, 
                            thumbnailCompensatedViewportHeight / thumbnailCompensatedExtentHeight); 
                    }
                    break; 

                case ViewMode.Zoom:
                    //We will not change the scale here, as this is not a "page-fit" mode.
                    break; 

                default: 
                    throw new InvalidOperationException(SR.Get(SRID.DocumentGridInvalidViewMode)); 
            }
 
            return scaleFactor;
        }
Пример #10
0
        /// <summary>
        /// Given a pivot row and a previously set ViewMode, the scale is adjusted so as
        /// to cause the pivot row to be fit based on the specified ViewMode.
        /// </summary> 
        /// <param name="pivotRow"></param>
        private void ApplyViewParameters(RowInfo pivotRow) 
        { 
            //Update our MaxPagesAcross property to the number of rows on the pivot row
            //if page sizes vary.  (If page sizes are uniform, this value will not change as a result of 
            //a layout change)
            if (_pageCache.DynamicPageSizes)
            {
                _maxPagesAcross = pivotRow.PageCount; 
            }
 
            //Get the scale factor necessary to fit the given row into the Viewport. 
            double scaleFactor = CalculateScaleFactor(pivotRow);
 
            //Calculate the new scale. We multiply our scale factor by the old scale factor to cancel out any
            //previously applied scale.
            double newScale = scaleFactor * _rowCache.Scale;
 
            //Clip the value into the acceptable range
            newScale = Math.Max(newScale, CurrentMinimumScale); 
            newScale = Math.Min(newScale, DocumentViewerConstants.MaximumScale); 

            //Update the Row Layout's scale. 
            UpdateLayoutScale(newScale);
        }
Пример #11
0
        /// <summary> 
        /// Checks that the current scale factor is optimal for the passed in row.
        /// </summary> 
        /// <param name="pivotRow">The Row to pass to the Delegate</param> 
        private void EnsureFit(RowInfo pivotRow)
        { 
            //Get the scale factor necessary to fit this row into view.
            //If the result is not 1.0 (within a certain margin of error)
            //then we need to re-layout, alas.
            double neededScaleFactor = CalculateScaleFactor(pivotRow); 
            double newScale = neededScaleFactor * _rowCache.Scale;
 
            //If the neededScaleFactor would require DocumentGrid scale the pages 
            //below the minimum allowed zoom, or above the maximum, then we won't
            //do anything here. 
            if (newScale < CurrentMinimumScale ||
                newScale > DocumentViewerConstants.MaximumScale)
            {
                return; 
            }
 
            if (!DoubleUtil.AreClose(1.0, neededScaleFactor)) 
            {
                //Rescale the row. 
                ApplyViewParameters( pivotRow );

                //Make the row visible again -- the offsets may have
                //changed due to the above rescaling. 
                SetVerticalOffsetInternal(pivotRow.VerticalOffset);
            } 
 
        }
Пример #12
0
        /// <summary> 
        /// Helper function that indicates whether all the pages on the specified
        /// row point to clean cache entries.
        /// </summary>
        /// <param name="row"></param> 
        /// <returns></returns>
        private bool RowIsClean(RowInfo row) 
        { 
            bool clean = true;
 
            for (int i = row.FirstPage; i < row.FirstPage + row.PageCount; i++)
            {
                if (_pageCache.IsPageDirty(i))
                { 
                    clean = false;
                    break; 
                } 
            }
 
            return clean;
        }