diff --git a/jdk/src/java.desktop/share/classes/javax/swing/Action.java b/jdk/src/java.desktop/share/classes/javax/swing/Action.java index b1ebbec40a8..8a7ae6ea1db 100644 --- a/jdk/src/java.desktop/share/classes/javax/swing/Action.java +++ b/jdk/src/java.desktop/share/classes/javax/swing/Action.java @@ -356,24 +356,41 @@ public interface Action extends ActionListener { public void putValue(String key, Object value); /** - * Sets the enabled state of the Action. When enabled, + * Sets the enabled state of the {@code Action}. When enabled, * any component associated with this object is active and - * able to fire this object's actionPerformed method. - * If the value has changed, a PropertyChangeEvent is sent + * able to fire this object's {@code actionPerformed} method. + * If the value has changed, a {@code PropertyChangeEvent} is sent * to listeners. * - * @param b true to enable this Action, false to disable it + * @param b true to enable this {@code Action}, false to disable it + * @see #accept */ public void setEnabled(boolean b); /** - * Returns the enabled state of the Action. When enabled, + * Returns the enabled state of the {@code Action}. When enabled, * any component associated with this object is active and - * able to fire this object's actionPerformed method. + * able to fire this object's {@code actionPerformed} method. * - * @return true if this Action is enabled + * @return true if this {@code Action} is enabled + * @see #accept */ public boolean isEnabled(); + /** + * Determines whether the action should be performed with the specified + * sender object. The {@code sender} can be {@code null}. + * The method must return false if the action is disabled. + *

+ * @param sender the object to check, can be null + * @return {@code true} if the action should be performed with the sender + * object, must be false if the action is disabled. + * @see isEnabled + * @see setEnabled + */ + default boolean accept(Object sender) { + return isEnabled(); + } + /** * Adds a PropertyChange listener. Containers and attached * components use these methods to register interest in this diff --git a/jdk/src/java.desktop/share/classes/javax/swing/SwingUtilities.java b/jdk/src/java.desktop/share/classes/javax/swing/SwingUtilities.java index 5c34dffb012..6a48eb423a3 100644 --- a/jdk/src/java.desktop/share/classes/javax/swing/SwingUtilities.java +++ b/jdk/src/java.desktop/share/classes/javax/swing/SwingUtilities.java @@ -1704,9 +1704,9 @@ public class SwingUtilities implements SwingConstants } /** - * Invokes actionPerformed on action if - * action is enabled (and non-{@code null}). The command for the - * ActionEvent is determined by: + * Invokes {@code actionPerformed} on {@code action} if {@code action} + * is non-{@code null} and accepts the sender object. + * The command for the ActionEvent is determined by: *

    *
  1. If the action was registered via * registerKeyboardAction, then the command string @@ -1725,23 +1725,18 @@ public class SwingUtilities implements SwingConstants * @param modifiers action modifiers * @return {@code true} if {@code action} is non-{@code null} and * actionPerformed is invoked on it. + * @see javax.swing.Action#accept * * @since 1.3 */ public static boolean notifyAction(Action action, KeyStroke ks, KeyEvent event, Object sender, int modifiers) { - if (action == null) { - return false; - } - if (action instanceof UIAction) { - if (!((UIAction)action).isEnabled(sender)) { - return false; - } - } - else if (!action.isEnabled()) { + + if (action == null || !action.accept(sender)) { return false; } + Object commandO; boolean stayNull; diff --git a/jdk/src/java.desktop/share/classes/javax/swing/TransferHandler.java b/jdk/src/java.desktop/share/classes/javax/swing/TransferHandler.java index 5201a0c26a5..70fe30a37be 100644 --- a/jdk/src/java.desktop/share/classes/javax/swing/TransferHandler.java +++ b/jdk/src/java.desktop/share/classes/javax/swing/TransferHandler.java @@ -1695,13 +1695,10 @@ public class TransferHandler implements Serializable { super(name); } - public boolean isEnabled(Object sender) { - if (sender instanceof JComponent - && ((JComponent)sender).getTransferHandler() == null) { - return false; - } - - return true; + @Override + public boolean accept(Object sender) { + return !(sender instanceof JComponent + && ((JComponent)sender).getTransferHandler() == null); } private static final JavaSecurityAccess javaSecurityAccess = diff --git a/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicButtonListener.java b/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicButtonListener.java index 3278d33e4a9..1fda40ee5df 100644 --- a/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicButtonListener.java +++ b/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicButtonListener.java @@ -319,13 +319,10 @@ public class BasicButtonListener implements MouseListener, MouseMotionListener, } } - public boolean isEnabled(Object sender) { - if(sender != null && (sender instanceof AbstractButton) && - !((AbstractButton)sender).getModel().isEnabled()) { - return false; - } else { - return true; - } + @Override + public boolean accept(Object sender) { + return !((sender instanceof AbstractButton) && + !((AbstractButton)sender).getModel().isEnabled()); } } -} +} \ No newline at end of file diff --git a/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicComboBoxUI.java b/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicComboBoxUI.java index 5a3f67da230..e08c8a3045f 100644 --- a/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicComboBoxUI.java +++ b/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicComboBoxUI.java @@ -1719,7 +1719,8 @@ public class BasicComboBoxUI extends ComboBoxUI { return comboBox.getSelectedIndex(); } - public boolean isEnabled(Object c) { + @Override + public boolean accept(Object c) { if (getName() == HIDE) { return (c != null && ((JComboBox)c).isPopupVisible()); } diff --git a/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicDesktopPaneUI.java b/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicDesktopPaneUI.java index c8a4d9d0a7a..e784d2d4b2a 100644 --- a/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicDesktopPaneUI.java +++ b/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicDesktopPaneUI.java @@ -646,7 +646,8 @@ public class BasicDesktopPaneUI extends DesktopPaneUI { } } - public boolean isEnabled(Object sender) { + @Override + public boolean accept(Object sender) { if (sender instanceof JDesktopPane) { JDesktopPane dp = (JDesktopPane)sender; String action = getName(); diff --git a/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicInternalFrameUI.java b/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicInternalFrameUI.java index 8f534ef786a..b185d785a25 100644 --- a/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicInternalFrameUI.java +++ b/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicInternalFrameUI.java @@ -200,7 +200,8 @@ public class BasicInternalFrameUI extends InternalFrameUI } } - public boolean isEnabled(Object sender){ + @Override + public boolean accept(Object sender){ if (sender instanceof JInternalFrame) { JInternalFrame iFrame = (JInternalFrame)sender; if (iFrame.getUI() instanceof BasicInternalFrameUI) { diff --git a/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicListUI.java b/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicListUI.java index ae8827a4f82..39585e86db8 100644 --- a/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicListUI.java +++ b/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicListUI.java @@ -2060,7 +2060,8 @@ public class BasicListUI extends ListUI } } - public boolean isEnabled(Object c) { + @Override + public boolean accept(Object c) { Object name = getName(); if (name == SELECT_PREVIOUS_COLUMN_CHANGE_LEAD || name == SELECT_NEXT_COLUMN_CHANGE_LEAD || diff --git a/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicMenuUI.java b/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicMenuUI.java index 7231ae2a570..98afcc95679 100644 --- a/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicMenuUI.java +++ b/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicMenuUI.java @@ -312,7 +312,8 @@ public class BasicMenuUI extends BasicMenuItemUI } } - public boolean isEnabled(Object c) { + @Override + public boolean accept(Object c) { if (c instanceof JMenu) { return ((JMenu)c).isEnabled(); } diff --git a/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicRootPaneUI.java b/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicRootPaneUI.java index 300e46a2e7b..714a06396c6 100644 --- a/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicRootPaneUI.java +++ b/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicRootPaneUI.java @@ -256,7 +256,8 @@ public class BasicRootPaneUI extends RootPaneUI implements } } - public boolean isEnabled(Object sender) { + @Override + public boolean accept(Object sender) { String key = getName(); if(key == POST_POPUP) { MenuElement[] elems = MenuSelectionManager @@ -278,7 +279,7 @@ public class BasicRootPaneUI extends RootPaneUI implements return false; } - if (sender != null && sender instanceof JRootPane) { + if (sender instanceof JRootPane) { JButton owner = ((JRootPane)sender).getDefaultButton(); return (owner != null && owner.getModel().isEnabled()); } diff --git a/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTableHeaderUI.java b/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTableHeaderUI.java index 2ec7e11ef71..a02d25f293b 100644 --- a/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTableHeaderUI.java +++ b/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTableHeaderUI.java @@ -855,7 +855,8 @@ public class BasicTableHeaderUI extends TableHeaderUI { super(name); } - public boolean isEnabled(Object sender) { + @Override + public boolean accept(Object sender) { if (sender instanceof JTableHeader) { JTableHeader th = (JTableHeader)sender; TableColumnModel cm = th.getColumnModel(); diff --git a/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTableUI.java b/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTableUI.java index 73fd9a51340..e6a3d0eb9b5 100644 --- a/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTableUI.java +++ b/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTableUI.java @@ -676,7 +676,8 @@ public class BasicTableUI extends TableUI } } - public boolean isEnabled(Object sender) { + @Override + public boolean accept(Object sender) { String key = getName(); if (sender instanceof JTable && diff --git a/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTreeUI.java b/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTreeUI.java index 7216dfe4a4b..b43423dbc02 100644 --- a/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTreeUI.java +++ b/jdk/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTreeUI.java @@ -4416,7 +4416,8 @@ public class BasicTreeUI extends TreeUI super(key); } - public boolean isEnabled(Object o) { + @Override + public boolean accept(Object o) { if (o instanceof JTree) { if (getName() == CANCEL_EDITING) { return ((JTree)o).isEditing(); diff --git a/jdk/src/java.desktop/share/classes/sun/swing/UIAction.java b/jdk/src/java.desktop/share/classes/sun/swing/UIAction.java index 98aa04c06c4..d87801fc683 100644 --- a/jdk/src/java.desktop/share/classes/sun/swing/UIAction.java +++ b/jdk/src/java.desktop/share/classes/sun/swing/UIAction.java @@ -87,7 +87,7 @@ public abstract class UIAction implements Action { * Cover method for isEnabled(null). */ public final boolean isEnabled() { - return isEnabled(null); + return accept(null); } /** @@ -96,7 +96,8 @@ public abstract class UIAction implements Action { * * @param sender Widget enabled state is being asked for, may be null. */ - public boolean isEnabled(Object sender) { + @Override + public boolean accept(Object sender) { return true; } diff --git a/jdk/test/javax/swing/Action/8133039/bug8133039.java b/jdk/test/javax/swing/Action/8133039/bug8133039.java new file mode 100644 index 00000000000..94ad929707b --- /dev/null +++ b/jdk/test/javax/swing/Action/8133039/bug8133039.java @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Robot; +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.JComboBox; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.KeyStroke; +import javax.swing.SwingUtilities; +import sun.swing.UIAction; + +/** + * @test + * @bug 8133039 + * @summary Provide public API to sun.swing.UIAction#isEnabled(Object) + * @author Alexander Scherbatiy + */ +public class bug8133039 { + + private static volatile int ACTION_PERFORMED_CALLS = 0; + private static volatile int ACTION_ACCEPTED_CALLS = 0; + + public static void main(String[] args) throws Exception { + testActionNotification(); + testPopupAction(); + } + + private static void testActionNotification() { + + KeyEvent keyEvent = new KeyEvent(new JLabel("Test"), 0, 0, 0, 0, 'A'); + SenderObject rejectedSenderObject = new SenderObject(); + SwingUtilities.notifyAction(new TestAction(false), null, keyEvent, + rejectedSenderObject, 0); + + if (rejectedSenderObject.accepted) { + throw new RuntimeException("The sender is incorrectly accepted!"); + } + + if (rejectedSenderObject.performed) { + throw new RuntimeException("The action is incorrectly performed!"); + } + + SenderObject acceptedSenderObject = new SenderObject(); + SwingUtilities.notifyAction(new TestAction(true), null, keyEvent, + acceptedSenderObject, 0); + + if (!acceptedSenderObject.accepted) { + throw new RuntimeException("The sender is not accepted!"); + } + + if (!acceptedSenderObject.performed) { + throw new RuntimeException("The action is not performed!"); + } + } + + private static void testPopupAction() throws Exception { + + SwingUtilities.invokeAndWait(bug8133039::createAndShowGUI); + + Robot robot = new Robot(); + robot.setAutoDelay(50); + robot.waitForIdle(); + + robot.keyPress(KeyEvent.VK_A); + robot.keyRelease(KeyEvent.VK_A); + robot.waitForIdle(); + + if (ACTION_ACCEPTED_CALLS != 1) { + throw new RuntimeException("Method accept is not invoked!"); + } + + if (ACTION_PERFORMED_CALLS != 1) { + throw new RuntimeException("Method actionPerformed is not invoked!"); + } + + robot.keyPress(KeyEvent.VK_A); + robot.keyRelease(KeyEvent.VK_A); + robot.waitForIdle(); + + if (ACTION_ACCEPTED_CALLS != 2) { + throw new RuntimeException("Method accept is not invoked!"); + } + + if (ACTION_PERFORMED_CALLS != 1) { + throw new RuntimeException("Method actionPerformed is invoked twice!"); + } + } + + private static void createAndShowGUI() { + + JFrame frame = new JFrame(); + frame.setSize(300, 300); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + + JComboBox comboBox = new JComboBox<>(new String[]{"1", "2", "3"}); + + Action showPopupAction = new ShowPopupAction(); + comboBox.getInputMap().put(KeyStroke.getKeyStroke("A"), "showPopup"); + comboBox.getActionMap().put("showPopup", showPopupAction); + + frame.getContentPane().add(comboBox); + frame.setVisible(true); + } + + private static class ShowPopupAction extends UIAction { + + public ShowPopupAction() { + super("showPopup"); + } + + @Override + public void actionPerformed(ActionEvent e) { + ACTION_PERFORMED_CALLS++; + Object src = e.getSource(); + if (src instanceof JComboBox) { + ((JComboBox) src).showPopup(); + } + } + + @Override + public boolean accept(Object sender) { + ACTION_ACCEPTED_CALLS++; + if (sender instanceof JComboBox) { + JComboBox c = (JComboBox) sender; + return !c.isPopupVisible(); + } + return false; + } + } + + private static class SenderObject { + + private boolean accepted; + private boolean performed; + } + + private static class TestAction extends AbstractAction { + + private final boolean acceptSender; + + public TestAction(boolean acceptSender) { + this.acceptSender = acceptSender; + } + + @Override + public boolean accept(Object sender) { + ((SenderObject) sender).accepted = acceptSender; + return acceptSender; + } + + @Override + public void actionPerformed(ActionEvent e) { + ((SenderObject) e.getSource()).performed = true; + } + } +}