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 }