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