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 private 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 }