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 }