United StatesChange Country, Oracle Worldwide Web Sites Communities I am a... I want to...
Bug ID: JDK-4531849 REG: Extra ActionEvent fired on win32
JDK-4531849 : REG: Extra ActionEvent fired on win32

Details
Type:
Bug
Submit Date:
2001-11-27
Status:
Resolved
Updated Date:
2002-09-20
Project Name:
JDK
Resolved Date:
2002-09-20
Component:
client-libs
OS:
windows_nt
Sub-Component:
java.awt
CPU:
x86
Priority:
P4
Resolution:
Fixed
Affected Versions:
1.4.0
Fixed Versions:
1.4.2 (mantis)

Related Reports
Relates:
Relates:
Relates:
Relates:

Sub Tasks

Description
Pressing mouse buttons in a certain way on java.awt.Buttons can cause two ActionEvents to be fired, instead of just one.  Consider this simple test case:

// Test of double ActionEvents

import java.awt.*;
import java.awt.event.*;

public class ActionTest extends Frame implements ActionListener {
    Button b;

    public ActionTest() {
        super("ActionTest");
        b = new Button("Action Listening Button");
        b.addActionListener(this);
        add(b);
        setSize(200, 200);
    }

    static int i = 0;
    public void actionPerformed(ActionEvent e) {        
        System.out.println("actionPerformed() called - " + (i++));
    }

    public static void main(String[] args) {
        ActionTest at = new ActionTest();
        at.show();
    }
}

---
Press both the left and right mouse buttons while over the Action Listening Button.  Next, release the left mouse button.  You will see that actionPerformed() is called twice.  This is a regression since 1.3.1, and does not happen on Solaris.  See also bug 4530087.

                                    

Comments
EVALUATION

Regression - commit to hopper.
###@###.### 2001-11-27

Here is another test case which fires two action events when a Window is Activated with a Button press:

import java.awt.*;
import java.awt.event.*;

public class ButtonActionEventTest extends Frame
    implements ActionListener
{
    int actionCount = 0;
    public ButtonActionEventTest() {
        super("button ActionEvent test");
        Button b = new Button("Press Me");
        b.addActionListener(this);
        b.addMouseListener(new MouseAdapter() {
                public void mouseClicked(MouseEvent me) {
                    System.out.println("mouseClicked");
                }
                public void mousePressed(MouseEvent me) {
                    System.out.println("mousePressed");
                }
                public void mouseReleased(MouseEvent me) {
                    System.out.println("mouseReleased");
                }
            });
        add(b);
        addWindowListener(new WindowAdapter() {
                public void windowClosing(WindowEvent we) {
                    ButtonActionEventTest.this.dispose();
                    System.exit(0);
                }
                public void windowActivated(WindowEvent we) {
                    //System.out.println(we);
                    try {
                        System.out.println(we);
                        Thread.sleep(1000);
                    } catch (InterruptedException ie) {
                    }                    
                }
            });
        pack();
        setVisible(true);
    }

    public void actionPerformed(ActionEvent ae) {
        System.out.println("action performed " + actionCount++);
    }

    public static void main(String[] args) {
        new ButtonActionEventTest();
    }
}

I did notice that without the sleep(), the problem disappears.  Timing issue?

###@###.### 2001-11-28

The cause of this bug is the fix for 4327679, which was putback with fixes for similar bugs 4327618, 4327623, 4327639, 4327654, 4327664, 4327676.  This could be confirmed by running the test cases with Merlin builds 30 and 31 - it would be introduced in b31.
     
MOUSE_DRAGGED and MOUSE_RELEASED events were fixed by capturing the mouse on all
button presses.  Several regressions were found and fixed before the putback,
but this one was missed.

Before the mouse capturing, ActionEvents were sent to Buttons in response to
WM_COMMAND messages (via WmNotify()).  This covered the case of mouse clicks as well as pressing the space bar.  The MSDN docs for SetCapture() state, "When the mouse is captured, menu hotkeys and other keyboard accelerators do not work."  This is true in that when we capture the mouse, no WM_COMMAND message are received.

With mouse capturing, space bar still sent WM_COMMAND messages, but mouse clicks didn't, so WM_LBUTTONUP handling was changed to also call WmNotify(), sending ActionEvents as appropriate.

This bug is due in part to bugs/underspecification in the Win32 APIs.  Clearly,
there is some relationship between WM_COMMAND events and mouse capture, but it
is not well specified.

It appears that Windows treats left and right mouse buttons differently with respect to capture.  Using Spy++, I observed that clicking on a Button w/ the left mouse button results in two WM_CAPTURECHANGED events.  But w/ the right mouse button, we see a WM_CAPTURECHANGED only on the release.  If both buttons are held down and only the left button is  released, we appear to lose mouse capture (we see a WM_CAPTURECHANGED event) though we don't call ReleaseCapture(), and the attempt to release capture on the right button release fails because we don't have capture.  It is these capture-related behaviors of Windows that cause our problems. For some reason, Windows sees fit to release our mouse capture AND send a WM_COMMAND message if we left click on a Button while the right mouse button is also down.  This is where our two ActionEvents come from: one through WmMouseUp->WmNotify(), and one through WmCommand()->WmNotify().
  
There a couple possible solutions, which I'm now investigating.
###@###.### 2002-08-26
                                     
2002-08-26
SUGGESTED FIX

------- awt_Button.h -------
*** /tmp/geta29970      Thu Aug 29 13:52:45 2002
--- /tmp/getb29970      Thu Aug 29 13:52:45 2002
***************
*** 32,38 ****
      /* Windows message handler functions */
      MsgRouting WmMouseDown(UINT flags, int x, int y, int button);
      MsgRouting WmMouseUp(UINT flags, int x, int y, int button);
!     MsgRouting WmNotify(UINT notifyCode);
      MsgRouting OwnerDrawItem(UINT ctrlId, DRAWITEMSTRUCT& drawInfo);
      MsgRouting WmPaint(HDC hDC);
  
--- 32,38 ----
      /* Windows message handler functions */
      MsgRouting WmMouseDown(UINT flags, int x, int y, int button);
      MsgRouting WmMouseUp(UINT flags, int x, int y, int button);
!     MsgRouting WmKeyUp(UINT vkey, UINT repCnt, UINT flags, BOOL system);
      MsgRouting OwnerDrawItem(UINT ctrlId, DRAWITEMSTRUCT& drawInfo);
      MsgRouting WmPaint(HDC hDC);
  
***************
*** 41,46 ****
--- 41,47 ----
  private:
      // 4530087: variable to keep track of left mouse press
      BOOL leftButtonDown;
+     void NotifyListeners();
  };

------- awt_Button.cpp -------
*** /tmp/geta29999      Thu Aug 29 13:53:59 2002
--- /tmp/getb29999      Thu Aug 29 13:53:59 2002
***************
*** 136,143 ****
      // obscuring this AwtButton, and during event handling the Window was
      // removed.  This causes a WmMouseUp call to this AwtButton, even though
      // there was no accompanying WmMouseDown.  ActionEvents should ONLY be
!     // notified (via WmNotify() call) if the left button press happened on this
!     // AwtButton.  --bchristi
      if (button == LEFT_BUTTON && leftButtonDown) {
          leftButtonDown = FALSE;
  
--- 136,143 ----
      // obscuring this AwtButton, and during event handling the Window was
      // removed.  This causes a WmMouseUp call to this AwtButton, even though
      // there was no accompanying WmMouseDown.  ActionEvents should ONLY be
!     // notified (via NotifyListeners()) if the left button press happened on
!     // this AwtButton.  --bchristi
      if (button == LEFT_BUTTON && leftButtonDown) {
          leftButtonDown = FALSE;
  
***************
*** 146,152 ****
          ::GetClientRect(GetHWnd(), &rect);
  
          if (::PtInRect(&rect, p)) {
!             WmNotify(BN_CLICKED);
          }
      }
  
--- 146,152 ----
          ::GetClientRect(GetHWnd(), &rect);
  
          if (::PtInRect(&rect, p)) {
!             NotifyListeners();
          }
      }
  
***************
*** 153,166 ****
      return mrResult;
  }
  
  MsgRouting
! AwtButton::WmNotify(UINT notifyCode)
  {
!     if (notifyCode == BN_CLICKED) {
!         DoCallback("handleAction", "(JI)V", nowMillisUTC(),
!                    (jint)AwtComponent::GetJavaModifiers());
      }
!     return mrDoDefault;
  }
  
  MsgRouting
--- 153,183 ----
      return mrResult;
  }
  
+ void
+ AwtButton::NotifyListeners()
+ {
+     DoCallback("handleAction", "(JI)V", nowMillisUTC(),
+                (jint)AwtComponent::GetJavaModifiers());
+ }
+ 
+ /* 4531849 fix.  Previous to 1.4, mouse clicks and typing space bar on a
+  * Button would notify ActionListeners via WM_COMMAND/WmNotify().  In 1.4, mouse
+  * grabs are done for all presses in order to correctly send drag and release
+  * events.  However, WM_COMMAND message aren't sent when the mouse is grabbed,
+  * so ActionListeners for mouse clicks are sent via WmMouseUp/WmNotify().
+  * For some reason, if the right mouse button is held down when left-clicking 
+  * on a Button, WM_COMMAND _IS_ sent.  This resulted in two ActionEvents being
+  * sent in this case.  To fix the problem, we handle typing space bar similar to
+  * left clicks - in WmKeyUp(), and do nothing for WM_COMMAND.  -bchristi
+  */
  MsgRouting
! AwtButton::WmKeyUp(UINT wkey, UINT repCnt, UINT flags, BOOL system)
  {
!     MsgRouting mrResult = AwtComponent::WmKeyUp(wkey, repCnt, flags, system);
!     if (!system && wkey == VK_SPACE) {
!         NotifyListeners();
      }
!     return mrResult;
  }
                                     
2004-09-17
CONVERTED DATA

BugTraq+ Release Management Values

COMMIT TO FIX:
mantis

FIXED IN:
mantis

INTEGRATED IN:
mantis


                                     
2004-09-17



Hardware and Software, Engineered to Work Together