You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@flex.apache.org by cf...@apache.org on 2012/12/21 19:05:22 UTC

svn commit: r1425063 [8/13] - in /incubator/flex/sdk/branches/develop: frameworks/ frameworks/projects/spark/ frameworks/projects/spark/src/ frameworks/projects/spark/src/spark/collections/ frameworks/projects/spark/src/spark/components/ frameworks/pro...

Added: incubator/flex/sdk/branches/develop/frameworks/projects/spark/src/spark/components/gridClasses/GridViewLayout.as
URL: http://svn.apache.org/viewvc/incubator/flex/sdk/branches/develop/frameworks/projects/spark/src/spark/components/gridClasses/GridViewLayout.as?rev=1425063&view=auto
==============================================================================
--- incubator/flex/sdk/branches/develop/frameworks/projects/spark/src/spark/components/gridClasses/GridViewLayout.as (added)
+++ incubator/flex/sdk/branches/develop/frameworks/projects/spark/src/spark/components/gridClasses/GridViewLayout.as Fri Dec 21 18:05:20 2012
@@ -0,0 +1,3026 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  Licensed to the Apache Software Foundation (ASF) under one or more
+//  contributor license agreements.  See the NOTICE file distributed with
+//  this work for additional information regarding copyright ownership.
+//  The ASF licenses this file to You under the Apache License, Version 2.0
+//  (the "License"); you may not use this file except in compliance with
+//  the License.  You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+//  Unless required by applicable law or agreed to in writing, software
+//  distributed under the License is distributed on an "AS IS" BASIS,
+//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//  See the License for the specific language governing permissions and
+//  limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+package spark.components.gridClasses
+{
+import flash.events.Event;
+import flash.geom.Rectangle;
+import flash.utils.Dictionary;
+import flash.utils.getTimer;
+
+import mx.collections.IList;
+import mx.core.ClassFactory;
+import mx.core.IFactory;
+import mx.core.IInvalidating;
+import mx.core.IUITextField;
+import mx.core.IVisualElement;
+import mx.core.IVisualElementContainer;
+import mx.core.Singleton;
+import mx.core.mx_internal;
+import mx.events.CollectionEvent;
+import mx.events.CollectionEventKind;
+import mx.events.PropertyChangeEvent;
+import mx.managers.ILayoutManagerClient;
+import mx.managers.LayoutManager;
+
+import spark.collections.SubListView;
+import spark.components.DataGrid;
+import spark.components.Grid;
+import spark.components.supportClasses.GroupBase;
+import spark.core.IGraphicElement;
+import spark.layouts.supportClasses.DropLocation;
+import spark.layouts.supportClasses.LayoutBase;
+
+use namespace mx_internal;
+
+[ExcludeClass]
+
+/**
+ *  @private
+ *  A virtual two dimensional layout for the Grid class.   This is not a general purpose layout,
+ *  it's only intended to be use with GridView.
+ */
+public class GridViewLayout extends LayoutBase
+{
+    include "../../core/Version.as";    
+
+    //--------------------------------------------------------------------------
+    //
+    //  Variables
+    //
+    //-------------------------------------------------------------------------- 
+
+    /**
+     *  @private
+     *  The following variables define the visible part of the grid, where each item
+     *  renderer typically displays dataProvider[rowIndex][columns[columnIndex]].dataField.
+     *  The index vectors are sorted in increasing order but their items may not be
+     *  sequential. 
+     */
+    private var visibleRowIndices:Vector.<int> = new Vector.<int>(0);
+    private var visibleColumnIndices:Vector.<int> = new Vector.<int>(0); 
+    
+    /**
+     *  @private
+     *  The previous values of the corresponding variables.   Set by layoutItemRenderers()
+     *  and only valid during updateDisplayList(), for a complete relayout.
+     */
+    private var oldVisibleRowIndices:Vector.<int> = new Vector.<int>(0);
+    private var oldVisibleColumnIndices:Vector.<int> = new Vector.<int>(0);
+        
+    /** 
+     *  TODO (hmuller): document how do these vectors relate to visibleRow,ColumnIndices
+     */
+    private var visibleRowBackgrounds:Vector.<IVisualElement> = new Vector.<IVisualElement>(0);
+    private var visibleRowSeparators:Vector.<IVisualElement> = new Vector.<IVisualElement>(0);
+    private var visibleColumnSeparators:Vector.<IVisualElement> = new Vector.<IVisualElement>(0);
+    private var visibleItemRenderers:Vector.<IGridItemRenderer> = new Vector.<IGridItemRenderer>(0);
+    
+    /**
+	 *  @private
+     *  TODO (hmuller): provide documentation
+     */
+    private var hoverIndicator:IVisualElement = null;
+    private var caretIndicator:IVisualElement = null;
+    private var editorIndicator:IVisualElement = null;
+    
+    /**
+     *  @private
+     *  The bounding rectangle for all of the visible item renderers.  Note that this
+     *  rectangle may be larger than the scrollRect, since the first/last rows/columns
+     *  of item renderers may only be partially visible.   See scrollPositionChanged().
+     */
+    private const visibleItemRenderersBounds:Rectangle = new Rectangle();
+    
+    /**
+     *  @private
+     *  The viewport's bounding rectangle; often smaller then visibleItemRenderersBounds.
+     *  Initialized by updateDisplayList with the current scrollPosition, and grid.width,Height.
+     */
+    private const visibleGridBounds:Rectangle = new Rectangle();
+    
+    /**
+     *  @private
+     *  The elements available for reuse.  Maps from an IFactory to a list of the elements 
+     *  that have been allocated by that factory and then freed.   The list is represented 
+     *  by a Vector.<IVisualElement>.
+     * 
+     *  Updated by allocateGridElement().
+     */
+    private const freeElementMap:Dictionary = new Dictionary();
+    
+    /**
+     *  @private
+     *  Records the IFactory used to allocate a Element so that free(Element) can find it again.
+     * 
+     *  Updated by createGridElement().
+     */
+    private const elementToFactoryMap:Dictionary = new Dictionary();
+    
+    /**
+     *  @private
+     *  Used by scrollPositionChanged() to determine which scroll position properties changed.
+     */    
+    private var oldVerticalScrollPosition:Number = 0;
+    private var oldHorizontalScrollPosition:Number = 0;
+    
+    //--------------------------------------------------------------------------
+    //
+    //  Class methods and properties
+    //
+    //-------------------------------------------------------------------------- 
+    
+    /**
+     *  @private
+     *  The static embeddedFontsRegistryExists property is initialized lazily. 
+     */
+    private static var  _embeddedFontRegistryExists:Boolean = false;
+    private static var embeddedFontRegistryExistsInitialized:Boolean = false;
+    
+    /**
+     *  @private
+     *  True if an embedded font registry singleton exists.
+     */
+    private static function get embeddedFontRegistryExists():Boolean
+    {
+        if (!embeddedFontRegistryExistsInitialized)
+        {
+            embeddedFontRegistryExistsInitialized = true;
+            try
+            {
+                _embeddedFontRegistryExists = Singleton.getInstance("mx.core::IEmbeddedFontRegistry") != null;
+            }
+            catch (e:Error)
+            {
+                _embeddedFontRegistryExists = false;
+            }
+        }
+        
+        return _embeddedFontRegistryExists;
+    }    
+    
+    //--------------------------------------------------------------------------
+    //
+    //  Constructor
+    //
+    //--------------------------------------------------------------------------
+    
+    /**
+     *  Constructor. 
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 2.4
+     *  @productversion Flex 4.5
+     */    
+    public function GridViewLayout()
+    {
+        super();
+    }
+    
+    //--------------------------------------------------------------------------
+    //
+    //  Properties
+    //
+    //--------------------------------------------------------------------------
+    
+    /**
+     *  @private
+     */
+    private function dispatchChangeEvent(type:String):void
+    {
+        if (hasEventListener(type))
+            dispatchEvent(new Event(type));
+    }    
+	
+	//----------------------------------
+	//  columnsView
+	//----------------------------------	
+	
+    private var _columnsView:SubListView = null;
+    
+    [Bindable("columnsViewChanged")]
+    
+    /**
+     *  @default null
+     */
+    public function get columnsView():SubListView
+    {
+        return _columnsView;
+    }
+    
+    /**
+     *  @private
+     */
+    public function set columnsView(value:SubListView):void
+    {
+        if (value == _columnsView)
+            return;
+        
+        _columnsView = value;
+        dispatchChangeEvent("columnsViewChanged");
+    }
+        
+	//----------------------------------
+	//  dataProviderView
+	//----------------------------------
+	
+    private var _dataProviderView:SubListView = null;
+    
+    [Bindable("dataProviderViewChanged")]
+    
+    /**
+     *  @default null
+     */
+    public function get dataProviderView():SubListView
+    {
+        return _dataProviderView;
+    }
+    
+    /**
+     *  @private
+     */
+    public function set dataProviderView(value:SubListView):void
+    {
+        if (value == _dataProviderView)
+            return;
+        
+        _dataProviderView = value;
+        dispatchChangeEvent("dataProviderViewChanged");
+    }    
+    
+    //----------------------------------
+    //  grid
+    //----------------------------------
+    
+    private var _grid:Grid = null;
+    
+    /**
+     *  @private
+     */
+    public function get grid():Grid
+    {
+        return _grid;
+    }
+    
+    /**
+     *  The Grid parent of this layout's target.   This property is set by the Grid when the 
+     *  target GridView is added/removed from the Grid.
+     * 
+     */
+    public function set grid(value:Grid):void
+    {
+        if (_grid == value)
+            return;
+
+        if (_grid)
+        {
+            _grid.removeEventListener("dataProviderChanged", grid_dataProviderChangedHandler);
+            _grid.removeEventListener("columnsChanged", grid_columnsChangedHandler);            
+        }
+        
+        _grid = value;
+        
+        if (_grid)
+        {
+            dataProviderView = new SubListView(grid.dataProvider);
+            columnsView = new SubListView(grid.columns);
+            gridDimensionsView = new GridDimensionsView(grid.gridDimensions);
+            
+            _grid.addEventListener("dataProviderChanged", grid_dataProviderChangedHandler);
+            _grid.addEventListener("columnsChanged", grid_columnsChangedHandler);
+        }
+        else 
+        {
+            dataProviderView = null;
+            columnsView = null;
+            gridDimensionsView = null;
+        }
+    }
+    
+    /** 
+     *  @private
+     *  Called when the Grid's dataProvider property is set - not when the dataProvider itself changes.
+     */
+    private function grid_dataProviderChangedHandler(ignored:Event):void
+    {
+        dataProviderView = new SubListView(grid.dataProvider);
+        dataProviderView.startIndex = viewRowIndex;
+        dataProviderView.count = viewRowCount;
+    }
+    
+    /** 
+     *  @private
+     *  Called when the Grid's column property is set - not when columns are added/removed (etc).
+     */
+    private function grid_columnsChangedHandler(ignored:Event):void
+    {
+        columnsView = new SubListView(grid.columns);
+        columnsView.startIndex = viewColumnIndex;
+        columnsView.count = viewColumnCount;
+    }      
+    
+	//----------------------------------
+	//  gridDimensionsView
+	//----------------------------------
+	
+    private var _gridDimensionsView:GridDimensionsView = null;
+    
+    [Bindable("gridDimensionsViewChanged")]
+    
+    /**
+     *  @default null
+     */
+    public function get gridDimensionsView():GridDimensionsView
+    {
+        return _gridDimensionsView;
+    }
+    
+    /**
+     *  @private
+     */
+    public function set gridDimensionsView(value:GridDimensionsView):void
+    {
+        if (value == _gridDimensionsView)
+            return;
+        
+        _gridDimensionsView = value;
+        dispatchChangeEvent("gridDimensionsViewChanged");
+    }
+    
+    //----------------------------------
+    //  horizontalScrollingLocked
+    //----------------------------------
+    
+    private var _horizontalScrollingLocked:Boolean = false;
+    
+    [Bindable("horizontalScrollingLockedChanged")]
+    
+    /**
+     *  @default false
+     */
+    public function get horizontalScrollingLocked():Boolean
+    {
+        return _horizontalScrollingLocked;
+    }
+    
+    /**
+     *  @private
+     */
+    public function set horizontalScrollingLocked(value:Boolean):void
+    {
+        if (value == _horizontalScrollingLocked)
+            return;
+        
+        _horizontalScrollingLocked = value;
+        dispatchChangeEvent("horizontalScrollingLockedChanged");
+    }
+    
+    //----------------------------------
+    //  requestedColumnCount
+    //----------------------------------
+    
+    private var _requestedColumnCount:int = 0;
+    
+    [Bindable("requestedColumnCountChanged")]
+    
+    /**
+     *  @default 0
+     */
+    public function get requestedColumnCount():int
+    {
+        return _requestedColumnCount;
+    }
+    
+    /**
+     *  @private
+     */
+    public function set requestedColumnCount(value:int):void
+    {
+        if (value == _requestedColumnCount)
+            return;
+        
+        _requestedColumnCount = value;
+        dispatchChangeEvent("requestedColumnCountChanged");
+    }    
+    
+    //----------------------------------
+    //  requestedRowCount
+    //----------------------------------
+    
+    private var _requestedRowCount:int = 0;
+    
+    [Bindable("requestedRowCountChanged")]
+    
+    /**
+     *  @default 0
+     */
+    public function get requestedRowCount():int
+    {
+        return _requestedRowCount;
+    }
+    
+    /**
+     *  @private
+     */
+    public function set requestedRowCount(value:int):void
+    {
+        if (value == _requestedRowCount)
+            return;
+        
+        _requestedRowCount = value;
+        dispatchChangeEvent("requestedRowCountChanged");
+    }
+
+    //----------------------------------
+    //  useVirtualLayout (override)
+    //----------------------------------
+    
+    /**
+     *  GridLayout only supports virtual layout, the value of this property can not be changed.
+     *  
+     *  @return true.
+     * 
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 2.0
+     *  @productversion Flex 4.5
+     */
+    override public function get useVirtualLayout():Boolean
+    {
+        return true;
+    }
+    
+    /**
+     *  @private
+     */
+    override public function set useVirtualLayout(value:Boolean):void
+    {
+    } 
+    
+    //----------------------------------
+    //  verticalScrollingLocked
+    //----------------------------------
+    
+    private var _verticalScrollingLocked:Boolean = false;
+    
+    [Bindable("verticalScrollingLockedChanged")]
+    
+    /**
+     *  @default false
+     */
+    public function get verticalScrollingLocked():Boolean
+    {
+        return _verticalScrollingLocked;
+    }
+    
+    /**
+     *  @private
+     */
+    public function set verticalScrollingLocked(value:Boolean):void
+    {
+        if (value == _verticalScrollingLocked)
+            return;
+        
+        _verticalScrollingLocked = value;
+        dispatchChangeEvent("verticalScrollingLockedChanged");
+    }       
+        
+    //----------------------------------
+    //  viewColumnCount
+    //----------------------------------	
+    
+    /**
+     *  The number of columns displayed by the target GridView.
+     * 
+     *  @default -1
+     */	
+    public function get viewColumnCount():int
+    {
+        return gridDimensionsView.viewColumnCount;
+    }
+    
+    /**
+     *  @private
+     */
+    public function set viewColumnCount(value:int):void
+    {
+        gridDimensionsView.viewColumnCount = value;
+        columnsView.count = value;	
+    }	
+    
+    //----------------------------------
+    //  viewColumnIndex
+    //----------------------------------
+    
+    /**
+     *  The column index origin of the grid region displayed by the target GridView.
+     * 
+     *  @default 0
+     */
+    public function get viewColumnIndex():int
+    {
+        return gridDimensionsView.viewColumnIndex;
+    }
+    
+    /**
+     *  @private
+     */
+    public function set viewColumnIndex(value:int):void
+    {
+        gridDimensionsView.viewColumnIndex = value;
+        columnsView.startIndex = value;
+    }
+    
+    //----------------------------------
+    //  viewRowCount
+    //----------------------------------
+    
+    /**
+     *  The number of rows displayed by the target GridView.
+     * 
+     *  @default -1
+     */	
+    public function get viewRowCount():int
+    {
+        return gridDimensionsView.viewRowCount;
+    }
+    
+    /**
+     *  @private
+     */
+    public function set viewRowCount(value:int):void
+    {
+        gridDimensionsView.viewRowCount = value;
+        dataProviderView.count = value;	
+    }		
+    
+    //----------------------------------
+    //  viewRowIndex
+    //----------------------------------
+    
+    /**
+     *  The row index origin of the grid region displayed by the target GridView.
+     * 
+     *  @default 0
+     */
+    public function get viewRowIndex():int
+    {
+        return gridDimensionsView.viewRowIndex;
+    }
+    
+    /**
+     *  @private
+     */
+    public function set viewRowIndex(value:int):void
+    {
+        gridDimensionsView.viewRowIndex = value;
+        dataProviderView.startIndex = value;
+    }    
+    
+    //--------------------------------------------------------------------------
+    //
+    //  Method Overrides
+    //
+    //-------------------------------------------------------------------------- 
+    
+    /**
+     *  Returns the index where a new item should be inserted if
+     *  the user releases the mouse at the specified coordinates
+     *  while completing a drag and drop gesture.
+     * 
+     *  Called by the <code>calculatedDropLocation()</code> method.
+     *
+     *  @param x The x coordinate of the drag and drop gesture, in 
+     *  local coordinates.
+     * 
+     *  @param y The y coordinate of the drag and drop gesture, in  
+     *  the drop target's local coordinates.
+     *
+     *  @return The drop index or -1 if the drop operation is not available
+     *  at the specified coordinates.
+     * 
+     *  @see #calculateDropLocation()
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 11
+     *  @playerversion AIR 3.0
+     *  @productversion Flex 5.0
+     */
+    override protected function calculateDropIndex(x:Number, y:Number):int
+    {
+        var rowIndex:int = gridDimensionsView.getRowIndexAt(x, y);
+        if (rowIndex == -1)
+        {
+            rowIndex = gridDimensionsView.rowCount;
+        }
+        else
+        {
+            // If we are closer to the next row then drop on the next row.
+            var bounds:Rectangle = gridDimensionsView.getRowBounds(rowIndex);
+            if (y > (bounds.y + (bounds.height / 2)))
+                rowIndex++;
+        }
+        
+        return rowIndex + gridDimensionsView.viewRowIndex;
+    }
+    
+    /**
+     *  Calculates the bounds for the drop indicator that provides visual feedback
+     *  to the user of where the items will be inserted at the end of a drag and drop
+     *  gesture.
+     * 
+     *  Called by the <code>showDropIndicator()</code> method.
+     * 
+     *  @param dropLocation A valid DropLocation object previously returned 
+     *  by the <code>calculateDropLocation()</code> method.
+     * 
+     *  @return The bounds for the drop indicator or null.
+     * 
+     *  @see spark.layouts.supportClasses.DropLocation
+     *  @see #calculateDropIndex()
+     *  @see #calculateDragScrollDelta()
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 11
+     *  @playerversion AIR 3.0
+     *  @productversion Flex 5.0
+     */
+    override protected function calculateDropIndicatorBounds(dropLocation:DropLocation):Rectangle
+    {
+        var rowIndex:int = gridDimensionsView.getRowIndexAt(dropLocation.dropPoint.x, dropLocation.dropPoint.y); 
+        if (rowIndex == -1)
+        {
+            // If the last row is visible then put the drop indicator below the last row.
+            if (grid.dataProvider && isCellVisible(grid.dataProvider.length - 1, -1))
+                rowIndex = grid.dataProvider.length - 1;
+            else
+                return null;
+        }
+        
+        var bounds:Rectangle = gridDimensionsView.getRowBounds(rowIndex);
+        
+        // If we are closer to the next row then put the drop indicator on 
+        // the next row.
+        // TODO (dloverin): TBD how the drop indicator should be sized when
+        // rowGap is implemented.
+        // NOTE: The bounds indicator for the MX DataGrid start at the top of 
+        // the cell. The top of the drag indicator was moved up by two pixels
+        // so it could be seen when the indicator bounds are below the last row.
+        // The issue is there may not be any space below the last row and the drop
+        // indicator would not be visible if it started at the top of the next cell.
+        if (dropLocation.dropPoint.y > (bounds.top + (bounds.height * 1) / 2))
+            return new Rectangle(2, bounds.bottom - 2, bounds.width -4, 4);
+        
+        return new Rectangle(2, Math.max(0, bounds.y - 2), bounds.width - 4, 4);
+    }
+    
+    /**
+     *  @private
+     *  Clear everything.
+     */
+    override public function clearVirtualLayoutCache():void
+    {
+        freeGridElements(visibleRowBackgrounds);
+        freeGridElements(visibleRowSeparators);
+        visibleRowIndices.length = 0;
+        
+        freeGridElements(visibleColumnSeparators);        
+        visibleColumnIndices.length = 0;
+        
+        freeItemRenderers(visibleItemRenderers);
+        
+        clearSelectionIndicators();
+        
+        freeGridElement(hoverIndicator)
+        hoverIndicator = null;
+        
+        freeGridElement(caretIndicator);
+        caretIndicator = null;
+        
+        freeGridElement(editorIndicator);
+        editorIndicator = null;
+        
+        visibleItemRenderersBounds.setEmpty();
+        visibleGridBounds.setEmpty();
+    }      
+
+    /**
+     *  @private
+     *  This version of the method uses gridDimensions to calcuate the bounds
+     *  of the specified cell.   The index is the cell's position in the row-major
+     *  layout. 
+     */
+    override public function getElementBounds(index:int):Rectangle
+    {
+        const columnsLength:int = gridDimensionsView.columnCount;
+        if (columnsLength == -1)
+            return null;
+        
+        const rowIndex:int = index / columnsLength;
+        const columnIndex:int = index - (rowIndex * columnsLength);
+        return gridDimensionsView.getCellBounds(rowIndex, columnIndex); 
+    }
+    
+    /**
+     *  @private
+     */    
+    override protected function getElementBoundsAboveScrollRect(scrollRect:Rectangle):Rectangle
+    {
+        const y:int = Math.max(0, scrollRect.top - 1);
+        const rowIndex:int = gridDimensionsView.getRowIndexAt(scrollRect.x, y);
+        return gridDimensionsView.getRowBounds(rowIndex);
+    }
+    
+    /**
+     *  @private
+     */    
+    override protected function getElementBoundsBelowScrollRect(scrollRect:Rectangle):Rectangle
+    {
+        const rowCount:int = gridDimensionsView.rowCount;
+        const maxY:int = Math.max(0, gridDimensionsView.getContentHeight(rowCount) - 1); 
+        const y:int = Math.min(maxY, scrollRect.bottom + 1);
+        const rowIndex:int = gridDimensionsView.getRowIndexAt(scrollRect.x, y);
+        return gridDimensionsView.getRowBounds(rowIndex);
+    }
+    
+    /**
+     *  @private
+     */    
+    override protected function getElementBoundsLeftOfScrollRect(scrollRect:Rectangle):Rectangle
+    {
+        const x:int = Math.max(0, scrollRect.left - 1);
+        const columnIndex:int = gridDimensionsView.getColumnIndexAt(x, scrollRect.y);
+        return gridDimensionsView.getColumnBounds(columnIndex);
+    }
+    
+    /**
+     *  @private
+     */    
+    override protected function getElementBoundsRightOfScrollRect(scrollRect:Rectangle):Rectangle
+    {
+        const columnCount:int = gridDimensionsView.columnCount;
+        const maxX:int = Math.max(0, gridDimensionsView.getContentWidth(columnCount) - 1); 
+        const x:int = Math.min(maxX, scrollRect.right + 1);
+        const columnIndex:int = gridDimensionsView.getColumnIndexAt(x, scrollRect.y);
+        return gridDimensionsView.getColumnBounds(columnIndex);
+    }
+    
+    /**
+     *  @private
+     */
+    override protected function scrollPositionChanged():void
+    {
+        if (!grid)
+            return;
+        
+        grid.hoverRowIndex = -1;
+        grid.hoverColumnIndex = -1;
+            
+        super.scrollPositionChanged();  // sets GridView's scrollRect
+        
+        const hspChanged:Boolean = oldHorizontalScrollPosition != horizontalScrollPosition;
+        const vspChanged:Boolean = oldVerticalScrollPosition != verticalScrollPosition;
+        
+        oldHorizontalScrollPosition = horizontalScrollPosition;
+        oldVerticalScrollPosition = verticalScrollPosition;
+        
+        // Only invalidate if we're clipping and rows and/or columns covered
+		// by the scrollR changes.  If so, the visible row/column indicies need
+		// to be updated.
+        
+		var invalidate:Boolean = false;
+		
+		if (visibleRowIndices.length == 0 || visibleColumnIndices.length == 0)
+			invalidate = true;
+		
+		if (!invalidate && vspChanged)
+		{
+			const oldFirstRowIndex:int = visibleRowIndices[0];
+			const oldLastRowIndex:int = visibleRowIndices[visibleRowIndices.length - 1];
+			
+			const newFirstRowIndex:int = 
+				gridDimensionsView.getRowIndexAt(horizontalScrollPosition, verticalScrollPosition);
+			const newLastRowIndex:int = 
+				gridDimensionsView.getRowIndexAt(horizontalScrollPosition, verticalScrollPosition + target.height);
+			
+			if (oldFirstRowIndex != newFirstRowIndex || oldLastRowIndex != newLastRowIndex)
+				invalidate = true;
+		}
+		
+		if (!invalidate && hspChanged)
+		{
+			const oldFirstColIndex:int = visibleColumnIndices[0];			
+			const oldLastColIndex:int = visibleColumnIndices[visibleColumnIndices.length - 1];
+			
+			const newFirstColIndex:int = 
+				gridDimensionsView.getColumnIndexAt(horizontalScrollPosition, verticalScrollPosition);
+			const newLastColIndex:int = 
+				gridDimensionsView.getColumnIndexAt(horizontalScrollPosition + target.width, verticalScrollPosition);
+			
+			if (oldFirstColIndex != newFirstColIndex || oldLastColIndex != newLastColIndex)
+				invalidate = true;
+		}
+		
+		if (invalidate)
+        {
+            var reason:String = "none";
+            if (vspChanged && hspChanged)
+                reason = "bothScrollPositions";
+            else if (vspChanged)
+                reason = "verticalScrollPosition"
+            else if (hspChanged)
+                reason = "horizontalScrollPosition";
+            
+            grid.invalidateDisplayListFor(reason);
+        }
+    }
+    
+    /**
+     *  @private
+     *  Computes new values for the grid's measuredWidth,Height and 
+     *  measuredMinWidth,Height properties.  
+     * 
+     *  If grid.requestedRowCount is GTE 0, then measuredHeight is estimated 
+     *  content height for as many rows.  Otherwise the measuredHeight is the estimated 
+     *  content height for all rows.  The measuredWidth calculation is similar.  The 
+     *  measuredMinWidth,Height properties are also similar however if the corresponding 
+     *  requestedMin property isn't specified, then the measuredMin size is the same 
+     *  as the measured size.
+     */
+    override public function measure():void
+    {
+        const gridView:GridView = target as GridView;  // TBD: requestedRowCount should be a local property...
+        const grid:Grid = this.grid;
+        
+        if (!gridView || !grid)
+            return;
+
+        updateTypicalCellSizes();
+        
+        var measuredRowCount:int = requestedRowCount;
+        if (measuredRowCount == -1)
+        {
+            measuredRowCount = gridDimensionsView.rowCount;
+            if (grid.requestedMaxRowCount != -1)
+                measuredRowCount = Math.min(grid.requestedMaxRowCount, measuredRowCount);
+            if (grid.requestedMinRowCount != -1)
+                measuredRowCount = Math.max(grid.requestedMinRowCount, measuredRowCount);                
+        }
+        
+        var measuredColumnCount:int = requestedColumnCount;
+        if (measuredColumnCount == -1)
+        {
+            measuredColumnCount = getColumnsLength();
+            if (grid.requestedMinColumnCount != -1)
+                measuredColumnCount = Math.max(grid.requestedMinColumnCount, measuredColumnCount);
+        }
+        
+        var measuredWidth:Number = gridDimensionsView.getTypicalContentWidth(measuredColumnCount);
+        var measuredHeight:Number = gridDimensionsView.getTypicalContentHeight(measuredRowCount);
+        var measuredMinWidth:Number = gridDimensionsView.getTypicalContentWidth(grid.requestedMinColumnCount);
+        var measuredMinHeight:Number = gridDimensionsView.getTypicalContentHeight(grid.requestedMinRowCount);
+        
+        // Use Math.ceil() to make sure that if the content partially occupies
+        // the last pixel, we'll count it as if the whole pixel is occupied.
+        
+        target.measuredWidth = Math.ceil(measuredWidth);    
+        target.measuredHeight = Math.ceil(measuredHeight);
+        target.measuredMinWidth = Math.ceil(measuredMinWidth);    
+        target.measuredMinHeight = Math.ceil(measuredMinHeight);
+
+		//trace("measure", target.measuredWidth, target.measuredHeight);
+}
+    
+    /**
+     *  @private
+     */
+    override public function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
+    {
+        const grid:Grid = this.grid;
+        if (!grid)
+            return;
+
+		//trace("updateDisplayList", unscaledWidth, unscaledHeight);
+		
+        // Find the index of the last GridColumn.visible==true column
+        
+        const columnsLength:int = gridDimensionsView.columnCount;
+        const lastVisibleColumnIndex:int = (columnsLength > 0) ? getPreviousVisibleColumnIndex(columnsLength) : -1;
+        if (lastVisibleColumnIndex < 0)
+            return;
+        
+        // Layers
+        
+        const backgroundLayer:GridLayer = getLayer("backgroundLayer");
+        const selectionLayer:GridLayer = getLayer("selectionLayer");    
+        const editorIndicatorLayer:GridLayer = getLayer("editorIndicatorLayer");
+        const rendererLayer:GridLayer = getLayer("rendererLayer");
+        const overlayLayer:GridLayer = getLayer("overlayLayer"); 
+        
+        // Relayout everything if the scroll position changed or if no 
+        // "invalidateDisplayList reason" was specified.  See
+        // Grid/invalidateDisplayListFor(reason)
+        
+        const completeLayoutNeeded:Boolean = 
+            grid.isInvalidateDisplayListReason("verticalScrollPosition") ||
+            grid.isInvalidateDisplayListReason("horizontalScrollPosition");
+            
+        
+        // Layout the columns and item renderers; compute new values for visibleRowIndices et al.
+        
+        if (completeLayoutNeeded)
+        {
+            oldVisibleRowIndices = visibleRowIndices;
+            oldVisibleColumnIndices = visibleColumnIndices;
+            
+            // Determine the x/y position of the visible content.  Note that the 
+            // actual scroll positions may be negative.
+            
+            const scrollX:Number = Math.max(0, horizontalScrollPosition);
+            const scrollY:Number = Math.max(0, verticalScrollPosition);
+            
+            visibleGridBounds.x = scrollX;
+            visibleGridBounds.y = scrollY;
+            visibleGridBounds.width = unscaledWidth;
+            visibleGridBounds.height = unscaledHeight;
+            
+            layoutColumns(scrollX, scrollY, unscaledWidth);
+            layoutItemRenderers(rendererLayer, scrollX, scrollY, unscaledWidth, unscaledHeight);
+            
+            // Update the content size.  Make sure that if the content spans partially 
+            // over a pixel to the right/bottom, the content size includes the whole pixel.
+            
+            const columnCount:int = getColumnsLength();
+            const rowCount:int = gridDimensionsView.rowCount;
+            
+            const contentWidth:Number = Math.ceil(gridDimensionsView.getContentWidth(columnCount));
+            const contentHeight:Number = Math.ceil(gridDimensionsView.getContentHeight(rowCount));
+            target.setContentSize(contentWidth, contentHeight);
+			
+            // If the grid's contentHeight is smaller than than the available height 
+            // (unscaledHeight) then pad the visible rows
+            
+            var paddedRowCount:int = rowCount;
+            if ((scrollY == 0) && (contentHeight < unscaledHeight))
+            {
+                const unusedHeight:Number = unscaledHeight - gridDimensionsView.getContentHeight(rowCount);
+                paddedRowCount += Math.ceil(unusedHeight / gridDimensionsView.defaultRowHeight);
+            }
+            
+            for (var rowIndex:int = rowCount; rowIndex < paddedRowCount; rowIndex++)
+                visibleRowIndices.push(rowIndex);
+            
+            // Layout the row backgrounds
+            
+            visibleRowBackgrounds = layoutLinearElements(grid.rowBackground, backgroundLayer,
+                visibleRowBackgrounds, oldVisibleRowIndices, visibleRowIndices, layoutRowBackground);
+    
+            // Layout the row and column separators. 
+            
+            const lastRowIndex:int = paddedRowCount - 1;
+    
+            visibleRowSeparators = layoutLinearElements(grid.rowSeparator, overlayLayer, 
+                visibleRowSeparators, oldVisibleRowIndices, visibleRowIndices, layoutRowSeparator, lastRowIndex);
+            
+            visibleColumnSeparators = layoutLinearElements(grid.columnSeparator, overlayLayer, 
+                visibleColumnSeparators, oldVisibleColumnIndices, visibleColumnIndices, layoutColumnSeparator, lastVisibleColumnIndex);
+            
+            
+            // The old visible row,column indices are no longer needed
+            
+            oldVisibleRowIndices.length = 0;
+            oldVisibleColumnIndices.length = 0;            
+        }
+        
+        // Layout the hoverIndicator, caretIndicator, and selectionIndicators
+        
+        if (completeLayoutNeeded || grid.isInvalidateDisplayListReason("hoverIndicator"))
+            layoutHoverIndicator(backgroundLayer);
+        
+        if (completeLayoutNeeded || grid.isInvalidateDisplayListReason("selectionIndicator"))
+            layoutSelectionIndicators(selectionLayer);
+        
+        if (completeLayoutNeeded || grid.isInvalidateDisplayListReason("caretIndicator"))
+            layoutCaretIndicator(overlayLayer);
+        
+        if (completeLayoutNeeded || grid.isInvalidateDisplayListReason("editorIndicator"))
+            layoutEditorIndicator(editorIndicatorLayer);
+        
+        if (!completeLayoutNeeded)
+            updateVisibleItemRenderers();
+        
+        // To avoid flashing, force all of the layers to render now
+        
+        target.validateNow();
+    }
+    
+    /** 
+     *  @private
+     *  Reset the selected, showsCaret, and hovered properties for all visible item renderers.
+     *  Run the prepare() method for renderers that have changed.
+     * 
+     *  This method is only called when the item renderers are not updated as part of a general
+     *  redisplay, by layoutItemRenderers(). 
+     */
+    private function updateVisibleItemRenderers():void
+    {
+        const grid:Grid = grid;  // avoid get method cost
+        const rowSelectionMode:Boolean = isRowSelectionMode();
+        const cellSelectionMode:Boolean = isCellSelectionMode();
+        
+        if (!rowSelectionMode && !cellSelectionMode)
+            return;
+        
+        for each (var renderer:IGridItemRenderer in visibleItemRenderers)            
+        {
+            var rowIndex:int = renderer.rowIndex;  // TBD: need grid-relative row,column indices here
+            var columnIndex:int = renderer.columnIndex;
+            
+            var oldSelected:Boolean  = renderer.selected;
+            var oldShowsCaret:Boolean = renderer.showsCaret;
+            var oldHovered:Boolean = renderer.hovered;
+            
+            // The following initializations should match what's done in initializeItemRenderer()
+            if (rowSelectionMode)
+            {                
+                renderer.selected = grid.selectionContainsIndex(rowIndex);
+                renderer.showsCaret = grid.caretRowIndex == rowIndex;
+                renderer.hovered = grid.hoverRowIndex == rowIndex;
+            }
+            else if (cellSelectionMode)
+            {
+                renderer.selected = grid.selectionContainsCell(rowIndex, columnIndex);
+                renderer.showsCaret = (grid.caretRowIndex == rowIndex) && (grid.caretColumnIndex == columnIndex);
+                renderer.hovered = (grid.hoverRowIndex == rowIndex) && (grid.hoverColumnIndex == columnIndex);                    
+            }
+            
+            if ((oldSelected != renderer.selected) || 
+                (oldShowsCaret != renderer.showsCaret) || 
+                (oldHovered != renderer.hovered))
+                renderer.prepare(true);
+        }
+    }
+
+    //--------------------------------------------------------------------------
+    //
+    //  Target GridView Access
+    //
+    //--------------------------------------------------------------------------
+    
+    private function gridRowIndexToViewIndex(gridRowIndex:int):int
+    {
+        return (gridRowIndex == -1) ? -1 : gridRowIndex - viewRowIndex;
+    }
+    
+    private function gridColumnIndexToViewIndex(gridColumnIndex:int):int
+    {
+        return (gridColumnIndex == -1) ? -1 : gridColumnIndex - viewColumnIndex;
+    }    
+    
+    private function getLayer(name:String):GridLayer
+    {
+        return target.getChildByName(name) as GridLayer;
+
+    }
+    
+    /**
+     *  @private
+     */
+    private function getGridColumn(columnIndex:int):GridColumn
+    {
+        const columnsView:IList = columnsView;
+		if ((columnsView == null) || (columnIndex >= columnsView.length) || (columnIndex < 0))
+            return null;
+        
+        return columnsView.getItemAt(columnIndex) as GridColumn;
+    }
+    
+    /**
+     *  @private
+     */
+    private function getColumnsLength():int
+    {
+        return (columnsView) ? columnsView.length : 0;
+    }
+    
+    
+    /**
+     *  @private
+     *  Returns the index of the next Grid.visible==true column
+     *  after index. Returns -1 if there are no more visible columns.
+     * 
+     *  To find the first GridColumn.visible==true column index, use
+     *  getNextVisibleColumnIndex(-1).
+     */
+    private function getNextVisibleColumnIndex(index:int=-1):int
+    {
+        if (index < -1)
+            return -1;
+        
+        const columns:IList = columnsView;        
+        const columnsLength:int = (columns) ? columns.length : 0;
+        
+        for (var i:int = index + 1; i < columnsLength; i++)
+        {
+            var column:GridColumn = columns.getItemAt(i) as GridColumn;
+            if (column && column.visible)
+                return i;
+        }
+        
+        return -1;
+    }
+    
+    /**
+     *  @private
+     *  Returns the index of the previous GridColumn.visible==true column
+     *  before index.  Returns -1 if there are no more visible columns.
+     * 
+     *  To find the last GridColumn.visible==true column index, use
+     *  getPreviousVisibleColumnIndex(columns.length).
+     */
+    private function getPreviousVisibleColumnIndex(index:int):int
+    {
+        const columns:IList = columnsView;
+        if (!columns || (index > columns.length))
+            return -1;
+        
+        for (var i:int = index - 1; i >= 0; i--)
+        {
+            var column:GridColumn = columns.getItemAt(i) as GridColumn;
+            if (column && column.visible)
+                return i;
+        }
+        
+        return -1;
+    }
+        
+    /**
+     *  @private
+     */
+    private function getDataProviderItem(rowIndex:int):Object
+    {
+        const dataProviderView:IList = this.dataProviderView;
+		
+        if ((dataProviderView == null) || (rowIndex >= dataProviderView.length) || (rowIndex < 0))
+            return null;
+        
+        return dataProviderView.getItemAt(rowIndex);
+    }
+    
+    
+    /**
+     *  @private
+     */
+    private function getDataProviderLength():int 
+    {
+		const dataProviderView:IList = this.dataProviderView;
+		
+        return (dataProviderView) ? dataProviderView.length : -1;
+    }
+    
+    //--------------------------------------------------------------------------
+    //
+    //  Updating the GridDimensions' typicalCell sizes and columnWidths
+    //
+    //-------------------------------------------------------------------------- 
+    
+    /**
+     *  @private
+     *  Return width clamped to the column's minWidth and maxWidth properties.
+     */
+    private static function clampColumnWidth(width:Number, column:GridColumn):Number
+    {
+       const minColumnWidth:Number = column.minWidth;
+       const maxColumnWidth:Number = column.maxWidth;
+       
+       if (!isNaN(minColumnWidth))
+           width = Math.max(width, minColumnWidth);
+       if (!isNaN(maxColumnWidth))
+           width = Math.min(width, maxColumnWidth);
+       
+       return width;
+    }
+    
+    /**
+     *  @private
+     *  Use the specified GridColumn's itemRenderer (IFactory) to create a temporary
+     *  item renderer.   The returned item renderer must be freed, with freeGridElement(),
+     *  and removed from the rendererLayer after it's used.
+     */
+    private function createTypicalItemRenderer(columnIndex:int):IGridItemRenderer
+    {
+        const rendererLayer:GridLayer = getLayer("rendererLayer");
+        if (!rendererLayer)
+            return null;
+        
+        var typicalItem:Object = grid.typicalItem;
+        if (typicalItem == null)
+            typicalItem = getDataProviderItem(0);
+        
+        const column:GridColumn = getGridColumn(columnIndex);
+        const factory:IFactory = itemToRenderer(column, typicalItem);
+        const renderer:IGridItemRenderer = allocateGridElement(factory) as IGridItemRenderer;
+        
+        rendererLayer.addElement(renderer);
+
+        initializeItemRenderer(renderer, 0 /* rowIndex */, columnIndex, grid.typicalItem, false);
+        
+        // If the column's width isn't specified, then use the renderer's explicit
+        // width, if any.   If that isn't specified, then use 4096, to avoid wrapping.
+        
+        var columnWidth:Number = column.width;
+        
+        if (isNaN(columnWidth))
+        {
+            // Sadly, IUIComponent, UITextField, and UIFTETextField all have an 
+            // explicitWidth property but do not share a common type.  
+            if ("explicitWidth" in renderer)
+                columnWidth = Object(renderer).explicitWidth;
+        }
+        
+        // The default width of a UI[FTE]TextField is 100.  If autoWrap is true, and
+        // multiline is true, the measured text will wrap if it is wider than
+        // the TextField's width. This is not what we want when measuring the 
+        // width of typicalItem columns that lack an explicit column width.
+        
+        if (isNaN(columnWidth))
+            columnWidth = 4096;
+        
+        layoutItemRenderer(renderer, 0, 0, columnWidth, NaN);
+  
+        return renderer;
+    }
+    
+    /**
+     *  @private
+     *  Update the typicalCellWidth,Height for all of the columns starting 
+     *  with x coordinate startX and column startIndex that fit within the 
+     *  specified width.  Typical sizes are only updated if the current 
+     *  typical cell size is NaN. 
+     * 
+     *  The typicalCellWidth for GridColumns with an explicit width, is just 
+     *  the explicit width.  Otherwise an item renderer is created for the column 
+     *  and the item renderer's preferred bounds become the typical cell size.   
+     */
+    private function updateVisibleTypicalCellSizes(width:Number, scrollX:Number, firstVisibleColumnIndex:int):void
+    {
+        const rendererLayer:GridLayer = getLayer("rendererLayer");
+        if (!rendererLayer)
+            return;        
+        
+        const columnCount:int = getColumnsLength();
+        const startCellX:Number = gridDimensionsView.getCellX(0 /* rowIndex */, firstVisibleColumnIndex);
+        const columnGap:int = gridDimensionsView.columnGap;
+        
+        for (var columnIndex:int = firstVisibleColumnIndex;
+            (width > 0) && (columnIndex >= 0) && (columnIndex < columnCount);
+            columnIndex = getNextVisibleColumnIndex(columnIndex))
+        {
+            var cellHeight:Number = gridDimensionsView.getTypicalCellHeight(columnIndex);
+            var cellWidth:Number = gridDimensionsView.getTypicalCellWidth(columnIndex);
+            
+            var column:GridColumn = getGridColumn(columnIndex);
+            if (!isNaN(column.width))
+            {
+                cellWidth = column.width;
+                gridDimensionsView.setTypicalCellWidth(columnIndex, cellWidth);
+            }
+            
+            if (isNaN(cellWidth) || isNaN(cellHeight))
+            {
+                var renderer:IGridItemRenderer = createTypicalItemRenderer(columnIndex);
+                if (isNaN(cellWidth))
+                {
+                    cellWidth = clampColumnWidth(renderer.getPreferredBoundsWidth(), column);
+                    gridDimensionsView.setTypicalCellWidth(columnIndex, cellWidth);
+                }
+                if (isNaN(cellHeight))
+                {
+                    cellHeight = renderer.getPreferredBoundsHeight();
+                    gridDimensionsView.setTypicalCellHeight(columnIndex, cellHeight);
+                }
+                
+                rendererLayer.removeElement(renderer);                
+                freeGridElement(renderer);
+            }
+            
+            if (columnIndex == firstVisibleColumnIndex)
+                width -= startCellX + cellWidth - scrollX;
+            else
+                width -= cellWidth + columnGap;
+        }
+    }
+    
+    /**
+     *  @private
+     *  Used by the measure() method to initialize the GridDimensions typical width,height of 
+     *  requestedColumnCount columns, and the typical width of *all* columns with an explicit width.
+     */
+    private function updateTypicalCellSizes():void
+    {
+        const rendererLayer:GridLayer = getLayer("rendererLayer");
+        if (!rendererLayer)
+            return;  
+        
+        const columnCount:int = getColumnsLength();
+        const columnGap:int = gridDimensionsView.columnGap;
+        const requestedColumnCount:int = grid.requestedColumnCount;  // TBD GridView...
+        var measuredColumnCount:int = 0;
+        
+        for (var columnIndex:int = 0; (columnIndex < columnCount); columnIndex++)
+        {
+            var cellHeight:Number = gridDimensionsView.getTypicalCellHeight(columnIndex);
+            var cellWidth:Number = gridDimensionsView.getTypicalCellWidth(columnIndex);
+            
+            var column:GridColumn = getGridColumn(columnIndex);
+            
+            // GridColumn.visible==false columns have a typical size of (0,0)
+            // to distinguish them from the GridColumn.visible==true columns
+            // that aren't in view yet.
+            
+            if (!column.visible)
+            {
+                gridDimensionsView.setTypicalCellWidth(columnIndex, 0);
+                gridDimensionsView.setTypicalCellHeight(columnIndex, 0);
+                continue;
+            }
+            
+            if (!isNaN(column.width))
+            {
+                cellWidth = column.width;
+                gridDimensionsView.setTypicalCellWidth(columnIndex, cellWidth);
+            }
+            
+            var needTypicalRenderer:Boolean = (requestedColumnCount == -1) || (measuredColumnCount < requestedColumnCount);
+            if (needTypicalRenderer && (isNaN(cellWidth) || isNaN(cellHeight)))
+            {
+                var renderer:IGridItemRenderer = createTypicalItemRenderer(columnIndex);
+                if (isNaN(cellWidth))
+                {
+                    cellWidth = clampColumnWidth(renderer.getPreferredBoundsWidth(), column);
+                    gridDimensionsView.setTypicalCellWidth(columnIndex, cellWidth);
+                }
+                if (isNaN(cellHeight))
+                {
+                    cellHeight = renderer.getPreferredBoundsHeight();
+                    gridDimensionsView.setTypicalCellHeight(columnIndex, cellHeight);
+                }
+                
+                rendererLayer.removeElement(renderer);
+                freeGridElement(renderer);
+            }
+            measuredColumnCount++;
+        }
+		
+    }
+
+    /**
+     *  @private
+     *  Update the column widths for the columns visible beginning at scrollX, that will fit
+     *  within the specified width, or for all columns if width is NaN.  The width of 
+     *  GridColumns that lack an explicit width is the preferred width of an item renderer 
+     *  for the grid's typicalItem. 
+     * 
+     *  If width is specified and all columns are visible, then we'll increase the widths
+     *  of GridDimensions columns for GridColumns without an explicit width so that all of
+     *  the available space is consumed.
+     */
+    private function layoutColumns(scrollX:Number, scrollY:Number, width:Number):void
+    {
+        var columnCount:int = gridDimensionsView.columnCount;
+        if (columnCount <= 0)
+            return;
+        
+        // Update the GridDimensions typicalCellWidth,Height values as needed.
+
+        const firstVisibleColumnIndex:int = gridDimensionsView.getColumnIndexAt(scrollX, scrollY);
+        updateVisibleTypicalCellSizes(width, scrollX, firstVisibleColumnIndex);
+        
+        // Set the GridDimensions columnWidth for no more than columnCount columns.
+        
+        const columnGap:int = gridDimensionsView.columnGap;
+        const startCellX:Number = gridDimensionsView.getCellX(0 /* rowIndex */, firstVisibleColumnIndex);
+        var availableWidth:Number = width;
+        var flexibleColumnCount:uint = 0;
+        
+        for (var columnIndex:int = firstVisibleColumnIndex;
+             (availableWidth > 0) && (columnIndex >= 0) && (columnIndex < columnCount);
+             columnIndex = getNextVisibleColumnIndex(columnIndex))
+        {
+            var columnWidth:Number = gridDimensionsView.getTypicalCellWidth(columnIndex);
+            var gridColumn:GridColumn = getGridColumn(columnIndex);
+            
+            if (isNaN(gridColumn.width)) // if this column's width wasn't explicitly specified
+            {
+                flexibleColumnCount += 1;
+                columnWidth = clampColumnWidth(columnWidth, gridColumn);
+            }
+            else
+                columnWidth = gridColumn.width;
+            
+            gridDimensionsView.setColumnWidth(columnIndex, columnWidth);  // store the column width
+            
+            if (columnIndex == firstVisibleColumnIndex)
+                availableWidth -= startCellX + columnWidth - scrollX;
+            else
+                availableWidth -= columnWidth + columnGap;
+        }
+        
+        // If we haven't scrolled horizontally, and there's space left over, widen 
+        // the columns whose GridColumn width isn't set explicitly, to fill the extra space.
+        
+        if ((scrollX != 0) || (availableWidth < 1.0) || (flexibleColumnCount == 0))
+            return;
+        
+        const columnWidthDelta:Number = Math.ceil(availableWidth / flexibleColumnCount);
+
+        for (columnIndex = firstVisibleColumnIndex;
+             (columnIndex >= 0) && (columnIndex < columnCount) && (availableWidth >= 1.0);
+             columnIndex = getNextVisibleColumnIndex(columnIndex))
+        {
+            gridColumn = getGridColumn(columnIndex);
+            
+            if (isNaN(gridColumn.width)) // if this column's width wasn't explicitly specified 
+            {
+                var oldColumnWidth:Number = gridDimensionsView.getColumnWidth(columnIndex);
+                columnWidth = oldColumnWidth + Math.min(availableWidth, columnWidthDelta);
+                columnWidth = clampColumnWidth(columnWidth, gridColumn);
+                gridDimensionsView.setColumnWidth(columnIndex, columnWidth);  // store the column width
+                availableWidth -= (columnWidth - oldColumnWidth);
+            }
+        }    
+    }
+
+    //--------------------------------------------------------------------------
+    //
+    //  Item Renderer Management and Layout
+    //
+    //--------------------------------------------------------------------------    
+    
+    private const gridItemRendererClassFactories:Dictionary = new Dictionary(true);
+    
+    /**
+     *  @private
+     *  Return the item renderer for the specified column and dataProvider item,
+     *  essentially column.itemToRenderer(dataItem). 
+     * 
+     *  If this app might have embedded fonts then item renderers must be created with the Grid's
+     *  module factory.  To enable that, we wrap the real item renderer ClassFactory with 
+     *  a GridItemRendererClassFactory.  Wrapped factories are cached in 
+     *  the gridItemRendererClassFactories Dictionary.
+     */
+    private function itemToRenderer(column:GridColumn, dataItem:Object):IFactory
+    {
+        var factory:IFactory = column.itemToRenderer(dataItem);
+        var rendererClassFactory:IFactory = null;
+
+        if (embeddedFontRegistryExists && (factory is ClassFactory))
+        {
+            rendererClassFactory = gridItemRendererClassFactories[factory];
+            if (!rendererClassFactory)
+            {
+                rendererClassFactory = new GridItemRendererClassFactory(grid, ClassFactory(factory));
+                gridItemRendererClassFactories[factory] = rendererClassFactory;
+            }
+        }
+        
+        return (rendererClassFactory) ? rendererClassFactory : factory;
+    }
+
+    private function layoutItemRenderers(rendererLayer:GridLayer, scrollX:Number, scrollY:Number, width:Number, height:Number):void
+    {
+        if (!rendererLayer)
+            return;
+        
+        var rowIndex:int;
+        var colIndex:int;
+        
+        const rowCount:int = gridDimensionsView.rowCount;
+        const colCount:int = getColumnsLength();
+        const rowGap:int = gridDimensionsView.rowGap;
+        const colGap:int = gridDimensionsView.columnGap;
+        
+        // Compute the row,column index and bounds of the upper left "start" cell
+         
+        const startColIndex:int = gridDimensionsView.getColumnIndexAt(scrollX, scrollY);
+        const startRowIndex:int = gridDimensionsView.getRowIndexAt(scrollX, scrollY);
+        const startCellX:Number = gridDimensionsView.getCellX(startRowIndex, startColIndex); 
+        const startCellY:Number = gridDimensionsView.getCellY(startRowIndex, startColIndex); 
+        
+        // Compute newVisibleColumns
+        
+        const newVisibleColumnIndices:Vector.<int> = new Vector.<int>();
+        var availableWidth:Number = width;
+        var column:GridColumn;
+        
+        for (colIndex = startColIndex; 
+             (availableWidth > 0) && (colIndex >= 0) && (colIndex < colCount);
+             colIndex = getNextVisibleColumnIndex(colIndex))
+        {
+            newVisibleColumnIndices.push(colIndex);
+            var columnWidth:Number = gridDimensionsView.getColumnWidth(colIndex);
+            if (colIndex == startColIndex)
+                availableWidth -= startCellX + columnWidth - scrollX;
+            else
+                availableWidth -= columnWidth + colGap;
+        }
+        
+        // compute newVisibleRowIndices, newVisibleItemRenderers, layout item renderers
+        
+        const newVisibleRowIndices:Vector.<int> = new Vector.<int>();
+        const newVisibleItemRenderers:Vector.<IGridItemRenderer> = new Vector.<IGridItemRenderer>();
+        
+        var cellX:Number = startCellX;
+        var cellY:Number = startCellY;
+        var availableHeight:Number = height;
+        
+        for (rowIndex = startRowIndex; (availableHeight > 0) && (rowIndex >= 0) && (rowIndex < rowCount); rowIndex++)
+        {
+            newVisibleRowIndices.push(rowIndex);
+            
+            var rowHeight:Number = gridDimensionsView.getRowHeight(rowIndex);
+            for each (colIndex in newVisibleColumnIndices)
+            {
+                var renderer:IGridItemRenderer = takeVisibleItemRenderer(rowIndex, colIndex);
+                if (!renderer)
+                {       
+                    var dataItem:Object = getDataProviderItem(rowIndex);
+                    column = getGridColumn(colIndex);
+                    var factory:IFactory = itemToRenderer(column, dataItem);
+                    renderer = allocateGridElement(factory) as IGridItemRenderer;
+                }
+                if (renderer.parent != rendererLayer)
+                    rendererLayer.addElement(renderer);
+                newVisibleItemRenderers.push(renderer);
+
+                initializeItemRenderer(renderer, rowIndex, colIndex);
+                
+                var colWidth:Number = gridDimensionsView.getColumnWidth(colIndex);
+                layoutItemRenderer(renderer, cellX, cellY, colWidth, rowHeight);                
+                
+                var preferredRowHeight:Number = renderer.getPreferredBoundsHeight()
+                gridDimensionsView.setCellHeight(rowIndex, colIndex, preferredRowHeight);
+                cellX += colWidth + colGap;
+            }
+           
+            // If gridDimensions.rowHeight is now larger, we need to make another
+            // pass to fix up the item renderer heights. 
+            
+            const finalRowHeight:Number = gridDimensionsView.getRowHeight(rowIndex);
+            if (rowHeight != finalRowHeight)
+            {
+                const visibleColumnsLength:int = newVisibleColumnIndices.length;
+                rowHeight = finalRowHeight;
+                for each (colIndex in newVisibleColumnIndices)
+                {
+                    var rowOffset:int = newVisibleRowIndices.indexOf(rowIndex);
+                    var colOffset:int = newVisibleColumnIndices.indexOf(colIndex);                    
+                    var index:int = (rowOffset * visibleColumnsLength) + colOffset;
+                    renderer = newVisibleItemRenderers[index];                    
+                    
+                    // We're using layoutBoundsX,Y,Width instead of x,y.width because
+                    // the IUITextField item renderers pad their x,y,width,height properties 
+                    var rendererX:Number = renderer.getLayoutBoundsX();
+                    var rendererY:Number = renderer.getLayoutBoundsY();
+                    var rendererWidth:Number = renderer.getLayoutBoundsWidth();
+
+                    layoutItemRenderer(renderer, rendererX, rendererY, rendererWidth, rowHeight);
+                    gridDimensionsView.setCellHeight(rowIndex, colIndex, renderer.getPreferredBoundsHeight());
+                }
+            } 
+                                               
+            cellX = startCellX;
+            cellY += rowHeight + rowGap;
+            
+            if (rowIndex == startRowIndex)
+                availableHeight -= startCellY + rowHeight - scrollY;
+            else
+                availableHeight -= rowHeight + rowGap;            
+        }
+        
+        // Free renderers that aren't in use
+        
+        for each (var oldRenderer:IGridItemRenderer in visibleItemRenderers)
+            freeItemRenderer(oldRenderer);
+        
+        // Update visibleItemRenderersBounds
+        
+        if ((newVisibleRowIndices.length > 0) && (newVisibleColumnIndices.length > 0))
+        {
+            const lastRowIndex:int = newVisibleRowIndices[newVisibleRowIndices.length - 1];
+            const lastColIndex:int = newVisibleColumnIndices[newVisibleColumnIndices.length - 1];
+            const lastCellR:Rectangle = gridDimensionsView.getCellBounds(lastRowIndex, lastColIndex);
+            
+            visibleItemRenderersBounds.x = startCellX;
+            visibleItemRenderersBounds.y = startCellY; 
+            visibleItemRenderersBounds.width = lastCellR.x + lastCellR.width - startCellX;
+            visibleItemRenderersBounds.height = lastCellR.y + lastCellR.height - startCellY;
+        }
+        else
+        {
+            visibleItemRenderersBounds.setEmpty();
+        }
+        
+        // Update visibleItemRenderers et al
+        
+        visibleItemRenderers = newVisibleItemRenderers;
+        visibleRowIndices = newVisibleRowIndices;
+        visibleColumnIndices = newVisibleColumnIndices;
+    }
+    
+    /**
+     *  Reinitialize and layout the visible renderer at rowIndex, columnIndex.  If the cell's preferred 
+     *  height changes and the Grid has been configured with variableRowHeight=true, the entire grid is 
+     *  invalidated.
+     * 
+     *  <p>If row,columnIndex do not correspond to a visible cell, nothing is done.</p>
+     * 
+     *  @param rowIndex The 0-based row index of the cell that changed.
+     *  @param columnIndex The 0-based column index of the cell that changed.
+     */
+    public function invalidateCell(rowIndex:int, columnIndex:int):void
+    {
+        const renderer:IGridItemRenderer = getVisibleItemRenderer(rowIndex, columnIndex);
+        if (!renderer)
+            return;
+        
+        // If the renderer at rowIndex,columnIndex is going to have to be replaced, because
+        // this columns itemRendererFunction now returns a different (IFactory) value, punt.
+       
+        if (itemRendererFunctionValueChanged(renderer))
+        {
+            renderer.grid.invalidateDisplayList();
+            return;
+        }
+        
+        initializeItemRenderer(renderer, rowIndex, columnIndex);
+        
+        // We're using layoutBoundsX,Y,Width,Height instead of x,y,width,height because
+        // the IUITextField item renderers pad their x,y,width,height properties 
+        
+        const rendererX:Number = renderer.getLayoutBoundsX();
+        const rendererY:Number = renderer.getLayoutBoundsY();
+        const rendererWidth:Number = renderer.getLayoutBoundsWidth();
+        const rendererHeight:Number = renderer.getLayoutBoundsHeight();
+        
+        layoutItemRenderer(renderer, rendererX, rendererY, rendererWidth, rendererHeight);
+        
+        // If the renderer's preferredHeight has changed and variableRowHeight=true, then
+        // the row's height may have changed, which implies we need to layout -everything-.
+        // Warning: the unconditional getPreferredBoundsHeight() call also serves to 
+        // force DefaultGridItemRenderer and UITextFieldGridItemRenderer to validate;
+        // similar to what happens in layoutItemRenderers() and updateTypicalCellSizes()
+        
+        const preferredRendererHeight:Number = renderer.getPreferredBoundsHeight();
+        if (gridDimensionsView.variableRowHeight && (rendererHeight != preferredRendererHeight))
+            grid.invalidateDisplayList();
+    }
+    
+    /**
+     *  @private
+     *  Return true if the specified item renderer was defined by an itemRendererFunction whose
+     *  value has changed.
+     */
+    private function itemRendererFunctionValueChanged(renderer:IGridItemRenderer):Boolean
+    {
+        const column:GridColumn = renderer.column;
+        if (!column || (column.itemRendererFunction === null))
+            return false;
+        
+        const factory:IFactory = itemToRenderer(column, renderer.data);
+        return factory !== elementToFactoryMap[renderer];
+    }
+
+    /**
+     *  @private
+     */
+    private function getVisibleItemRendererIndex(rowIndex:int, columnIndex:int):int
+    {
+        if ((visibleRowIndices == null) || (visibleColumnIndices == null))
+            return -1;
+        
+        // TODO (hmuller) - binary search would be faster than indexOf()
+        
+        const rowOffset:int = visibleRowIndices.indexOf(rowIndex);
+        const colOffset:int = visibleColumnIndices.indexOf(columnIndex);
+        if ((rowOffset == -1) || (colOffset == -1))
+            return -1;
+
+        const index:int = (rowOffset * visibleColumnIndices.length) + colOffset;
+        return index;
+    }
+    
+    /**
+     *  Return the visible item renderer at the specified GridView row,columnIndex.
+     */
+    public function getVisibleItemRenderer(rowIndex:int, columnIndex:int):IGridItemRenderer
+    {
+        const index:int = getVisibleItemRendererIndex(rowIndex, columnIndex);
+        if (index == -1 || index >= visibleItemRenderers.length)
+            return null;
+        
+        const renderer:IGridItemRenderer = visibleItemRenderers[index];
+        return renderer;        
+    }
+    
+    /**
+     *  @private
+     */
+    private function takeVisibleItemRenderer(rowIndex:int, columnIndex:int):IGridItemRenderer
+    {
+        const index:int = getVisibleItemRendererIndex(rowIndex, columnIndex);
+        if (index == -1 || index >= visibleItemRenderers.length)
+            return null;
+        
+        const renderer:IGridItemRenderer = visibleItemRenderers[index];
+        visibleItemRenderers[index] = null;
+        
+        // If the renderer at rowIndex,columnIndex is going to have to be replaced, because
+        // this column's itemRendererFunction now returns a different (IFactory) value, then 
+        // get rid of the old one and return null.
+        
+        if (renderer && itemRendererFunctionValueChanged(renderer))
+        {
+            freeItemRenderer(renderer);
+            return null;
+        }
+        
+        return renderer;
+    }
+    
+    /**
+     *  @private
+     */
+    private function initializeItemRenderer(
+        renderer:IGridItemRenderer, 
+        rowIndex:int, columnIndex:int,
+        dataItem:Object=null,
+        visible:Boolean=true):void
+    {
+        renderer.visible = visible;
+        
+        const gridColumn:GridColumn = getGridColumn(columnIndex);
+        if (gridColumn)
+        {
+            renderer.rowIndex = rowIndex;
+            renderer.column = gridColumn;
+            if (dataItem == null)
+                dataItem = getDataProviderItem(rowIndex);
+            
+            renderer.label = gridColumn.itemToLabel(dataItem);
+            
+            // The following code must be kept in sync with updateVisibleItemRenderers()
+            if (isRowSelectionMode())
+            {
+                renderer.selected = grid.selectionContainsIndex(rowIndex);
+                renderer.showsCaret = grid.caretRowIndex == rowIndex;
+                renderer.hovered = grid.hoverRowIndex == rowIndex;
+            }
+            else if (isCellSelectionMode())
+            {
+                renderer.selected = grid.selectionContainsCell(rowIndex, columnIndex);
+                renderer.showsCaret = (grid.caretRowIndex == rowIndex) && (grid.caretColumnIndex == columnIndex);
+                renderer.hovered = (grid.hoverRowIndex == rowIndex) && (grid.hoverColumnIndex == columnIndex);
+            }
+            
+            renderer.data = dataItem;
+            
+            if (grid.dataGrid)
+                renderer.owner = grid.dataGrid;
+            
+            renderer.prepare(!createdGridElement);             
+        }
+    }
+    
+    private function freeItemRenderer(renderer:IGridItemRenderer):void
+    {
+        if (!renderer)
+            return;
+        
+        freeGridElement(renderer);
+        renderer.discard(true);          
+    }
+    
+    private function freeItemRenderers(renderers:Vector.<IGridItemRenderer>):void
+    {
+        for each (var renderer:IGridItemRenderer in renderers)
+            freeItemRenderer(renderer);
+        renderers.length = 0;
+    }
+    
+    //--------------------------------------------------------------------------
+    //
+    //  Linear elements: row,column separators, backgrounds 
+    //
+    //-------------------------------------------------------------------------- 
+    
+    /**
+     *  @private
+     *  Common code for laying out the rowBackround, rowSeparator, columnSeparator visual elements.
+     * 
+     *  For row,columnSeparators, lastIndex identifies the element in the new layout for which 
+     *  no separator is drawn.  If the previous layout - oldVisibleIndices - included the lastIndex,
+     *  it needs to be freed, even though it exists in the new layout (newVisibleIndices).   See
+     *  freeLinearElements().
+     */
+    private function layoutLinearElements(
+        factory:IFactory,
+        layer:GridLayer, 
+        oldVisibleElements:Vector.<IVisualElement>,
+        oldVisibleIndices:Vector.<int>,
+        newVisibleIndices:Vector.<int>,
+        layoutFunction:Function,
+        lastIndex:int = -1):Vector.<IVisualElement>
+    {
+        if (!layer)
+            return new Vector.<IVisualElement>(0);
+        
+        // If a factory changed, free the old visual elements and set oldVisibleElements.length=0
+        
+        discardGridElementsIfFactoryChanged(factory, layer, oldVisibleElements);
+                       
+        if (factory == null)
+            return new Vector.<IVisualElement>(0);
+
+        // Free and clear oldVisibleElements that are no long visible
+        
+        freeLinearElements(oldVisibleElements, oldVisibleIndices, newVisibleIndices, lastIndex);
+            
+        // Create, layout, and return newVisibleElements
+        
+        const newVisibleElementCount:uint = newVisibleIndices.length;
+        const newVisibleElements:Vector.<IVisualElement> = new Vector.<IVisualElement>(newVisibleElementCount);
+
+        for (var index:int = 0; index < newVisibleElementCount; index++) 
+        {
+            var newEltIndex:int = newVisibleIndices[index];
+            if (newEltIndex == lastIndex)
+            {
+                newVisibleElements.length = index;
+                break;
+            }
+            
+            // If an element already exists for visibleIndex then use it, otherwise create one
+            
+            var eltOffset:int = oldVisibleIndices.indexOf(newEltIndex);
+            var elt:IVisualElement = (eltOffset != -1 && eltOffset < oldVisibleElements.length) ? oldVisibleElements[eltOffset] : null;
+            if (elt == null)
+                elt = allocateGridElement(factory);
+            
+            // Initialize the element, and then delegate to the layout function
+            
+            newVisibleElements[index] = elt;
+                
+            layer.addElement(elt);
+            
+            elt.visible = true;
+            
+            layoutFunction(elt, newEltIndex);
+        }
+
+        return newVisibleElements;
+    }
+    
+    private function layoutCellElements(
+        factory:IFactory,
+        layer:GridLayer,
+        oldVisibleElements:Vector.<IVisualElement>,
+        oldVisibleRowIndices:Vector.<int>, oldVisibleColumnIndices:Vector.<int>,
+        newVisibleRowIndices:Vector.<int>, newVisibleColumnIndices:Vector.<int>,
+        layoutFunction:Function):Vector.<IVisualElement>
+    {
+        if (!layer)
+            return new Vector.<IVisualElement>(0);
+
+        // If a factory changed, discard the old visual elements.
+        
+        if (discardGridElementsIfFactoryChanged(factory, layer, oldVisibleElements))
+        {
+            oldVisibleRowIndices.length = 0;
+            oldVisibleColumnIndices.length = 0;
+        }
+
+        if (factory == null)
+            return new Vector.<IVisualElement>(0);
+        
+        // Create, layout, and return newVisibleElements
+        
+        const newVisibleElementCount:uint = newVisibleRowIndices.length;
+        const newVisibleElements:Vector.<IVisualElement> = new Vector.<IVisualElement>(newVisibleElementCount);
+
+        // Free and clear oldVisibleElements that are no long visible.
+        
+        freeCellElements(oldVisibleElements, newVisibleElements,
+                         oldVisibleRowIndices, newVisibleRowIndices,
+                         oldVisibleColumnIndices, newVisibleColumnIndices);
+                 
+        for (var index:int = 0; index < newVisibleElementCount; index++) 
+        {
+            var newEltRowIndex:int = newVisibleRowIndices[index];
+            var newEltColumnIndex:int = newVisibleColumnIndices[index];
+            
+            // If an element already exists for visibleIndex then use it, 
+            // otherwise create one.
+            
+            var elt:IVisualElement = newVisibleElements[index];
+            if (elt === null)
+            {
+                // Initialize the element, and then delegate to the layout 
+                // function.
+                elt = allocateGridElement(factory);
+                newVisibleElements[index] = elt;
+            }
+                        
+            layer.addElement(elt);
+            
+            elt.visible = true;
+            
+            layoutFunction(elt, newEltRowIndex, newEltColumnIndex);
+        }
+        
+        return newVisibleElements;
+    }
+
+    /** 
+     *  @private
+     *  If the factory has changed, or is now null, remove and free all the old
+     *  visual elements, if there were any.
+     * 
+     *  @returns True if at least one visual element was removed.
+     */
+    private function discardGridElementsIfFactoryChanged(
+        factory:IFactory,
+        layer:GridLayer,
+        oldVisibleElements:Vector.<IVisualElement>):Boolean    
+    {
+        if ((oldVisibleElements.length) > 0 && (factory != elementToFactoryMap[oldVisibleElements[0]]))
+        {
+            for each (var oldElt:IVisualElement in oldVisibleElements)
+            {
+                layer.removeElement(oldElt);
+                freeGridElement(oldElt);
+            }
+            oldVisibleElements.length = 0;
+            return true;
+        }
+        
+        return false;
+    }
+    
+    /** 
+     *  @private
+     *  Free each member of elements if the corresponding member of oldIndices doesn't 
+     *  appear in newIndices.  Both vectors of indices must have been sorted in increasing
+     *  order.  When an element is freed, the corresponding member of the vector parameter
+     *  is set to null.
+     * 
+     *  This method is [supposed to be a] somewhat more efficient implementation of the following:
+     * 
+     *  for (var i:int = 0; i < elements.length; i++)
+     *     {
+     *     if ((oldIndices[i] == lastIndex) || (newIndices.indexOf(oldIndices[i]) == -1))
+     *         freeGridElement(elements[i]);
+     *         elements[i] = null;
+     *     }
+     *  
+     *  The lastIndex parameter is used to handle row and column separators, where the last
+     *  element is left out since separators only appear in between elements.  If the lastIndex
+     *  appears in oldIndices, we're not going to need the old element.
+     */
+    private function freeLinearElements (
+        elements:Vector.<IVisualElement>, 
+        oldIndices:Vector.<int>, 
+        newIndices:Vector.<int>, 
+        lastIndex:int):void
+    {
+        // TODO(hmuller): rewrite this, should be one pass (no indexOf)
+        for (var i:int = 0; i < elements.length; i++)
+        {
+            const offset:int = newIndices.indexOf(oldIndices[i]);
+            if ((oldIndices[i] == lastIndex) || (offset == -1))
+            {
+                const elt:IVisualElement = elements[i];
+                if (elt)
+                {
+                    freeGridElement(elt);
+                    elements[i] = null;
+                }
+            }
+        }
+    }      
+    
+    private function freeCellElements (
+        elements:Vector.<IVisualElement>, newElements:Vector.<IVisualElement>, 
+        oldRowIndices:Vector.<int>, newRowIndices:Vector.<int>,
+        oldColumnIndices:Vector.<int>, newColumnIndices:Vector.<int>):void
+    {
+        var freeElement:Boolean = true;
+       
+        // assumes newRowIndices.length == newColumnIndices.length
+        const numNewCells:int = newRowIndices.length;
+        var newIndex:int = 0;
+        
+        for (var i:int = 0; i < elements.length; i++)
+        {
+            const elt:IVisualElement = elements[i];
+            if (elt == null)
+                continue;
+            
+            // assumes oldIndices.length == elements.length
+            const oldRowIndex:int = oldRowIndices[i];
+            const oldColumnIndex:int = oldColumnIndices[i];
+            
+            for ( ; newIndex < numNewCells; newIndex++)
+            {
+                const newRowIndex:int = newRowIndices[newIndex];
+                const newColumnIndex:int = newColumnIndices[newIndex];
+                
+                if (newRowIndex == oldRowIndex)
+                {
+                    if (newColumnIndex == oldColumnIndex)
+                    {
+                        // Same cell still selected so reuse the selection.
+                        // Save it in the correct place in newElements.  That 
+                        // way we know its location based on 
+                        // newRowIndices[newIndex], newColumnIndices[newIndex].
+                        newElements[newIndex] = elt;
+                        freeElement = false;
+                        break;
+                    }
+                    else if (newColumnIndex > oldColumnIndex)
+                    {
+                        // not found
+                        break;
+                    }
+                }
+                else if (newRowIndex > oldRowIndex)
+                {
+                    // not found
+                    break;
+                }
+            }
+            
+            if (freeElement)
+                freeGridElement(elt);
+                
+            freeElement = true;
+        }
+        
+        elements.length = 0;
+    }      
+    
+    private function layoutRowBackground(rowBackground:IVisualElement, rowIndex:int):void
+    {
+        const rowCount:int = gridDimensionsView.rowCount;
+        const bounds:Rectangle = (rowIndex < rowCount) 
+            ? gridDimensionsView.getRowBounds(rowIndex)
+            : gridDimensionsView.getPadRowBounds(rowIndex);
+        
+        if (!bounds)
+            return;
+        
+        if  ((rowIndex < rowCount) && (bounds.width == 0)) // implies no columns
+            bounds.width = visibleGridBounds.width;
+        
+        // Initialize this visual element
+        intializeGridVisualElement(rowBackground, rowIndex);
+        
+        layoutGridElementR(rowBackground, bounds);
+    }
+
+    private function layoutRowSeparator(separator:IVisualElement, rowIndex:int):void
+    {
+        // Initialize this visual element
+        intializeGridVisualElement(separator, rowIndex);
+        
+        const height:Number = separator.getPreferredBoundsHeight();
+        const rowCount:int = gridDimensionsView.rowCount;
+        const bounds:Rectangle = (rowIndex < rowCount) 
+            ? gridDimensionsView.getRowBounds(rowIndex)
+            : gridDimensionsView.getPadRowBounds(rowIndex);
+        
+        if (!bounds)
+            return;
+        
+        const x:Number = bounds.x;
+        const width:Number =  Math.max(bounds.width, visibleGridBounds.right);
+        const y:Number = bounds.bottom; // TODO (klin): should center on gap here.
+        layoutGridElement(separator, x, y, width, height);
+    }
+    
+    private function layoutColumnSeparator(separator:IVisualElement, columnIndex:int):void
+    {
+        // Initialize this visual element
+        intializeGridVisualElement(separator, -1, columnIndex);
+        
+        const r:Rectangle = visibleItemRenderersBounds;
+        const width:Number = separator.getPreferredBoundsWidth();
+        const height:Number = Math.max(r.height, visibleGridBounds.height); 
+        const x:Number = gridDimensionsView.getCellX(0, columnIndex) + gridDimensionsView.getColumnWidth(columnIndex); // TODO (klin): should center on gap here.
+        const y:Number = r.y;
+        layoutGridElement(separator, x, y, width, height);
+    }
+    
+    //--------------------------------------------------------------------------
+    //
+    //  Selection Indicators
+    //
+    //--------------------------------------------------------------------------
+    
+    private var visibleSelectionIndicators:Vector.<IVisualElement> = new Vector.<IVisualElement>(0);
+    private var visibleRowSelectionIndices:Vector.<int> = new Vector.<int>(0);    
+    private var visibleColumnSelectionIndices:Vector.<int> = new Vector.<int>(0);
+    
+    private function isRowSelectionMode():Boolean
+    {
+        const mode:String = grid.selectionMode;
+        return (mode == GridSelectionMode.SINGLE_ROW) || (mode == GridSelectionMode.MULTIPLE_ROWS);
+    }
+    
+    private function isCellSelectionMode():Boolean
+    {
+        const mode:String = grid.selectionMode;        
+        return mode == (GridSelectionMode.SINGLE_CELL) || (mode == GridSelectionMode.MULTIPLE_CELLS);
+    }     
+    
+    private function layoutSelectionIndicators(layer:GridLayer):void
+    {
+        const selectionIndicatorFactory:IFactory = grid.selectionIndicator;
+        const viewRowIndex:int = viewRowIndex;
+        const viewColumnIndex:int = viewColumnIndex;
+
+        // layout and update visibleSelectionIndicators,Indices
+                
+        if (isRowSelectionMode())
+        {
+            // Selection is row-based so if there are existing cell selections, 
+            // free them since they can't be reused.
+            if (visibleColumnSelectionIndices.length > 0)
+                clearSelectionIndicators();
+
+            var oldVisibleRowSelectionIndices:Vector.<int> = visibleRowSelectionIndices;
+            
+            // Load this up with the currently selected rows.
+            visibleRowSelectionIndices = new Vector.<int>();
+            
+            for each (var rowIndex:int in visibleRowIndices)
+            {
+                if (grid.selectionContainsIndex(rowIndex + viewRowIndex))
+                {
+                    visibleRowSelectionIndices.push(rowIndex);
+                }
+            }
+            
+            // Display the row selections.
+            visibleSelectionIndicators = layoutLinearElements(
+                selectionIndicatorFactory,
+                layer,
+                visibleSelectionIndicators, 
+                oldVisibleRowSelectionIndices, 
+                visibleRowSelectionIndices, 
+                layoutRowSelectionIndicator);
+            
+            return;
+        }
+        
+        // Selection is not row-based so if there are existing row selections, 
+        // free them since they can't be reused.
+        if (visibleRowSelectionIndices.length > 0 && visibleColumnSelectionIndices.length == 0)
+        {
+            clearSelectionIndicators();
+        }
+        
+        if (isCellSelectionMode())
+        {
+            oldVisibleRowSelectionIndices = visibleRowSelectionIndices;
+            const oldVisibleColumnSelectionIndices:Vector.<int> = visibleColumnSelectionIndices;
+            
+            // Load up the vectors with the row/column of each selected cell.
+            visibleRowSelectionIndices = new Vector.<int>();
+            visibleColumnSelectionIndices = new Vector.<int>();
+            for each (rowIndex in visibleRowIndices)
+            {
+                for each (var columnIndex:int in visibleColumnIndices)
+                {
+                    if (grid.selectionContainsCell(rowIndex + viewRowIndex, columnIndex + viewColumnIndex))
+                    {
+                        visibleRowSelectionIndices.push(rowIndex);
+                        visibleColumnSelectionIndices.push(columnIndex);
+                    }
+                }
+            } 
+                 
+            // Display the cell selections.
+            visibleSelectionIndicators = layoutCellElements(
+                selectionIndicatorFactory,
+                layer,
+                visibleSelectionIndicators, 
+                oldVisibleRowSelectionIndices, oldVisibleColumnSelectionIndices,
+                visibleRowSelectionIndices, visibleColumnSelectionIndices,
+                layoutCellSelectionIndicator);
+            
+            return;
+        }
+        
+        // No selection.
+        
+        // If there are existing cell selections, 
+        // free them since there is no selection.
+        if (visibleColumnSelectionIndices.length > 0)
+            clearSelectionIndicators();
+    }
+    
+    private function layoutRowSelectionIndicator(indicator:IVisualElement, rowIndex:int):void
+    {
+        // Initialize this visual element
+        intializeGridVisualElement(indicator, rowIndex);
+        layoutGridElementR(indicator, gridDimensionsView.getRowBounds(rowIndex));
+    }    
+    
+    private function layoutCellSelectionIndicator(indicator:IVisualElement, 
+                                                  rowIndex:int,
+                                                  columnIndex:int):void
+    {
+        // Initialize this visual element
+        intializeGridVisualElement(indicator, rowIndex, columnIndex);
+        layoutGridElementR(indicator, gridDimensionsView.getCellBounds(rowIndex, columnIndex));
+    }    
+
+    private function clearSelectionIndicators():void
+    {
+        freeGridElements(visibleSelectionIndicators);
+        visibleRowSelectionIndices.length = 0;
+        visibleColumnSelectionIndices.length = 0;
+    }
+    
+    //--------------------------------------------------------------------------
+    //
+    //  Indicators: hover, caret
+    //
+    //--------------------------------------------------------------------------
+    
+    private function layoutIndicator(
+        layer:GridLayer,
+        indicatorFactory:IFactory,
+        indicator:IVisualElement, 
+        rowIndex:int,
+        columnIndex:int):IVisualElement
+    {
+        if (!layer)
+            return null;
+        
+        // If the indicatorFactory has changed for the specified non-null indicator, 
+        // then free the old indicator.
+        
+        if (indicator && (indicatorFactory != elementToFactoryMap[indicator]))
+        {
+            removeGridElement(indicator);
+            indicator = null;
+            if (indicatorFactory == null)
+                return null;
+        }
+        
+        if (rowIndex == -1 || grid.selectionMode == GridSelectionMode.NONE ||
+            (isCellSelectionMode() && (getNextVisibleColumnIndex(columnIndex - 1) != columnIndex)))
+        {
+            if (indicator)
+                indicator.visible = false;
+            return indicator;
+        }
+        
+        if (!indicator && indicatorFactory)
+            indicator = createGridElement(indicatorFactory);
+        
+        if (indicator)
+        {
+            const bounds:Rectangle = isRowSelectionMode() ? 
+                gridDimensionsView.getRowBounds(rowIndex) :
+                gridDimensionsView.getCellBounds(rowIndex, columnIndex);
+            
+            intializeGridVisualElement(indicator, rowIndex, columnIndex);
+            
+            // TODO (klin): Remove this special case for the caret overlapping separators
+            // when we implement column/row gaps.
+            if (indicatorFactory == grid.caretIndicator && bounds)
+            {
+                // increase width and height by 1 to cover separator.
+                const columnsLength:int = getColumnsLength();                
+                if (isCellSelectionMode() && (columnIndex < columnsLength - 1))
+                    bounds.width += 1;
+                
+                //if ((rowIndex < grid.dataProvider.length - 1) || (visibleRowIndices.length > grid.dataProvider.length))
+                //    bounds.height += 1;
+                const dataProviderLength:int = getDataProviderLength();

[... 697 lines stripped ...]