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 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) 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 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 111 GestureHandler() { } 112 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 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 133 public PerComponentNotifier(final Component component, final GestureHandler handler) { 134 this.component = component; 135 this.handler = handler; 136 } 137 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 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 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 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 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 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 }