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