////////////////////////////////////////////////////////////////////////////////
//
//  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
{
	/*	
	import flash.display.InteractiveObject;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import flash.utils.getTimer;
	*/
	import mx.events.MouseEvent;
	import org.apache.royale.geom.Point;
	import org.apache.royale.geom.Rectangle;
	
	//import mx.controls.DataGrid;
	
	import mx.collections.ArrayList;
	import mx.collections.IList;
	import mx.core.IFactory;
	import mx.core.IVisualElement;
	import mx.core.UIComponent;
	import mx.core.UIComponentGlobals;
	import mx.core.mx_internal;
	import mx.events.CollectionEvent;
	import mx.events.CollectionEventKind;
	import mx.events.FlexEvent;
	import mx.events.PropertyChangeEvent;
	import mx.graphics.SolidColorStroke;
	import mx.utils.ObjectUtil;
	
	// import spark.collections.SubListView;
	import spark.components.gridClasses.CellPosition;
	import spark.components.gridClasses.GridDoubleClickMode;
	import spark.components.gridClasses.GridColumn;
	import spark.components.gridClasses.GridDimensions;
	// import spark.components.gridClasses.GridDimensionsView;
	import spark.components.gridClasses.GridLayout;
	import spark.components.gridClasses.GridSelection;
	import spark.components.gridClasses.GridSelectionMode;
	import spark.components.gridClasses.GridView;
	// import spark.components.gridClasses.GridViewLayout;
	import spark.components.gridClasses.IDataGridElement;
	import spark.components.gridClasses.IGridItemRenderer;
	import spark.components.supportClasses.GroupBase;
	import spark.components.supportClasses.IDataProviderEnhance;
	import spark.components.supportClasses.RegExPatterns;
	import spark.events.GridCaretEvent;
	import spark.events.GridEvent;
	import spark.layouts.VerticalLayout;
	import spark.layouts.supportClasses.LayoutBase;
	import spark.primitives.Line;
	import spark.primitives.Rect;
	// import spark.utils.MouseEventUtil;
	
	use namespace mx_internal;
	
	//--------------------------------------
	//  Events
	//--------------------------------------
	
	/**
	 *  Dispatched when the mouse button is pressed over a Grid cell.
	 *
	 *  @eventType spark.events.GridEvent.GRID_MOUSE_DOWN
	 * 
	 *  @langversion 3.0
	 *  @playerversion Flash 10
	 *  @playerversion AIR 2.5
	 *  @productversion Flex 4.5
	 */
	[Event(name="gridMouseDown", type="spark.events.GridEvent")]
	
	/**
	 *  Dispatched after a <code>gridMouseDown</code> event 
	 *  if the mouse moves before the button is released.
	 *
	 *  @eventType spark.events.GridEvent.GRID_MOUSE_DRAG
	 * 
	 *  @langversion 3.0
	 *  @playerversion Flash 10
	 *  @playerversion AIR 2.5
	 *  @productversion Flex 4.5
	 */
	[Event(name="gridMouseDrag", type="spark.events.GridEvent")]
	
	/**
	 *  Dispatched when the mouse button is released over a Grid cell.
	 *  During a drag operation, it is also dispatched after a 
	 *  <code>gridMouseDown</code> event 
	 *  when the mouse button is released, even if the mouse is no longer 
	 *  in the Grid.
	 *
	 *  @eventType spark.events.GridEvent.GRID_MOUSE_UP
	 * 
	 *  @langversion 3.0
	 *  @playerversion Flash 10
	 *  @playerversion AIR 2.5
	 *  @productversion Flex 4.5
	 */
	[Event(name="gridMouseUp", type="spark.events.GridEvent")]
	
	/**
	 *  Dispatched when the mouse enters a grid cell.
	 *
	 *  @eventType spark.events.GridEvent.GRID_ROLL_OVER
	 * 
	 *  @langversion 3.0
	 *  @playerversion Flash 10
	 *  @playerversion AIR 2.5
	 *  @productversion Flex 4.5
	 */
	[Event(name="gridRollOver", type="spark.events.GridEvent")]
	
	/**
	 *  Dispatched when the mouse leaves a grid cell.
	 *
	 *  @eventType spark.events.GridEvent.GRID_ROLL_OUT
	 * 
	 *  @langversion 3.0
	 *  @playerversion Flash 10
	 *  @playerversion AIR 2.5
	 *  @productversion Flex 4.5
	 */
	[Event(name="gridRollOut", type="spark.events.GridEvent")]
	
	/**
	 *  Dispatched when the mouse is clicked over a cell
	 *
	 *  @eventType spark.events.GridEvent.GRID_CLICK
	 * 
	 *  @langversion 3.0
	 *  @playerversion Flash 10
	 *  @playerversion AIR 2.5
	 *  @productversion Flex 4.5
	 */
	[Event(name="gridClick", type="spark.events.GridEvent")]
	
	/**
	 *  Dispatched when the mouse is double-clicked over a cell
	 *
	 *  @eventType spark.events.GridEvent.GRID_DOUBLE_CLICK
	 * 
	 *  @langversion 3.0
	 *  @playerversion Flash 10
	 *  @playerversion AIR 2.5
	 *  @productversion Flex 4.5
	 */
	[Event(name="gridDoubleClick", type="spark.events.GridEvent")]
	
	/**
	 *  Dispatched after the caret changes.  
	 *
	 *  @eventType spark.events.GridCaretEvent.CARET_CHANGE
	 *  
	 *  @langversion 3.0
	 *  @playerversion Flash 10
	 *  @playerversion AIR 2.5
	 *  @productversion Flex 4.5
	 */
	[Event(name="caretChange", type="spark.events.GridCaretEvent")]
	
	/**
	 *  The Grid control displays a list of data items called
	 *  its <i>data provider</i> in a scrollable table or "grid", one item per row.
	 *  Each of the grid's columns, defined by a GridColumn object,
	 *  displays a value based on the item for the corresponding row.
	 *  The grid's data provider is mutable, meaning its items can be added or
	 *  removed, or changed.  
	 *  Similarly, the list of columns is mutable.
	 * 
	 *  <p>The Grid component is intended to be used as a DataGrid skin part, or
	 *  as an element of other custom composite components.  
	 *  Therefore, it is not skinnable, it does not include a scroller or scrollbars, 
	 *  and it does not provide default mouse or keyboard event handling.</p>
	 * 
	 *  <p>Each visible Grid <i>cell</i> is displayed by a GridItemRenderer
	 *  instance created by using the <code>itemRenderer</code> property.  
	 *  specify an item renderer for each column.
	 *  Before it is displayed, each item renderer instance is configured 
	 *  with the value of the data provider item for that row.
	 *  Item renderers are created as needed and then, to keep creation
	 *  overhead to a minimum, pooled and recycled.</p>
	 *
	 *  <p>The Grid control supports a doubleClick event, according the <code>doubleClickMode</code>
	 *  property.</p>
	 * 
	 *  <p>The Grid control supports selection, according the <code>selectionMode</code>
	 *  property.  The set of selected row or cell indices can be modified or
	 *  queried programatically using the selection methods, such as 
	 *  <code>setSelectedIndex</code> or <code>selectionContainsIndex()</code>.</p>
	 * 
	 *  <p>The Grid control displays hover, caret, and selection indicators based  
	 *  on the <code>selectionMode</code> property and the corresponding 
	 *  row index and column index properties, such as 
	 *  <code>hoverRowIndex</code> and <code>columnRowIndex</code>.   
	 *  An indicator can be any visual element.  
	 *  Indicators that implement IGridVisualElement can configure themselves 
	 *  according to the row and column in which they are used.</p>
	 * 
	 *  <p>The Grid control supports smooth scrolling.  
	 *  Their vertical and horizontal scroll positions define the pixel origin 
	 *  of the visible part of the grid and the grid's layout only displays 
	 *  as many cell item renderers as are needed to fill the available space.</p>
	 *
	 *  <p>The Grid control supports variable height rows that automatically compute 
	 *  their height based on the item renderers' contents.  
	 *  This support is called grid <i>virtualization</i>
	 *  because the mapping from (pixel) scroll positions to row and column indices
	 *  is typically based on incomplete information about the preferred sizes 
	 *  for grid cells.  
	 *  The Grid caches the computed heights of rows that have been
	 *  scrolled into view and estimates the rest based on a single 
	 *  <code>typicalItem</code>.</p>
	 * 
	 *  <p>Transitions in DataGrid item renderers aren't supported. The GridItemRenderer class 
	 *  has disabled its <code>transitions</code> property so setting it will have no effect.</p>
	 *
	 *  @mxml <p>The <code>&lt;s:Grid&gt;</code> tag inherits all of the tag 
	 *  attributes of its superclass and adds the following tag attributes:</p>
	 *
	 *  <pre>
	 *  &lt;s:Grid 
	 *    <strong>Properties</strong>
	 *  /&gt;
	 *  </pre>
	 *
	 *  @see DataGrid
	 *  @see spark.components.gridClasses.GridColumn
	 * 
	 *  @langversion 3.0
	 *  @playerversion Flash 10
	 *  @playerversion AIR 2.5
	 *  @productversion Flex 4.5
	 */
	public class Grid extends Group implements IDataGridElement, IDataProviderEnhance
	{
		// include "../core/Version.as";
		
		//--------------------------------------------------------------------------
		//
		//  Variables
		//
		//--------------------------------------------------------------------------
		
		/**
		 *  @private
		 *  A list of functions to be called at commitProperties() time, after the dataProvider
		 *  has been set.  This list is used to defer making grid selection updates per the 
		 *  set methods for the selectedIndex, selectedIndices, selectedItem, selectedItems, 
		 *  selectedCell and selectedCells properties.
		 */
		private const deferredOperations:Vector.<Function> = new Vector.<Function>();
		
		/**
		 *  @private
		 *  Cache the dataItem that goes with the caretRowIndex so we can find the
		 *  rowIndex of the caret after a collection refresh event.
		 */    
		private var caretSelectedItem:Object = null;
		private var updateCaretForDataProviderChanged:Boolean = false;
		private var updateCaretForDataProviderChangeLastEvent:CollectionEvent;
		
		/**
		 *  @private
		 *  True while updateDisplayList is running.  Use to disable invalidateSize(),
		 *  invalidateDisplayList() here and in the GridLayer class.
		 */
		mx_internal var inUpdateDisplayList:Boolean = false;  
		
		/**
		 *  @private
		 *  True while doing a drag operation with the mouse.
		 */
		private var dragInProgress:Boolean = false;
		
		/**
		 *  @private
		 *  True if the columns were generated rather than explicitly set.
		 */
		private var generatedColumns:Boolean = false;
		
		//--------------------------------------------------------------------------
		//
		//  Constructor
		//
		//--------------------------------------------------------------------------
		
		/**
		 *  Constructor. 
		 *  
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5
		 */
		public function Grid()
		{
			super();
			/*
			layout = new GridLayout();
			
			MouseEventUtil.addDownDragUpListeners(this, 
				grid_mouseDownDragUpHandler, 
				grid_mouseDownDragUpHandler, 
				grid_mouseDownDragUpHandler);
			
			addEventListener(MouseEvent.MOUSE_UP, grid_mouseUpHandler);
			addEventListener(MouseEvent.MOUSE_MOVE, grid_mouseMoveHandler);
			addEventListener(MouseEvent.ROLL_OUT, grid_mouseRollOutHandler);   
			*/
		}
		
		/**
		 *  @private
		 *  Return the GridView which contains the specified cell.   If rowIndex == -1,
		 *  then return the topmost GridView that contains the specified column and 
		 *  if columnIndex == -1 then return the leftmost GridView that contains the specified row. 
		 */
		private function getGridViewAt(rowIndex:int, columnIndex:int):GridView
		{
			if ((rowIndex < 0) && (columnIndex < 0))
				return null;
			
			const gridLayout:GridLayout = layout as GridLayout;
			
			if ((rowIndex >= lockedRowCount) || (rowIndex == -1))
			{
				if ((columnIndex >= lockedColumnCount) || (columnIndex == -1))
					return gridLayout.centerGridView;
				
				return gridLayout.leftGridView;
			}
			
			return (columnIndex < lockedColumnCount) ? gridLayout.topLeftGridView : gridLayout.topGridView;
		}
		
		
		
		//--------------------------------------------------------------------------
		//
		//  Properties
		//
		//--------------------------------------------------------------------------
		
		/**
		 *  @private
		 */
		private function dispatchChangeEvent(type:String):void
		{
			if (hasEventListener(type))
			/*dispatchEvent(new Event(type))*/;
		}
		
		/**
		 *  @private
		 */
		private function dispatchFlexEvent(type:String):void
		{
			if (hasEventListener(type))
				dispatchEvent(new FlexEvent(type));
		}
		
		//----------------------------------
		//  anchorColumnIndex
		//----------------------------------
		
		private var _anchorColumnIndex:int = 0;
		
		/**
		 *  @private
		 *  True if either anchorColumnIndex or anchorRowIndex changes.
		 */
		private var anchorChanged:Boolean = false;
		
		[Bindable("anchorColumnIndexChanged")]
		
		/**
		 *  The column index of the <i>anchor</i> for the next shift selection.
		 *  The anchor is the item most recently selected. 
		 *  It defines the anchor item when selecting multiple items in the grid. 
		 *  When you select multiple items, the set of items extends from 
		 *  the anchor to the caret item.
		 *
		 *  <p>Grid event handlers should use this property to record the
		 *  location of the most recent unshifted mouse down or keyboard
		 *  event that defines one end of the next potential shift
		 *  selection.  
		 *  The caret index defines the other end.</p>
		 * 
		 *  @default 0
		 * 
		 *  @see spark.components.Grid#caretRowIndex
		 *  @see spark.components.Grid#caretColumnIndex
		 * 
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5 
		 */
		public function get anchorColumnIndex():int
		{
			return _anchorColumnIndex;
		}
		
		/**
		 *  @private
		 */
		public function set anchorColumnIndex(value:int):void
		{
			if (_anchorColumnIndex == value || 
				selectionMode == GridSelectionMode.SINGLE_ROW || 
				selectionMode == GridSelectionMode.MULTIPLE_ROWS)
			{
				return;
			}
			
			_anchorColumnIndex = value;
			
			anchorChanged = true;
			invalidateProperties();
			
			dispatchChangeEvent("anchorColumnIndexChanged");
		}
		
		//----------------------------------
		//  anchorRowIndex
		//----------------------------------
		
		private var _anchorRowIndex:int = 0; 
		
		[Bindable("anchorRowIndexChanged")]
		
		/**
		 *  The row index of the <i>anchor</i> for the next shift selection.
		 *  The anchor is the item most recently selected. 
		 *  It defines the anchor item when selecting multiple items in the grid. 
		 *  When you select multiple items, the set of items extends from 
		 *  the anchor to the caret item.
		 *
		 *  <p>Grid event handlers should use this property to record the
		 *  location of the most recent unshifted mouse down or keyboard
		 *  event that defines one end of the next potential shift
		 *  selection.  
		 *  The caret index defines the other end.</p>
		 * 
		 *  @default 0
		 *
		 *  @see spark.components.Grid#caretRowIndex
		 *  @see spark.components.Grid#caretColumnIndex
		 * 
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5 
		 */
		public function get anchorRowIndex():int
		{
			return _anchorRowIndex;
		}
		
		/**
		 *  @private
		 */
		public function set anchorRowIndex(value:int):void
		{
			if (_anchorRowIndex == value)
				return;
			
			_anchorRowIndex = value;
			
			anchorChanged = true;
			invalidateProperties();
			
			dispatchChangeEvent("anchorRowIndexChanged");
		}
		
		//----------------------------------
		//  caretIndicator
		//----------------------------------
		
		private var _caretIndicator:IFactory = null;
		
		[Bindable("caretIndicatorChanged")]
		
		/**
		 *  If <code>selectionMode</code> is <code>GridSelectionMode.SINGLE_ROW</code> or
		 *  <code>GridSelectionMode.MULTIPLE_ROWS</code>, 
		 *  a single visual element displayed for the caret row, 
		 *  If <code>selectionMode</code> is
		 *  <code>GridSelectionMode.SINGLE_CELL</code> or
		 *  <code>GridSelectionMode.MULTIPLE_CELLS</code>, the
		 *  visual element displayed for the caret cell.
		 *  
		 *  @default null
		 * 
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5
		 */
		public function get caretIndicator():IFactory
		{
			return _caretIndicator;
		}
		
		/**
		 *  @private
		 */
		public function set caretIndicator(value:IFactory):void
		{
			if (_caretIndicator == value)
				return;
			
			_caretIndicator = value;
			invalidateDisplayListFor("caretIndicator");
			dispatchChangeEvent("caretIndicatorChanged");
		}    
		
		//----------------------------------
		//  caretColumnIndex
		//----------------------------------
		
		private var _caretColumnIndex:int = -1;
		private var _oldCaretColumnIndex:int = -1;
		private var caretChanged:Boolean = false;
		
		[Bindable("caretColumnIndexChanged")]
		
		/**
		 *  If <code>showCaretIndicator</code> is <code>true</code>,
		 *  the column index of the <code>caretIndicator</code>.
		 
		 *  <p>If <code>selectionMode</code> is
		 *  <code>GridSelectionMode.SINGLE_ROW</code> or
		 *  <code>GridSelectionMode.MULTIPLE_ROWS</code> then the indicator
		 *  occupies the entire row and <code>caretColumnIndex</code> is ignored.  
		 *  If <code>selectionMode</code> is <code>GridSelectionMode.SINGLE_CELL</code> or
		 *  <code>GridSelectionMode.MULTIPLE_CELLS</code>, then the 
		 *  <code>caretIndicator</code> occupies the specified cell.</p>
		 * 
		 *  <p>Setting <code>caretColumnIndex</code> to -1 means that the column 
		 *  index is undefined and a cell caret is not shown.</p>
		 *  
		 *  @default -1
		 * 
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5 
		 */
		public function get caretColumnIndex():int
		{
			return _caretColumnIndex;
		}
		
		/**
		 *  @private
		 */
		public function set caretColumnIndex(value:int):void
		{
			if (_caretColumnIndex == value || value < -1)
				return;
			
			_caretColumnIndex = value;
			
			caretChanged = true;
			invalidateProperties();
			
			// Unconditionally invalidate because renderers may depend on
			// caretColumnIndex even when the caretIndicator doesn't exist.
			invalidateDisplayListFor("caretIndicator");         
			dispatchChangeEvent("caretColumnIndexChanged");
		}
		
		//----------------------------------
		//  caretRowIndex
		//----------------------------------
		
		private var _caretRowIndex:int = -1;
		private var _oldCaretRowIndex:int = -1;
		
		[Bindable("caretRowIndexChanged")]
		
		/**
		 *  If <code>showCaretIndicator</code> is <code>true</code>,   
		 *  the row index of the <code>caretIndicator</code>.
		 *  If <code>selectionMode</code> is
		 *  <code>GridSelectionMode.SINGLE_ROW</code> or
		 *  <code>GridSelectionMode.MULTIPLE_ROWS</code> then the indicator
		 *  occupies the entire row and the <code>caretColumnIndex</code> 
		 *  property is ignored.  
		 *  If <code>selectionMode</code> is <code>GridSelectionMode.SINGLE_CELL</code> or
		 *  <code>GridSelectionMode.MULTIPLE_CELLS</code>, then the <code>caretIndicator</code>
		 *  occupies the specified cell.
		 * 
		 *  <p>Setting <code>caretRowIndex</code> to -1 means that the row index 
		 *  is undefined and the caret will not be shown.</p>
		 * 
		 *  @default -1
		 * 
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5 
		 */
		public function get caretRowIndex():int
		{
			return _caretRowIndex;
		}
		
		/**
		 *  @private
		 */
		public function set caretRowIndex(value:int):void
		{
			if (_caretRowIndex == value || value < -1)
				return;
			
			_caretRowIndex = value;
			
			caretChanged = true;
			invalidateProperties();
			
			// Unconditionally invalidate because renderers may depend on
			// caretRowIndex even when the caretIndicator doesn't exist.
			invalidateDisplayListFor("caretIndicator");         
			dispatchChangeEvent("caretRowIndexChanged");
		}
		
		//----------------------------------
		//  clipAndEnableScrolling (private override)
		//----------------------------------
		
		private var _clipAndEnableScrolling:Boolean = false;
		
		/**
		 *  @private
		 */
		override public function get clipAndEnableScrolling():Boolean 
		{
			return _clipAndEnableScrolling;
		}
		
		/**
		 *  @private
		 */
		override public function set clipAndEnableScrolling(value:Boolean):void 
		{
			if (value == _clipAndEnableScrolling)
				return;
			
			_clipAndEnableScrolling = value;
			/*
			const gridLayout:GridLayout = layout as GridLayout;
			const topGridView:GridView = gridLayout.topGridView;        
			const leftGridView:GridView = gridLayout.leftGridView;
			const centerGridView:GridView = gridLayout.centerGridView;		
			
			if (topGridView) topGridView.clipAndEnableScrolling = value;
			if (leftGridView) leftGridView.clipAndEnableScrolling = value;
			if (centerGridView) centerGridView.clipAndEnableScrolling = value;
			*/
		}
		
		
		//----------------------------------
		//  contentHeight (private get override)
		//---------------------------------- 
		
		/**
		 *  @private
		 */
		override public function get contentHeight():Number 
		{
			return Math.ceil(gridDimensions.getContentHeight());
		}
		
		//----------------------------------
		//  contentWidth (private get override)
		//---------------------------------- 
		
		/**
		 *  @private
		 */    
		override public function get contentWidth():Number 
		{
			return Math.ceil(gridDimensions.getContentWidth());
		}
		
		//----------------------------------
		//  horizontalScrollPosition (private override)
		//----------------------------------
		
		private var _horizontalScrollPosition:Number = 0;
		
		[Bindable]
		[Inspectable(minValue="0.0")] 
		
		/**
		 *  @copy spark.core.IViewport#horizontalScrollPosition
		 *  
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 1.5
		 *  @productversion Flex 4
		 */
		override public function get horizontalScrollPosition():Number 
		{
			return _horizontalScrollPosition;
		}
		
		/**
		 *  @private
		 */
		/*override*/ 
		override public function set horizontalScrollPosition(value:Number):void 
		{
			if (_horizontalScrollPosition == value)
				return;
			/*
			const gridLayout:GridLayout = layout as GridLayout;
			const topGridView:GridView = gridLayout.topGridView;        
			const centerGridView:GridView = gridLayout.centerGridView;	
			
			if (centerGridView)
			{
				const gridViewLayout:GridViewLayout = centerGridView.gridViewLayout;
				const gridMaxHSP:Number = contentWidth - width;
				const centerContentWidth:Number = Math.ceil(gridViewLayout.gridDimensionsView.getContentWidth());
				const centerMaxHSP:Number = centerContentWidth - centerGridView.width;
				const hsp:Number = (gridMaxHSP > 0) ? (centerMaxHSP / gridMaxHSP) * value : 0;
				
				centerGridView.horizontalScrollPosition = hsp;
				
				if (topGridView)
					topGridView.horizontalScrollPosition = hsp;
			}
			*/
			_horizontalScrollPosition = value;
			
		}
		
		
		//----------------------------------
		//  isFirstRow
		//----------------------------------
		
		/**
		 *  Returns if the selectedIndex is equal to the first row.
		 *
		 *  @langversion 3.0
		 *  @playerversion Flash 11.1
		 *  @playerversion AIR 3.4
		 *  @productversion Flex 4.10
		 */
		public function get isFirstRow():Boolean
		{
			if (dataProvider && dataProvider.length > 0)
			{
				if (selectedIndex == 0)
				{
					return true;
				}
				else
				{
					return false;
				}
			}
			else
			{
				return false;
			}
		}
		
		
		//----------------------------------
		//  isLastRow
		//----------------------------------
		
		/**
		 *  Returns if the selectedIndex is equal to the last row.
		 *
		 *  @langversion 3.0
		 *  @playerversion Flash 11.1
		 *  @playerversion AIR 3.4
		 *  @productversion Flex 4.10
		 */
		public function get isLastRow():Boolean
		{
			if (dataProvider && dataProvider.length > 0)
			{
				if (selectedIndex == dataProvider.length - 1)
				{
					return true;
				}
				else
				{
					return false;
				}
			}
			else
			{
				return false;
			}
		}
		
		
		//----------------------------------
		//  verticalScrollPosition (private override)
		//----------------------------------
		
		private var _verticalScrollPosition:Number = 0;
		
		[Bindable]
		[Inspectable(minValue="0.0")] 
		
		/**
		 *  @copy spark.core.IViewport#verticalScrollPosition
		 *  
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 1.5
		 *  @productversion Flex 4
		 */
		override public function get verticalScrollPosition():Number 
		{
			return _verticalScrollPosition;
		}
		
		/**
		 *  @private
		 */
		/* override */ 
		override public function set verticalScrollPosition(value:Number):void 
		{
			if (_verticalScrollPosition == value)
				return;
			/*
			const gridLayout:GridLayout = layout as GridLayout;
			const leftGridView:GridView = gridLayout.leftGridView;
			const centerGridView:GridView = gridLayout.centerGridView;	
			
			if (centerGridView)
			{
				const gridViewLayout:GridViewLayout = centerGridView.gridViewLayout;
				const gridMaxVSP:Number = contentHeight - height;
				const centerContentHeight:Number = Math.ceil(gridViewLayout.gridDimensionsView.getContentHeight());
				const centerMaxVSP:Number = centerContentHeight - centerGridView.height;
				const vsp:Number = (gridMaxVSP > 0) ? (centerMaxVSP / gridMaxVSP) * value : 0;
				
				centerGridView.verticalScrollPosition = vsp;
				
				if (leftGridView)
					leftGridView.verticalScrollPosition = vsp;
			}
			*/
			_verticalScrollPosition = value;
			
		}
		
		//----------------------------------
		//  hoverIndicator
		//----------------------------------
		
		private var _hoverIndicator:IFactory = null;
		
		[Bindable("hoverIndicatorChanged")]
		
		/**
		 *  If <code>selectionMode</code> is
		 *  <code>GridSelectionMode.SINGLE_ROW</code> or
		 *  <code>GridSelectionMode.MULTIPLE_ROWS</code>. 
		 *  a single visual element displayed for the row under the mouse.
		 *  If <code>selectionMode</code> is
		 *  <code>GridSelectionMode.SINGLE_CELL</code> or
		 *  <code>GridSelectionMode.MULTIPLE_CELLS</code>,
		 *  the visual element for the cell.
		 * 
		 *  @default null
		 * 
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5
		 */
		public function get hoverIndicator():IFactory
		{
			return _hoverIndicator;
		}
		
		/**
		 *  @private
		 */
		public function set hoverIndicator(value:IFactory):void
		{
			if (_hoverIndicator == value)
				return;
			
			_hoverIndicator = value;
			invalidateDisplayListFor("hoverIndicator");
			dispatchChangeEvent("hoverIndicatorChanged");
		}    
		
		//----------------------------------
		//  hoverColumnIndex 
		//----------------------------------
		
		private var _hoverColumnIndex:int = -1;
		
		[Bindable("hoverColumnIndexChanged")]
		
		/**
		 *  If <code>showHoverIndicator</code> is <code>true</code>,  
		 *  Specifies column index of the <code>hoverIndicator</code>.
		 *  If <code>selectionMode</code> is <code>GridSelectionMode.SINGLE_ROW</code> or
		 *  <code>GridSelectionMode.MULTIPLE_ROWS</code>, then the indicator
		 *  occupies the entire row and <code>hoverColumnIndex</code> is ignored. 
		 *  If <code>selectionMode</code> is <code>GridSelectionMode.SINGLE_CELL</code> or
		 *  <code>GridSelectionMode.MULTIPLE_CELLS</code> then the <code>hoverIndicator</code>
		 *  occupies the specified cell.
		 *  
		 *  <p>Setting <code>hoverColumnIndex</code> to -1 (the default) means 
		 *  that the column index is undefined and a cell hover indicator is not displayed.</p>
		 * 
		 *  @default -1
		 * 
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5 
		 */
		public function get hoverColumnIndex():int
		{
			return _hoverColumnIndex;
		}
		
		/**
		 *  @private
		 */
		public function set hoverColumnIndex(value:int):void
		{
			if (_hoverColumnIndex == value)
				return;
			
			_hoverColumnIndex = value;
			
			// Unconditionally invalidate because renderers may depend on
			// hoverColumnIndex even when the hoverIndicator doesn't exist.
			invalidateDisplayListFor("hoverIndicator");
			dispatchChangeEvent("hoverColumnIndexChanged");
		}
		
		//----------------------------------
		//  hoverRowIndex
		//----------------------------------
		
		private var _hoverRowIndex:int = -1;
		
		[Bindable("hoverRowIndexChanged")]
		
		/**
		 *  If <code>showHoverIndicator</code> is <code>true</code>,  
		 *  specifies the column index of the <code>hoverIndicator</code>.
		 *  If <code>selectionMode</code> is
		 *  <code>GridSelectionMode.SINGLE_ROW</code> or
		 *  <code>GridSelectionMode.MULTIPLE_ROWS</code>, then the indicator
		 *  occupies the entire row and <code>hoverColumnIndex</code> is ignored.   
		 *  If <code>selectionMode</code> is <code>GridSelectionMode.SINGLE_CELL</code> or
		 *  <code>GridSelectionMode.MULTIPLE_CELLS</code> then the <code>hoverIndicator</code>
		 *  occupies the specified cell.
		 * 
		 *  <p>Setting <code>hoverRowIndex</code> to -1,the default, means that 
		 *  the row index is undefined and a hover indicator is not displayed.</p>
		 * 
		 *  @default -1
		 * 
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5 
		 */
		public function get hoverRowIndex():int
		{
			return _hoverRowIndex;
		}
		
		/**
		 *  @private
		 */
		public function set hoverRowIndex(value:int):void
		{
			if (_hoverRowIndex == value)
				return;
			
			_hoverRowIndex = value;
			
			// Unconditionally invalidate because renderers may depend on
			// hoverRowIndex even when the hoverIndicator doesn't exist.
			invalidateDisplayListFor("hoverIndicator");           
			dispatchChangeEvent("hoverRowIndexChanged");
		}
		
		//----------------------------------
		//  columns
		//----------------------------------    
		
		private var _columns:IList = null;
		private var columnsChanged:Boolean = false;
		
		[Bindable("columnsChanged")]
		[Inspectable(category="General")]
		
		/**
		 *  The list of GridColumn objectss displayed by this grid.  
		 *  Each column selects different data provider item properties 
		 *  to display.
		 *  
		 *  <p>GridColumn objects can only appear in the <code>columns</code> 
		 *  for a single Grid control.</p> 
		 *  
		 *  @default null
		 * 
		 *  @see spark.components.Grid#dataProvider
		 * 
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5 
		 */
		public function get columns():IList
		{
			return _columns;
		}
		
		/**
		 *  @private
		 */
		public function set columns(value:IList):void
		{
			if (_columns == value)
				return;
			
			// Remove the old column listener, and set each column's grid=null, columnIndex=-1.
			
			const oldColumns:IList = _columns;
			if (oldColumns)
			{
				oldColumns.removeEventListener(CollectionEvent.COLLECTION_CHANGE, columns_collectionChangeHandler);
				for (var index:int = 0; index < oldColumns.length; index++)
				{
					
					var oldColumn:GridColumn = GridColumn(oldColumns.getItemAt(index));
					oldColumn.setGrid(null);
					oldColumn.setColumnIndex(-1);
					
				}
			}
			
			_columns = value; 
			
			// Add the new columns listener, and set their grid,columnIndex properties.
			// The listener is a local method, so creating a weak reference to it (last 
			// addEventListener parameter) is safe, since the listener's lifetime is the 
			// same as this object.        
			
			const newColumns:IList = _columns;
			if (newColumns)
			{
				newColumns.addEventListener(CollectionEvent.COLLECTION_CHANGE, columns_collectionChangeHandler, false, 0); //, true);
				for (index = 0; index < newColumns.length; index++)
				{
					
					var newColumn:GridColumn = GridColumn(newColumns.getItemAt(index));
					newColumn.setGrid(this);
					newColumn.setColumnIndex(index);
					
				}
			}
			
			columnsChanged = true;
			generatedColumns = false;        
			invalidateProperties();
			invalidateSize();
			invalidateDisplayList();
			
			dispatchChangeEvent("columnsChanged");             
		}
		
		/**
		 *  @private
		 */
		private function getColumnsLength():uint
		{
			const columns:IList = columns;
			return (columns) ? columns.length : 0;
		}
		
		/**
		 *  @private
		 *  This method is similar to mx.controls.DataGrid/ls().
		 */
		private function generateColumns():IList
		{
			var item:Object = typicalItem;
			if (!item && dataProvider && (dataProvider.length > 0))
				item = dataProvider.getItemAt(0);
			
			var itemColumns:IList = null;
			if (item)
			{
				itemColumns = new ArrayList();
				const classInfo:Object = ObjectUtil.getClassInfo(item, ["uid", "mx_internal_uid"]);
				if (classInfo)
				{
					for each (var property:QName in classInfo.properties)
					{
						
						var column:GridColumn = new GridColumn();
						column.dataField = property.localName;
						itemColumns.addItem(column);
						
					}
				}
			}
			
			return itemColumns;
		}
		
		//----------------------------------
		//  dataProvider
		//----------------------------------
		
		private var _dataProvider:IList = null;
		private var dataProviderChanged:Boolean;
		
		[Bindable("dataProviderChanged")]
		[Inspectable(category="Data")]
		
		/**
		 *  A list of data items that correspond to the rows in the grid.   
		 *  Each grid column is associated with a property of the 
		 *  data items to display that property in the grid <i>cells</i>.
		 * 
		 *  @default null
		 * 
		 *  @see spark.components.Grid#columns
		 *  
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5 
		 */
		public function get dataProvider():IList
		{
			return _dataProvider;
		}
		
		/**
		 *  @private
		 */
		public function set dataProvider(value:IList):void
		{
			if (_dataProvider == value)
				return;
			
			const oldDataProvider:IList = dataProvider;
			if (oldDataProvider)
				oldDataProvider.removeEventListener(CollectionEvent.COLLECTION_CHANGE, dataProvider_collectionChangeHandler);
			
			_dataProvider = value;
			
			// The listener is a local method, so creating a weak reference to it (last addEventListener 
			// parameter) is safe, since the listener's lifetime is the same as this object.
			
			const newDataProvider:IList = dataProvider;
			if (newDataProvider)
				newDataProvider.addEventListener(CollectionEvent.COLLECTION_CHANGE, dataProvider_collectionChangeHandler, false, 0); //, true);        
			
			dataProviderChanged = true;
			invalidateProperties();
			invalidateSize();
			invalidateDisplayList();
			
			dispatchChangeEvent("dataProviderChanged");
		}
		
		//----------------------------------
		//  dataTipField
		//----------------------------------
		
		private var _dataTipField:String = null;
		
		[Bindable("dataTipFieldChanged")]
		[Inspectable(category="Data", defaultValue="null")]
		
		/**
		 *  @copy spark.components.gridClasses.GridColumn#dataTipField
		 * 
		 *  @default null
		 * 
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5 
		 */
		public function get dataTipField():String
		{
			return _dataTipField;
		}
		
		/**
		 *  @private
		 */
		public function set dataTipField(value:String):void
		{
			if (_dataTipField == value)
				return;
			
			_dataTipField = value;
			invalidateDisplayList();
			dispatchChangeEvent("dataTipFieldChanged");
		}
		
		//----------------------------------
		//  dataTipFunction
		//----------------------------------
		
		private var _dataTipFunction:Function = null;
		
		[Bindable("dataTipFunctionChanged")]
		[Inspectable(category="Data")]
		
		/**
		 *  @copy spark.components.gridClasses.GridColumn#dataTipFunction
		 *
		 *  @default null
		 * 
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5 
		 */
		public function get dataTipFunction():Function
		{
			return _dataTipFunction;
		}
		
		/**
		 *  @private
		 */
		public function set dataTipFunction(value:Function):void
		{
			if (_dataTipFunction == value)
				return;
			
			_dataTipFunction = value;
			invalidateDisplayList();        
			dispatchChangeEvent("dataTipFunctionChanged");
		}    
		
		
		//----------------------------------
		//  doubleClickMode
		//----------------------------------
		private var _doubleClickMode:String = GridDoubleClickMode.ROW;
		
		[Bindable("doubleClickModeChanged")]
		[Inspectable(category="General", enumeration="cell,grid,row", defaultValue="row")]
		
		/**
		 *  The doubleClick mode of the control.  Possible values are:
		 *  <code>GridDoubleClickMode.CELL</code>, 
		 *  <code>GridDoubleClickMode.GRID</code>, 
		 *  <code>GridDoubleClickMode.ROW</code>, 
		 * 
		 *  <p>Changing the doubleClickMode changes the double click
		 *  criteria for firing the doubleClick event</p>
		 *
		 *  @default GridDoubleClickMode.ROW
		 * 
		 *  @see spark.components.gridClasses.GridDoubleClickMode
		 * 
		 *  @langversion 3.0
		 *  @playerversion Flash 11.1
		 *  @playerversion AIR 3.4
		 *  @productversion Flex 4.10
		 */
		public function get doubleClickMode():String
		{
			return _doubleClickMode;
		}
		
		/**
		 *  @private
		 */
		public function set doubleClickMode(newValue:String):void
		{
			if (newValue == _doubleClickMode)
			{
				return;
			}
			
			switch(newValue)
			{
				case GridDoubleClickMode.CELL:
				case GridDoubleClickMode.GRID:
				case GridDoubleClickMode.ROW:
				{
					_doubleClickMode = newValue;
					
					dispatchChangeEvent("doubleClickModeChanged");
					
					break;
				}
			}
		}
		
		
		//----------------------------------
		//  gridDimensions (mx_internal)
		//----------------------------------
		
		private var _gridDimensions:GridDimensions = null;
		
		mx_internal function get gridDimensions():GridDimensions
		{
			if (!_gridDimensions)
				_gridDimensions = new GridDimensions();
			
			return _gridDimensions;
		}
		
		//----------------------------------
		//  itemRenderer
		//----------------------------------
		
		private var _itemRenderer:IFactory = null;
		private var itemRendererChanged:Boolean = false;
		
		[Bindable("itemRendererChanged")]
		[Inspectable(category="Data")]
		
		/**
		 *  The item renderer that's used for columns that do not specify one.
		 * 
		 *  @default null
		 * 
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5 
		 */
		public function get itemRenderer():IFactory
		{
			return _itemRenderer;
		}
		
		/**
		 *  @private
		 */
		public function set itemRenderer(value:IFactory):void
		{
			if (_itemRenderer == value)
				return;
			
			_itemRenderer = value;
			
			itemRendererChanged = true;
			invalidateProperties();
			invalidateSize();
			invalidateDisplayList();
			
			dispatchChangeEvent("itemRendererChanged");
		}    
		
		//----------------------------------
		//  columnSeparator
		//----------------------------------
		
		private var _columnSeparator:IFactory = null;
		
		[Bindable("columnSeparatorChanged")]
		
		/**
		 *  A visual element displayed between each column.
		 * 
		 *  @default null
		 * 
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5
		 */
		public function get columnSeparator():IFactory
		{
			return _columnSeparator;
		}
		
		/**
		 *  @private
		 */
		public function set columnSeparator(value:IFactory):void
		{
			if (_columnSeparator == value)
				return;
			
			_columnSeparator = value;
			invalidateDisplayList();
			dispatchChangeEvent("columnSeparatorChanged");
		}    
		
		//----------------------------------
		//  gridSelection (mx_internal)
		//----------------------------------
		
		/**
		 *  @private
		 */
		
		private var _gridSelection:GridSelection;
		mx_internal function get gridSelection():GridSelection
		{
			if (!_gridSelection)
				_gridSelection = createGridSelection();
			
			return _gridSelection;
		}
		
		/**
		 *  @private
		 *  If this Grid is serving as a DataGrid skin part, then this property is created 
		 *  by DataGrid/partAdded() and then set here.   It is only set once, unless that 
		 *  "grid" part is removed, at which point it's set to null.
		 */
		
		mx_internal function set gridSelection(value:GridSelection):void
		{
			_gridSelection = value;
		}
		
		//----------------------------------
		//  dataGrid
		//----------------------------------
		
		private var _dataGrid:DataGrid = null;
		
		[Bindable("dataGridChanged")]
		
		/**
		 *  The DataGrid control for which this Grid is used as the grid skin part.
		 * 
		 *  @default null
		 *
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5
		 */
		public function get dataGrid():DataGrid
		{
			return _dataGrid;
		}
		
		/**
		 *  @private
		 */
		public function set dataGrid(value:DataGrid):void
		{
			if (_dataGrid == value)
				return;
			
			_dataGrid = value;
			dispatchChangeEvent("dataGridChanged");
		}
		
		//----------------------------------
		//  lockedColumnCount
		//----------------------------------
		
		private var _lockedColumnCount:int = 0;
		
		[Bindable("lockedColumnCountChanged")]
		[Inspectable(category="General", defaultValue="0", minValue="0")]	
		
		/**
		 *  The first lockedColumnCount columns are "locked", i.e. they do not scroll horizontally. 
		 *  If lockedColumnCount is zero (the default) then changes to the horizontalScrollPosition
		 *  affect all columns.
		 * 
		 *  <p>The locked columns are displayed in the topGridView and, if lockedRowCount is also
		 *  greater than zero, the topLeftGridView.  The locked columns are separated from the remaining 
		 *  columns by a lockedColumnSeparator.</p>
		 * 
		 *  @default 0
		 * 
		 *  @see spark.components.gridClasses.GridColumn#topGridView 
		 *  @see spark.components.gridClasses.GridColumn#topLeftGridView
		 * 	@see spark.components.gridClasses.GridColumn#lockedColumnSeparator
		 *
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 5.0
		 */
		public function get lockedColumnCount():int
		{
			return _lockedColumnCount;
		}
		
		/**
		 *  @private
		 */
		public function set lockedColumnCount(value:int):void
		{
			if (_lockedColumnCount == value)
				return;
			
			invalidateProperties();
			invalidateSize();
			invalidateDisplayList();
			
			_lockedColumnCount = value;
			dispatchChangeEvent("lockedColumnCountChanged");
		}
		
		//----------------------------------
		//  lockedColumnsSeparator
		//----------------------------------
		
		private var _lockedColumnsSeparator:IFactory = null;
		
		[Bindable("lockedColumnsSeparatorChanged")]
		
		/**
		 *  A visual element displayed between the locked and unlocked columns.  The factory value of this
		 *  property is used to create the lockedColumnsSeparatorElement.
		 * 
		 *  @see spark.components.Grid#lockedRowsSeparatorElement	
		 * 
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 5.0
		 * 
		 *  @default null
		 */
		public function get lockedColumnsSeparator():IFactory
		{
			return _lockedColumnsSeparator;
		}
		
		/**
		 *  @private
		 */
		public function set lockedColumnsSeparator(value:IFactory):void
		{
			if (_lockedColumnsSeparator == value)
				return;
			
			_lockedColumnsSeparator = value;
			invalidateDisplayList();
			dispatchChangeEvent("lockedColumnsSeparatorChanged");
		} 
		
		//----------------------------------
		//  lockedRowCount
		//----------------------------------
		
		private var _lockedRowCount:int = 0;
		
		[Bindable("lockedRowCountChanged")]
		[Inspectable(category="General", defaultValue="0", minValue="0")]	
		
		/**
		 *  The first lockedRowCount rows are "locked", i.e. they do not scroll vertically. 
		 *  If lockedRowCount is zero (the default) then changes to the verticalScrollPosition
		 *  affect all rows.
		 * 
		 *  <p>The locked rows are displayed in the leftGridView and, if lockedColumnCount is also
		 *  greater than zero, the topLeftGridView.  The locked rows are separated from the remaining 
		 *  rows by a lockedRowSeparator.</p>
		 * 
		 *  @default 0
		 * 
		 *  @see spark.components.gridClasses.GridColumn#leftGridView 
		 *  @see spark.components.gridClasses.GridColumn#topLeftGridView
		 * 	@see spark.components.gridClasses.GridColumn#lockedRowSeparator
		 *
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 5.0
		 */
		public function get lockedRowCount():int
		{
			return _lockedRowCount;
		}
		
		/**
		 *  @private
		 */
		public function set lockedRowCount(value:int):void
		{
			if (_lockedRowCount == value)
				return;
			
			invalidateProperties();
			invalidateSize();
			invalidateDisplayList();
			
			_lockedRowCount = value;
			dispatchChangeEvent("lockedRowCountChanged");
		}
		
		//----------------------------------
		//  lockedRowsSeparator
		//----------------------------------
		
		private var _lockedRowsSeparator:IFactory = null;
		
		[Bindable("lockedRowsSeparatorChanged")]
		
		/**
		 *  A visual element displayed between the locked and unlocked rows.   The factory value of this
		 *  property is used to create the lockedRowsSeparatorElement.
		 * 
		 *  @default null
		 * 
		 *  @see spark.components.Grid#lockedRowsSeparatorElement
		 * 
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 5.0
		 */
		public function get lockedRowsSeparator():IFactory
		{
			return _lockedRowsSeparator;
		}
		
		/**
		 *  @private
		 */
		public function set lockedRowsSeparator(value:IFactory):void
		{
			if (_lockedRowsSeparator == value)
				return;
			
			_lockedRowsSeparator = value;
			invalidateDisplayList();
			dispatchChangeEvent("lockedRowsSeparatorChanged");
		}	
		
		//----------------------------------
		//  preserveSelection (delegates to gridSelection.preserveSelection)
		//----------------------------------
		
		[Inspectable(category="General", defaultValue="true")]
		
		/**
		 *  If <code>true</code>, the selection is preserved when the data provider 
		 *  refreshes its collection. 
		 *  Because this refresh requires each item in the selection to be saved, 
		 *  this action is not desirable if the selection is large.
		 *
		 *  @default true
		 * 
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5
		 */
		public function get preserveSelection():Boolean
		{
			return gridSelection.preserveSelection;
		}
		
		/**
		 *  @private
		 */    
		public function set preserveSelection(value:Boolean):void
		{
			gridSelection.preserveSelection = value;
		}
		
		//----------------------------------
		//  requestedMaxRowCount
		//----------------------------------
		
		private var _requestedMaxRowCount:int = 10;
		
		[Inspectable(category="General", defaultValue="10", minValue="-1")]
		
		/**
		 *  The measured height of the grid is large enough to display 
		 *  no more than <code>requestedMaxRowCount</code> rows.
		 * 
		 *  <p>This property has no effect if any of the following are true;
		 *  <ul>
		 *      <li><code>requestedRowCount</code> is set.</li>
		 *      <li>The actual size of the grid has been explicitly set.</li>
		 *  </ul>
		 *  </p>
		 * 
		 *  @default 10
		 *  
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5
		 */
		public function get requestedMaxRowCount():int
		{
			return _requestedMaxRowCount;
		}
		
		/**
		 *  @private
		 */
		public function set requestedMaxRowCount(value:int):void
		{
			if (_requestedMaxRowCount == value)
				return;
			
			_requestedMaxRowCount = value;
			invalidateSize();
		}      
		
		//----------------------------------
		//  requestedMinRowCount
		//----------------------------------
		
		private var _requestedMinRowCount:int = -1;
		
		[Inspectable(category="General", minValue="-1")]
		
		/**
		 *  The measured height of this grid is large enough to display 
		 *  at least <code>requestedMinRowCount</code> rows.
		 * 
		 *  <p>This property has no effect if any of the following are true;
		 *  <ul>
		 *      <li><code>requestedRowCount</code> is set.</li>
		 *      <li>The actual size of the grid has been explicitly set.</li>
		 *  </ul>
		 *  </p>
		 * 
		 *  @default -1
		 *  
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5
		 */
		public function get requestedMinRowCount():int
		{
			return _requestedMinRowCount;
		}
		
		/**
		 *  @private
		 */
		public function set requestedMinRowCount(value:int):void
		{
			if (_requestedMinRowCount == value)
				return;
			
			_requestedMinRowCount = value;
			invalidateSize();
		}    
		
		//----------------------------------
		//  requestedRowCount
		//----------------------------------
		
		private var _requestedRowCount:int = -1;
		
		[Inspectable(category="General", minValue="-1")]
		
		/**
		 *  The measured height of this grid is large enough to display 
		 *  the first <code>requestedRowCount</code> rows. 
		 * 
		 *  <p>If <code>requestedRowCount</code> is -1, then the measured
		 *  size will be big enough for all of the layout elements.</p>
		 * 
		 *  <p>If the actual size of the grid has been explicitly set,
		 *  then this property has no effect.</p>
		 * 
		 *  @default -1
		 *  
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4
		 */
		public function get requestedRowCount():int
		{
			return _requestedRowCount;
		}
		
		/**
		 *  @private
		 */
		public function set requestedRowCount(value:int):void
		{
			if (_requestedRowCount == value)
				return;
			
			_requestedRowCount = value;
			invalidateSize();
		}
		
		//----------------------------------
		//  requestedMinColumnCount
		//----------------------------------
		
		private var _requestedMinColumnCount:int = -1;
		
		[Inspectable(category="General", minValue="-1")]
		
		/**
		 *  The measured width of this grid is large enough to display 
		 *  at least <code>requestedMinColumnCount</code> columns.
		 * 
		 *  <p>This property has no effect if any of the following are true;
		 *  <ul>
		 *      <li><code>requestedColumnCount</code> is set.</li>
		 *      <li>The actual size of the grid has been explicitly set.</li>
		 *      <li>The grid is inside a Scroller component.</li>
		 *  </ul>
		 *  </p>
		 *  
		 *  @default -1
		 *  
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5
		 */
		public function get requestedMinColumnCount():int
		{
			return _requestedMinColumnCount;
		}
		
		/**
		 *  @private
		 */
		public function set requestedMinColumnCount(value:int):void
		{
			if (_requestedMinColumnCount == value)
				return;
			
			_requestedMinColumnCount = value;
			invalidateSize();
		}   
		
		//----------------------------------
		//  requestedColumnCount
		//----------------------------------
		
		private var _requestedColumnCount:int = -1;
		
		[Inspectable(category="General", minValue="-1")]
		
		/**
		 *  The measured width of this grid is large enough to display 
		 *  the first <code>requestedColumnCount</code> columns. 
		 *  If <code>requestedColumnCount</code> is -1, then the measured
		 *  width is big enough for all of the columns.
		 * 
		 *  <p>If the actual size of the grid has been explicitly set,
		 *  then this property has no effect.</p>
		 * 
		 *  @default -1
		 *  
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5
		 */
		public function get requestedColumnCount():int
		{
			return _requestedColumnCount;
		}
		
		/**
		 *  @private
		 */
		public function set requestedColumnCount(value:int):void
		{
			if (_requestedColumnCount == value)
				return;
			
			_requestedColumnCount = value;
			invalidateSize();
		}    
		
		//----------------------------------
		//  requireSelection
		//----------------------------------
		
		[Inspectable(category="General", defaultValue="false")]
		
		/**
		 *  If <code>true</code> and the <code>selectionMode</code> property is not 
		 *  <code>GridSelectionMode.NONE</code>, an item must always be selected 
		 *  in the grid.
		 *
		 *  @default false
		 * 
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5
		 */
		public function get requireSelection():Boolean
		{
			return gridSelection.requireSelection;
		}
		
		/**
		 *  @private
		 */    
		public function set requireSelection(value:Boolean):void
		{
			gridSelection.requireSelection = value;
			
			if (value)
				invalidateDisplayListFor("selectionIndicator");
		}
		
		//----------------------------------
		//  resizableColumns
		//----------------------------------
		
		private var _resizableColumns:Boolean = true;
		
		[Bindable("resizableColumnsChanged")]
		[Inspectable(category="General", defaultValue="true")]
		
		/**
		 *  Indicates whether the user can change the size of the columns.
		 *  If <code>true</code>, the user can stretch or shrink the columns of 
		 *  the DataGrid control by dragging the grid lines between the header cells.
		 *  If <code>true</code>, individual columns must also have their 
		 *  <code>resizable</code> properties set to <code>false</code> to 
		 *  prevent the user from resizing a particular column.  
		 *
		 *  @default true
		 *    
		 *  @see spark.components.gridClasses.GridColumn
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5
		 */
		public function get resizableColumns():Boolean
		{
			return _resizableColumns;
		}
		
		/**
		 *  @private
		 */        
		public function set resizableColumns(value:Boolean):void
		{
			if (value == resizableColumns)
				return;
			
			_resizableColumns = value;        
			dispatchChangeEvent("resizableColumnsChanged");            
		}
		
		//----------------------------------
		//  rowBackground
		//----------------------------------
		
		private var _rowBackground:IFactory = null;
		
		[Bindable("rowBackgroundChanged")]
		
		/**
		 *  A visual element that's displays the background for each row.  
		 * 
		 *  @default null
		 * 
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5
		 */
		public function get rowBackground():IFactory
		{
			return _rowBackground;
		}
		
		/**
		 *  @private
		 */
		public function set rowBackground(value:IFactory):void
		{
			if (_rowBackground == value)
				return;
			
			_rowBackground = value;
			invalidateDisplayList();
			dispatchChangeEvent("rowBackgroundChanged");
		}
		
		//----------------------------------
		//  rowHeight
		//----------------------------------
		
		private var _rowHeight:Number = NaN;
		private var rowHeightChanged:Boolean;
		
		[Bindable("rowHeightChanged")]
		[Inspectable(category="General", minValue="0.0")]
		
		/**
		 *  If <code>variableRowHeight</code> is <code>false</code>, then 
		 *  this property specifies the actual height of each row, in pixels.
		 * 
		 *  <p>If <code>variableRowHeight</code> is <code>true</code>, 
		 *  the value of this property is used as the estimated
		 *  height for rows that haven't been scrolled into view yet, rather
		 *  than the preferred height of renderers configured with the <code>typicalItem</code>.
		 *  Similarly, when the Grid pads its display with empty rows, this property
		 *  specifies the empty rows' height.</p>
		 * 
		 *  <p>If <code>variableRowHeight</code> is <code>false</code>, 
		 *  the default value of this property is the maximum preferred height
		 *  of the per-column renderers created for the <code>typicalItem</code>.</p>
		 * 
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5
		 */
		public function get rowHeight():Number
		{
			return _rowHeight;
		}
		
		/**
		 *  @private
		 */
		public function set rowHeight(value:Number):void
		{
			if (_rowHeight == value)
				return;
			
			_rowHeight = value;
			rowHeightChanged = true;        
			invalidateProperties();
			
			dispatchChangeEvent("rowHeightChanged");            
		}
		
		/**
		 *  @private
		 */	
		private function setFixedRowHeight(value:Number):void
		{
			if (_rowHeight == value)
				return;
			
			_rowHeight = value;
			dispatchChangeEvent("rowHeightChanged");		
		}
		
		//----------------------------------
		//  rowSeparator
		//----------------------------------
		
		private var _rowSeparator:IFactory = null;
		
		[Bindable("rowSeparatorChanged")]
		
		/**
		 *  A visual element that's displayed in between each row.
		 * 
		 *  @default null
		 * 
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5
		 */
		public function get rowSeparator():IFactory
		{
			return _rowSeparator;
		}
		
		/**
		 *  @private
		 */
		public function set rowSeparator(value:IFactory):void
		{
			if (_rowSeparator == value)
				return;
			
			_rowSeparator = value;
			invalidateDisplayList();
			dispatchChangeEvent("rowSeparatorChanged");
		}    
		
		//----------------------------------
		//  selectedCell
		//----------------------------------
		
		[Bindable("selectionChange")]
		[Bindable("valueCommit")]
		
		/**
		 *  If <code>selectionMode</code> is <code>GridSelectionMode.SINGLE_CELL</code> 
		 *  or <code>GridSelectionMode.MULTIPLE_CELLS</code>, returns the first
		 *  selected cell starting at row 0 column 0 and progressing through each
		 *  column in a row before moving to the next row.
		 * 
		 *  <p>When the user changes the selection by interacting with the 
		 *  control, the control dispatches the <code>selectionChange</code> 
		 *  event. When the user changes the selection programmatically, the 
		 *  control dispatches the <code>valueCommit</code> event.</p>
		 * 
		 *  <p> This property is intended to be used to initialize or bind to the
		 *  selection in MXML markup.  The <code>setSelectedCell()</code> method 
		 *  should be used for programatic selection updates, for example 
		 *  when writing a keyboard or mouse event handler. </p> 
		 *
		 *  @default null
		 * 
		 *  @return CellPosition of the first selected cell or null if there is
		 *  no cell selection.
		 *  
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5
		 */
		public function get selectedCell():CellPosition
		{
			var selectedCells:Vector.<CellPosition> = gridSelection.allCells();
			return selectedCells.length ? selectedCells[0] : null;
		}
		
		/**
		 *  @private
		 */
		public function set selectedCell(value:CellPosition):void
		{
			const rowIndex:int = (value) ? value.rowIndex : -1;
			const columnIndex:int = (value) ? value.columnIndex : -1;
			
			// Defer the selection change if we haven't been initialized
			
			if (!initialized)
			{
				// Append a deferred operation function that selects the specified cell
				
				var f:Function = function():void
				{
					if ((rowIndex != -1) && (columnIndex != -1))
						setSelectedCell(rowIndex, columnIndex);
					else
						clearSelection();
				}
				
				deferredOperations.push(f);  // function f() to be called by commitProperties()
				invalidateProperties();
			}
			else
			{
				if ((rowIndex != -1) && (columnIndex != -1))
					setSelectedCell(rowIndex, columnIndex);
				else
					clearSelection();            
			}
		}        
		
		//----------------------------------
		//  selectedCells
		//----------------------------------
		
		[Bindable("selectionChange")]
		[Bindable("valueCommit")]
		
		/**
		 *  If <code>selectionMode</code> is <code>GridSelectionMode.SINGLE_CELL</code> 
		 *  or <code>GridSelectionMode.MULTIPLE_CELLS</code>, returns a Vector
		 *  of CellPosition objects representing the positions of the selected
		 *  cells in the grid.
		 * 
		 *  <p>When the user changes the selection by interacting with the 
		 *  control, the control dispatches the <code>selectionChange</code> 
		 *  event. When the user changes the selection programmatically, the 
		 *  control dispatches the <code>valueCommit</code> event.</p>
		 * 
		 *  <p> This property is intended to be used to initialize or bind to the
		 *  selection in MXML markup.  The <code>setSelectedCell()</code> method 
		 *  should be used for programatic selection updates, for example when 
		 *  writing a keyboard or mouse event handler. </p> 
		 * 
		 *  <p>The default value is an empty <code>Vector.&lt;CellPosition&gt;</code></p>
		 * 
		 *  @return Vector of CellPosition objects where each element represents
		 *  a selected cell.
		 *  
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5
		 */
		public function get selectedCells():Vector.<CellPosition>
		{
			return gridSelection.allCells();
		}
		
		/**
		 *  @private
		 */
		public function set selectedCells(value:Vector.<CellPosition>):void
		{
			// Defensively deep-copy the incoming value; tolerate value=null
			
			var valueCopy:Vector.<CellPosition> = new Vector.<CellPosition>(0);
			if (value)
			{
				for each (var cell:CellPosition in value)
				valueCopy.push(new CellPosition(cell.rowIndex, cell.columnIndex));
			}
			
			// Defer the selection change if we haven't been initialized
			
			if (!initialized)
			{        
				// Append a deferred operation function that selects the specified cells
				
				var f:Function = function():void
				{
					doSetSelectedCells(valueCopy);
				}
				deferredOperations.push(f);  // function f() to be called by commitProperties()
				invalidateProperties();
			}
			else
			{
				doSetSelectedCells(valueCopy);
			}
		}          
		
		//----------------------------------
		//  selectedIndex
		//----------------------------------
		
		[Bindable("selectionChange")]
		[Bindable("valueCommit")]
		[Inspectable(category="General", defaultValue="-1")]
		
		/**
		 *  If <code>selectionMode</code> is <code>GridSelectionMode.SINGLE_ROW</code> 
		 *  or <code>GridSelectionMode.MULTIPLE_ROWS</code>, returns the
		 *  rowIndex of the first selected row. 
		 * 
		 *  <p>When the user changes the selection by interacting with the 
		 *  control, the control dispatches the <code>selectionChange</code> 
		 *  event. When the user changes the selection programmatically, the 
		 *  control dispatches the <code>valueCommit</code> event.</p>
		 * 
		 *  <p> This property is intended to be used to initialize or bind to the
		 *  selection in MXML markup.  The <code>setSelectedCell()</code> method should be used
		 *  for programatic selection updates, for example when writing a keyboard
		 *  or mouse event handler. </p> 
		 *
		 *  @default -1
		 * 
		 *  @return rowIndex of first selected row or -1 if there are no
		 *  selected rows.
		 *  
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5
		 */
		public function get selectedIndex():int
		{
			var selectedRows:Vector.<int> = gridSelection.allRows();
			return (selectedRows.length > 0) ? selectedRows[0] : -1;
		}
		
		/**
		 *  @private
		 */
		public function set selectedIndex(value:int):void
		{
			// Defer the selection change if we haven't been initialized
			
			if (!initialized)
			{        
				// Append a deferred operation function that selects the specified index
				
				var f:Function = function():void
				{
					if (value != -1)
						setSelectedIndex(value);
					else
						clearSelection();
				}
				deferredOperations.push(f);  // function f() to be called by commitProperties()
				invalidateProperties();
			}
			else
			{
				if (value != -1)
					setSelectedIndex(value);
				else
					clearSelection();
			}
		}
		
		//----------------------------------
		//  selectedIndices
		//----------------------------------
		
		[Bindable("selectionChange")]
		[Bindable("valueCommit")]
		[Inspectable(category="General")]
		
		/**
		 *  If <code>selectionMode</code> is <code>GridSelectionMode.SINGLE_ROW</code> 
		 *  or <code>GridSelectionMode.MULTIPLE_ROWS</code>, returns a Vector of 
		 *  the selected rows indices.  For all other selection modes, this 
		 *  method has no effect.
		 * 
		 *  <p>When the user changes the selection by interacting with the 
		 *  control, the control dispatches the <code>selectionChange</code> 
		 *  event. When the user changes the selection programmatically, the 
		 *  control dispatches the <code>valueCommit</code> event.</p>
		 * 
		 *  <p> This property is intended to be used to initialize or bind to the
		 *  selection in MXML markup.  The setSelectedCell() method should be used
		 *  for programatic selection updates, for example when writing a keyboard
		 *  or mouse event handler. </p> > 
		 *
		 *  <p>The default value is an empty <code>Vector.&lt;int&gt;</code></p>
		 * 
		 *  @return Vector of ints where each element is the index in 
		 *  data provider of the selected row.
		 *  
		 *  @see spark.components.Grid#dataProvider
		 * 
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5
		 */
		public function get selectedIndices():Vector.<int>
		{
			return gridSelection.allRows();
		}
		
		/**
		 *  @private
		 */
		public function set selectedIndices(value:Vector.<int>):void
		{
			// Defensively copy the incoming value; tolerate value=null
			
			const valueCopy:Vector.<int> = (value) ? value.concat() : new Vector.<int>(0);
			
			// Defer the selection change if we haven't been initialized
			
			if (!initialized)
			{        
				// Append a deferred operation function that selects the specified indices
				
				var f:Function = function():void
				{
					doSetSelectedIndices(valueCopy);
				};
				deferredOperations.push(f);  // function f() to be called by commitProperties()
				invalidateProperties();
			}
			else
			{
				doSetSelectedIndices(valueCopy);
			}
		}        
		
		//----------------------------------
		//  selectedItem
		//----------------------------------
		
		[Bindable("selectionChange")]
		[Bindable("valueCommit")]
		[Inspectable(category="General", defaultValue="null")]
		
		/**
		 *  If <code>selectionMode</code> is <code>GridSelectionMode.SINGLE_ROW</code> 
		 *  or <code>GridSelectionMode.MULTIPLE_ROWS</code>, returns the 
		 *  item in the the data provider that is currently selected or
		 *  <code>undefined</code> if no rows are selected.  
		 * 
		 *  <p>When the user changes the selection by interacting with the 
		 *  control, the control dispatches the <code>selectionChange</code> 
		 *  event. When the user changes the selection programmatically, the 
		 *  control dispatches the <code>valueCommit</code> event.</p>
		 * 
		 *  <p> This property is intended to be used to initialize or bind to the
		 *  selection in MXML markup.  The <code>setSelectedCell()</code> method should be used
		 *  for programatic selection updates, for example when writing a keyboard
		 *  or mouse event handler. </p> 
		 *  
		 *  @default null
		 * 
		 *  @return Vector of data provider items.
		 *  
		 *  @see spark.components.Grid#dataProvider
		 * 
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5
		 */
		public function get selectedItem():Object
		{
			var rowIndex:int = selectedIndex;
			if (rowIndex == -1)
				return undefined;
			
			return getDataProviderItem(rowIndex);           
		}
		
		/**
		 *  @private
		 */
		public function set selectedItem(value:Object):void
		{
			// Defer the selection change if we haven't been initialized
			
			if (!initialized)
			{        
				// Append a deferred operation function that selects the specified item
				
				var f:Function = function():void
				{
					if (!dataProvider)
						return;
					
					const rowIndex:int = dataProvider.getItemIndex(value);
					if (rowIndex == -1)
						clearSelection();
					else
						setSelectedIndex(rowIndex);
				}
				deferredOperations.push(f);  // function f() to be called by commitProperties()
				invalidateProperties();
			}
			else
			{
				if (!dataProvider)
					return;
				
				const rowIndex:int = dataProvider.getItemIndex(value);
				if (rowIndex == -1)
					clearSelection();
				else
					setSelectedIndex(rowIndex);            
			}
		}        
		
		//----------------------------------
		//  selectedItems
		//----------------------------------
		
		[Bindable("selectionChange")]
		[Bindable("valueCommit")]
		[Inspectable(category="General")]
		
		/**
		 *  If <code>selectionMode</code> is <code>GridSelectionMode.SINGLE_ROW</code> 
		 *  or <code>GridSelectionMode.MULTIPLE_ROWS</code>, returns a Vector of 
		 *  the dataProvider items that are currently selected.
		 * 
		 *  <p>When the user changes the selection by interacting with the 
		 *  control, the control dispatches the <code>selectionChange</code> 
		 *  event. When the user changes the selection programmatically, the 
		 *  control dispatches the <code>valueCommit</code> event.</p>
		 * 
		 *  <p> This property is intended to be used to initialize or bind to the
		 *  selection in MXML markup.  The setSelectedCell() method should be used
		 *  for programatic selection updates, for example when writing a keyboard
		 *  or mouse event handler. </p> 
		 *  
		 *  <p>The default value is an empty <code>Vector.&lt;Object&gt;</code></p>
		 * 
		 *  @return Vector of data provider items.
		 *  
		 *  @see spark.components.Grid#dataProvider
		 * 
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5
		 */
		public function get selectedItems():Vector.<Object>
		{
			var rowIndices:Vector.<int> = selectedIndices;
			if (rowIndices.length == 0)
				return undefined;
			
			var items:Vector.<Object> = new Vector.<Object>();
			
			for each (var rowIndex:int in rowIndices)        
			items.push(dataProvider.getItemAt(rowIndex));
			
			return items;
		}
		
		/**
		 *  @private
		 */
		public function set selectedItems(value:Vector.<Object>):void
		{
			// Defensively copy the incoming value; tolerate value=null
			
			const valueCopy:Vector.<Object> = (value) ? value.concat() : new Vector.<Object>(0);
			
			// Defer the selection change if we haven't been initialized
			
			if (!initialized)
			{        
				// Append a deferred operation function that selects the specified items        
				
				var f:Function = function():void
				{
					doSetSelectedItems(valueCopy);
				}
				deferredOperations.push(f);  // function f() to be called by commitProperties()
				invalidateProperties();
			}
			else
			{
				doSetSelectedItems(valueCopy);
			}
		}        
		
		//----------------------------------
		//  selectionIndicator
		//----------------------------------
		
		private var _selectionIndicator:IFactory = null;
		
		[Bindable("selectionIndicatorChanged")]
		
		/**
		 *  If <code>selectionMode</code> is <code>GridSelectionMode.SINGLE_ROW</code> or
		 *  <code>GridSelectionMode.MULTIPLE_ROWS</code>, 
		 *  a visual element that's displayed for each selected row, 
		 *  If <code>selectionMode</code> is
		 *  <code>GridSelectionMode.SINGLE_CELL</code> or
		 *  <code>GridSelectionMode.MULTIPLE_CELLS</code>,
		 *  a visual element displayed for each selected cell.
		 *  
		 *  @default null
		 * 
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5
		 */
		public function get selectionIndicator():IFactory
		{
			return _selectionIndicator;
		}
		
		/**
		 *  @private
		 */
		public function set selectionIndicator(value:IFactory):void
		{
			if (_selectionIndicator == value)
				return;
			
			_selectionIndicator = value;
			invalidateDisplayListFor("selectionIndicator");
			dispatchChangeEvent("selectionIndicatorChanged");
		}    
		
		//----------------------------------
		//  selectionLength (delegates to gridSelection.selectionLength)
		//----------------------------------
		
		[Bindable("selectionChange")]
		[Bindable("valueCommit")]
		
		/**
		 *  If <code>selectionMode</code> is <code>GridSelectionMode.SINGLE_ROW</code> 
		 *  or <code>GridSelectionMode.MULTIPLE_ROWS</code>, 
		 *  returns the number of selected rows. 
		 *  If <code>selectionMode</code> is <code>GridSelectionMode.SINGLE_CELLS</code> 
		 *  or <code>GridSelectionMode.MULTIPLE_CELLS</code>, 
		 *  returns the number of selected cells.
		 * 
		 *  @default 0
		 * 
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5
		 */
		public function get selectionLength():int
		{
			return gridSelection.selectionLength;   
		}
		
		//----------------------------------
		//  selectionMode (delegates to gridSelection.selectionMode)
		//----------------------------------
		
		[Bindable("selectionModeChanged")]
		[Inspectable(category="General", enumeration="none,singleRow,multipleRows,singleCell,multipleCells", defaultValue="singleRow")]
		
		/**
		 *  The selection mode of the control.  Possible values are:
		 *  <code>GridSelectionMode.MULTIPLE_CELLS</code>, 
		 *  <code>GridSelectionMode.MULTIPLE_ROWS</code>, 
		 *  <code>GridSelectionMode.NONE</code>, 
		 *  <code>GridSelectionMode.SINGLE_CELL</code>, and 
		 *  <code>GridSelectionMode.SINGLE_ROW</code>.
		 * 
		 *  <p>Changing the selectionMode causes the current selection to be 
		 *  cleared and the caretRowIndex and caretColumnIndex to be set to -1.</p>
		 *
		 *  @default GridSelectionMode.SINGLE_ROW
		 * 
		 *  @see spark.components.gridClasses.GridSelectionMode
		 * 
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5
		 */
		public function get selectionMode():String
		{
			return gridSelection.selectionMode;
		}
		
		/**
		 *  @private
		 */
		public function set selectionMode(value:String):void
		{
			if (selectionMode == value)
				return;
			
			gridSelection.selectionMode = value;
			if (selectionMode != value) // value wasn't a valid GridSelectionMode constant
				return;
			
			initializeAnchorPosition();
			if (!requireSelection)
				initializeCaretPosition();
			
			invalidateDisplayListFor("selectionIndicator");
			
			dispatchChangeEvent("selectionModeChanged");
		}
		
		//----------------------------------
		//  showCaret
		//----------------------------------
		
		/**
		 *  @private
		 */
		private var _showCaret:Boolean = false;
		
		[Bindable("showCaretChanged")]    
		
		/**
		 *  Determines if the caret is visible.
		 *  TBD: when is this property automatically set?
		 */
		public function get showCaret():Boolean
		{
			return _showCaret;
		}
		
		/**
		 *  @private
		 */
		public function set showCaret(value:Boolean):void
		{
			if (_showCaret == value)
				return;
			
			_showCaret = value;
			invalidateDisplayListFor("caretIndicator");        
			dispatchChangeEvent("showCaretChanged");       
		}    
		
		//----------------------------------
		//  showDataTips
		//----------------------------------
		
		private var _showDataTips:Boolean = false;
		
		[Bindable("showDataTipsChanged")]
		[Inspectable(category="Data", defaultValue="false")]
		
		/**
		 *  If <code>true</code> then a dataTip is displayed for all visible cells.  
		 *  If <code>false</code>, the default,
		 *  then a dataTip is only displayed if the column's 
		 *  <code>showDataTips</code> property is <code>true</code>.
		 * 
		 *  @default false
		 *  
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5
		 */
		public function get showDataTips():Boolean
		{
			return _showDataTips;
		}
		
		/**
		 *  @private
		 */
		public function set showDataTips(value:Boolean):void
		{
			if (_showDataTips == value)
				return;
			
			_showDataTips = value;
			invalidateDisplayList();
			dispatchChangeEvent("showDataTipsChanged");
		}
		
		//----------------------------------
		//  typicalItem
		//----------------------------------
		
		private var _typicalItem:Object = null;
		private var typicalItemChanged:Boolean = false;
		
		[Bindable("typicalItemChanged")]
		[Inspectable(category="Data")]
		
		/**
		 *  The grid's layout ensures that columns whose width is not specified are wide
		 *  enough to display an item renderer for this default data provider item.  
		 *  If a typical item is not specified, then the first data provider item is used.
		 * 
		 *  <p>Restriction: if the <code>typicalItem</code> is an IVisualItem, it must not 
		 *  also be a member of the data provider.</p>
		 * 
		 *  @default null
		 *  
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5
		 */
		public function get typicalItem():Object
		{
			return _typicalItem;
		}
		
		/**
		 *  @private
		 */
		public function set typicalItem(value:Object):void
		{
			if (_typicalItem == value)
				return;
			
			_typicalItem = value;
			invalidateTypicalItemRenderer();
			dispatchChangeEvent("typicalItemChanged");
		}
		
		/**
		 *  Clears cached column width data that had been based on the 
		 *  <code>typicalItem</code> property, and requests a new layout pass.   
		 *  Call this method if some aspect of the <code>typicalItem</code> 
		 *  has changed that should be reflected by the Grid's layout.  
		 * 
		 *  <p>This method is called automatically if the <code>typicalItem</code> 
		 *  is changed directly. That means if the property is set to a new value 
		 *  that is not "==" to current value.</p>
		 * 
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 1.5
		 *  @productversion Flex 4.5 
		 */
		public function invalidateTypicalItemRenderer():void
		{
			typicalItemChanged = true;       
			invalidateProperties();
			invalidateSize();
			invalidateDisplayList();
		}
		
		//----------------------------------
		//  variableRowHeight
		//----------------------------------
		
		private var _variableRowHeight:Boolean = false;
		private var variableRowHeightChanged:Boolean = false;
		
		[Bindable("variableRowHeightChanged")]
		[Inspectable(category="General", defaultValue="false")]
		
		/**
		 *  If <code>true</code>, each row's height is the maximum of 
		 *  preferred heights of the cells displayed so far.
		 * 
		 *  <p>If <code>false</code>, the height of each row is just 
		 *  the value of the <code>rowHeight</code> property.
		 *  If <code>rowHeight</code> isn't specified, then the height of 
		 *  each row is defined by the <code>typicalItem</code> property.</p>
		 * 
		 *  @default false
		 * 
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5
		 */
		public function get variableRowHeight():Boolean
		{
			return _variableRowHeight;
		}
		
		/**
		 *  @private
		 */        
		public function set variableRowHeight(value:Boolean):void
		{
			if (value == variableRowHeight)
				return;
			
			_variableRowHeight = value;        
			variableRowHeightChanged = true;        
			invalidateProperties();
			
			dispatchChangeEvent("variableRowHeightChanged");            
		}
		
		//----------------------------------
		//  gridView
		//----------------------------------
		
		private var _gridView:IFactory = null;
		
		[Bindable("gridViewChanged")]
		
		/**
		 *  Used to initialize this grid's gridViews: centerGridView, leftGridView, topGridView, topLeftGridView. 
		 *  GridViews are created as needed, depending on the values of lockedRowCount and lockedColumnCount.
		 * 
		 *  @default null.
		 * 
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 5.0
		 */
		public function get gridView():IFactory
		{
			return _gridView;
		}
		
		/**
		 *  @private
		 */        
		public function set gridView(value:IFactory):void
		{
			if (value == _gridView)
				return;
			
			_gridView = value;
			invalidateProperties();
			
			// TBD clear everything
			
			dispatchChangeEvent("gridViewChanged");            
		}
		
		//--------------------------------------------------------------------------
		//
		//  GridSelection Cover Methods
		//
		//--------------------------------------------------------------------------
		
		/**
		 *  If <code>selectionMode</code> is 
		 *  <code>GridSelectionMode.MULTIPLE_ROWS</code>, selects all rows and
		 *  removes the caret or if <code>selectionMode</code> is 
		 *  <code>GridSelectionMode.MULTIPLE_CELLS</code> selects all cells  
		 *  and removes the caret.  For all other selection modes, this method 
		 *  has no effect.
		 *
		 *  <p>If items are added to the <code>dataProvider</code> or 
		 *  <code>columns</code> are added after this method is called, the
		 *  new rows or cells in the new column will be selected.</p>
		 * 
		 *  <p>This implicit "selectAll" mode ends when any of the following occur:
		 *  <ul>
		 *    <li>selection is cleared using <code>clearSelection</code></li>
		 *    <li>selection reset using one of <code>setSelectedCell</code>, 
		 *    <code>setSelectedCells</code>, <code>setSelectedIndex</code>, 
		 *    <code>selectIndices</code></li>
		 *    <li><code>dataProvider</code> is refreshed and <code>preserveSelection</code> is false</li>
		 *    <li><code>dataProvider</code> is reset</li>
		 *    <li><code>columns</code> is refreshed, 
		 *    <code>preserveSelection</code> is <code>false</code> and 
		 *    <code>selectionMode</code> is 
		 *    <code>GridSelectionMode.MULTIPLE_CELLS</code></li>
		 *    <li><code>columns</code> is reset and <code>selectionMode</code> is 
		 *    <code>GridSelectionMode.MULTIPLE_CELLS</code></li> 
		 *  </ul></p>
		 * 
		 *  @return <code>true</code> if the selection changed.
		 *    
		 *  @see spark.components.Grid#clearSelection
		 *  @see spark.components.Grid#selectIndices
		 *  @see spark.components.Grid#setSelectedCell
		 *  @see spark.components.Grid#setSelectedCells
		 *  @see spark.components.Grid#setSelectedIndex
		 * 
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5
		 */
		public function selectAll():Boolean
		{           
			// Need to apply pending dataProvider and column changes so selection
			// isn't reset after it is set here.
			/*if (invalidatePropertiesFlag)
				UIComponentGlobals.layoutManager.validateClient(this, false);*/
			
			const selectionChanged:Boolean = gridSelection.selectAll();
			if (selectionChanged)
			{               
				initializeCaretPosition();               
				invalidateDisplayListFor("selectionIndicator");
				dispatchFlexEvent(FlexEvent.VALUE_COMMIT);
			}
			
			return selectionChanged;
		}
		
		/**
		 *  Removes all of the selected rows and cells, if <code>selectionMode</code>  
		 *  is not <code>GridSelectionMode.NONE</code>.  Removes the caret and
		 *  sets the anchor to the initial item.
		 *
		 *  @return <code>true</code> if the selection changed.
		 *  <code>false</code> if there was nothing previously selected.
		 *    
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5
		 */
		public function clearSelection():Boolean
		{
			// Need to apply pending dataProvider and column changes so selection
			// isn't reset after it is set here.
			/*if (invalidatePropertiesFlag)
				UIComponentGlobals.layoutManager.validateClient(this, false);*/
			
			const selectionChanged:Boolean = gridSelection.removeAll();
			if (selectionChanged)
			{
				invalidateDisplayListFor("selectionIndicator");
				dispatchFlexEvent(FlexEvent.VALUE_COMMIT);
			}
			
			// Remove caret and reset the anchor.
			initializeCaretPosition();
			initializeAnchorPosition();
			
			return selectionChanged;
		}
		
		//----------------------------------
		//  selection for rows
		//----------------------------------    
		
		/**
		 *  If <code>selectionMode</code> is <code>GridSelectionMode.SINGLE_ROW</code>
		 *  or <code>GridSelectionMode.MULTIPLE_ROWS</code>, returns <code>true</code> 
		 *  if the row at <code>index</code> is in the current selection.
		 * 
		 *  <p>The <code>rowIndex</code> is the index in the data provider
		 *  of the item containing the selected cell.</p>
		 *
		 *  @param rowIndex The 0-based row index of the row.
		 * 
		 *  @return <code>true</code> if the selection contains the row.
		 *    
		 *  @see spark.components.Grid#dataProvider
		 * 
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5
		 */
		public function selectionContainsIndex(rowIndex:int):Boolean 
		{
			return gridSelection.containsRow(rowIndex);
		}
		
		/**
		 *  If <code>selectionMode</code> is 
		 *  <code>GridSelectionMode.MULTIPLE_ROWS</code>, returns <code>true</code> 
		 *  if the rows in <code>indices</code> are in the current selection.
		 * 
		 *  @param rowIndices Vector of 0-based row indices to include in selection. 
		 * 
		 *  @return <code>true</code> if the current selection contains these rows.
		 *    
		 *  @see spark.components.Grid#dataProvider
		 * 
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5
		 */
		public function selectionContainsIndices(rowIndices:Vector.<int>):Boolean 
		{
			return gridSelection.containsRows(rowIndices);
		}
		
		/**
		 *  If <code>selectionMode</code>
		 *  is <code>GridSelectionMode.SINGLE_ROW</code> or 
		 *  <code>GridSelectionMode.MULTIPLE_ROWS</code>, sets the selection and 
		 *  the caret position to this row.
		 *  For all other selection modes, this method has no effect.
		 * 
		 *  <p>The <code>rowIndex</code> is the index in the data provider
		 *  of the item containing the selected cell.</p>
		 *
		 *  @param rowIndex The 0-based row index of the cell.
		 *
		 *  @return <code>true</code> if if no errors.
		 *  <code>false</code> if <code>index</code> is invalid, or
		 *  the <code>selectionMode</code> is invalid. 
		 *    
		 *  @see spark.components.Grid#caretColumnIndex
		 *  @see spark.components.Grid#caretRowIndex
		 *  @see spark.components.Grid#dataProvider
		 * 
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5
		 */
		public function setSelectedIndex(rowIndex:int):Boolean
		{
			// Need to apply pending dataProvider and column changes so selection
			// isn't reset after it is set here.
			/*if (invalidatePropertiesFlag)
				UIComponentGlobals.layoutManager.validateClient(this, false);*/
			
			const selectionChanged:Boolean = gridSelection.setRow(rowIndex);
			if (selectionChanged)
			{
				caretRowIndex = rowIndex;
				caretColumnIndex = -1;
				
				invalidateDisplayListFor("selectionIndicator");
				dispatchFlexEvent(FlexEvent.VALUE_COMMIT);
			}
			
			return selectionChanged;
		}
		
		/**
		 *  If <code>selectionMode</code>
		 *  is <code>GridSelectionMode.MULTIPLE_ROWS</code>, adds this row to
		 *  the selection and sets the caret position to this row.
		 *  For all other selection modes, this method has no effect.
		 * 
		 *  <p>The <code>rowIndex</code> is the index in the data provider 
		 *  of the item containing the selected cell.</p>
		 *
		 *  @param rowIndex The 0-based row index of the cell.
		 *
		 *  @return <code>true</code> if no errors.
		 *  <code>false</code> if <code>index</code> is invalid or
		 *  the <code>selectionMode</code> is invalid. 
		 *    
		 *  @see spark.components.Grid#caretColumnIndex
		 *  @see spark.components.Grid#caretRowIndex
		 *  @see spark.components.Grid#dataProvider
		 * 
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5
		 */
		public function addSelectedIndex(rowIndex:int):Boolean
		{
			// Need to apply pending dataProvider and column changes so selection
			// isn't reset after it is set here.
			/*if (invalidatePropertiesFlag)
				UIComponentGlobals.layoutManager.validateClient(this, false);*/
			
			const selectionChanged:Boolean = gridSelection.addRow(rowIndex);
			if (selectionChanged)
			{
				caretRowIndex = rowIndex;
				caretColumnIndex = -1;                
				
				invalidateDisplayListFor("selectionIndicator");
				dispatchFlexEvent(FlexEvent.VALUE_COMMIT);
			}
			
			return selectionChanged;
		}
		
		/**
		 *  If <code>selectionMode</code>
		 *  is <code>GridSelectionMode.SINGLE_ROW</code> or 
		 *  <code>GridSelectionMode.MULTIPLE_ROWS</code>, removes this row
		 *  from the selection and sets the caret position to this row.
		 *  For all other selection modes, this method has no effect.
		 * 
		 *  <p>The <code>rowIndex</code> is the index in the data provider 
		 *  of the item containing the selected cell.</p>
		 *
		 *  @param rowIndex The 0-based row index of the cell.
		 *
		 *  @return <code>true</code> if no errors.
		 *  <code>false</code> if <code>index</code> is invalid or
		 *  the <code>selectionMode</code> is invalid. 
		 *       
		 *  @see spark.components.Grid#caretColumnIndex
		 *  @see spark.components.Grid#caretRowIndex
		 *  @see spark.components.Grid#dataProvider
		 * 
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5
		 */
		public function removeSelectedIndex(rowIndex:int):Boolean
		{
			// Need to apply pending dataProvider and column changes so selection
			// isn't reset after it is set here.
			/*if (invalidatePropertiesFlag)
				UIComponentGlobals.layoutManager.validateClient(this, false);*/
			
			const selectionChanged:Boolean = gridSelection.removeRow(rowIndex);
			if (selectionChanged)
			{
				caretRowIndex = rowIndex;
				caretColumnIndex = -1;
				
				invalidateDisplayListFor("selectionIndicator");
				dispatchFlexEvent(FlexEvent.VALUE_COMMIT);
			}
			
			return selectionChanged;
		}
		
		/**
		 *  If <code>selectionMode</code> is <code>GridSelectionMode.MULTIPLE_ROWS</code>,
		 *  sets the selection to the specfied rows and the caret position to
		 *  <code>endRowIndex</code>.
		 *  For all other selection modes, this method has no effect.
		 * 
		 *  <p>Each index represents an item in the data provider  
		 *  to include in the selection.</p>
		 *
		 *  @param rowIndex 0-based row index of the first row in the selection.
		 * 
		 *  @param rowCount Number of rows in the selection.
		 * 
		 *  @return <code>true</code> if no errors.
		 *  <code>false</code> if any of the indices are invalid, 
		 *  if <code>startRowIndex</code> is not less than or equal to 
		 *  <code>endRowIndex</code>, or the <code>selectionMode</code> is invalid. 
		 *    
		 *  @see spark.components.Grid#dataProvider
		 * 
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5
		 */
		public function selectIndices(rowIndex:int, rowCount:int):Boolean
		{
			// Need to apply pending dataProvider and column changes so selection
			// isn't reset after it is set here.
			/*if (invalidatePropertiesFlag)
				UIComponentGlobals.layoutManager.validateClient(this, false);*/
			
			const selectionChanged:Boolean = gridSelection.setRows(rowIndex, rowCount);
			if (selectionChanged)
			{
				caretRowIndex = rowIndex + rowCount - 1;
				caretColumnIndex = -1;
				
				invalidateDisplayListFor("selectionIndicator");
				dispatchFlexEvent(FlexEvent.VALUE_COMMIT);
			}
			
			return selectionChanged;
		}
		
		//----------------------------------
		//  selection for cells
		//----------------------------------    
		
		/**
		 *  If <code>selectionMode</code> is <code>GridSelectionMode.SINGLE_CELL</code>
		 *  or <code>GridSelectionMode.MULTIPLE_CELLS</code>, returns <code>true</code> 
		 *  if the cell is in the current selection.
		 * 
		 *  <p>The <code>rowIndex</code> must be between 0 and the
		 *  length of the data provider.  The <code>columnIndex</code>
		 *  must be between 0 and the length of <code>columns</code>. </p>
		 *
		 *  @param rowIndex The 0-based row index of the cell.
		 *
		 *  @param columnIndex The 0-based column index of the cell.
		 *  
		 *  @return <code>true</code> if the current selection contains the cell.
		 * 
		 *  @see spark.components.Grid#columns
		 *  @see spark.components.Grid#dataProvider
		 * 
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5
		 */
		public function selectionContainsCell(rowIndex:int, columnIndex:int):Boolean
		{
			return gridSelection.containsCell(rowIndex, columnIndex);
		}
		
		/**
		 *  If <code>selectionMode</code> is 
		 *  <code>GridSelectionMode.MULTIPLE_CELLS</code>, returns <code>true</code> 
		 *  if the cells in the cell region are in the current selection.
		 * 
		 *  <p>The <code>rowIndex</code> must be between 0 and the
		 *  length of the data provider.  The <code>columnIndex</code>
		 *  must be between 0 and the length of <code>columns</code>. </p>
		 *
		 *  @param rowIndex The 0-based row index of the cell.
		 *
		 *  @param columnIndex The 0-based column index of the cell.
		 *  
		 *  @param rowCount Number of rows, starting at <code>rowIndex</code> to 
		 *  include in the cell region.
		 *
		 *  @param columnCount Number of columns, starting at 
		 *  <code>columnIndex</code> to include in the cell region.
		 * 
		 *  @return <code>true</code> if the current selection contains all 
		 *  the cells in the cell region.
		 * 
		 *  @see spark.components.Grid#columns
		 *  @see spark.components.Grid#dataProvider
		 * 
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5
		 */
		public function selectionContainsCellRegion(rowIndex:int, columnIndex:int, 
													rowCount:int, columnCount:int):Boolean
		{
			return gridSelection.containsCellRegion(rowIndex, columnIndex, 
				rowCount, columnCount);
		}
		
		/**
		 *  If <code>selectionMode</code>
		 *  is <code>GridSelectionMode.SINGLE_CELL</code> or 
		 *  <code>GridSelectionMode.MULTIPLE_CELLS</code>, sets the selection
		 *  and the caret position to this cell.
		 *  For all other selection modes, this method has no effect.
		 * 
		 *  <p>The <code>rowIndex</code> is the index in the data provider 
		 *  of the item containing the selected cell.  The <code>columnIndex</code>
		 *  is the index in <code>columns</code> of the column containing the
		 *  selected cell.</p>
		 *
		 *  @param rowIndex The 0-based row index of the cell.
		 *
		 *  @param columnIndex The 0-based column index of the cell.
		 * 
		 *  @return <code>true</code> if no errors.
		 *  <code>false</code> if <code>rowIndex</code> 
		 *  or <code>columnIndex</code> is invalid or the <code>selectionMode</code> 
		 *  is invalid.     
		 *  
		 *  @see spark.components.Grid#caretColumnIndex
		 *  @see spark.components.Grid#caretRowIndex
		 *  @see spark.components.Grid#columns
		 *  @see spark.components.Grid#dataProvider
		 * 
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5
		 */
		public function setSelectedCell(rowIndex:int, columnIndex:int):Boolean
		{
			// Need to apply pending dataProvider and column changes so selection
			// isn't reset after it is set here.
			/*if (invalidatePropertiesFlag)
				UIComponentGlobals.layoutManager.validateClient(this, false);*/
			
			const selectionChanged:Boolean = gridSelection.setCell(rowIndex, columnIndex);
			if (selectionChanged)
			{
				caretRowIndex = rowIndex;
				caretColumnIndex = columnIndex;
				
				invalidateDisplayListFor("selectionIndicator");
				dispatchFlexEvent(FlexEvent.VALUE_COMMIT);
			}
			
			return selectionChanged;
		}
		
		/**
		 *  If <code>selectionMode</code>
		 *  is <code>GridSelectionMode.SINGLE_CELL</code> or
		 *  <code>GridSelectionMode.MULTIPLE_CELLS</code>, adds the cell to
		 *  the selection and sets the caret position to the cell.
		 *  For all other selection modes, this method has no effect.
		 * 
		 *  <p>The <code>rowIndex</code> is the index in the data provider 
		 *  of the item containing the selected cell.  The <code>columnIndex</code>
		 *  is the index in <code>columns</code> of the column containing the
		 *  selected cell.</p>
		 *
		 *  @param rowIndex The 0-based row index of the cell.
		 *
		 *  @param columnIndex The 0-based column index of the cell.
		 * 
		 *  @return <code>true</code> if no errors.
		 *  <code>false</code> if <code>rowIndex</code> 
		 *  or <code>columnIndex</code> is invalid, or the <code>selectionMode</code> 
		 *  is invalid.     
		 *  
		 *  @see spark.components.Grid#caretColumnIndex
		 *  @see spark.components.Grid#caretRowIndex
		 *  @see spark.components.Grid#columns
		 *  @see spark.components.Grid#dataProvider
		 * 
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5
		 */
		public function addSelectedCell(rowIndex:int, columnIndex:int):Boolean
		{
			// Need to apply pending dataProvider and column changes so selection
			// isn't reset after it is set here.
			/*if (invalidatePropertiesFlag)
				UIComponentGlobals.layoutManager.validateClient(this, false);*/
			
			const selectionChanged:Boolean = gridSelection.addCell(rowIndex, columnIndex);
			if (selectionChanged)
			{
				caretRowIndex = rowIndex;
				caretColumnIndex = columnIndex;
				
				invalidateDisplayListFor("selectionIndicator");
				dispatchFlexEvent(FlexEvent.VALUE_COMMIT);
			}
			
			return selectionChanged;
		}
		
		/**
		 *  If <code>selectionMode</code>
		 *  is <code>GridSelectionMode.SINGLE_CELL</code> or
		 *  <code>GridSelectionMode.MULTIPLE_CELLS</code>, removes the cell
		 *  from the selection and sets the caret position to the cell.
		 *  For all other selection modes, this method has no effect.
		 * 
		 *  <p>The <code>rowIndex</code> is the index in the data provider 
		 *  of the item containing the selected cell.  The <code>columnIndex</code>
		 *  is the index in <code>columns</code> of the column containing the
		 *  selected cell.</p>
		 *
		 *  @param rowIndex The 0-based row index of the cell.
		 *
		 *  @param columnIndex The 0-based column index of the cell.
		 * 
		 *  @return <code>true</code> if no errors.
		 *  <code>false</code> if <code>rowIndex</code> 
		 *  or <code>columnIndex</code> is invalid or the <code>selectionMode</code> 
		 *  is invalid.     
		 *  
		 *  @see spark.components.Grid#caretColumnIndex
		 *  @see spark.components.Grid#caretRowIndex
		 *  @see spark.components.Grid#columns
		 *  @see spark.components.Grid#dataProvider
		 * 
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5
		 */
		public function removeSelectedCell(rowIndex:int, columnIndex:int):Boolean
		{
			// Need to apply pending dataProvider and column changes so selection
			// isn't reset after it is set here.
			/*if (invalidatePropertiesFlag)
				UIComponentGlobals.layoutManager.validateClient(this, false);*/
			
			const selectionChanged:Boolean = gridSelection.removeCell(rowIndex, columnIndex);
			if (selectionChanged)
			{
				caretRowIndex = rowIndex;
				caretColumnIndex = columnIndex;
				
				invalidateDisplayListFor("selectionIndicator");
				dispatchFlexEvent(FlexEvent.VALUE_COMMIT);
			}
			
			return selectionChanged;
		}
		
		/** 
		 *  If <code>selectionMode</code> is <code>GridSelectionMode.MULTIPLE_CELLS</code>,
		 *  sets the selection to all the cells in the cell region and the
		 *  caret position to the last cell in the cell region.
		 *  For all other selection modes, this method has no effect.
		 * 
		 *  <p>The <code>rowIndex</code> is the index in the data provider 
		 *  of the item containing the origin of the cell region.  
		 *  The <code>columnIndex</code>
		 *  is the index in <code>columns</code> of the column containing the
		 *  origin of the cell region.</p>
		 *
		 *  <p>This method has no effect if the cell region is not wholly
		 *  contained within the grid.</p>
		 * 
		 *  @param rowIndex The 0-based row index of the origin of the cell region.
		 *
		 *  @param columnIndex The 0-based column index of the origin of the cell 
		 *  region.
		 *  
		 *  @param rowCount Number of rows, starting at <code>rowIndex</code> to 
		 *  include in the cell region.
		 *
		 *  @param columnCount Number of columns, starting at 
		 *  <code>columnIndex</code> to include in the cell region.
		 * 
		 *  @return <code>true</code> if no errors.
		 *  <code>false</code> if the cell region is invalid or 
		 *  the <code>selectionMode</code> is invalid.     
		 *  
		 *  @see spark.components.Grid#caretColumnIndex
		 *  @see spark.components.Grid#caretRowIndex
		 *  @see spark.components.Grid#columns
		 *  @see spark.components.Grid#dataProvider
		 * 
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5
		 */
		public function selectCellRegion(rowIndex:int, columnIndex:int, 
										 rowCount:uint, columnCount:uint):Boolean
		{
			// Need to apply pending dataProvider and column changes so selection
			// isn't reset after it is set here.
			/*if (invalidatePropertiesFlag)
				UIComponentGlobals.layoutManager.validateClient(this, false);*/
			
			const selectionChanged:Boolean = gridSelection.setCellRegion(
				rowIndex, columnIndex, 
				rowCount, columnCount);
			if (selectionChanged)
			{
				caretRowIndex = rowIndex + rowCount - 1;
				caretColumnIndex = columnIndex + columnCount - 1;
				
				invalidateDisplayListFor("selectionIndicator");
				dispatchFlexEvent(FlexEvent.VALUE_COMMIT);
			}
			
			return selectionChanged;
		}
		
		//--------------------------------------------------------------------------
		//
		//  Selection API Helper Methods
		//
		//-------------------------------------------------------------------------- 
		
		/**
		 * For performance reasons, make sure validateClient, anchor and caret updates,
		 * and selectionIndicator and VALUE_COMMIT updates are only done once.
		 */
		private function doSetSelectedCells(valueCopy:Vector.<CellPosition>):void
		{
			// Need to apply pending dataProvider and column changes so selection
			// isn't reset after it is set here.
			/*if (invalidatePropertiesFlag)
				UIComponentGlobals.layoutManager.validateClient(this, false);*/
			
			gridSelection.removeAll();
			for each (var cell:CellPosition in valueCopy)
			{
				gridSelection.addCell(cell.rowIndex, cell.columnIndex);
			}
			
			doFinalizeSetSelection(cell ? cell.rowIndex : -1, 
				cell ? cell.columnIndex : -1);
		}
		
		/**
		 * For performance reasons, make sure validateClient, anchor and caret updates,
		 * and selectionIndicator and VALUE_COMMIT updates are only done once.
		 */
		private function doSetSelectedIndices(valueCopy:Vector.<int>):void
		{
			// Need to apply pending dataProvider and column changes so selection
			// isn't reset after it is set here.
			/*if (invalidatePropertiesFlag)
				UIComponentGlobals.layoutManager.validateClient(this, false);*/
			
			var newRowIndex:int = -1;
			gridSelection.removeAll();
			for each (newRowIndex in valueCopy)
			{
				gridSelection.addRow(newRowIndex);
			}
			
			doFinalizeSetSelection(newRowIndex, -1);
		}
		
		/**
		 * For performance reasons, make sure validateClient, anchor and caret updates,
		 * and selectionIndicator and VALUE_COMMIT updates are only done once.
		 */
		private function doSetSelectedItems(valueCopy:Vector.<Object>):void
		{
			if (!dataProvider)
				return;
			
			// Need to apply pending dataProvider and column changes so selection
			// isn't reset after it is set here.
			/*if (invalidatePropertiesFlag)
				UIComponentGlobals.layoutManager.validateClient(this, false);*/
			
			var newRowIndex:int = -1;        
			gridSelection.removeAll();
			for each (var item:Object in valueCopy)
			{
				newRowIndex = dataProvider.getItemIndex(item);
				gridSelection.addRow(newRowIndex);
			}
			
			doFinalizeSetSelection(newRowIndex, -1);
		}
		
		/**
		 * Finished selection operations so update the anchor, caret, selection indicators and
		 * trigger the selection bindings.
		 */
		private function doFinalizeSetSelection(rowIndex:int, columnIndex:int):void
		{
			initializeAnchorPosition();       
			caretRowIndex = rowIndex
			caretColumnIndex = columnIndex
			
			invalidateDisplayListFor("selectionIndicator");
			dispatchFlexEvent(FlexEvent.VALUE_COMMIT);      
		}
		
		//--------------------------------------------------------------------------
		//
		//  GridViewLayout Cover Methods, Properties
		//
		//-------------------------------------------------------------------------- 
		
		/** 
		 *  @private
		 *  Update the scroll position so that the virtual Grid element at the specified
		 *  index is visible.   Note that getScrollPositionDeltaToElement() is only 
		 *  approximate when variableRowHeight=true, so calling this method once will
		 *  not necessarily scroll far enough to expose the specified element.
		 * 
		 *  @returns True if either the horizontalScrollPosition or verticalScrollPosition changed.
		 */
		
		private function scrollToIndex(view:GridView, elementIndex:int, scrollHorizontally:Boolean, scrollVertically:Boolean):Boolean
		{
		/*	var spDelta:Point = view.gridViewLayout.getScrollPositionDeltaToElement(elementIndex);
			
			// The cell is completely visible or the specified index is no longer valid so punt.
			
			if (!spDelta)
				return false; 
			
			// If the required scroll is locked then punt
			
			if ((spDelta.y != 0) && view.gridViewLayout.verticalScrollingLocked)
				return false;
			
			if ((spDelta.x != 0) && view.gridViewLayout.horizontalScrollingLocked)
				return false;
			
			// Update the scroll positions
			
			var scrollChanged:Boolean = false;
			
			if (scrollHorizontally)
			{
				horizontalScrollPosition += spDelta.x;
				scrollChanged = spDelta.x != 0;
			}
			
			if (scrollVertically)
			{
				verticalScrollPosition += spDelta.y;
				scrollChanged = scrollChanged || spDelta.y != 0;
			}
			
			return scrollChanged;*/ return false;
		}
		
		
		/**
		 *  If necessary, set the <code>verticalScrollPosition</code> and 
		 *  <code>horizontalScrollPosition</code> properties so that the 
		 *  specified cell is completely visible. 
		 *  If <code>rowIndex</code> is -1 and <code>columnIndex</code> is specified, 
		 *  then just adjust the <code>horizontalScrollPosition</code>
		 *  so that the specified column is visible. 
		 *  If <code>columnIndex</code> is -1 and <code>rowIndex</code>
		 *  is specified, then just adjust the <code>verticalScrollPosition</code> 
		 *  so that the specified row is visible.
		 * 
		 *  @param rowIndex The 0-based row index of the item renderer's cell, or -1 to specify a column.
		 *  
		 *  @param columnIndex The 0-based column index of the item renderer's cell, or -1 to specify a row.
		 *  
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5
		 */
		public function ensureCellIsVisible(rowIndex:int = -1, columnIndex:int = -1):void
		{
			
			const columns:IList = this.columns;
			
			// Check that each index is within range.
			if (!columns || columnIndex < -1 || columnIndex >= columns.length || 
				!dataProvider || rowIndex < -1 || rowIndex >= dataProvider.length || 
				(columnIndex == -1 && rowIndex == -1))
				return;
			
			// Check to see if any columns are visible or specified column is visible.
			
			if ((columnIndex == -1 && getNextVisibleColumnIndex(-1) == -1) || 
				(columnIndex != -1 && !(GridColumn(columns.getItemAt(columnIndex)).visible)))
				return;
			
			const scrollHorizontally:Boolean = columnIndex != -1;
			const scrollVertically:Boolean = rowIndex != -1;
			
			// If the row, column, or cell is locked, then there's nothing to do.
			
			if ((columnIndex < lockedColumnCount) && (rowIndex < lockedRowCount))
				return;
			
			if (!scrollVertically && (columnIndex < lockedColumnCount)) 
				return;
			
			if (!scrollHorizontally && (rowIndex < lockedRowCount))
				return;        
			
			// If called after the layout cache is cleared, need to rebuild the cache
			// before accessing visible rows/columns and attempting to scroll.
			//if (getVisibleRowIndices().length == 0 || getVisibleColumnIndices().length == 0)
			//	validateNow();
			
			// When not scrolling horizontally, columnIndex can just be 0.
			if (!scrollHorizontally)
				columnIndex = 0;
			
			// Find the GridView the specified cell is contained by and punt if it's not visible
			
			const cellGridView:GridView = getGridViewAt(rowIndex, columnIndex);	
			if (!cellGridView || 
				(cellGridView.getLayoutBoundsX() >= getLayoutBoundsWidth()) || 
				(cellGridView.getLayoutBoundsY() >= getLayoutBoundsHeight()))
				return;
			
			// If the row index isn't specified, use the first one that's visible.
			if (!scrollVertically)
			{
				// const visibleRowIndices:Vector.<int> = cellGridView.gridViewLayout.getVisibleRowIndices();
				// rowIndex = (visibleRowIndices.length > 0) ?  visibleRowIndices[0] : 0;
			}
			
			// A cell's index as defined by LayoutBase it's just its position
			// in the row-major linear ordering of the grid's cells.
			/*
			const gridViewLayout:GridViewLayout = cellGridView.gridViewLayout;
			const cellViewRowIndex:int = rowIndex - gridViewLayout.viewRowIndex;
			const cellViewColumnIndex:int  = columnIndex - gridViewLayout.viewColumnIndex;
			const cellElementIndex:int = (cellViewRowIndex * gridViewLayout.columnsView.length) + cellViewColumnIndex;
			
			var scrollChanged:Boolean = false;
			var firstScroll:Boolean = true;
			
			// Iterate until we've scrolled elementIndex at least partially into view.
			do
			{
				scrollChanged = scrollToIndex(cellGridView, cellElementIndex, scrollHorizontally, scrollVertically);
				
				// Fixed row heights, and we're only scrolling vertically.
				if (!variableRowHeight && !scrollHorizontally)
					return;
				
				// Bail. This could indicate there is a bug but it avoids an infinite loop.
				// scrollToIndex() then validateNow() then scrollToIndex() again and
				// no changes in scroll position.
				if (!firstScroll && !scrollChanged)
					return;
				
				validateNow();
				
				firstScroll = false;
			}
			while (!isCellVisible(scrollVertically ? rowIndex : -1, scrollHorizontally ? columnIndex : -1));
			
			// At this point we've only ensured that the requested cell is at least 
			// partially visible.  Ensure that it's completely visible.
			
			scrollToIndex(cellGridView, cellElementIndex, scrollHorizontally, scrollVertically);
			*/
		}        
		
		/**
		 *  Return the data provider indices and padding indices of the 
		 *  currently visible rows.  
		 *  Indices which are greater than or equal to the 
		 *  <code>dataProvider</code> length represent padding rows.
		 *  Note that the item renderers for the first and last rows 
		 *  may only be partially visible. 
		 *  The returned vector's contents are in the order they're displayed.
		 * 
		 *  @return A vector of the visible row indices.
		 *  
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5
		 */ 
		/*
		public function getVisibleRowIndices():Vector.<int>
		{
			const gridLayout:GridLayout = layout as GridLayout;
			const topGridView:GridView = gridLayout.topGridView;        
			const centerGridView:GridView = gridLayout.centerGridView;
			
			if (!centerGridView)
				return new Vector.<int>(0);
			
			// const centerRowIndices:Vector.<int> = centerGridView.gridViewLayout.getVisibleRowIndices();
			// if (!topGridView)
			//	return centerRowIndices;
			
			const topRowIndices:Vector.<int> = topGridView.gridViewLayout.getVisibleRowIndices();
			return topRowIndices.concat(centerRowIndices);
		}
		*/
		/**
		 *  Return the indices of the currently visible columns.  Note that the 
		 *  item renderers for the first and last columns may only be partially visible.  
		 *  The returned vector's contents are in the order they're displayed.
		 * 
		 *  <p>The following example function uses this method to compute a vector of 
		 *  visible GridColumn objects.</p>
		 *  <pre>
		 *  function getVisibleColumns():Vector.&lt;GridColumn&gt;
		 *  {
		 *      var visibleColumns = new Vector.&lt;GridColumn&gt;;
		 *      for each (var columnIndex:int in grid.getVisibleColumnIndices())
		 *          visibleColumns.push(grid.columns.getItemAt(columnIndex));
		 *      return visibleColumns;
		 *  }
		 *  </pre> 
		 * 
		 *  @return A vector of the visible column indices.
		 *  
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5
		 */ 
		/*		
		public function getVisibleColumnIndices():Vector.<int>
		{
			const gridLayout:GridLayout = layout as GridLayout;
			const leftGridView:GridView = gridLayout.leftGridView;        
			const centerGridView:GridView = gridLayout.centerGridView;
			
			//const centerColumnIndices:Vector.<int> = centerGridView.gridViewLayout.getVisibleColumnIndices();
			//if (!leftGridView)
			//	return centerColumnIndices;
			
			const leftColumnIndices:Vector.<int> = leftGridView.gridViewLayout.getVisibleColumnIndices();
			return leftColumnIndices.concat(centerColumnIndices);
		}
		*/
		/**
		 *  Returns the current pixel bounds of the specified cell, or null if no such cell exists.
		 *  Cell bounds are reported in grid coordinates.
		 * 
		 *  <p>If all of the columns for the the specfied row and all of the rows preceeding 
		 *  it have not yet been scrolled into view, the returned bounds may only be an approximation, 
		 *  based on all of the columns' <code>typicalItem</code>s.</p>
		 * 
		 *  @param rowIndex The 0-based index of the row.
		 *
		 *  @param columnIndex The 0-based index of the column. 
		 *
		 *  @return A <code>Rectangle</code> that represents the cell's pixel bounds, or null.
		 *  
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5
		 */ 
		public function getCellBounds(rowIndex:int, columnIndex:int):Rectangle
		{
			return gridDimensions.getCellBounds(rowIndex, columnIndex);
		}
		
		/**
		 *  Returns the current pixel bounds of the specified row, or null if no such row exists.
		 *  Row bounds are reported in grid coordinates.
		 *
		 *  <p>If all of the columns for the the specfied row and all of the rows preceeding 
		 *  it have not yet been scrolled into view, the returned bounds may only be an approximation, 
		 *  based on all of the columns' <code>typicalItem</code>s.</p>
		 * 
		 *  @param rowIndex The 0-based index of the row.
		 * 
		 *  @return A <code>Rectangle</code> that represents the row's pixel bounds, or null.
		 *  
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5
		 */
		public function getRowBounds(rowIndex:int):Rectangle
		{
			return gridDimensions.getRowBounds(rowIndex);      
		}
		
		/**
		 *  Returns the current pixel bounds of the specified column, or null if no such column exists.
		 *  Column bounds are reported in grid coordinates.
		 * 
		 *  <p>If all of the cells in the specified column have not yet been scrolled into view, the 
		 *  returned bounds may only be an approximation, based on the column's <code>typicalItem</code>.</p>
		 *  
		 *  @param columnIndex The 0-based index of the column. 
		 *
		 *  @return A <code>Rectangle</code> that represents the column's pixel bounds, or null.
		 *  
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5
		 */
		public function getColumnBounds(columnIndex:int):Rectangle
		{
			return gridDimensions.getColumnBounds(columnIndex);
		}
		
		/**
		 *  Returns the row index corresponding to the specified grid coordinates,
		 *  or -1 if the coordinates are out of bounds. 
		 * 
		 *  <p>If all of the columns or rows for the grid have not yet been scrolled
		 *  into view, the returned index may only be an approximation, 
		 *  based on all of the columns' <code>typicalItem</code>s.</p>
		 *  
		 *  @param x The x coordinate.
		 * 
		 *  @param y The y coordinate.
		 *
		 *  @return The index of the row corresponding to the specified coordinates.
		 *  
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5
		 */
		public function getRowIndexAt(x:Number, y:Number):int
		{
			return gridDimensions.getRowIndexAt(x, y);
		}
		
		/**
		 *  Returns the column index corresponding to the specified grid coordinates,
		 *  or -1 if the coordinates are out of bounds. 
		 * 
		 *  <p>If all of the columns or rows for the grid have not yet been scrolled
		 *  into view, the returned index may only be an approximation, 
		 *  based on all of the columns' <code>typicalItem</code>s.</p>
		 *  
		 *  @param x The pixel's x coordinate relative to the grid.
		 *
		 *  @param y The pixel's y coordinate relative to the grid.
		 *
		 *  @return The index of the column, or -1 if the coordinates are out of bounds. 
		 *  
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5
		 */
		public function getColumnIndexAt(x:Number, y:Number):int
		{
			return gridDimensions.getColumnIndexAt(x, y); 
		}
		
		/**
		 *  Return the width of the specified column.  If the cell's entire bounds
		 *  aren't needed, this method is more efficient than <code>getColumnBounds().width</code>.
		 * 
		 *  <p>If the specified column's width property isn't defined, then the returned value 
		 *  may only be an approximation.  The actual column width is only computed after the column
		 *  has been scrolled into view.</p>
		 * 
		 *  @param columnIndex The 0-based index of the column. 
		 *  @return The width of the specified column.
		 *  
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5
		 */
		
		public function getColumnWidth(columnIndex:int):Number
		{
			const column:GridColumn = getGridColumn(columnIndex);
			return (column && !isNaN(column.width)) ? column.width : gridDimensions.getColumnWidth(columnIndex);
		}
		
		/**
		 *  Return the row and column indices of the cell that overlaps the pixel at the 
		 *  specified grid coordinate.
		 *  If no such cell exists, null is returned.
		 * 
		 *  <p>The example function below uses this method to compute the value of the 
		 *  <code>dataField</code> for a grid cell.</p> 
		 *  <pre>
		 *  function getCellData(x:Number, y:Number):Object
		 *  {
		 *      var cell:CellPosition = getCellAt(x, y);
		 *      if (!cell)
		 *          return null;
		 *      var GridColumn:column = grid.columns.getItemAt(cell.columnIndex);
		 *      return grid.dataProvider.getItemAt(cell.rowIndex)[column.dataField];
		 *  }
		 *  </pre> 
		 * 
		 *  @param x The pixel's x coordinate relative to the grid.
		 *
		 *  @param y The pixel's y coordinate relative to the grid.
		 *
		 *  @return The cell position, or null. 
		 *  
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5
		 */
		public function getCellAt(x:Number, y:Number):CellPosition
		{
			const rowIndex:int = gridDimensions.getRowIndexAt(x, y);
			const columnIndex:int = gridDimensions.getColumnIndexAt(x, y);
			if ((rowIndex == -1) || (columnIndex == -1))
				return null;
			return new CellPosition(rowIndex, columnIndex);
		}
		
		/**
		 *  Returns a vector of CellPosition objects whose 
		 *  <code>rowIndex</code> and <code>columnIndex</code> properties specify the 
		 *  row and column indices of the cells that overlap the specified grid region.  
		 *  If no such cells exist, an empty vector is returned.
		 *  
		 *  @param x The x coordinate of the pixel at the origin of the region, relative to the grid.
		 * 
		 *  @param x The x coordinate of the pixel at the origin of the region, relative to the grid. 
		 * 
		 *  @param w The width of the region, in pixels. 
		 * 
		 *  @param h The height of the region, in pixels. 
		 *  
		 *  @return A vector of objects like <code>Vector.&lt;Object&gt;([{rowIndex:0, columnIndex:0}, ...])</code>. 
		 *  
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5
		 */
		public function getCellsAt(x:Number, y:Number, w:Number, h:Number):Vector.<CellPosition>
		{ 
			var cells:Vector.<CellPosition> = new Vector.<CellPosition>;
			
			if (w <= 0 || h <= 0)
				return cells;
			
			// Get the row/column indexes of the corners of the region.
			var topLeft:CellPosition = getCellAt(x, y);
			var bottomRight:CellPosition = getCellAt(x + w, y + h);
			if (!topLeft || !bottomRight)
				return cells;
			
			for (var rowIndex:int = topLeft.rowIndex; 
				rowIndex <= bottomRight.rowIndex; rowIndex++)
			{
				for (var columnIndex:int = topLeft.columnIndex; 
					columnIndex <= bottomRight.columnIndex; columnIndex++)
				{
					cells.push(new CellPosition(rowIndex, columnIndex));
				}
			}
			
			return cells;
		}
		
		/**
		 *  Return the X coordinate of the specified cell's origin.  If the cell's entire bounds
		 *  aren't needed, this method is more efficient than <code>getCellBounds().x</code>.
		 * 
		 *  <p>If all of the columns for the the specfied row and all of the rows preceeding 
		 *  it have not yet been scrolled into view, the returned value may only be an approximation, 
		 *  based on all of the columns' <code>typicalItem</code>s.</p>
		 * 
		 *  @param rowIndex The 0-based index of the row.
		 *  @param columnIndex The 0-based index of the column. 
		 *  @return The x coordindate of the specified cell's origin.
		 *  
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5
		 */
		public function getCellX(rowIndex:int, columnIndex:int):Number
		{ 
			return gridDimensions.getCellX(rowIndex, columnIndex);
		}
		
		/**
		 *  Return the Y coordinate of the specified cell's origin.  If the cell's entire bounds
		 *  aren't needed, this method is more efficient than <code>getCellBounds().y</code>.
		 * 
		 *  <p>If all of the columns for the the specfied row and all of the rows preceeding 
		 *  it have not yet been scrolled into view, the returned value may only be an approximation, 
		 *  based on all of the columns' <code>typicalItem</code>s.</p>
		 * 
		 *  @param rowIndex The 0-based index of the row.
		 *  @param columnIndex The 0-based index of the column. 
		 *  @return The y coordindate of the specified cell's origin.
		 * 
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5
		 */
		public function getCellY(rowIndex:int, columnIndex:int):Number
		{ 
			return gridDimensions.getCellY(rowIndex, columnIndex);
		}      
		
		/**
		 *  If the requested item renderer is visible, returns a reference to 
		 *  the item renderer currently displayed at the specified cell.  
		 *  Note that once the returned item renderer is no longer visible it may be 
		 *  recycled and its properties reset.  
		 * 
		 *  <p>If the requested item renderer is not visible. then 
		 *  each time this method is called, a new item renderer is created.  
		 *  The new item renderer is not visible</p>
		 * 
		 *  <p>If the specified column does not have an explicit width, then the width
		 *  of this cell is based on the <code>typicalItem</code>.  
		 *  If a <code>typicalItem</code> was not specified or has not been measured yet, 
		 *  then the item renderer's width defaults to <code>150</code>.</p>
		 * 
		 *  <p>If the grid property <code>variableRowHeight</code> is 
		 *  <code>true</code> (the default) and an overall row height hasn't been 
		 *  cached for the specified row, then the item renderer's height is based 
		 *  on the <code>typicalItem</code>.  
		 *  If the <code>typicalItem</code> was not 
		 *  specified or has not been measured yet, then the item renderer's height 
		 *  defaults to its preferred height.</p>
		 *  
		 *  @param rowIndex The 0-based row index of the item renderer's cell.
		 * 
		 *  @param columnIndex The 0-based column index of the item renderer's cell.
		 * 
		 *  @return The item renderer or null if the cell location is invalid.
		 *  
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5
		 */
		/*
		public function getItemRendererAt(rowIndex:int, columnIndex:int):IGridItemRenderer
		{
			const view:GridView = getGridViewAt(rowIndex, columnIndex);
			if (!view)
				return null;
			
			return view.gridViewLayout.getItemRendererAt(rowIndex, columnIndex);
		}    
		*/
		/**
		 *  Returns <code>true</code> if the specified cell is at least partially visible. 
		 *  If <code>columnIndex == -1</code>, then return 
		 *  <code>true</code> if the specified row is at least partially visible. 
		 *  If <code>rowIndex == -1</code>, then return <code>true</code> 
		 *  if the specified column is at least partially visible. 
		 *  If both <code>columnIndex</code> and <code>rowIndex</code> are -1, 
		 *  then return <code>false</code>.
		 *  
		 *  @param rowIndex The 0-based row index of the item renderer's cell.
		 * 
		 *  @param columnIndex The 0-based column index of the item renderer's cell.
		 * 
		 *  @return True if the specified cell (or row if columnIndex == -1) is at least partially visible
		 *  
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5
		 */        
		public function isCellVisible(rowIndex:int = -1, columnIndex:int = -1):Boolean
		{
			const view:GridView = getGridViewAt(rowIndex, columnIndex);
			return view ;//&& view.gridViewLayout.isCellVisible(rowIndex, columnIndex);
		}
		
		//--------------------------------------------------------------------------
		//
		//  Tracking Grid invalidateDisplayList() "reasons", invalid cells
		//
		//-------------------------------------------------------------------------- 
		
		/**
		 *  @private
		 *  Low cost "list" of invalidateDisplayList() reasons.
		 */
		private var invalidateDisplayListReasonsMask:uint = 0;
		
		/**
		 *  @private
		 *  This flag makes it possible to defer clearing the invalidateDisplayListReasonsMask
		 *  until after the Grid's subtree has been redisplayed.   It's set by updateDisplayList()
		 *  and not cleared until the next invalidateDisplayListFor() call, on the assumption
		 *  that the Grid subtree's updateDisplayList() methods will not reset any Grid properties
		 *  (that call invalidateDisplayListFor()).
		 */
		private var clearInvalidateDisplayListReasons:Boolean = false;
		
		/**
		 *  @private
		 *  Table that maps from reason names to bit fields.
		 */
		private static const invalidateDisplayListReasonBits:Object = {
			verticalScrollPosition: uint(1 << 0),
			horizontalScrollPosition: uint(1 << 1),
			bothScrollPositions: (uint(1 << 0) | uint(1 << 1)),
			hoverIndicator: uint(1 << 2),
			caretIndicator: uint(1 << 3),
			selectionIndicator: uint(1 << 4),
			editorIndicator: uint(1 << 5),
			none: uint(~0)
		};
		
		/**
		 *  @private
		 *  Set the bit that corresponds to reason.  Only used by invalidateDisplayListFor().
		 */
		private function setInvalidateDisplayListReason(reason:String):void
		{
			if (clearInvalidateDisplayListReasons)
			{
				invalidateDisplayListReasonsMask = 0;
				clearInvalidateDisplayListReasons = false;
			}
			
			invalidateDisplayListReasonsMask |= invalidateDisplayListReasonBits[reason];
		}
		
		/**
		 *  @private
		 *  Return true if invalidateDisplayListFor() was called with the specified reason
		 *  since the last updateDisplayList() pass.
		 */
		mx_internal function isInvalidateDisplayListReason(reason:String):Boolean
		{
			const bit:uint = invalidateDisplayListReasonBits[reason];
			return (invalidateDisplayListReasonsMask & bit) == bit;
		}
		
		//--------------------------------------------------------------------------
		//
		//  Method Overrides
		//
		//--------------------------------------------------------------------------  
		
		/**
		 *  @private
		 */
		/*override*/ 
		public function getHorizontalScrollPositionDelta(navigationUnit:uint):Number
		{
			const gridLayout:GridLayout = layout as GridLayout;
			const centerGridView:GridView = gridLayout.centerGridView;			
			return (centerGridView) ? 0 /*centerGridView.getHorizontalScrollPositionDelta(navigationUnit)*/ : 0;     
		}
		
		/**
		 *  @private
		 */
		/*override*/ 
		public function getVerticalScrollPositionDelta(navigationUnit:uint):Number
		{
			const gridLayout:GridLayout = layout as GridLayout;
			const centerGridView:GridView = gridLayout.centerGridView;			
			return (centerGridView) ? 0 /*centerGridView.getVerticalScrollPositionDelta(navigationUnit)*/ : 0;     
		}
		
		
		/**
		 *  @private
		 *  During virtual layout updateDisplayList() eagerly validates lazily
		 *  created (or recycled) IRs.   We don't want changes to those IRs to
		 *  invalidate the size of the grid.
		 * 
		 *  This method also dispatches an invalidateSize event that's used 
		 *  by the DataGrid to invalidate IDataGridElements.
		 */
		override public function invalidateSize():void
		{
			if (!inUpdateDisplayList)
			{
				super.invalidateSize();
				
				for each (var view:GridView in allGridViews)
				{
					if (!view)
						continue;
					view.invalidateSize();    
				}
				
				dispatchChangeEvent("invalidateSize");            
			}
		}
		
		/**
		 *  @private
		 *  During virtual layout updateDisplayList() eagerly validates lazily
		 *  created (or recycled) IRs.  Calls to invalidateDisplayList() eventually
		 *  short-circuit but doing so early saves a few percent.
		 * 
		 *  This method also dispatches an invalidateDisplayList event that's used 
		 *  by the DataGrid to invalidate IDataGridElements.
		 */
		override public function invalidateDisplayList():void
		{
			if (!inUpdateDisplayList)
			{
				setInvalidateDisplayListReason("none");            
				super.invalidateDisplayList();
				
				for each (var view:GridView in allGridViews)
				{
					if (!view)
						continue;
					view.invalidateDisplayList();    
				}
				
				dispatchChangeEvent("invalidateDisplayList");
			}
		}
		
		private function get allGridViews():Array
		{
			const gridLayout:GridLayout = layout as GridLayout;
			return gridLayout ? [gridLayout.topLeftGridView, gridLayout.topGridView, gridLayout.leftGridView, gridLayout.centerGridView] : [];
		}
		
		private function createGridView():GridView
		{
			const elt:GridView = gridView.newInstance() as GridView;
			addElement(elt);
			return elt;
		}
		
		private function configureGridView(gv:GridView, viewRowIndex:int, viewColumnIndex:int, viewRowCount:int, viewColumnCount:int):void
		{
			/*
			const gridViewLayout:GridViewLayout = gv.gridViewLayout;
			gridViewLayout.grid = this;
			gridViewLayout.viewRowIndex = viewRowIndex;
			gridViewLayout.viewColumnIndex = viewColumnIndex;
			gridViewLayout.viewRowCount = viewRowCount;
			gridViewLayout.viewColumnCount = viewColumnCount;
			*/
		}
		
		/**
		 *  Create and/or configure this Grid's GridViews.  We're assuming that the
		 *  Grid's viewFactory, columns and dataProvider are specified.
		 * 
		 *  If GridViews are added or removed, a "gridViewsChanged" event is dispatched.
		 */
		private function configureGridViews():void
		{
			const columnCount:int = columns.length;
			const rowCount:int = (dataProvider) ? dataProvider.length : 0;
			
			lockedColumnCount = Math.min(lockedColumnCount, columnCount);
			lockedRowCount = Math.min(lockedRowCount, rowCount);
			
			const centerRowCount:int = Math.max(0, rowCount - lockedRowCount);
			const centerColumnCount:int = Math.max(0, columnCount - lockedColumnCount); 
			
			const gridLayout:GridLayout = layout as GridLayout;
			var topLeftGridView:GridView = gridLayout.topLeftGridView;    
			var topGridView:GridView = gridLayout.topGridView;        
			var leftGridView:GridView = gridLayout.leftGridView;
			var centerGridView:GridView = gridLayout.centerGridView;
			var lockedRowsSeparatorElement:IVisualElement = gridLayout.lockedRowsSeparatorElement;
			var lockedColumnsSeparatorElement:IVisualElement = gridLayout.lockedColumnsSeparatorElement;
			
			var gridViewsChanged:Boolean = false;
			
			// Unconditionally create and configure the "center" GridView
			
			if (centerGridView == null)
			{
				gridLayout.centerGridView = centerGridView = createGridView();
				gridViewsChanged = true;
			}
			
			configureGridView(centerGridView, lockedRowCount, lockedColumnCount, -1, -1);
			
			// centerGridView.gridViewLayout.requestedRowCount = requestedRowCount - lockedRowCount;
			// centerGridView.gridViewLayout.requestedColumnCount = requestedColumnCount - lockedColumnCount; 
			
			// Remove or create/configure the topLeftGridView
			
			if ((lockedRowCount > 0) && (lockedColumnCount > 0))
			{
				if (!topLeftGridView)
				{
					gridLayout.topLeftGridView = topLeftGridView = createGridView();
					// topLeftGridView.gridViewLayout.verticalScrollingLocked = true;
					// topLeftGridView.gridViewLayout.horizontalScrollingLocked = true;
					gridViewsChanged = true;
				}
				
			}
			else if (topLeftGridView)
			{
				removeElement(topLeftGridView);
				gridLayout.topLeftGridView = topLeftGridView = null;
				gridViewsChanged = true;
			}
			
			if (topLeftGridView)
				configureGridView(topLeftGridView, 0, 0, lockedRowCount, lockedColumnCount);
			
			// Remove or create/configure the topGridView
			
			if (lockedRowCount > 0)
			{
				if (!topGridView)
				{
					gridLayout.topGridView = topGridView = createGridView();
					// topGridView.gridViewLayout.verticalScrollingLocked = true;
					gridViewsChanged = true;
				}
				
				if (lockedRowsSeparator && !lockedRowsSeparatorElement)
				{
					gridLayout.lockedRowsSeparatorElement = lockedRowsSeparatorElement /*= lockedRowsSeparator.newInstance() as IVisualElement*/;
					// addElement(lockedRowsSeparatorElement);
				}
			}
			else
			{
				if (topGridView)
				{
					removeElement(topGridView);
					gridLayout.topGridView = topGridView = null;
					gridViewsChanged = true;
				}
				
				if (lockedRowsSeparatorElement)
				{
					// removeElement(lockedRowsSeparatorElement);
					gridLayout.lockedRowsSeparatorElement = lockedRowsSeparatorElement = null;
				}
			}
			
			if (topGridView)
				configureGridView(topGridView, 0, lockedColumnCount, lockedRowCount, centerColumnCount);
			
			// Remove or create/configure the leftGridView
			
			if (lockedColumnCount > 0)
			{
				if (!leftGridView)
				{
					gridLayout.leftGridView = leftGridView = createGridView();
					// leftGridView.gridViewLayout.horizontalScrollingLocked = true;
					gridViewsChanged = true;
				}
				
				if (lockedColumnsSeparator && !lockedColumnsSeparatorElement)
				{
					gridLayout.lockedColumnsSeparatorElement = lockedColumnsSeparatorElement/* = lockedColumnsSeparator.newInstance() as IVisualElement*/;
					// addElement(lockedColumnsSeparatorElement);
				}
			}
			else
			{
				if (leftGridView)
				{
					removeElement(leftGridView);
					gridLayout.leftGridView = leftGridView = null;
					gridViewsChanged = true;
				}
				
				if (lockedColumnsSeparatorElement)
				{
					// removeElement(lockedColumnsSeparatorElement);
					gridLayout.lockedColumnsSeparatorElement = lockedColumnsSeparatorElement = null;
				}
			}
			
			if (leftGridView)
				configureGridView(leftGridView, lockedRowCount, 0, centerRowCount, lockedColumnCount);
			
			if (gridViewsChanged)
				dispatchChangeEvent("gridViewsChanged");
		}
		
		/**
		 *  @private
		 */
		override protected function commitProperties():void
		{
			// rowHeight and variableRowHeight can be set in either order
			if (variableRowHeightChanged || rowHeightChanged)
			{
				if (rowHeightChanged)
					gridDimensions.defaultRowHeight = _rowHeight;
				gridDimensions.variableRowHeight = variableRowHeight;
				
				if ((!variableRowHeight && rowHeightChanged) || variableRowHeightChanged)
				{
					clearGridLayoutCache(false);
					invalidateSize();
					invalidateDisplayList();
				}
				
				rowHeightChanged = false;
				variableRowHeightChanged = false;
			}
			
			// item renderer changed or typical item changed
			if (itemRendererChanged || typicalItemChanged)
			{
				clearGridLayoutCache(true);
				itemRendererChanged = false;
			}
			
			// Try to generate columns if there aren't any or there are generated
			// ones which need to be regenerated because the typicalItem or 
			// dataProvider changed.
			if (!columns || (generatedColumns && 
				(typicalItemChanged || (!typicalItem && dataProviderChanged))))
			{
				const oldColumns:IList = columns;
				columns = generateColumns();
				generatedColumns = (columns != null);
				columnsChanged = columns != oldColumns;
			}
			typicalItemChanged = false;
			
			// If the dataProvider or columns change, reset the selection and 
			// the grid dimensions.  This has to be done here rather than in the 
			// setters because the gridSelection and gridDimensions might not 
			// be set yet, depending on the order they are initialized when the 
			// grid skin part is added to the data grid.
			
			if (dataProviderChanged || columnsChanged)
			{
				
				// Remove the current selection and, if requireSelection, make
				// sure the selection is reset to row 0 or cell 0,0.
				if (gridSelection)
				{
					var savedRequireSelection:Boolean = gridSelection.requireSelection;
					gridSelection.requireSelection = false;
					gridSelection.removeAll();
					gridSelection.requireSelection = savedRequireSelection;
				} 
				
				// make sure we have the right number of columns.
				if (columnsChanged)
					gridDimensions.columnCount = _columns ? _columns.length : 0;
				
				// Keep typical item size cache only when the typical item is still valid
				// and the columns haven't changed.
				if (typicalItem != null && !columnsChanged)
					clearGridLayoutCache(false);
				else
					clearGridLayoutCache(true);
				
				if (!caretChanged)
					initializeCaretPosition();
				
				if (!anchorChanged)
					initializeAnchorPosition();
				
				dataProviderChanged = false;
				columnsChanged = false;
			}
			
			anchorChanged = false;
			
			// Create or reconfigure the Grid's GridViews
			
			if (gridView && columns)
				configureGridViews();
			
			// Deferred selection operations
			
			if (dataProvider)
			{
				for each (var deferredOperation:Function in deferredOperations)
				deferredOperation();
				deferredOperations.length = 0;                
			}
			
			// Only want one event if both caretRowIndex and caretColumnIndex changed
			
			if (caretChanged)
			{
				// Validate values now.  Need to let caret be set in the same
				// update as the dp and/or columns.  -1 is a valid value.            
				if (_dataProvider && caretRowIndex >= _dataProvider.length)
					_caretRowIndex = _dataProvider.length - 1;
				if (_columns && caretColumnIndex >= _columns.length)
					_caretColumnIndex = getPreviousVisibleColumnIndex(_columns.length - 1);
				
				caretSelectedItem = 
					_dataProvider && _caretRowIndex >= 0 ?
					_dataProvider.getItemAt(_caretRowIndex) : null;
				
				dispatchCaretChangeEvent();
				
				// Last reported values.
				_oldCaretRowIndex = _caretRowIndex;
				_oldCaretColumnIndex = _caretColumnIndex;
				
				caretChanged = false;        
			}
			
			if (updateCaretForDataProviderChanged)
			{
				updateCaretForDataProviderChanged = false;
				updateCaretForDataProviderChange(updateCaretForDataProviderChangeLastEvent);
				updateCaretForDataProviderChangeLastEvent = null;
			}
		}
		
		
		/**
		 *  @private
		 */
		override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
		{
			inUpdateDisplayList = true;
			super.updateDisplayList(unscaledWidth, unscaledHeight);
			inUpdateDisplayList = false;
			
			clearInvalidateDisplayListReasons = true;
			
			if (!variableRowHeight)
				setFixedRowHeight(gridDimensions.getRowHeight(0));
		}
		
		//--------------------------------------------------------------------------
		//
		//  Methods
		//
		//--------------------------------------------------------------------------      
		
		/**
		 *  @private
		 *  This version of invlaidateDisplayList() stores the "reason" for the invalidate
		 *  request so that GridLayout/updateDisplayList() can do its job more efficiently.
		 *  GridLayout tests the accumulated invalidateDisplayList reasons with 
		 *  isInvalidateDisplayListReason() and they're automatically cleared by 
		 *  updateDisplayList() here.
		 * 
		 *  Note that if invalidateDisplayList() is called directly, all possible 
		 *  invalidateDispayList reasons are implicitly specified, in other words if
		 *  no reason is specified then they all are (see invalidateDisplayListReasonBits.none).
		 *  That way, callers need not be aware of this internal API.
		 * 
		 *  Also: most reason="selectionIndicator" calls also change the caret index which
		 *  in turn adds reason="caretIndicator" to the invalidateDisplayList reasons, if the
		 *  caret index actually changed.
		 */
		mx_internal function invalidateDisplayListFor(reason:String):void
		{
			if (!inUpdateDisplayList)
			{
				setInvalidateDisplayListReason(reason);          
				super.invalidateDisplayList();
				
				// Minor optimization: if the reason for this invalidation is a 
				// scroll, don't invalidate GridViews that can't change. 
				
				const vspReason:Boolean = reason == "verticalScrollPosition";
				const hspReason:Boolean = reason == "horizontalScrollPosition";
				const bothReason:Boolean = reason == "bothScrollPositions";
				
				const gridLayout:GridLayout = layout as GridLayout;
				const topLeftGridView:GridView = gridLayout.topLeftGridView;    
				const topGridView:GridView = gridLayout.topGridView;        
				const leftGridView:GridView = gridLayout.leftGridView;
				const centerGridView:GridView = gridLayout.centerGridView;			
				
				if (topLeftGridView && !vspReason && !hspReason && !bothReason)
					topLeftGridView.invalidateDisplayList();
				
				if (topGridView && !vspReason)
					topGridView.invalidateDisplayList();
				
				if (leftGridView && !hspReason)
					leftGridView.invalidateDisplayList();			
				
				if (centerGridView)  
					centerGridView.invalidateDisplayList();
				
				dispatchChangeEvent("invalidateDisplayList");
			}
		}
		
		/**
		 *  If the specified cell is visible, it is redisplayed.  
		 *  If <code>variableRowHeight=true</code>, 
		 *  then doing so may cause the height of the corresponding row to change.
		 * 
		 *  <p>If columnIndex is -1, then the entire row is invalidated.  
		 *  Similarly if <code>rowIndex is -1</code>, then the entire column is invalidated.</p>
		 * 
		 *  <p>This method should be called when there is a change to any aspect of 
		 *  the data provider item at <code>rowIndex</code> that might have some 
		 *  impact on the way the  specified cell is displayed. 
		 *  Calling this method is similar to calling the
		 *  <code>dataProvider.itemUpdated()</code> method, which advises the Grid that all rows
		 *  displaying the specified item should be redisplayed.  
		 *  Using this method can be relatively efficient, since it narrows 
		 *  the scope of the change to a single cell.</p>
		 * 
		 *  @param rowIndex The 0-based row index of the cell that changed, or -1.
		 *
		 *  @param columnIndex The 0-based column index of the cell that changed or -1.
		 *  
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5
		 */
		/*public function invalidateCell(rowIndex:int, columnIndex:int):void
		{
			if ((rowIndex == -1) && (columnIndex == -1))
			{
				invalidateDisplayList();
				return;
			}
			
			const view:GridView = getGridViewAt(rowIndex, columnIndex);
			if (!dataProvider || !view)
				return;
			
			
			const gridLayout:GridViewLayout = view.gridViewLayout;
			const dataProviderLength:int = dataProvider.length;
			if (!gridLayout || (rowIndex >= dataProvider.length))
				return;
			
			if (!isCellVisible(rowIndex, columnIndex))
				return;
			
			if (invalidateDisplayListFlag || invalidateSizeFlag)
				return;        
			
			if ((rowIndex >= 0) && (columnIndex >= 0))
			{
				gridLayout.invalidateCell(rowIndex, columnIndex);
			}
			else if (rowIndex >= 0)  // invalidate a row
			{
				const visibleColumnIndices:Vector.<int> = getVisibleColumnIndices();
				for each (var visibleColumnIndex:int in visibleColumnIndices)
				{
					gridLayout.invalidateCell(rowIndex, visibleColumnIndex);
					
					// If invalidating the cell caused the entire grid to be invalid, punt 
					if (invalidateDisplayListFlag || invalidateSizeFlag)
						break;                
				}
			}
			else if (columnIndex >= 0)  // invalidate a column
			{
				const visibleRowIndices:Vector.<int> = getVisibleRowIndices();
				for each (var visibleRowIndex:int in visibleRowIndices)
				{
					// If there are any padding rows, skip them.
					if (visibleRowIndex >= dataProviderLength)
						break;
					
					gridLayout.invalidateCell(visibleRowIndex, columnIndex);
					
					// If invalidating the cell caused the entire grid to be invalid, punt 
					if (invalidateDisplayListFlag || invalidateSizeFlag)
						break;
				}
			}
			
		}*/
		
		/**
		 *  Creates a grid selection object to use to manage selection. Override this method if you have a custom grid 
		 *  selection that you want to use in place of the default and this grid is not a skin part for DataGrid.  
		 *  This method is not used when this grid is a skin part for DataGrid.
		 *
		 *  @see spark.components.DataGrid.createGridSelection
		 * 
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5
		 */
		mx_internal function createGridSelection():GridSelection
		{
			return new GridSelection();    
		}
		
		
		/**
		 *  This will search through a dataprovider checking the given field and for the given value and return the index for the match.
		 *  It can start the find from a given startingIndex;
		 *
		 *  @langversion 3.0
		 *  @playerversion Flash 11.1
		 *  @playerversion AIR 3.4
		 *  @productversion Flex 4.10
		 */
		public function findRowIndex(field:String, value:String, startingIndex:int = 0, patternType:String = RegExPatterns.EXACT):int
		{
			var pattern:RegExp; 
			var currentObject:Object = null;
			var dataProviderTotal:int = 0;
			var loopingIndex:int = startingIndex;
			
			
			pattern = RegExPatterns.createRegExp(value, patternType);
			
			
			if (dataProvider && dataProvider.length > 0)
			{
				dataProviderTotal = dataProvider.length;
				
				if (startingIndex >= dataProviderTotal)
				{
					return -1;
				}
				
				
				for (loopingIndex; loopingIndex < dataProviderTotal; loopingIndex++)
				{
					currentObject = dataProvider.getItemAt(loopingIndex);
					
					if (currentObject.hasOwnProperty(field) == true && pattern.test(currentObject[field]) == true)
					{
						return loopingIndex;
					}
				}
				
			}
			
			return -1;
		}
		
		
		/**
		 *  This will search through a dataprovider checking the given field and for the given values and return an array of indices that matched.
		 *
		 *  @langversion 3.0
		 *  @playerversion Flash 11.1
		 *  @playerversion AIR 3.4
		 *  @productversion Flex 4.10
		 */
		public function findRowIndices(field:String, values:Array, patternType:String = RegExPatterns.EXACT):Array
		{
			var currentObject:Object = null;
			var regexList:Array = [];
			var matchedIndices:Array = [];
			var dataProviderTotal:uint = 0;
			var valuesTotal:uint = 0;
			var loopingDataProviderIndex:uint = 0;
			var loopingValuesIndex:uint = 0;
			
			
			if (dataProvider != null && dataProvider.length > 0 && values != null && values.length > 0)
			{
				dataProviderTotal = dataProvider.length;
				valuesTotal = values.length;
				
				
				//Set the regex patterns in an array once.
				for (loopingValuesIndex = 0; loopingValuesIndex < valuesTotal; loopingValuesIndex++)
				{
					regexList.push(RegExPatterns.createRegExp(values[loopingValuesIndex], patternType));
				}
				
				
				//Loop through dataprovider
				for (loopingDataProviderIndex; loopingDataProviderIndex < dataProviderTotal; loopingDataProviderIndex++)
				{
					currentObject = dataProvider.getItemAt(loopingDataProviderIndex);
					
					if (currentObject.hasOwnProperty(field) == false)
					{
						continue;
					}
					
					//Loop through regex patterns from the values array.
					for (loopingValuesIndex = 0; loopingValuesIndex < valuesTotal; loopingValuesIndex++)
					{
						if (regexList[loopingValuesIndex].test(currentObject[field]) == true)
						{
							matchedIndices.push(loopingDataProviderIndex);
							
							break;
						}
					}
				}
				
			}
			
			
			return matchedIndices;
		}
		
		
		/**
		 *  This will search through a dataprovider checking the given field and will set the selectedIndex to a matching value.
		 *  It can start the search from the startingIndex;
		 *
		 *  @langversion 3.0
		 *  @playerversion Flash 11.1
		 *  @playerversion AIR 3.4
		 *  @productversion Flex 4.10
		 *
		 */
		public function moveIndexFindRow(field:String, value:String, startingIndex:int = 0, patternType:String = RegExPatterns.EXACT):Boolean
		{
			var indexFound:int = -1;
			
			indexFound = findRowIndex(field, value, startingIndex, patternType);
			
			if (indexFound != -1)
			{
				selectedIndex = indexFound;
				
				return true;
			}
			
			return false;
		}
		
		
		/**
		 *  Changes the selectedIndex to the first row of the dataProvider.
		 *
		 *  @langversion 3.0
		 *  @playerversion Flash 11.1
		 *  @playerversion AIR 3.4
		 *  @productversion Flex 4.10
		 */
		public function moveIndexFirstRow():void
		{
			if (dataProvider && dataProvider.length > 0)
			{
				selectedIndex = 0;
			}
		}
		
		
		/**
		 *  Changes the selectedIndex to the last row of the dataProvider.
		 *
		 *  @langversion 3.0
		 *  @playerversion Flash 11.1
		 *  @playerversion AIR 3.4
		 *  @productversion Flex 4.10
		 */
		public function moveIndexLastRow():void
		{
			if (dataProvider && dataProvider.length > 0)
			{
				selectedIndex = dataProvider.length - 1;
			}
		}
		
		
		/**
		 *  Changes the selectedIndex to the next row of the dataProvider.  If there isn't a current selectedIndex, it silently returns.
		 *  If the selectedIndex is on the first row, it does not wrap around.  However the <code>isFirstRow</code> property returns true.
		 *
		 *  @langversion 3.0
		 *  @playerversion Flash 11.1
		 *  @playerversion AIR 3.4
		 *  @productversion Flex 4.10
		 */
		public function moveIndexNextRow():void
		{
			if (dataProvider && dataProvider.length > 0 && selectedIndex >= 0)
			{
				if (isLastRow == false)
				{
					selectedIndex += 1;
				}
			}
		}
		
		
		/**
		 *  Changes the selectedIndex to the previous row of the dataProvider.  If there isn't a current selectedIndex, it silently returns.
		 *  If the selectedIndex is on the last row, it does not wrap around.  However the <code>isLastRow</code> property returns true.
		 *
		 *  @langversion 3.0
		 *  @playerversion Flash 11.1
		 *  @playerversion AIR 3.4
		 *  @productversion Flex 4.10
		 */
		public function moveIndexPreviousRow():void
		{
			if (dataProvider && dataProvider.length > 0 && selectedIndex >= 0)
			{
				if (isFirstRow == false)
				{
					selectedIndex -= 1;
				}
			}
		}
		
		
		//--------------------------------------------------------------------------
		//
		//  Methods: Internal Grid Access
		//
		//--------------------------------------------------------------------------      
		
		/**
		 *  @private
		 */
		
		private function getGridColumn(columnIndex:int):GridColumn
		{
			const columns:IList = columns;
			if ((columns == null) || (columnIndex < 0) || (columnIndex >= columns.length))
				return null;
			
			return columns.getItemAt(columnIndex) as GridColumn;
		}
		
		/**
		 *  @private
		 */
		mx_internal function getDataProviderItem(rowIndex:int):Object
		{
			const dataProvider:IList = dataProvider;
			if ((dataProvider == null) || (rowIndex >= dataProvider.length))
				return null;
			
			return dataProvider.getItemAt(rowIndex);
		}
		
		/**
		 *  @private
		 */
		
		private function getVisibleItemRenderer(rowIndex:int, columnIndex:int):IGridItemRenderer
		{
			const view:GridView = getGridViewAt(rowIndex, columnIndex);
			if (!view)
				return null;
			
			/*const gridViewLayout:GridViewLayout = view.gridViewLayout;
			const viewRowIndex:int = rowIndex - gridViewLayout.viewRowIndex;
			const viewColumnIndex:int = columnIndex - gridViewLayout.viewColumnIndex;*/
			return null; // gridViewLayout.getVisibleItemRenderer(viewRowIndex, viewColumnIndex);
		}
		
		
		//--------------------------------------------------------------------------
		//
		//  GridEvents
		//
		//--------------------------------------------------------------------------  
		
		private var rollRowIndex:int = -1;
		private var rollColumnIndex:int = -1;
		private var mouseDownRowIndex:int = -1;
		private var mouseDownColumnIndex:int = -1;
		private var lastClickedColumnIndex:int = -1;
		private var lastClickedRowIndex:int = -1;
		private var lastClickTime:Number;
		
		// default max time between clicks for a double click is 480ms.
		mx_internal var DOUBLE_CLICK_TIME:Number = 480;
		
		/** 
		 *  @private
		 *  Return the GridView whose bounds contain the MouseEvent, or null.  Note that the 
		 *  comparison is based strictly on the event's location and the GridViews' bounds.
		 *  The event's target can be anything.
		 */
		private function mouseEventGridView(event:MouseEvent):GridView
		{
			const gridLayout:GridLayout = layout as GridLayout;
			
			const centerGridView:GridView = gridLayout.centerGridView;
			if (centerGridView && centerGridView.containsMouseEvent(event))
				return centerGridView;
			
			const leftGridView:GridView = gridLayout.leftGridView;
			if (leftGridView && leftGridView.containsMouseEvent(event))
				return leftGridView;
			
			const topGridView:GridView = gridLayout.topGridView;
			if (topGridView && topGridView.containsMouseEvent(event))
				return topGridView;
			
			const topLeftGridView:GridView = gridLayout.topLeftGridView;
			if (topLeftGridView && topLeftGridView.containsMouseEvent(event))
				return topLeftGridView;
			
			return null;
		}
		
		/** 
		 *  @private
		 *  Return the Grid-relative row,column (gridCP) and X,Y location (gridXY) of the MouseEvent.
		 */    
		private function eventToGridLocations(event:MouseEvent, gridCP:CellPosition, gridXY:Point):void
		{
			const stageXY:Point = new Point(event.stageX, event.stageY);
			const localXY:Point = globalToLocal(stageXY);
			gridXY.x = localXY.x;  // event may not have targeted the Grid
			gridXY.y = localXY.y;
			
			const view:GridView = mouseEventGridView(event);
			if (view)
			{
				const viewXY:Point = view.globalToLocal(stageXY);
				/*
				const gridViewLayout:GridViewLayout = view.gridViewLayout;
				const gdv:GridDimensionsView = gridViewLayout.gridDimensionsView;
				
				
				gridCP.rowIndex = gdv.getRowIndexAt(viewXY.x, viewXY.y) + gridViewLayout.viewRowIndex;
				gridCP.columnIndex = gdv.getColumnIndexAt(viewXY.x, viewXY.y) + gridViewLayout.viewColumnIndex;
				
				gridXY.x = viewXY.x + gdv.viewOriginX;
				gridXY.y = viewXY.y + gdv.viewOriginY;
				*/
			}
			else
			{
				gridCP.rowIndex = -1;
				gridCP.columnIndex = -1;
			}
		}
		
		/**
		 *  @private
		 *  This method is called when a MOUSE_DOWN event occurs within the grid and 
		 *  for all subsequent MOUSE_MOVE events until the button is released (even if the 
		 *  mouse leaves the grid).  The last event in such a "down drag up" gesture is 
		 *  always a MOUSE_UP.  By default this method dispatches GRID_MOUSE_DOWN, 
		 *  GRID_MOUSE_DRAG, or a GRID_MOUSE_UP event in response to the the corresponding
		 *  mouse event.  The GridEvent's rowIndex, columnIndex, column, item, and itemRenderer 
		 *  properties correspond to the grid cell under the mouse.  
		 * 
		 *  @param event A MOUSE_DOWN, MOUSE_MOVE, or MOUSE_UP MouseEvent from a down/move/up gesture initiated within the grid.
		 * 
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5
		 */    
		protected function grid_mouseDownDragUpHandler(event:MouseEvent):void
		{
			const eventGridCP:CellPosition = new CellPosition();
			const eventGridXY:Point = new Point();
			eventToGridLocations(event, eventGridCP, eventGridXY);
			
			const eventRowIndex:int = eventGridCP.rowIndex;
			const eventColumnIndex:int = eventGridCP.columnIndex;
			
			var gridEventType:String;
			switch(event.type)
			{
				case MouseEvent.MOUSE_MOVE: 
				{
					gridEventType = GridEvent.GRID_MOUSE_DRAG; 
					break;
				}
				case MouseEvent.MOUSE_UP: 
				{
					gridEventType = GridEvent.GRID_MOUSE_UP;
					break;
				}
				case MouseEvent.MOUSE_DOWN:
				{
					gridEventType = GridEvent.GRID_MOUSE_DOWN;
					mouseDownRowIndex = eventRowIndex;
					mouseDownColumnIndex = eventColumnIndex;
					dragInProgress = true;
					break;
				}
			}
			
			dispatchGridEvent(event, gridEventType, eventGridXY, eventRowIndex, eventColumnIndex);
			if (gridEventType == GridEvent.GRID_MOUSE_UP)
				dispatchGridClickEvents(event, eventGridXY, eventRowIndex, eventColumnIndex);        
		}
		
		/**
		 *  @private
		 *  This method is called whenever a MOUSE_MOVE event occurs within the grid
		 *  without the button pressed.  By default it dispatches a GRID_ROLL_OVER for the
		 *  first MOUSE_MOVE GridEvent whose location is within a grid cell, and a 
		 *  GRID_ROLL_OUT GridEvent when the mouse leaves a cell.  Listeners are guaranteed
		 *  to receive a GRID_ROLL_OUT event for every GRID_ROLL_OVER event.
		 * 
		 *  @param event A MOUSE_MOVE MouseEvent within the grid, without the button pressed.
		 * 
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5
		 */    
		protected function grid_mouseMoveHandler(event:MouseEvent):void
		{
			const eventGridCP:CellPosition = new CellPosition();
			const eventGridXY:Point = new Point();
			eventToGridLocations(event, eventGridCP, eventGridXY);
			
			const eventRowIndex:int = eventGridCP.rowIndex;
			const eventColumnIndex:int = eventGridCP.columnIndex;
			
			if ((eventRowIndex != rollRowIndex) || (eventColumnIndex != rollColumnIndex))
			{
				if ((rollRowIndex != -1) || (rollColumnIndex != -1))
					dispatchGridEvent(event, GridEvent.GRID_ROLL_OUT, eventGridXY, rollRowIndex, rollColumnIndex);
				if ((eventRowIndex != -1) && (eventColumnIndex != -1))
					dispatchGridEvent(event, GridEvent.GRID_ROLL_OVER, eventGridXY, eventRowIndex, eventColumnIndex);
				rollRowIndex = eventRowIndex;
				rollColumnIndex = eventColumnIndex;
			}
		}
		
		/**
		 *  @private
		 *  This method is called whenever a ROLL_OUT occurs on the grid.
		 *  By default it dispatches a GRID_ROLL_OUT event.
		 * 
		 *  @param event A ROLL_OUT MouseEvent from the grid.
		 * 
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5
		 */       
		protected function grid_mouseRollOutHandler(event:MouseEvent):void
		{
			if ((rollRowIndex != -1) || (rollColumnIndex != -1))
			{
				const eventStageXY:Point = new Point(event.stageX, event.stageY);
				const eventGridXY:Point = globalToLocal(eventStageXY);      
				
				dispatchGridEvent(event, GridEvent.GRID_ROLL_OUT, eventGridXY, rollRowIndex, rollColumnIndex);
				rollRowIndex = -1;
				rollColumnIndex = -1;
			}
		}
		
		/**
		 *  @private
		 *  This method is called whenever a GRID_MOUSE_UP occurs on the grid.
		 *  By default it dispatches a GRID_MOUSE_UP event.
		 * 
		 *  @param event A GRID_MOUSE_UP MouseEvent from the grid.
		 * 
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 2.5
		 *  @productversion Flex 4.5
		 */       
		protected function grid_mouseUpHandler(event:MouseEvent):void 
		{
			if (dragInProgress)
			{
				// drag handler has already dispatched a mouse up event, don't do so again here
				dragInProgress = false;
				return;
			}
			
			const eventGridCP:CellPosition = new CellPosition();
			const eventGridXY:Point = new Point();
			eventToGridLocations(event, eventGridCP, eventGridXY);
			
			const eventRowIndex:int = eventGridCP.rowIndex;
			const eventColumnIndex:int = eventGridCP.columnIndex;        
			
			dispatchGridEvent(event, GridEvent.GRID_MOUSE_UP, eventGridXY, eventRowIndex, eventColumnIndex);
			dispatchGridClickEvents(event, eventGridXY, eventRowIndex, eventColumnIndex);
		}
		
		/**
		 *  @private
		 *  This method is called after we dispatch a GRID_MOUSE_UP.
		 *  It determines whether to dispatch a GRID_CLICK or GRID_DOUBLE_CLICK
		 *  event following a GRID_MOUSE_UP.
		 * 
		 *  A GRID_CLICK event is dispatched when the mouse up event happens in
		 *  the same cell as the mouse down event.
		 * 
		 *  A GRID_DOUBLE_CLICK event is dispatched in place of the GRID_CLICK
		 *  event when the last click event happened within DOUBLE_CLICK_TIME.
		 */       
		private function dispatchGridClickEvents(mouseEvent:MouseEvent, gridXY:Point, rowIndex:int, columnIndex:int):void
		{
			const dispatchGridClick:Boolean = ((rowIndex == mouseDownRowIndex) && (columnIndex == mouseDownColumnIndex));
			const newClickTime:Number = 0;// getTimer();
			var isDoubleClick:Boolean = false;
			
			// In the case that we dispatched a click last time, check if we
			// should dispatch a double click this time.  The type of check will be based on the double click mode.
			if (doubleClickEnabled && dispatchGridClick && !isNaN(lastClickTime) &&
				(newClickTime - lastClickTime <= DOUBLE_CLICK_TIME))
			{
				switch(_doubleClickMode)
				{
					case GridDoubleClickMode.CELL:
					{
						if (rowIndex != -1 && columnIndex != -1 && rowIndex == lastClickedRowIndex && columnIndex == lastClickedColumnIndex)
						{
							isDoubleClick = true;
						}
						
						break;
					}
						
					case GridDoubleClickMode.GRID:
					{
						isDoubleClick = true;
						
						break;
					}
						
					case GridDoubleClickMode.ROW:
					{
						if (rowIndex != -1 && rowIndex == lastClickedRowIndex)
						{
							isDoubleClick = true;
						}
						
						break;
					}
				}
				
				if (isDoubleClick == true)
				{
					dispatchGridEvent(mouseEvent, GridEvent.GRID_DOUBLE_CLICK, gridXY, rowIndex, columnIndex);
					lastClickTime = NaN;
					lastClickedColumnIndex = -1;
					lastClickedRowIndex = -1;
					isDoubleClick = false;
					
					return;
				}
			}
			
			// Otherwise, just dispatch the click event.
			if (dispatchGridClick)
			{
				dispatchGridEvent(mouseEvent, GridEvent.GRID_CLICK, gridXY, rowIndex, columnIndex);
				lastClickTime = newClickTime;
				lastClickedColumnIndex = columnIndex;
				lastClickedRowIndex = rowIndex;
				isDoubleClick = false;
			}
		}
		
		/**
		 *  @private
		 */
		private function dispatchGridEvent(mouseEvent:MouseEvent, type:String, gridXY:Point, rowIndex:int, columnIndex:int):void
		{
			
			const column:GridColumn = columnIndex >= 0 ? getGridColumn(columnIndex) : null;
			const item:Object = rowIndex >= 0 ? getDataProviderItem(rowIndex) : null;
			const itemRenderer:IGridItemRenderer = getVisibleItemRenderer(rowIndex, columnIndex);
			const bubbles:Boolean = mouseEvent.bubbles;
			const cancelable:Boolean = mouseEvent.cancelable;
			// const relatedObject:InteractiveObject = mouseEvent.relatedObject;
			const ctrlKey:Boolean = mouseEvent.ctrlKey;
			const altKey:Boolean = mouseEvent.altKey;
			const shiftKey:Boolean = mouseEvent.shiftKey;
			const buttonDown:Boolean = mouseEvent.buttonDown;
			const delta:int = mouseEvent.delta;        
			
			/*const event:GridEvent = new GridEvent(
				type, bubbles, cancelable, 
				gridXY.x, gridXY.y, 
				relatedObject, ctrlKey, altKey, shiftKey, buttonDown, delta,
				rowIndex, columnIndex, column, item, itemRenderer);
			dispatchEvent(event);*/
			
		}
		
		//--------------------------------------------------------------------------
		//
		//  IList listeners: columns, dataProvider
		//
		//--------------------------------------------------------------------------  
		
		/**
		 *  @private
		 *  Update caretRowIndex if necessary.  This method should only be called when 
		 *  caretRowIndex is valid, i.e. != -1.
		 */
		private function updateCaretForDataProviderChange(event:CollectionEvent):void
		{
			const oldCaretRowIndex:int = caretRowIndex;
			const location:int = event.location;
			const itemsLength:int = event.items ? event.items.length : 0;                
			var newCaretRowIndex:int; 
			
			switch (event.kind)
			{
				case CollectionEventKind.ADD:
					if (oldCaretRowIndex >= location)
						caretRowIndex += event.items.length;
					break;
				
				case CollectionEventKind.REMOVE:
					if (oldCaretRowIndex >= location)
					{
						// ToDo(cframpto):  If the caret is on an item that is deleted, rather than
						// removing the caret, which is what we do now, it is preferable
						// to have the caret "stick" to the same position.  There is some complexity 
						// to picking a row/cell based on what’s currently visible or partially 
						// visible – after – the delete operation.
						if (oldCaretRowIndex < (location + itemsLength))
							caretRowIndex = -1; 
						else
							caretRowIndex -= itemsLength;    
					}
					
					break;
				
				case CollectionEventKind.MOVE:
				{
					const oldLocation:int = event.oldLocation;
					if ((oldCaretRowIndex >= oldLocation) && (oldCaretRowIndex < (oldLocation + itemsLength)))
					{
						caretRowIndex += location - oldLocation;
						ensureCellIsVisible(caretRowIndex, -1);
					}
				}
					break;                        
				
				case CollectionEventKind.REPLACE:
				case CollectionEventKind.UPDATE:
					break;
				
				case CollectionEventKind.REFRESH:
				{
					newCaretRowIndex = 
						caretSelectedItem ?
						_dataProvider.getItemIndex(caretSelectedItem) : -1; 
					
					// Caret sticks to item if possible and ensure it is totally 
					// visible by scrolling vertically if necessary.
					if (newCaretRowIndex != -1)
					{
						caretRowIndex = newCaretRowIndex;
						ensureCellIsVisible(caretRowIndex, -1);
					}
					else
					{
						// No caret.  Maintain the existing scroll position if
						// within the current data.
						
						var oldVsp:int = verticalScrollPosition;
						
						validateNow();
						
						// If variable row heights the height is 
						// approximate so the scroll position may not be
						// in exactly the same place.
						
						const cHeight:Number = Math.ceil(gridDimensions.getContentHeight());
						const maximum:int = Math.max(cHeight - height, 0);
						verticalScrollPosition = (oldVsp > maximum) ? maximum : oldVsp;                        
					}
					break;
				}
					
				case CollectionEventKind.RESET:
				{
					newCaretRowIndex = 
						caretSelectedItem ?
						_dataProvider.getItemIndex(caretSelectedItem) : -1; 
					
					// Caret sticks to item if possible and ensure it is totally 
					// visible by scrolling vertically if necessary.
					if (newCaretRowIndex != -1)
					{
						caretRowIndex = newCaretRowIndex;
						ensureCellIsVisible(caretRowIndex, -1);
					}
						
						// No caret item so reset caret and vsp.
					else 
					{
						caretRowIndex = _dataProvider.length > 0 ? 0 : -1;
						// we need to call validateSize() to force computing maxTypicalCellHeight  or verticalScrollPosition will fail
						GridLayout(layout).centerGridView.validateSize();
						verticalScrollPosition = 0;
					}
					
					break;
				}
			}   
			
		}
		
		/**
		 *  @private
		 *  Update caretColumnIndex if necessary.  This method should only be 
		 *  called when caretColumnIndex is valid, i.e. != -1.
		 */
		private function updateCaretForColumnsChange(event:CollectionEvent):void
		{
			const oldCaretColumnIndex:int = caretColumnIndex;
			const location:int = event.location;
			const itemsLength:int = event.items ? event.items.length : 0;
			
			switch (event.kind)
			{
				case CollectionEventKind.ADD:
					if (oldCaretColumnIndex >= location)
						caretColumnIndex += itemsLength;
					break;
				
				case CollectionEventKind.REMOVE:
					if (oldCaretColumnIndex >= location)
					{
						if (oldCaretColumnIndex < (location + itemsLength))
							caretColumnIndex = _columns.length > 0 ? 0 : -1; 
						else
							caretColumnIndex -= itemsLength;    
					}                   
					break;
				
				case CollectionEventKind.MOVE:
					const oldLocation:int = event.oldLocation;
					if ((oldCaretColumnIndex >= oldLocation) && (oldCaretColumnIndex < (oldLocation + itemsLength)))
						caretColumnIndex += location - oldLocation;
					break;                        
				
				case CollectionEventKind.REPLACE:
					break;
				
				case CollectionEventKind.UPDATE:
					// column may have changed visiblity which matters if cell 
					// selection mode.
					var pe:PropertyChangeEvent;
					
					if (selectionMode == GridSelectionMode.SINGLE_CELL || 
						selectionMode == GridSelectionMode.MULTIPLE_CELLS)
					{
						for (var i:int = 0; i < itemsLength; i++)
						{
							pe = event.items[i] as PropertyChangeEvent;
							if (pe && pe.property == "visible")
							{	
								
								const column:GridColumn = pe.source as GridColumn;
								if (!column || column.visible)
									continue;
								
								if (column.columnIndex == caretColumnIndex)
									initializeCaretPosition(true);  // column only
								if (column.columnIndex == anchorColumnIndex)
									initializeAnchorPosition(true);  // column only
								
							}
						}
					}
					break;
				
				case CollectionEventKind.REFRESH:
				case CollectionEventKind.RESET:
					initializeCaretPosition(true);  // column only
					horizontalScrollPosition = 0;
					break;
			}            
		}
		
		/**
		 *  @private
		 *  Update hoverRowIndex if necessary.  This method should only be called when 
		 *  hoverRowIndex is valid, i.e. != -1.
		 */
		private function updateHoverForDataProviderChange(event:CollectionEvent):void
		{
			const oldHoverRowIndex:int = hoverRowIndex;
			const location:int = event.location;
			
			switch (event.kind)
			{
				case CollectionEventKind.ADD:
				case CollectionEventKind.REMOVE:
				case CollectionEventKind.REPLACE:
				case CollectionEventKind.UPDATE:
				case CollectionEventKind.MOVE:
					if (oldHoverRowIndex >= location)
						hoverRowIndex = gridDimensions.getRowIndexAt(mouseX, mouseY);
					break;
				
				case CollectionEventKind.REFRESH:
				case CollectionEventKind.RESET:
					hoverRowIndex = gridDimensions.getRowIndexAt(mouseX, mouseY);
					break;
			}                        
		}
		
		/**
		 *  @private
		 *  Update hoverColumnIndex if necessary.  This method should only be called when 
		 *  hoverColumnIndex is valid, i.e. != -1.
		 */
		private function updateHoverForColumnsChange(event:CollectionEvent):void
		{
			switch (event.kind)
			{
				case CollectionEventKind.ADD:
				case CollectionEventKind.REMOVE:
				case CollectionEventKind.REPLACE:
				case CollectionEventKind.UPDATE:
				case CollectionEventKind.MOVE:
					if (hoverColumnIndex >= event.location)
						hoverColumnIndex = gridDimensions.getColumnIndexAt(mouseX, mouseY);
					break;
				
				case CollectionEventKind.REFRESH:
				case CollectionEventKind.RESET:
					hoverColumnIndex = gridDimensions.getColumnIndexAt(mouseX, mouseY);
					break;
			}
		}
		
		/**
		 *  @private
		 */
		private function dataProvider_collectionChangeHandler(event:CollectionEvent):void
		{
			var selectionChanged:Boolean = false;
			
			
			// If no columns exist, we should try to generate them.
			if (!columns && dataProvider.length > 0)
			{
				columns = generateColumns();
				generatedColumns = (columns != null);
				this.gridDimensions.columnCount = generatedColumns ? columns.length : 0;
			}
			
			const gridDimensions:GridDimensions = this.gridDimensions;
			if (gridDimensions)
			{
				gridDimensions.dataProviderCollectionChanged(event);
				gridDimensions.rowCount = dataProvider.length;
			}
			
			for each (var view:GridView in allGridViews)
			{
				if (!view)
					continue;
				// view.gridViewLayout.dataProviderCollectionChanged(event);      
			}
			
			if (gridSelection)
				selectionChanged = gridSelection.dataProviderCollectionChanged(event);            
			
			if (gridDimensions && hoverRowIndex != -1)
				updateHoverForDataProviderChange(event);
			
			// The data has changed so need to do this here so the grid dimensions
			// will be accurate if setting the caret requires scrolling.
			invalidateSize();
			invalidateDisplayList();
			
			if (caretRowIndex != -1)  {
				if (event.kind == CollectionEventKind.RESET){
					// defer for reset events 
					updateCaretForDataProviderChanged = true;
					updateCaretForDataProviderChangeLastEvent = event;
					invalidateProperties();
				}
				else {
					updateCaretForDataProviderChange(event);
				}         
			}
			
			
			// Trigger bindings to selectedIndex/selectedCell/selectedItem and the plurals of those.
			if (selectionChanged)
				dispatchFlexEvent(FlexEvent.VALUE_COMMIT);
		}
		
		/**
		 *  @private
		 */
		private function columns_collectionChangeHandler(event:CollectionEvent):void
		{
			
			var column:GridColumn;
			var columnIndex:int = event.location;
			var i:int;
			var selectionChanged:Boolean = false;
			
			switch (event.kind)
			{
				case CollectionEventKind.ADD: 
				{
					// Note: multiple columns may be added.
					while (columnIndex < columns.length)
					{
						column = GridColumn(columns.getItemAt(columnIndex));
						column.setGrid(this);
						column.setColumnIndex(columnIndex);
						columnIndex++;
					}                  
					break;
				}
					
				case CollectionEventKind.MOVE:
				{
					// All columns between the old and new locations need to 
					// have their index updated.
					columnIndex = Math.min(event.oldLocation, event.location);
					var maxIndex:int = Math.max(event.oldLocation, event.location);
					while (columnIndex <= maxIndex)
					{
						column = GridColumn(columns.getItemAt(columnIndex));
						column.setColumnIndex(columnIndex);
						columnIndex++;
					}                
					break;
				}
					
				case CollectionEventKind.REPLACE:
				{
					var items:Array = event.items;                   
					var length:int = items.length;
					for (i = 0; i < length; i++)
					{
						if (items[i].oldValue is GridColumn)
						{
							column = GridColumn(items[i].oldValue);
							column.setGrid(null);
							column.setColumnIndex(-1);
						}
						if (items[i].newValue is GridColumn)
						{
							column = GridColumn(items[i].newValue);
							column.setGrid(this);
							column.setColumnIndex(columnIndex);
						}
					}
					break;
				}
					
				case CollectionEventKind.UPDATE:
				{
					break;
				}
					
				case CollectionEventKind.REFRESH:
				{
					for (columnIndex = 0; columnIndex < columns.length; columnIndex++)
					{
						column = GridColumn(columns.getItemAt(columnIndex));
						column.setColumnIndex(columnIndex);
					}                
					break;
				}
					
				case CollectionEventKind.REMOVE:
				{
					// Note: multiple columns may be removed.
					var count:int = event.items.length;
					
					for (i = 0; i < count; i++)
					{
						column = GridColumn(event.items[i]);
						column.setGrid(null);
						column.setColumnIndex(-1);
					}
					
					// Renumber the columns which follow the removed columns.
					while (columnIndex < columns.length)
					{
						column = GridColumn(columns.getItemAt(columnIndex));
						column.setColumnIndex(columnIndex);
						columnIndex++;
					}                  
					
					break;
				}
					
				case CollectionEventKind.RESET:
				{
					for (columnIndex = 0; columnIndex < columns.length; columnIndex++)
					{
						column = GridColumn(columns.getItemAt(columnIndex));
						column.setGrid(this);
						column.setColumnIndex(columnIndex);
					}                     
					break;
				}                                
			}
			
			if (gridDimensions)
				gridDimensions.columnsCollectionChanged(event);
			
			for each (var view:GridView in allGridViews)
			{
				if (!view)
					continue;
				// view.gridViewLayout.columnsCollectionChanged(event);      
			}
			
			
			if (gridSelection)
				selectionChanged = gridSelection.columnsCollectionChanged(event);
			
			if (caretColumnIndex != -1)
				updateCaretForColumnsChange(event);                
			
			if (gridDimensions && hoverColumnIndex != -1)
				updateHoverForColumnsChange(event); 
			
			invalidateSize();
			invalidateDisplayList(); 
			
			// Trigger bindings to selectedCell/selectedItem and the plurals of those.
			if (selectionChanged)
				dispatchFlexEvent(FlexEvent.VALUE_COMMIT);
			
		} 
		
		//--------------------------------------------------------------------------
		//
		//  Methods
		//
		//--------------------------------------------------------------------------  
		
		/**
		 *  @private
		 *  Clears the layout's renderers and cached sizes. Also clears
		 *  the typical item's size if clearTypicalSizes is true.
		 */
		mx_internal function clearGridLayoutCache(clearTypicalSizes:Boolean):void
		{
			for each (var view:GridView in allGridViews)
			{
				if (!view)
					continue;
				// view.gridViewLayout.clearVirtualLayoutCache();
			}
			
			const gridDimensions:GridDimensions = this.gridDimensions;
			if (gridDimensions)
			{
				if (clearTypicalSizes)
				{
					gridDimensions.clearTypicalCellWidthsAndHeights();
					gridDimensions.clearColumns(0, gridDimensions.columnCount);
				}
				
				gridDimensions.clearHeights();
				
				// Reset row count because dataProvider length may have changed.
				gridDimensions.rowCount = _dataProvider ? _dataProvider.length : 0;
			}
			
			// Reset content size so scroller's viewport can be resized.  There
			// is loop-prevention logic in the scroller which may not allow the
			// width/height to be reduced if there are automatic scrollbars.
			// See ScrollerLayout/measure().
			setContentSize(0, 0);
		}
		
		/**
		 *  @private
		 *  Returns the index of the next GridColumn.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).
		 */
		mx_internal function getNextVisibleColumnIndex(index:int=-1):int
		{
			if (index < -1)
				return -1;
			
			const columns:IList = this.columns;
			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).
		 */
		mx_internal function getPreviousVisibleColumnIndex(index:int):int
		{
			const columns:IList = this.columns;
			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 initializeAnchorPosition(columnOnly:Boolean=false):void
		{
			if (!columnOnly)
				anchorRowIndex = _dataProvider && _dataProvider.length > 0 ? 0 : -1; 
			
			// First visible column, or -1, if there are no columns or none are visible.
			anchorColumnIndex = getNextVisibleColumnIndex(); 
		}
		
		/**
		 *  @private
		 */
		private function initializeCaretPosition(columnOnly:Boolean=false):void
		{
			if (!columnOnly)
				caretRowIndex = _dataProvider && _dataProvider.length > 0 ? 0 : -1;
			
			// First visible column, or -1, if there are no columns or none are visible.
			caretColumnIndex = getNextVisibleColumnIndex();
		}
		
		/**
		 *  @private
		 *  The caret change has already been comitted.  Dispatch the "caretChange"
		 *  event.
		 */
		private function dispatchCaretChangeEvent():void
		{
			if (hasEventListener(GridCaretEvent.CARET_CHANGE))
			{
				const caretChangeEvent:GridCaretEvent = 
					new GridCaretEvent(GridCaretEvent.CARET_CHANGE);
				caretChangeEvent.oldRowIndex = _oldCaretRowIndex;
				caretChangeEvent.oldColumnIndex = _oldCaretColumnIndex;
				caretChangeEvent.newRowIndex = _caretRowIndex;
				caretChangeEvent.newColumnIndex = _caretColumnIndex;
				dispatchEvent(caretChangeEvent);
			}
		}
		
		
		/**
		 *  @private
		 *  Renders a background for the container, if necessary.  It is used to fill in
		 *  a transparent background fill as necessary to support the _mouseEnabledWhereTransparent flag.  It 
		 *  is also used in ItemRenderers when handleBackgroundColor is set to true.
		 *  We assume for now that we are the first layer to be rendered into the graphics
		 *  context.
		 * 
		 *  This is mostly copied from GroupBase, but always chooses the virtualLayout path.  The Grid's
		 *  layout has useVirtualLayout=false but the Grid's GridView always has useVirtualLayout=true
		 *  which causes the GroupBase logic to go down the wrong path.  It also always positions at 0,0
		 *  because the grid itself doesn't scroll, it scrols the layers
		 */
		/*override*/ 
		mx_internal function drawBackground():void
		{
			/*
			if (!mouseEnabledWhereTransparent || !hasMouseListeners)
				return;
			
			var w:Number = (resizeMode == ResizeMode.SCALE) ? measuredWidth : unscaledWidth;
			var h:Number = (resizeMode == ResizeMode.SCALE) ? measuredHeight : unscaledHeight;
			
			if (isNaN(w) || isNaN(h))
				return;
			
			graphics.clear();
			graphics.beginFill(0xFFFFFF, 0);
			
			graphics.drawRect(0, 0, w, h);
			
			graphics.endFill();
			*/
		}
	
		public function set nestLevel(value:int):void 
		{
			
		}
		
		public function get nestLevel():int {
			return 0;
		}
		
		public function set processedDescriptors(value:Boolean):void {
			
		}
		
		public function get processedDescriptors():Boolean {
			return false;
		}
		
		public function set updateCompletePendingFlag(value:Boolean):void {
			
		}
		
		public  function get updateCompletePendingFlag():Boolean {
			return false;
		}
		
		private var _invalidatePropertiesFlag:Boolean = false;
		
		public function get invalidatePropertiesFlag():Boolean {
			return _invalidatePropertiesFlag;
		}
		
		public function set invalidatePropertiesFlag(value:Boolean):void {
			_invalidatePropertiesFlag = value;
		}
		
	}

}
