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