1 /*
   2  * Copyright (c) 2011, 2012, 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.lang.reflect.Field;
  30 
  31 import javax.accessibility.Accessible;
  32 import javax.accessibility.AccessibleContext;
  33 import javax.swing.JProgressBar;
  34 import javax.swing.JSlider;
  35 import javax.swing.event.CaretEvent;
  36 import javax.swing.event.CaretListener;
  37 import javax.swing.event.ChangeEvent;
  38 import javax.swing.event.ChangeListener;
  39 import javax.swing.event.DocumentEvent;
  40 import javax.swing.event.DocumentListener;
  41 import javax.swing.text.JTextComponent;
  42 
  43 
  44 class CAccessible extends CFRetainedResource implements Accessible {
  45     static Field getNativeAXResourceField() {
  46         try {
  47             final Field field = AccessibleContext.class.getDeclaredField("nativeAXResource");
  48             field.setAccessible(true);
  49             return field;
  50         } catch (final Exception e) {
  51             e.printStackTrace();
  52             return null;
  53         }
  54     }
  55 
  56     private static Field nativeAXResourceField = getNativeAXResourceField();
  57 
  58     public static CAccessible getCAccessible(final Accessible a) {
  59         if (a == null) return null;
  60         AccessibleContext context = a.getAccessibleContext();
  61         try {
  62             final CAccessible cachedCAX = (CAccessible) nativeAXResourceField.get(context);
  63             if (cachedCAX != null) return cachedCAX;
  64 
  65             final CAccessible newCAX = new CAccessible(a);
  66             nativeAXResourceField.set(context, newCAX);
  67             return newCAX;
  68         }  catch (final Exception e) {
  69             e.printStackTrace();
  70             return null;
  71         }
  72     }
  73 
  74     private static native void unregisterFromCocoaAXSystem(long ptr);
  75     private static native void valueChanged(long ptr);
  76     private static native void selectionChanged(long ptr);
  77 
  78     private Accessible accessible;
  79 
  80     private CAccessible(final Accessible accessible) {
  81         super(0L, true); // real pointer will be poked in by native
  82 
  83         if (accessible == null) throw new NullPointerException();
  84         this.accessible = accessible;
  85 
  86         if (accessible instanceof Component) {
  87             addNotificationListeners((Component)accessible);
  88         }
  89     }
  90 
  91     @Override
  92     protected synchronized void dispose() {
  93         if (ptr != 0) unregisterFromCocoaAXSystem(ptr);
  94         super.dispose();
  95     }
  96 
  97     @Override
  98     public AccessibleContext getAccessibleContext() {
  99         return accessible.getAccessibleContext();
 100     }
 101 
 102     // currently only supports text components
 103     public void addNotificationListeners(Component c) {
 104         if (c instanceof JTextComponent) {
 105             JTextComponent tc = (JTextComponent) c;
 106             AXTextChangeNotifier listener = new AXTextChangeNotifier();
 107             tc.getDocument().addDocumentListener(listener);
 108             tc.addCaretListener(listener);
 109         }
 110         if (c instanceof JProgressBar) {
 111             JProgressBar pb = (JProgressBar) c;
 112             pb.addChangeListener(new AXProgressChangeNotifier());
 113         } else if (c instanceof JSlider) {
 114             JSlider slider = (JSlider) c;
 115             slider.addChangeListener(new AXProgressChangeNotifier());
 116         }
 117     }
 118 
 119 
 120     private class AXTextChangeNotifier implements DocumentListener, CaretListener {
 121         @Override
 122         public void changedUpdate(DocumentEvent e) {
 123             if (ptr != 0) valueChanged(ptr);
 124         }
 125 
 126         @Override
 127         public void insertUpdate(DocumentEvent e) {
 128             if (ptr != 0) valueChanged(ptr);
 129         }
 130 
 131         @Override
 132         public void removeUpdate(DocumentEvent e) {
 133             if (ptr != 0) valueChanged(ptr);
 134         }
 135 
 136         @Override
 137         public void caretUpdate(CaretEvent e) {
 138             if (ptr != 0) selectionChanged(ptr);
 139         }
 140     }
 141 
 142     private class AXProgressChangeNotifier implements ChangeListener {
 143         public void stateChanged(ChangeEvent e) {
 144             if (ptr != 0) valueChanged(ptr);
 145         }
 146     }
 147 
 148     static Accessible getSwingAccessible(final Accessible a) {
 149         return (a instanceof CAccessible) ? ((CAccessible)a).accessible : a;
 150     }
 151 }