1 /* 2 * Copyright (c) 2011, 2013, 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.apple.eawt.event; 27 28 import sun.awt.SunToolkit; 29 30 import java.awt.*; 31 import java.util.*; 32 import java.util.List; 33 34 import javax.swing.*; 35 36 import java.lang.annotation.Native; 37 38 final class GestureHandler { 39 private static final String CLIENT_PROPERTY = "com.apple.eawt.event.internalGestureHandler"; 40 41 // native constants for the supported types of high-level gestures 42 @Native static final int PHASE = 1; 43 @Native static final int ROTATE = 2; 44 @Native static final int MAGNIFY = 3; 45 @Native static final int SWIPE = 4; 46 47 // installs a private instance of GestureHandler, if necessary addGestureListenerTo(final JComponent component, final GestureListener listener)48 static void addGestureListenerTo(final JComponent component, final GestureListener listener) { 49 final Object value = component.getClientProperty(CLIENT_PROPERTY); 50 if (value instanceof GestureHandler) { 51 ((GestureHandler)value).addListener(listener); 52 return; 53 } 54 55 if (value != null) return; // some other garbage is in our client property 56 57 final GestureHandler newHandler = new GestureHandler(); 58 newHandler.addListener(listener); 59 component.putClientProperty(CLIENT_PROPERTY, newHandler); 60 } 61 62 // asks the installed GestureHandler to remove it's listener (does not uninstall the GestureHandler) removeGestureListenerFrom(final JComponent component, final GestureListener listener)63 static void removeGestureListenerFrom(final JComponent component, final GestureListener listener) { 64 final Object value = component.getClientProperty(CLIENT_PROPERTY); 65 if (!(value instanceof GestureHandler)) return; 66 ((GestureHandler)value).removeListener(listener); 67 } 68 69 70 // called from native - finds the deepest component with an installed GestureHandler, 71 // creates a single event, and dispatches it to a recursive notifier handleGestureFromNative(final Window window, final int type, final double x, final double y, final double a, final double b)72 static void handleGestureFromNative(final Window window, final int type, final double x, final double y, final double a, final double b) { 73 if (window == null) return; // should never happen... 74 75 SunToolkit.executeOnEventHandlerThread(window, new Runnable() { 76 public void run() { 77 final Component component = SwingUtilities.getDeepestComponentAt(window, (int)x, (int)y); 78 79 final PerComponentNotifier firstNotifier; 80 if (component instanceof RootPaneContainer) { 81 firstNotifier = getNextNotifierForComponent(((RootPaneContainer)component).getRootPane()); 82 } else { 83 firstNotifier = getNextNotifierForComponent(component); 84 } 85 if (firstNotifier == null) return; 86 87 switch (type) { 88 case PHASE: 89 firstNotifier.recursivelyHandlePhaseChange(a, new GesturePhaseEvent()); 90 return; 91 case ROTATE: 92 firstNotifier.recursivelyHandleRotate(new RotationEvent(a)); 93 return; 94 case MAGNIFY: 95 firstNotifier.recursivelyHandleMagnify(new MagnificationEvent(a)); 96 return; 97 case SWIPE: 98 firstNotifier.recursivelyHandleSwipe(a, b, new SwipeEvent()); 99 return; 100 } 101 } 102 }); 103 } 104 105 106 final List<GesturePhaseListener> phasers = new LinkedList<GesturePhaseListener>(); 107 final List<RotationListener> rotaters = new LinkedList<RotationListener>(); 108 final List<MagnificationListener> magnifiers = new LinkedList<MagnificationListener>(); 109 final List<SwipeListener> swipers = new LinkedList<SwipeListener>(); 110 GestureHandler()111 GestureHandler() { } 112 addListener(final GestureListener listener)113 void addListener(final GestureListener listener) { 114 if (listener instanceof GesturePhaseListener) phasers.add((GesturePhaseListener)listener); 115 if (listener instanceof RotationListener) rotaters.add((RotationListener)listener); 116 if (listener instanceof MagnificationListener) magnifiers.add((MagnificationListener)listener); 117 if (listener instanceof SwipeListener) swipers.add((SwipeListener)listener); 118 } 119 removeListener(final GestureListener listener)120 void removeListener(final GestureListener listener) { 121 phasers.remove(listener); 122 rotaters.remove(listener); 123 magnifiers.remove(listener); 124 swipers.remove(listener); 125 } 126 127 // notifies all listeners in a particular component/handler pair 128 // and recursively notifies up the component hierarchy 129 static class PerComponentNotifier { 130 final Component component; 131 final GestureHandler handler; 132 PerComponentNotifier(final Component component, final GestureHandler handler)133 public PerComponentNotifier(final Component component, final GestureHandler handler) { 134 this.component = component; 135 this.handler = handler; 136 } 137 recursivelyHandlePhaseChange(final double phase, final GesturePhaseEvent e)138 void recursivelyHandlePhaseChange(final double phase, final GesturePhaseEvent e) { 139 for (final GesturePhaseListener listener : handler.phasers) { 140 if (phase < 0) { 141 listener.gestureBegan(e); 142 } else { 143 listener.gestureEnded(e); 144 } 145 if (e.isConsumed()) return; 146 } 147 148 final PerComponentNotifier next = getNextNotifierForComponent(component.getParent()); 149 if (next != null) next.recursivelyHandlePhaseChange(phase, e); 150 } 151 recursivelyHandleRotate(final RotationEvent e)152 void recursivelyHandleRotate(final RotationEvent e) { 153 for (final RotationListener listener : handler.rotaters) { 154 listener.rotate(e); 155 if (e.isConsumed()) return; 156 } 157 158 final PerComponentNotifier next = getNextNotifierForComponent(component.getParent()); 159 if (next != null) next.recursivelyHandleRotate(e); 160 } 161 recursivelyHandleMagnify(final MagnificationEvent e)162 void recursivelyHandleMagnify(final MagnificationEvent e) { 163 for (final MagnificationListener listener : handler.magnifiers) { 164 listener.magnify(e); 165 if (e.isConsumed()) return; 166 } 167 168 final PerComponentNotifier next = getNextNotifierForComponent(component.getParent()); 169 if (next != null) next.recursivelyHandleMagnify(e); 170 } 171 recursivelyHandleSwipe(final double x, final double y, final SwipeEvent e)172 void recursivelyHandleSwipe(final double x, final double y, final SwipeEvent e) { 173 for (final SwipeListener listener : handler.swipers) { 174 if (x < 0) listener.swipedLeft(e); 175 if (x > 0) listener.swipedRight(e); 176 if (y < 0) listener.swipedDown(e); 177 if (y > 0) listener.swipedUp(e); 178 if (e.isConsumed()) return; 179 } 180 181 final PerComponentNotifier next = getNextNotifierForComponent(component.getParent()); 182 if (next != null) next.recursivelyHandleSwipe(x, y, e); 183 } 184 } 185 186 // helper function to get a handler from a Component getHandlerForComponent(final Component c)187 static GestureHandler getHandlerForComponent(final Component c) { 188 if (!(c instanceof JComponent)) return null; 189 final Object value = ((JComponent)c).getClientProperty(CLIENT_PROPERTY); 190 if (!(value instanceof GestureHandler)) return null; 191 return (GestureHandler)value; 192 } 193 194 // recursive helper to find the next component/handler pair getNextNotifierForComponent(final Component c)195 static PerComponentNotifier getNextNotifierForComponent(final Component c) { 196 if (c == null) return null; 197 198 final GestureHandler handler = getHandlerForComponent(c); 199 if (handler != null) { 200 return new PerComponentNotifier(c, handler); 201 } 202 203 return getNextNotifierForComponent(c.getParent()); 204 } 205 } 206