JDK-6700748 : Cursor flickering during D&D when using CellRendererPane with validation
  • Type: Bug
  • Component: client-libs
  • Sub-Component: java.awt
  • Affected Version: 6
  • Priority: P3
  • Status: Open
  • Resolution: Unresolved
  • OS: windows_xp
  • CPU: x86
  • Submitted: 2008-05-12
  • Updated: 2011-04-28
Description
FULL PRODUCT VERSION :
1.6.0_05 and newer including Java 6 Update 10

ADDITIONAL OS VERSION INFORMATION :
Windows XP

A DESCRIPTION OF THE PROBLEM :
During a drag the mouse cursor flickers, meaning it changes between the cursor used for the drag and drop and the cursor of the component underneath the cursor. This only happens if the component uses a CellRendererPane and then invokes the pane's paintComponent() method with the validate flag turned on ("true").

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
An SSCCE has been attached. Simply initiate a drag and drop sequence in the left panel and try dragging to the right panel. Each panel shows a button, which gets painted by a renderer and the CellRendererPane. If the checkbox in the upper left corner is selected the renderer will be called with the "validate" flag set to TRUE. The cursor flickering only happens if validation is turned on.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
When the user initiates the drag and drop sequence the cursor should show the drag mouse cursor and only when the user lets go of the mouse button the cursor should change back to the default cursor.
ACTUAL -
Cursor changes between two different shapes during drag and drop. You will see the default arrow cursor and then the drag cursor (with the rectangle in the lower right corner).

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
package com.dlsc.test;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
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.io.IOException;

import javax.swing.CellRendererPane;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.border.TitledBorder;


public class DnDTestFrame extends JFrame {
private JCheckBox checkBox = new JCheckBox("Cell renderer validates", true);

public DnDTestFrame() {
super("DnDTest");

add(BorderLayout.NORTH, checkBox);
add(BorderLayout.WEST,new DnDTestPanel("Left", this));
add(BorderLayout.EAST,new DnDTestPanel("Right", this));
pack();
setVisible(true);
}

public boolean isValidating() {
return checkBox.isSelected();
}

/**
* @param args
*/
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new DnDTestFrame();
}
});
}
}


class DnDTestPanel extends JPanel implements DragGestureListener,
DragSourceListener, DropTargetListener {

private String title;
private CellRendererPane rendererPane = new CellRendererPane();
private JButton rendererComp = new JButton("Renderer");
private DnDTestFrame frame;

public DnDTestPanel(String title, DnDTestFrame frame) {
this.title = title;
this.frame = frame;

setLayout(null);
add(rendererPane);

setBackground(Color.WHITE);
setPreferredSize(new Dimension(300, 300));
setBorder(new TitledBorder(title));
DragSource.getDefaultDragSource().createDefaultDragGestureRecognizer(
this, DnDConstants.ACTION_COPY_OR_MOVE, this);
new DropTarget(this, DnDConstants.ACTION_COPY_OR_MOVE, this);
}

@Override
protected void paintComponent(Graphics g) {
rendererPane.paintComponent(g, rendererComp, this, 20, 20, 100, 30,
frame.isValidating());
}

public void dragGestureRecognized(DragGestureEvent dge) {
dge.startDrag(null, new Transferable() {

public Object getTransferData(DataFlavor flavor)
throws UnsupportedFlavorException, IOException {
return null;
}

public DataFlavor[] getTransferDataFlavors() {
return new DataFlavor[] { DataFlavor.plainTextFlavor };
}

public boolean isDataFlavorSupported(DataFlavor flavor) {
return true;
}
});
}

// drag source
public void dragDropEnd(DragSourceDropEvent dsde) {
}

public void dragEnter(DragSourceDragEvent dsde) {
}

public void dragExit(DragSourceEvent dse) {
}

public void dragOver(DragSourceDragEvent dsde) {
}

public void dropActionChanged(DragSourceDragEvent dsde) {
}

// drop target
public void dragEnter(DropTargetDragEvent dtde) {
repaint();
}

public void dragExit(DropTargetEvent dte) {
repaint();
}

public void dragOver(DropTargetDragEvent dtde) {
repaint();
}

public void drop(DropTargetDropEvent dtde) {
}

public void dropActionChanged(DropTargetDragEvent dtde) {
}
}

---------- END SOURCE ----------

Comments
EVALUATION The issue looks like a problem in AWT Cursor implementation, there are two conflicting ::SetCursor calls in: - Cursor implementation - AwtCursor::GlobalSetCursor updates the global cursor when container is validated (the attached testcase uses CellRendererPane with validation). - DnD implementation - AwtDragSource::GiveFeedback updates the global cursor when dragging objects. One after another, these calls set the global cursor to its cursors. The cursors are different and this causes a flickering effect.
27-05-2008