blob: 67de4eb4e47c0f9ee4690022726fa84d30f4221c [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.accessibility
{
import flash.accessibility.Accessibility;
import flash.events.Event;
import flash.events.FocusEvent;
import mx.accessibility.AccConst;
import mx.collections.IList;
import mx.core.UIComponent;
import mx.core.mx_internal;
import mx.resources.ResourceManager;
import mx.resources.IResourceManager;
import spark.components.DataGrid;
import spark.components.Grid;
import spark.components.gridClasses.GridSelectionMode;
import spark.components.gridClasses.CellPosition;
import spark.events.GridEvent;
import spark.events.GridCaretEvent;
import spark.events.GridSelectionEvent;
import spark.events.GridSelectionEventKind;
import spark.events.GridItemEditorEvent;
import spark.skins.spark.DefaultGridItemRenderer;
use namespace mx_internal;
/**
* This is the accessibility implementation class for
* spark.components.DataGrid.
*
* <p>When a Spark DataGrid is created, its <code>focusOwner</code> child
* object's <code>accessibilityImplementation</code> property is set to an
* instance of this class. The accessibility implementation is placed on
* this placeholder <code>focusOwner</code> object so that the DataGrid's
* accessibility implementation does not obscure the item editor's
* accessibility implementation. The DataGrid component itself does not
* have an accessibility implementation. This step is required as the
* current version of the Flash Player does not support multiple levels of
* MSAA objects. Item editors can be any component and need to be full MSAA
* objects. The item editor objects appear as sibling objects to the
* DataGrid in the MSAA tree structure. The accessibility implementation
* for each item editor is thus handled by the accessibility implementation
* associated with that component, such as <code>CheckBoxAccImpl</code> for
* a CheckBox. The item editor's accessibility implementation only exists
* when there is an item editor session, and there can only be one item
* editor active at one time; thus, there is one or zero instances of the
* item editor accessibility implementation active at any time.</p>
*
* <p>Two methods are overwritten in the <code>DataGrid</code> class to
* properly handle focus among <code>DataGrid</code> and the
* <code>focusOwner</code> child of <code>DataGrid</code>. The
* <code>GridItemRenderer</code> class turns accessibility off for item
* renderers, as by default these will be handled as simple objects under
* the DataGrid accessibility implementation. While this limits how
* non-item editor components such as CheckBox, Panel, etc. can be used in
* DataGrids, it prevents these items from showing up as siblings to the
* DataGrid in the MSAA tree. Allowing all grid item renderers to show up
* as siblings to the DataGrid in the MSAA tree would be very confusing to
* users of screen readers as there would be no context or relationship.
* Developers can of course override this default behavior if desired to
* display these renderers with accessibility enabled.</p>
*
* <p>The Flash Player then uses this class to allow MSAA clients such as
* screen readers to see and manipulate the DataGrid. See the
* mx.accessibility.AccImpl and
* flash.accessibility.AccessibilityImplementation classes for background
* information about accessibility implementation classes and MSAA.</p>
*
* <p>The <code>DataGridAccImpl</code> extends the
* <code>ListBaseAccImpl</code> (as the <code>DataGrid</code> extends the
* <code>DataGridBase</code> which extends the <code>ListBase</code> class).
* The Spark <code>DataGridAccImpl</code> is most similar to the MX
* <code>AdvancedDataGridAccImpl</code> as the AdvancedDataGrid also
* supports single cell and row selection which the MX DataGrid did not.</p>
*
* <p><b>Children</b></p>
*
* <p>The MSAA children of a DataGrid are, in this order</p>
* <ul>
* <li>One child for each visible header cell, starting from the left.
* "Visible" here means not hidden by the developer
* (<code>column.visible=false</code>). The header for a column that is not
* marked invisible by the developer but which is scrolled off screen is
* considered "visible" here.</li>
* <li>In row selection mode, one child for each data row in the grid; OR</li>
* <li>In cell selection mode, one child for each cell in the grid,
* excluding cells in invisible (as just described) columns.</li>
* </ul>
*
* <p>The number of children depends on the number of rows and columns in
* the <code>dataProvider</code>, not on the number of items currently
* displayed on screen.</p>
*
* <p>Note that, unlike for <code>ListBase</code>, DataGrid child count does
* not reflect the number of data rows in the control. Assistive technology
* should therefore avoid using <code>AccChildCount</code> as a means of
* reporting row count.</p>
*
* <p>This property is not handled by the DataGrid accessibility
* implementation for item editors as item editors manage themselves.</p>
*
* <p><b>Role</b></p>
*
* <p>The MSAA Role of a DataGrid is <code>ROLE_SYSTEM_LIST</code>.</p>
*
* <p>The Role of each data row or cell in the DataGrid is
* <code>ROLE_SYSTEM_LISTITEM</code>.</p>
*
* <p>The Role of each header cell in the DataGrid is
* <code>ROLE_SYSTEM_COLUMNHEADER</code>.</p>
*
* <p>This property is not handled by the DataGrid accessibility
* implementation for item editors as item editors manage themselves.</p>
*
* <p><b>Name</b></p>
*
* <p>The MSAA Name of a DataGrid is, by default, an empty string. When
* wrapped in a <code>FormItem</code> element, the Name is the FormItem's
* label. To override this behavior, set the DataGrids's
* <code>accessibilityName</code> property. Setting the
* <code>accessibilityName</code> property will also apply the accessible
* name to the <code>focusOwner</code> child object of the DataGrid which
* represents the DataGrid.</p>
*
* <p>The Name of each data row (when in row selection mode) is a string of
* the form "_column1Name_: _column1Value_, _column2Name_: _column2Value_,
* ..., _columnNName_: _columnNValue_, Row _m_ of _n_." Columns are
* separated from each other by commas, and column names and values are
* separated from each other by colons. Columns hidden by the developer are
* omitted entirely from the Name string. Example Name string: "Contact
* Name: Doug, Contact Phone: 555-1212, Contact Zip: 12345, row 3 of 7."</p>
* <p>Note that "Row _m_ of _n_" is localized.</p>
*
* <p>The Name of each data cell in column 1 (when in cell selection mode)
* is a string of the form "_columnName_: _columnValue_, Row _m_ of _n_."
* Example: "Contact Phone: 555-1212, Row 2 of 5." Subsequent columns use
* the same format but omit the "Row _m_ of _n_" portion.</p>
* <p>Note that "Row _m_ of _n_" is localized.</p>
*
* <p>The Name string for a column header (in cell or row selection mode) is
* normally the text of the header. Example: "Contact Phone." If the grid
* is sorted by the corresponding column however, the string "sorted" or
* "sorted descending" is appended to the column name, to indicate the sort
* and its direction. Example: "Contact Name sorted." For a multicolumn
* sort, level strings are also appended indicating each column's level in
* the set of sorting columns. For example, if a grid is sorted first by
* column 3 and then by column 2, and column 2 is sorted in descending
* order, column 3's name will end with "Sorted Level 1," and column 2's
* name will end with "Sorted descending level 2." The strings for
* indicating ascending sort, descending sort, and sort level are
* localized.</p>
*
* <p>When the Name of the DataGrid or one of its items changes, a DataGrid
* dispatches the MSAA event <code>EVENT_OBJECT_NAMECHANGE</code> with the
* proper childID for a row or cell or 0 for itself.</p>
*
* <p>If an accessibility name is not set for an item editor, one is set
* based on the column header name for the cell.</p>
*
* <p><b>Description</b></p>
*
* <p>The MSAA Description of a DataGrid is, by default, an empty string,
* but you can set the DataGrid's <code>accessibilityDescription</code>
* property.</p>
*
* <p>The Description of each row, cell, or header is the empty string and
* can not be set by an AccImpl.</p>
*
* <p>This property is not handled by the DataGrid accessibility
* implementation for item editors as item editors manage themselves.</p>
*
* <p><b>State</b></p>
*
* <p>The MSAA State of a DataGrid is a combination of:</p>
* <ul>
* <li><code>STATE_SYSTEM_UNAVAILABLE</code> (when <code>enabled</code> is
* <code>false</code>)</li>
* <li><code>STATE_SYSTEM_FOCUSABLE</code> (when <code>enabled</code> is
* <code>true</code>)</li>
* <li><code>STATE_SYSTEM_FOCUSED</code> (when <code>enabled</code> is
* <code>true</code> and the DataGrid has focus)</li>
* <li><code>STATE_SYSTEM_MULTISELECTABLE</code> (when
* <code>allowMultipleSelection</code> is true)</li>
* </ul>
*
* <p>The State of a data row or cell is a combination of:</p>
* <ul>
* <li><code>STATE_SYSTEM_FOCUSABLE</code></li>
* <li><code>STATE_SYSTEM_FOCUSED</code> (when focused)</li>
* <li><code>STATE_SYSTEM_OFFSCREEN</code> (when the row or cell has
* scrolled offscreen)</li>
* <li><code>STATE_SYSTEM_SELECTABLE</code></li>
* <li><code>STATE_SYSTEM_SELECTED</code> (when it is selected)</li>
* </ul>
*
* <p>The State of a header cell is <code>STATE_SYSTEM_NORMAL</code>, since
* header cells may not receive focus or be selected. As currently
* implemented, header cells may not report
* <code>STATE_SYSTEM_OFFSCREEN</code> even if the grid itself is moved such
* that its headers are offscreen.</p>
*
* <p>When the State of the DataGrid or one of its items changes, a DataGrid
* dispatches the MSAA event <code>EVENT_OBJECT_STATECHANGE</code> with the
* proper childID for the row or cell or 0 for itself.</p>
*
* <p>This property is not handled by the DataGrid accessibility
* implementation for item editors as item editors manage themselves.</p>
*
* <p><b>Value</b></p>
*
* <p>DataGrids and their children (rows, cells, and headers) do not have
* MSAA Values.</p>
*
* <p><b>Location</b></p>
*
* <p>The MSAA Location of a DataGrid or a row, data cell, or header cell
* within it is its bounding rectangle. The Location of an item that is
* currently not displayed on screen is undefined.</p>
*
* <p>This property is not handled by the DataGrid accessibility
* implementation for item editors as item editors manage themselves.</p>
*
* <p><b>Default Action</b></p>
*
* <p>A DataGrid does not have an MSAA DefaultAction. The MSAA DefaultAction
* for a row or cell is "Double Click" and for a header cell is "Click," and
* the corresponding localized string will be returned when the default
* action string is requested.</p>
*
* <p>Performing the default action on a data row or cell will cause it to
* be focused and selected and may cause other behavior depending on
* cell/row type. Performing the default action on a header will cause the
* grid to be sorted by that column. Repeated default actions on the header
* will toggle the sort order between ascending and descending. At this
* writing, there is no way via the AccImpl to arrange for a multilevel sort
* on several columns at once.</p>
*
* <p>This property is not handled by the DataGrid accessibility
* implementation for item editors as item editors manage themselves.</p>
*
* <p><b>Focus</b></p>
*
* <p>When there is no specific item (row or cell depending on selection
* mode) in focus within the grid, Focus returns 0 indicating that the grid
* itself has focus. This should only happen when the grid contains no
* data.</p>
*
* <p>When a row (row selection mode) or cell (cell selection mode) has
* focus, Focus returns the childID of the focused item.</p>
*
* <p>When a DataGrid receives focus, it dispatches the MSAA event
* <code>EVENT_OBJECT_FOCUS</code>. This event is also dispatched when
* focus moves among rows or cells within the grid.</p>
*
* <p>A focus change event is fired on the item editor when it
* starts/appears. A focus change event is fired on the DataGrid when the
* item editor is saved or closed.</p>
*
* <p><b>Selection</b></p>
*
* <p>A DataGrid allows either a single row or cell or multiple rows or
* cells to be selected, depending on the
* <code>allowMultipleSelection</code> property. Selection returns an array
* of the integer childIDs of the selected items.</p>
*
* <p>When an item is selected exclusively, it dispatches MSAA event
* <code>EVENT_OBJECT_SELECTION</code>. When a cell (cell selection mode)
* or row (row selection mode) is added to the current set of selections,
* the dispatched event is <code>EVENT_OBJECT_SELECTIONADD</code>.
* Similarly, if an item (cell or row) is removed from selection, the
* dispatched event is <code>EVENT_OBJECT_SELECTIONREMOVE</code>. If all
* selections are cleared (regardless of how many items were selected) or a
* select-all or select-region action is performed, the dispatched event is
* <code>EVENT_OBJECT_SELECTIONWITHIN</code>. Any selection operation not
* matching one of those listed above will dispatch
* <code>EVENT_OBJECT_SELECTION</code>.</p>
*
* <p>This property is not handled by the DataGrid accessibility
* implementation for item editors as item editors manage themselves.</p>
*
* <p><b>Select</b></p>
*
* <p>The <code>accSelect</code> method implements requests made via MSAA
* for changes in selection and/or focus within the DataGrid. The AccImpl
* for the DataGrid supports the setting of focus to a DataGrid itself or to
* a data item or set of items (row or cell depending on selection mode)
* within it. Supported actions include setting focus, exclusively
* selecting one item, and adding and removing an item or set of items from
* selection, all as defined in the Microsoft Active Accessibility
* specification. At this writing, attempting to use <code>accSelect</code>
* to extend an already-selected multi-cell region in cell multiselection
* mode to include more rows and columns at once may yield different results
* than doing the same action with a mouse.</p>
*
* <p>This property is not handled by the DataGrid accessibility
* implementation for item editors as item editors manage themselves.</p>
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public class DataGridAccImpl extends ListBaseAccImpl
{
include "../core/Version.as";
// See the DataGridAccImpl constructor for why this is not initialized here.
private static var dgAccInfo:ItemAccInfo; // = new ItemAccInfo();
//--------------------------------------------------------------------------
//
// Class methods
//
//--------------------------------------------------------------------------
/**
* Enables accessibility in the DataGrid class.
*
* <p>This method is called by application startup code
* that is autogenerated by the MXML compiler.
* Afterwards, when instances of DataGrid are initialized,
* their <code>accessibilityImplementation</code> property
* will be set to an instance of this class.</p>
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public static function enableAccessibility():void
{
DataGrid.createAccessibilityImplementation =
createAccessibilityImplementation;
}
/**
* @private
* Creates a DataGrid's AccessibilityImplementation object.
* This method is called from UIComponent's
* initializeAccessibility() method.
*/
mx_internal static function createAccessibilityImplementation(
component:UIComponent):void
{
// attach AccImpl to placeholder focusOwner component so that item editors
// are exposed as sibling of the dataGrid allow for correct exposure in MSAA
// and the ability for iSimpleTextSelection interface to work as it requires
// that the stage focused component be the same MSAA component
var accImpl:DataGridAccImpl = new DataGridAccImpl(component);
DataGrid(component).focusOwner.accessibilityImplementation = accImpl;
}
//--------------------------------------------------------------------------
//
// Constructor
//
//--------------------------------------------------------------------------
/**
* Constructor.
*
* @param master The UIComponent instance that this AccImpl instance
* is making accessible.
*
* @langversion 3.0
* @playerversion Flash 10
* @playerversion AIR 1.5
* @productversion Flex 4
*/
public function DataGridAccImpl(master:UIComponent)
{
super(master);
// Normally this would not be done here, but at this writing,
// initializing dgAccInfo from its declaration line causes an RTE,
// apparently because the AS compiler does not yet detect a forward-ref
// problem that results in an incorrect initialization at run time.
// [DGL, 2010-09-07]
if (!dgAccInfo)
dgAccInfo = new ItemAccInfo();
}
//--------------------------------------------------------------------------
//
// Overridden properties: AccImpl
//
//--------------------------------------------------------------------------
//----------------------------------
// eventsToHandle
//----------------------------------
/**
* @private
* Array of events that we should listen for from the master component.
*/
override protected function get eventsToHandle():Array
{
return super.eventsToHandle.concat([GridSelectionEvent.SELECTION_CHANGE, FocusEvent.FOCUS_IN, GridItemEditorEvent.GRID_ITEM_EDITOR_SESSION_START,
GridItemEditorEvent.GRID_ITEM_EDITOR_SESSION_SAVE, GridItemEditorEvent.GRID_ITEM_EDITOR_SESSION_CANCEL]);
}
//--------------------------------------------------------------------------
//
// Overridden methods: AccessibilityImplementation
//
//--------------------------------------------------------------------------
/**
* @private
* Gets the role for the component.
*
* @param childID Children of the component
*/
override public function get_accRole(childID:uint):uint
{
if (childID == 0)
// Get this out of the way quick; it's often requested by AT.
return role;
dgAccInfo.setup(master, childID);
if (dgAccInfo.isInvalid || !dgAccInfo.dataGrid.columns)
return null;
if (dgAccInfo.isColumnHeader)
return AccConst.ROLE_SYSTEM_COLUMNHEADER;
else
// Same role for all childIDs regardless of mode (row or cell).
// Valid and invalid childIDs return this;
// this behavior is common to most list-based controls we've seen.
// [DGL, 2010-08-10]
return AccConst.ROLE_SYSTEM_LISTITEM;
}
/**
* @private
* IAccessible method for returning the state of the GridItem.
* States are predefined for all the components in MSAA.
* Values are assigned to each state.
* Depending upon the GridItem being Selected, Selectable, Invisible,
* Offscreen, a value is returned.
*
* @param childID uint
*
* @return State uint
*/
override public function get_accState(childID:uint):uint
{
var accState:uint = getState(childID);
if (childID == 0
&& DataGrid(master).focusOwner == UIComponent(master).getFocus())
accState |= AccConst.STATE_SYSTEM_FOCUSED;
if (int(childID) <= 0)
return accState;
dgAccInfo.setup(master, childID);
if (!dgAccInfo.dataGrid.columns)
return accState;
if (dgAccInfo.isInvalid)
// Child ID out of bounds most likely.
return accState;
if (dgAccInfo.isColumnHeader)
{
// isColumnHeader implies columnHeaderGroup.visible is true.
if (dgAccInfo.dataGrid.columnHeaderGroup
&& !dgAccInfo.dataGrid.columnHeaderGroup.getHeaderRendererAt(dgAccInfo.columnIndex).visible)
accState |= AccConst.STATE_SYSTEM_OFFSCREEN;
// There are some states we don't allow for these.
return accState & ~(
AccConst.STATE_SYSTEM_FOCUSABLE
| AccConst.STATE_SYSTEM_SELECTABLE
| AccConst.STATE_SYSTEM_FOCUSED
| AccConst.STATE_SYSTEM_SELECTED
);
}
// We now have only rows and data cells to consider.
// Determine if this item is focused.
if (childID == get_accFocus())
accState |= AccConst.STATE_SYSTEM_FOCUSED;
// Must recalculate dgAccInfo here because get_accFocus() changed it.
dgAccInfo.setup(master, childID);
// Anything (row or cell) is selectable unless selection is not allowed at all.
var mode:String = dgAccInfo.dataGrid.selectionMode;
if (mode != GridSelectionMode.NONE)
{
accState |= AccConst.STATE_SYSTEM_SELECTABLE;
if (dgAccInfo.isMultiSelect)
{
accState |= AccConst.STATE_SYSTEM_MULTISELECTABLE;
}
}
// Figure out selectedness.
var isSelected:Boolean;
if (dgAccInfo.isCellMode)
isSelected = dgAccInfo.dataGrid.selectionContainsCell(
dgAccInfo.rowIndex,
dgAccInfo.columnIndex
);
else
isSelected = dgAccInfo.dataGrid.selectionContainsIndex(
dgAccInfo.rowIndex
);
if (isSelected)
accState |= AccConst.STATE_SYSTEM_SELECTED;
// Figure out visibility and offscreenness.
var rowIndex:int = dgAccInfo.rowIndex;
var columnIndex:int = dgAccInfo.columnIndex;
if (!dgAccInfo.isCellMode)
columnIndex = -1;
if (!dgAccInfo.dataGrid.grid.isCellVisible(rowIndex, columnIndex))
accState |= AccConst.STATE_SYSTEM_OFFSCREEN
return accState;
}
/**
* @private
* IAccessible method for returning the Default Action.
*
* @param childID uint
*
* @return DefaultAction String
*/
override public function get_accDefaultAction(childID:uint):String
{
if (get_accRole(childID) == AccConst.ROLE_SYSTEM_COLUMNHEADER)
return "Click";;
return super.get_accDefaultAction(childID);
}
/**
* @private
* IAccessible method for executing the Default Action.
*
* @param childID uint
*/
override public function accDoDefaultAction(childID:uint):void
{
dgAccInfo.setup(master, childID);
if (!dgAccInfo.dataGrid.columns || dgAccInfo.isInvalid)
return;
if (dgAccInfo.isColumnHeader)
{
if (dgAccInfo.dataGrid.sortableColumns
&& dgAccInfo.dataGrid.columns.getItemAt(dgAccInfo.columnIndex).sortable)
{
// TODO: This only allows sorting by one column at a time.
var columnIndices:Vector.<int> = Vector.<int>([dgAccInfo.columnIndex]);
dgAccInfo.dataGrid.sortByColumns(columnIndices);
dgAccInfo.dataGrid.columnHeaderGroup.visibleSortIndicatorIndices = columnIndices;
}
return;
}
// TODO: Allow doDefaultAction to go into edit mode if editable
accSelect(AccConst.SELFLAG_TAKESELECTION | AccConst.SELFLAG_TAKEFOCUS, childID);
}
/**
* @private
* Method to return an array of childIDs.
*
* @return Array
*/
override public function getChildIDArray():Array
{
dgAccInfo.setup(master, 0);
if (!dgAccInfo.dataGrid.columns)
return null;
return createChildIDArray(dgAccInfo.maxChildID);
}
/**
* @private
* IAccessible method for returning the bounding box of the GridItem.
*
* @param childID uint
*
* @return Location Object
*/
override public function accLocation(childID:uint):*
{
dgAccInfo.setup(master, childID);
if (!dgAccInfo.dataGrid.columns)
return null;
if (dgAccInfo.isInvalid)
return null;
return dgAccInfo.boundingRect();
}
/**
* @private
* IAccessible method for returning the childFocus of the DataGrid.
*
* @param childID uint
*
* @return focused childID.
*/
override public function get_accFocus():uint
{
dgAccInfo.setup(master, 0);
if (!dgAccInfo.dataGrid.columns || !dgAccInfo.dataGrid.dataProvider)
return null;
return dgAccInfo.childIDFromRowAndColumn(
dgAccInfo.dataGrid.grid.caretRowIndex,
dgAccInfo.dataGrid.grid.caretColumnIndex
);
}
//--------------------------------------------------------------------------
//
// Overridden methods: AccImpl
//
//--------------------------------------------------------------------------
/**
* @private
* method for returning the name of the ListItem/DataGrid
* which is spoken out by the screen reader
* The ListItem should return the label as the name with m of n string and
* DataGrid should return the name specified in the AccessibilityProperties.
*
* @param childID uint
*
* @return Name String
*/
override protected function getName(childID:uint):String
{
if (int(childID) <= 0)
return null;
dgAccInfo.setup(master, childID);
if (dgAccInfo.isInvalid || !dgAccInfo.dataGrid.columns)
return null;
var resourceManager:IResourceManager;
if (dgAccInfo.isColumnHeader)
{
var column:Object = dgAccInfo.dataGrid.columns.getItemAt(dgAccInfo.columnIndex);
var headerName:String = column.headerText;
var sortColumnIndices:Vector.<int> = dgAccInfo.dataGrid.columnHeaderGroup.visibleSortIndicatorIndices;
var sortIndex:int = sortColumnIndices.indexOf(dgAccInfo.columnIndex);
if (sortIndex < 0)
return headerName;
resourceManager = ResourceManager.getInstance();
var sortString:String;
if (column.sortDescending)
sortString = resourceManager.getString("components", "sortedDescending");
else
sortString = resourceManager.getString("components", "sortedAscending");
if (sortColumnIndices.length > 1)
sortString += resourceManager.getString("components", "sortLevel"
).replace("%1", String(sortIndex+1));
headerName = headerName +" " +sortString;
return headerName;
}
if (!dgAccInfo.dataGrid.dataProvider)
return null;
// We now have only rows and data cells to consider.
// String representation of row position.
var rowString:String = makeRowString(dgAccInfo);
// Construct the name to return.
var name:String = "";
var rowObject:Object = dgAccInfo.dataGrid.dataProvider.getItemAt(dgAccInfo.rowIndex);
var columns:IList = dgAccInfo.dataGrid.columns;
if (dgAccInfo.isCellMode)
{
if (dgAccInfo.headerCount > 0)
name = columns.getItemAt(dgAccInfo.columnIndex).headerText + ": "
name += cellName(rowObject, dgAccInfo.columnIndex);
}
else if (rowObject) // row mode
{
var idx:int = -1;
for (var c:int = 0; c < dgAccInfo.reachableColumnCount; c++)
{
if (c > 0)
name += ", ";
idx = dgAccInfo.dataGrid.grid.getNextVisibleColumnIndex(idx);
if (dgAccInfo.headerCount > 0)
name += columns.getItemAt(idx).headerText + ": ";
name += columns.getItemAt(idx).itemToLabel(rowObject);
}
} // cell or row mode
if (rowString)
name += ", " +rowString;
return name;
}
/**
* @private
* IAccessible method for focusing an item or altering selection.
* This is a full implementation based on the Microsoft Active Accessibility
* (MSAA) specification.
*
* @param selFlag:uint A combination of flags indicating what to do.
* Flags may be combined as indicated below:
* <dl>
* <dt><code>SELFLAG_TAKEFOCUS</code>
* <dd>Set focus to the childID given.
* May be combined with the below flags.
* <dt><code>SELFLAG_TAKESELECTION</code>
* <dd>Select the given child and unselect any other selected ones.
* Combining this with <code>SELFLAG_TAKEFOCUS</code>
* emulates a single mouse click.
* <dt><code>SELFLAG_ADDSELECTION</code>
* <dd>Add the given child to those that are selected.
* Combining this with <code>SELFLAG_TAKEFOCUS</code>
* emulates a mouse click on an unselected item with the <kbd>Ctrl</kbd> key down.
* <dt><code>SELFLAG_REMOVESELECTION</code>
* <dd>Remove the given child from those that are selected.
* Combining this with <code>SELFLAG_TAKEFOCUS</code>
* emulates a mouse click on a selected item with the <kbd>Ctrl</kbd> key down.
* <dt><code>SELFLAG_ADDSELECTION | SELFLAG_EXTENDSELECTION</code>
* <dd>Select all children from the current focus to the given child.
* <dt><code>SELFLAG_REMOVESELECTION | SELFLAG_EXTENDSELECTION</code>
* <dd>Unselect all children from the current focus to the given child.
* <dt><code>SELFLAG_EXTENDSELECTION</code>
* <dd>Duplicate the selected/unselected state of the currently focused child,
* for all children through the given child.
* Combining this with <code>SELFLAG_TAKEFOCUS</code>
* emulates a mouse click with the <kbd>Shift</kbd> key down.
* </dl>
* @param childID uint The ID of the child to use.
* For extending selection or unselection, this is the endpoint,
* and current focus is the anchor.
*/
override public function accSelect(selFlag:uint, childID:uint):void
{
dgAccInfo.setup(master, childID);
if (dgAccInfo.isColumnHeader || dgAccInfo.isInvalid)
return;
// TODO: Adjust for there being no apparent way to just set focus
// without altering selection: For now, treate SELFLAG_TAKEFOCUS like
// SELFLAG_TAKESELECTION, and remove the TAKEFOCUS bit from
// all other requests, thus letting other selection calls also
// handle focus changes.
// Code for handling focus properly remains below in case a
// way is found to make the code at the end of this function
// actually set focus independent of selection.
if (selFlag == AccConst.SELFLAG_TAKEFOCUS)
selFlag = AccConst.SELFLAG_TAKESELECTION
else if (selFlag & AccConst.SELFLAG_TAKEFOCUS)
selFlag -= AccConst.SELFLAG_TAKEFOCUS
var settingFocus:Boolean = Boolean(selFlag & AccConst.SELFLAG_TAKEFOCUS);
if (settingFocus)
selFlag -= AccConst.SELFLAG_TAKEFOCUS;
var rowIndex:int = dgAccInfo.rowIndex;
var columnIndex:int = dgAccInfo.columnIndex;
if (!dgAccInfo.isCellMode)
columnIndex = -1;
var grid:DataGrid = dgAccInfo.dataGrid;
grid.ensureCellIsVisible(rowIndex, columnIndex);
// First selection, then focus if requested.
// Caveat: Invalid selection flags will cause no changes to be made,
// including a focus change if one was requested.
if (selFlag == AccConst.SELFLAG_TAKESELECTION)
{
if (columnIndex == -1)
grid.setSelectedIndex(rowIndex);
else
grid.setSelectedCell(rowIndex, columnIndex);
}
else if (selFlag == AccConst.SELFLAG_ADDSELECTION)
{
if (columnIndex == -1)
grid.addSelectedIndex(rowIndex);
else
grid.addSelectedCell(rowIndex, columnIndex);
}
else if (selFlag == AccConst.SELFLAG_REMOVESELECTION)
{
if (columnIndex == -1)
grid.removeSelectedIndex(rowIndex);
else
grid.removeSelectedCell(rowIndex, columnIndex);
}
else if (Boolean(selFlag & AccConst.SELFLAG_EXTENDSELECTION))
{
if (Boolean(selFlag & AccConst.SELFLAG_ADDSELECTION)
&& Boolean(selFlag & AccConst.SELFLAG_REMOVESELECTION))
return;
var focusedID:uint = get_accFocus();
if (!focusedID)
{
// This could be assumed to be 1, but this is probably safer.
return;
}
// We have to recalculate dgAccInfo for the anchor,
// but we already have row/column indices for the requested item,
// so we can do that without needing to reset it again afterward.
dgAccInfo.setup(master, focusedID);
if (dgAccInfo.isColumnHeader || dgAccInfo.isInvalid)
return;
var anchorRowIndex:int = dgAccInfo.rowIndex;
var anchorColumnIndex:int = dgAccInfo.columnIndex;
var adding:Boolean;
if (Boolean(selFlag & AccConst.SELFLAG_ADDSELECTION))
adding = true;
else if (Boolean(selFlag & AccConst.SELFLAG_REMOVESELECTION))
adding = false;
else
{
// MSAA docs say use selection state of anchor here.
// This method of figuring that out is a bit more
// intensive than necessary (other states calculated also),
// but selection extension without ADD/REMOVE being specified
// should be a very infrequent occurrence, and this method
// centralizes the logic for figuring out what is selected.
adding = Boolean(get_accState(focusedID) & AccConst.STATE_SYSTEM_SELECTED);
}
if (columnIndex == -1)
grid.selectIndices(
Math.min(anchorRowIndex, rowIndex),
Math.abs(rowIndex - anchorRowIndex) + 1
);
else
grid.selectCellRegion(
Math.min(anchorRowIndex, rowIndex),
Math.min(anchorColumnIndex, columnIndex),
Math.abs(rowIndex - anchorRowIndex) + 1,
Math.abs(columnIndex - anchorColumnIndex) + 1
);
}
// Now handle the focus change request if there was one
// (and if invalid flags didn't cause a return above).
// TODO: This approach does not work properly, and to date,
// no approach that does has been found.
// Code at the top of this function effectively makes the
// below "if" never true.
if (settingFocus)
grid.grid.caretRowIndex = rowIndex;
grid.grid.caretColumnIndex = columnIndex;
}
/**
* @private
* IAccessible method for returning the child Selections in the List.
*
* @param childID uint
*
* @return focused childID.
*/
override public function get_accSelection():Array
{
var accSelection:Array = [];
dgAccInfo.setup(master, 0);
if (!dgAccInfo.dataGrid.columns)
return null;
var i:int
var n:int
var items:Object;
if (dgAccInfo.isCellMode)
{
items = dgAccInfo.dataGrid.selectedCells;
n = items.length;
for (i = 0; i < n; i++)
{
// The selected childID is effectively the 1-based cell index.
// This is row*colCount + columnInRow +columnHeaderCount +1.
accSelection[i] = items[i].rowIndex * dgAccInfo.reachableColumnCount
+ items[i].columnIndex
+ dgAccInfo.headerCount + 1;
}
}
else // row mode
{
items = dgAccInfo.dataGrid.selectedIndices;
n = items.length;
for (i = 0; i < n; i++)
{
// This time we just need rowIndex (0-based) + headerCount + 1.
accSelection[i] = items[i] + dgAccInfo.headerCount + 1;
}
}
return accSelection;
}
//--------------------------------------------------------------------------
//
// Overridden event handlers: AccImpl
//
//--------------------------------------------------------------------------
/**
* @private
* Override the generic event handler.
* All AccImpl must implement this to listen
* for events from its master component.
*/
override protected function eventHandler(event:Event):void
{
// Let AccImpl class handle the events
// that all accessible UIComponents understand.
$eventHandler(event);
dgAccInfo.setup(master, 0);
if (!dgAccInfo.dataGrid.columns)
return;
var childID:uint;
switch (event.type)
{
case GridCaretEvent.CARET_CHANGE:
{
childID = dgAccInfo.childIDFromRowAndColumn(
int(GridCaretEvent(event).newRowIndex),
int(GridCaretEvent(event).newColumnIndex)
)
if (int(childID) > 0)
if (!dgAccInfo.dataGrid.itemEditorInstance)
Accessibility.sendEvent(dgAccInfo.dataGrid.focusOwner, childID, AccConst.EVENT_OBJECT_FOCUS);
else
Accessibility.sendEvent(UIComponent(dgAccInfo.dataGrid.itemEditorInstance), 0, AccConst.EVENT_OBJECT_FOCUS);
break;
}
case GridSelectionEvent.SELECTION_CHANGE:
{
var gridSelectionEvent:GridSelectionEvent = GridSelectionEvent(event);
childID = dgAccInfo.childIDFromRowAndColumn(
gridSelectionEvent.selectionChange.rowIndex,
gridSelectionEvent.selectionChange.columnIndex
);
if (int(childID) <= 0)
return;
var eventID:int = AccConst.EVENT_OBJECT_SELECTION;
var kind:String = gridSelectionEvent.kind;
if (kind == GridSelectionEventKind.ADD_CELL
|| kind == GridSelectionEventKind.ADD_ROW)
eventID = AccConst.EVENT_OBJECT_SELECTIONADD;
else if (kind == GridSelectionEventKind.REMOVE_CELL
|| kind == GridSelectionEventKind.REMOVE_ROW)
eventID = AccConst.EVENT_OBJECT_SELECTIONREMOVE;
else if (kind == GridSelectionEventKind.CLEAR_SELECTION
|| kind == GridSelectionEventKind.SELECT_ALL
|| kind == GridSelectionEventKind.SET_CELL_REGION
|| kind == GridSelectionEventKind.SET_ROWS)
eventID = AccConst.EVENT_OBJECT_SELECTIONWITHIN;
Accessibility.sendEvent(dgAccInfo.dataGrid.focusOwner, childID, eventID);
break;
}
case FocusEvent.FOCUS_IN:
{
// do not fire focus changes for list when a child editor is focused
// as this causes an extra event being fired
if (event.target == DataGrid(master).focusOwner)
Accessibility.sendEvent(DataGrid(master).focusOwner, 0, AccConst.EVENT_OBJECT_FOCUS);
break;
}
case GridItemEditorEvent.GRID_ITEM_EDITOR_SESSION_START:
{
dgAccInfo.setup(master, 0);
childID = dgAccInfo.childIDFromRowAndColumn(
GridItemEditorEvent(event).rowIndex,
GridItemEditorEvent(event).columnIndex
);
var editor:Object = event.currentTarget.itemEditorInstance;
var defaultGridItemEditorClass:Class = Class(getDefinition("spark.components.gridClasses.DefaultGridItemEditor", master.moduleFactory));
if (editor is defaultGridItemEditorClass)
{
// The specific part with focus.
try
{
editor = Object(editor).textArea;
}
catch(e:Error)
{
}
}
else
{
// Try to find the specific part with focus,
// falling back to the itemEditorInstance if we don't know it.
var realEditor:UIComponent = null;
try
{
realEditor = UIComponent(editor.stage.focus);
}
catch(e:Error)
{
}
if (Boolean(realEditor) && editor != realEditor)
{
editor = realEditor;
}
}
// Name the editor with this cell's name.
// This applies the same rules for row identification for both edit and non-edit cells.
if (!editor.accessibilityName)
{
var edName:String = "";
if (dgAccInfo.headerCount > 0)
{
var columns:IList = dgAccInfo.dataGrid.columns;
var columnIndex:int = GridItemEditorEvent(event).columnIndex;
edName += columns.getItemAt(columnIndex).headerText;
}
// For the row string, we need to indicate which cell to use.
dgAccInfo.setup(master, childID);
var rowString:String = makeRowString(dgAccInfo);
if (rowString)
edName += " " +rowString;
editor.accessibilityName = edName;
Accessibility.updateProperties();
}
Accessibility.sendEvent(UIComponent(editor), 0, AccConst.EVENT_OBJECT_FOCUS);
break;
}
case GridItemEditorEvent.GRID_ITEM_EDITOR_SESSION_SAVE, GridItemEditorEvent.GRID_ITEM_EDITOR_SESSION_CANCEL:
{
dgAccInfo.setup(master, 0);
childID = dgAccInfo.childIDFromRowAndColumn(
GridItemEditorEvent(event).rowIndex, GridItemEditorEvent(event).columnIndex);
Accessibility.sendEvent(DataGrid(master).focusOwner, childID, AccConst.EVENT_OBJECT_FOCUS);
}
}
}
//--------------------------------------------------------------------------
//
// Methods
//
//--------------------------------------------------------------------------
/**
* @private
*/
private function makeRowString(dgAccInfo:ItemAccInfo):String
{
var rowString:String = "";
if ((dgAccInfo.isCellMode && dgAccInfo.reachableColumnIndex == 0) || !dgAccInfo.isCellMode)
{
var resourceManager:IResourceManager = ResourceManager.getInstance();
rowString = resourceManager.getString("components", "rowMofN");
rowString = rowString.replace("%1", dgAccInfo.reachableRowIndex + 1).replace("%2", dgAccInfo.reachableRowCount);
}
return rowString;
}
/**
* @private
*/
private function cellName(rowObject:Object, columnIndex:int):String
{
var item:Object = rowObject;
var dataGrid:DataGrid = DataGrid(master);
var columns:IList = dataGrid.columns;
if (!columns)
return null;
var column:Object = columns.getItemAt(columnIndex);
if (!column)
return null;
return column.itemToLabel(rowObject);
}
}
}
/**
* @private
* ItemAccInfo is a support class used by DataGridAccImpl to determine various
* things about a DataGrid. For performance reasons, this class is
* instantiated once by DataGridAccImpl and repopulated as needed from
* DataGridAccImpl code via calls to ItemAccInfo.setup().
*
* <p>Terminology: A "reachable" cell, row, or column refers to an item that
* the developer has allowed a user to view, whether or not it happens to be
* visible on screen at the moment.
* At this writing, the ability to hide rows from the user is not anticipated,
* but the hiding of columns will be possible.
* (When rows of data are hidden via DataProvider filtering, they simply don't
* appear in the DataGrid at all.)
*/
internal class ItemAccInfo
{
import mx.core.UIComponent;
import spark.components.DataGrid;
import spark.components.Grid;
import spark.components.gridClasses.GridSelectionMode;
import spark.components.gridClasses.CellPosition;
import flash.geom.Rectangle;
import flash.geom.Point;
/**
* Constructor.
*/
public function ItemAccInfo()
{
super();
}
//--------------------------------------------------------------------------
//
// Methods
//
//--------------------------------------------------------------------------
/**
* @private
* Sets up for use with a particular DataGrid and item within it.
*
* @param master The UIComponent instance that the calling AccImpl instance
* is making accessible.
* @param childID The childID of the DataGrid item of interest.
* This may refer to a header cell, a data cell, or a data row.
*/
public function setup(master:UIComponent, childID:uint):void
{
this.master = master;
this.childID = childID;
dataGrid = DataGrid(master);
reachableRowIndices = null;
reachableColumnIndices = null;
if (dataGrid.columns)
{
columnCount = dataGrid.columns.length;
// For efficiency in the common case, assume all is visible,
// and only build a vector of reachable indices if this is wrong.
var somethingIsInvisible:Boolean = false;
var column:Object;
var i:int;
for (i = 0; i < columnCount; i++)
{
column = dataGrid.columns.getItemAt(i);
if (!column.visible)
{
somethingIsInvisible = true;
break;
}
}
if (somethingIsInvisible)
{
reachableColumnIndices = new Vector.<int>();
for (i = 0; i < columnCount; i++)
{
column = dataGrid.columns.getItemAt(i);
if (column.visible)
reachableColumnIndices.push(column.columnIndex);
}
}
reachableColumnCount = reachableColumnIndices == null ?
columnCount : reachableColumnIndices.length;
}
else
{
columnCount = 0;
reachableColumnCount = 0;
}
if (dataGrid.dataProvider)
{
rowCount = dataGrid.dataProvider.length;
reachableRowCount = reachableRowIndices == null ?
rowCount : reachableRowIndices.length;
}
else
{
rowCount = 0;
reachableRowCount = 0;
}
headerCount = 0;
reachableHeaderCount = 0;
maxChildID = 0;
isCellMode = false;
isMultiSelect = false;
isInvalid = false;
isColumnHeader = false;
rowIndex = 0;
columnIndex = 0;
reachableRowIndex = 0;
reachableColumnIndex = 0;
var itemIndex:int = childID - 1;
if (dataGrid.columnHeaderGroup && dataGrid.columnHeaderGroup.visible)
{
// There are visible column headers, so their childIDs come first.
itemIndex -= reachableColumnCount;
headerCount = columnCount;
reachableHeaderCount = reachableColumnCount;
}
else
{
// No header bar or it's invisible,
// so we should not try to expose any data within it.
headerCount = 0;
reachableHeaderCount = 0;
}
var mode:String = dataGrid.selectionMode;
isCellMode = (
mode == GridSelectionMode.SINGLE_CELL
|| mode == GridSelectionMode.MULTIPLE_CELLS
);
isMultiSelect = (
mode == GridSelectionMode.MULTIPLE_CELLS
|| mode == GridSelectionMode.MULTIPLE_ROWS
);
maxChildID = 0;
// Account for reachable headers.
maxChildID += reachableHeaderCount;
// Then for reachable cells or rows as appropriate.
if (isCellMode)
maxChildID += reachableRowCount * reachableColumnCount;
else
maxChildID += reachableRowCount;
isColumnHeader = false;
isInvalid = false;
if (childIDOutOfBounds(childID))
{
isInvalid = true;
reachableColumnIndex = -1;
reachableRowIndex = -1;
itemIndex = -1;
}
else if (itemIndex < 0)
{
// This childID refers to a header, not a data row or cell.
isColumnHeader = true;
reachableColumnIndex = itemIndex + reachableColumnCount;
reachableRowIndex = -1;
itemIndex = -1;
}
else if (isCellMode)
{
reachableRowIndex = Math.floor(itemIndex / reachableColumnCount);
reachableColumnIndex = itemIndex % reachableColumnCount;
}
else
{
reachableRowIndex = itemIndex;
// Using 0 here so, for example, getItemRendererAt() calls still work for a row.
reachableColumnIndex = 0;
}
rowIndex = reachableRowIndex;
columnIndex = reachableColumnIndex;
if (reachableRowIndex >= 0 && reachableRowIndices && reachableRowIndices.length)
rowIndex = reachableRowIndices[reachableRowIndex];
if (reachableColumnIndex >= 0 && reachableColumnIndices && reachableColumnIndices.length)
columnIndex = reachableColumnIndices[reachableColumnIndex];
}
/**
* @private
*/
// The master component reference for which this AccImpl is instantiated.
public var master:UIComponent;
// The childID within that component for which this accInfo is calculated.
public var childID:uint;
// The DataGrid reference for this instance.
public var dataGrid:DataGrid;
// Number of columns, headers, and rows overall.
public var columnCount:int;
public var headerCount:int;
public var rowCount:int;
// Number of columns, headers, and rows that are reachable.
// ("Reachable" means not marked invisible by the developer.)
public var reachableColumnCount:int;
public var reachableHeaderCount:int;
public var reachableRowCount:int;
// Indices of reachable rows and columns.
// These are null when nothing is filtered, for performance reasons.
protected var reachableRowIndices:Vector.<int>;
protected var reachableColumnIndices:Vector.<int>;
// The highest valid childID.
public var maxChildID:int;
// True if we are in cell navigation mode (single or multiple selection).
public var isCellMode:Boolean;
// True if we are in a multiple selection mode (row or cell).
public var isMultiSelect:Boolean;
// True if the data in this object is invalid for some reason.
// Usually this means an invalid childID was passed to setup().
public var isInvalid:Boolean;
// True if the given childID represents a column header cell.
public var isColumnHeader:Boolean;
// 0-based indices of row and column in the sets of reachable ones.
public var reachableColumnIndex:int;
public var reachableRowIndex:int;
// 0-based indices of row and column in the set of all of each.
public var columnIndex:int;
public var rowIndex:int;
/**
* @private
* Determine the childID corresponding to the given DataGrid row and column.
* The row and column indices taken here are from the set of reachable
* rows and colums; they are not absolute row/column indices.
* This method is used internally; see childIDFromRowAndColumn() for the
* external interface.
*
* @param reachableRowIndex The 0-based index of the row among reachable rows.
* @param reachableColumnIndex The 0-based index of the column among reachable columns.
* Ignored if this grid is in a row navigation mode.
*
* @return The childID corresponding to the row and column indices passed.
*/
protected function childIDFromReachableRowAndColumn(reachableRowIndex:int, reachableColumnIndex:int):uint
{
var childID:int = reachableHeaderCount + 1;
if (reachableRowIndex < 0)
childID = 0;
else if (isCellMode)
if (reachableColumnIndex < 0)
childID = 0;
else
childID += reachableRowIndex * reachableColumnCount + reachableColumnIndex;
else
childID += reachableRowIndex;
return uint(childID);
}
/**
* @private
* Determine the childID corresponding to the given DataGrid row and column.
* The indices passed to this method are mapped onto the set of rows and
* columns that are or can be exposed to the user.
*
* @param rowIndex The 0-based index of the row.
* @param columnIndex The 0-based index of the column.
* Ignored if this grid is in a row navigation mode.
*
* @return The childID corresponding to the row and column indices passed.
*/
public function childIDFromRowAndColumn(rowIndex:int, columnIndex:int):uint
{
return childIDFromReachableRowAndColumn(
reachableRowIndices == null ? rowIndex : reachableRowIndices.indexOf(rowIndex),
reachableColumnIndices == null ? columnIndex : reachableColumnIndices.indexOf(columnIndex)
);
}
/**
* @private
* Internal method for checking if a childID is above or below those allowed.
*
* @param childID The childID to check.
*
* @return true if the childID is out of bounds and false if not.
*/
private function childIDOutOfBounds(childID: int):Boolean
{
if (int(childID) <= 0)
return true;
if (!dataGrid.dataProvider || !dataGrid.columns)
return true;
if (childID > maxChildID)
return true;
return false;
}
/**
* @private
* Return an object giving the bounds of this grid item (row or cell).
* The object returned is either an IVisualElement (renderer), in which
* case its coordinates are assumed to be stage-based, or a
* flash.geom.Rectangle, in which case its coordinates are relative to
* the top left corner of the DataGrid. These are the requirements of
* the AccImpl::get_accLocation() method.
*
* @return The Rectangle indicating the item's bounds.
*/
public function boundingRect():Object
{
// First see if this item is on screen.
// We could skip this, but we'd run the risk of having
// assistive technology create huge numbers of itemRenderers below
// by quickly scanning an entire grid.
if (!isColumnHeader && (rowIndex < 0 || rowIndex >= rowCount))
// Also covers rowCount == 0 effectively.
return null;
if (isCellMode && (columnIndex < 0 || columnIndex >= columnCount))
return null;
var vri:Vector.<int> = dataGrid.grid.getVisibleRowIndices();
var vci:Vector.<int> = dataGrid.grid.getVisibleColumnIndices();
if (isColumnHeader && vci.indexOf(columnIndex) < 0)
return null;
if ((!isColumnHeader && vri.indexOf(rowIndex) < 0)
|| (isCellMode && vci.indexOf(columnIndex) < 0))
return null;
var result:Object;
if (isColumnHeader)
{
result = dataGrid.columnHeaderGroup.getHeaderRendererAt(columnIndex);
}
else if (isCellMode)
{
result = dataGrid.grid.getItemRendererAt(rowIndex, columnIndex);
}
else // row mode
{
// Use the item renderers at both ends of this row
// to calculate the width.
// We assume here that top and bottom of all renderers for one row are equal.
var r1:Object = dataGrid.grid.getItemRendererAt(rowIndex, vci[0]);
var r2:Object = dataGrid.grid.getItemRendererAt(rowIndex, vci[vci.length-1]);
var xy:Point = new Point(
r1.getLayoutBoundsX(),
r1.getLayoutBoundsY()
);
xy = dataGrid.grid.localToGlobal(xy);
xy = dataGrid.globalToLocal(xy);
result = new Rectangle(
xy.x, xy.y,
r2.getLayoutBoundsX() + r2.getLayoutBoundsWidth() - r1.getLayoutBoundsX(),
r1.getLayoutBoundsHeight()
);
}
return result;
}
}