[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