Home » openjdk-7 » java » awt » dnd » [javadoc | source]

    1   /*
    2    * Copyright (c) 1997, 2008, 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 java.awt.dnd;
   27   
   28   import java.awt.Component;
   29   import java.awt.Cursor;
   30   import java.awt.Image;
   31   import java.awt.Point;
   32   
   33   import java.awt.datatransfer.DataFlavor;
   34   import java.awt.datatransfer.Transferable;
   35   import java.awt.datatransfer.UnsupportedFlavorException;
   36   
   37   import java.awt.dnd.peer.DragSourceContextPeer;
   38   
   39   import java.io.IOException;
   40   import java.io.ObjectOutputStream;
   41   import java.io.ObjectInputStream;
   42   import java.io.Serializable;
   43   
   44   import java.util.TooManyListenersException;
   45   
   46   /**
   47    * The <code>DragSourceContext</code> class is responsible for managing the
   48    * initiator side of the Drag and Drop protocol. In particular, it is responsible
   49    * for managing drag event notifications to the
   50    * {@linkplain DragSourceListener DragSourceListeners}
   51    * and {@linkplain DragSourceMotionListener DragSourceMotionListeners}, and providing the
   52    * {@link Transferable} representing the source data for the drag operation.
   53    * <p>
   54    * Note that the <code>DragSourceContext</code> itself
   55    * implements the <code>DragSourceListener</code> and
   56    * <code>DragSourceMotionListener</code> interfaces.
   57    * This is to allow the platform peer
   58    * (the {@link DragSourceContextPeer} instance)
   59    * created by the {@link DragSource} to notify
   60    * the <code>DragSourceContext</code> of
   61    * state changes in the ongoing operation. This allows the
   62    * <code>DragSourceContext</code> object to interpose
   63    * itself between the platform and the
   64    * listeners provided by the initiator of the drag operation.
   65    * <p>
   66    * <a name="defaultCursor" />
   67    * By default, {@code DragSourceContext} sets the cursor as appropriate
   68    * for the current state of the drag and drop operation. For example, if
   69    * the user has chosen {@linkplain DnDConstants#ACTION_MOVE the move action},
   70    * and the pointer is over a target that accepts
   71    * the move action, the default move cursor is shown. When
   72    * the pointer is over an area that does not accept the transfer,
   73    * the default "no drop" cursor is shown.
   74    * <p>
   75    * This default handling mechanism is disabled when a custom cursor is set
   76    * by the {@link #setCursor} method. When the default handling is disabled,
   77    * it becomes the responsibility
   78    * of the developer to keep the cursor up to date, by listening
   79    * to the {@code DragSource} events and calling the {@code setCursor()} method.
   80    * Alternatively, you can provide custom cursor behavior by providing
   81    * custom implementations of the {@code DragSource}
   82    * and the {@code DragSourceContext} classes.
   83    *
   84    * @see DragSourceListener
   85    * @see DragSourceMotionListener
   86    * @see DnDConstants
   87    * @since 1.2
   88    */
   89   
   90   public class DragSourceContext
   91       implements DragSourceListener, DragSourceMotionListener, Serializable {
   92   
   93       private static final long serialVersionUID = -115407898692194719L;
   94   
   95       // used by updateCurrentCursor
   96   
   97       /**
   98        * An <code>int</code> used by updateCurrentCursor()
   99        * indicating that the <code>Cursor</code> should change
  100        * to the default (no drop) <code>Cursor</code>.
  101        */
  102       protected static final int DEFAULT = 0;
  103   
  104       /**
  105        * An <code>int</code> used by updateCurrentCursor()
  106        * indicating that the <code>Cursor</code>
  107        * has entered a <code>DropTarget</code>.
  108        */
  109       protected static final int ENTER   = 1;
  110   
  111       /**
  112        * An <code>int</code> used by updateCurrentCursor()
  113        * indicating that the <code>Cursor</code> is
  114        * over a <code>DropTarget</code>.
  115        */
  116       protected static final int OVER    = 2;
  117   
  118       /**
  119        * An <code>int</code> used by updateCurrentCursor()
  120        * indicating that the user operation has changed.
  121        */
  122   
  123       protected static final int CHANGED = 3;
  124   
  125       /**
  126        * Called from <code>DragSource</code>, this constructor creates a new
  127        * <code>DragSourceContext</code> given the
  128        * <code>DragSourceContextPeer</code> for this Drag, the
  129        * <code>DragGestureEvent</code> that triggered the Drag, the initial
  130        * <code>Cursor</code> to use for the Drag, an (optional)
  131        * <code>Image</code> to display while the Drag is taking place, the offset
  132        * of the <code>Image</code> origin from the hotspot at the instant of the
  133        * triggering event, the <code>Transferable</code> subject data, and the
  134        * <code>DragSourceListener</code> to use during the Drag and Drop
  135        * operation.
  136        * <br>
  137        * If <code>DragSourceContextPeer</code> is <code>null</code>
  138        * <code>NullPointerException</code> is thrown.
  139        * <br>
  140        * If <code>DragGestureEvent</code> is <code>null</code>
  141        * <code>NullPointerException</code> is thrown.
  142        * <br>
  143        * If <code>Cursor</code> is <code>null</code> no exception is thrown and
  144        * the default drag cursor behavior is activated for this drag operation.
  145        * <br>
  146        * If <code>Image</code> is <code>null</code> no exception is thrown.
  147        * <br>
  148        * If <code>Image</code> is not <code>null</code> and the offset is
  149        * <code>null</code> <code>NullPointerException</code> is thrown.
  150        * <br>
  151        * If <code>Transferable</code> is <code>null</code>
  152        * <code>NullPointerException</code> is thrown.
  153        * <br>
  154        * If <code>DragSourceListener</code> is <code>null</code> no exception
  155        * is thrown.
  156        *
  157        * @param dscp       the <code>DragSourceContextPeer</code> for this drag
  158        * @param trigger    the triggering event
  159        * @param dragCursor     the initial {@code Cursor} for this drag operation
  160        *                       or {@code null} for the default cursor handling;
  161        *                       see <a href="DragSourceContext.html#defaultCursor">class level documentation</a>
  162        *                       for more details on the cursor handling mechanism during drag and drop
  163        * @param dragImage  the <code>Image</code> to drag (or <code>null</code>)
  164        * @param offset     the offset of the image origin from the hotspot at the
  165        *                   instant of the triggering event
  166        * @param t          the <code>Transferable</code>
  167        * @param dsl        the <code>DragSourceListener</code>
  168        *
  169        * @throws IllegalArgumentException if the <code>Component</code> associated
  170        *         with the trigger event is <code>null</code>.
  171        * @throws IllegalArgumentException if the <code>DragSource</code> for the
  172        *         trigger event is <code>null</code>.
  173        * @throws IllegalArgumentException if the drag action for the
  174        *         trigger event is <code>DnDConstants.ACTION_NONE</code>.
  175        * @throws IllegalArgumentException if the source actions for the
  176        *         <code>DragGestureRecognizer</code> associated with the trigger
  177        *         event are equal to <code>DnDConstants.ACTION_NONE</code>.
  178        * @throws NullPointerException if dscp, trigger, or t are null, or
  179        *         if dragImage is non-null and offset is null
  180        */
  181       public DragSourceContext(DragSourceContextPeer dscp,
  182                                DragGestureEvent trigger, Cursor dragCursor,
  183                                Image dragImage, Point offset, Transferable t,
  184                                DragSourceListener dsl) {
  185           if (dscp == null) {
  186               throw new NullPointerException("DragSourceContextPeer");
  187           }
  188   
  189           if (trigger == null) {
  190               throw new NullPointerException("Trigger");
  191           }
  192   
  193           if (trigger.getDragSource() == null) {
  194               throw new IllegalArgumentException("DragSource");
  195           }
  196   
  197           if (trigger.getComponent() == null) {
  198               throw new IllegalArgumentException("Component");
  199           }
  200   
  201           if (trigger.getSourceAsDragGestureRecognizer().getSourceActions() ==
  202                    DnDConstants.ACTION_NONE) {
  203               throw new IllegalArgumentException("source actions");
  204           }
  205   
  206           if (trigger.getDragAction() == DnDConstants.ACTION_NONE) {
  207               throw new IllegalArgumentException("no drag action");
  208           }
  209   
  210           if (t == null) {
  211               throw new NullPointerException("Transferable");
  212           }
  213   
  214           if (dragImage != null && offset == null) {
  215               throw new NullPointerException("offset");
  216           }
  217   
  218           peer         = dscp;
  219           this.trigger = trigger;
  220           cursor       = dragCursor;
  221           transferable = t;
  222           listener     = dsl;
  223           sourceActions =
  224               trigger.getSourceAsDragGestureRecognizer().getSourceActions();
  225   
  226           useCustomCursor = (dragCursor != null);
  227   
  228           updateCurrentCursor(trigger.getDragAction(), getSourceActions(), DEFAULT);
  229       }
  230   
  231       /**
  232        * Returns the <code>DragSource</code>
  233        * that instantiated this <code>DragSourceContext</code>.
  234        *
  235        * @return the <code>DragSource</code> that
  236        *   instantiated this <code>DragSourceContext</code>
  237        */
  238   
  239       public DragSource   getDragSource() { return trigger.getDragSource(); }
  240   
  241       /**
  242        * Returns the <code>Component</code> associated with this
  243        * <code>DragSourceContext</code>.
  244        *
  245        * @return the <code>Component</code> that started the drag
  246        */
  247   
  248       public Component    getComponent() { return trigger.getComponent(); }
  249   
  250       /**
  251        * Returns the <code>DragGestureEvent</code>
  252        * that initially triggered the drag.
  253        *
  254        * @return the Event that triggered the drag
  255        */
  256   
  257       public DragGestureEvent getTrigger() { return trigger; }
  258   
  259       /**
  260        * Returns a bitwise mask of <code>DnDConstants</code> that
  261        * represent the set of drop actions supported by the drag source for the
  262        * drag operation associated with this <code>DragSourceContext</code>.
  263        *
  264        * @return the drop actions supported by the drag source
  265        */
  266       public int  getSourceActions() {
  267           return sourceActions;
  268       }
  269   
  270       /**
  271        * Sets the cursor for this drag operation to the specified
  272        * <code>Cursor</code>.  If the specified <code>Cursor</code>
  273        * is <code>null</code>, the default drag cursor behavior is
  274        * activated for this drag operation, otherwise it is deactivated.
  275        *
  276        * @param c     the initial {@code Cursor} for this drag operation,
  277        *                       or {@code null} for the default cursor handling;
  278        *                       see {@linkplain Cursor class
  279        *                       level documentation} for more details
  280        *                       on the cursor handling during drag and drop
  281        *
  282        */
  283   
  284       public synchronized void setCursor(Cursor c) {
  285           useCustomCursor = (c != null);
  286           setCursorImpl(c);
  287       }
  288   
  289       /**
  290        * Returns the current drag <code>Cursor</code>.
  291        * <P>
  292        * @return the current drag <code>Cursor</code>
  293        */
  294   
  295       public Cursor getCursor() { return cursor; }
  296   
  297       /**
  298        * Add a <code>DragSourceListener</code> to this
  299        * <code>DragSourceContext</code> if one has not already been added.
  300        * If a <code>DragSourceListener</code> already exists,
  301        * this method throws a <code>TooManyListenersException</code>.
  302        * <P>
  303        * @param dsl the <code>DragSourceListener</code> to add.
  304        * Note that while <code>null</code> is not prohibited,
  305        * it is not acceptable as a parameter.
  306        * <P>
  307        * @throws TooManyListenersException if
  308        * a <code>DragSourceListener</code> has already been added
  309        */
  310   
  311       public synchronized void addDragSourceListener(DragSourceListener dsl) throws TooManyListenersException {
  312           if (dsl == null) return;
  313   
  314           if (equals(dsl)) throw new IllegalArgumentException("DragSourceContext may not be its own listener");
  315   
  316           if (listener != null)
  317               throw new TooManyListenersException();
  318           else
  319               listener = dsl;
  320       }
  321   
  322       /**
  323        * Removes the specified <code>DragSourceListener</code>
  324        * from  this <code>DragSourceContext</code>.
  325        *
  326        * @param dsl the <code>DragSourceListener</code> to remove;
  327        *     note that while <code>null</code> is not prohibited,
  328        *     it is not acceptable as a parameter
  329        */
  330   
  331       public synchronized void removeDragSourceListener(DragSourceListener dsl) {
  332           if (listener != null && listener.equals(dsl)) {
  333               listener = null;
  334           } else
  335               throw new IllegalArgumentException();
  336       }
  337   
  338       /**
  339        * Notifies the peer that the <code>Transferable</code>'s
  340        * <code>DataFlavor</code>s have changed.
  341        */
  342   
  343       public void transferablesFlavorsChanged() {
  344           if (peer != null) peer.transferablesFlavorsChanged();
  345       }
  346   
  347       /**
  348        * Calls <code>dragEnter</code> on the
  349        * <code>DragSourceListener</code>s registered with this
  350        * <code>DragSourceContext</code> and with the associated
  351        * <code>DragSource</code>, and passes them the specified
  352        * <code>DragSourceDragEvent</code>.
  353        *
  354        * @param dsde the <code>DragSourceDragEvent</code>
  355        */
  356       public void dragEnter(DragSourceDragEvent dsde) {
  357           DragSourceListener dsl = listener;
  358           if (dsl != null) {
  359               dsl.dragEnter(dsde);
  360           }
  361           getDragSource().processDragEnter(dsde);
  362   
  363           updateCurrentCursor(getSourceActions(), dsde.getTargetActions(), ENTER);
  364       }
  365   
  366       /**
  367        * Calls <code>dragOver</code> on the
  368        * <code>DragSourceListener</code>s registered with this
  369        * <code>DragSourceContext</code> and with the associated
  370        * <code>DragSource</code>, and passes them the specified
  371        * <code>DragSourceDragEvent</code>.
  372        *
  373        * @param dsde the <code>DragSourceDragEvent</code>
  374        */
  375       public void dragOver(DragSourceDragEvent dsde) {
  376           DragSourceListener dsl = listener;
  377           if (dsl != null) {
  378               dsl.dragOver(dsde);
  379           }
  380           getDragSource().processDragOver(dsde);
  381   
  382           updateCurrentCursor(getSourceActions(), dsde.getTargetActions(), OVER);
  383       }
  384   
  385       /**
  386        * Calls <code>dragExit</code> on the
  387        * <code>DragSourceListener</code>s registered with this
  388        * <code>DragSourceContext</code> and with the associated
  389        * <code>DragSource</code>, and passes them the specified
  390        * <code>DragSourceEvent</code>.
  391        *
  392        * @param dse the <code>DragSourceEvent</code>
  393        */
  394       public void dragExit(DragSourceEvent dse) {
  395           DragSourceListener dsl = listener;
  396           if (dsl != null) {
  397               dsl.dragExit(dse);
  398           }
  399           getDragSource().processDragExit(dse);
  400   
  401           updateCurrentCursor(DnDConstants.ACTION_NONE, DnDConstants.ACTION_NONE, DEFAULT);
  402       }
  403   
  404       /**
  405        * Calls <code>dropActionChanged</code> on the
  406        * <code>DragSourceListener</code>s registered with this
  407        * <code>DragSourceContext</code> and with the associated
  408        * <code>DragSource</code>, and passes them the specified
  409        * <code>DragSourceDragEvent</code>.
  410        *
  411        * @param dsde the <code>DragSourceDragEvent</code>
  412        */
  413       public void dropActionChanged(DragSourceDragEvent dsde) {
  414           DragSourceListener dsl = listener;
  415           if (dsl != null) {
  416               dsl.dropActionChanged(dsde);
  417           }
  418           getDragSource().processDropActionChanged(dsde);
  419   
  420           updateCurrentCursor(getSourceActions(), dsde.getTargetActions(), CHANGED);
  421       }
  422   
  423       /**
  424        * Calls <code>dragDropEnd</code> on the
  425        * <code>DragSourceListener</code>s registered with this
  426        * <code>DragSourceContext</code> and with the associated
  427        * <code>DragSource</code>, and passes them the specified
  428        * <code>DragSourceDropEvent</code>.
  429        *
  430        * @param dsde the <code>DragSourceDropEvent</code>
  431        */
  432       public void dragDropEnd(DragSourceDropEvent dsde) {
  433           DragSourceListener dsl = listener;
  434           if (dsl != null) {
  435               dsl.dragDropEnd(dsde);
  436           }
  437           getDragSource().processDragDropEnd(dsde);
  438       }
  439   
  440       /**
  441        * Calls <code>dragMouseMoved</code> on the
  442        * <code>DragSourceMotionListener</code>s registered with the
  443        * <code>DragSource</code> associated with this
  444        * <code>DragSourceContext</code>, and them passes the specified
  445        * <code>DragSourceDragEvent</code>.
  446        *
  447        * @param dsde the <code>DragSourceDragEvent</code>
  448        * @since 1.4
  449        */
  450       public void dragMouseMoved(DragSourceDragEvent dsde) {
  451           getDragSource().processDragMouseMoved(dsde);
  452       }
  453   
  454       /**
  455        * Returns the <code>Transferable</code> associated with
  456        * this <code>DragSourceContext</code>.
  457        *
  458        * @return the <code>Transferable</code>
  459        */
  460       public Transferable getTransferable() { return transferable; }
  461   
  462       /**
  463        * If the default drag cursor behavior is active, this method
  464        * sets the default drag cursor for the specified actions
  465        * supported by the drag source, the drop target action,
  466        * and status, otherwise this method does nothing.
  467        *
  468        * @param sourceAct the actions supported by the drag source
  469        * @param targetAct the drop target action
  470        * @param status one of the fields <code>DEFAULT</code>,
  471        *               <code>ENTER</code>, <code>OVER</code>,
  472        *               <code>CHANGED</code>
  473        */
  474   
  475       protected synchronized void updateCurrentCursor(int sourceAct, int targetAct, int status) {
  476   
  477           // if the cursor has been previously set then dont do any defaults
  478           // processing.
  479   
  480           if (useCustomCursor) {
  481               return;
  482           }
  483   
  484           // do defaults processing
  485   
  486           Cursor c = null;
  487   
  488           switch (status) {
  489               default:
  490                   targetAct = DnDConstants.ACTION_NONE;
  491               case ENTER:
  492               case OVER:
  493               case CHANGED:
  494                   int    ra = sourceAct & targetAct;
  495   
  496                   if (ra == DnDConstants.ACTION_NONE) { // no drop possible
  497                       if ((sourceAct & DnDConstants.ACTION_LINK) == DnDConstants.ACTION_LINK)
  498                           c = DragSource.DefaultLinkNoDrop;
  499                       else if ((sourceAct & DnDConstants.ACTION_MOVE) == DnDConstants.ACTION_MOVE)
  500                           c = DragSource.DefaultMoveNoDrop;
  501                       else
  502                           c = DragSource.DefaultCopyNoDrop;
  503                   } else { // drop possible
  504                       if ((ra & DnDConstants.ACTION_LINK) == DnDConstants.ACTION_LINK)
  505                           c = DragSource.DefaultLinkDrop;
  506                       else if ((ra & DnDConstants.ACTION_MOVE) == DnDConstants.ACTION_MOVE)
  507                           c = DragSource.DefaultMoveDrop;
  508                       else
  509                           c = DragSource.DefaultCopyDrop;
  510                   }
  511           }
  512   
  513           setCursorImpl(c);
  514       }
  515   
  516       private void setCursorImpl(Cursor c) {
  517           if (cursor == null || !cursor.equals(c)) {
  518               cursor = c;
  519               if (peer != null) peer.setCursor(cursor);
  520           }
  521       }
  522   
  523       /**
  524        * Serializes this <code>DragSourceContext</code>. This method first
  525        * performs default serialization. Next, this object's
  526        * <code>Transferable</code> is written out if and only if it can be
  527        * serialized. If not, <code>null</code> is written instead. In this case,
  528        * a <code>DragSourceContext</code> created from the resulting deserialized
  529        * stream will contain a dummy <code>Transferable</code> which supports no
  530        * <code>DataFlavor</code>s. Finally, this object's
  531        * <code>DragSourceListener</code> is written out if and only if it can be
  532        * serialized. If not, <code>null</code> is written instead.
  533        *
  534        * @serialData The default serializable fields, in alphabetical order,
  535        *             followed by either a <code>Transferable</code> instance, or
  536        *             <code>null</code>, followed by either a
  537        *             <code>DragSourceListener</code> instance, or
  538        *             <code>null</code>.
  539        * @since 1.4
  540        */
  541       private void writeObject(ObjectOutputStream s) throws IOException {
  542           s.defaultWriteObject();
  543   
  544           s.writeObject(SerializationTester.test(transferable)
  545                         ? transferable : null);
  546           s.writeObject(SerializationTester.test(listener)
  547                         ? listener : null);
  548       }
  549   
  550       /**
  551        * Deserializes this <code>DragSourceContext</code>. This method first
  552        * performs default deserialization for all non-<code>transient</code>
  553        * fields. This object's <code>Transferable</code> and
  554        * <code>DragSourceListener</code> are then deserialized as well by using
  555        * the next two objects in the stream. If the resulting
  556        * <code>Transferable</code> is <code>null</code>, this object's
  557        * <code>Transferable</code> is set to a dummy <code>Transferable</code>
  558        * which supports no <code>DataFlavor</code>s.
  559        *
  560        * @since 1.4
  561        */
  562       private void readObject(ObjectInputStream s)
  563           throws ClassNotFoundException, IOException
  564       {
  565           s.defaultReadObject();
  566   
  567           transferable = (Transferable)s.readObject();
  568           listener = (DragSourceListener)s.readObject();
  569   
  570           // Implementation assumes 'transferable' is never null.
  571           if (transferable == null) {
  572               if (emptyTransferable == null) {
  573                   emptyTransferable = new Transferable() {
  574                           public DataFlavor[] getTransferDataFlavors() {
  575                               return new DataFlavor[0];
  576                           }
  577                           public boolean isDataFlavorSupported(DataFlavor flavor)
  578                           {
  579                               return false;
  580                           }
  581                           public Object getTransferData(DataFlavor flavor)
  582                               throws UnsupportedFlavorException
  583                           {
  584                               throw new UnsupportedFlavorException(flavor);
  585                           }
  586                       };
  587               }
  588               transferable = emptyTransferable;
  589           }
  590       }
  591   
  592       private static Transferable emptyTransferable;
  593   
  594       /*
  595        * fields
  596        */
  597   
  598       private transient DragSourceContextPeer peer;
  599   
  600       /**
  601        * The event which triggered the start of the drag.
  602        *
  603        * @serial
  604        */
  605       private DragGestureEvent    trigger;
  606   
  607       /**
  608        * The current drag cursor.
  609        *
  610        * @serial
  611        */
  612       private Cursor              cursor;
  613   
  614       private transient Transferable      transferable;
  615   
  616       private transient DragSourceListener    listener;
  617   
  618       /**
  619        * <code>true</code> if the custom drag cursor is used instead of the
  620        * default one.
  621        *
  622        * @serial
  623        */
  624       private boolean useCustomCursor;
  625   
  626       /**
  627        * A bitwise mask of <code>DnDConstants</code> that represents the set of
  628        * drop actions supported by the drag source for the drag operation associated
  629        * with this <code>DragSourceContext.</code>
  630        *
  631        * @serial
  632        */
  633       private final int sourceActions;
  634   }

Home » openjdk-7 » java » awt » dnd » [javadoc | source]