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