On 8/4/2015 10:50 AM, Walter Laan wrote:
Below a use case where I had to cast to sun.swing.UIAction to correctly check
if it is enabled or not.
Let me know if I need to make the bug report or if you can add it as a comment
to any existing issue.
Thank you for the report. Yes, please, file an enhancement on it.
Thanks,
Alexandr.
The issue with sun.swing.UIAction is as follows:
It does not return the correct value from #isEnabled(), so you need to cast and
call #isEnabled(Object) instead (check argument). This is because a single
instance of UIAction is shared between all components with the same UI class, I
assume originally for performance reasons.
If you just want to perform the action when really enabled, you can use
SwingUtilities#notifyAction(Action, KeyStroke, KeyEvent, Object, int) where the
Object is the 'sender' (the action event source component) which is passed to
UIAction#isEnabled(Object).
It is technically possible to work around it with current public API, but that
means not using any of the Swing UI classes at all and rewrite them without
using UIAction.
Solutions:
1) Fix UIAction by removing #isEnabled(Object) and have an instance per
component
Lots of work and large impact.
Swing API users can remove references to UIAction and just call
Action#isEnabled()
2) Make UIAction public API (move to javax.swing.plaf?)
Simple refactor but lots of files changed
Swing API users only need to update their imports
3) Do nothing and force Swing API to do point 1 themselves by re-implementing
the components and UI classes using only public API
Even more work than first solution but only for Swing API users
Or they can access through reflection if that is not blocked by the module
system? It will probably depend on the SecurityManager I guess.
4) Provide boolean SwingUtilities.canNotifyAction(Action, Object) which returns
true if #notifyAction(Action, KeyStroke, KeyEvent, Object, int) would call
action performed
Minimal work and minimal impact
Swing API users need to change code from ((sun.swing.UIAction)
action).isEnabled(component) to SwingUtilities.canNotifyAction(component,
action)
5) Something else?
My preference would be solution 4 due to minimal impact - solution 2 also has
not much impact but then you have public API which does not implement the
Action interface correctly. Solution 1 would be the correct one but a lot of
work.
A simple test case:
import java.awt.EventQueue;
import javax.swing.Action;
import javax.swing.JTable;
import javax.swing.table.DefaultTableModel;
public class TestUIAction {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
JTable table = new JTable(new DefaultTableModel(1, 1));
Action action = table.getActionMap().get("cancel");
System.out.println("JTable#isEditing() = " +
table.isEditing());
System.out.println("Action#isEnabled() = " + action.isEnabled() + " but
should be false!");
System.out.println("UIAction#isEnabled(Object) = " + ((sun.swing.UIAction)
action).isEnabled(table));
table.editCellAt(0, 0);
System.out.println("JTable#isEditing() = " + table.isEditing());
System.out.println("Action#isEnabled() = " + action.isEnabled());
System.out.println("UIAction#isEnabled(Object) = " + ((sun.swing.UIAction)
action).isEnabled(table));
}
});
}
}
Output:
JTable#isEditing() = false
Action#isEnabled() = true but should be false!
UIAction#isEnabled(Object) = false
JTable#isEditing() = true
Action#isEnabled() = true
UIAction#isEnabled(Object) = true
Below a (quite long) scenario is which I had to use it:
Using Jidesoft HierarchicalTable (see
http://www.jidesoft.com/images/hierarchicaltable.png) which has (multiple)
JTables as child components of a JTable. Their implementation has a work around
to avoid keystrokes from being processed by the parent table if the focus is in
a child component table. They do this by placing a JPanel between the parent
and child table which registers a no-op action for each action in the JTable
Input/ActionMap.
For example:
-top row is selected in the focused child table
-users presses up arrow
-child table up action is not enabled (since top row selection cannot move up)
-key press goes up to the inserted panel and is consumed (action performed that
does nothing)
-parent table up arrow action does not get executed so its row selection is not
changed
As mentioned this is a workaround to avoid the parent component handling the
key input, but a 'correct' solution would require re-writing BasicTableUI (and
all the sub-classes for all the look and feels) for their HierarchicalTable
instead of re-using the JTable UI.
To improve the hack in the case of the escape key, which I want to use to close
a JDialog the HierarchicalTable (skip the parent table actions instead of
consuming it before gets there), I changed the no-op action to be only enabled
if the original action is enabled in the parent table.
For example:
-child table is focused and not cell editing
-user presses escape
-child table 'cancel cell edit action' does not get notified because is not
enabled
-key press goes up the inserted panel but does not consume the action because
the parent table 'cancel cell edit' action is also not enabled
-parent table 'cancel cell edit action' does not get notified because is not
enabled
-eventually key press comes to the JDialog root pane and executes the close
dialog action
But since the 'cancel cell edit action' is an sun.swing.UIAction, to check if
it is really enabled, I need to cast to the interal API and call
#isEnabled(parentTable):
/**
* Mute an action by doing nothing if the original action is enabled
*/
private static class MutedAction extends AbstractAction {
private final Object sender;
private final Action action;
public MutedAction(Object sender, Action action) {
this.sender = sender;
this.action = action;
}
@Override
public void actionPerformed(ActionEvent e) {
// muted
}
@Override
public boolean isEnabled() {
if(action instanceof sun.swing.UIAction) {
return ((sun.swing.UIAction) action).isEnabled(sender);
}
else {
return action.isEnabled();
}
}
}
Kind regards,
Walter Laan
Cost Engineering Consultancy
-----Original Message-----
From: swing-dev [mailto:swing-dev-boun...@openjdk.java.net] On Behalf Of
Alexander Scherbatiy
Sent: maandag 3 augustus 2015 13:22
To: Van Den Borre, Koen <koen.vandenbo...@esko.com>
Cc: swing-dev@openjdk.java.net; macosx-port-...@openjdk.java.net
Subject: Re: <Swing Dev> Public API for internal Swing classes.
Hello Koen,
Are you using the isEnabled(Object sender) method just to separate a logic
that checks that an action needs to be executed from the action execution in
the same way as it it done in the UIAction?
Could you file an enhancement on it and provide a simple use case:
http://bugreport.java.com/bugreport
Thanks,
Alexandr.
On 7/27/2015 4:13 PM, Van Den Borre, Koen wrote:
Hey,
We are using sun.swing.UIAction in a custom ListUI where we override the
following method and use the sender object:
@Override
public boolean isEnabled(Object sender)
Regards,
Koen
On 27 Jul 2015, at 14:30, Alexander Scherbatiy <alexandr.scherba...@oracle.com>
wrote:
According to the JEP 200: The Modular JDK (see
http://openjdk.java.net/jeps/200) we expect that the standard Java SE modules
will not export any internal packages.
It means that classes from internal packages (like sun.swing) will not be
accessible.
For example:
sun.swing.FilePane
sun.swing.SwingUtilities2
sun.swing.sun.swing.plaf.synth.SynthIcon
and others.
Please, let us known if you are using the internal Swing API and it is not
possible to replace it by public API.
There are some known requests:
JDK-8132119 Provide public API for text related methods in SwingUtilities2
https://bugs.openjdk.java.net/browse/JDK-8132119
JDK-8132120 Provide public API for screen menu bar support on MacOS
https://bugs.openjdk.java.net/browse/JDK-8132120
JDK-6274842 RFE: Provide a means for a custom look and feel to use desktop
font antialiasing settings.
https://bugs.openjdk.java.net/browse/JDK-6274842
If you don't know if you use these types (because you use 3rd party
jars) you can use the JDK 8 "jdeps" tool to find such dependencies :-
~/jdk1.8/bin/jdeps
Usage: jdeps <options> <classes...>
where <classes> can be a pathname to a .class file, a directory, a
JAR file, or a fully-qualified class name
Thanks,
Alexandr.