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