blob: 5f9f0cde4885dc32484011a99f70147aa24ced27 [file] [log] [blame]
////////////////////////////////////////////////////////////////////////////////
//
// 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.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 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 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 var _gridSelection:GridSelection;
/**
* @private
*/
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;
}
/**
* 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) ? 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) ? 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 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 = 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();
}
}
}