1 /*
   2  * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.glass.ui.win;
  27 
  28 import java.util.function.Function;
  29 import javafx.collections.FXCollections;
  30 import javafx.collections.ObservableList;
  31 import javafx.geometry.Bounds;
  32 import javafx.geometry.Point2D;
  33 import javafx.scene.AccessibleAction;
  34 import javafx.scene.AccessibleAttribute;
  35 import javafx.scene.AccessibleRole;
  36 import javafx.scene.Node;
  37 import javafx.scene.Scene;
  38 import javafx.scene.input.KeyCombination;
  39 import com.sun.glass.ui.Accessible;
  40 import com.sun.glass.ui.View;
  41 import static javafx.scene.AccessibleAttribute.*;
  42 
  43 /*
  44  * This class is the Java peer for GlassAccessible.
  45  * GlassAccessible implements all UIA interfaces required including:
  46  * IRawElementProviderSimple
  47  * IRawElementProviderFragment
  48  * IRawElementProviderFragmentRoot
  49  * IInvokeProvider
  50  *
  51  *
  52  * Ideas (performance, low priority):
  53  * 1. Only raise events when needed:
  54  *   - Either implement or IRawElementProviderAdviseEvents use UiaClientsAreListening
  55  *
  56  * 2. When returning the same string to UIA we could save the BSTR instead
  57  *    of creating a new one every time.
  58  *
  59  */
  60 
  61 final class WinAccessible extends Accessible {
  62 
  63     private native static void _initIDs();
  64     static {
  65         _initIDs();
  66     }
  67 
  68     private static int idCount = 1;
  69 
  70     /* PROPERTYID  */
  71     private static final int UIA_BoundingRectanglePropertyId     = 30001;
  72     private static final int UIA_ProcessIdPropertyId             = 30002;
  73     private static final int UIA_ControlTypePropertyId           = 30003;
  74     private static final int UIA_LocalizedControlTypePropertyId  = 30004;
  75     private static final int UIA_NamePropertyId                  = 30005;
  76     private static final int UIA_AcceleratorKeyPropertyId        = 30006;
  77     private static final int UIA_AccessKeyPropertyId             = 30007;
  78     private static final int UIA_HasKeyboardFocusPropertyId      = 30008;
  79     private static final int UIA_IsKeyboardFocusablePropertyId   = 30009;
  80     private static final int UIA_IsEnabledPropertyId             = 30010;
  81     private static final int UIA_AutomationIdPropertyId          = 30011;
  82     private static final int UIA_ClassNamePropertyId             = 30012;
  83     private static final int UIA_HelpTextPropertyId              = 30013;
  84     private static final int UIA_ClickablePointPropertyId        = 30014;
  85     private static final int UIA_CulturePropertyId               = 30015;
  86     private static final int UIA_IsControlElementPropertyId      = 30016;
  87     private static final int UIA_IsContentElementPropertyId      = 30017;
  88     private static final int UIA_LabeledByPropertyId             = 30018;
  89     private static final int UIA_IsPasswordPropertyId            = 30019;
  90     private static final int UIA_NativeWindowHandlePropertyId    = 30020;
  91     private static final int UIA_ItemTypePropertyId              = 30021;
  92     private static final int UIA_IsOffscreenPropertyId           = 30022;
  93     private static final int UIA_OrientationPropertyId           = 30023;
  94     private static final int UIA_FrameworkIdPropertyId           = 30024;
  95     private static final int UIA_ValueValuePropertyId            = 30045;
  96     private static final int UIA_RangeValueValuePropertyId       = 30047;
  97     private static final int UIA_ExpandCollapseExpandCollapseStatePropertyId = 30070;
  98     private static final int UIA_ToggleToggleStatePropertyId     = 30086;
  99     private static final int UIA_AriaRolePropertyId              = 30101;
 100     private static final int UIA_ProviderDescriptionPropertyId   = 30107;
 101 
 102     /* Control Pattern Identifiers */
 103     private static final int UIA_InvokePatternId                 = 10000;
 104     private static final int UIA_SelectionPatternId              = 10001;
 105     private static final int UIA_ValuePatternId                  = 10002;
 106     private static final int UIA_RangeValuePatternId             = 10003;
 107     private static final int UIA_ScrollPatternId                 = 10004;
 108     private static final int UIA_ExpandCollapsePatternId         = 10005;
 109     private static final int UIA_GridPatternId                   = 10006;
 110     private static final int UIA_GridItemPatternId               = 10007;
 111     private static final int UIA_SelectionItemPatternId          = 10010;
 112     private static final int UIA_TablePatternId                  = 10012;
 113     private static final int UIA_TableItemPatternId              = 10013;
 114     private static final int UIA_TextPatternId                   = 10014;
 115     private static final int UIA_TogglePatternId                 = 10015;
 116     private static final int UIA_TransformPatternId              = 10016;
 117     private static final int UIA_ScrollItemPatternId             = 10017;
 118     private static final int UIA_ItemContainerPatternId          = 10019;
 119 
 120     /* UIA_ControlTypeIds */
 121     private static final int UIA_ButtonControlTypeId             = 50000;
 122     private static final int UIA_CheckBoxControlTypeId           = 50002;
 123     private static final int UIA_ComboBoxControlTypeId           = 50003;
 124     private static final int UIA_EditControlTypeId               = 50004;
 125     private static final int UIA_HyperlinkControlTypeId          = 50005;
 126     private static final int UIA_ImageControlTypeId              = 50006;
 127     private static final int UIA_ListItemControlTypeId           = 50007;
 128     private static final int UIA_ListControlTypeId               = 50008;
 129     private static final int UIA_MenuControlTypeId               = 50009;
 130     private static final int UIA_MenuBarControlTypeId            = 50010;
 131     private static final int UIA_MenuItemControlTypeId           = 50011;
 132     private static final int UIA_ProgressBarControlTypeId        = 50012;
 133     private static final int UIA_RadioButtonControlTypeId        = 50013;
 134     private static final int UIA_ScrollBarControlTypeId          = 50014;
 135     private static final int UIA_SliderControlTypeId             = 50015;
 136     private static final int UIA_SpinnerControlTypeId            = 50016;
 137     private static final int UIA_TabControlTypeId                = 50018;
 138     private static final int UIA_TabItemControlTypeId            = 50019;
 139     private static final int UIA_TextControlTypeId               = 50020;
 140     private static final int UIA_ToolBarControlTypeId            = 50021;
 141     private static final int UIA_TreeControlTypeId               = 50023;
 142     private static final int UIA_TreeItemControlTypeId           = 50024;
 143     private static final int UIA_GroupControlTypeId              = 50026;
 144     private static final int UIA_ThumbControlTypeId              = 50027;
 145     private static final int UIA_DataGridControlTypeId           = 50028;
 146     private static final int UIA_DataItemControlTypeId           = 50029;
 147     private static final int UIA_SplitButtonControlTypeId        = 50031;
 148     private static final int UIA_WindowControlTypeId             = 50032;
 149     private static final int UIA_PaneControlTypeId               = 50033;
 150     private static final int UIA_TableControlTypeId              = 50036;
 151 
 152     /* NavigateDirection */
 153     private static final int NavigateDirection_Parent            = 0;
 154     private static final int NavigateDirection_NextSibling       = 1;
 155     private static final int NavigateDirection_PreviousSibling   = 2;
 156     private static final int NavigateDirection_FirstChild        = 3;
 157     private static final int NavigateDirection_LastChild         = 4;
 158 
 159     /* RowOrColumnMajor */
 160     private static final int RowOrColumnMajor_RowMajor          = 0;
 161     private static final int RowOrColumnMajor_ColumnMajor       = 1;
 162     private static final int RowOrColumnMajor_Indeterminate     = 2;
 163 
 164     /* Event ID constants */
 165     private static final int UIA_MenuOpenedEventId               = 20003;
 166     private static final int UIA_AutomationPropertyChangedEventId= 20004;
 167     private static final int UIA_AutomationFocusChangedEventId   = 20005;
 168     private static final int UIA_MenuClosedEventId               = 20007;
 169     private static final int UIA_SelectionItem_ElementRemovedFromSelectionEventId = 20011;
 170     private static final int UIA_SelectionItem_ElementSelectedEventId = 20012;
 171     private static final int UIA_Text_TextSelectionChangedEventId = 20014;
 172     private static final int UIA_Text_TextChangedEventId         = 20015;
 173     private static final int UIA_MenuModeStartEventId            = 20018;
 174     private static final int UIA_MenuModeEndEventId              = 20019;
 175 
 176     /* SupportedTextSelection */
 177     private static final int SupportedTextSelection_None         = 0;
 178     private static final int SupportedTextSelection_Single       = 1;
 179     private static final int SupportedTextSelection_Multiple     = 2;
 180 
 181     /* ExpandCollapseState */
 182     private static final int ExpandCollapseState_Collapsed          = 0;
 183     private static final int ExpandCollapseState_Expanded           = 1;
 184     private static final int ExpandCollapseState_PartiallyExpanded  = 2;
 185     private static final int ExpandCollapseState_LeafNode           = 3;
 186 
 187     /* ScrollAmount */
 188     private static final int ScrollAmount_LargeDecrement        = 0;
 189     private static final int ScrollAmount_SmallDecrement        = 1;
 190     private static final int ScrollAmount_NoAmount              = 2;
 191     private static final int ScrollAmount_LargeIncrement        = 3;
 192     private static final int ScrollAmount_SmallIncrement        = 4;
 193 
 194     /* Scroll */
 195     private static final int UIA_ScrollPatternNoScroll          = -1;
 196 
 197     /* ToggleState */
 198     private static final int ToggleState_Off                    = 0;
 199     private static final int ToggleState_On                     = 1;
 200     private static final int ToggleState_Indeterminate          = 2;
 201 
 202     /* Other constants */
 203     private static final int UiaAppendRuntimeId                  = 3;
 204 
 205     private long peer;
 206     private int id;
 207 
 208     /* Text Support */
 209     private WinTextRangeProvider documentRange;
 210     private WinTextRangeProvider selectionRange;
 211 
 212     /* The lastIndex is used by parents to keep track of the index of the last child
 213      * returned in Navigate. It is very common for Narrator to traverse the children
 214      * list by calling next sibling sequentially, without lastIndex the caller would
 215      * have to traverse the list to find the location of the current child before it
 216      * can return the next sibling.
 217      */
 218     private int lastIndex = 0;
 219 
 220     /* Creates a GlassAccessible linked to the caller (GlobalRef) */
 221     private native long _createGlassAccessible();
 222 
 223     /* Releases the GlassAccessible and deletes the GlobalRef */
 224     private native void _destroyGlassAccessible(long accessible);
 225 
 226     private native static long UiaRaiseAutomationEvent(long pProvider, int id);
 227     private native static long UiaRaiseAutomationPropertyChangedEvent(long pProvider, int id, WinVariant oldV, WinVariant newV);
 228     private native static boolean UiaClientsAreListening();
 229 
 230     WinAccessible() {
 231         this.peer = _createGlassAccessible();
 232         if (this.peer == 0L) {
 233             throw new RuntimeException("could not create platform accessible");
 234         }
 235         this.id = idCount++;
 236     }
 237 
 238     @Override
 239     public void dispose() {
 240         super.dispose();
 241         if (selectionRange != null) {
 242             selectionRange.dispose();
 243             selectionRange = null;
 244         }
 245         if (documentRange != null) {
 246             documentRange.dispose();
 247             documentRange = null;
 248         }
 249         if (peer != 0L) {
 250             _destroyGlassAccessible(peer);
 251             peer = 0L;
 252         }
 253     }
 254 
 255 
 256     @Override
 257     public void sendNotification(AccessibleAttribute notification) {
 258         if (isDisposed()) return;
 259 
 260         switch (notification) {
 261             case FOCUS_NODE:
 262                 if (getView() != null) {
 263                     // This is a Scene
 264                     long focus = GetFocus();
 265                     if (focus != 0) {
 266                         UiaRaiseAutomationEvent(focus, UIA_AutomationFocusChangedEventId);
 267                     }
 268                 } else {
 269                     // This is a Scene.transientFocusContainer
 270                     Node node = (Node)getAttribute(FOCUS_NODE);
 271                     if (node != null) {
 272                         UiaRaiseAutomationEvent(getNativeAccessible(node), UIA_AutomationFocusChangedEventId);
 273                     } else {
 274                         // Delegate back to the Scene if the transient focus owner is null
 275                         Scene scene = (Scene)getAttribute(SCENE);
 276                         Accessible acc = getAccessible(scene);
 277                         if (acc != null) {
 278                             acc.sendNotification(FOCUS_NODE);
 279                         }
 280                     }
 281                 }
 282                 break;
 283             case FOCUS_ITEM: {
 284                 Node node = (Node)getAttribute(FOCUS_ITEM);
 285                 long id = getNativeAccessible(node);
 286                 if (id != 0) {
 287                     UiaRaiseAutomationEvent(id, UIA_AutomationFocusChangedEventId);
 288                 }
 289                 break;
 290             }
 291             case INDETERMINATE: {
 292                 if (getAttribute(ROLE) == AccessibleRole.CHECK_BOX) {
 293                     notifyToggleState();
 294                 }
 295                 break;
 296             }
 297             case SELECTED: {
 298                 Object role = getAttribute(ROLE); 
 299                 if (role == AccessibleRole.CHECK_BOX || role == AccessibleRole.TOGGLE_BUTTON) {
 300                     notifyToggleState();
 301                     break;
 302                 }
 303                 Boolean selected = (Boolean)getAttribute(SELECTED);
 304                 if (selected != null) {
 305                     if (selected) {
 306                         UiaRaiseAutomationEvent(peer, UIA_SelectionItem_ElementSelectedEventId);
 307                     } else {
 308                         UiaRaiseAutomationEvent(peer, UIA_SelectionItem_ElementRemovedFromSelectionEventId);
 309                     }
 310                 }
 311                 break;
 312             }
 313             case FOCUSED: {
 314                 /* HANDLED IN FOCUS_NODE */
 315                 break;
 316             }
 317             case VALUE: {
 318                 Double value = (Double)getAttribute(VALUE);
 319                 if (value != null) {
 320                     WinVariant vo = new WinVariant();
 321                     vo.vt = WinVariant.VT_R8;
 322                     vo.dblVal = 0;
 323                     WinVariant vn = new WinVariant();
 324                     vn.vt = WinVariant.VT_R8;
 325                     vn.dblVal = value;
 326                     UiaRaiseAutomationPropertyChangedEvent(peer, UIA_RangeValueValuePropertyId, vo, vn);
 327                 }
 328                 break;
 329             }
 330             case SELECTION_START:
 331             case SELECTION_END:
 332                 if (selectionRange != null) {
 333                     Integer start = (Integer)getAttribute(SELECTION_START);
 334                     boolean newStart = start != null && start != selectionRange.getStart();
 335                     Integer end = (Integer)getAttribute(SELECTION_END);
 336                     boolean newEnd = end != null && end != selectionRange.getEnd();
 337                     /* Sending unnecessary selection changed events causes Narrator
 338                      * not to focus an empty text control when clicking.
 339                      */
 340                     if (newStart || newEnd) {
 341                         UiaRaiseAutomationEvent(peer, UIA_Text_TextSelectionChangedEventId);
 342                     }
 343                 }
 344                 break;
 345             case TEXT:
 346                 String value = (String)getAttribute(TEXT);
 347                 if (value != null) {
 348                     WinVariant vo = new WinVariant();
 349                     vo.vt = WinVariant.VT_BSTR;
 350                     vo.bstrVal = "";
 351                     WinVariant vn = new WinVariant();
 352                     vn.vt = WinVariant.VT_BSTR;
 353                     vn.bstrVal = value;
 354                     if (getAttribute(ROLE) == AccessibleRole.SPINNER) {
 355                         UiaRaiseAutomationPropertyChangedEvent(peer, UIA_NamePropertyId, vo, vn);
 356                     } else {
 357                         /* Combo and Text both implement IValueProvider */
 358                         UiaRaiseAutomationPropertyChangedEvent(peer, UIA_ValueValuePropertyId, vo, vn);
 359                     }
 360                 }
 361 
 362                 if (selectionRange != null || documentRange != null) {
 363                     UiaRaiseAutomationEvent(peer, UIA_Text_TextChangedEventId);
 364                 }
 365                 break;
 366             case EXPANDED: {
 367                 Boolean expanded = (Boolean)getAttribute(EXPANDED);
 368                 if (expanded != null) {
 369                     WinVariant vo = new WinVariant();
 370                     vo.vt = WinVariant.VT_I4;
 371                     vo.lVal = expanded ? ExpandCollapseState_Collapsed : ExpandCollapseState_Expanded;
 372                     WinVariant vn = new WinVariant();
 373                     vn.vt = WinVariant.VT_I4;
 374                     vn.lVal = expanded ? ExpandCollapseState_Expanded : ExpandCollapseState_Collapsed;
 375                     if (getAttribute(ROLE) == AccessibleRole.TREE_TABLE_ROW) {
 376                         Accessible treeTableView = getContainer();
 377                         Integer rowIndex = (Integer)getAttribute(INDEX);
 378                         if (treeTableView != null && rowIndex != null) {
 379                             Node node = (Node)treeTableView.getAttribute(CELL_AT_ROW_COLUMN, rowIndex, 0);
 380                             if (node != null) {
 381                                 long ptr = ((WinAccessible)getAccessible(node)).getNativeAccessible();
 382                                 UiaRaiseAutomationPropertyChangedEvent(ptr, UIA_ExpandCollapseExpandCollapseStatePropertyId, vo, vn);
 383                             }
 384                         }
 385                     } else {
 386                         UiaRaiseAutomationPropertyChangedEvent(peer, UIA_ExpandCollapseExpandCollapseStatePropertyId, vo, vn);
 387                     }
 388                 }
 389                 break;
 390             }
 391             case PARENT:
 392                 break;
 393             default:
 394                 UiaRaiseAutomationEvent(peer, UIA_AutomationPropertyChangedEventId);
 395         }
 396     }
 397 
 398     private void notifyToggleState() {
 399         int state = get_ToggleState();
 400         WinVariant vo = new WinVariant();
 401         vo.vt = WinVariant.VT_I4;
 402         vo.lVal = state;
 403         WinVariant vn = new WinVariant();
 404         vn.vt = WinVariant.VT_I4;
 405         vn.lVal = state;
 406         UiaRaiseAutomationPropertyChangedEvent(peer, UIA_ToggleToggleStatePropertyId, vo, vn);
 407     }
 408 
 409     @Override
 410     protected long getNativeAccessible() {
 411         return peer;
 412     }
 413 
 414     private Accessible getContainer() {
 415         if (isDisposed()) return null;
 416         AccessibleRole role = (AccessibleRole) getAttribute(ROLE);
 417         if (role != null) {
 418             switch (role) {
 419                 case TABLE_ROW:
 420                 case TABLE_CELL: return getContainerAccessible(AccessibleRole.TABLE_VIEW);
 421                 case LIST_ITEM: return getContainerAccessible(AccessibleRole.LIST_VIEW);
 422                 case TAB_ITEM: return getContainerAccessible(AccessibleRole.TAB_PANE);
 423                 case PAGE_ITEM: return getContainerAccessible(AccessibleRole.PAGINATION);
 424                 case TREE_ITEM: return getContainerAccessible(AccessibleRole.TREE_VIEW);
 425                 case TREE_TABLE_ROW:
 426                 case TREE_TABLE_CELL: return getContainerAccessible(AccessibleRole.TREE_TABLE_VIEW);
 427                 default:
 428             }
 429         }
 430         return null;
 431     }
 432 
 433     private int getControlType() {
 434         AccessibleRole role = (AccessibleRole)getAttribute(ROLE);
 435         if (role == null) return UIA_GroupControlTypeId;
 436         switch (role) {
 437             case CONTEXT_MENU: return UIA_MenuControlTypeId;
 438             case RADIO_MENU_ITEM:
 439             case CHECK_MENU_ITEM:
 440             case MENU:
 441             case MENU_ITEM: return UIA_MenuItemControlTypeId;
 442             case BUTTON:
 443             case MENU_BUTTON:
 444             case TOGGLE_BUTTON:
 445             case INCREMENT_BUTTON:
 446             case DECREMENT_BUTTON: return UIA_ButtonControlTypeId;
 447             case SPLIT_MENU_BUTTON: return UIA_SplitButtonControlTypeId;
 448             case PAGINATION:
 449             case TAB_PANE: return UIA_TabControlTypeId;
 450             case PAGE_ITEM:
 451             case TAB_ITEM: return UIA_TabItemControlTypeId;
 452             case SLIDER: return UIA_SliderControlTypeId;
 453             case PARENT: return getView() != null ? UIA_WindowControlTypeId : UIA_PaneControlTypeId;
 454             case TEXT: return UIA_TextControlTypeId;
 455             case TEXT_FIELD:
 456             case PASSWORD_FIELD:
 457             case TEXT_AREA: return UIA_EditControlTypeId;
 458             case TREE_TABLE_VIEW:
 459             case TABLE_VIEW: return UIA_TableControlTypeId;
 460             case LIST_VIEW: return UIA_ListControlTypeId;
 461             case LIST_ITEM: return UIA_ListItemControlTypeId;
 462             case TREE_TABLE_CELL:
 463             case TABLE_CELL: return UIA_DataItemControlTypeId;
 464             case IMAGE_VIEW: return UIA_ImageControlTypeId;
 465             case RADIO_BUTTON: return UIA_RadioButtonControlTypeId;
 466             case CHECK_BOX: return UIA_CheckBoxControlTypeId;
 467             case COMBO_BOX: return UIA_ComboBoxControlTypeId;
 468             case HYPERLINK: return UIA_HyperlinkControlTypeId;
 469             case TREE_VIEW: return UIA_TreeControlTypeId;
 470             case TREE_ITEM: return UIA_TreeItemControlTypeId;
 471             case PROGRESS_INDICATOR: return UIA_ProgressBarControlTypeId;
 472             case TOOL_BAR: return UIA_ToolBarControlTypeId;
 473             case TITLED_PANE: return UIA_GroupControlTypeId;
 474             case SCROLL_PANE: return UIA_PaneControlTypeId;
 475             case SCROLL_BAR: return UIA_ScrollBarControlTypeId;
 476             case THUMB: return UIA_ThumbControlTypeId;
 477             case MENU_BAR: return UIA_MenuBarControlTypeId;
 478             case DATE_PICKER: return UIA_PaneControlTypeId;
 479             case SPINNER: return UIA_SpinnerControlTypeId;
 480             default: return 0;
 481         }
 482     }
 483 
 484     /* Helper used by TreeTableCell to find the TableRow */ 
 485     private Accessible getRow() {
 486         Integer columnIndex = (Integer)getAttribute(COLUMN_INDEX);
 487         if (columnIndex == null) return null;
 488         if (columnIndex != 0) return null;
 489         Integer rowIndex = (Integer)getAttribute(ROW_INDEX);
 490         if (rowIndex == null) return null;
 491         Accessible treeTableView = getContainer();
 492         if (treeTableView == null) return null;
 493         Node treeTableRow = (Node)treeTableView.getAttribute(ROW_AT_INDEX, rowIndex);
 494         return getAccessible(treeTableRow);
 495     }
 496 
 497     private void changeSelection(boolean add, boolean clear) {
 498         AccessibleRole role = (AccessibleRole)getAttribute(ROLE);
 499         if (role == null) return;
 500         Accessible container = getContainer();
 501         if (container == null) return;
 502         Node item = null;
 503         switch (role) {
 504             case LIST_ITEM: {
 505                 Integer index = (Integer)getAttribute(INDEX);
 506                 if (index != null) {
 507                     item = (Node)container.getAttribute(ITEM_AT_INDEX, index);
 508                 }
 509                 break;
 510             }
 511             case TREE_ITEM: {
 512                 Integer index = (Integer)getAttribute(INDEX);
 513                 if (index != null) {
 514                     item = (Node)container.getAttribute(ROW_AT_INDEX, index);
 515                 }
 516                 break;
 517             }
 518             case TABLE_CELL:
 519             case TREE_TABLE_CELL: {
 520                 Integer rowIndex = (Integer)getAttribute(ROW_INDEX);
 521                 Integer columnIndex = (Integer)getAttribute(COLUMN_INDEX);
 522                 if (rowIndex != null && columnIndex != null) {
 523                     item = (Node)container.getAttribute(CELL_AT_ROW_COLUMN, rowIndex, columnIndex);
 524                 }
 525                 break;
 526             }
 527             default:
 528         }
 529         if (item != null) {
 530             ObservableList<Node> newItems = FXCollections.observableArrayList();
 531             if (!clear) {
 532                 @SuppressWarnings("unchecked")
 533                 ObservableList<Node> items = (ObservableList<Node>)container.getAttribute(SELECTED_ITEMS);
 534                 if (items != null) {
 535                     newItems.addAll(items);
 536                 }
 537             }
 538             if (add) {
 539                 newItems.add(item);
 540             } else {
 541                 newItems.remove(item);
 542             }
 543             container.executeAction(AccessibleAction.SET_SELECTED_ITEMS, newItems);
 544         }
 545     }
 546 
 547     /***********************************************/
 548     /*        IRawElementProviderSimple            */
 549     /***********************************************/
 550     private long GetPatternProvider(int patternId) {
 551         if (isDisposed()) return 0;
 552         AccessibleRole role = (AccessibleRole)getAttribute(ROLE);
 553         boolean impl = false;
 554         switch (role) {
 555             case MENU:
 556             case SPLIT_MENU_BUTTON:
 557                 impl = patternId == UIA_InvokePatternId ||
 558                        patternId == UIA_ExpandCollapsePatternId;
 559                 break;
 560             case RADIO_MENU_ITEM:
 561             case CHECK_MENU_ITEM:
 562                 impl = patternId == UIA_InvokePatternId ||
 563                        patternId == UIA_TogglePatternId;
 564                 break;
 565             case HYPERLINK:
 566             case BUTTON:
 567             case INCREMENT_BUTTON:
 568             case DECREMENT_BUTTON:
 569             case MENU_BUTTON:
 570             case MENU_ITEM:
 571                 impl = patternId == UIA_InvokePatternId;
 572                 break;
 573             case PAGE_ITEM:
 574             case TAB_ITEM:
 575                 impl = patternId == UIA_SelectionItemPatternId;
 576                 break;
 577             case PAGINATION:
 578             case TAB_PANE:
 579                 impl = patternId == UIA_SelectionPatternId;
 580                 break;
 581             case SCROLL_PANE:
 582                 impl = patternId == UIA_ScrollPatternId;
 583                 break;
 584             case TREE_TABLE_VIEW:
 585             case TABLE_VIEW:
 586                 impl = patternId == UIA_SelectionPatternId ||
 587                        patternId == UIA_GridPatternId ||
 588                        patternId == UIA_TablePatternId ||
 589                        patternId == UIA_ScrollPatternId;
 590                 break;
 591             case TREE_TABLE_CELL:
 592                 impl = patternId == UIA_SelectionItemPatternId ||
 593                        patternId == UIA_GridItemPatternId ||
 594                        patternId == UIA_TableItemPatternId ||
 595                        patternId == UIA_ExpandCollapsePatternId ||
 596                        patternId == UIA_ScrollItemPatternId;
 597                 break;
 598             case TABLE_CELL:
 599                 impl = patternId == UIA_SelectionItemPatternId ||
 600                        patternId == UIA_GridItemPatternId ||
 601                        patternId == UIA_TableItemPatternId ||
 602                        patternId == UIA_ScrollItemPatternId;
 603                 break;
 604             case TREE_VIEW:
 605                 impl = patternId == UIA_SelectionPatternId ||
 606                        patternId == UIA_ScrollPatternId;
 607                 break;
 608             case TREE_ITEM:
 609                 impl = patternId == UIA_SelectionItemPatternId ||
 610                        patternId == UIA_ExpandCollapsePatternId ||
 611                        patternId == UIA_ScrollItemPatternId;
 612                 break;
 613             case LIST_VIEW:
 614                 impl = patternId == UIA_SelectionPatternId ||
 615                        patternId == UIA_ScrollPatternId;
 616                 break;
 617             case LIST_ITEM:
 618                 impl = patternId == UIA_SelectionItemPatternId ||
 619                        patternId == UIA_ScrollItemPatternId;
 620                 break;
 621             /* 
 622              * MSDN doc is confusing if text elements should implement
 623              * UIA_ValuePatternId. The article 'Text and TextRange Control
 624              * Patterns' says to implement for backward compatibility (MSAA).
 625              * The article 'Text Control Type' says to never implement it,
 626              * and says to use 'Edit control type' instead (which is only
 627              * available on Windows 8).
 628              *
 629              * For now UIA_ValuePatternId is implemented to enable us to set the
 630              * value on TextField / TextArea controls.
 631              */
 632             case TEXT_FIELD:
 633             case TEXT_AREA:
 634                 impl = patternId == UIA_TextPatternId ||
 635                        patternId == UIA_ValuePatternId;
 636                 break;
 637             case TEXT:
 638                 /* UIA_TextPatternId seems overkill for text. Use UIA_NamePropertyId instead */
 639                 break;
 640             case RADIO_BUTTON:
 641                 impl = patternId == UIA_SelectionItemPatternId;
 642                 break;
 643             case CHECK_BOX:
 644             case TOGGLE_BUTTON:
 645                 impl = patternId == UIA_TogglePatternId;
 646                 break;
 647             case TITLED_PANE:
 648             case TOOL_BAR:
 649                 impl = patternId == UIA_ExpandCollapsePatternId;
 650                 break;
 651             case COMBO_BOX:
 652                 impl = patternId == UIA_ExpandCollapsePatternId ||
 653                        patternId == UIA_ValuePatternId;
 654                 break;
 655             case SCROLL_BAR:
 656             case SLIDER:
 657             case PROGRESS_INDICATOR:
 658                 impl = patternId == UIA_RangeValuePatternId;
 659                 break;
 660             default:
 661         }
 662         return impl ? getNativeAccessible() : 0L;
 663     }
 664 
 665     private long get_HostRawElementProvider() {
 666         if (isDisposed()) return 0;
 667         /* 
 668          * Returning NULL makes this accessible 'lightweight',
 669          * GetRuntimeID() will be called for it.
 670          */
 671         View view = getView();
 672         return view != null ? view.getNativeView() : 0;
 673     }
 674 
 675     private WinVariant GetPropertyValue(int propertyId) {
 676         if (isDisposed()) return null;
 677         WinVariant variant = null;
 678         switch (propertyId) {
 679             case UIA_ControlTypePropertyId: {
 680                 int controlType = getControlType();
 681                 if (controlType != 0) {
 682                     variant = new WinVariant();
 683                     variant.vt = WinVariant.VT_I4;
 684                     variant.lVal = controlType;
 685                 }
 686                 break;
 687             }
 688             case UIA_AccessKeyPropertyId: {
 689                 String mnemonic = (String)getAttribute(MNEMONIC);
 690                 if (mnemonic != null) {
 691                     variant = new WinVariant();
 692                     variant.vt = WinVariant.VT_BSTR;
 693                     variant.bstrVal = "Alt " + mnemonic.toLowerCase();
 694                 }
 695                 break;
 696             }
 697             case UIA_AcceleratorKeyPropertyId: {
 698                 KeyCombination kc = (KeyCombination)getAttribute(ACCELERATOR);
 699                 if (kc != null) {
 700                     variant = new WinVariant();
 701                     variant.vt = WinVariant.VT_BSTR;
 702                     /* Note: KeyCombination should have a getDisplayText() which encapsulates 
 703                      * KeystrokeUtils.toString()
 704                      */
 705                     variant.bstrVal = kc.toString().replaceAll("Shortcut", "Ctrl");
 706                 }
 707                 break;
 708             }
 709             case UIA_NamePropertyId: {
 710                 String name;
 711 
 712                 AccessibleRole role = (AccessibleRole)getAttribute(ROLE);
 713                 if (role == null) role = AccessibleRole.NODE;
 714                 switch (role) {
 715                     case TEXT_FIELD:
 716                     case TEXT_AREA:
 717                     case COMBO_BOX:
 718                         /*
 719                          *  IValueProvider controls use UIA_NamePropertyId to
 720                          *  return the LABELED_BY and get_ValueString() to
 721                          *  return the TITLE.
 722                          */
 723                         name = null;
 724                         break;
 725                     case DECREMENT_BUTTON:
 726                     case INCREMENT_BUTTON: {
 727                         name = (String)getAttribute(TEXT);
 728                         if (name == null || name.length() == 0) {
 729                             if (role == AccessibleRole.INCREMENT_BUTTON) {
 730                                 name = "increment";
 731                             } else {
 732                                 name = "decrement";
 733                             }
 734                         }
 735                         break;
 736                     }
 737                     default:
 738                         name = (String)getAttribute(TEXT);
 739                 }
 740 
 741                 if (name == null || name.length() == 0) {
 742                     Node label = (Node)getAttribute(LABELED_BY);
 743                     if (label != null) {
 744                         name = (String)getAccessible(label).getAttribute(TEXT);
 745                     }
 746                 }
 747                 if (name == null || name.length() == 0) {
 748                     /* Code intentionally commented - use for debugging only */
 749 //                    if (getView() != null) {
 750 //                        name = "JavaFXWindow" + id;
 751 //                    } else {
 752 //                        name = toString() + "-" + id;
 753 //                    }
 754                     break;
 755                 }
 756                 variant = new WinVariant();
 757                 variant.vt = WinVariant.VT_BSTR;
 758                 variant.bstrVal = (String)name;
 759                 break;
 760             }
 761             case UIA_HelpTextPropertyId: {
 762                 String help = (String)getAttribute(HELP);
 763                 if (help != null) {
 764                     variant = new WinVariant();
 765                     variant.vt = WinVariant.VT_BSTR;
 766                     variant.bstrVal = help;
 767                 }
 768                 break;
 769             }
 770             case UIA_LocalizedControlTypePropertyId: {
 771                 String description = (String)getAttribute(ROLE_DESCRIPTION);
 772                 if (description == null) {
 773                     AccessibleRole role = (AccessibleRole)getAttribute(ROLE);
 774                     if (role == null) role = AccessibleRole.NODE;
 775                     switch (role) {
 776                         case TITLED_PANE: description = "title pane"; break;
 777                         case PAGE_ITEM: description = "page"; break;
 778                         default:
 779                     }
 780                 }
 781                 if (description != null) {
 782                     variant = new WinVariant();
 783                     variant.vt = WinVariant.VT_BSTR;
 784                     variant.bstrVal = description;
 785                 }
 786                 break;
 787             }
 788             case UIA_HasKeyboardFocusPropertyId: {
 789                 Boolean focus = (Boolean)getAttribute(FOCUSED);
 790                 /* 
 791                  * Note that accessibility focus and scene focus are not the same.
 792                  * Windows won't work correctly unless the accessible returned in GetFocus() 
 793                  * answer TRUE in UIA_HasKeyboardFocusPropertyId.
 794                  * Note that UIA_HasKeyboardFocusPropertyId reports true for the main parent
 795                  * of a 'focus item', but that doesn't seem to cause problems.
 796                  */
 797                 if (Boolean.FALSE.equals(focus)) {
 798                     Scene scene = (Scene)getAttribute(SCENE);
 799                     if (scene != null) {
 800                         Accessible acc = getAccessible(scene);
 801                         if (acc != null) {
 802                             Node node = (Node)acc.getAttribute(FOCUS_NODE);
 803                             if (node != null) {
 804                                 Node item = (Node)getAccessible(node).getAttribute(FOCUS_ITEM);
 805                                 if (getNativeAccessible(item) == peer) {
 806                                     focus = true;
 807                                 }
 808                             }
 809                         }
 810                     }
 811                 }
 812                 variant = new WinVariant();
 813                 variant.vt = WinVariant.VT_BOOL;
 814                 variant.boolVal = focus != null ? focus : false;
 815                 break;
 816             }
 817             case UIA_IsContentElementPropertyId:
 818             case UIA_IsControlElementPropertyId: {
 819                 //TODO how to handle ControlElement versus ContentElement
 820                 variant = new WinVariant();
 821                 variant.vt = WinVariant.VT_BOOL;
 822                 variant.boolVal = getView() != null || !isIgnored();
 823                 break;
 824             }
 825             case UIA_IsEnabledPropertyId: {
 826                 Boolean disabled = (Boolean)getAttribute(DISABLED);
 827                 variant = new WinVariant();
 828                 variant.vt = WinVariant.VT_BOOL;
 829                 variant.boolVal = disabled != null ? !disabled : true;
 830                 break;
 831             }
 832             case UIA_IsKeyboardFocusablePropertyId: {
 833                 variant = new WinVariant();
 834                 variant.vt = WinVariant.VT_BOOL;
 835                 variant.boolVal = true; //TODO return focusTraversable
 836                 break;
 837             }
 838             case UIA_IsPasswordPropertyId: {
 839                 AccessibleRole role = (AccessibleRole)getAttribute(ROLE);
 840                 variant = new WinVariant();
 841                 variant.vt = WinVariant.VT_BOOL;
 842                 variant.boolVal = role == AccessibleRole.PASSWORD_FIELD;
 843                 break;
 844             }
 845             case UIA_AutomationIdPropertyId: {
 846                 /* Not required but useful for debugging */
 847                 variant = new WinVariant();
 848                 variant.vt = WinVariant.VT_BSTR;
 849                 variant.bstrVal = "JavaFX"+id;
 850                 break;
 851             }
 852             case UIA_ProviderDescriptionPropertyId: {
 853                 /* Not required but useful for debugging */
 854                 variant = new WinVariant();
 855                 variant.vt = WinVariant.VT_BSTR;
 856                 variant.bstrVal = "JavaFXProvider";
 857                 break;
 858             }
 859             default:
 860         }
 861         return variant;
 862     }
 863 
 864     /***********************************************/
 865     /*       IRawElementProviderFragment           */
 866     /***********************************************/
 867     private float[] get_BoundingRectangle() {
 868         if (isDisposed()) return null;
 869         /* No needs to answer for the root */
 870         if (getView() != null) return null;
 871 
 872         Bounds bounds = (Bounds)getAttribute(BOUNDS);
 873         if (bounds != null) {
 874             return new float[] {(float)bounds.getMinX(), (float)bounds.getMinY(),
 875                                 (float)bounds.getWidth(), (float)bounds.getHeight()};
 876         }
 877         return null;
 878     }
 879 
 880     private long get_FragmentRoot() {
 881         if (isDisposed()) return 0L;
 882         Scene scene = (Scene)getAttribute(SCENE);
 883         if (scene == null) return 0L;
 884         WinAccessible acc = (WinAccessible)getAccessible(scene);
 885         if (acc == null || acc.isDisposed()) return 0L;
 886         return acc.getNativeAccessible();
 887     }
 888 
 889     private long[] GetEmbeddedFragmentRoots() {
 890         if (isDisposed()) return null;
 891         return null;
 892     }
 893 
 894     private int[] GetRuntimeId() {
 895         if (isDisposed()) return null;
 896 
 897         /* MSDN: Implementations should return NULL for a top-level element that is hosted in a window. */
 898         if (getView() != null) return null;
 899         return new int[] {UiaAppendRuntimeId, id};
 900     }
 901 
 902     private long NavigateListView(WinAccessible listItemAccessible, int direction) {
 903         Accessible listAccessible = listItemAccessible.getContainer();
 904         if (listAccessible == null) return 0;
 905         Integer count = (Integer)listAccessible.getAttribute(ITEM_COUNT);
 906         if (count == null || count == 0) return 0;
 907         Integer index = (Integer)listItemAccessible.getAttribute(INDEX);
 908         if (index == null) return 0;
 909 
 910         /* A view implementation can use stock items to measuring, these items can 
 911          * have index equal to -1 for identification. See ViewFlow#accumCell as an example.
 912          * These items should be ignored to avoid incorrect item count computation by
 913          * the screen reader.
 914          */
 915         if (!(0 <= index && index < count)) return 0;
 916         switch (direction) {
 917             case NavigateDirection_NextSibling: index++; break;
 918             case NavigateDirection_PreviousSibling: index--; break;
 919             case NavigateDirection_FirstChild: index = 0; break;
 920             case NavigateDirection_LastChild: index = count - 1; break;
 921         }
 922         if (!(0 <= index && index < count)) return 0;
 923         Node node = (Node)listAccessible.getAttribute(ITEM_AT_INDEX, index);
 924         return getNativeAccessible(node);
 925     }
 926 
 927     private long Navigate(int direction) {
 928         if (isDisposed()) return 0;
 929         AccessibleRole role = (AccessibleRole)getAttribute(ROLE);
 930         /* special case for the tree item hierarchy, as expected by Windows */
 931         boolean treeCell = role == AccessibleRole.TREE_ITEM;
 932         Node node = null;
 933         switch (direction) {
 934             case NavigateDirection_Parent: {
 935                 /* Return null for the top level node */
 936                 if (getView() != null) return 0L;
 937 
 938                 if (treeCell) {
 939                     node = (Node)getAttribute(TREE_ITEM_PARENT);
 940                     if (node == null) {
 941                         /* root tree item case*/
 942                         WinAccessible acc = (WinAccessible)getContainer();
 943                         return acc != null ? acc.getNativeAccessible() : 0L;
 944                     }
 945                 } else {
 946                     node = (Node)getAttribute(PARENT);
 947                     if (node == null) {
 948                         /* scene root node case */
 949                         Scene scene = (Scene)getAttribute(SCENE);
 950                         WinAccessible acc = (WinAccessible)getAccessible(scene);
 951                         if (acc == null || acc.isDisposed()) return 0L;
 952                         return acc.getNativeAccessible();
 953                     }
 954                 }
 955                 break;
 956             }
 957             case NavigateDirection_NextSibling:
 958             case NavigateDirection_PreviousSibling: {
 959                 if (role == AccessibleRole.LIST_ITEM) {
 960                     return NavigateListView(this, direction);
 961                 }
 962 
 963                 Node parent = (Node)getAttribute(treeCell ? TREE_ITEM_PARENT : PARENT);
 964                 /* 
 965                  * When the parent is NULL is indicates either the root node for the scene
 966                  * or the root tree item in a tree view. Either way, there is no siblings. 
 967                  */
 968                 if (parent != null) {
 969                     WinAccessible parentAccessible = (WinAccessible)getAccessible(parent);
 970                     Function<Integer, Node> getChild;
 971                     int count = 0;
 972                     if (treeCell) {
 973                         Integer result = (Integer)parentAccessible.getAttribute(TREE_ITEM_COUNT);
 974                         if (result == null) return 0;
 975                         count = result;
 976                         getChild = index -> {
 977                             return (Node)parentAccessible.getAttribute(AccessibleAttribute.TREE_ITEM_AT_INDEX, index);
 978                         };
 979                     } else {
 980                         @SuppressWarnings("unchecked")
 981                         ObservableList<Node> children = (ObservableList<Node>)parentAccessible.getAttribute(CHILDREN);
 982                         if (children == null) return 0;
 983                         count = children.size();
 984                         getChild = index -> {
 985                             return children.get(index);
 986                         };
 987                     }
 988 
 989                     int lastIndex = parentAccessible.lastIndex;
 990                     int currentIndex = -1;
 991                     if (0 <= lastIndex && lastIndex < count && getNativeAccessible(getChild.apply(lastIndex)) == peer) {
 992                         currentIndex = lastIndex;
 993                     } else {
 994                         for (int i = 0; i < count; i++) {
 995                             if (getNativeAccessible(getChild.apply(i)) == peer) {
 996                                 currentIndex = i;
 997                                 break;
 998                             }
 999                         }
1000                     }
1001                     if (currentIndex != -1) {
1002                         if (direction == NavigateDirection_NextSibling) {
1003                             currentIndex++;
1004                         } else {
1005                             currentIndex--;
1006                         }
1007                         if (0 <= currentIndex && currentIndex < count) {
1008                             node = getChild.apply(currentIndex);
1009                             parentAccessible.lastIndex = currentIndex;
1010                         }
1011                     }
1012                 }
1013                 break;
1014             }
1015             case NavigateDirection_FirstChild:
1016             case NavigateDirection_LastChild: {
1017                 lastIndex = -1;
1018                 if (role == AccessibleRole.LIST_VIEW) {
1019                     /* Windows 7. Initially the ComboBox contains the ListView,
1020                      * but the ListCells will only be created if one an item is 
1021                      * selected. This causes Narrator to read combo box with
1022                      * zero items. The fix is to ask for first item, which will
1023                      * cause NavigateListView to be used.
1024                      * */
1025                     getAttribute(ITEM_AT_INDEX, 0);
1026                 }
1027                 if (role == AccessibleRole.TREE_VIEW) {
1028                     /* The TreeView only returns the root node as child */
1029                     lastIndex = 0;
1030                     node = (Node)getAttribute(ROW_AT_INDEX, 0);
1031                 } else if (treeCell) {
1032                     Integer count = (Integer)getAttribute(TREE_ITEM_COUNT);
1033                     if (count != null && count > 0) {
1034                         lastIndex = direction == NavigateDirection_FirstChild ? 0 : count - 1;
1035                         node = (Node)getAttribute(TREE_ITEM_AT_INDEX, lastIndex);
1036                     }
1037                 } else {
1038                     @SuppressWarnings("unchecked")
1039                     ObservableList<Node> children = (ObservableList<Node>)getAttribute(CHILDREN);
1040                     if (children != null && children.size() > 0) {
1041                         lastIndex = direction == NavigateDirection_FirstChild ? 0 : children.size() - 1;
1042                         node = children.get(lastIndex);
1043                     }
1044                     if (node != null) {
1045                         role = (AccessibleRole)getAccessible(node).getAttribute(ROLE);
1046                         if (role == AccessibleRole.LIST_ITEM) {
1047                             WinAccessible itemAcc = (WinAccessible)getAccessible(node);
1048                             return NavigateListView(itemAcc, direction);
1049                         }
1050                     }
1051                 }
1052                 break;
1053             }
1054         }
1055         return getNativeAccessible(node);
1056     }
1057 
1058     private void SetFocus() {
1059         if (isDisposed()) return;
1060         executeAction(AccessibleAction.REQUEST_FOCUS);
1061     }
1062 
1063     /***********************************************/
1064     /*     IRawElementProviderFragmentRoot         */
1065     /***********************************************/
1066     private long ElementProviderFromPoint(double x, double y) {
1067         if (isDisposed()) return 0;
1068         Node node = (Node)getAttribute(NODE_AT_POINT, new Point2D(x, y));
1069         return getNativeAccessible(node);
1070     }
1071 
1072     private long GetFocus() {
1073         if (isDisposed()) return 0;
1074         Node node = (Node)getAttribute(FOCUS_NODE);
1075         if (node == null) return 0L;
1076         Node item = (Node)getAccessible(node).getAttribute(FOCUS_ITEM);
1077         if (item != null) return getNativeAccessible(item);
1078         return getNativeAccessible(node);
1079     }
1080 
1081     /***********************************************/
1082     /*     IRawElementProviderAdviseEvents         */
1083     /***********************************************/
1084     private void AdviseEventAdded(int eventId, long propertyIDs) {
1085         /* Implementing IRawElementProviderAdviseEvents ensures
1086          * that the window is announced by Narrator when it first
1087          * opens. No further action is required.
1088          */
1089     }
1090 
1091     private void AdviseEventRemoved(int eventId, long propertyIDs) {
1092         /* See AdviseEventAdded() */
1093     }
1094 
1095     /***********************************************/
1096     /*             IInvokeProvider                 */
1097     /***********************************************/
1098     private void Invoke() {
1099         if (isDisposed()) return;
1100         executeAction(AccessibleAction.FIRE);
1101     }
1102 
1103     /***********************************************/
1104     /*           ISelectionProvider                */
1105     /***********************************************/
1106     private long[] GetSelection() {
1107         if (isDisposed()) return null;
1108 
1109         /* 
1110          * GetSelection() is sent by ISelectionProvider and ITextProvider.
1111          * Check the role before processing message.
1112          */
1113         AccessibleRole role = (AccessibleRole)getAttribute(ROLE);
1114         if (role == null) return null;
1115         switch (role) {
1116             case TREE_TABLE_VIEW:
1117             case TABLE_VIEW:
1118             case TREE_VIEW:
1119             case LIST_VIEW: {
1120                 @SuppressWarnings("unchecked")
1121                 ObservableList<Node> selection = (ObservableList<Node>)getAttribute(SELECTED_ITEMS);
1122                 if (selection != null) {
1123                     return selection.stream().mapToLong(n -> getNativeAccessible(n)).toArray();
1124                 }
1125                 break;
1126             }
1127             case TAB_PANE:
1128             case PAGINATION: {
1129                 Node node = (Node)getAttribute(FOCUS_ITEM);
1130                 if (node != null) {
1131                     return new long[] {getNativeAccessible(node)};
1132                 }
1133                 break;
1134             }
1135             case TEXT_FIELD:
1136             case TEXT_AREA: {
1137                 if (selectionRange == null) {
1138                     selectionRange = new WinTextRangeProvider(this);
1139                 }
1140                 Integer result = (Integer)getAttribute(SELECTION_START);
1141                 int start = result != null ? result : 0;
1142                 int end = -1;
1143                 int length = -1;
1144                 if (start >= 0) {
1145                     result = (Integer)getAttribute(SELECTION_END);
1146                     end = result != null ? result : 0;
1147                     if (end >= start) {
1148                         String string = (String)getAttribute(TEXT);
1149                         length = string != null ? string.length() : 0;
1150                     }
1151                 }
1152                 if (length != -1 && end <= length) {
1153                     selectionRange.setRange(start, end);
1154                 } else {
1155                     selectionRange.setRange(0, 0);
1156                 }
1157                 return new long[] {selectionRange.getNativeProvider()};
1158             }
1159             default:
1160         }
1161         return null;
1162     }
1163 
1164     private boolean get_CanSelectMultiple() {
1165         if (isDisposed()) return false;
1166         AccessibleRole role = (AccessibleRole)getAttribute(ROLE);
1167         if (role != null) {
1168             switch (role) {
1169                 case LIST_VIEW:
1170                 case TABLE_VIEW:
1171                 case TREE_VIEW:
1172                 case TREE_TABLE_VIEW:
1173                     return Boolean.TRUE.equals(getAttribute(MULTIPLE_SELECTION));
1174                 default:
1175             }
1176         }
1177         return false;
1178     }
1179 
1180     private boolean get_IsSelectionRequired() {
1181         if (isDisposed()) return false;
1182         //TODO: this may not be true...
1183         return true;
1184     }
1185 
1186     /***********************************************/
1187     /*           IRangeValueProvider               */
1188     /***********************************************/
1189     private void SetValue(double val) {
1190         if (isDisposed()) return;
1191         AccessibleRole role = (AccessibleRole)getAttribute(ROLE);
1192         if (role != null) {
1193             switch (role) {
1194                 case SLIDER:
1195                 case SCROLL_BAR:
1196                     executeAction(AccessibleAction.SET_VALUE, val);
1197                     break;
1198                 default:
1199             }
1200         }
1201     }
1202 
1203     private double get_Value() {
1204         if (isDisposed()) return 0;
1205         if (Boolean.TRUE.equals(getAttribute(INDETERMINATE))) return 0;
1206         Double value = (Double)getAttribute(VALUE);
1207         return value != null ? value : 0;
1208     }
1209 
1210     /*
1211      * Note that this method is called by the IValueProvider also.
1212      */
1213     private boolean get_IsReadOnly() {
1214         if (isDisposed()) return false;
1215         AccessibleRole role = (AccessibleRole)getAttribute(ROLE);
1216         if (role != null) {
1217             switch (role) {
1218                 case SLIDER: return false;
1219                 case SCROLL_BAR: return true;
1220                 case TEXT_FIELD:
1221                 case TEXT_AREA:
1222                 case COMBO_BOX: return Boolean.FALSE.equals(getAttribute(EDITABLE));
1223                 default:
1224             }
1225         }
1226         return true;
1227     }
1228 
1229     private double get_Maximum() {
1230         if (isDisposed()) return 0;
1231         Double value = (Double)getAttribute(MAX_VALUE);
1232         return value != null ? value : 0;
1233     }
1234 
1235     private double get_Minimum() {
1236         if (isDisposed()) return 0;
1237         Double value = (Double)getAttribute(MIN_VALUE);
1238         return value != null ? value : 0;
1239     }
1240 
1241     private double get_LargeChange() {
1242         if (isDisposed()) return 0;
1243         return 10;//TODO
1244     }
1245 
1246     private double get_SmallChange() {
1247         if (isDisposed()) return 0;
1248         return 3;//TODO
1249     }
1250 
1251     /***********************************************/
1252     /*             IValueProvider                  */
1253     /***********************************************/
1254     private void SetValueString(String val) {
1255         if (isDisposed()) return;
1256         AccessibleRole role = (AccessibleRole)getAttribute(ROLE);
1257         if (role != null) {
1258             switch (role) {
1259                 case TEXT_FIELD:
1260                 case TEXT_AREA:
1261                     executeAction(AccessibleAction.SET_TEXT, val);
1262                     break;
1263                 default:
1264             }
1265         }
1266     }
1267 
1268     private String get_ValueString() {
1269         if (isDisposed()) return null;
1270         return (String)getAttribute(TEXT);
1271     }
1272 
1273     /***********************************************/
1274     /*          ISelectionItemProvider             */
1275     /***********************************************/
1276     private void Select() {
1277         if (isDisposed()) return;
1278         AccessibleRole role = (AccessibleRole)getAttribute(ROLE);
1279         if (role != null) {
1280             switch (role) {
1281                 case PAGE_ITEM:
1282                 case TAB_ITEM:
1283                     executeAction(AccessibleAction.REQUEST_FOCUS);
1284                     break;
1285                 case RADIO_BUTTON:
1286                 case BUTTON:
1287                 case TOGGLE_BUTTON:
1288                 case INCREMENT_BUTTON:
1289                 case DECREMENT_BUTTON:
1290                     executeAction(AccessibleAction.FIRE);
1291                     break;
1292                 case LIST_ITEM:
1293                 case TREE_ITEM:
1294                 case TABLE_CELL:
1295                 case TREE_TABLE_CELL:
1296                     changeSelection(true, true);
1297                     break;
1298                 default:
1299             }
1300         }
1301     }
1302 
1303     private void AddToSelection() {
1304         if (isDisposed()) return;
1305         changeSelection(true, false);
1306     }
1307 
1308     private void RemoveFromSelection() {
1309         if (isDisposed()) return;
1310         changeSelection(false, false);
1311     }
1312 
1313     private boolean get_IsSelected() {
1314         if (isDisposed()) return false;
1315         return Boolean.TRUE.equals(getAttribute(SELECTED));
1316     }
1317 
1318     private long get_SelectionContainer() {
1319         if (isDisposed()) return 0;
1320         WinAccessible acc = (WinAccessible)getContainer(); 
1321         return acc != null ? acc.getNativeAccessible() : 0L;
1322     }
1323 
1324     /***********************************************/
1325     /*              ITextProvider                  */
1326     /***********************************************/
1327     private long[] GetVisibleRanges() {
1328         if (isDisposed()) return null;
1329         return new long[] {get_DocumentRange()};
1330     }
1331 
1332     private long RangeFromChild(long childElement) {
1333         if (isDisposed()) return 0;
1334         return 0;
1335     }
1336 
1337     private long RangeFromPoint(double x, double y) {
1338         if (isDisposed()) return 0;
1339         Integer offset = (Integer)getAttribute(OFFSET_AT_POINT, new Point2D(x, y));
1340         if (offset != null) {
1341             WinTextRangeProvider range = new WinTextRangeProvider(this);
1342             range.setRange(offset, offset);
1343             return range.getNativeProvider();
1344         }
1345         return 0;
1346     }
1347 
1348     private long get_DocumentRange() {
1349         if (isDisposed()) return 0;
1350         if (documentRange == null) {
1351             documentRange = new WinTextRangeProvider(this);
1352         }
1353         String text = (String)getAttribute(TEXT);
1354         documentRange.setRange(0, text.length());
1355         return documentRange.getNativeProvider();
1356     }
1357 
1358     private int get_SupportedTextSelection() {
1359         if (isDisposed()) return 0;
1360         /* Before this can be done extra API for multiple selection will be required. */
1361 //        if (Boolean.TRUE.equals(getAttribute(MULTIPLE_SELECTION))) {
1362 //            return SupportedTextSelection_Multiple;
1363 //        }
1364         return SupportedTextSelection_Single;
1365     }
1366 
1367     /***********************************************/
1368     /*             IGridProvider                   */
1369     /***********************************************/
1370     private int get_ColumnCount() {
1371         if (isDisposed()) return 0;
1372         Integer count = (Integer)getAttribute(COLUMN_COUNT);
1373 
1374         /* 
1375          * JFX does not require ListView to report column count == 1
1376          * But Windows wants a column count of (at least) 1.
1377          */
1378         return count != null ? count : 1;
1379     }
1380 
1381     private int get_RowCount() {
1382         if (isDisposed()) return 0;
1383         Integer count = (Integer)getAttribute(ROW_COUNT);
1384         return count != null ? count : 0;
1385     }
1386 
1387     private long GetItem(int row, int column) {
1388         if (isDisposed()) return 0;
1389         Node node = (Node)getAttribute(CELL_AT_ROW_COLUMN, row, column);
1390         return getNativeAccessible(node);
1391     }
1392 
1393     /***********************************************/
1394     /*             IGridItemProvider               */
1395     /***********************************************/
1396     private int get_Column() {
1397         if (isDisposed()) return 0;
1398         Integer result = (Integer)getAttribute(COLUMN_INDEX);
1399         return result != null ? result : 0;
1400     }
1401 
1402     private int get_ColumnSpan() {
1403         if (isDisposed()) return 0;
1404         return 1;
1405     }
1406 
1407     private long get_ContainingGrid() {
1408         if (isDisposed()) return 0;
1409         WinAccessible acc = (WinAccessible)getContainer();
1410         return acc != null ? acc.getNativeAccessible() : 0L;
1411     }
1412 
1413     private int get_Row() {
1414         if (isDisposed()) return 0;
1415         Integer result = null;
1416         AccessibleRole role = (AccessibleRole) getAttribute(ROLE);
1417         if (role != null) {
1418             switch (role) {
1419                 case TABLE_ROW:
1420                 case TREE_TABLE_ROW:
1421                 case LIST_ITEM: result = (Integer)getAttribute(INDEX); break;
1422                 case TREE_TABLE_CELL:
1423                 case TABLE_CELL: result = (Integer)getAttribute(ROW_INDEX); break;
1424                 default:
1425             }
1426         }
1427         return result != null ? result : 0;
1428     }
1429 
1430     private int get_RowSpan() {
1431         if (isDisposed()) return 0;
1432         return 1;
1433     }
1434 
1435     /***********************************************/
1436     /*               ITableProvider                */
1437     /***********************************************/
1438     private long[] GetColumnHeaders() {
1439         if (isDisposed()) return null;
1440        /* No support in JFX to return all columns with a single call */
1441        return null;
1442     }
1443 
1444     private long[] GetRowHeaders() {
1445         if (isDisposed()) return null;
1446        /* No row header support on JFX */
1447        return null;
1448     }
1449 
1450     private int get_RowOrColumnMajor() {
1451         if (isDisposed()) return 0;
1452         return RowOrColumnMajor_RowMajor;
1453     }
1454 
1455     /***********************************************/
1456     /*             ITableItemProvider              */
1457     /***********************************************/
1458     private long[] GetColumnHeaderItems() {
1459         if (isDisposed()) return null;
1460        Integer columnIndex = (Integer)getAttribute(COLUMN_INDEX);
1461        if (columnIndex == null) return null;
1462        Accessible acc = getContainer();
1463        if (acc == null) return null;
1464        Node column = (Node)acc.getAttribute(COLUMN_AT_INDEX, columnIndex);
1465        if (column == null) return null;
1466        return new long[] {getNativeAccessible(column)};
1467     }
1468 
1469     private long[] GetRowHeaderItems() {
1470         if (isDisposed()) return null;
1471         /* No row header support on JFX */
1472        return null;
1473     }
1474 
1475     /***********************************************/
1476     /*             IToggleProvider                 */
1477     /***********************************************/
1478     private void Toggle() {
1479         if (isDisposed()) return;
1480         executeAction(AccessibleAction.FIRE);
1481     }
1482 
1483     private int get_ToggleState() {
1484         if (isDisposed()) return 0;
1485         if (Boolean.TRUE.equals(getAttribute(INDETERMINATE))) {
1486             return ToggleState_Indeterminate;
1487         }
1488         boolean selected = Boolean.TRUE.equals(getAttribute(SELECTED));
1489         return selected ? ToggleState_On : ToggleState_Off;
1490     }
1491 
1492     /***********************************************/
1493     /*             IExpandCollapseProvider         */
1494     /***********************************************/
1495     private void Collapse() {
1496         if (isDisposed()) return;
1497         AccessibleRole role = (AccessibleRole)getAttribute(ROLE);
1498         if (role == AccessibleRole.TOOL_BAR) {
1499             Node button = (Node)getAttribute(OVERFLOW_BUTTON);
1500             if (button != null) {
1501                 getAccessible(button).executeAction(AccessibleAction.FIRE);
1502             }
1503             return;
1504         }
1505         if (role == AccessibleRole.TREE_TABLE_CELL) {
1506             Accessible row = getRow();
1507             if (row != null) row.executeAction(AccessibleAction.COLLAPSE);
1508             return;
1509         }
1510         executeAction(AccessibleAction.COLLAPSE);
1511     }
1512 
1513     private void Expand() {
1514         if (isDisposed()) return;
1515         AccessibleRole role = (AccessibleRole)getAttribute(ROLE);
1516         if (role == AccessibleRole.TOOL_BAR) {
1517             Node button = (Node)getAttribute(OVERFLOW_BUTTON);
1518             if (button != null) {
1519                 getAccessible(button).executeAction(AccessibleAction.FIRE);
1520             }
1521             return;
1522         }
1523         if (role == AccessibleRole.TREE_TABLE_CELL) {
1524             Accessible row = getRow();
1525             if (row != null) row.executeAction(AccessibleAction.EXPAND);
1526             return;
1527         }
1528         executeAction(AccessibleAction.EXPAND);
1529     }
1530 
1531     private int get_ExpandCollapseState() {
1532         if (isDisposed()) return 0;
1533 
1534         AccessibleRole role = (AccessibleRole)getAttribute(ROLE);
1535         if (role == AccessibleRole.TOOL_BAR) {
1536             Node button = (Node)getAttribute(OVERFLOW_BUTTON);
1537             if (button != null) {
1538                 boolean visible = Boolean.TRUE.equals(getAccessible(button).getAttribute(VISIBLE));
1539                 return visible ? ExpandCollapseState_Collapsed : ExpandCollapseState_Expanded;
1540             }
1541         }
1542 
1543         if (role == AccessibleRole.TREE_TABLE_CELL) {
1544             Accessible row = getRow();
1545             if (row == null) return ExpandCollapseState_LeafNode;
1546             Object o = row.getAttribute(LEAF);
1547             if (Boolean.TRUE.equals(o)) return ExpandCollapseState_LeafNode;
1548             o = row.getAttribute(EXPANDED);
1549             boolean isExpanded = Boolean.TRUE.equals(o);
1550             return isExpanded ? ExpandCollapseState_Expanded : ExpandCollapseState_Collapsed;
1551         }
1552 
1553         /* 
1554          * We ask if the accessible is a leaf for the TreeItem case where
1555          * we want to return that it is a leaf node. In other cases
1556          * (e.g. ComboBox) this will return false which means the ComboBox is
1557          * not a leaf and the final return statement falls through to returning
1558          * either expanded or collapsed, as expected.
1559          */
1560         Object o = getAttribute(LEAF);
1561         if (Boolean.TRUE.equals(o)) return ExpandCollapseState_LeafNode;
1562 
1563         o = getAttribute(EXPANDED);
1564         boolean isExpanded = Boolean.TRUE.equals(o);
1565         return isExpanded ? ExpandCollapseState_Expanded : ExpandCollapseState_Collapsed;
1566     }
1567 
1568     /***********************************************/
1569     /*             ITransformProvider              */
1570     /***********************************************/
1571     private boolean get_CanMove() {
1572         return false;
1573     }
1574 
1575     private boolean get_CanResize() {
1576         return false;
1577     }
1578 
1579     private boolean get_CanRotate() {
1580         return false;
1581     }
1582 
1583     private void Move(double x, double y) {
1584     }
1585 
1586     private void Resize(double width, double height) {
1587     }
1588 
1589     private void Rotate(double degrees) {
1590     }
1591 
1592     /***********************************************/
1593     /*             IScrollProvider                 */
1594     /***********************************************/
1595     private void Scroll(int horizontalAmount, int verticalAmount) {
1596         if (isDisposed()) return;
1597 
1598         /* dealing with vertical scroll first */
1599         if (get_VerticallyScrollable()) {
1600             Node vsb = (Node)getAttribute(VERTICAL_SCROLLBAR);
1601             Accessible vsba = getAccessible(vsb);
1602             if (vsba == null) return;
1603             switch (verticalAmount) {
1604                 case ScrollAmount_LargeIncrement:
1605                     vsba.executeAction(AccessibleAction.BLOCK_INCREMENT);
1606                     break;
1607                 case ScrollAmount_SmallIncrement:
1608                     vsba.executeAction(AccessibleAction.INCREMENT);
1609                     break;
1610                 case ScrollAmount_LargeDecrement:
1611                     vsba.executeAction(AccessibleAction.BLOCK_DECREMENT);
1612                     break;
1613                 case ScrollAmount_SmallDecrement:
1614                     vsba.executeAction(AccessibleAction.DECREMENT);
1615                     break;
1616                 default:
1617             }
1618         }
1619 
1620         /* now dealing with horizontal scroll */
1621         if (get_HorizontallyScrollable()) {
1622             Node hsb = (Node)getAttribute(HORIZONTAL_SCROLLBAR);
1623             Accessible hsba = getAccessible(hsb);
1624             if (hsba == null) return;
1625             switch (horizontalAmount) {
1626                 case ScrollAmount_LargeIncrement:
1627                     hsba.executeAction(AccessibleAction.BLOCK_INCREMENT);
1628                     break;
1629                 case ScrollAmount_SmallIncrement:
1630                     hsba.executeAction(AccessibleAction.INCREMENT);
1631                     break;
1632                 case ScrollAmount_LargeDecrement:
1633                     hsba.executeAction(AccessibleAction.BLOCK_DECREMENT);
1634                     break;
1635                 case ScrollAmount_SmallDecrement:
1636                     hsba.executeAction(AccessibleAction.DECREMENT);
1637                     break;
1638                 default:
1639             }
1640         }
1641     }
1642 
1643     private void SetScrollPercent(double horizontalPercent, double verticalPercent) {
1644         if (isDisposed()) return;
1645 
1646         /* dealing with vertical scroll first */
1647         if (verticalPercent != UIA_ScrollPatternNoScroll && get_VerticallyScrollable()) {
1648             Node vsb = (Node)getAttribute(VERTICAL_SCROLLBAR);
1649             Accessible acc = getAccessible(vsb);
1650             if (acc == null) return;
1651             Double min = (Double)acc.getAttribute(MIN_VALUE);
1652             Double max = (Double)acc.getAttribute(MAX_VALUE);
1653             if (min != null && max != null) {
1654                 acc.executeAction(AccessibleAction.SET_VALUE, (max-min)*(verticalPercent/100)+min);
1655             }
1656         }
1657 
1658         /* now dealing with horizontal scroll */
1659         if (horizontalPercent != UIA_ScrollPatternNoScroll && get_HorizontallyScrollable()) {
1660             Node hsb = (Node)getAttribute(HORIZONTAL_SCROLLBAR);
1661             Accessible acc = getAccessible(hsb);
1662             if (acc == null) return;
1663             Double min = (Double)acc.getAttribute(MIN_VALUE);
1664             Double max = (Double)acc.getAttribute(MAX_VALUE);
1665             if (min != null && max != null) {
1666                 acc.executeAction(AccessibleAction.SET_VALUE, (max-min)*(horizontalPercent/100)+min);
1667             }
1668         }
1669     }
1670 
1671     private boolean get_HorizontallyScrollable() {
1672         if (isDisposed()) return false;
1673 
1674         Node hsb = (Node)getAttribute(HORIZONTAL_SCROLLBAR);
1675         if (hsb == null) return false;
1676 
1677         Boolean visible = (Boolean)getAccessible(hsb).getAttribute(VISIBLE);
1678         return Boolean.TRUE.equals(visible);
1679     }
1680 
1681     private double get_HorizontalScrollPercent() {
1682         if (isDisposed()) return 0;
1683 
1684         if (!get_HorizontallyScrollable()) {
1685             return UIA_ScrollPatternNoScroll;
1686         }
1687 
1688         Node hsb = (Node) getAttribute(HORIZONTAL_SCROLLBAR);
1689         if (hsb != null) {
1690             /* Windows expects a percentage between 0 and 100 */
1691             Accessible hsba = getAccessible(hsb);
1692             Double value = (Double)hsba.getAttribute(VALUE);
1693             if (value == null) return 0;
1694             Double max = (Double)hsba.getAttribute(MAX_VALUE);
1695             if (max == null) return 0;
1696             Double min = (Double)hsba.getAttribute(MIN_VALUE);
1697             if (min == null) return 0;
1698             return (100 * (value - min)) / (max - min);
1699         }
1700 
1701         return 0;
1702     }
1703 
1704     private double get_HorizontalViewSize() {
1705         if (isDisposed()) return 0;
1706         if (!get_HorizontallyScrollable()) return 100; /* MSDN spec */
1707         Node content = (Node) getAttribute(CONTENTS);
1708         if (content == null) return 100;
1709         Bounds contentBounds = (Bounds)getAccessible(content).getAttribute(BOUNDS);
1710         if (contentBounds == null) return 0;
1711         Bounds scrollPaneBounds = (Bounds)getAttribute(BOUNDS);
1712         if (scrollPaneBounds == null) return 0;
1713         return scrollPaneBounds.getWidth() / contentBounds.getWidth() * 100;
1714     }
1715 
1716     private boolean get_VerticallyScrollable() {
1717         if (isDisposed()) return false;
1718 
1719         Node vsb = (Node) getAttribute(VERTICAL_SCROLLBAR);
1720         if (vsb == null) return false;
1721 
1722         Boolean visible = (Boolean)getAccessible(vsb).getAttribute(VISIBLE);
1723         return Boolean.TRUE.equals(visible);
1724     }
1725 
1726     private double get_VerticalScrollPercent() {
1727         if (isDisposed()) return 0;
1728 
1729         if (!get_VerticallyScrollable()) {
1730             return UIA_ScrollPatternNoScroll;
1731         }
1732 
1733         Node vsb = (Node) getAttribute(AccessibleAttribute.VERTICAL_SCROLLBAR);
1734         if (vsb != null) {
1735             /* Windows expects a percentage between 0 and 100 */
1736             Accessible vsba = getAccessible(vsb);
1737             Double value = (Double)vsba.getAttribute(VALUE);
1738             if (value == null) return 0;
1739             Double max = (Double)vsba.getAttribute(MAX_VALUE);
1740             if (max == null) return 0;
1741             Double min = (Double)vsba.getAttribute(MIN_VALUE);
1742             if (min == null) return 0;
1743             return (100 * (value - min)) / (max - min);
1744         }
1745 
1746         return 0;
1747     }
1748 
1749     private double get_VerticalViewSize() {
1750         if (isDisposed()) return 0;
1751         if (!get_VerticallyScrollable()) return 100;
1752 
1753         double contentHeight = 0;
1754 
1755         Bounds scrollPaneBounds = (Bounds) getAttribute(BOUNDS);
1756         if (scrollPaneBounds == null) return 0;
1757         double scrollPaneHeight = scrollPaneBounds.getHeight();
1758 
1759         AccessibleRole role = (AccessibleRole) getAttribute(ROLE);
1760         if (role == null) return 0;
1761         if (role == AccessibleRole.SCROLL_PANE) {
1762             Node content = (Node) getAttribute(CONTENTS);
1763             if (content != null) {
1764                 Bounds contentBounds = (Bounds)getAccessible(content).getAttribute(BOUNDS);
1765                 contentHeight = contentBounds == null ? 0 : contentBounds.getHeight();
1766             }
1767         } else {
1768             Integer itemCount = 0;
1769             switch (role) {
1770                 case LIST_VIEW:
1771                     itemCount = (Integer) getAttribute(ITEM_COUNT);
1772                     break;
1773                 case TABLE_VIEW:
1774                 case TREE_VIEW:
1775                 case TREE_TABLE_VIEW:
1776                     itemCount = (Integer) getAttribute(ROW_COUNT);
1777                     break;
1778                 default:
1779             }
1780 
1781             /*
1782              * Do a quick calculation to approximate the height of the
1783              * content area by assuming a fixed height multiplied by the number
1784              * of items. The default height we use is 24px, which is the default
1785              * height as specified in com.sun.javafx.scene.control.skin.CellSkinBase.
1786              */
1787             contentHeight = itemCount == null ? 0 : itemCount * 24;
1788         }
1789 
1790         return contentHeight == 0 ? 0 : scrollPaneHeight / contentHeight * 100;
1791     }
1792 
1793     /***********************************************/
1794     /*             IScrollItemProvider             */
1795     /***********************************************/
1796     private void ScrollIntoView() {
1797         if (isDisposed()) return;
1798         AccessibleRole role = (AccessibleRole)getAttribute(ROLE);
1799         if (role == null) return;
1800         Accessible container = getContainer();
1801         if (container == null) return;
1802         Node item = null;
1803         switch (role) {
1804             case LIST_ITEM: {
1805                 Integer index = (Integer)getAttribute(INDEX);
1806                 if (index != null) {
1807                     item = (Node)container.getAttribute(ITEM_AT_INDEX, index);
1808                 }
1809                 break;
1810             }
1811             case TREE_ITEM: {
1812                 Integer index = (Integer)getAttribute(INDEX);
1813                 if (index != null) {
1814                     item = (Node)container.getAttribute(ROW_AT_INDEX, index);
1815                 }
1816                 break;
1817             }
1818             case TABLE_CELL:
1819             case TREE_TABLE_CELL: {
1820                 Integer rowIndex = (Integer)getAttribute(ROW_INDEX);
1821                 Integer columnIndex = (Integer)getAttribute(COLUMN_INDEX);
1822                 if (rowIndex != null && columnIndex != null) {
1823                     item = (Node)container.getAttribute(CELL_AT_ROW_COLUMN, rowIndex, columnIndex);
1824                 }
1825                 break;
1826             }
1827             default:
1828         }
1829         if (item != null) {
1830             container.executeAction(AccessibleAction.SHOW_ITEM, item);
1831         }
1832     }
1833 }