8165234: Provide a way to not close toggle menu items on mouse click on component level
authoralexsch
Fri, 30 Sep 2016 22:57:41 +0400
changeset 41407 ac6be88670ea
parent 41406 d9b9053b5c5d
child 41408 9bc553d242eb
8165234: Provide a way to not close toggle menu items on mouse click on component level Reviewed-by: serb, ssadetsky
jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifCheckBoxMenuItemUI.java
jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifRadioButtonMenuItemUI.java
jdk/src/java.desktop/share/classes/javax/swing/JCheckBoxMenuItem.java
jdk/src/java.desktop/share/classes/javax/swing/JRadioButtonMenuItem.java
jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicLookAndFeel.java
jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicMenuItemUI.java
jdk/src/java.desktop/share/classes/sun/swing/SwingUtilities2.java
jdk/test/javax/swing/JMenuItem/8158566/CloseOnMouseClickPropertyTest.java
--- a/jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifCheckBoxMenuItemUI.java	Fri Sep 30 11:45:30 2016 -0700
+++ b/jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifCheckBoxMenuItemUI.java	Fri Sep 30 22:57:41 2016 +0400
@@ -33,6 +33,7 @@
 
 import java.awt.*;
 import java.awt.event.*;
+import sun.swing.SwingUtilities2;
 
 
 /**
@@ -89,7 +90,8 @@
             Point p = e.getPoint();
             if(p.x >= 0 && p.x < menuItem.getWidth() &&
                p.y >= 0 && p.y < menuItem.getHeight()) {
-                if (UIManager.getBoolean("CheckBoxMenuItem.closeOnMouseClick")) {
+                String property = "CheckBoxMenuItem.doNotCloseOnMouseClick";
+                if (!SwingUtilities2.getBoolean(menuItem, property)) {
                     manager.clearSelectedPath();
                 }
                 menuItem.doClick(0);
--- a/jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifRadioButtonMenuItemUI.java	Fri Sep 30 11:45:30 2016 -0700
+++ b/jdk/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifRadioButtonMenuItemUI.java	Fri Sep 30 22:57:41 2016 +0400
@@ -33,6 +33,7 @@
 import java.awt.*;
 import java.awt.event.*;
 import java.io.Serializable;
+import sun.swing.SwingUtilities2;
 
 
 /**
@@ -97,8 +98,8 @@
             Point p = e.getPoint();
             if(p.x >= 0 && p.x < menuItem.getWidth() &&
                p.y >= 0 && p.y < menuItem.getHeight()) {
-                String property = "RadioButtonMenuItem.closeOnMouseClick";
-                if (UIManager.getBoolean(property)) {
+                String property = "RadioButtonMenuItem.doNotCloseOnMouseClick";
+                if (!SwingUtilities2.getBoolean(menuItem, property)) {
                     manager.clearSelectedPath();
                 }
                 menuItem.doClick(0);
--- a/jdk/src/java.desktop/share/classes/javax/swing/JCheckBoxMenuItem.java	Fri Sep 30 11:45:30 2016 -0700
+++ b/jdk/src/java.desktop/share/classes/javax/swing/JCheckBoxMenuItem.java	Fri Sep 30 22:57:41 2016 +0400
@@ -58,10 +58,13 @@
  * <p>
  * Some times it is required to select several check box menu items from a menu.
  * In this case it is useful that clicking on one check box menu item does not
- * close the menu. Such behavior can be controlled by the Look and Feel property
- * named {@code "CheckBoxMenuItem.closeOnMouseClick"}. The default value is
- * {@code true}. Setting the property to {@code false} prevents the menu from
- * closing when it is clicked by the mouse.
+ * close the menu. Such behavior can be controlled either by client
+ * {@link JComponent#putClientProperty} or the Look and Feel
+ * {@link UIManager#put} property named
+ * {@code "CheckBoxMenuItem.doNotCloseOnMouseClick"}. The default value is
+ * {@code false}. Setting the property to {@code true} prevents the menu from
+ * closing when it is clicked by the mouse. If the client property is set its
+ * value is always used; otherwise the {@literal L&F} property is queried.
  * Note: some {@code L&F}s may ignore this property. All built-in {@code L&F}s
  * inherit this behaviour.
  * <p>
--- a/jdk/src/java.desktop/share/classes/javax/swing/JRadioButtonMenuItem.java	Fri Sep 30 11:45:30 2016 -0700
+++ b/jdk/src/java.desktop/share/classes/javax/swing/JRadioButtonMenuItem.java	Fri Sep 30 22:57:41 2016 +0400
@@ -52,10 +52,13 @@
  * <p>
  * Some menus can have several button groups with radio button menu items. In
  * this case it is useful that clicking on one radio button menu item does not
- * close the menu. Such behavior can be controlled by the Look and Feel property
- * named {@code "RadioButtonMenuItem.closeOnMouseClick"}. The default value is
- * {@code true}. Setting the property to {@code false} prevents the menu from
- * closing when it is clicked by the mouse.
+ * close the menu. Such behavior can be controlled either by client
+ * {@link JComponent#putClientProperty} or the Look and Feel
+ * {@link UIManager#put} property named
+ * {@code "RadioButtonMenuItem.doNotCloseOnMouseClick"}. The default value is
+ * {@code false}. Setting the property to {@code true} prevents the menu from
+ * closing when it is clicked by the mouse. If the client property is set its
+ * value is always used; otherwise the {@literal L&F} property is queried.
  * Note: some {@code L&F}s may ignore this property. All built-in {@code L&F}s
  * inherit this behaviour.
  * <p>
--- a/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicLookAndFeel.java	Fri Sep 30 11:45:30 2016 -0700
+++ b/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicLookAndFeel.java	Fri Sep 30 22:57:41 2016 +0400
@@ -1055,7 +1055,6 @@
             "RadioButtonMenuItem.checkIcon", radioButtonMenuItemIcon,
             "RadioButtonMenuItem.arrowIcon", menuItemArrowIcon,
             "RadioButtonMenuItem.commandSound", null,
-            "RadioButtonMenuItem.closeOnMouseClick", Boolean.TRUE,
 
             "CheckBoxMenuItem.font", dialogPlain12,
             "CheckBoxMenuItem.acceleratorFont", dialogPlain12,
@@ -1072,7 +1071,6 @@
             "CheckBoxMenuItem.checkIcon", checkBoxMenuItemIcon,
             "CheckBoxMenuItem.arrowIcon", menuItemArrowIcon,
             "CheckBoxMenuItem.commandSound", null,
-            "CheckBoxMenuItem.closeOnMouseClick", Boolean.TRUE,
 
             "Menu.font", dialogPlain12,
             "Menu.acceleratorFont", dialogPlain12,
--- a/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicMenuItemUI.java	Fri Sep 30 11:45:30 2016 -0700
+++ b/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicMenuItemUI.java	Fri Sep 30 22:57:41 2016 +0400
@@ -936,13 +936,15 @@
         }
     }
 
-    boolean closeOnMouseClick() {
+    boolean doNotCloseOnMouseClick() {
         if (menuItem instanceof JCheckBoxMenuItem) {
-            return UIManager.getBoolean("CheckBoxMenuItem.closeOnMouseClick");
+            String property = "CheckBoxMenuItem.doNotCloseOnMouseClick";
+            return SwingUtilities2.getBoolean(menuItem, property);
         } else if (menuItem instanceof JRadioButtonMenuItem) {
-            return UIManager.getBoolean("RadioButtonMenuItem.closeOnMouseClick");
+            String property = "RadioButtonMenuItem.doNotCloseOnMouseClick";
+            return SwingUtilities2.getBoolean(menuItem, property);
         }
-        return true;
+        return false;
     }
 
     /**
@@ -967,7 +969,7 @@
             BasicLookAndFeel.playSound(menuItem, getPropertyPrefix() +
                                        ".commandSound");
         }
-        if (closeOnMouseClick()) {
+        if (!doNotCloseOnMouseClick()) {
             // Visual feedback
             if (msm == null) {
                 msm = MenuSelectionManager.defaultManager();
--- a/jdk/src/java.desktop/share/classes/sun/swing/SwingUtilities2.java	Fri Sep 30 11:45:30 2016 -0700
+++ b/jdk/src/java.desktop/share/classes/sun/swing/SwingUtilities2.java	Fri Sep 30 22:57:41 2016 +0400
@@ -2047,6 +2047,25 @@
     }
 
     /**
+     * Returns the client property for the given key if it is set; otherwise
+     * returns the {@L&F} property.
+     *
+     * @param component the component
+     * @param key an {@code String} specifying the key for the desired boolean value
+     * @return the boolean value of the client property if it is set or the {@L&F}
+     *         property in other case.
+     */
+    public static boolean getBoolean(JComponent component, String key) {
+        Object clientProperty = component.getClientProperty(key);
+
+        if (clientProperty instanceof Boolean) {
+            return Boolean.TRUE.equals(clientProperty);
+        }
+
+        return UIManager.getBoolean(key);
+    }
+
+    /**
      * Used to listen to "blit" repaints in RepaintManager.
      */
     public interface RepaintListener {
--- a/jdk/test/javax/swing/JMenuItem/8158566/CloseOnMouseClickPropertyTest.java	Fri Sep 30 11:45:30 2016 -0700
+++ b/jdk/test/javax/swing/JMenuItem/8158566/CloseOnMouseClickPropertyTest.java	Fri Sep 30 22:57:41 2016 +0400
@@ -37,45 +37,67 @@
 
 /*
  * @test
- * @bug 8158566 8160879 8160977
+ * @bug 8158566 8160879 8160977 8158566
  * @summary Provide a Swing property which modifies MenuItemUI behaviour
  */
 public class CloseOnMouseClickPropertyTest {
 
+    private static final String CHECK_BOX_PROP = "CheckBoxMenuItem."
+            + "doNotCloseOnMouseClick";
+    private static final String RADIO_BUTTON_PROP = "RadioButtonMenuItem"
+            + ".doNotCloseOnMouseClick";
+
     private static JFrame frame;
     private static JMenu menu;
 
+    private static TestItem[] TEST_ITEMS = {
+        new TestItem(TestType.CHECK_BOX_MENU_ITEM, true, true),
+        new TestItem(TestType.CHECK_BOX_MENU_ITEM, true, false),
+        new TestItem(TestType.CHECK_BOX_MENU_ITEM, false, true),
+        new TestItem(TestType.CHECK_BOX_MENU_ITEM, false, false),
+
+        new TestItem(TestType.CHECK_BOX_MENU_ITEM, null, true),
+        new TestItem(TestType.CHECK_BOX_MENU_ITEM, null, false),
+        new TestItem(TestType.CHECK_BOX_MENU_ITEM, true, null),
+        new TestItem(TestType.CHECK_BOX_MENU_ITEM, false, null),
+
+
+        new TestItem(TestType.RADIO_BUTTON_MENU_ITEM, true, true),
+        new TestItem(TestType.RADIO_BUTTON_MENU_ITEM, true, false),
+        new TestItem(TestType.RADIO_BUTTON_MENU_ITEM, false, true),
+        new TestItem(TestType.RADIO_BUTTON_MENU_ITEM, false, false),
+
+        new TestItem(TestType.RADIO_BUTTON_MENU_ITEM, true, null),
+        new TestItem(TestType.RADIO_BUTTON_MENU_ITEM, false, null),
+        new TestItem(TestType.RADIO_BUTTON_MENU_ITEM, null, true),
+        new TestItem(TestType.RADIO_BUTTON_MENU_ITEM, null, false),
+
+        new TestItem(TestType.MENU_ITEM, true, true),
+        new TestItem(TestType.MENU_ITEM, true, false),
+        new TestItem(TestType.MENU_ITEM, false, true),
+        new TestItem(TestType.MENU_ITEM, false, false),
+
+        new TestItem(TestType.MENU_ITEM, true, null),
+        new TestItem(TestType.MENU_ITEM, false, null),
+        new TestItem(TestType.MENU_ITEM, null, true),
+        new TestItem(TestType.MENU_ITEM, null, false),
+    };
+
     public static void main(String[] args) throws Exception {
 
         for (UIManager.LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
             UIManager.setLookAndFeel(info.getClassName());
-            test(true);
-
-            setProperty(false);
-            test(false);
-
-            setProperty(true);
-            test(true);
+            for (TestItem testItem : TEST_ITEMS) {
+                test(testItem);
+            }
         }
     }
 
-    private static void setProperty(boolean closeOnMouseClick) {
-        UIManager.put("CheckBoxMenuItem.closeOnMouseClick", closeOnMouseClick);
-        UIManager.put("RadioButtonMenuItem.closeOnMouseClick", closeOnMouseClick);
-    }
-
-    private static void test(boolean closeOnMouseClick) throws Exception {
-        for (TestType testType : TestType.values()) {
-            test(testType, closeOnMouseClick);
-        }
-    }
-
-    private static void test(TestType testType, boolean closeOnMouseClick)
-            throws Exception {
+    private static void test(TestItem item) throws Exception {
 
         Robot robot = new Robot();
         robot.setAutoDelay(50);
-        SwingUtilities.invokeAndWait(() -> createAndShowGUI(testType));
+        SwingUtilities.invokeAndWait(() -> createAndShowGUI(item));
         robot.waitForIdle();
 
         Point point = getClickPoint(true);
@@ -94,21 +116,13 @@
             JMenuItem menuItem = menu.getItem(0);
             boolean isShowing = menuItem.isShowing();
             frame.dispose();
-
-            if (TestType.MENU_ITEM.equals(testType)) {
-                if (isShowing) {
-                    throw new RuntimeException("Menu Item is not closed!");
-                }
-            } else {
-                if (isShowing ^ !closeOnMouseClick) {
-                    throw new RuntimeException("Property is not taken into account:"
-                            + " closeOnMouseClick");
-                }
+            if (isShowing ^ item.doNotCloseOnMouseClick()) {
+                throw new RuntimeException("Property is not taken into account!");
             }
         });
     }
 
-    private static void createAndShowGUI(TestType testType) {
+    private static void createAndShowGUI(TestItem testItem) {
 
         frame = new JFrame();
         frame.setSize(300, 300);
@@ -116,23 +130,15 @@
 
         JMenuBar menuBar = new JMenuBar();
         menu = new JMenu("Menu");
-        menu.add(getMenuItem(testType));
+        JMenuItem menuItem = testItem.getMenuItem();
+        testItem.setProperties(menuItem);
+        menu.add(menuItem);
         menuBar.add(menu);
+
         frame.setJMenuBar(menuBar);
         frame.setVisible(true);
     }
 
-    private static JMenuItem getMenuItem(TestType testType) {
-        switch (testType) {
-            case CHECK_BOX_MENU_ITEM:
-                return new JCheckBoxMenuItem("Check Box");
-            case RADIO_BUTTON_MENU_ITEM:
-                return new JRadioButtonMenuItem("Radio Button");
-            default:
-                return new JMenuItem("Menu Item");
-        }
-    }
-
     private static Point getClickPoint(boolean parent) throws Exception {
         Point points[] = new Point[1];
 
@@ -157,4 +163,60 @@
         CHECK_BOX_MENU_ITEM,
         RADIO_BUTTON_MENU_ITEM
     }
-}
+
+    static class TestItem {
+
+        TestType type;
+        Boolean compDoNotCloseOnMouseClick;
+        Boolean lafDoNotCloseOnMouseClick;
+
+        public TestItem(TestType type,
+                        Boolean compDoNotCloseOnMouseClick,
+                        Boolean lafDoNotCloseOnMouseClick)
+        {
+            this.type = type;
+            this.compDoNotCloseOnMouseClick = compDoNotCloseOnMouseClick;
+            this.lafDoNotCloseOnMouseClick = lafDoNotCloseOnMouseClick;
+        }
+
+        boolean doNotCloseOnMouseClick() {
+            switch (type) {
+                case MENU_ITEM:
+                    return false;
+                default:
+                    return compDoNotCloseOnMouseClick != null
+                            ? compDoNotCloseOnMouseClick
+                            : lafDoNotCloseOnMouseClick;
+            }
+        }
+
+        void setProperties(JMenuItem menuItem) {
+            switch (type) {
+                case CHECK_BOX_MENU_ITEM:
+                    menuItem.putClientProperty(CHECK_BOX_PROP, compDoNotCloseOnMouseClick);
+                    UIManager.put(CHECK_BOX_PROP, lafDoNotCloseOnMouseClick);
+                    break;
+                case RADIO_BUTTON_MENU_ITEM:
+                    menuItem.putClientProperty(RADIO_BUTTON_PROP, compDoNotCloseOnMouseClick);
+                    UIManager.put(RADIO_BUTTON_PROP, lafDoNotCloseOnMouseClick);
+                    break;
+                default:
+                    menuItem.putClientProperty(CHECK_BOX_PROP, compDoNotCloseOnMouseClick);
+                    menuItem.putClientProperty(RADIO_BUTTON_PROP, compDoNotCloseOnMouseClick);
+                    UIManager.put(CHECK_BOX_PROP, lafDoNotCloseOnMouseClick);
+                    UIManager.put(RADIO_BUTTON_PROP, lafDoNotCloseOnMouseClick);
+            }
+        }
+
+        JMenuItem getMenuItem() {
+            switch (type) {
+                case CHECK_BOX_MENU_ITEM:
+                    return new JCheckBoxMenuItem("Check Box");
+                case RADIO_BUTTON_MENU_ITEM:
+                    return new JRadioButtonMenuItem("Radio Button");
+                default:
+                    return new JMenuItem("Menu Item");
+            }
+        }
+    }
+}
\ No newline at end of file