jdk/src/share/classes/sun/awt/dnd/SunDropTargetContextPeer.java
changeset 2 90ce3da70b43
child 3938 ef327bd847c0
equal deleted inserted replaced
0:fd16c54261b3 2:90ce3da70b43
       
     1 /*
       
     2  * Copyright 2000-2007 Sun Microsystems, Inc.  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.  Sun designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
       
    22  * CA 95054 USA or visit www.sun.com if you need additional information or
       
    23  * have any questions.
       
    24  */
       
    25 
       
    26 package sun.awt.dnd;
       
    27 
       
    28 import java.awt.Component;
       
    29 import java.awt.Point;
       
    30 
       
    31 import java.awt.datatransfer.DataFlavor;
       
    32 import java.awt.datatransfer.Transferable;
       
    33 import java.awt.datatransfer.UnsupportedFlavorException;
       
    34 
       
    35 import java.awt.dnd.DnDConstants;
       
    36 
       
    37 import java.awt.dnd.DropTarget;
       
    38 import java.awt.dnd.DropTargetContext;
       
    39 import java.awt.dnd.DropTargetListener;
       
    40 import java.awt.dnd.DropTargetEvent;
       
    41 import java.awt.dnd.DropTargetDragEvent;
       
    42 import java.awt.dnd.DropTargetDropEvent;
       
    43 import java.awt.dnd.InvalidDnDOperationException;
       
    44 
       
    45 import java.awt.dnd.peer.DropTargetContextPeer;
       
    46 
       
    47 import java.util.HashSet;
       
    48 import java.util.Map;
       
    49 import java.util.Arrays;
       
    50 
       
    51 import java.util.logging.*;
       
    52 
       
    53 import java.io.IOException;
       
    54 import java.io.InputStream;
       
    55 
       
    56 import sun.awt.AppContext;
       
    57 import sun.awt.SunToolkit;
       
    58 import sun.awt.datatransfer.DataTransferer;
       
    59 import sun.awt.datatransfer.ToolkitThreadBlockedHandler;
       
    60 
       
    61 /**
       
    62  * <p>
       
    63  * The SunDropTargetContextPeer class is the generic class responsible for handling
       
    64  * the interaction between a windowing systems DnD system and Java.
       
    65  * </p>
       
    66  *
       
    67  * @since JDK1.3.1
       
    68  *
       
    69  */
       
    70 
       
    71 public abstract class SunDropTargetContextPeer implements DropTargetContextPeer, Transferable {
       
    72 
       
    73     /*
       
    74      * A boolean constant that requires the peer to wait until the
       
    75      * SunDropTargetEvent is processed and return the status back
       
    76      * to the native code.
       
    77      */
       
    78     public static final boolean DISPATCH_SYNC = true;
       
    79     private   DropTarget              currentDT;
       
    80     private   DropTargetContext       currentDTC;
       
    81     private   long[]                  currentT;
       
    82     private   int                     currentA;   // target actions
       
    83     private   int                     currentSA;  // source actions
       
    84     private   int                     currentDA;  // current drop action
       
    85     private   int                     previousDA;
       
    86 
       
    87     private   long                    nativeDragContext;
       
    88 
       
    89     private   Transferable            local;
       
    90 
       
    91     private boolean                   dragRejected = false;
       
    92 
       
    93     protected int                     dropStatus   = STATUS_NONE;
       
    94     protected boolean                 dropComplete = false;
       
    95 
       
    96     /*
       
    97      * global lock
       
    98      */
       
    99 
       
   100     protected static final Object _globalLock = new Object();
       
   101 
       
   102     private static final Logger dndLog = Logger.getLogger("sun.awt.dnd.SunDropTargetContextPeer");
       
   103 
       
   104     /*
       
   105      * a primitive mechanism for advertising intra-JVM Transferables
       
   106      */
       
   107 
       
   108     protected static Transferable         currentJVMLocalSourceTransferable = null;
       
   109 
       
   110     public static void setCurrentJVMLocalSourceTransferable(Transferable t) throws InvalidDnDOperationException {
       
   111         synchronized(_globalLock) {
       
   112             if (t != null && currentJVMLocalSourceTransferable != null) {
       
   113                     throw new InvalidDnDOperationException();
       
   114             } else {
       
   115                 currentJVMLocalSourceTransferable = t;
       
   116             }
       
   117         }
       
   118     }
       
   119 
       
   120     /**
       
   121      * obtain the transferable iff the operation is in the same VM
       
   122      */
       
   123 
       
   124     private static Transferable getJVMLocalSourceTransferable() {
       
   125         return currentJVMLocalSourceTransferable;
       
   126     }
       
   127 
       
   128     /*
       
   129      * constants used by dropAccept() or dropReject()
       
   130      */
       
   131 
       
   132     protected final static int STATUS_NONE   =  0; // none pending
       
   133     protected final static int STATUS_WAIT   =  1; // drop pending
       
   134     protected final static int STATUS_ACCEPT =  2;
       
   135     protected final static int STATUS_REJECT = -1;
       
   136 
       
   137     /**
       
   138      * create the peer
       
   139      */
       
   140 
       
   141     public SunDropTargetContextPeer() {
       
   142         super();
       
   143     }
       
   144 
       
   145     /**
       
   146      * @return the DropTarget associated with this peer
       
   147      */
       
   148 
       
   149     public DropTarget getDropTarget() { return currentDT; }
       
   150 
       
   151     /**
       
   152      * @param actions set the current actions
       
   153      */
       
   154 
       
   155     public synchronized void setTargetActions(int actions) {
       
   156         currentA = actions &
       
   157             (DnDConstants.ACTION_COPY_OR_MOVE | DnDConstants.ACTION_LINK);
       
   158     }
       
   159 
       
   160     /**
       
   161      * @return the current target actions
       
   162      */
       
   163 
       
   164     public int getTargetActions() {
       
   165         return currentA;
       
   166     }
       
   167 
       
   168     /**
       
   169      * get the Transferable associated with the drop
       
   170      */
       
   171 
       
   172     public Transferable getTransferable() {
       
   173         return this;
       
   174     }
       
   175 
       
   176     /**
       
   177      * @return current DataFlavors available
       
   178      */
       
   179     // NOTE: This method may be called by privileged threads.
       
   180     //       DO NOT INVOKE CLIENT CODE ON THIS THREAD!
       
   181 
       
   182     public DataFlavor[] getTransferDataFlavors() {
       
   183         final Transferable    localTransferable = local;
       
   184 
       
   185         if (localTransferable != null) {
       
   186             return localTransferable.getTransferDataFlavors();
       
   187         } else {
       
   188             return DataTransferer.getInstance().getFlavorsForFormatsAsArray
       
   189                 (currentT, DataTransferer.adaptFlavorMap
       
   190                     (currentDT.getFlavorMap()));
       
   191         }
       
   192     }
       
   193 
       
   194     /**
       
   195      * @return if the flavor is supported
       
   196      */
       
   197 
       
   198     public boolean isDataFlavorSupported(DataFlavor df) {
       
   199         Transferable localTransferable = local;
       
   200 
       
   201         if (localTransferable != null) {
       
   202             return localTransferable.isDataFlavorSupported(df);
       
   203         } else {
       
   204             return DataTransferer.getInstance().getFlavorsForFormats
       
   205                 (currentT, DataTransferer.adaptFlavorMap
       
   206                     (currentDT.getFlavorMap())).
       
   207                 containsKey(df);
       
   208         }
       
   209     }
       
   210 
       
   211     /**
       
   212      * @return the data
       
   213      */
       
   214 
       
   215     public Object getTransferData(DataFlavor df)
       
   216       throws UnsupportedFlavorException, IOException,
       
   217         InvalidDnDOperationException
       
   218     {
       
   219         Long lFormat = null;
       
   220         Transferable localTransferable = local;
       
   221 
       
   222         if (localTransferable != null) {
       
   223             return localTransferable.getTransferData(df);
       
   224         }
       
   225 
       
   226         if (dropStatus != STATUS_ACCEPT || dropComplete) {
       
   227             throw new InvalidDnDOperationException("No drop current");
       
   228         }
       
   229 
       
   230         Map flavorMap = DataTransferer.getInstance().getFlavorsForFormats
       
   231             (currentT, DataTransferer.adaptFlavorMap
       
   232                 (currentDT.getFlavorMap()));
       
   233 
       
   234         lFormat = (Long)flavorMap.get(df);
       
   235         if (lFormat == null) {
       
   236             throw new UnsupportedFlavorException(df);
       
   237         }
       
   238 
       
   239         if (df.isRepresentationClassRemote() &&
       
   240             currentDA != DnDConstants.ACTION_LINK) {
       
   241             throw new InvalidDnDOperationException("only ACTION_LINK is permissable for transfer of java.rmi.Remote objects");
       
   242         }
       
   243 
       
   244         final long format = lFormat.longValue();
       
   245         Object ret = getNativeData(format);
       
   246 
       
   247         if (ret instanceof byte[]) {
       
   248             try {
       
   249                 return DataTransferer.getInstance().
       
   250                     translateBytes((byte[])ret, df, format, this);
       
   251             } catch (IOException e) {
       
   252                 throw new InvalidDnDOperationException(e.getMessage());
       
   253             }
       
   254         } else if (ret instanceof InputStream) {
       
   255             try {
       
   256                 return DataTransferer.getInstance().
       
   257                     translateStream((InputStream)ret, df, format, this);
       
   258             } catch (IOException e) {
       
   259                 throw new InvalidDnDOperationException(e.getMessage());
       
   260             }
       
   261         } else {
       
   262             throw new IOException("no native data was transfered");
       
   263         }
       
   264     }
       
   265 
       
   266     protected abstract Object getNativeData(long format)
       
   267       throws IOException;
       
   268 
       
   269     /**
       
   270      * @return if the transfer is a local one
       
   271      */
       
   272     public boolean isTransferableJVMLocal() {
       
   273         return local != null || getJVMLocalSourceTransferable() != null;
       
   274     }
       
   275 
       
   276     private int handleEnterMessage(final Component component,
       
   277                                    final int x, final int y,
       
   278                                    final int dropAction,
       
   279                                    final int actions, final long[] formats,
       
   280                                    final long nativeCtxt) {
       
   281         return postDropTargetEvent(component, x, y, dropAction, actions,
       
   282                                    formats, nativeCtxt,
       
   283                                    SunDropTargetEvent.MOUSE_ENTERED,
       
   284                                    SunDropTargetContextPeer.DISPATCH_SYNC);
       
   285     }
       
   286 
       
   287     /**
       
   288      * actual processing on EventQueue Thread
       
   289      */
       
   290 
       
   291     protected void processEnterMessage(SunDropTargetEvent event) {
       
   292         Component  c    = (Component)event.getSource();
       
   293         DropTarget dt   = c.getDropTarget();
       
   294         Point      hots = event.getPoint();
       
   295 
       
   296         local = getJVMLocalSourceTransferable();
       
   297 
       
   298         if (currentDTC != null) { // some wreckage from last time
       
   299             currentDTC.removeNotify();
       
   300             currentDTC = null;
       
   301         }
       
   302 
       
   303         if (c.isShowing() && dt != null && dt.isActive()) {
       
   304             currentDT  = dt;
       
   305             currentDTC = currentDT.getDropTargetContext();
       
   306 
       
   307             currentDTC.addNotify(this);
       
   308 
       
   309             currentA   = dt.getDefaultActions();
       
   310 
       
   311             try {
       
   312                 ((DropTargetListener)dt).dragEnter(new DropTargetDragEvent(currentDTC,
       
   313                                                                            hots,
       
   314                                                                            currentDA,
       
   315                                                                            currentSA));
       
   316             } catch (Exception e) {
       
   317                 e.printStackTrace();
       
   318                 currentDA = DnDConstants.ACTION_NONE;
       
   319             }
       
   320         } else {
       
   321             currentDT  = null;
       
   322             currentDTC = null;
       
   323             currentDA   = DnDConstants.ACTION_NONE;
       
   324             currentSA   = DnDConstants.ACTION_NONE;
       
   325             currentA   = DnDConstants.ACTION_NONE;
       
   326         }
       
   327 
       
   328     }
       
   329 
       
   330     /**
       
   331      * upcall to handle exit messages
       
   332      */
       
   333 
       
   334     private void handleExitMessage(final Component component,
       
   335                                    final long nativeCtxt) {
       
   336         /*
       
   337          * Even though the return value is irrelevant for this event, it is
       
   338          * dispatched synchronously to fix 4393148 properly.
       
   339          */
       
   340         postDropTargetEvent(component, 0, 0, DnDConstants.ACTION_NONE,
       
   341                             DnDConstants.ACTION_NONE, null, nativeCtxt,
       
   342                             SunDropTargetEvent.MOUSE_EXITED,
       
   343                             SunDropTargetContextPeer.DISPATCH_SYNC);
       
   344     }
       
   345 
       
   346     /**
       
   347      *
       
   348      */
       
   349 
       
   350     protected void processExitMessage(SunDropTargetEvent event) {
       
   351         Component         c   = (Component)event.getSource();
       
   352         DropTarget        dt  = c.getDropTarget();
       
   353         DropTargetContext dtc = null;
       
   354 
       
   355         if (dt == null) {
       
   356             currentDT = null;
       
   357             currentT  = null;
       
   358 
       
   359             if (currentDTC != null) {
       
   360                 currentDTC.removeNotify();
       
   361             }
       
   362 
       
   363             currentDTC = null;
       
   364 
       
   365             return;
       
   366         }
       
   367 
       
   368         if (dt != currentDT) {
       
   369 
       
   370             if (currentDTC != null) {
       
   371                 currentDTC.removeNotify();
       
   372             }
       
   373 
       
   374             currentDT  = dt;
       
   375             currentDTC = dt.getDropTargetContext();
       
   376 
       
   377             currentDTC.addNotify(this);
       
   378         }
       
   379 
       
   380         dtc = currentDTC;
       
   381 
       
   382         if (dt.isActive()) try {
       
   383             ((DropTargetListener)dt).dragExit(new DropTargetEvent(dtc));
       
   384         } catch (Exception e) {
       
   385             e.printStackTrace();
       
   386         } finally {
       
   387             currentA  = DnDConstants.ACTION_NONE;
       
   388             currentSA = DnDConstants.ACTION_NONE;
       
   389             currentDA = DnDConstants.ACTION_NONE;
       
   390             currentDT = null;
       
   391             currentT  = null;
       
   392 
       
   393             currentDTC.removeNotify();
       
   394             currentDTC = null;
       
   395 
       
   396             local = null;
       
   397 
       
   398             dragRejected = false;
       
   399         }
       
   400     }
       
   401 
       
   402     private int handleMotionMessage(final Component component,
       
   403                                     final int x, final int y,
       
   404                                     final int dropAction,
       
   405                                     final int actions, final long[] formats,
       
   406                                     final long nativeCtxt) {
       
   407         return postDropTargetEvent(component, x, y, dropAction, actions,
       
   408                                    formats, nativeCtxt,
       
   409                                    SunDropTargetEvent.MOUSE_DRAGGED,
       
   410                                    SunDropTargetContextPeer.DISPATCH_SYNC);
       
   411     }
       
   412 
       
   413     /**
       
   414      *
       
   415      */
       
   416 
       
   417     protected void processMotionMessage(SunDropTargetEvent event,
       
   418                                       boolean operationChanged) {
       
   419         Component         c    = (Component)event.getSource();
       
   420         Point             hots = event.getPoint();
       
   421         int               id   = event.getID();
       
   422         DropTarget        dt   = c.getDropTarget();
       
   423         DropTargetContext dtc  = null;
       
   424 
       
   425         if (c.isShowing() && (dt != null) && dt.isActive()) {
       
   426             if (currentDT != dt) {
       
   427                 if (currentDTC != null) {
       
   428                     currentDTC.removeNotify();
       
   429                 }
       
   430 
       
   431                 currentDT  = dt;
       
   432                 currentDTC = null;
       
   433             }
       
   434 
       
   435             dtc = currentDT.getDropTargetContext();
       
   436             if (dtc != currentDTC) {
       
   437                 if (currentDTC != null) {
       
   438                     currentDTC.removeNotify();
       
   439                 }
       
   440 
       
   441                 currentDTC = dtc;
       
   442                 currentDTC.addNotify(this);
       
   443             }
       
   444 
       
   445             currentA = currentDT.getDefaultActions();
       
   446 
       
   447             try {
       
   448                 DropTargetDragEvent dtde = new DropTargetDragEvent(dtc,
       
   449                                                                    hots,
       
   450                                                                    currentDA,
       
   451                                                                    currentSA);
       
   452                 DropTargetListener dtl = (DropTargetListener)dt;
       
   453                 if (operationChanged) {
       
   454                     dtl.dropActionChanged(dtde);
       
   455                 } else {
       
   456                     dtl.dragOver(dtde);
       
   457                 }
       
   458 
       
   459                 if (dragRejected) {
       
   460                     currentDA = DnDConstants.ACTION_NONE;
       
   461                 }
       
   462             } catch (Exception e) {
       
   463                 e.printStackTrace();
       
   464                 currentDA = DnDConstants.ACTION_NONE;
       
   465             }
       
   466         } else {
       
   467             currentDA = DnDConstants.ACTION_NONE;
       
   468         }
       
   469     }
       
   470 
       
   471     /**
       
   472      * upcall to handle the Drop message
       
   473      */
       
   474 
       
   475     private void handleDropMessage(final Component component,
       
   476                                    final int x, final int y,
       
   477                                    final int dropAction, final int actions,
       
   478                                    final long[] formats,
       
   479                                    final long nativeCtxt) {
       
   480         postDropTargetEvent(component, x, y, dropAction, actions,
       
   481                             formats, nativeCtxt,
       
   482                             SunDropTargetEvent.MOUSE_DROPPED,
       
   483                             !SunDropTargetContextPeer.DISPATCH_SYNC);
       
   484     }
       
   485 
       
   486     /**
       
   487      *
       
   488      */
       
   489 
       
   490     protected void processDropMessage(SunDropTargetEvent event) {
       
   491         Component  c    = (Component)event.getSource();
       
   492         Point      hots = event.getPoint();
       
   493         DropTarget dt   = c.getDropTarget();
       
   494 
       
   495         dropStatus   = STATUS_WAIT; // drop pending ACK
       
   496         dropComplete = false;
       
   497 
       
   498         if (c.isShowing() && dt != null && dt.isActive()) {
       
   499             DropTargetContext dtc = dt.getDropTargetContext();
       
   500 
       
   501             currentDT = dt;
       
   502 
       
   503             if (currentDTC != null) {
       
   504                 currentDTC.removeNotify();
       
   505             }
       
   506 
       
   507             currentDTC = dtc;
       
   508             currentDTC.addNotify(this);
       
   509             currentA = dt.getDefaultActions();
       
   510 
       
   511             synchronized(_globalLock) {
       
   512                 if ((local = getJVMLocalSourceTransferable()) != null)
       
   513                     setCurrentJVMLocalSourceTransferable(null);
       
   514             }
       
   515 
       
   516             try {
       
   517                 ((DropTargetListener)dt).drop(new DropTargetDropEvent(dtc,
       
   518                                                                       hots,
       
   519                                                                       currentDA,
       
   520                                                                       currentSA,
       
   521                                                                       local != null));
       
   522             } finally {
       
   523                 if (dropStatus == STATUS_WAIT) {
       
   524                     rejectDrop();
       
   525                 } else if (dropComplete == false) {
       
   526                     dropComplete(false);
       
   527                 }
       
   528             }
       
   529         } else {
       
   530             rejectDrop();
       
   531         }
       
   532     }
       
   533 
       
   534     protected int postDropTargetEvent(final Component component,
       
   535                                       final int x, final int y,
       
   536                                       final int dropAction,
       
   537                                       final int actions,
       
   538                                       final long[] formats,
       
   539                                       final long nativeCtxt,
       
   540                                       final int eventID,
       
   541                                       final boolean dispatchType) {
       
   542         AppContext appContext = SunToolkit.targetToAppContext(component);
       
   543 
       
   544         EventDispatcher dispatcher =
       
   545             new EventDispatcher(this, dropAction, actions, formats, nativeCtxt,
       
   546                                 dispatchType);
       
   547 
       
   548         SunDropTargetEvent event =
       
   549             new SunDropTargetEvent(component, eventID, x, y, dispatcher);
       
   550 
       
   551         if (dispatchType == SunDropTargetContextPeer.DISPATCH_SYNC) {
       
   552             DataTransferer.getInstance().getToolkitThreadBlockedHandler().lock();
       
   553         }
       
   554 
       
   555         // schedule callback
       
   556         SunToolkit.postEvent(appContext, event);
       
   557 
       
   558         eventPosted(event);
       
   559 
       
   560         if (dispatchType == SunDropTargetContextPeer.DISPATCH_SYNC) {
       
   561             while (!dispatcher.isDone()) {
       
   562                 DataTransferer.getInstance().getToolkitThreadBlockedHandler().enter();
       
   563             }
       
   564 
       
   565             DataTransferer.getInstance().getToolkitThreadBlockedHandler().unlock();
       
   566 
       
   567             // return target's response
       
   568             return dispatcher.getReturnValue();
       
   569         } else {
       
   570             return 0;
       
   571         }
       
   572     }
       
   573 
       
   574     /**
       
   575      * acceptDrag
       
   576      */
       
   577 
       
   578     public synchronized void acceptDrag(int dragOperation) {
       
   579         if (currentDT == null) {
       
   580             throw new InvalidDnDOperationException("No Drag pending");
       
   581         }
       
   582         currentDA = mapOperation(dragOperation);
       
   583         if (currentDA != DnDConstants.ACTION_NONE) {
       
   584             dragRejected = false;
       
   585         }
       
   586     }
       
   587 
       
   588     /**
       
   589      * rejectDrag
       
   590      */
       
   591 
       
   592     public synchronized void rejectDrag() {
       
   593         if (currentDT == null) {
       
   594             throw new InvalidDnDOperationException("No Drag pending");
       
   595         }
       
   596         currentDA = DnDConstants.ACTION_NONE;
       
   597         dragRejected = true;
       
   598     }
       
   599 
       
   600     /**
       
   601      * acceptDrop
       
   602      */
       
   603 
       
   604     public synchronized void acceptDrop(int dropOperation) {
       
   605         if (dropOperation == DnDConstants.ACTION_NONE)
       
   606             throw new IllegalArgumentException("invalid acceptDrop() action");
       
   607 
       
   608         if (dropStatus != STATUS_WAIT) {
       
   609             throw new InvalidDnDOperationException("invalid acceptDrop()");
       
   610         }
       
   611 
       
   612         currentDA = currentA = mapOperation(dropOperation & currentSA);
       
   613 
       
   614         dropStatus   = STATUS_ACCEPT;
       
   615         dropComplete = false;
       
   616     }
       
   617 
       
   618     /**
       
   619      * reject Drop
       
   620      */
       
   621 
       
   622     public synchronized void rejectDrop() {
       
   623         if (dropStatus != STATUS_WAIT) {
       
   624             throw new InvalidDnDOperationException("invalid rejectDrop()");
       
   625         }
       
   626         dropStatus = STATUS_REJECT;
       
   627         /*
       
   628          * Fix for 4285634.
       
   629          * The target rejected the drop means that it doesn't perform any
       
   630          * drop action. This change is to make Solaris behavior consistent
       
   631          * with Win32.
       
   632          */
       
   633         currentDA = DnDConstants.ACTION_NONE;
       
   634         dropComplete(false);
       
   635     }
       
   636 
       
   637     /**
       
   638      * mapOperation
       
   639      */
       
   640 
       
   641     private int mapOperation(int operation) {
       
   642         int[] operations = {
       
   643                 DnDConstants.ACTION_MOVE,
       
   644                 DnDConstants.ACTION_COPY,
       
   645                 DnDConstants.ACTION_LINK,
       
   646         };
       
   647         int   ret = DnDConstants.ACTION_NONE;
       
   648 
       
   649         for (int i = 0; i < operations.length; i++) {
       
   650             if ((operation & operations[i]) == operations[i]) {
       
   651                     ret = operations[i];
       
   652                     break;
       
   653             }
       
   654         }
       
   655 
       
   656         return ret;
       
   657     }
       
   658 
       
   659     /**
       
   660      * signal drop complete
       
   661      */
       
   662 
       
   663     public synchronized void dropComplete(boolean success) {
       
   664         if (dropStatus == STATUS_NONE) {
       
   665             throw new InvalidDnDOperationException("No Drop pending");
       
   666         }
       
   667 
       
   668         if (currentDTC != null) currentDTC.removeNotify();
       
   669 
       
   670         currentDT  = null;
       
   671         currentDTC = null;
       
   672         currentT   = null;
       
   673         currentA   = DnDConstants.ACTION_NONE;
       
   674 
       
   675         synchronized(_globalLock) {
       
   676             currentJVMLocalSourceTransferable = null;
       
   677         }
       
   678 
       
   679         dropStatus   = STATUS_NONE;
       
   680         dropComplete = true;
       
   681 
       
   682         try {
       
   683             doDropDone(success, currentDA, local != null);
       
   684         } finally {
       
   685             currentDA = DnDConstants.ACTION_NONE;
       
   686             // The native context is invalid after the drop is done.
       
   687             // Clear the reference to prohibit access.
       
   688             nativeDragContext = 0;
       
   689         }
       
   690     }
       
   691 
       
   692     protected abstract void doDropDone(boolean success,
       
   693                                        int dropAction, boolean isLocal);
       
   694 
       
   695     protected synchronized long getNativeDragContext() {
       
   696         return nativeDragContext;
       
   697     }
       
   698 
       
   699     protected void eventPosted(SunDropTargetEvent e) {}
       
   700 
       
   701     protected void eventProcessed(SunDropTargetEvent e, int returnValue,
       
   702                                   boolean dispatcherDone) {}
       
   703 
       
   704     protected static class EventDispatcher {
       
   705 
       
   706         private final SunDropTargetContextPeer peer;
       
   707 
       
   708         // context fields
       
   709         private final int dropAction;
       
   710         private final int actions;
       
   711         private final long[] formats;
       
   712         private long nativeCtxt;
       
   713         private final boolean dispatchType;
       
   714         private boolean dispatcherDone = false;
       
   715 
       
   716         // dispatcher state fields
       
   717         private int returnValue = 0;
       
   718         // set of events to be dispatched by this dispatcher
       
   719         private final HashSet eventSet = new HashSet(3);
       
   720 
       
   721         static final ToolkitThreadBlockedHandler handler =
       
   722             DataTransferer.getInstance().getToolkitThreadBlockedHandler();
       
   723 
       
   724         EventDispatcher(SunDropTargetContextPeer peer,
       
   725                         int dropAction,
       
   726                         int actions,
       
   727                         long[] formats,
       
   728                         long nativeCtxt,
       
   729                         boolean dispatchType) {
       
   730 
       
   731             this.peer         = peer;
       
   732             this.nativeCtxt   = nativeCtxt;
       
   733             this.dropAction   = dropAction;
       
   734             this.actions      = actions;
       
   735             this.formats =
       
   736                      (null == formats) ? null : Arrays.copyOf(formats, formats.length);
       
   737             this.dispatchType = dispatchType;
       
   738         }
       
   739 
       
   740         void dispatchEvent(SunDropTargetEvent e) {
       
   741             int id = e.getID();
       
   742 
       
   743             switch (id) {
       
   744             case SunDropTargetEvent.MOUSE_ENTERED:
       
   745                 dispatchEnterEvent(e);
       
   746                 break;
       
   747             case SunDropTargetEvent.MOUSE_DRAGGED:
       
   748                 dispatchMotionEvent(e);
       
   749                 break;
       
   750             case SunDropTargetEvent.MOUSE_EXITED:
       
   751                 dispatchExitEvent(e);
       
   752                 break;
       
   753             case SunDropTargetEvent.MOUSE_DROPPED:
       
   754                 dispatchDropEvent(e);
       
   755                 break;
       
   756             default:
       
   757                 throw new InvalidDnDOperationException();
       
   758             }
       
   759         }
       
   760 
       
   761         private void dispatchEnterEvent(SunDropTargetEvent e) {
       
   762             synchronized (peer) {
       
   763 
       
   764                 // store the drop action here to track operation changes
       
   765                 peer.previousDA = dropAction;
       
   766 
       
   767                 // setup peer context
       
   768                 peer.nativeDragContext = nativeCtxt;
       
   769                 peer.currentT          = formats;
       
   770                 peer.currentSA         = actions;
       
   771                 peer.currentDA         = dropAction;
       
   772                 // To allow data retrieval.
       
   773                 peer.dropStatus        = STATUS_ACCEPT;
       
   774                 peer.dropComplete      = false;
       
   775 
       
   776                 try {
       
   777                     peer.processEnterMessage(e);
       
   778                 } finally {
       
   779                     peer.dropStatus        = STATUS_NONE;
       
   780                 }
       
   781 
       
   782                 setReturnValue(peer.currentDA);
       
   783             }
       
   784         }
       
   785 
       
   786         private void dispatchMotionEvent(SunDropTargetEvent e) {
       
   787             synchronized (peer) {
       
   788 
       
   789                 boolean operationChanged = peer.previousDA != dropAction;
       
   790                 peer.previousDA = dropAction;
       
   791 
       
   792                 // setup peer context
       
   793                 peer.nativeDragContext = nativeCtxt;
       
   794                 peer.currentT          = formats;
       
   795                 peer.currentSA         = actions;
       
   796                 peer.currentDA         = dropAction;
       
   797                 // To allow data retrieval.
       
   798                 peer.dropStatus        = STATUS_ACCEPT;
       
   799                 peer.dropComplete      = false;
       
   800 
       
   801                 try {
       
   802                     peer.processMotionMessage(e, operationChanged);
       
   803                 } finally {
       
   804                     peer.dropStatus        = STATUS_NONE;
       
   805                 }
       
   806 
       
   807                 setReturnValue(peer.currentDA);
       
   808             }
       
   809         }
       
   810 
       
   811         private void dispatchExitEvent(SunDropTargetEvent e) {
       
   812             synchronized (peer) {
       
   813 
       
   814                 // setup peer context
       
   815                 peer.nativeDragContext = nativeCtxt;
       
   816 
       
   817                 peer.processExitMessage(e);
       
   818             }
       
   819         }
       
   820 
       
   821         private void dispatchDropEvent(SunDropTargetEvent e) {
       
   822             synchronized (peer) {
       
   823 
       
   824                 // setup peer context
       
   825                 peer.nativeDragContext = nativeCtxt;
       
   826                 peer.currentT          = formats;
       
   827                 peer.currentSA         = actions;
       
   828                 peer.currentDA         = dropAction;
       
   829 
       
   830                 peer.processDropMessage(e);
       
   831             }
       
   832         }
       
   833 
       
   834         void setReturnValue(int ret) {
       
   835             returnValue = ret;
       
   836         }
       
   837 
       
   838         int getReturnValue() {
       
   839             return returnValue;
       
   840         }
       
   841 
       
   842         boolean isDone() {
       
   843             return eventSet.isEmpty();
       
   844         }
       
   845 
       
   846         void registerEvent(SunDropTargetEvent e) {
       
   847             handler.lock();
       
   848             if (!eventSet.add(e) && dndLog.isLoggable(Level.FINE)) {
       
   849                 dndLog.log(Level.FINE, "Event is already registered: " + e);
       
   850             }
       
   851             handler.unlock();
       
   852         }
       
   853 
       
   854         void unregisterEvent(SunDropTargetEvent e) {
       
   855             handler.lock();
       
   856             try {
       
   857                 if (!eventSet.remove(e)) {
       
   858                     // This event has already been unregistered.
       
   859                     return;
       
   860                 }
       
   861                 if (eventSet.isEmpty()) {
       
   862                     if (!dispatcherDone && dispatchType == DISPATCH_SYNC) {
       
   863                         handler.exit();
       
   864                     }
       
   865                     dispatcherDone = true;
       
   866                 }
       
   867             } finally {
       
   868                 handler.unlock();
       
   869             }
       
   870 
       
   871             try {
       
   872                 peer.eventProcessed(e, returnValue, dispatcherDone);
       
   873             } finally {
       
   874                 /*
       
   875                  * Clear the reference to the native context if all copies of
       
   876                  * the original event are processed.
       
   877                  */
       
   878                 if (dispatcherDone) {
       
   879                     nativeCtxt = 0;
       
   880                     // Fix for 6342381
       
   881                     peer.nativeDragContext = 0;
       
   882 
       
   883                 }
       
   884             }
       
   885         }
       
   886 
       
   887         public void unregisterAllEvents() {
       
   888             Object[] events = null;
       
   889             handler.lock();
       
   890             try {
       
   891                 events = eventSet.toArray();
       
   892             } finally {
       
   893                 handler.unlock();
       
   894             }
       
   895 
       
   896             if (events != null) {
       
   897                 for (int i = 0; i < events.length; i++) {
       
   898                     unregisterEvent((SunDropTargetEvent)events[i]);
       
   899                 }
       
   900             }
       
   901         }
       
   902     }
       
   903 }