JDK-5018702 : CURSOR APPEARANCE INDICATES DRAGGING IS SUPPORTED WHEN IT IS NOT
  • Type: Bug
  • Component: client-libs
  • Sub-Component: java.awt
  • Affected Version: 1.4.1
  • Priority: P3
  • Status: Closed
  • Resolution: Not an Issue
  • OS: windows_xp
  • CPU: x86
  • Submitted: 2004-03-23
  • Updated: 2004-03-24
  • Resolved: 2004-03-24
Related Reports
Relates :  
Description
blic Object getTransferData( DataFlavor flavor )
      {
         if (flavor.equals( objectsFlavor ))
            return this;
         else if (flavor.equals( DataFlavor.stringFlavor ) )
            return this.toString();
            
         return null;
      }

      public DataFlavor[] getTransferDataFlavors()
      {
         return new DataFlavor[] { objectsFlavor, DataFlavor.stringFlavor };
      }

      public boolean isDataFlavorSupported(DataFlavor flavor)
      {
         return (flavor.equals( objectsFlavor ) || flavor.equals( DataFlavor.stringFlavor ));
      }
   }
}


======================================================================


Name: rv122619			Date: 03/23/2004

When attempting to drag, cursor appearance changes to indicate dragging is allowed when dragging is not supported.  The cursor's appearance should not change to indicate dragging if dragging is not allowed.  

/**
 * Title:        MainFrame.java
 * Description:  
 */


import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Point;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DragGestureEvent;
import java.awt.dnd.DragGestureListener;
import java.awt.dnd.DragSource;
import java.awt.dnd.DragSourceDragEvent;
import java.awt.dnd.DragSourceDropEvent;
import java.awt.dnd.DragSourceEvent;
import java.awt.dnd.DragSourceListener;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener;
import java.awt.event.ActionEvent;
import java.awt.event.WindowEvent;
import java.util.ArrayList;

import javax.swing.AbstractAction;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;

/*
 * 
 * 
 * 
 * 
 */
public class MainFrame extends JFrame
{
   private AbstractAction m_actFile;
   private AbstractAction m_actExit;

   private JTree            m_tree;
   private DefaultTreeModel m_mdl;
   
   private DragSourceListener m_lsnrDragSource;
   private DragSource         m_dragSource;
   
   
   public MainFrame()
   {
      setDefaultCloseOperation( EXIT_ON_CLOSE );
      initialize();
      setLocation( new Point( 200, 200 ) );
      setSize( new Dimension( 550, 250 ) );
   }

   private void initialize()
   {
      setTitle( "Drag And Drop Test" );

      createActions();
      createMainMenu();
      createContents();
      createListeners();
   }
   
   private void createActions()
   {
      m_actFile = new cFileAction();
      m_actExit = new cExitAction();
   }

   private void createListeners()
   {
      m_dragSource = new DragSource();
      m_dragSource.createDefaultDragGestureRecognizer( m_tree, DnDConstants.ACTION_COPY_OR_MOVE, new cDragGestureListener() );
      
      m_lsnrDragSource = new cDragSourceListener();

      new DropTarget( m_tree, DnDConstants.ACTION_COPY_OR_MOVE, new cDropTargetListener() );
   }
      
   private void createMainMenu()
   {
      JMenuBar menuMain = new JMenuBar();
      
      JMenu menuFile = new JMenu( m_actFile );
      menuFile.add( m_actExit );
      
      menuMain.add( menuFile );
      
      setJMenuBar( menuMain );
   }

   private void createContents()
   {
      m_tree = new JTree();
      m_mdl  = (DefaultTreeModel) m_tree.getModel();
      expand( m_tree, (TreeNode) m_mdl.getRoot() );
      
      m_tree.getSelectionModel().setSelectionMode( TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION );
      m_tree.setAutoscrolls( true );
   
      JScrollPane scrTree = new JScrollPane( m_tree );
      JPanel pnlContents = (JPanel) getContentPane();
      pnlContents.setLayout( new GridBagLayout() );
      pnlContents.add( scrTree, new GridBagConstraints( 0, 0, 1, 1, 1, 1, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets( 0, 0, 0, 0 ), 0, 0 ) );
   }
   
   private void expand( JTree tree, TreeNode node )
   {
      if (node.isLeaf())
         return;
      
      TreeNode[] aPathNodes = ((DefaultTreeModel) m_tree.getModel()).getPathToRoot( node );
      tree.expandPath( new TreePath( aPathNodes ) );

      for ( int iChild=0; iChild<node.getChildCount(); iChild++ )
         expand( tree, node.getChildAt( iChild ) );
   }

   public static void main( String[] saArgs )
   {
      // set the UI to the system UI
      try
      {
         UIManager.setLookAndFeel( UIManager.getSystemLookAndFeelClassName() );
      }
      catch (ClassNotFoundException          e) {}
      catch (IllegalAccessException          e) {}
      catch (InstantiationException          e) {}
      catch (UnsupportedLookAndFeelException e) {}

      // create the main frame and show it
      MainFrame f = new MainFrame();
      f.setVisible( true );
   }

   //---------------------------------------------------------------------------
   // Actions      
   //---------------------------------------------------------------------------
   protected class cFileAction extends AbstractAction
   {
      public cFileAction()
      {
         putValue( AbstractAction.NAME,              "File"             );
         putValue( AbstractAction.SHORT_DESCRIPTION, "Files"            );
         putValue( AbstractAction.MNEMONIC_KEY,      new Integer( 'F' ) );
         putValue( AbstractAction.SMALL_ICON,        null               );
      }
      
      public void actionPerformed( ActionEvent e )
      {
      }
   } // cFileAction
   
   protected class cExitAction extends AbstractAction
   {
      public cExitAction()
      {
         putValue( AbstractAction.NAME,              "Exit"             );
         putValue( AbstractAction.SHORT_DESCRIPTION, "Exits"            );
         putValue( AbstractAction.MNEMONIC_KEY,      new Integer( 'x' ) );
         putValue( AbstractAction.SMALL_ICON,        null               );
      }
      
      public void actionPerformed( ActionEvent e )
      {
         // there should be a better way to do this but there is no close
         // and this seems to be what the system exit menu does, so ...
         MainFrame.this.dispatchEvent( new WindowEvent( MainFrame.this,  WindowEvent.WINDOW_CLOSING ) );
      }
   } // cExitAction
   
   //---------------------------------------------------------------------------
   //---------------------------------------------------------------------------
   protected class cDragGestureListener implements DragGestureListener
   {
      public void dragGestureRecognized(DragGestureEvent event)
      {
         TreePath[] aSelectedPaths = m_tree.getSelectionPaths();
         if (aSelectedPaths.length != 0)
         {
            DraggedObjects draggedObjects = new DraggedObjects();
            
            for ( int iSelectedPath=0; iSelectedPath < aSelectedPaths.length; iSelectedPath++ )
            {
               TreeNode node = (TreeNode) aSelectedPaths[ iSelectedPath ].getLastPathComponent();
               if (!node.isLeaf())
                  return;
               System.out.println( "Dragging " + node );
               draggedObjects.add( node );
            }

            m_dragSource.startDrag( event,
                                    DragSource.DefaultCopyDrop,
//                                    image, new Point( 0, 0 ),
                                    draggedObjects, m_lsnrDragSource );
         }
      }
   } // cDragGestureListener


   protected class cDragSourceListener implements DragSourceListener
   {
      /**
       * Handles the drag operation termination event.  This event occurs for
       * all types of termination (accepted drops, rejected drops, and
       * terminations that occur outside of any drop target).  This event is
       * handled by changing the dragging flag back to false.
       *
       * @param e the drag event for the drag source
       */
      public void dragDropEnd( DragSourceDropEvent e )
      {
//       System.out.println( "drag source end" );
      }

      /**
       * Handles the drag entering a drop target event.  This event is handled
       * by doing nothing.
       *
       * @param e the drag event for the drag source
       */
      public void dragEnter( DragSourceDragEvent e )
      {
//       System.out.println( "drag source enter" );
      }

      /**
       * Handles the drag exiting a drop target event.  This event is handled by
       * doing nothing.
       *
       * @param e the event for the drag source
       */
      public void dragExit( DragSourceEvent event )
      {
//       System.out.println( "drag source exit" );
      }

      /**
       * Handles the drag over the drop target event.  This event is handled by
       * doing nothing.
       *
       * @param e the drag event for the drag source
       */
      public void dragOver( DragSourceDragEvent e )
      {
//       System.out.println( "drag source over" );
      }

      /**
       * Handles the drop action being changed by the user (by changing the drag
       * gesture).  This event is handled by doing nothing.
       *
       * @param e the drag event for the drag source
       */
      public void dropActionChanged( DragSourceDragEvent e )
      {
//       System.out.println( "drag source action changed" );
      }
   }

   //---------------------------------------------------------------------------
   // cDropTargetListener - handles drop target events by checking to make sure
   //                       the drag operation is over a node that can be a drop
   //                       target.
   //---------------------------------------------------------------------------
   protected class cDropTargetListener implements DropTargetListener
   {
      protected boolean    m_bFirstDragOver;           // true = next DragOver is first DragOver
                                                       // for drag cursor kludge
      protected TreePath[] m_aOriginalSelectedPaths;   // the originally selected paths

      /**
       * Handles the drag operation entering the drop target event.  The event
       * is handled by selecting the node (if any) over which the drag occurred.
       * The drag is accepted if the drag is over a node that can be a drop
       * target.
       *
       * @param e the drag entering the drop target event.
       */
      public void dragEnter( DropTargetDragEvent e )
      {
//       System.out.println( "drop target enter" );

         // save the originally selected path
         m_aOriginalSelectedPaths = m_tree.getSelectionPaths();

         // get and select the node that drag event is over
         Point    pt   = e.getLocation();
         TreePath path = m_tree.getPathForLocation( pt.x, pt.y );
         m_tree.setSelectionPath( path );

         // if not over any node, reject
         if (path == null)
            e.rejectDrag();

         // otherwise, ...
         else
         {
            // if the node is not a drop target, reject
            // otherwise, accept
            TreeNode node = (TreeNode) path.getLastPathComponent();
            if (node.isLeaf() || m_mdl.getRoot() == node)
            {
               System.out.println( "drag rejected in enter" );
               e.rejectDrag();
            }
            else
            {
               System.out.println( "drag accepted in enter" );
               e.acceptDrag( e.getSourceActions() );
            }
         }
      }

      /**
       * Handles the event that the drag operation has exited the drop target.
       * The event is handled by reselecting the originally selected paths.
       *
       * @param e the drop target event
       */
      public void dragExit( DropTargetEvent e )
      {
//       System.out.println( "drop target exit" );
         m_tree.setSelectionPaths( m_aOriginalSelectedPaths );
      }

      /**
       * Handles the drag over the drop target event.  The event is handled by
       * by selecting the node (if any) over which the drag occurred.  The drag
       * is accepted if the drag is over a node that can be a drop target.
       *
       * @param e the drag over the drop target event.
       */
      public void dragOver( DropTargetDragEvent e )
      {
//       System.out.println( "drop target drag over" );

         // get and select the node that drag event is over
         Point    pt   = e.getLocation();
         TreePath path = m_tree.getPathForLocation( pt.x, pt.y );
         if (!m_tree.isPathSelected( path ))
            m_tree.setSelectionPath( path );

         // if not over any node, reject
         if (path == null)
            e.rejectDrag();

         // otherwise, ...
         else
         {
            // if the node is not a drop target, reject
            // otherwise, accept
            TreeNode node = (TreeNode) path.getLastPathComponent();
            if (node.isLeaf() || m_mdl.getRoot() == node)
            {
               System.out.println( "drag rejected in over" );
               e.rejectDrag();
            }
            else
            {
               System.out.println( "drag accepted in over" );
               e.acceptDrag( e.getSourceActions() );
            }
         }
      }

      /**
       * Handles the event when the drag operation has terminated with a drop on
       * the drop target.  The event is handled by by selecting the node (if
       * any) on which the drop occurred.  The drop is accepted if the drop is
       * on a node that can be a drop target.  The node is then notified of the
       * drop.
       *
       * @param e the drop event on the drop target
       */
      public void drop( DropTargetDropEvent e )
      {
//       System.out.println( "drop target drop" );

         // get and select the node that drag event is over
         Point    pt   = e.getLocation();
         TreePath path = m_tree.getPathForLocation( pt.x, pt.y );
         if (!m_tree.isPathSelected( path ))
            m_tree.setSelectionPath( path );

         // if not over any node, reject
         if (path == null)
            e.rejectDrop();

         // otherwise, ...
         else
         {
            // if the node is a drop target, accept and notify the node of the drop
            // otherwise, reject
            // if the node is not a drop target, reject
            // otherwise, accept
            TreeNode node = (TreeNode) path.getLastPathComponent();
            if (node.isLeaf() || m_mdl.getRoot() == node)
            {
               System.out.println( "Drop rejected" );
               e.rejectDrop();
            }
            else
            {
               e.acceptDrop( e.getSourceActions() );
               System.out.println( "Dropped on " + node );
               e.dropComplete( true );
            }
         }
      }

      /**
       * Handles the event when the user changes the current drop gesture.  The
       * event is handled by doing nothing.
       *
       * @param e the new drop target drag event
       */
      public void dropActionChanged( DropTargetDragEvent e )
      {
      }

   } // cDropTargetListener

   protected static class DraggedObjects extends ArrayList implements Transferable
   {
      public static final DataFlavor objectsFlavor = new DataFlavor( DraggedObjects.class, "Objects" );

      pu

Comments
WORK AROUND Name: rv122619 Date: 03/23/2004 None ======================================================================
24-08-2004

EVALUATION There does seem to be a problem here. The interesting aspect of this is that if you try dragging data from _another_ application, this example works as it is supposed to. That is, some nodes reject dragging and others accept it. So, the problem is related to the fact that the drag initiates with the same component. Re-assigning to drag&drop to investigate. Note: I am concerned about how this may affect Swing's drag and drop support (although I haven't anything concrete). It's still a fairly immature, and difficult area, and I'd like to see any bugs that might interfere with it fixed. ###@###.### 2004-03-23 Name: agR10216 Date: 03/24/2004 May be related to 4774532: Incorrect drop action after rejectDrag(). ###@###.### 2004-03-24 ====================================================================== Name: agR10216 Date: 03/24/2004 Actually the reason why the copy cursor is always shown on drags is that a custom cursor is used in the test (non-null cursor is specified in the startDrag()). So it is never updated. This behaviour was documented in 1.4.1: see the bug 4455820. If startDrag() takes null cursor, the drag cursor is updated properly. ###@###.### 2004-03-24 ======================================================================
24-03-2004