JDK-4508327 : REGRESSION: MouseEvent is incorrectly triggered by clicking JComboBox item
  • Type: Bug
  • Component: client-libs
  • Sub-Component: java.awt
  • Affected Version: 1.4.0
  • Priority: P2
  • Status: Closed
  • Resolution: Fixed
  • OS:
    generic,linux,windows_nt,windows_2000 generic,linux,windows_nt,windows_2000
  • CPU: generic,x86
  • Submitted: 2001-09-28
  • Updated: 2001-12-05
  • Resolved: 2001-11-21
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.
Other
1.4.0 rc1Fixed
Related Reports
Duplicate :  
Duplicate :  
Duplicate :  
Relates :  
Relates :  
Description

Name: rmT116609			Date: 09/27/2001


java version "1.4.0-beta2"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.0-beta2-b77)
Java HotSpot(TM) Client VM (build 1.4.0-beta2-b77, mixed mode)


I have a JFrame containing a JComboBox (north) and a JPanel (center).  Clicking
on the JComboBox drops down the list of items, when I click on one of these
items the mouse click event is sent to the JPanel.  (when the JComboBox drops
down it overlaps the JPanel).  This doesn't seem to happen in an earlier beta
of 1.4 I have on another machine (sorry can't get -version at the moment).

Demo:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class TestJComboBox extends JFrame
{
        public static void main(String[] args)
        {
                TestJComboBox   tjcb = new TestJComboBox();
        }


        public TestJComboBox()
        {
                JComboBox       combo = new JComboBox();
                JPanel          panel = new JPanel();

                combo.addItem("java");
                combo.addItem("rocks");

                panel.addMouseListener(new QuitMouseListener());

                getContentPane().setLayout(new BorderLayout());
                getContentPane().add(combo, BorderLayout.NORTH);
                getContentPane().add(panel, BorderLayout.CENTER);

                setSize(800, 600);
                setVisible(true);
        }

        class QuitMouseListener extends MouseAdapter
        {
                public void mouseClicked(MouseEvent ev)
                {
                        System.err.println("you clicked the panel!");
                        System.exit(0);
                }
        }
}
(Review ID: 132761) 
======================================================================

Comments
CONVERTED DATA BugTraq+ Release Management Values COMMIT TO FIX: merlin-rc1 FIXED IN: merlin-rc1 INTEGRATED IN: merlin-rc1 VERIFIED IN: merlin-rc1
14-06-2004

SUGGESTED FIX ------- MouseEvent.java ------- *** /tmp/dJLaOIQ Thu Nov 8 17:39:04 2001 --- MouseEvent.java Thu Nov 8 17:30:43 2001 *************** *** 588,606 **** if ((modifiers & BUTTON1_MASK) != 0) { button = BUTTON1; modifiers &= ~BUTTON2_MASK & ~BUTTON3_MASK; ! if (id == MOUSE_PRESSED) { modifiers &= ~BUTTON1_DOWN_MASK; } } else if ((modifiers & BUTTON2_MASK) != 0) { button = BUTTON2; modifiers &= ~BUTTON1_MASK & ~BUTTON3_MASK; ! if (id == MOUSE_PRESSED) { modifiers &= ~BUTTON2_DOWN_MASK; } } else if ((modifiers & BUTTON3_MASK) != 0) { button = BUTTON3; modifiers &= ~BUTTON1_MASK & ~BUTTON2_MASK; ! if (id == MOUSE_PRESSED) { modifiers &= ~BUTTON3_DOWN_MASK; } } --- 588,606 ---- if ((modifiers & BUTTON1_MASK) != 0) { button = BUTTON1; modifiers &= ~BUTTON2_MASK & ~BUTTON3_MASK; ! if (id != MOUSE_PRESSED) { modifiers &= ~BUTTON1_DOWN_MASK; } } else if ((modifiers & BUTTON2_MASK) != 0) { button = BUTTON2; modifiers &= ~BUTTON1_MASK & ~BUTTON3_MASK; ! if (id != MOUSE_PRESSED) { modifiers &= ~BUTTON2_DOWN_MASK; } } else if ((modifiers & BUTTON3_MASK) != 0) { button = BUTTON3; modifiers &= ~BUTTON1_MASK & ~BUTTON2_MASK; ! if (id != MOUSE_PRESSED) { modifiers &= ~BUTTON3_DOWN_MASK; } } ------- Container.java ------- *** /tmp/sccs.Q_ay1J Wed Nov 7 22:30:18 2001 --- Container.java Wed Nov 7 17:57:00 2001 *************** *** 2909,2915 **** trackMouseEnterExit(mouseOver, e); ! if (!isMouseGrab(e)) { mouseEventTarget = (mouseOver != nativeContainer) ? mouseOver: null; } --- 2909,2918 ---- trackMouseEnterExit(mouseOver, e); ! // 4508327 : MOUSE_CLICKED should only go to the recipient of ! // the accompanying MOUSE_PRESSED, so don't reset mouseEventTarget on a ! // MOUSE_CLICKED. ! if (!isMouseGrab(e) && id != MouseEvent.MOUSE_CLICKED) { mouseEventTarget = (mouseOver != nativeContainer) ? mouseOver: null; } *************** *** 2921,2951 **** case MouseEvent.MOUSE_PRESSED: retargetMouseEvent(mouseEventTarget, id, e); break; ! case MouseEvent.MOUSE_RELEASED: ! if (isMouseGrab(e)) { ! retargetMouseEvent(mouseEventTarget, id, e); ! // Fix 4155217: component was hidden or moved in user ! // code handling MOUSE_RELEASED ! isClickOrphaned = (mouseOver != mouseEventTarget); ! } else { ! isClickOrphaned = true; ! } ! break; ! case MouseEvent.MOUSE_CLICKED: ! if (!isClickOrphaned) { ! // Fix 4155217: click event should not be ! // redirected since component has moved or hidden ! retargetMouseEvent(mouseEventTarget, id, e); ! } ! isClickOrphaned = false; ! break; case MouseEvent.MOUSE_MOVED: retargetMouseEvent(mouseEventTarget, id, e); break; ! case MouseEvent.MOUSE_DRAGGED: ! if (isMouseGrab(e)) { ! retargetMouseEvent(mouseEventTarget, id, e); ! } break; case MouseEvent.MOUSE_WHEEL: // This may send it somewhere that doesn't have MouseWheelEvents --- 2924,2949 ---- case MouseEvent.MOUSE_PRESSED: retargetMouseEvent(mouseEventTarget, id, e); break; ! case MouseEvent.MOUSE_RELEASED: ! retargetMouseEvent(mouseEventTarget, id, e); ! break; ! case MouseEvent.MOUSE_CLICKED: ! // 4508327: MOUSE_CLICKED should never be dispatched to a Component ! // other than that which received the MOUSE_PRESSED event. If the ! // mouse is now over a different Component, don't dispatch the event. ! // The previous fix for a similar problem was associated with bug ! // 4155217. ! if (mouseOver == mouseEventTarget) { ! retargetMouseEvent(mouseOver, id, e); ! } ! break; case MouseEvent.MOUSE_MOVED: retargetMouseEvent(mouseEventTarget, id, e); break; ! case MouseEvent.MOUSE_DRAGGED: ! if (isMouseGrab(e)) { ! retargetMouseEvent(mouseEventTarget, id, e); ! } break; case MouseEvent.MOUSE_WHEEL: // This may send it somewhere that doesn't have MouseWheelEvents *************** *** 3249,3259 **** private transient boolean isMouseInNativeContainer = false; /** - * Is the next click event orphaned because the component hid/moved - */ - private transient boolean isClickOrphaned = false; - - /** * This variable is not used, but kept for serialization compatibility */ private Cursor nativeCursor;
11-06-2004

PUBLIC COMMENTS I have verified this bug in Merlin-rc and it is NOT REPRODUCIBLE
10-06-2004

EVALUATION Mark, I'm dropping to a P4, if you find this bug is a possible showstopper for AWT please talk to Eric about it after you reassign. Lara ###@###.### 2001-10-02 Evaluation ---------- This bug was introduced in b75 on all platforms and still exists in b81. The mouse event delivery cycle is: MOUSE_PRESSED, MOUSE_RELEASED, MOUSE_CLICKED. A MouseListener instance, BasicComboPopup.ListMouseHandler is added to the JList portion of the combo poup. The popup portion of the combo box is hidden on the mouseReleased() method. The MOUSE_CLICKED event is not delivered to the JList because it is not visible so it seemed to be delivered to the JPanel behind the popup instead. Not sure if the MOUSE_CLICKED part of the sequence has ever been delived to the JList previously. Events Delivered to BasicComboPopup.ListMouseHandler: java.awt.event.MouseEvent[MOUSE_PRESSED,(334,29),button=1,modifiers=Button1,extModifiers=Button1,clickCount=1] on javax.swing.plaf.basic.BasicComboPopup$2[,0,0,388x38,alignmentX=null,alignmentY=null,border=,flags=296,maximumSize=,minimumSize=,preferredSize=,fixedCellHeight=-1,fixedCellWidth=-1,horizontalScrollIncrement=-1,selectionBackground=javax.swing.plaf.ColorUIResource[r=153,g=153,b=204],selectionForeground=javax.swing.plaf.ColorUIResource[r=0,g=0,b=0],visibleRowCount=8,layoutOrientation=0] java.awt.event.MouseEvent[MOUSE_RELEASED,(334,29),button=1,modifiers=Button1,extModifiers=Button1,clickCount=1] on javax.swing.plaf.basic.BasicComboPopup$2[,0,0,388x38,alignmentX=null,alignmentY=null,border=,flags=296,maximumSize=,minimumSize=,preferredSize=,fixedCellHeight=-1,fixedCellWidth=-1,horizontalScrollIncrement=-1,selectionBackground=javax.swing.plaf.ColorUIResource[r=153,g=153,b=204],selectionForeground=javax.swing.plaf.ColorUIResource[r=0,g=0,b=0],visibleRowCount=8,layoutOrientation=0] Event delivered to panel: java.awt.event.MouseEvent[MOUSE_CLICKED,(335,30),button=1,modifiers=Button1,extModifiers=Button1,clickCount=1] on javax.swing.JPanel[,0,26,390x347,layout=java.awt.FlowLayout,alignmentX=null,alignmentY=null,border=,flags=9,maximumSize=,minimumSize=,preferredSize=] There are some suspicious fixes that went into b75 including 408887 and 4386941. Closer investigation should look at the diffs around the begining of August 2001 in this files: java.awt.EventDispatchThread:1.40 and java.awt.SequencedEvent:1.3, java.awt.EventQueue:1.79. This is a fairly critical bug since any registered MouseListener on a panel which handles mouseClicked() will inadvertantly be executed. ###@###.### 2001-10-03 This bug exists on the Win32 platform (testing with WinNT). I can't reproduce this on Linux or Solaris. I'm going to re-assign to AWT. You can reproduce this with the Popup tester at /home/davidson/ws/popup by increasing the size of the frame so it it totally encloses the combo box popup. ###@###.### 2001-10-18 I can reproduce this on Solaris and Win2k w/ b84. ###@###.### 2001-10-22 Here is a clue. I added an AWTEventListener to the test case to listen for mouse events. With 1.3.1 and 1.4b74, the troublesome MOUSE_CLICKED event never even makes it to the AWTEventListener. ###@###.### 2001-10-22 The problem occurs with JMenus as well as JComboBoxes. ###@###.### 2001-11-02 Two problems are combining to cause this bug, both related to RFE 4421515: (1) MOUSE_RELEASED events have incorrect modifiers when dispatched to LightweightComponents (this is somewhat serious in and of itself). (2) Changes to the LightweightDispatcher have broken the code which used to keep MOUSE_CLICKED events from being delivered to the wrong Component. (1): For 1.4, new modifier masks (so-called "_DOWN_" masks) were added to InputEvent because there are fundamental problems with the original modifier masks (namely, that ALT_MASK/BUTTON2_MASK are indistinguishable, as are META_MASK/BUTTON3_MASK). To maintain backwards compatibility, the MouseEvent constructor maps the old modifier set to the new one, or vice versa, depending on the value of modifiers passed into MouseEvent. This mapping code is not perfect, especially considering that there a number of scenarios in which MouseEvents may be constructed (by the toolkit itself, manually by a developer, etc). When MouseEvents are dispatched to LightweightComponents, the LightweightDispatcher must change the target Component of the MouseEvent. Since there is no MouseEvent API to do this, a new MouseEvent must be consructed with the appropriate lightweight target. In effect, for MouseEvents on lightweight Components, two MouseEvents are constructed and thus, the modifiers are mapped twice. This is particularly bad for MOUSE_RELEASED events. Simply put, the algorithm for mapping modifiers is that if only new modifier masks are set, the old modifier masks should be set, and vice versa. When the first MOUSE_RELEASED event is constructed, the Toolkit only sets new modifiers, and the old modifiers are set by the MouseEvent constructor. What you get is this: Original Mods: -------------- getModifiers(): 0 (none set by toolkit) getModifiersEx(): 0 (no modifier keys or buttons down for a mouse release) button: 1 (mouse button 1 is the one being released) 1st Modifier Mapping: --------------------- getModifiers(): BUTTON1_MASK (for pre-1.4, indicates that BUTTON1 was released) getModifiersEx(): 0 (unchanged) button: 1 (unchanged) When this MouseEvent gets to the Lightweight Dispatcher, it creates a new MouseEvent with a lightweight target. The MouseEvent constructor sees that there are old masks set and no new masks set, so it sets the new modifiers by the old ones. The result is: 2nd Modifier Mapping: --------------------- getModifiers(): BUTTON1_MASK (unchanged) getModifiersEx(): BUTTON1_DOWN_MASK (since BUTTON1_MASK is set, _DOWN_ is set) button: 1 (unchanged) What you end up with is a MOUSE_RELEASED event for BUTTON1 in which BUTTON1_DOWN_MASK is set, which doesn't make sense and should never happen. This helped to throw off the Lightweight Dispatcher and cause the incorrect dispatching of MOUSE_CLICKED. It turns out there is a bug in the setNewModifiers() method, which is called to set the new modifiers by the old ones. The BUTTONX_DOWN_MASK was being unset for MOUSE_PRESSED, and remained set for BUTTON_RELEASED and BUTTON_CLICKED, which is backwards, and has been remedied. See the suggested fix. (2): Not only were bogus modifiers showing up in the MouseEvents coming from LightweightDispatcher, but other work related to the new modifier masks disturbed the code already in place to ensure that MOUSE_CLICKED events aren't delivered to the wrong Component. The LightweightDispatcher keeps track of the previous MouseEvent target for various uses ("mouseEventTarget"), including correctly delivering events during a drag. In the case of MOUSE_CLICKED, if the previous event (always a MOUSE_PRESSED) had a different target than the Component under the mouse cursor for the MOUSE_CLICKED (i.e. the previous target has moved or been hidden), then the MOUSE_CLICKED should not be delivered. Code changes for 4421515 caused the mouseEventTarget to be changed for the MOUSE_CLICKED, and so it appeared that the MOUSE_CLICKED was going to be delivered to the same Component as the MOUSE_PRESSED, which was not really the case. Code was added to make sure mouseEventTarget is not changed for a MOUSE_CLICKED, and the MOUSE_RELEASED and MOUSE_PRESSED cases were cleaned up. See the suggested fix. ###@###.### 2001-11-07
07-11-2001