I extended the menu closing mechanism from yesterday so that arbitrary
popups can now register with the BasicLookAndFeel to get autoclosed when
the user clicks outside of the popup. I also changed the BasicComboPopup
to use this, so that now ComboBoxes close automatically and reliably
when the user clicks outside the combobox.

2006-02-24  Roman Kennke  <[EMAIL PROTECTED]>

        * javax/swing/plaf/basic/BasicLookAndFeel.java
        (PopupHelper.autoClosePopups): New field.
        (PopupHelper.mousePressed): Also autoclose any registered popups.
        (PopupHelper.registerForAutoClose): New method.
        (PopupHelper.autoClosePopups): New method.
        (popupHelper): Changed type of field to PopupHelper.
        (registerForAutoClose): New method.
        * javax/swing/plaf/basic/BasicComboPopup.java
        (show): Register this popup for autoclosing.

/Roman
Index: javax/swing/plaf/basic/BasicLookAndFeel.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/plaf/basic/BasicLookAndFeel.java,v
retrieving revision 1.85
diff -u -r1.85 BasicLookAndFeel.java
--- javax/swing/plaf/basic/BasicLookAndFeel.java	23 Feb 2006 23:57:50 -0000	1.85
+++ javax/swing/plaf/basic/BasicLookAndFeel.java	24 Feb 2006 11:57:38 -0000
@@ -52,7 +52,10 @@
 import java.io.InputStream;
 import java.io.Serializable;
 import java.util.Enumeration;
+import java.util.Iterator;
 import java.util.ResourceBundle;
+import java.util.Set;
+import java.util.WeakHashMap;
 
 import javax.sound.sampled.AudioInputStream;
 import javax.sound.sampled.AudioSystem;
@@ -63,9 +66,11 @@
 import javax.swing.Action;
 import javax.swing.ActionMap;
 import javax.swing.BorderFactory;
+import javax.swing.JPopupMenu;
 import javax.swing.KeyStroke;
 import javax.swing.LookAndFeel;
 import javax.swing.MenuSelectionManager;
+import javax.swing.SwingUtilities;
 import javax.swing.UIDefaults;
 import javax.swing.UIManager;
 import javax.swing.border.BevelBorder;
@@ -99,6 +104,11 @@
   {
 
     /**
+     * Registered popups for autoclose.
+     */
+    private WeakHashMap autoClosePopups = new WeakHashMap();
+
+    /**
      * Receives an event from the event queue.
      *
      * @param event
@@ -120,12 +130,52 @@
      */
     private void mousePressed(MouseEvent ev)
     {
+      // Autoclose all menus managed by the MenuSelectionManager.
       MenuSelectionManager m = MenuSelectionManager.defaultManager();
       Component target = ev.getComponent();
       if (target instanceof Container)
         target = ((Container) target).findComponentAt(ev.getPoint());
       if (! m.isComponentPartOfCurrentMenu(target))
         m.clearSelectedPath();
+
+      // Handle other registered popup instances, like ComboBox popups.
+      autoClosePopups(ev, target);
+    }
+
+    /**
+     * Registers Popup and its content to be autoclosed when a mouseclick
+     * occurs outside of the popup.
+     *
+     * @param popup the popup to be autoclosed when clicked outside
+     */
+    void registerForAutoClose(JPopupMenu popup)
+    {
+      autoClosePopups.put(popup, null);
+    }
+
+    /**
+     * Automatically closes all popups that are not 'hit' by the mouse event.
+     *
+     * @param ev the mouse event
+     * @param target the target of the mouse event
+     */
+    private void autoClosePopups(MouseEvent ev, Component target)
+    {
+      if (autoClosePopups.size() != 0)
+        {
+          Set popups = autoClosePopups.keySet();
+          Iterator i = popups.iterator();
+          while (i.hasNext())
+            {
+              JPopupMenu popup = (JPopupMenu) i.next();
+              if (!(target == popup
+                    || SwingUtilities.isDescendingFrom(target, popup)))
+                {
+                  popup.setVisible(false);
+                  i.remove();
+                }
+            }
+        }
     }
   }
 
@@ -192,7 +242,7 @@
   /**
    * Helps closing menu popups when user clicks outside of the menu area.
    */
-  private transient AWTEventListener popupHelper;
+  private transient PopupHelper popupHelper;
 
   private ActionMap audioActionMap;
 
@@ -1598,4 +1648,17 @@
     toolkit.removeAWTEventListener(popupHelper);
     popupHelper = null;
   }
+
+  /**
+   * Registers a JPopupMenu for autoclosing when a mouseclick occurs outside
+   * of the JPopupMenu. This must be called when the popup gets opened. The
+   * popup is unregistered from autoclosing as soon as it either got closed
+   * by this helper, or when it has been garbage collected.
+   *
+   * @param popup the popup menu to autoclose
+   */
+  void registerForAutoClose(JPopupMenu popup)
+  {
+    popupHelper.registerForAutoClose(popup);
+  }
 }
Index: javax/swing/plaf/basic/BasicComboPopup.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/plaf/basic/BasicComboPopup.java,v
retrieving revision 1.12
diff -u -r1.12 BasicComboPopup.java
--- javax/swing/plaf/basic/BasicComboPopup.java	19 Dec 2005 09:44:31 -0000	1.12
+++ javax/swing/plaf/basic/BasicComboPopup.java	24 Feb 2006 11:57:38 -0000
@@ -69,6 +69,7 @@
 import javax.swing.SwingConstants;
 import javax.swing.SwingUtilities;
 import javax.swing.Timer;
+import javax.swing.UIManager;
 import javax.swing.event.ListDataEvent;
 import javax.swing.event.ListDataListener;
 import javax.swing.event.ListSelectionEvent;
@@ -193,6 +194,11 @@
     if (selectedIndex > comboBox.getMaximumRowCount())
       scrollbar.setValue(getPopupHeightForRowCount(selectedIndex));
 
+    // Register this popup to be autoclosed when user clicks outside the
+    // popup.
+    BasicLookAndFeel laf = (BasicLookAndFeel) UIManager.getLookAndFeel();
+    laf.registerForAutoClose(this);
+
     // location specified is relative to comboBox
     super.show(comboBox, 0, cbBounds.height);
   }

Reply via email to