1//////////////////////////////////////////////////////////////////////////////// 2// 3// ADOBE SYSTEMS INCORPORATED 4// Copyright 2010 Adobe Systems Incorporated 5// All Rights Reserved. 6// 7// NOTICE: Adobe permits you to use, modify, and distribute this file 8// in accordance with the terms of the license agreement accompanying it. 9// 10//////////////////////////////////////////////////////////////////////////////// 11 12package spark.accessibility 13{ 14 15import flash.accessibility.Accessibility; 16import flash.events.Event; 17import flash.events.FocusEvent; 18import mx.accessibility.AccConst; 19import mx.collections.IList; 20import mx.core.UIComponent; 21import mx.core.mx_internal; 22import mx.resources.ResourceManager; 23import mx.resources.IResourceManager; 24import spark.components.DataGrid; 25import spark.components.Grid; 26import spark.components.gridClasses.GridSelectionMode; 27import spark.components.gridClasses.CellPosition; 28import spark.events.GridEvent; 29import spark.events.GridCaretEvent; 30import spark.events.GridSelectionEvent; 31import spark.events.GridSelectionEventKind; 32import spark.events.GridItemEditorEvent; 33import spark.skins.spark.DefaultGridItemRenderer; 34 35use namespace mx_internal; 36 37/** 38 * This is the accessibility implementation class for 39 * spark.components.DataGrid. 40 * 41 * <p>When a Spark DataGrid is created, its <code>focusOwner</code> child 42 * object's <code>accessibilityImplementation</code> property is set to an 43 * instance of this class. The accessibility implementation is placed on 44 * this placeholder <code>focusOwner</code> object so that the DataGrid's 45 * accessibility implementation does not obscure the item editor's 46 * accessibility implementation. The DataGrid component itself does not 47 * have an accessibility implementation. This step is required as the 48 * current version of the Flash Player does not support multiple levels of 49 * MSAA objects. Item editors can be any component and need to be full MSAA 50 * objects. The item editor objects appear as sibling objects to the 51 * DataGrid in the MSAA tree structure. The accessibility implementation 52 * for each item editor is thus handled by the accessibility implementation 53 * associated with that component, such as <code>CheckBoxAccImpl</code> for 54 * a CheckBox. The item editor's accessibility implementation only exists 55 * when there is an item editor session, and there can only be one item 56 * editor active at one time; thus, there is one or zero instances of the 57 * item editor accessibility implementation active at any time.</p> 58 * 59 * <p>Two methods are overwritten in the <code>DataGrid</code> class to 60 * properly handle focus among <code>DataGrid</code> and the 61 * <code>focusOwner</code> child of <code>DataGrid</code>. The 62 * <code>GridItemRenderer</code> class turns accessibility off for item 63 * renderers, as by default these will be handled as simple objects under 64 * the DataGrid accessibility implementation. While this limits how 65 * non-item editor components such as CheckBox, Panel, etc. can be used in 66 * DataGrids, it prevents these items from showing up as siblings to the 67 * DataGrid in the MSAA tree. Allowing all grid item renderers to show up 68 * as siblings to the DataGrid in the MSAA tree would be very confusing to 69 * users of screen readers as there would be no context or relationship. 70 * Developers can of course override this default behavior if desired to 71 * display these renderers with accessibility enabled.</p> 72 * 73 * <p>The Flash Player then uses this class to allow MSAA clients such as 74 * screen readers to see and manipulate the DataGrid. See the 75 * mx.accessibility.AccImpl and 76 * flash.accessibility.AccessibilityImplementation classes for background 77 * information about accessibility implementation classes and MSAA.</p> 78 * 79 * <p>The <code>DataGridAccImpl</code> extends the 80 * <code>ListBaseAccImpl</code> (as the <code>DataGrid</code> extends the 81 * <code>DataGridBase</code> which extends the <code>ListBase</code> class). 82 * The Spark <code>DataGridAccImpl</code> is most similar to the MX 83 * <code>AdvancedDataGridAccImpl</code> as the AdvancedDataGrid also 84 * supports single cell and row selection which the MX DataGrid did not.</p> 85 * 86 * <p><b>Children</b></p> 87 * 88 * <p>The MSAA children of a DataGrid are, in this order</p> 89 * <ul> 90 * <li>One child for each visible header cell, starting from the left. 91 * "Visible" here means not hidden by the developer 92 * (<code>column.visible=false</code>). The header for a column that is not 93 * marked invisible by the developer but which is scrolled off screen is 94 * considered "visible" here.</li> 95 * <li>In row selection mode, one child for each data row in the grid; OR</li> 96 * <li>In cell selection mode, one child for each cell in the grid, 97 * excluding cells in invisible (as just described) columns.</li> 98 * </ul> 99 * 100 * <p>The number of children depends on the number of rows and columns in 101 * the <code>dataProvider</code>, not on the number of items currently 102 * displayed on screen.</p> 103 * 104 * <p>Note that, unlike for <code>ListBase</code>, DataGrid child count does 105 * not reflect the number of data rows in the control. Assistive technology 106 * should therefore avoid using <code>AccChildCount</code> as a means of 107 * reporting row count.</p> 108 * 109 * <p>This property is not handled by the DataGrid accessibility 110 * implementation for item editors as item editors manage themselves.</p> 111 * 112 * <p><b>Role</b></p> 113 * 114 * <p>The MSAA Role of a DataGrid is <code>ROLE_SYSTEM_LIST</code>.</p> 115 * 116 * <p>The Role of each data row or cell in the DataGrid is 117 * <code>ROLE_SYSTEM_LISTITEM</code>.</p> 118 * 119 * <p>The Role of each header cell in the DataGrid is 120 * <code>ROLE_SYSTEM_COLUMNHEADER</code>.</p> 121 * 122 * <p>This property is not handled by the DataGrid accessibility 123 * implementation for item editors as item editors manage themselves.</p> 124 * 125 * <p><b>Name</b></p> 126 * 127 * <p>The MSAA Name of a DataGrid is, by default, an empty string. When 128 * wrapped in a <code>FormItem</code> element, the Name is the FormItem's 129 * label. To override this behavior, set the DataGrids's 130 * <code>accessibilityName</code> property. Setting the 131 * <code>accessibilityName</code> property will also apply the accessible 132 * name to the <code>focusOwner</code> child object of the DataGrid which 133 * represents the DataGrid.</p> 134 * 135 * <p>The Name of each data row (when in row selection mode) is a string of 136 * the form "_column1Name_: _column1Value_, _column2Name_: _column2Value_, 137 * ..., _columnNName_: _columnNValue_, Row _m_ of _n_." Columns are 138 * separated from each other by commas, and column names and values are 139 * separated from each other by colons. Columns hidden by the developer are 140 * omitted entirely from the Name string. Example Name string: "Contact 141 * Name: Doug, Contact Phone: 555-1212, Contact Zip: 12345, row 3 of 7."</p> 142 * <p>Note that "Row _m_ of _n_" is localized.</p> 143 * 144 * <p>The Name of each data cell in column 1 (when in cell selection mode) 145 * is a string of the form "_columnName_: _columnValue_, Row _m_ of _n_." 146 * Example: "Contact Phone: 555-1212, Row 2 of 5." Subsequent columns use 147 * the same format but omit the "Row _m_ of _n_" portion.</p> 148 * <p>Note that "Row _m_ of _n_" is localized.</p> 149 * 150 * <p>The Name string for a column header (in cell or row selection mode) is 151 * normally the text of the header. Example: "Contact Phone." If the grid 152 * is sorted by the corresponding column however, the string "sorted" or 153 * "sorted descending" is appended to the column name, to indicate the sort 154 * and its direction. Example: "Contact Name sorted." For a multicolumn 155 * sort, level strings are also appended indicating each column's level in 156 * the set of sorting columns. For example, if a grid is sorted first by 157 * column 3 and then by column 2, and column 2 is sorted in descending 158 * order, column 3's name will end with "Sorted Level 1," and column 2's 159 * name will end with "Sorted descending level 2." The strings for 160 * indicating ascending sort, descending sort, and sort level are 161 * localized.</p> 162 * 163 * <p>When the Name of the DataGrid or one of its items changes, a DataGrid 164 * dispatches the MSAA event <code>EVENT_OBJECT_NAMECHANGE</code> with the 165 * proper childID for a row or cell or 0 for itself.</p> 166 * 167 * <p>If an accessibility name is not set for an item editor, one is set 168 * based on the column header name for the cell.</p> 169 * 170 * <p><b>Description</b></p> 171 * 172 * <p>The MSAA Description of a DataGrid is, by default, an empty string, 173 * but you can set the DataGrid's <code>accessibilityDescription</code> 174 * property.</p> 175 * 176 * <p>The Description of each row, cell, or header is the empty string and 177 * can not be set by an AccImpl.</p> 178 * 179 * <p>This property is not handled by the DataGrid accessibility 180 * implementation for item editors as item editors manage themselves.</p> 181 * 182 * <p><b>State</b></p> 183 * 184 * <p>The MSAA State of a DataGrid is a combination of:</p> 185 * <ul> 186 * <li><code>STATE_SYSTEM_UNAVAILABLE</code> (when <code>enabled</code> is 187 * <code>false</code>)</li> 188 * <li><code>STATE_SYSTEM_FOCUSABLE</code> (when <code>enabled</code> is 189 * <code>true</code>)</li> 190 * <li><code>STATE_SYSTEM_FOCUSED</code> (when <code>enabled</code> is 191 * <code>true</code> and the DataGrid has focus)</li> 192 * <li><code>STATE_SYSTEM_MULTISELECTABLE</code> (when 193 * <code>allowMultipleSelection</code> is true)</li> 194 * </ul> 195 * 196 * <p>The State of a data row or cell is a combination of:</p> 197 * <ul> 198 * <li><code>STATE_SYSTEM_FOCUSABLE</code></li> 199 * <li><code>STATE_SYSTEM_FOCUSED</code> (when focused)</li> 200 * <li><code>STATE_SYSTEM_OFFSCREEN</code> (when the row or cell has 201 * scrolled offscreen)</li> 202 * <li><code>STATE_SYSTEM_SELECTABLE</code></li> 203 * <li><code>STATE_SYSTEM_SELECTED</code> (when it is selected)</li> 204 * </ul> 205 * 206 * <p>The State of a header cell is <code>STATE_SYSTEM_NORMAL</code>, since 207 * header cells may not receive focus or be selected. As currently 208 * implemented, header cells may not report 209 * <code>STATE_SYSTEM_OFFSCREEN</code> even if the grid itself is moved such 210 * that its headers are offscreen.</p> 211 * 212 * <p>When the State of the DataGrid or one of its items changes, a DataGrid 213 * dispatches the MSAA event <code>EVENT_OBJECT_STATECHANGE</code> with the 214 * proper childID for the row or cell or 0 for itself.</p> 215 * 216 * <p>This property is not handled by the DataGrid accessibility 217 * implementation for item editors as item editors manage themselves.</p> 218 * 219 * <p><b>Value</b></p> 220 * 221 * <p>DataGrids and their children (rows, cells, and headers) do not have 222 * MSAA Values.</p> 223 * 224 * <p><b>Location</b></p> 225 * 226 * <p>The MSAA Location of a DataGrid or a row, data cell, or header cell 227 * within it is its bounding rectangle. The Location of an item that is 228 * currently not displayed on screen is undefined.</p> 229 * 230 * <p>This property is not handled by the DataGrid accessibility 231 * implementation for item editors as item editors manage themselves.</p> 232 * 233 * <p><b>Default Action</b></p> 234 * 235 * <p>A DataGrid does not have an MSAA DefaultAction. The MSAA DefaultAction 236 * for a row or cell is "Double Click" and for a header cell is "Click," and 237 * the corresponding localized string will be returned when the default 238 * action string is requested.</p> 239 * 240 * <p>Performing the default action on a data row or cell will cause it to 241 * be focused and selected and may cause other behavior depending on 242 * cell/row type. Performing the default action on a header will cause the 243 * grid to be sorted by that column. Repeated default actions on the header 244 * will toggle the sort order between ascending and descending. At this 245 * writing, there is no way via the AccImpl to arrange for a multilevel sort 246 * on several columns at once.</p> 247 * 248 * <p>This property is not handled by the DataGrid accessibility 249 * implementation for item editors as item editors manage themselves.</p> 250 * 251 * <p><b>Focus</b></p> 252 * 253 * <p>When there is no specific item (row or cell depending on selection 254 * mode) in focus within the grid, Focus returns 0 indicating that the grid 255 * itself has focus. This should only happen when the grid contains no 256 * data.</p> 257 * 258 * <p>When a row (row selection mode) or cell (cell selection mode) has 259 * focus, Focus returns the childID of the focused item.</p> 260 * 261 * <p>When a DataGrid receives focus, it dispatches the MSAA event 262 * <code>EVENT_OBJECT_FOCUS</code>. This event is also dispatched when 263 * focus moves among rows or cells within the grid.</p> 264 * 265 * <p>A focus change event is fired on the item editor when it 266 * starts/appears. A focus change event is fired on the DataGrid when the 267 * item editor is saved or closed.</p> 268 * 269 * <p><b>Selection</b></p> 270 * 271 * <p>A DataGrid allows either a single row or cell or multiple rows or 272 * cells to be selected, depending on the 273 * <code>allowMultipleSelection</code> property. Selection returns an array 274 * of the integer childIDs of the selected items.</p> 275 * 276 * <p>When an item is selected exclusively, it dispatches MSAA event 277 * <code>EVENT_OBJECT_SELECTION</code>. When a cell (cell selection mode) 278 * or row (row selection mode) is added to the current set of selections, 279 * the dispatched event is <code>EVENT_OBJECT_SELECTIONADD</code>. 280 * Similarly, if an item (cell or row) is removed from selection, the 281 * dispatched event is <code>EVENT_OBJECT_SELECTIONREMOVE</code>. If all 282 * selections are cleared (regardless of how many items were selected) or a 283 * select-all or select-region action is performed, the dispatched event is 284 * <code>EVENT_OBJECT_SELECTIONWITHIN</code>. Any selection operation not 285 * matching one of those listed above will dispatch 286 * <code>EVENT_OBJECT_SELECTION</code>.</p> 287 * 288 * <p>This property is not handled by the DataGrid accessibility 289 * implementation for item editors as item editors manage themselves.</p> 290 * 291 * <p><b>Select</b></p> 292 * 293 * <p>The <code>accSelect</code> method implements requests made via MSAA 294 * for changes in selection and/or focus within the DataGrid. The AccImpl 295 * for the DataGrid supports the setting of focus to a DataGrid itself or to 296 * a data item or set of items (row or cell depending on selection mode) 297 * within it. Supported actions include setting focus, exclusively 298 * selecting one item, and adding and removing an item or set of items from 299 * selection, all as defined in the Microsoft Active Accessibility 300 * specification. At this writing, attempting to use <code>accSelect</code> 301 * to extend an already-selected multi-cell region in cell multiselection 302 * mode to include more rows and columns at once may yield different results 303 * than doing the same action with a mouse.</p> 304 * 305 * <p>This property is not handled by the DataGrid accessibility 306 * implementation for item editors as item editors manage themselves.</p> 307 * 308 * @langversion 3.0 309 * @playerversion Flash 10 310 * @playerversion AIR 1.5 311 * @productversion Flex 4 312 */ 313public class DataGridAccImpl extends ListBaseAccImpl 314{ 315 include "../core/Version.as"; 316 317 // See the DataGridAccImpl constructor for why this is not initialized here. 318 private static var dgAccInfo:ItemAccInfo; // = new ItemAccInfo(); 319 320 //-------------------------------------------------------------------------- 321 // 322 // Class methods 323 // 324 //-------------------------------------------------------------------------- 325 326 /** 327 * Enables accessibility in the DataGrid class. 328 * 329 * <p>This method is called by application startup code 330 * that is autogenerated by the MXML compiler. 331 * Afterwards, when instances of DataGrid are initialized, 332 * their <code>accessibilityImplementation</code> property 333 * will be set to an instance of this class.</p> 334 * 335 * @langversion 3.0 336 * @playerversion Flash 10 337 * @playerversion AIR 1.5 338 * @productversion Flex 4 339 */ 340 public static function enableAccessibility():void 341 { 342 DataGrid.createAccessibilityImplementation = 343 createAccessibilityImplementation; 344 } 345 346 /** 347 * @private 348 * Creates a DataGrid's AccessibilityImplementation object. 349 * This method is called from UIComponent's 350 * initializeAccessibility() method. 351 */ 352 mx_internal static function createAccessibilityImplementation( 353 component:UIComponent):void 354 { 355 // attach AccImpl to placeholder focusOwner component so that item editors 356 // are exposed as sibling of the dataGrid allow for correct exposure in MSAA 357 // and the ability for iSimpleTextSelection interface to work as it requires 358 // that the stage focused component be the same MSAA component 359 var accImpl:DataGridAccImpl = new DataGridAccImpl(component); 360 DataGrid(component).focusOwner.accessibilityImplementation = accImpl; 361 } 362 363 //-------------------------------------------------------------------------- 364 // 365 // Constructor 366 // 367 //-------------------------------------------------------------------------- 368 369 /** 370 * Constructor. 371 * 372 * @param master The UIComponent instance that this AccImpl instance 373 * is making accessible. 374 * 375 * @langversion 3.0 376 * @playerversion Flash 10 377 * @playerversion AIR 1.5 378 * @productversion Flex 4 379 */ 380 public function DataGridAccImpl(master:UIComponent) 381 { 382 super(master); 383 // Normally this would not be done here, but at this writing, 384 // initializing dgAccInfo from its declaration line causes an RTE, 385 // apparently because the AS compiler does not yet detect a forward-ref 386 // problem that results in an incorrect initialization at run time. 387 // [DGL, 2010-09-07] 388 if (!dgAccInfo) 389 dgAccInfo = new ItemAccInfo(); 390 } 391 392 //-------------------------------------------------------------------------- 393 // 394 // Overridden properties: AccImpl 395 // 396 //-------------------------------------------------------------------------- 397 398 //---------------------------------- 399 // eventsToHandle 400 //---------------------------------- 401 402 /** 403 * @private 404 * Array of events that we should listen for from the master component. 405 */ 406 override protected function get eventsToHandle():Array 407 { 408 return super.eventsToHandle.concat([GridSelectionEvent.SELECTION_CHANGE, FocusEvent.FOCUS_IN, GridItemEditorEvent.GRID_ITEM_EDITOR_SESSION_START, 409 GridItemEditorEvent.GRID_ITEM_EDITOR_SESSION_SAVE, GridItemEditorEvent.GRID_ITEM_EDITOR_SESSION_CANCEL]); 410 } 411 412 //-------------------------------------------------------------------------- 413 // 414 // Overridden methods: AccessibilityImplementation 415 // 416 //-------------------------------------------------------------------------- 417 418 /** 419 * @private 420 * Gets the role for the component. 421 * 422 * @param childID Children of the component 423 */ 424 override public function get_accRole(childID:uint):uint 425 { 426 if (childID == 0) 427 // Get this out of the way quick; it's often requested by AT. 428 return role; 429 dgAccInfo.setup(master, childID); 430 if (dgAccInfo.isInvalid || !dgAccInfo.dataGrid.columns) 431 return null; 432 433 if (dgAccInfo.isColumnHeader) 434 return AccConst.ROLE_SYSTEM_COLUMNHEADER; 435 else 436 // Same role for all childIDs regardless of mode (row or cell). 437 // Valid and invalid childIDs return this; 438 // this behavior is common to most list-based controls we've seen. 439 // [DGL, 2010-08-10] 440 return AccConst.ROLE_SYSTEM_LISTITEM; 441 } 442 443 /** 444 * @private 445 * IAccessible method for returning the state of the GridItem. 446 * States are predefined for all the components in MSAA. 447 * Values are assigned to each state. 448 * Depending upon the GridItem being Selected, Selectable, Invisible, 449 * Offscreen, a value is returned. 450 * 451 * @param childID uint 452 * 453 * @return State uint 454 */ 455 override public function get_accState(childID:uint):uint 456 { 457 var accState:uint = getState(childID); 458 if (childID == 0 459 && DataGrid(master).focusOwner == UIComponent(master).getFocus()) 460 accState |= AccConst.STATE_SYSTEM_FOCUSED; 461 if (int(childID) <= 0) 462 return accState; 463 464 dgAccInfo.setup(master, childID); 465 if (!dgAccInfo.dataGrid.columns) 466 return accState; 467 if (dgAccInfo.isInvalid) 468 // Child ID out of bounds most likely. 469 return accState; 470 if (dgAccInfo.isColumnHeader) 471 { 472 // isColumnHeader implies columnHeaderGroup.visible is true. 473 if (dgAccInfo.dataGrid.columnHeaderGroup 474 && !dgAccInfo.dataGrid.columnHeaderGroup.getHeaderRendererAt(dgAccInfo.columnIndex).visible) 475 accState |= AccConst.STATE_SYSTEM_OFFSCREEN; 476 // There are some states we don't allow for these. 477 return accState & ~( 478 AccConst.STATE_SYSTEM_FOCUSABLE 479 | AccConst.STATE_SYSTEM_SELECTABLE 480 | AccConst.STATE_SYSTEM_FOCUSED 481 | AccConst.STATE_SYSTEM_SELECTED 482 ); 483 } 484 485 // We now have only rows and data cells to consider. 486 487 // Determine if this item is focused. 488 if (childID == get_accFocus()) 489 accState |= AccConst.STATE_SYSTEM_FOCUSED; 490 491 // Must recalculate dgAccInfo here because get_accFocus() changed it. 492 dgAccInfo.setup(master, childID); 493 494 // Anything (row or cell) is selectable unless selection is not allowed at all. 495 var mode:String = dgAccInfo.dataGrid.selectionMode; 496 if (mode != GridSelectionMode.NONE) 497 { 498 accState |= AccConst.STATE_SYSTEM_SELECTABLE; 499 if (dgAccInfo.isMultiSelect) 500 { 501 accState |= AccConst.STATE_SYSTEM_MULTISELECTABLE; 502 } 503 } 504 505 // Figure out selectedness. 506 var isSelected:Boolean; 507 if (dgAccInfo.isCellMode) 508 isSelected = dgAccInfo.dataGrid.selectionContainsCell( 509 dgAccInfo.rowIndex, 510 dgAccInfo.columnIndex 511 ); 512 else 513 isSelected = dgAccInfo.dataGrid.selectionContainsIndex( 514 dgAccInfo.rowIndex 515 ); 516 if (isSelected) 517 accState |= AccConst.STATE_SYSTEM_SELECTED; 518 519 // Figure out visibility and offscreenness. 520 var rowIndex:int = dgAccInfo.rowIndex; 521 var columnIndex:int = dgAccInfo.columnIndex; 522 if (!dgAccInfo.isCellMode) 523 columnIndex = -1; 524 if (!dgAccInfo.dataGrid.grid.isCellVisible(rowIndex, columnIndex)) 525 accState |= AccConst.STATE_SYSTEM_OFFSCREEN 526 527 return accState; 528 } 529 530 /** 531 * @private 532 * IAccessible method for returning the Default Action. 533 * 534 * @param childID uint 535 * 536 * @return DefaultAction String 537 */ 538 override public function get_accDefaultAction(childID:uint):String 539 { 540 if (get_accRole(childID) == AccConst.ROLE_SYSTEM_COLUMNHEADER) 541 return "Click";; 542 return super.get_accDefaultAction(childID); 543 } 544 545 /** 546 * @private 547 * IAccessible method for executing the Default Action. 548 * 549 * @param childID uint 550 */ 551 override public function accDoDefaultAction(childID:uint):void 552 { 553 dgAccInfo.setup(master, childID); 554 if (!dgAccInfo.dataGrid.columns || dgAccInfo.isInvalid) 555 return; 556 557 if (dgAccInfo.isColumnHeader) 558 { 559 if (dgAccInfo.dataGrid.sortableColumns 560 && dgAccInfo.dataGrid.columns.getItemAt(dgAccInfo.columnIndex).sortable) 561 { 562 // TODO: This only allows sorting by one column at a time. 563 var columnIndices:Vector.<int> = Vector.<int>([dgAccInfo.columnIndex]); 564 dgAccInfo.dataGrid.sortByColumns(columnIndices); 565 dgAccInfo.dataGrid.columnHeaderGroup.visibleSortIndicatorIndices = columnIndices; 566 } 567 return; 568 } 569 570 // TODO: Allow doDefaultAction to go into edit mode if editable 571 accSelect(AccConst.SELFLAG_TAKESELECTION | AccConst.SELFLAG_TAKEFOCUS, childID); 572 } 573 574 /** 575 * @private 576 * Method to return an array of childIDs. 577 * 578 * @return Array 579 */ 580 override public function getChildIDArray():Array 581 { 582 dgAccInfo.setup(master, 0); 583 if (!dgAccInfo.dataGrid.columns) 584 return null; 585 return createChildIDArray(dgAccInfo.maxChildID); 586 } 587 588 /** 589 * @private 590 * IAccessible method for returning the bounding box of the GridItem. 591 * 592 * @param childID uint 593 * 594 * @return Location Object 595 */ 596 override public function accLocation(childID:uint):* 597 { 598 dgAccInfo.setup(master, childID); 599 if (!dgAccInfo.dataGrid.columns) 600 return null; 601 if (dgAccInfo.isInvalid) 602 return null; 603 return dgAccInfo.boundingRect(); 604 } 605 606 /** 607 * @private 608 * IAccessible method for returning the childFocus of the DataGrid. 609 * 610 * @param childID uint 611 * 612 * @return focused childID. 613 */ 614 override public function get_accFocus():uint 615 { 616 dgAccInfo.setup(master, 0); 617 if (!dgAccInfo.dataGrid.columns || !dgAccInfo.dataGrid.dataProvider) 618 return null; 619 620 return dgAccInfo.childIDFromRowAndColumn( 621 dgAccInfo.dataGrid.grid.caretRowIndex, 622 dgAccInfo.dataGrid.grid.caretColumnIndex 623 ); 624 } 625 626 //-------------------------------------------------------------------------- 627 // 628 // Overridden methods: AccImpl 629 // 630 //-------------------------------------------------------------------------- 631 632 /** 633 * @private 634 * method for returning the name of the ListItem/DataGrid 635 * which is spoken out by the screen reader 636 * The ListItem should return the label as the name with m of n string and 637 * DataGrid should return the name specified in the AccessibilityProperties. 638 * 639 * @param childID uint 640 * 641 * @return Name String 642 */ 643 override protected function getName(childID:uint):String 644 { 645 if (int(childID) <= 0) 646 return null; 647 dgAccInfo.setup(master, childID); 648 if (dgAccInfo.isInvalid || !dgAccInfo.dataGrid.columns) 649 return null; 650 var resourceManager:IResourceManager; 651 if (dgAccInfo.isColumnHeader) 652 { 653 var column:Object = dgAccInfo.dataGrid.columns.getItemAt(dgAccInfo.columnIndex); 654 var headerName:String = column.headerText; 655 var sortColumnIndices:Vector.<int> = dgAccInfo.dataGrid.columnHeaderGroup.visibleSortIndicatorIndices; 656 var sortIndex:int = sortColumnIndices.indexOf(dgAccInfo.columnIndex); 657 if (sortIndex < 0) 658 return headerName; 659 resourceManager = ResourceManager.getInstance(); 660 var sortString:String; 661 if (column.sortDescending) 662 sortString = resourceManager.getString("components", "sortedDescending"); 663 else 664 sortString = resourceManager.getString("components", "sortedAscending"); 665 if (sortColumnIndices.length > 1) 666 sortString += resourceManager.getString("components", "sortLevel" 667 ).replace("%1", String(sortIndex+1)); 668 headerName = headerName +" " +sortString; 669 return headerName; 670 } 671 if (!dgAccInfo.dataGrid.dataProvider) 672 return null; 673 674 // We now have only rows and data cells to consider. 675 676 // String representation of row position. 677 var rowString:String = makeRowString(dgAccInfo); 678 679 // Construct the name to return. 680 var name:String = ""; 681 var rowObject:Object = dgAccInfo.dataGrid.dataProvider.getItemAt(dgAccInfo.rowIndex); 682 var columns:IList = dgAccInfo.dataGrid.columns; 683 if (dgAccInfo.isCellMode) 684 { 685 if (dgAccInfo.headerCount > 0) 686 name = columns.getItemAt(dgAccInfo.columnIndex).headerText + ": " 687 name += cellName(rowObject, dgAccInfo.columnIndex); 688 } 689 else if (rowObject) // row mode 690 { 691 var idx:int = -1; 692 for (var c:int = 0; c < dgAccInfo.reachableColumnCount; c++) 693 { 694 if (c > 0) 695 name += ", "; 696 idx = dgAccInfo.dataGrid.grid.getNextVisibleColumnIndex(idx); 697 if (dgAccInfo.headerCount > 0) 698 name += columns.getItemAt(idx).headerText + ": "; 699 name += columns.getItemAt(idx).itemToLabel(rowObject); 700 } 701 } // cell or row mode 702 if (rowString) 703 name += ", " +rowString; 704 705 return name; 706 } 707 708 /** 709 * @private 710 * IAccessible method for focusing an item or altering selection. 711 * This is a full implementation based on the Microsoft Active Accessibility 712 * (MSAA) specification. 713 * 714 * @param selFlag:uint A combination of flags indicating what to do. 715 * Flags may be combined as indicated below: 716 * <dl> 717 * <dt><code>SELFLAG_TAKEFOCUS</code> 718 * <dd>Set focus to the childID given. 719 * May be combined with the below flags. 720 * <dt><code>SELFLAG_TAKESELECTION</code> 721 * <dd>Select the given child and unselect any other selected ones. 722 * Combining this with <code>SELFLAG_TAKEFOCUS</code> 723 * emulates a single mouse click. 724 * <dt><code>SELFLAG_ADDSELECTION</code> 725 * <dd>Add the given child to those that are selected. 726 * Combining this with <code>SELFLAG_TAKEFOCUS</code> 727 * emulates a mouse click on an unselected item with the <kbd>Ctrl</kbd> key down. 728 * <dt><code>SELFLAG_REMOVESELECTION</code> 729 * <dd>Remove the given child from those that are selected. 730 * Combining this with <code>SELFLAG_TAKEFOCUS</code> 731 * emulates a mouse click on a selected item with the <kbd>Ctrl</kbd> key down. 732 * <dt><code>SELFLAG_ADDSELECTION | SELFLAG_EXTENDSELECTION</code> 733 * <dd>Select all children from the current focus to the given child. 734 * <dt><code>SELFLAG_REMOVESELECTION | SELFLAG_EXTENDSELECTION</code> 735 * <dd>Unselect all children from the current focus to the given child. 736 * <dt><code>SELFLAG_EXTENDSELECTION</code> 737 * <dd>Duplicate the selected/unselected state of the currently focused child, 738 * for all children through the given child. 739 * Combining this with <code>SELFLAG_TAKEFOCUS</code> 740 * emulates a mouse click with the <kbd>Shift</kbd> key down. 741 * </dl> 742 * @param childID uint The ID of the child to use. 743 * For extending selection or unselection, this is the endpoint, 744 * and current focus is the anchor. 745 */ 746 override public function accSelect(selFlag:uint, childID:uint):void 747 { 748 dgAccInfo.setup(master, childID); 749 if (dgAccInfo.isColumnHeader || dgAccInfo.isInvalid) 750 return; 751 752 // TODO: Adjust for there being no apparent way to just set focus 753 // without altering selection: For now, treate SELFLAG_TAKEFOCUS like 754 // SELFLAG_TAKESELECTION, and remove the TAKEFOCUS bit from 755 // all other requests, thus letting other selection calls also 756 // handle focus changes. 757 // Code for handling focus properly remains below in case a 758 // way is found to make the code at the end of this function 759 // actually set focus independent of selection. 760 if (selFlag == AccConst.SELFLAG_TAKEFOCUS) 761 selFlag = AccConst.SELFLAG_TAKESELECTION 762 else if (selFlag & AccConst.SELFLAG_TAKEFOCUS) 763 selFlag -= AccConst.SELFLAG_TAKEFOCUS 764 765 var settingFocus:Boolean = Boolean(selFlag & AccConst.SELFLAG_TAKEFOCUS); 766 if (settingFocus) 767 selFlag -= AccConst.SELFLAG_TAKEFOCUS; 768 769 var rowIndex:int = dgAccInfo.rowIndex; 770 var columnIndex:int = dgAccInfo.columnIndex; 771 if (!dgAccInfo.isCellMode) 772 columnIndex = -1; 773 var grid:DataGrid = dgAccInfo.dataGrid; 774 grid.ensureCellIsVisible(rowIndex, columnIndex); 775 776 // First selection, then focus if requested. 777 // Caveat: Invalid selection flags will cause no changes to be made, 778 // including a focus change if one was requested. 779 780 if (selFlag == AccConst.SELFLAG_TAKESELECTION) 781 { 782 if (columnIndex == -1) 783 grid.setSelectedIndex(rowIndex); 784 else 785 grid.setSelectedCell(rowIndex, columnIndex); 786 } 787 else if (selFlag == AccConst.SELFLAG_ADDSELECTION) 788 { 789 if (columnIndex == -1) 790 grid.addSelectedIndex(rowIndex); 791 else 792 grid.addSelectedCell(rowIndex, columnIndex); 793 } 794 else if (selFlag == AccConst.SELFLAG_REMOVESELECTION) 795 { 796 if (columnIndex == -1) 797 grid.removeSelectedIndex(rowIndex); 798 else 799 grid.removeSelectedCell(rowIndex, columnIndex); 800 } 801 else if (Boolean(selFlag & AccConst.SELFLAG_EXTENDSELECTION)) 802 { 803 if (Boolean(selFlag & AccConst.SELFLAG_ADDSELECTION) 804 && Boolean(selFlag & AccConst.SELFLAG_REMOVESELECTION)) 805 return; 806 var focusedID:uint = get_accFocus(); 807 if (!focusedID) 808 { 809 // This could be assumed to be 1, but this is probably safer. 810 return; 811 } 812 // We have to recalculate dgAccInfo for the anchor, 813 // but we already have row/column indices for the requested item, 814 // so we can do that without needing to reset it again afterward. 815 dgAccInfo.setup(master, focusedID); 816 if (dgAccInfo.isColumnHeader || dgAccInfo.isInvalid) 817 return; 818 var anchorRowIndex:int = dgAccInfo.rowIndex; 819 var anchorColumnIndex:int = dgAccInfo.columnIndex; 820 var adding:Boolean; 821 if (Boolean(selFlag & AccConst.SELFLAG_ADDSELECTION)) 822 adding = true; 823 else if (Boolean(selFlag & AccConst.SELFLAG_REMOVESELECTION)) 824 adding = false; 825 else 826 { 827 // MSAA docs say use selection state of anchor here. 828 // This method of figuring that out is a bit more 829 // intensive than necessary (other states calculated also), 830 // but selection extension without ADD/REMOVE being specified 831 // should be a very infrequent occurrence, and this method 832 // centralizes the logic for figuring out what is selected. 833 adding = Boolean(get_accState(focusedID) & AccConst.STATE_SYSTEM_SELECTED); 834 } 835 if (columnIndex == -1) 836 grid.selectIndices( 837 Math.min(anchorRowIndex, rowIndex), 838 Math.abs(rowIndex - anchorRowIndex) + 1 839 ); 840 else 841 grid.selectCellRegion( 842 Math.min(anchorRowIndex, rowIndex), 843 Math.min(anchorColumnIndex, columnIndex), 844 Math.abs(rowIndex - anchorRowIndex) + 1, 845 Math.abs(columnIndex - anchorColumnIndex) + 1 846 ); 847 } 848 849 // Now handle the focus change request if there was one 850 // (and if invalid flags didn't cause a return above). 851 // TODO: This approach does not work properly, and to date, 852 // no approach that does has been found. 853 // Code at the top of this function effectively makes the 854 // below "if" never true. 855 if (settingFocus) 856 grid.grid.caretRowIndex = rowIndex; 857 grid.grid.caretColumnIndex = columnIndex; 858 } 859 860 /** 861 * @private 862 * IAccessible method for returning the child Selections in the List. 863 * 864 * @param childID uint 865 * 866 * @return focused childID. 867 */ 868 override public function get_accSelection():Array 869 { 870 var accSelection:Array = []; 871 dgAccInfo.setup(master, 0); 872 if (!dgAccInfo.dataGrid.columns) 873 return null; 874 var i:int 875 var n:int 876 var items:Object; 877 878 if (dgAccInfo.isCellMode) 879 { 880 items = dgAccInfo.dataGrid.selectedCells; 881 n = items.length; 882 for (i = 0; i < n; i++) 883 { 884 // The selected childID is effectively the 1-based cell index. 885 // This is row*colCount + columnInRow +columnHeaderCount +1. 886 accSelection[i] = items[i].rowIndex * dgAccInfo.reachableColumnCount 887 + items[i].columnIndex 888 + dgAccInfo.headerCount + 1; 889 } 890 } 891 else // row mode 892 { 893 items = dgAccInfo.dataGrid.selectedIndices; 894 n = items.length; 895 for (i = 0; i < n; i++) 896 { 897 // This time we just need rowIndex (0-based) + headerCount + 1. 898 accSelection[i] = items[i] + dgAccInfo.headerCount + 1; 899 } 900 901 } 902 903 return accSelection; 904 905 } 906 907 //-------------------------------------------------------------------------- 908 // 909 // Overridden event handlers: AccImpl 910 // 911 //-------------------------------------------------------------------------- 912 913 /** 914 * @private 915 * Override the generic event handler. 916 * All AccImpl must implement this to listen 917 * for events from its master component. 918 */ 919 override protected function eventHandler(event:Event):void 920 { 921 // Let AccImpl class handle the events 922 // that all accessible UIComponents understand. 923 $eventHandler(event); 924 925 dgAccInfo.setup(master, 0); 926 if (!dgAccInfo.dataGrid.columns) 927 return; 928 929 var childID:uint; 930 switch (event.type) 931 { 932 case GridCaretEvent.CARET_CHANGE: 933 { 934 childID = dgAccInfo.childIDFromRowAndColumn( 935 int(GridCaretEvent(event).newRowIndex), 936 int(GridCaretEvent(event).newColumnIndex) 937 ) 938 if (int(childID) > 0) 939 if (!dgAccInfo.dataGrid.itemEditorInstance) 940 Accessibility.sendEvent(dgAccInfo.dataGrid.focusOwner, childID, AccConst.EVENT_OBJECT_FOCUS); 941 else 942 Accessibility.sendEvent(UIComponent(dgAccInfo.dataGrid.itemEditorInstance), 0, AccConst.EVENT_OBJECT_FOCUS); 943 break; 944 } 945 case GridSelectionEvent.SELECTION_CHANGE: 946 { 947 var gridSelectionEvent:GridSelectionEvent = GridSelectionEvent(event); 948 childID = dgAccInfo.childIDFromRowAndColumn( 949 gridSelectionEvent.selectionChange.rowIndex, 950 gridSelectionEvent.selectionChange.columnIndex 951 ); 952 if (int(childID) <= 0) 953 return; 954 955 var eventID:int = AccConst.EVENT_OBJECT_SELECTION; 956 var kind:String = gridSelectionEvent.kind; 957 if (kind == GridSelectionEventKind.ADD_CELL 958 || kind == GridSelectionEventKind.ADD_ROW) 959 eventID = AccConst.EVENT_OBJECT_SELECTIONADD; 960 else if (kind == GridSelectionEventKind.REMOVE_CELL 961 || kind == GridSelectionEventKind.REMOVE_ROW) 962 eventID = AccConst.EVENT_OBJECT_SELECTIONREMOVE; 963 else if (kind == GridSelectionEventKind.CLEAR_SELECTION 964 || kind == GridSelectionEventKind.SELECT_ALL 965 || kind == GridSelectionEventKind.SET_CELL_REGION 966 || kind == GridSelectionEventKind.SET_ROWS) 967 eventID = AccConst.EVENT_OBJECT_SELECTIONWITHIN; 968 969 Accessibility.sendEvent(dgAccInfo.dataGrid.focusOwner, childID, eventID); 970 break; 971 } 972 case FocusEvent.FOCUS_IN: 973 { 974 // do not fire focus changes for list when a child editor is focused 975 // as this causes an extra event being fired 976 if (event.target == DataGrid(master).focusOwner) 977 Accessibility.sendEvent(DataGrid(master).focusOwner, 0, AccConst.EVENT_OBJECT_FOCUS); 978 break; 979 } 980 case GridItemEditorEvent.GRID_ITEM_EDITOR_SESSION_START: 981 { 982 dgAccInfo.setup(master, 0); 983 984 childID = dgAccInfo.childIDFromRowAndColumn( 985 GridItemEditorEvent(event).rowIndex, 986 GridItemEditorEvent(event).columnIndex 987 ); 988 var editor:Object = event.currentTarget.itemEditorInstance; 989 var defaultGridItemEditorClass:Class = Class(getDefinition("spark.components.gridClasses.DefaultGridItemEditor", master.moduleFactory)); 990 if (editor is defaultGridItemEditorClass) 991 { 992 // The specific part with focus. 993 try 994 { 995 editor = Object(editor).textArea; 996 } 997 catch(e:Error) 998 { 999 } 1000 } 1001 else 1002 { 1003 // Try to find the specific part with focus, 1004 // falling back to the itemEditorInstance if we don't know it. 1005 var realEditor:UIComponent = null; 1006 try 1007 { 1008 realEditor = UIComponent(editor.stage.focus); 1009 } 1010 catch(e:Error) 1011 { 1012 } 1013 if (Boolean(realEditor) && editor != realEditor) 1014 { 1015 editor = realEditor; 1016 } 1017 } 1018 // Name the editor with this cell's name. 1019 // This applies the same rules for row identification for both edit and non-edit cells. 1020 if (!editor.accessibilityName) 1021 { 1022 var edName:String = ""; 1023 if (dgAccInfo.headerCount > 0) 1024 { 1025 var columns:IList = dgAccInfo.dataGrid.columns; 1026 var columnIndex:int = GridItemEditorEvent(event).columnIndex; 1027 edName += columns.getItemAt(columnIndex).headerText; 1028 } 1029 // For the row string, we need to indicate which cell to use. 1030 dgAccInfo.setup(master, childID); 1031 var rowString:String = makeRowString(dgAccInfo); 1032 if (rowString) 1033 edName += " " +rowString; 1034 editor.accessibilityName = edName; 1035 Accessibility.updateProperties(); 1036 } 1037 Accessibility.sendEvent(UIComponent(editor), 0, AccConst.EVENT_OBJECT_FOCUS); 1038 break; 1039 } 1040 case GridItemEditorEvent.GRID_ITEM_EDITOR_SESSION_SAVE, GridItemEditorEvent.GRID_ITEM_EDITOR_SESSION_CANCEL: 1041 { 1042 dgAccInfo.setup(master, 0); 1043 1044 childID = dgAccInfo.childIDFromRowAndColumn( 1045 GridItemEditorEvent(event).rowIndex, GridItemEditorEvent(event).columnIndex); 1046 1047 Accessibility.sendEvent(DataGrid(master).focusOwner, childID, AccConst.EVENT_OBJECT_FOCUS); 1048 } 1049 } 1050 } 1051 1052 //-------------------------------------------------------------------------- 1053 // 1054 // Methods 1055 // 1056 //-------------------------------------------------------------------------- 1057 1058 /** 1059 * @private 1060 */ 1061 private function makeRowString(dgAccInfo:ItemAccInfo):String 1062 { 1063 var rowString:String = ""; 1064 if ((dgAccInfo.isCellMode && dgAccInfo.reachableColumnIndex == 0) || !dgAccInfo.isCellMode) 1065 { 1066 var resourceManager:IResourceManager = ResourceManager.getInstance(); 1067 rowString = resourceManager.getString("components", "rowMofN"); 1068 rowString = rowString.replace("%1", dgAccInfo.reachableRowIndex + 1).replace("%2", dgAccInfo.reachableRowCount); 1069 } 1070 return rowString; 1071 } 1072 1073 /** 1074 * @private 1075 */ 1076 private function cellName(rowObject:Object, columnIndex:int):String 1077 { 1078 var item:Object = rowObject; 1079 var dataGrid:DataGrid = DataGrid(master); 1080 var columns:IList = dataGrid.columns; 1081 if (!columns) 1082 return null; 1083 var column:Object = columns.getItemAt(columnIndex); 1084 if (!column) 1085 return null; 1086 return column.itemToLabel(rowObject); 1087 } 1088 1089} 1090 1091} 1092 1093/** 1094 * @private 1095 * ItemAccInfo is a support class used by DataGridAccImpl to determine various 1096 * things about a DataGrid. For performance reasons, this class is 1097 * instantiated once by DataGridAccImpl and repopulated as needed from 1098 * DataGridAccImpl code via calls to ItemAccInfo.setup(). 1099 * 1100 * <p>Terminology: A "reachable" cell, row, or column refers to an item that 1101 * the developer has allowed a user to view, whether or not it happens to be 1102 * visible on screen at the moment. 1103 * At this writing, the ability to hide rows from the user is not anticipated, 1104 * but the hiding of columns will be possible. 1105 * (When rows of data are hidden via DataProvider filtering, they simply don't 1106 * appear in the DataGrid at all.) 1107 */ 1108internal class ItemAccInfo 1109{ 1110 import mx.core.UIComponent; 1111 import spark.components.DataGrid; 1112 import spark.components.Grid; 1113 import spark.components.gridClasses.GridSelectionMode; 1114 import spark.components.gridClasses.CellPosition; 1115 import flash.geom.Rectangle; 1116 import flash.geom.Point; 1117 1118 /** 1119 * Constructor. 1120 */ 1121 public function ItemAccInfo() 1122 { 1123 super(); 1124 } 1125 1126 //-------------------------------------------------------------------------- 1127 // 1128 // Methods 1129 // 1130 //-------------------------------------------------------------------------- 1131 1132 /** 1133 * @private 1134 * Sets up for use with a particular DataGrid and item within it. 1135 * 1136 * @param master The UIComponent instance that the calling AccImpl instance 1137 * is making accessible. 1138 * @param childID The childID of the DataGrid item of interest. 1139 * This may refer to a header cell, a data cell, or a data row. 1140 */ 1141 public function setup(master:UIComponent, childID:uint):void 1142 { 1143 this.master = master; 1144 this.childID = childID; 1145 dataGrid = DataGrid(master); 1146 reachableRowIndices = null; 1147 reachableColumnIndices = null; 1148 if (dataGrid.columns) 1149 { 1150 columnCount = dataGrid.columns.length; 1151 // For efficiency in the common case, assume all is visible, 1152 // and only build a vector of reachable indices if this is wrong. 1153 var somethingIsInvisible:Boolean = false; 1154 var column:Object; 1155 var i:int; 1156 for (i = 0; i < columnCount; i++) 1157 { 1158 column = dataGrid.columns.getItemAt(i); 1159 if (!column.visible) 1160 { 1161 somethingIsInvisible = true; 1162 break; 1163 } 1164 } 1165 if (somethingIsInvisible) 1166 { 1167 reachableColumnIndices = new Vector.<int>(); 1168 for (i = 0; i < columnCount; i++) 1169 { 1170 column = dataGrid.columns.getItemAt(i); 1171 if (column.visible) 1172 reachableColumnIndices.push(column.columnIndex); 1173 } 1174 } 1175 reachableColumnCount = reachableColumnIndices == null ? 1176 columnCount : reachableColumnIndices.length; 1177 } 1178 else 1179 { 1180 columnCount = 0; 1181 reachableColumnCount = 0; 1182 } 1183 if (dataGrid.dataProvider) 1184 { 1185 rowCount = dataGrid.dataProvider.length; 1186 reachableRowCount = reachableRowIndices == null ? 1187 rowCount : reachableRowIndices.length; 1188 } 1189 else 1190 { 1191 rowCount = 0; 1192 reachableRowCount = 0; 1193 } 1194 headerCount = 0; 1195 reachableHeaderCount = 0; 1196 maxChildID = 0; 1197 isCellMode = false; 1198 isMultiSelect = false; 1199 isInvalid = false; 1200 isColumnHeader = false; 1201 rowIndex = 0; 1202 columnIndex = 0; 1203 reachableRowIndex = 0; 1204 reachableColumnIndex = 0; 1205 1206 var itemIndex:int = childID - 1; 1207 if (dataGrid.columnHeaderGroup && dataGrid.columnHeaderGroup.visible) 1208 { 1209 // There are visible column headers, so their childIDs come first. 1210 itemIndex -= reachableColumnCount; 1211 headerCount = columnCount; 1212 reachableHeaderCount = reachableColumnCount; 1213 } 1214 else 1215 { 1216 // No header bar or it's invisible, 1217 // so we should not try to expose any data within it. 1218 headerCount = 0; 1219 reachableHeaderCount = 0; 1220 } 1221 var mode:String = dataGrid.selectionMode; 1222 isCellMode = ( 1223 mode == GridSelectionMode.SINGLE_CELL 1224 || mode == GridSelectionMode.MULTIPLE_CELLS 1225 ); 1226 isMultiSelect = ( 1227 mode == GridSelectionMode.MULTIPLE_CELLS 1228 || mode == GridSelectionMode.MULTIPLE_ROWS 1229 ); 1230 maxChildID = 0; 1231 // Account for reachable headers. 1232 maxChildID += reachableHeaderCount; 1233 // Then for reachable cells or rows as appropriate. 1234 if (isCellMode) 1235 maxChildID += reachableRowCount * reachableColumnCount; 1236 else 1237 maxChildID += reachableRowCount; 1238 isColumnHeader = false; 1239 isInvalid = false; 1240 if (childIDOutOfBounds(childID)) 1241 { 1242 isInvalid = true; 1243 reachableColumnIndex = -1; 1244 reachableRowIndex = -1; 1245 itemIndex = -1; 1246 } 1247 else if (itemIndex < 0) 1248 { 1249 // This childID refers to a header, not a data row or cell. 1250 isColumnHeader = true; 1251 reachableColumnIndex = itemIndex + reachableColumnCount; 1252 reachableRowIndex = -1; 1253 itemIndex = -1; 1254 } 1255 else if (isCellMode) 1256 { 1257 reachableRowIndex = Math.floor(itemIndex / reachableColumnCount); 1258 reachableColumnIndex = itemIndex % reachableColumnCount; 1259 } 1260 else 1261 { 1262 reachableRowIndex = itemIndex; 1263 // Using 0 here so, for example, getItemRendererAt() calls still work for a row. 1264 reachableColumnIndex = 0; 1265 } 1266 rowIndex = reachableRowIndex; 1267 columnIndex = reachableColumnIndex; 1268 if (reachableRowIndex >= 0 && reachableRowIndices && reachableRowIndices.length) 1269 rowIndex = reachableRowIndices[reachableRowIndex]; 1270 if (reachableColumnIndex >= 0 && reachableColumnIndices && reachableColumnIndices.length) 1271 columnIndex = reachableColumnIndices[reachableColumnIndex]; 1272 } 1273 1274 /** 1275 * @private 1276 */ 1277 // The master component reference for which this AccImpl is instantiated. 1278 public var master:UIComponent; 1279 // The childID within that component for which this accInfo is calculated. 1280 public var childID:uint; 1281 // The DataGrid reference for this instance. 1282 public var dataGrid:DataGrid; 1283 // Number of columns, headers, and rows overall. 1284 public var columnCount:int; 1285 public var headerCount:int; 1286 public var rowCount:int; 1287 // Number of columns, headers, and rows that are reachable. 1288 // ("Reachable" means not marked invisible by the developer.) 1289 public var reachableColumnCount:int; 1290 public var reachableHeaderCount:int; 1291 public var reachableRowCount:int; 1292 // Indices of reachable rows and columns. 1293 // These are null when nothing is filtered, for performance reasons. 1294 protected var reachableRowIndices:Vector.<int>; 1295 protected var reachableColumnIndices:Vector.<int>; 1296 // The highest valid childID. 1297 public var maxChildID:int; 1298 // True if we are in cell navigation mode (single or multiple selection). 1299 public var isCellMode:Boolean; 1300 // True if we are in a multiple selection mode (row or cell). 1301 public var isMultiSelect:Boolean; 1302 // True if the data in this object is invalid for some reason. 1303 // Usually this means an invalid childID was passed to setup(). 1304 public var isInvalid:Boolean; 1305 // True if the given childID represents a column header cell. 1306 public var isColumnHeader:Boolean; 1307 // 0-based indices of row and column in the sets of reachable ones. 1308 public var reachableColumnIndex:int; 1309 public var reachableRowIndex:int; 1310 // 0-based indices of row and column in the set of all of each. 1311 public var columnIndex:int; 1312 public var rowIndex:int; 1313 1314 /** 1315 * @private 1316 * Determine the childID corresponding to the given DataGrid row and column. 1317 * The row and column indices taken here are from the set of reachable 1318 * rows and colums; they are not absolute row/column indices. 1319 * This method is used internally; see childIDFromRowAndColumn() for the 1320 * external interface. 1321 * 1322 * @param reachableRowIndex The 0-based index of the row among reachable rows. 1323 * @param reachableColumnIndex The 0-based index of the column among reachable columns. 1324 * Ignored if this grid is in a row navigation mode. 1325 * 1326 * @return The childID corresponding to the row and column indices passed. 1327 */ 1328 protected function childIDFromReachableRowAndColumn(reachableRowIndex:int, reachableColumnIndex:int):uint 1329 { 1330 var childID:int = reachableHeaderCount + 1; 1331 if (reachableRowIndex < 0) 1332 childID = 0; 1333 else if (isCellMode) 1334 if (reachableColumnIndex < 0) 1335 childID = 0; 1336 else 1337 childID += reachableRowIndex * reachableColumnCount + reachableColumnIndex; 1338 else 1339 childID += reachableRowIndex; 1340 return uint(childID); 1341 } 1342 1343 /** 1344 * @private 1345 * Determine the childID corresponding to the given DataGrid row and column. 1346 * The indices passed to this method are mapped onto the set of rows and 1347 * columns that are or can be exposed to the user. 1348 * 1349 * @param rowIndex The 0-based index of the row. 1350 * @param columnIndex The 0-based index of the column. 1351 * Ignored if this grid is in a row navigation mode. 1352 * 1353 * @return The childID corresponding to the row and column indices passed. 1354 */ 1355 public function childIDFromRowAndColumn(rowIndex:int, columnIndex:int):uint 1356 { 1357 return childIDFromReachableRowAndColumn( 1358 reachableRowIndices == null ? rowIndex : reachableRowIndices.indexOf(rowIndex), 1359 reachableColumnIndices == null ? columnIndex : reachableColumnIndices.indexOf(columnIndex) 1360 ); 1361 } 1362 1363 /** 1364 * @private 1365 * Internal method for checking if a childID is above or below those allowed. 1366 * 1367 * @param childID The childID to check. 1368 * 1369 * @return true if the childID is out of bounds and false if not. 1370 */ 1371 private function childIDOutOfBounds(childID: int):Boolean 1372 { 1373 if (int(childID) <= 0) 1374 return true; 1375 if (!dataGrid.dataProvider || !dataGrid.columns) 1376 return true 1377 if (childID > maxChildID) 1378 return true; 1379 return false; 1380 } 1381 1382 /** 1383 * @private 1384 * Return an object giving the bounds of this grid item (row or cell). 1385 * The object returned is either an IVisualElement (renderer), in which 1386 * case its coordinates are assumed to be stage-based, or a 1387 * flash.geom.Rectangle, in which case its coordinates are relative to 1388 * the top left corner of the DataGrid. These are the requirements of 1389 * the AccImpl::get_accLocation() method. 1390 * 1391 * @return The Rectangle indicating the item's bounds. 1392 */ 1393 public function boundingRect():Object 1394 { 1395 // First see if this item is on screen. 1396 // We could skip this, but we'd run the risk of having 1397 // assistive technology create huge numbers of itemRenderers below 1398 // by quickly scanning an entire grid. 1399 if (!isColumnHeader && (rowIndex < 0 || rowIndex >= rowCount)) 1400 // Also covers rowCount == 0 effectively. 1401 return null; 1402 if (isCellMode && (columnIndex < 0 || columnIndex >= columnCount)) 1403 return null; 1404 var vri:Vector.<int> = dataGrid.grid.getVisibleRowIndices(); 1405 var vci:Vector.<int> = dataGrid.grid.getVisibleColumnIndices(); 1406 if (isColumnHeader && vci.indexOf(columnIndex) < 0) 1407 return null; 1408 if ((!isColumnHeader && vri.indexOf(rowIndex) < 0) 1409 || (isCellMode && vci.indexOf(columnIndex) < 0)) 1410 return null; 1411 1412 var result:Object; 1413 if (isColumnHeader) 1414 { 1415 result = dataGrid.columnHeaderGroup.getHeaderRendererAt(columnIndex); 1416 } 1417 else if (isCellMode) 1418 { 1419 result = dataGrid.grid.getItemRendererAt(rowIndex, columnIndex); 1420 } 1421 else // row mode 1422 { 1423 // Use the item renderers at both ends of this row 1424 // to calculate the width. 1425 // We assume here that top and bottom of all renderers for one row are equal. 1426 var r1:Object = dataGrid.grid.getItemRendererAt(rowIndex, vci[0]); 1427 var r2:Object = dataGrid.grid.getItemRendererAt(rowIndex, vci[vci.length-1]); 1428 var xy:Point = new Point( 1429 r1.getLayoutBoundsX(), 1430 r1.getLayoutBoundsY() 1431 ); 1432 xy = dataGrid.grid.localToGlobal(xy); 1433 xy = dataGrid.globalToLocal(xy); 1434 result = new Rectangle( 1435 xy.x, xy.y, 1436 r2.getLayoutBoundsX() + r2.getLayoutBoundsWidth() - r1.getLayoutBoundsX(), 1437 r1.getLayoutBoundsHeight() 1438 ); 1439 } 1440 return result; 1441 } 1442 1443} 1444