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