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                         /* This is the root node of the scene or the scene itself */
 949                         Scene scene = (Scene)getAttribute(SCENE);
 950                         WinAccessible acc = (WinAccessible)getAccessible(scene);
 951                         /* Return 0 if we are already at the scene or if scene is null */
 952                         if (acc == null || acc == this || acc.isDisposed()) return 0L;
 953                         return acc.getNativeAccessible();
 954                     }
 955                 }
 956                 break;
 957             }
 958             case NavigateDirection_NextSibling:
 959             case NavigateDirection_PreviousSibling: {
 960                 if (role == AccessibleRole.LIST_ITEM) {
 961                     return NavigateListView(this, direction);
 962                 }
 963 
 964                 Node parent = (Node)getAttribute(treeCell ? TREE_ITEM_PARENT : PARENT);
 965                 /* 
 966                  * When the parent is NULL is indicates either the root node for the scene
 967                  * or the root tree item in a tree view. Either way, there is no siblings. 
 968                  */
 969                 if (parent != null) {
 970                     WinAccessible parentAccessible = (WinAccessible)getAccessible(parent);
 971                     Function<Integer, Node> getChild;
 972                     int count = 0;
 973                     if (treeCell) {
 974                         Integer result = (Integer)parentAccessible.getAttribute(TREE_ITEM_COUNT);
 975                         if (result == null) return 0;
 976                         count = result;
 977                         getChild = index -> {
 978                             return (Node)parentAccessible.getAttribute(AccessibleAttribute.TREE_ITEM_AT_INDEX, index);
 979                         };
 980                     } else {
 981                         @SuppressWarnings("unchecked")
 982                         ObservableList<Node> children = (ObservableList<Node>)parentAccessible.getAttribute(CHILDREN);
 983                         if (children == null) return 0;
 984                         count = children.size();
 985                         getChild = index -> {
 986                             return children.get(index);
 987                         };
 988                     }
 989 
 990                     int lastIndex = parentAccessible.lastIndex;
 991                     int currentIndex = -1;
 992                     if (0 <= lastIndex && lastIndex < count && getNativeAccessible(getChild.apply(lastIndex)) == peer) {
 993                         currentIndex = lastIndex;
 994                     } else {
 995                         for (int i = 0; i < count; i++) {
 996                             if (getNativeAccessible(getChild.apply(i)) == peer) {
 997                                 currentIndex = i;
 998                                 break;
 999                             }
1000                         }
1001                     }
1002                     if (currentIndex != -1) {
1003                         if (direction == NavigateDirection_NextSibling) {
1004                             currentIndex++;
1005                         } else {
1006                             currentIndex--;
1007                         }
1008                         if (0 <= currentIndex && currentIndex < count) {
1009                             node = getChild.apply(currentIndex);
1010                             parentAccessible.lastIndex = currentIndex;
1011                         }
1012                     }
1013                 }
1014                 break;
1015             }
1016             case NavigateDirection_FirstChild:
1017             case NavigateDirection_LastChild: {
1018                 lastIndex = -1;
1019                 if (role == AccessibleRole.LIST_VIEW) {
1020                     /* Windows 7. Initially the ComboBox contains the ListView,
1021                      * but the ListCells will only be created if one an item is 
1022                      * selected. This causes Narrator to read combo box with
1023                      * zero items. The fix is to ask for first item, which will
1024                      * cause NavigateListView to be used.
1025                      * */
1026                     getAttribute(ITEM_AT_INDEX, 0);
1027                 }
1028                 if (role == AccessibleRole.TREE_VIEW) {
1029                     /* The TreeView only returns the root node as child */
1030                     lastIndex = 0;
1031                     node = (Node)getAttribute(ROW_AT_INDEX, 0);
1032                 } else if (treeCell) {
1033                     Integer count = (Integer)getAttribute(TREE_ITEM_COUNT);
1034                     if (count != null && count > 0) {
1035                         lastIndex = direction == NavigateDirection_FirstChild ? 0 : count - 1;
1036                         node = (Node)getAttribute(TREE_ITEM_AT_INDEX, lastIndex);
1037                     }
1038                 } else {
1039                     @SuppressWarnings("unchecked")
1040                     ObservableList<Node> children = (ObservableList<Node>)getAttribute(CHILDREN);
1041                     if (children != null && children.size() > 0) {
1042                         lastIndex = direction == NavigateDirection_FirstChild ? 0 : children.size() - 1;
1043                         node = children.get(lastIndex);
1044                     }
1045                     if (node != null) {
1046                         role = (AccessibleRole)getAccessible(node).getAttribute(ROLE);
1047                         if (role == AccessibleRole.LIST_ITEM) {
1048                             WinAccessible itemAcc = (WinAccessible)getAccessible(node);
1049                             return NavigateListView(itemAcc, direction);
1050                         }
1051                     }
1052                 }
1053                 break;
1054             }
1055         }
1056         return getNativeAccessible(node);
1057     }
1058 
1059     private void SetFocus() {
1060         if (isDisposed()) return;
1061         executeAction(AccessibleAction.REQUEST_FOCUS);
1062     }
1063 
1064     /***********************************************/
1065     /*     IRawElementProviderFragmentRoot         */
1066     /***********************************************/
1067     private long ElementProviderFromPoint(double x, double y) {
1068         if (isDisposed()) return 0;
1069         Node node = (Node)getAttribute(NODE_AT_POINT, new Point2D(x, y));
1070         return getNativeAccessible(node);
1071     }
1072 
1073     private long GetFocus() {
1074         if (isDisposed()) return 0;
1075         Node node = (Node)getAttribute(FOCUS_NODE);
1076         if (node == null) return 0L;
1077         Node item = (Node)getAccessible(node).getAttribute(FOCUS_ITEM);
1078         if (item != null) return getNativeAccessible(item);
1079         return getNativeAccessible(node);
1080     }
1081 
1082     /***********************************************/
1083     /*     IRawElementProviderAdviseEvents         */
1084     /***********************************************/
1085     private void AdviseEventAdded(int eventId, long propertyIDs) {
1086         /* Implementing IRawElementProviderAdviseEvents ensures
1087          * that the window is announced by Narrator when it first
1088          * opens. No further action is required.
1089          */
1090     }
1091 
1092     private void AdviseEventRemoved(int eventId, long propertyIDs) {
1093         /* See AdviseEventAdded() */
1094     }
1095 
1096     /***********************************************/
1097     /*             IInvokeProvider                 */
1098     /***********************************************/
1099     private void Invoke() {
1100         if (isDisposed()) return;
1101         executeAction(AccessibleAction.FIRE);
1102     }
1103 
1104     /***********************************************/
1105     /*           ISelectionProvider                */
1106     /***********************************************/
1107     private long[] GetSelection() {
1108         if (isDisposed()) return null;
1109 
1110         /* 
1111          * GetSelection() is sent by ISelectionProvider and ITextProvider.
1112          * Check the role before processing message.
1113          */
1114         AccessibleRole role = (AccessibleRole)getAttribute(ROLE);
1115         if (role == null) return null;
1116         switch (role) {
1117             case TREE_TABLE_VIEW:
1118             case TABLE_VIEW:
1119             case TREE_VIEW:
1120             case LIST_VIEW: {
1121                 @SuppressWarnings("unchecked")
1122                 ObservableList<Node> selection = (ObservableList<Node>)getAttribute(SELECTED_ITEMS);
1123                 if (selection != null) {
1124                     return selection.stream().mapToLong(n -> getNativeAccessible(n)).toArray();
1125                 }
1126                 break;
1127             }
1128             case TAB_PANE:
1129             case PAGINATION: {
1130                 Node node = (Node)getAttribute(FOCUS_ITEM);
1131                 if (node != null) {
1132                     return new long[] {getNativeAccessible(node)};
1133                 }
1134                 break;
1135             }
1136             case TEXT_FIELD:
1137             case TEXT_AREA: {
1138                 if (selectionRange == null) {
1139                     selectionRange = new WinTextRangeProvider(this);
1140                 }
1141                 Integer result = (Integer)getAttribute(SELECTION_START);
1142                 int start = result != null ? result : 0;
1143                 int end = -1;
1144                 int length = -1;
1145                 if (start >= 0) {
1146                     result = (Integer)getAttribute(SELECTION_END);
1147                     end = result != null ? result : 0;
1148                     if (end >= start) {
1149                         String string = (String)getAttribute(TEXT);
1150                         length = string != null ? string.length() : 0;
1151                     }
1152                 }
1153                 if (length != -1 && end <= length) {
1154                     selectionRange.setRange(start, end);
1155                 } else {
1156                     selectionRange.setRange(0, 0);
1157                 }
1158                 return new long[] {selectionRange.getNativeProvider()};
1159             }
1160             default:
1161         }
1162         return null;
1163     }
1164 
1165     private boolean get_CanSelectMultiple() {
1166         if (isDisposed()) return false;
1167         AccessibleRole role = (AccessibleRole)getAttribute(ROLE);
1168         if (role != null) {
1169             switch (role) {
1170                 case LIST_VIEW:
1171                 case TABLE_VIEW:
1172                 case TREE_VIEW:
1173                 case TREE_TABLE_VIEW:
1174                     return Boolean.TRUE.equals(getAttribute(MULTIPLE_SELECTION));
1175                 default:
1176             }
1177         }
1178         return false;
1179     }
1180 
1181     private boolean get_IsSelectionRequired() {
1182         if (isDisposed()) return false;
1183         //TODO: this may not be true...
1184         return true;
1185     }
1186 
1187     /***********************************************/
1188     /*           IRangeValueProvider               */
1189     /***********************************************/
1190     private void SetValue(double val) {
1191         if (isDisposed()) return;
1192         AccessibleRole role = (AccessibleRole)getAttribute(ROLE);
1193         if (role != null) {
1194             switch (role) {
1195                 case SLIDER:
1196                 case SCROLL_BAR:
1197                     executeAction(AccessibleAction.SET_VALUE, val);
1198                     break;
1199                 default:
1200             }
1201         }
1202     }
1203 
1204     private double get_Value() {
1205         if (isDisposed()) return 0;
1206         if (Boolean.TRUE.equals(getAttribute(INDETERMINATE))) return 0;
1207         Double value = (Double)getAttribute(VALUE);
1208         return value != null ? value : 0;
1209     }
1210 
1211     /*
1212      * Note that this method is called by the IValueProvider also.
1213      */
1214     private boolean get_IsReadOnly() {
1215         if (isDisposed()) return false;
1216         AccessibleRole role = (AccessibleRole)getAttribute(ROLE);
1217         if (role != null) {
1218             switch (role) {
1219                 case SLIDER: return false;
1220                 case SCROLL_BAR: return true;
1221                 case TEXT_FIELD:
1222                 case TEXT_AREA:
1223                 case COMBO_BOX: return Boolean.FALSE.equals(getAttribute(EDITABLE));
1224                 default:
1225             }
1226         }
1227         return true;
1228     }
1229 
1230     private double get_Maximum() {
1231         if (isDisposed()) return 0;
1232         Double value = (Double)getAttribute(MAX_VALUE);
1233         return value != null ? value : 0;
1234     }
1235 
1236     private double get_Minimum() {
1237         if (isDisposed()) return 0;
1238         Double value = (Double)getAttribute(MIN_VALUE);
1239         return value != null ? value : 0;
1240     }
1241 
1242     private double get_LargeChange() {
1243         if (isDisposed()) return 0;
1244         return 10;//TODO
1245     }
1246 
1247     private double get_SmallChange() {
1248         if (isDisposed()) return 0;
1249         return 3;//TODO
1250     }
1251 
1252     /***********************************************/
1253     /*             IValueProvider                  */
1254     /***********************************************/
1255     private void SetValueString(String val) {
1256         if (isDisposed()) return;
1257         AccessibleRole role = (AccessibleRole)getAttribute(ROLE);
1258         if (role != null) {
1259             switch (role) {
1260                 case TEXT_FIELD:
1261                 case TEXT_AREA:
1262                     executeAction(AccessibleAction.SET_TEXT, val);
1263                     break;
1264                 default:
1265             }
1266         }
1267     }
1268 
1269     private String get_ValueString() {
1270         if (isDisposed()) return null;
1271         return (String)getAttribute(TEXT);
1272     }
1273 
1274     /***********************************************/
1275     /*          ISelectionItemProvider             */
1276     /***********************************************/
1277     private void Select() {
1278         if (isDisposed()) return;
1279         AccessibleRole role = (AccessibleRole)getAttribute(ROLE);
1280         if (role != null) {
1281             switch (role) {
1282                 case PAGE_ITEM:
1283                 case TAB_ITEM:
1284                     executeAction(AccessibleAction.REQUEST_FOCUS);
1285                     break;
1286                 case RADIO_BUTTON:
1287                 case BUTTON:
1288                 case TOGGLE_BUTTON:
1289                 case INCREMENT_BUTTON:
1290                 case DECREMENT_BUTTON:
1291                     executeAction(AccessibleAction.FIRE);
1292                     break;
1293                 case LIST_ITEM:
1294                 case TREE_ITEM:
1295                 case TABLE_CELL:
1296                 case TREE_TABLE_CELL:
1297                     changeSelection(true, true);
1298                     break;
1299                 default:
1300             }
1301         }
1302     }
1303 
1304     private void AddToSelection() {
1305         if (isDisposed()) return;
1306         changeSelection(true, false);
1307     }
1308 
1309     private void RemoveFromSelection() {
1310         if (isDisposed()) return;
1311         changeSelection(false, false);
1312     }
1313 
1314     private boolean get_IsSelected() {
1315         if (isDisposed()) return false;
1316         return Boolean.TRUE.equals(getAttribute(SELECTED));
1317     }
1318 
1319     private long get_SelectionContainer() {
1320         if (isDisposed()) return 0;
1321         WinAccessible acc = (WinAccessible)getContainer(); 
1322         return acc != null ? acc.getNativeAccessible() : 0L;
1323     }
1324 
1325     /***********************************************/
1326     /*              ITextProvider                  */
1327     /***********************************************/
1328     private long[] GetVisibleRanges() {
1329         if (isDisposed()) return null;
1330         return new long[] {get_DocumentRange()};
1331     }
1332 
1333     private long RangeFromChild(long childElement) {
1334         if (isDisposed()) return 0;
1335         return 0;
1336     }
1337 
1338     private long RangeFromPoint(double x, double y) {
1339         if (isDisposed()) return 0;
1340         Integer offset = (Integer)getAttribute(OFFSET_AT_POINT, new Point2D(x, y));
1341         if (offset != null) {
1342             WinTextRangeProvider range = new WinTextRangeProvider(this);
1343             range.setRange(offset, offset);
1344             return range.getNativeProvider();
1345         }
1346         return 0;
1347     }
1348 
1349     private long get_DocumentRange() {
1350         if (isDisposed()) return 0;
1351         if (documentRange == null) {
1352             documentRange = new WinTextRangeProvider(this);
1353         }
1354         String text = (String)getAttribute(TEXT);
1355         documentRange.setRange(0, text.length());
1356         return documentRange.getNativeProvider();
1357     }
1358 
1359     private int get_SupportedTextSelection() {
1360         if (isDisposed()) return 0;
1361         /* Before this can be done extra API for multiple selection will be required. */
1362 //        if (Boolean.TRUE.equals(getAttribute(MULTIPLE_SELECTION))) {
1363 //            return SupportedTextSelection_Multiple;
1364 //        }
1365         return SupportedTextSelection_Single;
1366     }
1367 
1368     /***********************************************/
1369     /*             IGridProvider                   */
1370     /***********************************************/
1371     private int get_ColumnCount() {
1372         if (isDisposed()) return 0;
1373         Integer count = (Integer)getAttribute(COLUMN_COUNT);
1374 
1375         /* 
1376          * JFX does not require ListView to report column count == 1
1377          * But Windows wants a column count of (at least) 1.
1378          */
1379         return count != null ? count : 1;
1380     }
1381 
1382     private int get_RowCount() {
1383         if (isDisposed()) return 0;
1384         Integer count = (Integer)getAttribute(ROW_COUNT);
1385         return count != null ? count : 0;
1386     }
1387 
1388     private long GetItem(int row, int column) {
1389         if (isDisposed()) return 0;
1390         Node node = (Node)getAttribute(CELL_AT_ROW_COLUMN, row, column);
1391         return getNativeAccessible(node);
1392     }
1393 
1394     /***********************************************/
1395     /*             IGridItemProvider               */
1396     /***********************************************/
1397     private int get_Column() {
1398         if (isDisposed()) return 0;
1399         Integer result = (Integer)getAttribute(COLUMN_INDEX);
1400         return result != null ? result : 0;
1401     }
1402 
1403     private int get_ColumnSpan() {
1404         if (isDisposed()) return 0;
1405         return 1;
1406     }
1407 
1408     private long get_ContainingGrid() {
1409         if (isDisposed()) return 0;
1410         WinAccessible acc = (WinAccessible)getContainer();
1411         return acc != null ? acc.getNativeAccessible() : 0L;
1412     }
1413 
1414     private int get_Row() {
1415         if (isDisposed()) return 0;
1416         Integer result = null;
1417         AccessibleRole role = (AccessibleRole) getAttribute(ROLE);
1418         if (role != null) {
1419             switch (role) {
1420                 case TABLE_ROW:
1421                 case TREE_TABLE_ROW:
1422                 case LIST_ITEM: result = (Integer)getAttribute(INDEX); break;
1423                 case TREE_TABLE_CELL:
1424                 case TABLE_CELL: result = (Integer)getAttribute(ROW_INDEX); break;
1425                 default:
1426             }
1427         }
1428         return result != null ? result : 0;
1429     }
1430 
1431     private int get_RowSpan() {
1432         if (isDisposed()) return 0;
1433         return 1;
1434     }
1435 
1436     /***********************************************/
1437     /*               ITableProvider                */
1438     /***********************************************/
1439     private long[] GetColumnHeaders() {
1440         if (isDisposed()) return null;
1441        /* No support in JFX to return all columns with a single call */
1442        return null;
1443     }
1444 
1445     private long[] GetRowHeaders() {
1446         if (isDisposed()) return null;
1447        /* No row header support on JFX */
1448        return null;
1449     }
1450 
1451     private int get_RowOrColumnMajor() {
1452         if (isDisposed()) return 0;
1453         return RowOrColumnMajor_RowMajor;
1454     }
1455 
1456     /***********************************************/
1457     /*             ITableItemProvider              */
1458     /***********************************************/
1459     private long[] GetColumnHeaderItems() {
1460         if (isDisposed()) return null;
1461        Integer columnIndex = (Integer)getAttribute(COLUMN_INDEX);
1462        if (columnIndex == null) return null;
1463        Accessible acc = getContainer();
1464        if (acc == null) return null;
1465        Node column = (Node)acc.getAttribute(COLUMN_AT_INDEX, columnIndex);
1466        if (column == null) return null;
1467        return new long[] {getNativeAccessible(column)};
1468     }
1469 
1470     private long[] GetRowHeaderItems() {
1471         if (isDisposed()) return null;
1472         /* No row header support on JFX */
1473        return null;
1474     }
1475 
1476     /***********************************************/
1477     /*             IToggleProvider                 */
1478     /***********************************************/
1479     private void Toggle() {
1480         if (isDisposed()) return;
1481         executeAction(AccessibleAction.FIRE);
1482     }
1483 
1484     private int get_ToggleState() {
1485         if (isDisposed()) return 0;
1486         if (Boolean.TRUE.equals(getAttribute(INDETERMINATE))) {
1487             return ToggleState_Indeterminate;
1488         }
1489         boolean selected = Boolean.TRUE.equals(getAttribute(SELECTED));
1490         return selected ? ToggleState_On : ToggleState_Off;
1491     }
1492 
1493     /***********************************************/
1494     /*             IExpandCollapseProvider         */
1495     /***********************************************/
1496     private void Collapse() {
1497         if (isDisposed()) return;
1498         AccessibleRole role = (AccessibleRole)getAttribute(ROLE);
1499         if (role == AccessibleRole.TOOL_BAR) {
1500             Node button = (Node)getAttribute(OVERFLOW_BUTTON);
1501             if (button != null) {
1502                 getAccessible(button).executeAction(AccessibleAction.FIRE);
1503             }
1504             return;
1505         }
1506         if (role == AccessibleRole.TREE_TABLE_CELL) {
1507             Accessible row = getRow();
1508             if (row != null) row.executeAction(AccessibleAction.COLLAPSE);
1509             return;
1510         }
1511         executeAction(AccessibleAction.COLLAPSE);
1512     }
1513 
1514     private void Expand() {
1515         if (isDisposed()) return;
1516         AccessibleRole role = (AccessibleRole)getAttribute(ROLE);
1517         if (role == AccessibleRole.TOOL_BAR) {
1518             Node button = (Node)getAttribute(OVERFLOW_BUTTON);
1519             if (button != null) {
1520                 getAccessible(button).executeAction(AccessibleAction.FIRE);
1521             }
1522             return;
1523         }
1524         if (role == AccessibleRole.TREE_TABLE_CELL) {
1525             Accessible row = getRow();
1526             if (row != null) row.executeAction(AccessibleAction.EXPAND);
1527             return;
1528         }
1529         executeAction(AccessibleAction.EXPAND);
1530     }
1531 
1532     private int get_ExpandCollapseState() {
1533         if (isDisposed()) return 0;
1534 
1535         AccessibleRole role = (AccessibleRole)getAttribute(ROLE);
1536         if (role == AccessibleRole.TOOL_BAR) {
1537             Node button = (Node)getAttribute(OVERFLOW_BUTTON);
1538             if (button != null) {
1539                 boolean visible = Boolean.TRUE.equals(getAccessible(button).getAttribute(VISIBLE));
1540                 return visible ? ExpandCollapseState_Collapsed : ExpandCollapseState_Expanded;
1541             }
1542         }
1543 
1544         if (role == AccessibleRole.TREE_TABLE_CELL) {
1545             Accessible row = getRow();
1546             if (row == null) return ExpandCollapseState_LeafNode;
1547             Object o = row.getAttribute(LEAF);
1548             if (Boolean.TRUE.equals(o)) return ExpandCollapseState_LeafNode;
1549             o = row.getAttribute(EXPANDED);
1550             boolean isExpanded = Boolean.TRUE.equals(o);
1551             return isExpanded ? ExpandCollapseState_Expanded : ExpandCollapseState_Collapsed;
1552         }
1553 
1554         /* 
1555          * We ask if the accessible is a leaf for the TreeItem case where
1556          * we want to return that it is a leaf node. In other cases
1557          * (e.g. ComboBox) this will return false which means the ComboBox is
1558          * not a leaf and the final return statement falls through to returning
1559          * either expanded or collapsed, as expected.
1560          */
1561         Object o = getAttribute(LEAF);
1562         if (Boolean.TRUE.equals(o)) return ExpandCollapseState_LeafNode;
1563 
1564         o = getAttribute(EXPANDED);
1565         boolean isExpanded = Boolean.TRUE.equals(o);
1566         return isExpanded ? ExpandCollapseState_Expanded : ExpandCollapseState_Collapsed;
1567     }
1568 
1569     /***********************************************/
1570     /*             ITransformProvider              */
1571     /***********************************************/
1572     private boolean get_CanMove() {
1573         return false;
1574     }
1575 
1576     private boolean get_CanResize() {
1577         return false;
1578     }
1579 
1580     private boolean get_CanRotate() {
1581         return false;
1582     }
1583 
1584     private void Move(double x, double y) {
1585     }
1586 
1587     private void Resize(double width, double height) {
1588     }
1589 
1590     private void Rotate(double degrees) {
1591     }
1592 
1593     /***********************************************/
1594     /*             IScrollProvider                 */
1595     /***********************************************/
1596     private void Scroll(int horizontalAmount, int verticalAmount) {
1597         if (isDisposed()) return;
1598 
1599         /* dealing with vertical scroll first */
1600         if (get_VerticallyScrollable()) {
1601             Node vsb = (Node)getAttribute(VERTICAL_SCROLLBAR);
1602             Accessible vsba = getAccessible(vsb);
1603             if (vsba == null) return;
1604             switch (verticalAmount) {
1605                 case ScrollAmount_LargeIncrement:
1606                     vsba.executeAction(AccessibleAction.BLOCK_INCREMENT);
1607                     break;
1608                 case ScrollAmount_SmallIncrement:
1609                     vsba.executeAction(AccessibleAction.INCREMENT);
1610                     break;
1611                 case ScrollAmount_LargeDecrement:
1612                     vsba.executeAction(AccessibleAction.BLOCK_DECREMENT);
1613                     break;
1614                 case ScrollAmount_SmallDecrement:
1615                     vsba.executeAction(AccessibleAction.DECREMENT);
1616                     break;
1617                 default:
1618             }
1619         }
1620 
1621         /* now dealing with horizontal scroll */
1622         if (get_HorizontallyScrollable()) {
1623             Node hsb = (Node)getAttribute(HORIZONTAL_SCROLLBAR);
1624             Accessible hsba = getAccessible(hsb);
1625             if (hsba == null) return;
1626             switch (horizontalAmount) {
1627                 case ScrollAmount_LargeIncrement:
1628                     hsba.executeAction(AccessibleAction.BLOCK_INCREMENT);
1629                     break;
1630                 case ScrollAmount_SmallIncrement:
1631                     hsba.executeAction(AccessibleAction.INCREMENT);
1632                     break;
1633                 case ScrollAmount_LargeDecrement:
1634                     hsba.executeAction(AccessibleAction.BLOCK_DECREMENT);
1635                     break;
1636                 case ScrollAmount_SmallDecrement:
1637                     hsba.executeAction(AccessibleAction.DECREMENT);
1638                     break;
1639                 default:
1640             }
1641         }
1642     }
1643 
1644     private void SetScrollPercent(double horizontalPercent, double verticalPercent) {
1645         if (isDisposed()) return;
1646 
1647         /* dealing with vertical scroll first */
1648         if (verticalPercent != UIA_ScrollPatternNoScroll && get_VerticallyScrollable()) {
1649             Node vsb = (Node)getAttribute(VERTICAL_SCROLLBAR);
1650             Accessible acc = getAccessible(vsb);
1651             if (acc == null) return;
1652             Double min = (Double)acc.getAttribute(MIN_VALUE);
1653             Double max = (Double)acc.getAttribute(MAX_VALUE);
1654             if (min != null && max != null) {
1655                 acc.executeAction(AccessibleAction.SET_VALUE, (max-min)*(verticalPercent/100)+min);
1656             }
1657         }
1658 
1659         /* now dealing with horizontal scroll */
1660         if (horizontalPercent != UIA_ScrollPatternNoScroll && get_HorizontallyScrollable()) {
1661             Node hsb = (Node)getAttribute(HORIZONTAL_SCROLLBAR);
1662             Accessible acc = getAccessible(hsb);
1663             if (acc == null) return;
1664             Double min = (Double)acc.getAttribute(MIN_VALUE);
1665             Double max = (Double)acc.getAttribute(MAX_VALUE);
1666             if (min != null && max != null) {
1667                 acc.executeAction(AccessibleAction.SET_VALUE, (max-min)*(horizontalPercent/100)+min);
1668             }
1669         }
1670     }
1671 
1672     private boolean get_HorizontallyScrollable() {
1673         if (isDisposed()) return false;
1674 
1675         Node hsb = (Node)getAttribute(HORIZONTAL_SCROLLBAR);
1676         if (hsb == null) return false;
1677 
1678         Boolean visible = (Boolean)getAccessible(hsb).getAttribute(VISIBLE);
1679         return Boolean.TRUE.equals(visible);
1680     }
1681 
1682     private double get_HorizontalScrollPercent() {
1683         if (isDisposed()) return 0;
1684 
1685         if (!get_HorizontallyScrollable()) {
1686             return UIA_ScrollPatternNoScroll;
1687         }
1688 
1689         Node hsb = (Node) getAttribute(HORIZONTAL_SCROLLBAR);
1690         if (hsb != null) {
1691             /* Windows expects a percentage between 0 and 100 */
1692             Accessible hsba = getAccessible(hsb);
1693             Double value = (Double)hsba.getAttribute(VALUE);
1694             if (value == null) return 0;
1695             Double max = (Double)hsba.getAttribute(MAX_VALUE);
1696             if (max == null) return 0;
1697             Double min = (Double)hsba.getAttribute(MIN_VALUE);
1698             if (min == null) return 0;
1699             return (100 * (value - min)) / (max - min);
1700         }
1701 
1702         return 0;
1703     }
1704 
1705     private double get_HorizontalViewSize() {
1706         if (isDisposed()) return 0;
1707         if (!get_HorizontallyScrollable()) return 100; /* MSDN spec */
1708         Node content = (Node) getAttribute(CONTENTS);
1709         if (content == null) return 100;
1710         Bounds contentBounds = (Bounds)getAccessible(content).getAttribute(BOUNDS);
1711         if (contentBounds == null) return 0;
1712         Bounds scrollPaneBounds = (Bounds)getAttribute(BOUNDS);
1713         if (scrollPaneBounds == null) return 0;
1714         return scrollPaneBounds.getWidth() / contentBounds.getWidth() * 100;
1715     }
1716 
1717     private boolean get_VerticallyScrollable() {
1718         if (isDisposed()) return false;
1719 
1720         Node vsb = (Node) getAttribute(VERTICAL_SCROLLBAR);
1721         if (vsb == null) return false;
1722 
1723         Boolean visible = (Boolean)getAccessible(vsb).getAttribute(VISIBLE);
1724         return Boolean.TRUE.equals(visible);
1725     }
1726 
1727     private double get_VerticalScrollPercent() {
1728         if (isDisposed()) return 0;
1729 
1730         if (!get_VerticallyScrollable()) {
1731             return UIA_ScrollPatternNoScroll;
1732         }
1733 
1734         Node vsb = (Node) getAttribute(AccessibleAttribute.VERTICAL_SCROLLBAR);
1735         if (vsb != null) {
1736             /* Windows expects a percentage between 0 and 100 */
1737             Accessible vsba = getAccessible(vsb);
1738             Double value = (Double)vsba.getAttribute(VALUE);
1739             if (value == null) return 0;
1740             Double max = (Double)vsba.getAttribute(MAX_VALUE);
1741             if (max == null) return 0;
1742             Double min = (Double)vsba.getAttribute(MIN_VALUE);
1743             if (min == null) return 0;
1744             return (100 * (value - min)) / (max - min);
1745         }
1746 
1747         return 0;
1748     }
1749 
1750     private double get_VerticalViewSize() {
1751         if (isDisposed()) return 0;
1752         if (!get_VerticallyScrollable()) return 100;
1753 
1754         double contentHeight = 0;
1755 
1756         Bounds scrollPaneBounds = (Bounds) getAttribute(BOUNDS);
1757         if (scrollPaneBounds == null) return 0;
1758         double scrollPaneHeight = scrollPaneBounds.getHeight();
1759 
1760         AccessibleRole role = (AccessibleRole) getAttribute(ROLE);
1761         if (role == null) return 0;
1762         if (role == AccessibleRole.SCROLL_PANE) {
1763             Node content = (Node) getAttribute(CONTENTS);
1764             if (content != null) {
1765                 Bounds contentBounds = (Bounds)getAccessible(content).getAttribute(BOUNDS);
1766                 contentHeight = contentBounds == null ? 0 : contentBounds.getHeight();
1767             }
1768         } else {
1769             Integer itemCount = 0;
1770             switch (role) {
1771                 case LIST_VIEW:
1772                     itemCount = (Integer) getAttribute(ITEM_COUNT);
1773                     break;
1774                 case TABLE_VIEW:
1775                 case TREE_VIEW:
1776                 case TREE_TABLE_VIEW:
1777                     itemCount = (Integer) getAttribute(ROW_COUNT);
1778                     break;
1779                 default:
1780             }
1781 
1782             /*
1783              * Do a quick calculation to approximate the height of the
1784              * content area by assuming a fixed height multiplied by the number
1785              * of items. The default height we use is 24px, which is the default
1786              * height as specified in com.sun.javafx.scene.control.skin.CellSkinBase.
1787              */
1788             contentHeight = itemCount == null ? 0 : itemCount * 24;
1789         }
1790 
1791         return contentHeight == 0 ? 0 : scrollPaneHeight / contentHeight * 100;
1792     }
1793 
1794     /***********************************************/
1795     /*             IScrollItemProvider             */
1796     /***********************************************/
1797     private void ScrollIntoView() {
1798         if (isDisposed()) return;
1799         AccessibleRole role = (AccessibleRole)getAttribute(ROLE);
1800         if (role == null) return;
1801         Accessible container = getContainer();
1802         if (container == null) return;
1803         Node item = null;
1804         switch (role) {
1805             case LIST_ITEM: {
1806                 Integer index = (Integer)getAttribute(INDEX);
1807                 if (index != null) {
1808                     item = (Node)container.getAttribute(ITEM_AT_INDEX, index);
1809                 }
1810                 break;
1811             }
1812             case TREE_ITEM: {
1813                 Integer index = (Integer)getAttribute(INDEX);
1814                 if (index != null) {
1815                     item = (Node)container.getAttribute(ROW_AT_INDEX, index);
1816                 }
1817                 break;
1818             }
1819             case TABLE_CELL:
1820             case TREE_TABLE_CELL: {
1821                 Integer rowIndex = (Integer)getAttribute(ROW_INDEX);
1822                 Integer columnIndex = (Integer)getAttribute(COLUMN_INDEX);
1823                 if (rowIndex != null && columnIndex != null) {
1824                     item = (Node)container.getAttribute(CELL_AT_ROW_COLUMN, rowIndex, columnIndex);
1825                 }
1826                 break;
1827             }
1828             default:
1829         }
1830         if (item != null) {
1831             container.executeAction(AccessibleAction.SHOW_ITEM, item);
1832         }
1833     }
1834 }