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