// Copyright 2007. Adobe Systems Incorporated. All Rights Reserved. package fl.accessibility { import flash.events.Event; import flash.accessibility.Accessibility; import fl.events.DataGridEvent; import fl.controls.listClasses.ICellRenderer; import fl.controls.SelectableList; import fl.controls.DataGrid; import fl.core.UIComponent; /** * The DataGridAccImpl class, also called the DataGrid Accessibility Implementation class, * is used to make a DataGrid component accessible. * *

The DataGridAccImpl class supports system roles, object-based events, and states.

* *

A DataGrid reports the role ROLE_SYSTEM_LIST (0x21) to a screen * reader. Items of a DataGrid report the role ROLE_SYSTEM_LISTITEM (0x22).

* *

A DataGrid reports the following states to a screen reader:

* * *

Additionally, items of a DataGrid report the following states:

* * *

A DataGrid dispatches the following events to a screen reader:

* * * @see fl.controls.DataGrid DataGrid * * @langversion 3.0 * @playerversion Flash 9.0.28.0 */ public class DataGridAccImpl extends SelectableListAccImpl { /** * @private * Static variable triggering the hookAccessibility() method. * This is used for initializing DataGridAccImpl class to hook its * createAccessibilityImplementation() method to DataGrid class * before it gets called from UIComponent. * * @langversion 3.0 * @playerversion Flash 9.0.28.0 */ private static var accessibilityHooked:Boolean = hookAccessibility(); /** * @private * Static method for swapping the createAccessibilityImplementation() * method of DataGrid with the DataGridAccImpl class. * * @langversion 3.0 * @playerversion Flash 9.0.28.0 */ private static function hookAccessibility():Boolean { DataGrid.createAccessibilityImplementation = createAccessibilityImplementation; return true; } //-------------------------------------------------------------------------- // Class constants //-------------------------------------------------------------------------- /** * @private * Role of listItem. * * @langversion 3.0 * @playerversion Flash 9.0.28.0 */ private static const ROLE_SYSTEM_LISTITEM:uint = 0x22; /** * @private * * @langversion 3.0 * @playerversion Flash 9.0.28.0 */ private static const STATE_SYSTEM_FOCUSED:uint = 0x00000004; /** * @private * * @langversion 3.0 * @playerversion Flash 9.0.28.0 */ private static const STATE_SYSTEM_INVISIBLE:uint = 0x00008000; /** * @private * * @langversion 3.0 * @playerversion Flash 9.0.28.0 */ private static const STATE_SYSTEM_OFFSCREEN:uint = 0x00010000; /** * @private * * @langversion 3.0 * @playerversion Flash 9.0.28.0 */ private static const STATE_SYSTEM_SELECTABLE:uint = 0x00200000; /** * @private * * @langversion 3.0 * @playerversion Flash 9.0.28.0 */ private static const STATE_SYSTEM_SELECTED:uint = 0x00000002; /** * @private * Event emitted if 1 item is selected. * * @langversion 3.0 * @playerversion Flash 9.0.28.0 */ private static const EVENT_OBJECT_FOCUS:uint = 0x8005; /** * @private * Event emitted if 1 item is selected. * * @langversion 3.0 * @playerversion Flash 9.0.28.0 */ private static const EVENT_OBJECT_SELECTION:uint = 0x8006; //-------------------------------------------------------------------------- // Class methods //-------------------------------------------------------------------------- /** * @private * Method for creating the Accessibility class. * This method is called from UIComponent. * * @param component The UIComponent instance that this AccImpl instance * is making accessible. * * @langversion 3.0 * @playerversion Flash 9.0.28.0 */ public static function createAccessibilityImplementation(component:UIComponent):void { component.accessibilityImplementation = new DataGridAccImpl(component); } /** * Enables accessibility for a DataGrid component. * This method is required for the compiler to activate * the accessibility classes for a component. * * @langversion 3.0 * @playerversion Flash 9.0.28.0 */ public static function enableAccessibility():void { } //-------------------------------------------------------------------------- // Constructor //-------------------------------------------------------------------------- /** * @private * @internal Nivesh says: I don't think we should document the constructors * for the accessibility classes. End-users just have to call the * static enableAccessibility method. They don't really create an * instance of the classes. * * Creates a new List Accessibility Implementation. * * @param master The UIComponent instance that this AccImpl instance * is making accessible. * * @langversion 3.0 * @playerversion Flash 9.0.28.0 */ public function DataGridAccImpl(master:UIComponent) { super(master); } //-------------------------------------------------------------------------- // Overridden properties: AccImpl //-------------------------------------------------------------------------- /** * @private * Array of events that we should listen for from the master component. * * @langversion 3.0 * @playerversion Flash 9.0.28.0 */ override protected function get eventsToHandle():Array { return super.eventsToHandle.concat([ DataGridEvent.ITEM_FOCUS_IN ]); } //-------------------------------------------------------------------------- // Overridden methods: AccessibilityImplementation //-------------------------------------------------------------------------- /** * @private * Gets the role for the component. * * @param childID Children of the component * * @langversion 3.0 * @playerversion Flash 9.0.28.0 */ override public function get_accRole(childID:uint):uint { if (childID == 0) { return role; } return ROLE_SYSTEM_LISTITEM; } /** * @private * IAccessible method for returning the value of the ListItem/DataGrid * which is spoken out by the screen reader * The DataGrid should return the name of the currently selected item * with m of n string as value when focus moves to DataGrid. * * @param childID * * @return Name * * @langversion 3.0 * @playerversion Flash 9.0.28.0 */ override public function get_accValue(childID:uint):String { var accValue:String; var dataGrid:DataGrid = DataGrid(master); if (childID == 0) { var row:int; var item:Object; var columns:Array; var n:int; var i:int; if (!dataGrid.editable) { row = dataGrid.selectedIndex; if (row > -1) { item = getItemAt(row); if (item is String) { accValue = "Row " + (row + 1) + " of " + dataGrid.dataProvider.length + " " + item; } else { accValue = "Row " + (row + 1) + " of " + dataGrid.dataProvider.length; columns = dataGrid.columns; n = columns.length; for (i = 0; i < n; i++) { accValue += " " + columns[i].headerText + " " + columns[i].itemToLabel(item); } } } } else { var coord:Object = dataGrid.editedItemPosition; if (coord) { row = coord.rowIndex; var col:int = coord.columnIndex; item = getItemAt(row); if (item is String) { accValue = "Row " + (row + 1) + " of " + dataGrid.dataProvider.length + " " + item; } else { columns = dataGrid.columns; var itemName:String = columns[col].itemToLabel(item); var headerText:String = columns[col].headerText; accValue = "Row " + (row + 1) + " of " + dataGrid.dataProvider.length; n = columns.length; for (i = 0; i < n; i++) { accValue += " " + columns[i].headerText + " " + columns[i].itemToLabel(item); } accValue += ", Editing " + headerText + " " + itemName; } } } } return accValue; } /** * @private * IAccessible method for returning the state of the grid item. * 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 * * @langversion 3.0 * @playerversion Flash 9.0.28.0 */ override public function get_accState(childID:uint):uint { var dataGrid:DataGrid = DataGrid(master); var accState:uint = getState(childID); var row:int; var col:int; if (childID > 0) { var index:int = childID - 1; if (!dataGrid.editable) { row = index; if (row < dataGrid.verticalScrollPosition || row >= dataGrid.verticalScrollPosition + dataGrid.rowCount) { accState |= (STATE_SYSTEM_OFFSCREEN | STATE_SYSTEM_INVISIBLE); } else { accState |= STATE_SYSTEM_SELECTABLE; var item:Object = dataGrid.getItemAt(row); var selItems:Array = dataGrid.selectedIndices; for(var i:int = 0; i < selItems.length; i++) { if(selItems[i] == row) { accState |= STATE_SYSTEM_SELECTED | STATE_SYSTEM_FOCUSED; break; } } } } else { row = Math.floor(index / dataGrid.columns.length); col = index % dataGrid.columns.length; if (row < dataGrid.verticalScrollPosition || row >= dataGrid.verticalScrollPosition + dataGrid.rowCount) { accState |= (STATE_SYSTEM_OFFSCREEN | STATE_SYSTEM_INVISIBLE); } else if (dataGrid.columns[col].editable) { accState |= STATE_SYSTEM_SELECTABLE; var coord:Object = dataGrid.editedItemPosition; if (coord && coord.rowIndex == row && coord.columnIndex == col) { accState |= STATE_SYSTEM_SELECTED | STATE_SYSTEM_FOCUSED; } } } } return accState; } /** * @private * IAccessible method for executing the Default Action. * * @param childID uint * * @langversion 3.0 * @playerversion Flash 9.0.28.0 */ override public function accDoDefaultAction(childID:uint):void { var dataGrid:DataGrid = DataGrid(master); if (childID > 0) { // Assuming childID is always ItemID + 1 // because getChildIDArray may not always be invoked. var index:int = childID - 1; // index is the (0 based) index of the elements after the headers if (!dataGrid.editable) { // index is the row id dataGrid.selectedIndex = index; } else { var row:int = Math.floor(index / dataGrid.columns.length); var col:int = index % dataGrid.columns.length; dataGrid.editedItemPosition = { rowIndex: row, columnIndex: col }; } } } /** * @private * Method to return an array of childIDs. * * @return Array * * @langversion 3.0 * @playerversion Flash 9.0.28.0 */ override public function getChildIDArray():Array { var childIDs:Array = []; var dataGrid:DataGrid = DataGrid(master); if (dataGrid.dataProvider) { // 0 is DataGrid, 1 to columnCount * Rows -> ItemRenderers var n:int = 0; if (!dataGrid.editable) { // non editable case (itemRenderers) n = dataGrid.dataProvider.length; } else { // editable case (rows) n = dataGrid.columns.length * dataGrid.dataProvider.length; } for (var i:int = 0; i < n; i++) { childIDs[i] = i + 1; } } return childIDs; } /** * @private * IAccessible method for returning the bounding box of the GridItem. * * @param childID uint * * @return Location Object * * @langversion 3.0 * @playerversion Flash 9.0.28.0 */ override public function accLocation(childID:uint):* { var dataGrid:DataGrid = DataGrid(master); if(childID > 0) { var index:int = childID - 1; var row:int; var col:int; var item:Object; if (!dataGrid.editable) { row = index if (row < dataGrid.verticalScrollPosition || row >= dataGrid.verticalScrollPosition + dataGrid.rowCount) { return null; } item = dataGrid.getItemAt(row); return dataGrid.itemToCellRenderer(item); } else { row = Math.floor(index / dataGrid.columns.length); col = index % dataGrid.columns.length; if (row < dataGrid.verticalScrollPosition || row >= dataGrid.verticalScrollPosition + dataGrid.rowCount) { return null; } item = dataGrid.getItemAt(row); return dataGrid.itemToCellRenderer(item); } } return dataGrid; } /** * @private * IAccessible method for returning the childFocus of the DataGrid. * * @param childID uint * * @return focused childID. * * @langversion 3.0 * @playerversion Flash 9.0.28.0 */ override public function get_accFocus():uint { var dataGrid:DataGrid = DataGrid(master); if (!dataGrid.editable) { var index:uint = dataGrid.selectedIndex; return (index >= 0) ? index + 1 : 0; } else { var coord:Object = dataGrid.editedItemPosition; if (!coord) { return 0; } var row:int = coord.rowIndex; var col:int = coord.columnIndex; return dataGrid.columns.length * row + col + 1; } } //-------------------------------------------------------------------------- // Overridden methods: AccImpl //-------------------------------------------------------------------------- /** * @private * method for returning the name of the DataGrid/ListItem * 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 * * @langversion 3.0 * @playerversion Flash 9.0.28.0 */ override protected function getName(childID:uint):String { // 0 -> DataGrid if (childID == 0) { return ""; } var name:String; var dataGrid:DataGrid = DataGrid(master); // 1 to columnCount * Rows -> ItemRenderers if (childID > 0) { // assuming childID is always ItemID + 1 // because getChildIDArray may not always be invoked. var index:int = childID - 1; // index is the (0 based) index of the elements after the headers var row:int var item:Object; var columns:Array; var n:int; var i:int; if (!dataGrid.editable) { // index is the row id row = index; item = getItemAt(index); if (item is String) { name = "Row " + (row + 1) + " of " + dataGrid.dataProvider.length + " " + item; } else { name = "Row " + (row + 1) + " of " + dataGrid.dataProvider.length; columns = dataGrid.columns; n = columns.length; for (i = 0; i < n; i++) { name += " " + columns[i].headerText + " " + columns[i].itemToLabel(item); } } } else { row = Math.floor(index / dataGrid.columns.length); var col:int = index % dataGrid.columns.length; item = getItemAt(row); if (item is String) { name = "Row " + (row + 1) + " of " + dataGrid.dataProvider.length + " " + item; } else { columns = dataGrid.columns; var itemName:String = columns[col].itemToLabel(item); var headerText:String = columns[col].headerText; name = "Row " + (row + 1) + " of " + dataGrid.dataProvider.length; n = columns.length; for (i = 0; i < columns.length; i++) { name += " " + columns[i].headerText + " " + columns[i].itemToLabel(item); } name += ", Editing " + headerText + " " + itemName; } } } return name; } //-------------------------------------------------------------------------- // Overridden event handlers: AccImpl //-------------------------------------------------------------------------- /** * @private * Override the generic event handler. * All AccImpl must implement this to listen * for events from its master component. * * @langversion 3.0 * @playerversion Flash 9.0.28.0 */ override protected function eventHandler(event:Event):void { var dataGrid:DataGrid = DataGrid(master); switch (event.type) { case "change": if (Accessibility.active && !dataGrid.editable) { var index:int = dataGrid.selectedIndex; if (index >= 0) { var childID:uint = index + 1; Accessibility.sendEvent(dataGrid, childID, EVENT_OBJECT_FOCUS); Accessibility.sendEvent(dataGrid, childID, EVENT_OBJECT_SELECTION); } } break; case DataGridEvent.ITEM_FOCUS_IN: if (Accessibility.active && dataGrid.editable) { var item:int = int(DataGridEvent(event).rowIndex); var col:int = DataGridEvent(event).columnIndex; Accessibility.sendEvent(dataGrid, dataGrid.columns.length * item + col + 1, EVENT_OBJECT_FOCUS); Accessibility.sendEvent(dataGrid, dataGrid.columns.length * item + col + 1, EVENT_OBJECT_SELECTION); } break; } } //-------------------------------------------------------------------------- // Methods //-------------------------------------------------------------------------- /** * @private * * @langversion 3.0 * @playerversion Flash 9.0.28.0 */ private function getItemAt(index:int):Object { var dataGrid:DataGrid = DataGrid(master); return dataGrid.getItemAt(index); } } }