1 /*
   2  * Copyright (c) 2000, 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 sun.awt.dnd;
  27 
  28 import java.awt.AWTEvent;
  29 import java.awt.Component;
  30 import java.awt.Cursor;
  31 import java.awt.EventQueue;
  32 import java.awt.Image;
  33 import java.awt.Point;
  34 
  35 import java.awt.datatransfer.Transferable;
  36 
  37 import java.awt.dnd.DnDConstants;
  38 import java.awt.dnd.DragSourceContext;
  39 import java.awt.dnd.DragSourceEvent;
  40 import java.awt.dnd.DragSourceDropEvent;
  41 import java.awt.dnd.DragSourceDragEvent;
  42 import java.awt.dnd.DragGestureEvent;
  43 import java.awt.dnd.InvalidDnDOperationException;
  44 
  45 import java.awt.dnd.peer.DragSourceContextPeer;
  46 
  47 import java.awt.event.InputEvent;
  48 import java.awt.event.MouseEvent;
  49 
  50 import java.util.Map;
  51 import java.util.SortedMap;
  52 
  53 import sun.awt.SunToolkit;
  54 import sun.awt.datatransfer.DataTransferer;
  55 import java.awt.datatransfer.DataFlavor;
  56 
  57 
  58 /**
  59  * <p>
  60  * TBC
  61  * </p>
  62  *
  63  * @since JDK1.3.1
  64  *
  65  */
  66 public abstract class SunDragSourceContextPeer implements DragSourceContextPeer {
  67 
  68     private DragGestureEvent  trigger;
  69     private Component         component;
  70     private Cursor            cursor;
  71     private Image             dragImage;
  72     private Point             dragImageOffset;
  73     private long              nativeCtxt;
  74     private DragSourceContext dragSourceContext;
  75     private int               sourceActions;
  76 
  77     private static boolean    dragDropInProgress = false;
  78     private static boolean    discardingMouseEvents = false;
  79 
  80     /*
  81      * dispatch constants
  82      */
  83 
  84     protected final static int DISPATCH_ENTER   = 1;
  85     protected final static int DISPATCH_MOTION  = 2;
  86     protected final static int DISPATCH_CHANGED = 3;
  87     protected final static int DISPATCH_EXIT    = 4;
  88     protected final static int DISPATCH_FINISH  = 5;
  89     protected final static int DISPATCH_MOUSE_MOVED  = 6;
  90 
  91     /**
  92      * construct a new SunDragSourceContextPeer
  93      */
  94 
  95     public SunDragSourceContextPeer(DragGestureEvent dge) {
  96         trigger = dge;
  97         if (trigger != null) {
  98             component = trigger.getComponent();
  99         } else {
 100             component = null;
 101         }
 102     }
 103 
 104     /**
 105      * Synchro messages in AWT
 106      */
 107     public void startSecondaryEventLoop(){}
 108     public void quitSecondaryEventLoop(){}
 109 
 110     /**
 111      * initiate a DnD operation ...
 112      */
 113 
 114     public void startDrag(DragSourceContext dsc, Cursor c, Image di, Point p)
 115       throws InvalidDnDOperationException {
 116 
 117         /* Fix for 4354044: don't initiate a drag if event sequence provided by
 118          * DragGestureRecognizer is empty */
 119         if (getTrigger().getTriggerEvent() == null) {
 120             throw new InvalidDnDOperationException("DragGestureEvent has a null trigger");
 121         }
 122 
 123         dragSourceContext = dsc;
 124         cursor            = c;
 125         sourceActions     = getDragSourceContext().getSourceActions();
 126         dragImage         = di;
 127         dragImageOffset   = p;
 128 
 129         Transferable transferable  = getDragSourceContext().getTransferable();
 130         SortedMap<Long,DataFlavor> formatMap = DataTransferer.getInstance().
 131             getFormatsForTransferable(transferable, DataTransferer.adaptFlavorMap
 132                 (getTrigger().getDragSource().getFlavorMap()));
 133         long[] formats = DataTransferer.getInstance().
 134             keysToLongArray(formatMap);
 135         startDrag(transferable, formats, formatMap);
 136 
 137         /*
 138          * Fix for 4613903.
 139          * Filter out all mouse events that are currently on the event queue.
 140          */
 141         discardingMouseEvents = true;
 142         EventQueue.invokeLater(new Runnable() {
 143                 public void run() {
 144                     discardingMouseEvents = false;
 145                 }
 146             });
 147     }
 148 
 149     protected abstract void startDrag(Transferable trans,
 150                                       long[] formats, Map formatMap);
 151 
 152     /**
 153      * set cursor
 154      */
 155 
 156     public void setCursor(Cursor c) throws InvalidDnDOperationException {
 157         synchronized (this) {
 158             if (cursor == null || !cursor.equals(c)) {
 159                 cursor = c;
 160                 // NOTE: native context can be null at this point.
 161                 // setNativeCursor() should handle it properly.
 162                 setNativeCursor(getNativeContext(), c,
 163                                 c != null ? c.getType() : 0);
 164             }
 165         }
 166     }
 167 
 168     /**
 169      * return cursor
 170      */
 171 
 172     public Cursor getCursor() {
 173         return cursor;
 174     }
 175 
 176     /**
 177      * Returns the drag image. If there is no image to drag,
 178      * the returned value is {@code null}
 179      *
 180      * @return the reference to the drag image
 181      */
 182     public Image getDragImage() {
 183         return dragImage;
 184     }
 185 
 186     /**
 187      * Returns an anchor offset for the image to drag.
 188      *
 189      * @return a {@code Point} object that corresponds
 190      * to coordinates of an anchor offset of the image
 191      * relative to the upper left corner of the image.
 192      * The point {@code (0,0)} returns by default.
 193      */
 194     public Point getDragImageOffset() {
 195         if (dragImageOffset == null) {
 196             return new Point(0,0);
 197         }
 198         return new Point(dragImageOffset);
 199     }
 200 
 201     /**
 202      * downcall into native code
 203      */
 204 
 205 
 206     protected abstract void setNativeCursor(long nativeCtxt, Cursor c,
 207                                             int cType);
 208 
 209     protected synchronized void setTrigger(DragGestureEvent dge) {
 210         trigger = dge;
 211         if (trigger != null) {
 212             component = trigger.getComponent();
 213         } else {
 214             component = null;
 215         }
 216     }
 217 
 218     protected DragGestureEvent getTrigger() {
 219         return trigger;
 220     }
 221 
 222     protected Component getComponent() {
 223         return component;
 224     }
 225 
 226     protected synchronized void setNativeContext(long ctxt) {
 227         nativeCtxt = ctxt;
 228     }
 229 
 230     protected synchronized long getNativeContext() {
 231         return nativeCtxt;
 232     }
 233 
 234     protected DragSourceContext getDragSourceContext() {
 235         return dragSourceContext;
 236     }
 237 
 238     /**
 239      * Notify the peer that the transferables' DataFlavors have changed.
 240      *
 241      * No longer useful as the transferables are determined at the time
 242      * of the drag.
 243      */
 244 
 245     public void transferablesFlavorsChanged() {
 246     }
 247 
 248 
 249 
 250 
 251 
 252     protected final void postDragSourceDragEvent(final int targetAction,
 253                                                  final int modifiers,
 254                                                  final int x, final int y,
 255                                                  final int dispatchType) {
 256 
 257         final int dropAction =
 258             SunDragSourceContextPeer.convertModifiersToDropAction(modifiers,
 259                                                                   sourceActions);
 260 
 261         DragSourceDragEvent event =
 262             new DragSourceDragEvent(getDragSourceContext(),
 263                                     dropAction,
 264                                     targetAction & sourceActions,
 265                                     modifiers, x, y);
 266         EventDispatcher dispatcher = new EventDispatcher(dispatchType, event);
 267 
 268         SunToolkit.invokeLaterOnAppContext(
 269             SunToolkit.targetToAppContext(getComponent()), dispatcher);
 270 
 271         startSecondaryEventLoop();
 272     }
 273 
 274     /**
 275      * upcall from native code
 276      */
 277 
 278     private void dragEnter(final int targetActions,
 279                            final int modifiers,
 280                            final int x, final int y) {
 281         postDragSourceDragEvent(targetActions, modifiers, x, y, DISPATCH_ENTER);
 282     }
 283 
 284     /**
 285      * upcall from native code
 286      */
 287 
 288     private void dragMotion(final int targetActions,
 289                             final int modifiers,
 290                             final int x, final int y) {
 291         postDragSourceDragEvent(targetActions, modifiers, x, y, DISPATCH_MOTION);
 292     }
 293 
 294     /**
 295      * upcall from native code
 296      */
 297 
 298     private void operationChanged(final int targetActions,
 299                                   final int modifiers,
 300                                   final int x, final int y) {
 301         postDragSourceDragEvent(targetActions, modifiers, x, y, DISPATCH_CHANGED);
 302     }
 303 
 304     /**
 305      * upcall from native code
 306      */
 307 
 308     protected final void dragExit(final int x, final int y) {
 309         DragSourceEvent event =
 310             new DragSourceEvent(getDragSourceContext(), x, y);
 311         EventDispatcher dispatcher =
 312             new EventDispatcher(DISPATCH_EXIT, event);
 313 
 314         SunToolkit.invokeLaterOnAppContext(
 315             SunToolkit.targetToAppContext(getComponent()), dispatcher);
 316 
 317         startSecondaryEventLoop();
 318     }
 319 
 320     /**
 321      * upcall from native code
 322      */
 323 
 324     private void dragMouseMoved(final int targetActions,
 325                                 final int modifiers,
 326                                 final int x, final int y) {
 327         postDragSourceDragEvent(targetActions, modifiers, x, y,
 328                                 DISPATCH_MOUSE_MOVED);
 329     }
 330 
 331     /**
 332      * upcall from native code via implemented class (do)
 333      */
 334 
 335     protected final void dragDropFinished(final boolean success,
 336                                           final int operations,
 337                                           final int x, final int y) {
 338         DragSourceEvent event =
 339             new DragSourceDropEvent(getDragSourceContext(),
 340                                     operations & sourceActions,
 341                                     success, x, y);
 342         EventDispatcher dispatcher =
 343             new EventDispatcher(DISPATCH_FINISH, event);
 344 
 345         SunToolkit.invokeLaterOnAppContext(
 346             SunToolkit.targetToAppContext(getComponent()), dispatcher);
 347 
 348         startSecondaryEventLoop();
 349         setNativeContext(0);
 350         dragImage = null;
 351         dragImageOffset = null;
 352     }
 353 
 354     public static void setDragDropInProgress(boolean b)
 355       throws InvalidDnDOperationException {
 356         if (dragDropInProgress == b) {
 357             throw new InvalidDnDOperationException(getExceptionMessage(b));
 358         }
 359 
 360         synchronized (SunDragSourceContextPeer.class) {
 361             if (dragDropInProgress == b) {
 362                 throw new InvalidDnDOperationException(getExceptionMessage(b));
 363             }
 364             dragDropInProgress = b;
 365         }
 366     }
 367 
 368     /**
 369      * Filters out all mouse events that were on the java event queue when
 370      * startDrag was called.
 371      */
 372     public static boolean checkEvent(AWTEvent event) {
 373         if (discardingMouseEvents && event instanceof MouseEvent) {
 374             MouseEvent mouseEvent = (MouseEvent)event;
 375             if (!(mouseEvent instanceof SunDropTargetEvent)) {
 376                 return false;
 377             }
 378         }
 379         return true;
 380     }
 381 
 382     public static void checkDragDropInProgress()
 383       throws InvalidDnDOperationException {
 384         if (dragDropInProgress) {
 385             throw new InvalidDnDOperationException(getExceptionMessage(true));
 386         }
 387     }
 388 
 389     private static String getExceptionMessage(boolean b) {
 390         return b ? "Drag and drop in progress" : "No drag in progress";
 391     }
 392 
 393     public static int convertModifiersToDropAction(final int modifiers,
 394                                                    final int supportedActions) {
 395         int dropAction = DnDConstants.ACTION_NONE;
 396 
 397         /*
 398          * Fix for 4285634.
 399          * Calculate the drop action to match Motif DnD behavior.
 400          * If the user selects an operation (by pressing a modifier key),
 401          * return the selected operation or ACTION_NONE if the selected
 402          * operation is not supported by the drag source.
 403          * If the user doesn't select an operation search the set of operations
 404          * supported by the drag source for ACTION_MOVE, then for
 405          * ACTION_COPY, then for ACTION_LINK and return the first operation
 406          * found.
 407          */
 408         switch (modifiers & (InputEvent.SHIFT_DOWN_MASK |
 409                              InputEvent.CTRL_DOWN_MASK)) {
 410         case InputEvent.SHIFT_DOWN_MASK | InputEvent.CTRL_DOWN_MASK:
 411             dropAction = DnDConstants.ACTION_LINK; break;
 412         case InputEvent.CTRL_DOWN_MASK:
 413             dropAction = DnDConstants.ACTION_COPY; break;
 414         case InputEvent.SHIFT_DOWN_MASK:
 415             dropAction = DnDConstants.ACTION_MOVE; break;
 416         default:
 417             if ((supportedActions & DnDConstants.ACTION_MOVE) != 0) {
 418                 dropAction = DnDConstants.ACTION_MOVE;
 419             } else if ((supportedActions & DnDConstants.ACTION_COPY) != 0) {
 420                 dropAction = DnDConstants.ACTION_COPY;
 421             } else if ((supportedActions & DnDConstants.ACTION_LINK) != 0) {
 422                 dropAction = DnDConstants.ACTION_LINK;
 423             }
 424         }
 425 
 426         return dropAction & supportedActions;
 427     }
 428 
 429     private void cleanup() {
 430         trigger = null;
 431         component = null;
 432         cursor = null;
 433         dragSourceContext = null;
 434         SunDropTargetContextPeer.setCurrentJVMLocalSourceTransferable(null);
 435         SunDragSourceContextPeer.setDragDropInProgress(false);
 436     }
 437 
 438     private class EventDispatcher implements Runnable {
 439 
 440         private final int dispatchType;
 441 
 442         private final DragSourceEvent event;
 443 
 444         EventDispatcher(int dispatchType, DragSourceEvent event) {
 445             switch (dispatchType) {
 446             case DISPATCH_ENTER:
 447             case DISPATCH_MOTION:
 448             case DISPATCH_CHANGED:
 449             case DISPATCH_MOUSE_MOVED:
 450                 if (!(event instanceof DragSourceDragEvent)) {
 451                     throw new IllegalArgumentException("Event: " + event);
 452                 }
 453                 break;
 454             case DISPATCH_EXIT:
 455                 break;
 456             case DISPATCH_FINISH:
 457                 if (!(event instanceof DragSourceDropEvent)) {
 458                     throw new IllegalArgumentException("Event: " + event);
 459                 }
 460                 break;
 461             default:
 462                 throw new IllegalArgumentException("Dispatch type: " +
 463                                                    dispatchType);
 464             }
 465 
 466             this.dispatchType  = dispatchType;
 467             this.event         = event;
 468         }
 469 
 470         public void run() {
 471             DragSourceContext dragSourceContext =
 472                 SunDragSourceContextPeer.this.getDragSourceContext();
 473             try {
 474                 switch (dispatchType) {
 475                 case DISPATCH_ENTER:
 476                     dragSourceContext.dragEnter((DragSourceDragEvent)event);
 477                     break;
 478                 case DISPATCH_MOTION:
 479                     dragSourceContext.dragOver((DragSourceDragEvent)event);
 480                     break;
 481                 case DISPATCH_CHANGED:
 482                     dragSourceContext.dropActionChanged((DragSourceDragEvent)event);
 483                     break;
 484                 case DISPATCH_EXIT:
 485                     dragSourceContext.dragExit(event);
 486                     break;
 487                 case DISPATCH_MOUSE_MOVED:
 488                     dragSourceContext.dragMouseMoved((DragSourceDragEvent)event);
 489                     break;
 490                 case DISPATCH_FINISH:
 491                     try {
 492                         dragSourceContext.dragDropEnd((DragSourceDropEvent)event);
 493                     } finally {
 494                         SunDragSourceContextPeer.this.cleanup();
 495                     }
 496                     break;
 497                 default:
 498                     throw new IllegalStateException("Dispatch type: " +
 499                                                     dispatchType);
 500                 }
 501             } finally {
 502                  SunDragSourceContextPeer.this.quitSecondaryEventLoop();
 503             }
 504         }
 505     }
 506 }