1 /*
   2  * Copyright (c) 2005, 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 package javax.swing.plaf.basic;
  26 
  27 import java.awt.Toolkit;
  28 import java.awt.event.*;
  29 import java.awt.dnd.DragSource;
  30 import javax.swing.*;
  31 import sun.awt.dnd.SunDragSourceContextPeer;
  32 import sun.awt.AppContext;
  33 
  34 /**
  35  * Drag gesture recognition support for classes that have a
  36  * <code>TransferHandler</code>. The gesture for a drag in this class is a mouse
  37  * press followed by movement by <code>DragSource.getDragThreshold()</code>
  38  * pixels. An instance of this class is maintained per AppContext, and the
  39  * public static methods call into the appropriate instance.
  40  *
  41  * @author Shannon Hickey
  42  */
  43 class DragRecognitionSupport {
  44     private int motionThreshold;
  45     private MouseEvent dndArmedEvent;
  46     private JComponent component;
  47 
  48     /**
  49      * This interface allows us to pass in a handler to mouseDragged,
  50      * so that we can be notified immediately before a drag begins.
  51      */
  52     public static interface BeforeDrag {
  53         public void dragStarting(MouseEvent me);
  54     }
  55 
  56     /**
  57      * Returns the DragRecognitionSupport for the caller's AppContext.
  58      */
  59     private static DragRecognitionSupport getDragRecognitionSupport() {
  60         DragRecognitionSupport support =
  61             (DragRecognitionSupport)AppContext.getAppContext().
  62                 get(DragRecognitionSupport.class);
  63 
  64         if (support == null) {
  65             support = new DragRecognitionSupport();
  66             AppContext.getAppContext().put(DragRecognitionSupport.class, support);
  67         }
  68 
  69         return support;
  70     }
  71 
  72     /**
  73      * Returns whether or not the event is potentially part of a drag sequence.
  74      */
  75     public static boolean mousePressed(MouseEvent me) {
  76         return getDragRecognitionSupport().mousePressedImpl(me);
  77     }
  78 
  79     /**
  80      * If a dnd recognition has been going on, return the MouseEvent
  81      * that started the recognition. Otherwise, return null.
  82      */
  83     public static MouseEvent mouseReleased(MouseEvent me) {
  84         return getDragRecognitionSupport().mouseReleasedImpl(me);
  85     }
  86 
  87     /**
  88      * Returns whether or not a drag gesture recognition is ongoing.
  89      */
  90     public static boolean mouseDragged(MouseEvent me, BeforeDrag bd) {
  91         return getDragRecognitionSupport().mouseDraggedImpl(me, bd);
  92     }
  93 
  94     private void clearState() {
  95         dndArmedEvent = null;
  96         component = null;
  97     }
  98 
  99     private int mapDragOperationFromModifiers(MouseEvent me,
 100                                               TransferHandler th) {
 101 
 102         if (th == null || !SwingUtilities.isLeftMouseButton(me)) {
 103             return TransferHandler.NONE;
 104         }
 105 
 106         return SunDragSourceContextPeer.
 107             convertModifiersToDropAction(me.getModifiersEx(),
 108                                          th.getSourceActions(component));
 109     }
 110 
 111     /**
 112      * Returns whether or not the event is potentially part of a drag sequence.
 113      */
 114     private boolean mousePressedImpl(MouseEvent me) {
 115         component = (JComponent)me.getSource();
 116 
 117         if (mapDragOperationFromModifiers(me, component.getTransferHandler())
 118                 != TransferHandler.NONE) {
 119 
 120             motionThreshold = DragSource.getDragThreshold();
 121             dndArmedEvent = me;
 122             return true;
 123         }
 124 
 125         clearState();
 126         return false;
 127     }
 128 
 129     /**
 130      * If a dnd recognition has been going on, return the MouseEvent
 131      * that started the recognition. Otherwise, return null.
 132      */
 133     private MouseEvent mouseReleasedImpl(MouseEvent me) {
 134         /* no recognition has been going on */
 135         if (dndArmedEvent == null) {
 136             return null;
 137         }
 138 
 139         MouseEvent retEvent = null;
 140 
 141         if (me.getSource() == component) {
 142             retEvent = dndArmedEvent;
 143         } // else component has changed unexpectedly, so return null
 144 
 145         clearState();
 146         return retEvent;
 147     }
 148 
 149     /**
 150      * Returns whether or not a drag gesture recognition is ongoing.
 151      */
 152     private boolean mouseDraggedImpl(MouseEvent me, BeforeDrag bd) {
 153         /* no recognition is in progress */
 154         if (dndArmedEvent == null) {
 155             return false;
 156         }
 157 
 158         /* component has changed unexpectedly, so bail */
 159         if (me.getSource() != component) {
 160             clearState();
 161             return false;
 162         }
 163 
 164         int dx = Math.abs(me.getX() - dndArmedEvent.getX());
 165         int dy = Math.abs(me.getY() - dndArmedEvent.getY());
 166         if ((dx > motionThreshold) || (dy > motionThreshold)) {
 167             TransferHandler th = component.getTransferHandler();
 168             int action = mapDragOperationFromModifiers(me, th);
 169             if (action != TransferHandler.NONE) {
 170                 /* notify the BeforeDrag instance */
 171                 if (bd != null) {
 172                     bd.dragStarting(dndArmedEvent);
 173                 }
 174                 th.exportAsDrag(component, dndArmedEvent, action);
 175                 clearState();
 176             }
 177         }
 178 
 179         return true;
 180     }
 181 }