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 }