JDK-4942216 : javax.swing.Autoscroller doesn't check if the Component is showing
  • Type: Bug
  • Component: client-libs
  • Sub-Component: javax.swing
  • Affected Version: 1.4.0,6
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: generic,windows_2000
  • CPU: generic,x86
  • Submitted: 2003-10-22
  • Updated: 2006-04-26
  • Resolved: 2006-04-26
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 6
6 b82Fixed
Related Reports
Relates :  
Description
Name: rmT116609			Date: 10/22/2003


FULL PRODUCT VERSION :
java version "1.4.2_01"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.2_01-b06)
Java HotSpot(TM) Client VM (build 1.4.2_01-b06, mixed mode)

FULL OS VERSION :
Microsoft Windows 2000 [Version 5.00.2195]

A DESCRIPTION OF THE PROBLEM :
javax.swing.AutoScroller / JComponent has a problem when Autoscrolling in a JTextField when the JTextField has been hidden from another MouseListener.


STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1) Compile the code below
2) run the code
3) click the button to bring up the window
4) with the mouse click in the JTextField and drag the mouse out of the window


EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The window closes and no exception is raised.
ACTUAL -
the window closes and there are errors on the EventThread.

ERROR MESSAGES/STACK TRACES THAT OCCUR :
java.awt.IllegalComponentStateException: component must be showing on the screen
 to determine its location
        at java.awt.Component.getLocationOnScreen_NoTreeLock(Unknown Source)
        at java.awt.Component.getLocationOnScreen(Unknown Source)
        at javax.swing.Autoscroller.mouseDragged(Unknown Source)
        at javax.swing.JComponent.processMouseMotionEvent(Unknown Source)
        at java.awt.Component.processEvent(Unknown Source)
        at java.awt.Container.processEvent(Unknown Source)
        at java.awt.Component.dispatchEventImpl(Unknown Source)
        at java.awt.Container.dispatchEventImpl(Unknown Source)
        at java.awt.Component.dispatchEvent(Unknown Source)
        at java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source)
        at java.awt.LightweightDispatcher.processMouseEvent(Unknown Source)
        at java.awt.LightweightDispatcher.dispatchEvent(Unknown Source)
        at java.awt.Container.dispatchEventImpl(Unknown Source)
        at java.awt.Window.dispatchEventImpl(Unknown Source)
        at java.awt.Component.dispatchEvent(Unknown Source)
        at java.awt.EventQueue.dispatchEvent(Unknown Source)
        at java.awt.EventDispatchThread.pumpOneEventForHierarchy(Unknown Source)

        at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
        at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
        at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
        at java.awt.EventDispatchThread.run(Unknown Source)

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;


public class Whoops implements ActionListener, MouseListener {

	private JWindow w;
	private JPanel jp;
	
	public Whoops() {
		JFrame f = new JFrame("test");
		JButton jb = new JButton("Show Window");
		jb.addActionListener(this);
		f.getContentPane().add(jb);
		w = new JWindow(f);
		JTextField tf = new JTextField("Hello");
		jp = new JPanel(new GridBagLayout());
		jp.add(tf, new GridBagConstraints(0,0,1,1,1.0,1.0,
																			GridBagConstraints.LINE_START,
																			GridBagConstraints.BOTH,
																			new Insets(10,10,10,10),
																			0,0));
		jp.addMouseListener(this);
		w.getContentPane().add(jp);
		f.pack();
		w.pack();
		f.show();
	}

	public void mouseClicked(MouseEvent e){}
	public void mousePressed(MouseEvent e){}
	public void mouseReleased(MouseEvent e){}
	public void mouseEntered(MouseEvent e){}

	public void mouseExited(MouseEvent e){
		Point p = e.getPoint();
		if (!jp.getBounds().contains(p)) {
			w.hide();
			e.consume();
		}
	}

	public void actionPerformed(ActionEvent e) {
		w.setLocationRelativeTo((Component)e.getSource());
		w.show();
	}
	
	
	
	public static void main(String[] args) {
		new Whoops();
	}

	
}

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

CUSTOMER SUBMITTED WORKAROUND :
disable autoscrolling but that disables a usefull feature.

  Fix could be to check isShowing inside MouseDragged.
(Incident Review ID: 216719) 
======================================================================
Contribution by java.net member leouser:

A DESCRIPTION OF THE FIX :
BUGID: 4942216 javax.swing.Autoscroller doesn't check if the Component is showing
Files affected: javax.swing.Autoscroller
Autoscroller enhanced with java 6 version:
jdk-6-rc-bin-b64-linux-i586-15_dec_2005.bin

Discusion(embeded in test case as well):
/**
 * BUGID: 4942216 javax.swing.Autoscroller doesn't check if the Component is showing
 * Actually the Autoscroller does check if the component is showing, it just
 * doesn't do it comprehensively. actionPerformed has a good check in place
 * but _processMouseDragged does not, opening a window of opportunity for
 * an exception to be tossed as the test case shows.  I have refactored
 * the test in actionPerformed into its own test method.  This allows us
 * to do the test/stop code in one place.  It also simplifies the implementations
 * of actionPerformed and _processMouseDragged(if the same code was going to
 * be dropped into it).  Also if the Autoscroller gains new behavior and we
 * need to test for the exception condition then its just a matter of calling
 * the testForStop method.
 *
 * TESTING STRATEGY
 * To quote from the bug report since we use the code in it to show the problem:
 * "1) Compile the code below
 * 2) run the code
 * 3) click the button to bring up the window
 * 4) with the mouse click in the JTextField and drag the mouse out of the
 * window
 * EXPECTED VS ACTUAL BEHAVIOR:
 * EXPECTED-
 * The window closes and no exception is raised
 * ACTUAL-
 * the window closes and there are errors on the EventThread"
 * Test without the patch and it will show the problem.  Test with the patch
 * and there isn't any problem.
 *
 * Files affected: javax.swing.Autoscroller
 * Autoscroller enhanced with java 6 version:
 * jdk-6-rc-bin-b64-linux-i586-15_dec_2005.bin
 *
 * test ran succesfully on a SUSE 7.3 Linux distribution
 *
 * Brian Harry
 * ###@###.###
 * Jan 20, 2006
 */

UNIFIED DIFF:
--- /home/nstuff/java6/jdk1.6.0/javax/swing/Autoscroller.java	Thu Dec 15 02:17:33 2005
+++ /home/javarefs/javax/swing/Autoscroller.java	Fri Jan 20 10:44:54 2006
@@ -115,11 +115,21 @@
         return (c == component && timer != null && timer.isRunning());
     }
 
+    private boolean testForStop(JComponent component){
+        if (component == null || !component.isShowing() || (event == null)) {
+            _stop(component);
+            return true;
+	}
+	return false;
+    }
+
     /**
      * MouseListener method, invokes start/stop as necessary.
      */
     private void _processMouseDragged(MouseEvent e) {
         JComponent component = (JComponent)e.getComponent();
+
+	if(testForStop(component)) return;
 	Rectangle visibleRect = component.getVisibleRect();
 	boolean contains = visibleRect.contains(e.getX(), e.getY());
 
@@ -140,10 +150,7 @@
     public void actionPerformed(ActionEvent x) {
         JComponent component = Autoscroller.component;
 
-        if (component == null || !component.isShowing() || (event == null)) {
-            _stop(component);
-            return;
-        }
+        if(testForStop(component)) return;
         Point screenLocation = component.getLocationOnScreen();
         MouseEvent e = new MouseEvent(component, event.getID(),
                                       event.getWhen(), event.getModifiers(),


JUnit TESTCASE :
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;

/**
 * BUGID: 4942216 javax.swing.Autoscroller doesn't check if the Component is showing
 * Actually the Autoscroller does check if the component is showing, it just
 * doesn't do it comprehensively. actionPerformed has a good check in place
 * but _processMouseDragged does not, opening a window of opportunity for
 * an exception to be tossed as the test case shows.  I have refactored
 * the test in actionPerformed into its own test method.  This allows us
 * to do the test/stop code in one place.  It also simplifies the implementations
 * of actionPerformed and _processMouseDragged(if the same code was going to
 * be dropped into it).  Also if the Autoscroller gains new behavior and we
 * need to test for the exception condition then its just a matter of calling
 * the testForStop method.
 *
 * TESTING STRATEGY
 * To quote from the bug report since we use the code in it to show the problem:
 * "1) Compile the code below
 * 2) run the code
 * 3) click the button to bring up the window
 * 4) with the mouse click in the JTextField and drag the mouse out of the
 * window
 * EXPECTED VS ACTUAL BEHAVIOR:
 * EXPECTED-
 * The window closes and no exception is raised
 * ACTUAL-
 * the window closes and there are errors on the EventThread"
 * Test without the patch and it will show the problem.  Test with the patch
 * and there isn't any problem.
 *
 * Files affected: javax.swing.Autoscroller
 * Autoscroller enhanced with java 6 version:
 * jdk-6-rc-bin-b64-linux-i586-15_dec_2005.bin
 *
 * test ran succesfully on a SUSE 7.3 Linux distribution
 *
 * Brian Harry
 * ###@###.###
 * Jan 20, 2006
 */
public class TestAutoScroll extends MouseAdapter implements ActionListener{

    private JWindow w;
    private JPanel jp;

    public TestAutoScroll(){
	JFrame f = new JFrame("test");
	JButton jb = new JButton("Show Window");
	jb.addActionListener(this);
	f.getContentPane().add(jb);
	w = new JWindow(f);
	JTextField tf = new JTextField("Hello");
	jp =  new JPanel(new GridBagLayout());
	jp.add(tf, new GridBagConstraints(0,0,1,1,1.0,1.0,
                                          GridBagConstraints.LINE_START,
					  GridBagConstraints.BOTH,
                                          new Insets(10,10,10,10), 0,0));

	jp.addMouseListener(this);
	w.getContentPane().add(jp);
	f.pack();
	w.pack();
	f.show();
    }

    public void mouseExited(MouseEvent me){
	Point p = me.getPoint();
	if(!jp.getBounds().contains(p)){
	    System.out.println("Hiding the window!");
	    w.hide();
	    me.consume();
	}
    }

    public void actionPerformed(ActionEvent ae){
	w.setLocationRelativeTo((Component)ae.getSource());
	w.show();
    }

    public static void testAutoScroll(){
	Runnable run = new Runnable(){

		public void run(){
		    new TestAutoScroll();

		}

	};
	SwingUtilities.invokeLater(run);

    }

    public static void main(String ... args){

	testAutoScroll();


    }



}


FIX FOR BUG NUMBER:
4942216

Comments
EVALUATION The Swing workaround will be to make sure the component is showing before asking for the screen location.
31-03-2006

EVALUATION While this appears to be a bug in AWT, NetBeans and others would like it fixed in Swing ASAP. The change to AWT is a bit risky now, so we're going to work around it in Swing. As part of fixing 4084431 we'll remove the work around from Swing.
21-03-2006

WORK AROUND For working around the Swing problem before hiding the parent invoke setAutoscrolls(false) on the JComponent. This will force removing the MouseListener that is causing the problems.
16-03-2006

EVALUATION The core issue that is causing Autoscroller to NPE is AWT delivering a mouse event when the component is no longer showing. This appears to be specific to light weight components and is likely a bug in the LighweightDispatcher. I believe the LightweightDispatcher assumes the target component is showing for the life of a mouse drag session. This looks like a dup of 4084431, reassigning to AWT for further analysis.
27-02-2006

EVALUATION Contribution-forum:https://jdk-collaboration.dev.java.net/servlets/ProjectForumMessageView?forumID=1463&messageID=10979
23-01-2006