[patch] Bug #20015 JMenu stays open but should not
Michael Koch
konqueror@gmx.de
Mon May 9 16:14:00 GMT 2005
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
>
>
> 2005-05-09 Olga Rodimina <rodimina@redhat.com>
>
> * javax/swing/MenuSelectionManager.java
> (processMouseEvent): Clear selected path if the mouse
> was released over non-menu component.
> * plaf/basic/BasicPopupMenuUI.java
javax/swing part missing in file name.
> (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: javax/swing/MenuSelectionManager.java
> ===================================================================
> RCS file: /cvs/gcc/gcc/libjava/javax/swing/MenuSelectionManager.java,v
> retrieving revision 1.7
> diff -u -r1.7 MenuSelectionManager.java
> --- javax/swing/MenuSelectionManager.java 16 Feb 2005 20:02:39 -0000 1.7
> +++ javax/swing/MenuSelectionManager.java 9 May 2005 14:44:26 -0000
> @@ -35,7 +35,6 @@
> obligated to do so. If you do not wish to do so, delete this
> exception statement from your version. */
>
> -
> package javax.swing;
>
> import java.awt.Component;
> @@ -45,11 +44,11 @@
> import java.awt.event.MouseEvent;
> import java.util.ArrayList;
> import java.util.Vector;
> -
> import javax.swing.event.ChangeEvent;
> import javax.swing.event.ChangeListener;
> import javax.swing.event.EventListenerList;
>
> +
> /**
> * This class manages current menu selectection. It provides
> * methods to clear and set current selected menu path.
Please dont change the empty lines around the imports. We are trying to standardize them.
> @@ -157,7 +156,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 +276,11 @@
> }
> */
> }
> + else
> + {
> + if (event.getID() == MouseEvent.MOUSE_RELEASED)
> + clearSelectedPath();
> + }
> }
Looks indented the wrong way.
> /**
> Index: javax/swing/plaf/basic/BasicPopupMenuUI.java
> ===================================================================
> RCS file: /cvs/gcc/gcc/libjava/javax/swing/plaf/basic/BasicPopupMenuUI.java,v
> retrieving revision 1.7
> diff -u -r1.7 BasicPopupMenuUI.java
> --- javax/swing/plaf/basic/BasicPopupMenuUI.java 26 Apr 2005 18:57:35 -0000 1.7
> +++ javax/swing/plaf/basic/BasicPopupMenuUI.java 9 May 2005 14:44:27 -0000
> @@ -37,18 +37,23 @@
>
> 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.GridBagLayout;
> +import java.awt.Point;
> import java.awt.event.ComponentEvent;
> import java.awt.event.ComponentListener;
> import java.awt.event.MouseEvent;
> -
> import javax.swing.JComponent;
> +import javax.swing.JLayeredPane;
Please done remove the empty line. We want empty one empty line between java.* and javax.*
(and gnu.* and org.*). There are more instances of this below.
Michael
> +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 +88,6 @@
> public BasicPopupMenuUI()
> {
> popupMenuListener = new PopupMenuHandler();
> - mouseInputListener = new MouseInputHandler();
> topWindowListener = new TopWindowListener();
> }
>
> @@ -120,8 +124,8 @@
> }
>
> /**
> - * This method installs the defaults that are defined in the Basic look and
> - * feel for this {@link JPopupMenu}.
> + * This method installs the defaults that are defined in the Basic look
> + * and feel for this {@link JPopupMenu}.
> */
> public void installDefaults()
> {
> @@ -139,8 +143,6 @@
> */
> protected void installListeners()
> {
> - popupMenu.addMouseListener(mouseInputListener);
> - popupMenu.addMouseMotionListener(mouseInputListener);
> popupMenu.addPopupMenuListener(popupMenuListener);
> }
>
> @@ -183,8 +185,6 @@
> */
> protected void uninstallListeners()
> {
> - popupMenu.removeMouseListener(mouseInputListener);
> - popupMenu.removeMouseMotionListener(mouseInputListener);
> popupMenu.removePopupMenuListener(popupMenuListener);
> }
>
> @@ -273,8 +273,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 +306,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
> @@ -301,8 +331,10 @@
> // Set selected path to point to the first item in the popup menu
> MenuElement[] path = new MenuElement[2];
> path[0] = popupMenu;
> +
> Component[] comps = popupMenu.getComponents();
> - if (comps.length != 0 && comps[0] instanceof MenuElement)
> +
> + if ((comps.length != 0) && comps[0] instanceof MenuElement)
> {
> path[1] = (MenuElement) comps[0];
> manager.setSelectedPath(path);
> @@ -344,8 +376,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 +400,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;
> + }
> + }
> + }
> }
> }
> }
--
Escape the Java Trap with GNU Classpath!
http://www.gnu.org/philosophy/java-trap.html
Join the community at http://planet.classpath.org/
More information about the Java-patches
mailing list