[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[cp-patches] [Fwd: Re: [patch] Bug #20015 JMenu stays open but should no
From: |
Thomas Fitzsimmons |
Subject: |
[cp-patches] [Fwd: Re: [patch] Bug #20015 JMenu stays open but should not] |
Date: |
Wed, 18 May 2005 12:04:53 -0400 |
Hi,
I committed this patch to GNU Classpath on Olga's behalf.
Tom
-------- Forwarded Message --------
> From: Olga Rodimina <address@hidden>
> To: Michael Koch <address@hidden>
> Cc: address@hidden
> Subject: Re: [patch] Bug #20015 JMenu stays open but should not
> Date: Mon, 09 May 2005 14:26:15 -0400
>
> On Mon, 2005-05-09 at 17:19 +0200, Michael Koch wrote:
> > On Mon, May 09, 2005 at 10:55:08AM -0400, Olga Rodimina wrote:
> > > Hi,
> > >
> > > Here is the patch that fixes Bug#20015: JMenu stays open but should not
> > > http://gcc.gnu.org/bugzilla/show_bug.cgi?id=20015
> > >
>
> I made the suggested fixes. Here is the updated patch.
>
> Thanks,
> Olga.
>
>
> 2005-05-09 Olga Rodimina <address@hidden>
>
> * javax/swing/MenuSelectionManager.java
> (processMouseEvent): Clear selected path if the mouse
> was released over non-menu component.
> * javax/swing/plaf/basic/BasicPopupMenuUI.java
> (Constructor): Removed initialization of mouseInputListener.
> (installListeners): Do not add mouseInputListener to this
> popup menu. Instead it will be added to the root container
> of the popup menu.
> (uninstallListeners): Remove code that removed
> mouseInputListener from popupMenu.
> (popupMenuWillBecomeInvisible): If this popup menu is the
> last menu on the screen, then stop interrupting mouse events
> through the glass pane.
> (popupMenuWillBecomeVisible): Add mouseInputListener to glass
> pane if it was not added before and make glass pane visible
> in order to interrupt mouse evevents.
> (MouseInputHandler): Close menu hierarchy if the mouse was
> clicked on non menu component.
>
Index: ChangeLog
===================================================================
RCS file: /cvsroot/classpath/classpath/ChangeLog,v
retrieving revision 1.3634
diff -u -u -r1.3634 ChangeLog
--- ChangeLog 18 May 2005 15:05:59 -0000 1.3634
+++ ChangeLog 18 May 2005 15:50:51 -0000
@@ -1,3 +1,24 @@
+2005-05-18 Olga Rodimina <address@hidden>
+
+ * javax/swing/MenuSelectionManager.java
+ (processMouseEvent): Clear selected path if the mouse was released
+ over non-menu component.
+ * javax/swing/plaf/basic/BasicPopupMenuUI.java
+ (Constructor): Removed initialization of mouseInputListener.
+ (installListeners): Do not add mouseInputListener to this popup
+ menu. Instead it will be added to the root container of the popup
+ menu.
+ (uninstallListeners): Remove code that removed mouseInputListener
+ from popupMenu.
+ (popupMenuWillBecomeInvisible): If this popup menu is the last
+ menu on the screen, then stop interrupting mouse events through
+ the glass pane.
+ (popupMenuWillBecomeVisible): Add mouseInputListener to glass pane
+ if it was not added before and make glass pane visible in order to
+ interrupt mouse evevents.
+ (MouseInputHandler): Close menu hierarchy if the mouse was clicked
+ on non menu component.
+
2005-05-18 Thomas Fitzsimmons <address@hidden>
* gnu/java/awt/peer/gtk/GdkGraphicsConfiguration.java
Index: javax/swing/MenuSelectionManager.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/MenuSelectionManager.java,v
retrieving revision 1.8
diff -u -u -r1.8 MenuSelectionManager.java
--- javax/swing/MenuSelectionManager.java 22 Oct 2004 12:43:59 -0000
1.8
+++ javax/swing/MenuSelectionManager.java 18 May 2005 15:50:53 -0000
@@ -157,7 +157,7 @@
for (int i = 0; i < selectedPath.size(); i++)
{
Component comp = ((Component) selectedPath.get(i));
- Dimension size = comp.getSize();
+ Dimension size = comp.getSize();
// convert location of this menu item to screen coordinates
compPointOnScreen = comp.getLocationOnScreen();
@@ -277,6 +277,11 @@
}
*/
}
+ else
+ {
+ if (event.getID() == MouseEvent.MOUSE_RELEASED)
+ clearSelectedPath();
+ }
}
/**
Index: javax/swing/plaf/basic/BasicPopupMenuUI.java
===================================================================
RCS file:
/cvsroot/classpath/classpath/javax/swing/plaf/basic/BasicPopupMenuUI.java,v
retrieving revision 1.8
diff -u -u -r1.8 BasicPopupMenuUI.java
--- javax/swing/plaf/basic/BasicPopupMenuUI.java 11 May 2005 15:13:02
-0000 1.8
+++ javax/swing/plaf/basic/BasicPopupMenuUI.java 18 May 2005 15:50:53
-0000
@@ -37,18 +37,24 @@
package javax.swing.plaf.basic;
+import java.awt.AWTEvent;
import java.awt.Component;
import java.awt.Container;
+import java.awt.Cursor;
import java.awt.Dimension;
+import java.awt.Point;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.MouseEvent;
import javax.swing.BoxLayout;
import javax.swing.JComponent;
+import javax.swing.JLayeredPane;
+import javax.swing.JMenu;
import javax.swing.JPopupMenu;
import javax.swing.MenuElement;
import javax.swing.MenuSelectionManager;
+import javax.swing.RootPaneContainer;
import javax.swing.SwingUtilities;
import javax.swing.UIDefaults;
import javax.swing.UIManager;
@@ -83,7 +89,6 @@
public BasicPopupMenuUI()
{
popupMenuListener = new PopupMenuHandler();
- mouseInputListener = new MouseInputHandler();
topWindowListener = new TopWindowListener();
}
@@ -120,8 +125,8 @@
}
/**
- * This method installs the defaults that are defined in the Basic look and
- * feel for this address@hidden JPopupMenu}.
+ * This method installs the defaults that are defined in the Basic look
+ * and feel for this address@hidden JPopupMenu}.
*/
public void installDefaults()
{
@@ -139,8 +144,6 @@
*/
protected void installListeners()
{
- popupMenu.addMouseListener(mouseInputListener);
- popupMenu.addMouseMotionListener(mouseInputListener);
popupMenu.addPopupMenuListener(popupMenuListener);
}
@@ -183,8 +186,6 @@
*/
protected void uninstallListeners()
{
- popupMenu.removeMouseListener(mouseInputListener);
- popupMenu.removeMouseMotionListener(mouseInputListener);
popupMenu.removePopupMenuListener(popupMenuListener);
}
@@ -273,8 +274,26 @@
// by the top - level window that this popup belongs to.
Component invoker = popupMenu.getInvoker();
- Container rootContainer = (Container) SwingUtilities.getRoot(invoker);
- rootContainer.removeComponentListener(topWindowListener);
+ RootPaneContainer rootContainer = (RootPaneContainer) SwingUtilities
+ .getRoot(invoker);
+ ((Container) rootContainer).removeComponentListener(topWindowListener);
+
+ // If this popup menu is the last popup menu visible on the screen, then
+ // stop interrupting mouse events in the glass pane before hiding this
+ // last popup menu.
+ boolean topLevelMenu = (popupMenu.getInvoker() instanceof JMenu)
+ && ((JMenu) popupMenu.getInvoker())
+ .isTopLevelMenu();
+
+ if (topLevelMenu || ! (popupMenu.getInvoker() instanceof MenuElement))
+ {
+ // set glass pane not to interrupt mouse events and remove
+ // mouseInputListener
+ Container glassPane = (Container) rootContainer.getGlassPane();
+ glassPane.setVisible(false);
+ glassPane.removeMouseListener(mouseInputListener);
+ mouseInputListener = null;
+ }
}
/**
@@ -288,8 +307,20 @@
// ComponentEvents fired by it. We need to cancel this popup menu
// if topWindow to which this popup belongs was resized or moved.
Component invoker = popupMenu.getInvoker();
- Container rootContainer = (Container) SwingUtilities.getRoot(invoker);
- rootContainer.addComponentListener(topWindowListener);
+ RootPaneContainer rootContainer = (RootPaneContainer) SwingUtilities
+ .getRoot(invoker);
+ ((Container) rootContainer).addComponentListener(topWindowListener);
+
+ // Set the glass pane to interrupt all mouse events originating in root
+ // container
+ if (mouseInputListener == null)
+ {
+ Container glassPane = (Container) rootContainer.getGlassPane();
+ glassPane.setVisible(true);
+ mouseInputListener = new MouseInputHandler(rootContainer);
+ glassPane.addMouseListener(mouseInputListener);
+ glassPane.addMouseMotionListener(mouseInputListener);
+ }
// if this popup menu is a free floating popup menu,
// then by default its first element should be always selected when
@@ -344,8 +375,8 @@
}
/**
- * This method is invoked when top-level window is shown This method does
- * nothing by default.
+ * This method is invoked when top-level window is shown This method
+ * does nothing by default.
*
* @param e The ComponentEvent
*/
@@ -368,34 +399,272 @@
}
}
+ /**
+ * MouseInputHandler listens to all mouse events originated in the root
+ * container. This class is responsible for closing menu hierarchy when the
+ * user presses mouse over any component that do not belong to the current
+ * menu hierarchy. This is acomplished by interrupting all mouse event in
+ * the glass pane and checking if other component was pressed while menu
+ * was open, before redestributing events further to intended components
+ */
private class MouseInputHandler implements MouseInputListener
{
+ private JLayeredPane layeredPane;
+ private Container glassPane;
+ private Cursor nativeCursor;
+ private transient Component mouseEventTarget;
+ private transient Component pressedComponent;
+ private transient Component lastComponentEntered;
+ private transient int pressCount;
+
+ /**
+ * Creates a new MouseInputHandler object.
+ *
+ * @param c the top most root container
+ */
+ public MouseInputHandler(RootPaneContainer c)
+ {
+ layeredPane = c.getLayeredPane();
+ glassPane = (Container) c.getGlassPane();
+ }
+
+ /**
+ * Handles mouse clicked event
+ *
+ * @param e Mouse event
+ */
public void mouseClicked(MouseEvent e)
{
+ handleEvent(e);
}
+ /**
+ * Handles mouseDragged event
+ *
+ * @param e MouseEvent
+ */
public void mouseDragged(MouseEvent e)
{
+ handleEvent(e);
}
+ /**
+ * Handles mouseEntered event
+ *
+ * @param e MouseEvent
+ */
public void mouseEntered(MouseEvent e)
{
+ handleEvent(e);
}
+ /**
+ * Handles mouseExited event
+ *
+ * @param e MouseEvent
+ */
public void mouseExited(MouseEvent e)
{
+ handleEvent(e);
}
+ /**
+ * Handles mouse moved event
+ *
+ * @param e MouseEvent
+ */
public void mouseMoved(MouseEvent e)
{
+ handleEvent(e);
}
+ /**
+ * Handles mouse pressed event
+ *
+ * @param e MouseEvent
+ */
public void mousePressed(MouseEvent e)
{
+ handleEvent(e);
}
+ /**
+ * Handles mouse released event
+ *
+ * @param e MouseEvent
+ */
public void mouseReleased(MouseEvent e)
{
+ handleEvent(e);
+ }
+
+ /*
+ * This method determines component that was intended to received mouse
+ * event, before it was interrupted within the glass pane. This method
+ * also redispatches mouse entered and mouse exited events to the
+ * appropriate components. This code is slightly modified code from
+ * Container.LightweightDispatcher class, which is private inside
+ * Container class and cannot be used here.
+ */
+ public void acquireComponentForMouseEvent(MouseEvent me)
+ {
+ int x = me.getX();
+ int y = me.getY();
+
+ // Find the candidate which should receive this event.
+ Component parent = layeredPane;
+ Component candidate = null;
+ Point p = me.getPoint();
+ while ((candidate == null) && (parent != null))
+ {
+ p = SwingUtilities.convertPoint(glassPane, p.x, p.y, parent);
+ candidate = SwingUtilities.getDeepestComponentAt(parent, p.x, p.y);
+
+ if (candidate == null)
+ {
+ p = SwingUtilities.convertPoint(parent, p.x, p.y,
+ parent.getParent());
+ parent = parent.getParent();
+ }
+ }
+
+ // If the only candidate we found was the native container itself,
+ // don't dispatch any event at all. We only care about the lightweight
+ // children here.
+ if (candidate == layeredPane)
+ candidate = null;
+
+ // If our candidate is new, inform the old target we're leaving.
+ if ((lastComponentEntered != null) && lastComponentEntered.isShowing()
+ && (lastComponentEntered != candidate))
+ {
+ // Old candidate could have been removed from
+ // the layeredPane so we check first.
+ if (SwingUtilities.isDescendingFrom(lastComponentEntered,
layeredPane))
+ {
+ Point tp = SwingUtilities.convertPoint(layeredPane, x, y,
+ lastComponentEntered);
+ MouseEvent exited = new MouseEvent(lastComponentEntered,
+ MouseEvent.MOUSE_EXITED,
+ me.getWhen(),
+ me.getModifiersEx(), tp.x,
+ tp.y, me.getClickCount(),
+ me.isPopupTrigger(),
+ me.getButton());
+
+ lastComponentEntered.dispatchEvent(exited);
+ }
+
+ lastComponentEntered = null;
+ }
+
+ // If we have a candidate, maybe enter it.
+ if (candidate != null)
+ {
+ mouseEventTarget = candidate;
+
+ if (candidate.isLightweight() && candidate.isShowing()
+ && (candidate != layeredPane)
+ && (candidate != lastComponentEntered))
+ {
+ lastComponentEntered = mouseEventTarget;
+
+ Point cp = SwingUtilities.convertPoint(layeredPane, x, y,
+ lastComponentEntered);
+ MouseEvent entered = new MouseEvent(lastComponentEntered,
+ MouseEvent.MOUSE_ENTERED,
+ me.getWhen(),
+ me.getModifiersEx(), cp.x,
+ cp.y, me.getClickCount(),
+ me.isPopupTrigger(),
+ me.getButton());
+ lastComponentEntered.dispatchEvent(entered);
+ }
+ }
+
+ if ((me.getID() == MouseEvent.MOUSE_RELEASED)
+ || ((me.getID() == MouseEvent.MOUSE_PRESSED) && (pressCount > 0))
+ || (me.getID() == MouseEvent.MOUSE_DRAGGED))
+ {
+ // If any of the following events occur while a button is held down,
+ // they should be dispatched to the same component to which the
+ // original MOUSE_PRESSED event was dispatched:
+ // - MOUSE_RELEASED
+ // - MOUSE_PRESSED: another button pressed while the first is held
down
+ // - MOUSE_DRAGGED
+ if (SwingUtilities.isDescendingFrom(pressedComponent, layeredPane))
+ mouseEventTarget = pressedComponent;
+ else if (me.getID() == MouseEvent.MOUSE_CLICKED)
+ {
+ // Don't dispatch CLICKED events whose target is not the same as
the
+ // target for the original PRESSED event.
+ if (candidate != pressedComponent)
+ mouseEventTarget = null;
+ else if (pressCount == 0)
+ pressedComponent = null;
+ }
+ }
+ }
+
+ /*
+ * This method handles mouse events interrupted by glassPane. It
+ * redispatches the mouse events appropriately to the intended components.
+ * The code in this method is also taken from
+ * Container.LightweightDispatcher class. The code is slightly modified
+ * to handle the case when mouse is released over non-menu component. In
+ * this case this method closes current menu hierarchy before
+ * redispatching the event further.
+ */
+ public void handleEvent(AWTEvent e)
+ {
+ if (e instanceof MouseEvent)
+ {
+ MouseEvent me = (MouseEvent) e;
+
+ acquireComponentForMouseEvent(me);
+
+ // Avoid dispatching ENTERED and EXITED events twice.
+ if (mouseEventTarget != null && mouseEventTarget.isShowing()
+ && (e.getID() != MouseEvent.MOUSE_ENTERED)
+ && (e.getID() != MouseEvent.MOUSE_EXITED))
+ {
+ MouseEvent newEvt = SwingUtilities.convertMouseEvent(glassPane,
+ me,
+
mouseEventTarget);
+
+ mouseEventTarget.dispatchEvent(newEvt);
+
+ // If mouse was clicked over the component that is not part
+ // of menu hierarchy,then must close the menu hierarchy */
+ if (e.getID() == MouseEvent.MOUSE_RELEASED)
+ {
+ boolean partOfMenuHierarchy = false;
+ MenuSelectionManager manager = MenuSelectionManager
+ .defaultManager();
+
+ partOfMenuHierarchy =
manager.isComponentPartOfCurrentMenu(mouseEventTarget);
+
+ if (! partOfMenuHierarchy)
+ manager.clearSelectedPath();
+ }
+
+ switch (e.getID())
+ {
+ case MouseEvent.MOUSE_PRESSED:
+ if (pressCount++ == 0)
+ pressedComponent = mouseEventTarget;
+ break;
+ case MouseEvent.MOUSE_RELEASED:
+ // Clear our memory of the original PRESSED event, only if
+ // we're not expecting a CLICKED event after this. If
+ // there is a CLICKED event after this, it will do clean up.
+ if ((--pressCount == 0)
+ && (mouseEventTarget != pressedComponent))
+ pressedComponent = null;
+ break;
+ }
+ }
+ }
}
}
}
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [cp-patches] [Fwd: Re: [patch] Bug #20015 JMenu stays open but should not],
Thomas Fitzsimmons <=