1 /* 2 * Copyright (c) 2011, 2016, 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 sun.lwawt.macosx; 27 28 import java.awt.Component; 29 import java.beans.PropertyChangeEvent; 30 import java.beans.PropertyChangeListener; 31 import java.lang.reflect.Field; 32 33 import javax.accessibility.Accessible; 34 import javax.accessibility.AccessibleContext; 35 import javax.swing.JProgressBar; 36 import javax.swing.JSlider; 37 import javax.swing.event.ChangeEvent; 38 import javax.swing.event.ChangeListener; 39 40 import static javax.accessibility.AccessibleContext.ACCESSIBLE_ACTIVE_DESCENDANT_PROPERTY; 41 import static javax.accessibility.AccessibleContext.ACCESSIBLE_CARET_PROPERTY; 42 import static javax.accessibility.AccessibleContext.ACCESSIBLE_SELECTION_PROPERTY; 43 import static javax.accessibility.AccessibleContext.ACCESSIBLE_STATE_PROPERTY; 44 import static javax.accessibility.AccessibleContext.ACCESSIBLE_TEXT_PROPERTY; 45 import javax.accessibility.AccessibleRole; 46 import javax.accessibility.AccessibleState; 47 48 49 class CAccessible extends CFRetainedResource implements Accessible { 50 static Field getNativeAXResourceField() { 51 try { 52 final Field field = AccessibleContext.class.getDeclaredField("nativeAXResource"); 53 field.setAccessible(true); 54 return field; 55 } catch (final Exception e) { 56 e.printStackTrace(); 57 return null; 58 } 59 } 60 61 private static Field nativeAXResourceField = getNativeAXResourceField(); 62 63 public static CAccessible getCAccessible(final Accessible a) { 64 if (a == null) return null; 65 AccessibleContext context = a.getAccessibleContext(); 66 try { 67 final CAccessible cachedCAX = (CAccessible) nativeAXResourceField.get(context); 68 if (cachedCAX != null) return cachedCAX; 69 70 final CAccessible newCAX = new CAccessible(a); 71 nativeAXResourceField.set(context, newCAX); 72 return newCAX; 73 } catch (final Exception e) { 74 e.printStackTrace(); 75 return null; 76 } 77 } 78 79 private static native void unregisterFromCocoaAXSystem(long ptr); 80 private static native void valueChanged(long ptr); 81 private static native void selectedTextChanged(long ptr); 82 private static native void selectionChanged(long ptr); 83 private static native void menuOpened(long ptr); 84 private static native void menuClosed(long ptr); 85 private static native void menuItemSelected(long ptr); 86 87 private Accessible accessible; 88 89 private AccessibleContext activeDescendant; 90 91 private CAccessible(final Accessible accessible) { 92 super(0L, true); // real pointer will be poked in by native 93 94 if (accessible == null) throw new NullPointerException(); 95 this.accessible = accessible; 96 97 if (accessible instanceof Component) { 98 addNotificationListeners((Component)accessible); 99 } 100 } 101 102 @Override 103 protected synchronized void dispose() { 104 if (ptr != 0) unregisterFromCocoaAXSystem(ptr); 105 super.dispose(); 106 } 107 108 @Override 109 public AccessibleContext getAccessibleContext() { 110 return accessible.getAccessibleContext(); 111 } 112 113 public void addNotificationListeners(Component c) { 114 if (c instanceof Accessible) { 115 AccessibleContext ac = ((Accessible)c).getAccessibleContext(); 116 ac.addPropertyChangeListener(new AXChangeNotifier()); 117 } 118 if (c instanceof JProgressBar) { 119 JProgressBar pb = (JProgressBar) c; 120 pb.addChangeListener(new AXProgressChangeNotifier()); 121 } else if (c instanceof JSlider) { 122 JSlider slider = (JSlider) c; 123 slider.addChangeListener(new AXProgressChangeNotifier()); 124 } 125 } 126 127 128 private class AXChangeNotifier implements PropertyChangeListener { 129 130 @Override 131 public void propertyChange(PropertyChangeEvent e) { 132 String name = e.getPropertyName(); 133 if ( ptr != 0 ) { 134 Object newValue = e.getNewValue(); 135 Object oldValue = e.getOldValue(); 136 if (name.compareTo(ACCESSIBLE_CARET_PROPERTY) == 0) { 137 selectedTextChanged(ptr); 138 } else if (name.compareTo(ACCESSIBLE_TEXT_PROPERTY) == 0 ) { 139 valueChanged(ptr); 140 } else if (name.compareTo(ACCESSIBLE_SELECTION_PROPERTY) == 0 ) { 141 selectionChanged(ptr); 142 } else if (name.compareTo(ACCESSIBLE_ACTIVE_DESCENDANT_PROPERTY) == 0 ) { 143 if (newValue instanceof AccessibleContext) { 144 activeDescendant = (AccessibleContext)newValue; 145 } 146 } else if (name.compareTo(ACCESSIBLE_STATE_PROPERTY) == 0) { 147 AccessibleContext thisAC = accessible.getAccessibleContext(); 148 AccessibleRole thisRole = thisAC.getAccessibleRole(); 149 Accessible parentAccessible = thisAC.getAccessibleParent(); 150 AccessibleRole parentRole = null; 151 if (parentAccessible != null) { 152 parentRole = parentAccessible.getAccessibleContext().getAccessibleRole(); 153 } 154 // At least for now don't handle combo box menu state changes. 155 // This may change when later fixing issues which currently 156 // exist for combo boxes, but for now the following is only 157 // for JPopupMenus, not for combobox menus. 158 if (parentRole != AccessibleRole.COMBO_BOX) { 159 if (thisRole == AccessibleRole.POPUP_MENU) { 160 if ( newValue != null && 161 ((AccessibleState)newValue) == AccessibleState.VISIBLE ) { 162 menuOpened(ptr); 163 } else if ( oldValue != null && 164 ((AccessibleState)oldValue) == AccessibleState.VISIBLE ) { 165 menuClosed(ptr); 166 } 167 } else if (thisRole == AccessibleRole.MENU_ITEM) { 168 if ( newValue != null && 169 ((AccessibleState)newValue) == AccessibleState.FOCUSED ) { 170 menuItemSelected(ptr); 171 } 172 } 173 } 174 } 175 } 176 } 177 } 178 179 private class AXProgressChangeNotifier implements ChangeListener { 180 @Override 181 public void stateChanged(ChangeEvent e) { 182 if (ptr != 0) valueChanged(ptr); 183 } 184 } 185 186 static Accessible getSwingAccessible(final Accessible a) { 187 return (a instanceof CAccessible) ? ((CAccessible)a).accessible : a; 188 } 189 190 static AccessibleContext getActiveDescendant(final Accessible a) { 191 return (a instanceof CAccessible) ? ((CAccessible)a).activeDescendant : null; 192 } 193 194 }