1 /*
   2  * Copyright (c) 2000, 2014, 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     @SuppressWarnings("static")
 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     protected 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         synchronized (SunDragSourceContextPeer.class) {
 357             if (dragDropInProgress == b) {
 358                 throw new InvalidDnDOperationException(getExceptionMessage(b));
 359             }
 360             dragDropInProgress = b;
 361         }
 362     }
 363 
 364     /**
 365      * Filters out all mouse events that were on the java event queue when
 366      * startDrag was called.
 367      */
 368     public static boolean checkEvent(AWTEvent event) {
 369         if (discardingMouseEvents && event instanceof MouseEvent) {
 370             MouseEvent mouseEvent = (MouseEvent)event;
 371             if (!(mouseEvent instanceof SunDropTargetEvent)) {
 372                 return false;
 373             }
 374         }
 375         return true;
 376     }
 377 
 378     public static void checkDragDropInProgress()
 379       throws InvalidDnDOperationException {
 380         if (dragDropInProgress) {
 381             throw new InvalidDnDOperationException(getExceptionMessage(true));
 382         }
 383     }
 384 
 385     private static String getExceptionMessage(boolean b) {
 386         return b ? "Drag and drop in progress" : "No drag in progress";
 387     }
 388 
 389     public static int convertModifiersToDropAction(final int modifiers,
 390                                                    final int supportedActions) {
 391         int dropAction = DnDConstants.ACTION_NONE;
 392 
 393         /*
 394          * Fix for 4285634.
 395          * Calculate the drop action to match Motif DnD behavior.
 396          * If the user selects an operation (by pressing a modifier key),
 397          * return the selected operation or ACTION_NONE if the selected
 398          * operation is not supported by the drag source.
 399          * If the user doesn't select an operation search the set of operations
 400          * supported by the drag source for ACTION_MOVE, then for
 401          * ACTION_COPY, then for ACTION_LINK and return the first operation
 402          * found.
 403          */
 404         switch (modifiers & (InputEvent.SHIFT_DOWN_MASK |
 405                              InputEvent.CTRL_DOWN_MASK)) {
 406         case InputEvent.SHIFT_DOWN_MASK | InputEvent.CTRL_DOWN_MASK:
 407             dropAction = DnDConstants.ACTION_LINK; break;
 408         case InputEvent.CTRL_DOWN_MASK:
 409             dropAction = DnDConstants.ACTION_COPY; break;
 410         case InputEvent.SHIFT_DOWN_MASK:
 411             dropAction = DnDConstants.ACTION_MOVE; break;
 412         default:
 413             if ((supportedActions & DnDConstants.ACTION_MOVE) != 0) {
 414                 dropAction = DnDConstants.ACTION_MOVE;
 415             } else if ((supportedActions & DnDConstants.ACTION_COPY) != 0) {
 416                 dropAction = DnDConstants.ACTION_COPY;
 417             } else if ((supportedActions & DnDConstants.ACTION_LINK) != 0) {
 418                 dropAction = DnDConstants.ACTION_LINK;
 419             }
 420         }
 421 
 422         return dropAction & supportedActions;
 423     }
 424 
 425     private void cleanup() {
 426         trigger = null;
 427         component = null;
 428         cursor = null;
 429         dragSourceContext = null;
 430         SunDropTargetContextPeer.setCurrentJVMLocalSourceTransferable(null);
 431         SunDragSourceContextPeer.setDragDropInProgress(false);
 432     }
 433 
 434     private class EventDispatcher implements Runnable {
 435 
 436         private final int dispatchType;
 437 
 438         private final DragSourceEvent event;
 439 
 440         EventDispatcher(int dispatchType, DragSourceEvent event) {
 441             switch (dispatchType) {
 442             case DISPATCH_ENTER:
 443             case DISPATCH_MOTION:
 444             case DISPATCH_CHANGED:
 445             case DISPATCH_MOUSE_MOVED:
 446                 if (!(event instanceof DragSourceDragEvent)) {
 447                     throw new IllegalArgumentException("Event: " + event);
 448                 }
 449                 break;
 450             case DISPATCH_EXIT:
 451                 break;
 452             case DISPATCH_FINISH:
 453                 if (!(event instanceof DragSourceDropEvent)) {
 454                     throw new IllegalArgumentException("Event: " + event);
 455                 }
 456                 break;
 457             default:
 458                 throw new IllegalArgumentException("Dispatch type: " +
 459                                                    dispatchType);
 460             }
 461 
 462             this.dispatchType  = dispatchType;
 463             this.event         = event;
 464         }
 465 
 466         public void run() {
 467             DragSourceContext dragSourceContext =
 468                 SunDragSourceContextPeer.this.getDragSourceContext();
 469             try {
 470                 switch (dispatchType) {
 471                 case DISPATCH_ENTER:
 472                     dragSourceContext.dragEnter((DragSourceDragEvent)event);
 473                     break;
 474                 case DISPATCH_MOTION:
 475                     dragSourceContext.dragOver((DragSourceDragEvent)event);
 476                     break;
 477                 case DISPATCH_CHANGED:
 478                     dragSourceContext.dropActionChanged((DragSourceDragEvent)event);
 479                     break;
 480                 case DISPATCH_EXIT:
 481                     dragSourceContext.dragExit(event);
 482                     break;
 483                 case DISPATCH_MOUSE_MOVED:
 484                     dragSourceContext.dragMouseMoved((DragSourceDragEvent)event);
 485                     break;
 486                 case DISPATCH_FINISH:
 487                     try {
 488                         dragSourceContext.dragDropEnd((DragSourceDropEvent)event);
 489                     } finally {
 490                         SunDragSourceContextPeer.this.cleanup();
 491                     }
 492                     break;
 493                 default:
 494                     throw new IllegalStateException("Dispatch type: " +
 495                                                     dispatchType);
 496                 }
 497             } finally {
 498                  SunDragSourceContextPeer.this.quitSecondaryEventLoop();
 499             }
 500         }
 501     }
 502 }