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;
}
|
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
|