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 }