I have a Popup that is shown when a user clicks on a button. I would like to hide the popup when any of the following events occur:
The user clicks somewhere else in the application. (The background panel for example)
The user minimizes the application.
The JPopupMenu has this behavior, but I need more than just JMenuItems. The following code block is a simplified illustration to demonstrate the current usage.
import java.awt.*;
import java.awt.event.ActionEvent;
import javax.swing.*;
public class PopupTester extends JFrame {
public static void main(String[] args) {
final PopupTester popupTester = new PopupTester();
popupTester.setLayout(new FlowLayout());
popupTester.setSize(300, 100);
popupTester.add(new JButton("Click Me") {
#Override
protected void fireActionPerformed(ActionEvent event) {
Point location = getLocationOnScreen();
int y = (int) (location.getY() + getHeight());
int x = (int) location.getX();
JLabel myComponent = new JLabel("Howdy");
Popup popup = PopupFactory.getSharedInstance().getPopup(popupTester, myComponent, x, y);
popup.show();
}
});
popupTester.add(new JButton("No Click Me"));
popupTester.setVisible(true);
popupTester.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
Use a JPopupMenu. You can add any component to it, not just menu items.
As pajton noted in a previous comment, Popup is not a JComponent to which listeners can be readily bound. But, as its documentation states, "implementations of Popup are responsible for creating and maintaining their own Components to render [its subject] to the user."
So in using it as your presentation mechanism, your Popup is going to have to present itself as an actual Swing component anyway. Have it register itself to that component. Have it hide itself when the component loses focus.
import java.awt.FlowLayout;
import java.awt.Frame;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.WindowEvent;
import java.awt.event.WindowFocusListener;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.Popup;
public class PopupTester extends JFrame {
private static class MessagePopup extends Popup
implements WindowFocusListener
{
private final JDialog dialog;
public MessagePopup(Frame base, String message) {
super();
dialog = new JOptionPane().createDialog( base, "Message" );
dialog.setModal( false );
dialog.setContentPane( new JLabel( message ) );
}
#Override public void show() {
dialog.addWindowFocusListener( this );
dialog.setVisible( true );
}
#Override public void hide() {
dialog.setVisible( false );
dialog.removeWindowFocusListener( this );
}
public void windowGainedFocus( WindowEvent e ) {
// NO-OP
}
public void windowLostFocus( WindowEvent e ) {
hide();
}
}
public static void main(String[] args) {
final PopupTester popupTester = new PopupTester();
popupTester.setLayout(new FlowLayout());
popupTester.setSize(300, 100);
popupTester.add(new JButton("Click Me") {
#Override
protected void fireActionPerformed(ActionEvent event) {
Point location = getLocationOnScreen();
MessagePopup popup = new MessagePopup( popupTester, "Howdy" );
popup.show();
}
});
popupTester.add(new JButton("No Click Me"));
popupTester.setVisible(true);
popupTester.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
You can add MouseListener to your background panel and hide the popup when somebody clicks on the panel.
To react on application minimization, use WindowListener attached to a JFrame.
Etc, etc. May seem tedious, but surely will work.
Thanks pajton and Noel Ang for getting me pointed in the right direction! Here is the solution that I ended up with. I'm just including it here so that others may benefit from it.
I ended up going with a JWindow since it doesn't get the window decorations but does get focus events.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class PopupTester extends JFrame {
private static class MessagePopup extends Popup implements WindowFocusListener {
private final JWindow dialog;
public MessagePopup(Frame base, JLabel component, int x, int y) {
super();
dialog = new JWindow(base);
dialog.setFocusable(true);
dialog.setLocation(x, y);
dialog.setContentPane(component);
component.setBorder(new JPopupMenu().getBorder());
dialog.setSize(component.getPreferredSize());
dialog.addKeyListener(new KeyAdapter() {
#Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
dialog.setVisible(false);
}
}
});
}
#Override
public void show() {
dialog.addWindowFocusListener(this);
dialog.setVisible(true);
}
#Override
public void hide() {
dialog.setVisible(false);
dialog.removeWindowFocusListener(this);
}
public void windowGainedFocus(WindowEvent e) {
// NO-OP
}
public void windowLostFocus(WindowEvent e) {
hide();
}
}
public static void main(String[] args) {
final PopupTester popupTester = new PopupTester();
popupTester.setLayout(new FlowLayout());
popupTester.setSize(300, 100);
popupTester.add(new JButton("Click Me") {
#Override
protected void fireActionPerformed(ActionEvent event) {
Point location = getLocationOnScreen();
int x = (int) location.getX();
int y = (int) (location.getY() + getHeight());
JLabel myComponent = new JLabel("Howdy");
MessagePopup popup = new MessagePopup(popupTester, myComponent, x, y);
popup.show();
}
});
popupTester.add(new JButton("No Click Me"));
popupTester.setVisible(true);
popupTester.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
You could add a FocusListener to your popup-window, and dispose it when it loses focus. However, that will cause you some troubles when the focus loss is due to some other application (new windows comes to the foreground, you switch virtual desktops, etc.)
But perhaps you (a) know that that cannot happen in your case or (b) would want to close the popup in such cases anyway, a focus-based approach may still be interesting to you.
I know this is an old question but I really needed the Popup to work in my case. So I tried a few things and the following is my solution.
Add a FocusListener to the component you add to the popup and program the focusLost event on that component to hide the popup when focus is lost. Call the requestFocus method on your component just after showing the popup.
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import javax.swing.*;
public class PopupTester extends JFrame {
JButton myButton = new JButton("Click Me");
JLabel myComponent = new JLabel("Howdy");
Popup popup = null;
public PopupTester() {
setLayout(new FlowLayout());
setSize(300, 100);
add(myButton);
add(new JButton("No Click Me"));
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
myComponent.addFocusListener(new FocusAdapter() {
public void focusLost(FocusEvent e) {
if (popup != null) {
popup.hide();
}
}
});
myButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
if (popup != null) {
popup.hide();
popup = null;
}
Point location = myButton.getLocationOnScreen();
int y = (int) (location.getY() + myButton.getHeight());
int x = (int) location.getX();
popup = PopupFactory.getSharedInstance().getPopup(PopupTester.this, myComponent, x, y);
popup.show();
myComponent.requestFocus();
}
});
}
public static void main(String[] args) {
PopupTester popupTester = new PopupTester();
popupTester.setVisible(true);
}
}
Related
Once the steps below are taken, the whole application is frozen and the modal dialog cannot be closed.
(This is related to another question but this time we have a reproducible scenario)
The steps:
Open dropdown
Select "Han-Ra" (the last value in the drop down)
After this, trying to resize or close the modal dialog will not succeed. It reproduces 1 out of 3 times (might be easier to reproduce if you do the selection by arrow down but happens with mouse selection also)
This happens on jdk 1.8 (tried 1.8.0_162 and 1.8.0_144) and jdk 10 (10.0.1) but not when using 1.7 (tried 1.7.0_80)
This is just the most obvious case we could find but it randomly (rarely) happens for most modal dialogs.
Anyone else had this problem and found a workaround? We'll report it to Oracle but we'd be more interested in a workaround.
import javax.swing.*;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
public class FreezePleeze {
public static final Object[] ALL_THE_SINGLE_LADIES = {"Rahan", "Crao", "Naouna", "Han-ra"};
public static void main(String[] args) {
new FreezePleeze();
}
public FreezePleeze() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JButton push_me = new JButton("Push me");
JFrame frame = new JFrame("Mmmmm");
JPanel containerPanel = new JPanel();
frame.add(containerPanel);
final JComboBox<Object> comboBox = new JComboBox<>(ALL_THE_SINGLE_LADIES);
containerPanel.add(comboBox);
frame.setSize(300, 300);
comboBox.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
JDialog jDialog = new JDialog((JFrame) null, true);
jDialog.add(push_me);
if (comboBox.getSelectedIndex() == ALL_THE_SINGLE_LADIES.length - 1) {
jDialog.setLocationRelativeTo(frame);
jDialog.setSize(300, 300);
jDialog.setVisible(true);
}
}
});
comboBox.addPopupMenuListener(new PopupMenuListener() {
#Override
public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
}
#Override
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
try {
Robot robot = new Robot();
robot.keyPress(KeyEvent.VK_WINDOWS);
robot.keyRelease(KeyEvent.VK_WINDOWS);
} catch (AWTException e1) {
e1.printStackTrace();
}
push_me.setText("Finished counting");
}
#Override
public void popupMenuCanceled(PopupMenuEvent e) {
}
});
frame.setVisible(true);
}
});
}
}
I can reproduce your problem. The solution is to submit the correct window to the constructor of your dialog:
Example:
JDialog jDialog = new JDialog(frame, true);
or, if you have no window instance when you create a dialog:
JDialog jDialog = new JDialog(FocusManager.getCurrentKeyboardFocusManager().getActiveWindow(),
ModalityType.APPLICATION_MODAL);
Wrting a chat application, I want the user to be able to send images out of his/her clipboard. For this, I would like to catch any CTRL+Vkeyboard input. Since pasting text should be possible as by default, the original ctrl+v-function (pasting text) must not be overridden.
I see can two approaches, of which none works for me:
1st: Taken from the official Java documentation: KEY LISTENER
editorPane.addKeyListener(new KeyListener() {
#Override
public void keyPressed(KeyEvent e) {
e.getKeyChar()
// when I press ctrl+v, ^ this is falsely a white square character, looks like (U+25A1). Plain v without ctrl does work.
e.getKeyCode()
// ^ this is falsely 0
// (e.getModifiersEx() correctly returns InputEvent.CTRL_DOWN_MASK)
}
2nd: KEY BINDING
InputMap iMap = editorPane.getInputMap(condition);
ActionMap aMap = editorPane.getActionMap();
iMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_V, InputEvent.CTRL_DOWN_MASK), "ctrlV");
aMap.put("ctrlV", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
// works, but overrides natural ctrl+v function!
}
});
Any ideas?
Note: I am using a "foreign" keyboard layout (German). But I can't see why this should make any difference - I would pretty much like to have my application work internationally.
Cheers
edit. Alt+SomeKey however is correctly recognized by the KeyListener
edit2. after changing keyboard layout to US, problem persists.
Stick to Keybindings: KeyListener is a low-level API, while Keybindings will provide you consistent, predictable and robust behaviour.
The solution here is quite easy. You can simply combine the actions yourself by adding a CombinedAction class that will execute the "original" action bound to CTRL+V and the "custom" action you want to execute.
See a small example below combining both actions (here my custom action is a Sysout):
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import javax.swing.JComponent;
import javax.swing.JEditorPane;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.KeyStroke;
import javax.swing.ScrollPaneConstants;
public class TestEditorPane {
private JEditorPane editorPane;
public static class CombinedAction implements ActionListener {
private final ActionListener action1;
private final ActionListener action2;
public CombinedAction(ActionListener action1, ActionListener action2) {
super();
this.action1 = action1;
this.action2 = action2;
}
#Override
public void actionPerformed(ActionEvent e) {
if (action1 != null) {
action1.actionPerformed(e);
}
if (action2 != null) {
action2.actionPerformed(e);
}
}
}
public TestEditorPane() {
}
private void initUI() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// JTEXTBOX
editorPane = new JEditorPane();
KeyStroke ctrlV = KeyStroke.getKeyStroke(KeyEvent.VK_V, KeyEvent.CTRL_DOWN_MASK);
final ActionListener ctrlVAction = editorPane.getActionForKeyStroke(ctrlV);
editorPane.registerKeyboardAction(new CombinedAction(ctrlVAction, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("This is my action on CTRL+V");
}
}), ctrlV, JComponent.WHEN_FOCUSED);
// JSCROLLPANE
JScrollPane scroll1 = new JScrollPane(editorPane);
scroll1.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
scroll1.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
frame.add(scroll1);
frame.setSize(400, 400);
frame.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
TestEditorPane test = new TestEditorPane();
test.initUI();
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
}
Creating a transparent and undercoated JDialog with a JFrame as its parent
will lose transparency upon an iconify/deiconify sequence.
Example:
final JFrame f = new JFrame();
f.setSize(200, 200);
final JDialog d = new JDialog(f, false);
d.setSize(200, 200);
d.setUndecorated(true);
d.setOpacity(.8f);
f.setLocationRelativeTo(null);
d.setLocation(f.getLocation().x + 210, f.getLocation().y);
f.setVisible(true);
d.setVisible(true);
Before iconify, the JDialog is created fine. Here is a screenshot.
After an iconify/deiconify sequence, the JDialog is 100% opaque.
I'm looking for solution to this problem and thinking I'm doing something
incorrect or missing some code.
My environment is Java(TM) SE Runtime Environment (build 1.8.0_11-b12)
running on Microsoft Windows [Version 6.1.7601].
2014-11-13: There is an open JDK bug for this problem
JDK-8062946 Transparent JDialog will lose transparency upon iconify/deiconify sequence.
https://bugs.openjdk.java.net/browse/JDK-8062946
Seems like a bit of a bug to me.
The following seems to work using JDK7 on Windows 7:
f.addWindowListener( new WindowAdapter()
{
#Override
public void windowDeiconified(WindowEvent e)
{
d.setVisible(false);
d.setVisible(true);
}
});
Try the following. I've had various problems with the undecorated JDialog and when I find a
bypass to a new symptom I add it to this method. This is a trimmed example. Add error checking, exception handling and modify as necessary to suit your needs ( ie add listener removal code if your app creates and destroys passed parentWindow objects).
package trimmed.stackoverflow.example;
import java.awt.Color;
import java.awt.Window;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import javax.swing.JDialog;
import javax.swing.SwingUtilities;
public class Helper_Swing {
private static Helper_Swing instance;
private Helper_Swing() {
instance = this;
}
public static Helper_Swing getInstance() {
return instance == null ? new Helper_Swing() : instance;
}
private static class J42WindowAdapter extends WindowAdapter {
final private Window window;
final private JDialog dialog;
private Color colorBG = null;
private float opacity = 0.0f;
private J42WindowAdapter(final Window window, final JDialog dialog) {
super();
this.window = window;
this.dialog = dialog;
this.colorBG = window.getBackground();
this.opacity = window.getOpacity();
}
#Override
public void windowIconified(final WindowEvent e) {
colorBG = dialog.getBackground();
opacity = dialog.getOpacity();
dialog.setOpacity(1.0f); // Must call 1st
dialog.setBackground(Color.BLACK); // Must call 2nd
}
#Override
public void windowDeiconified(final WindowEvent e) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
dialog.setBackground(colorBG);
dialog.setOpacity(opacity);
dialog.setVisible(true);
dialog.repaint();
}
});
}
}
public void bugFix_transparentJDialog(final Window parentWindow, JDialog childWindow) {
if (parentWindow != null && childWindow != null && childWindow.isUndecorated()) {
final WindowListener[] listeners = parentWindow.getWindowListeners();
for (int x = 0; x != listeners.length; x++) {
if (listeners[x] instanceof J42WindowAdapter) {
final J42WindowAdapter adapter = (J42WindowAdapter) listeners[x];
if (adapter.window == parentWindow && adapter.dialog == childWindow) {
childWindow = null;
break;
}
}
}
if (childWindow != null) {
parentWindow.addWindowListener(new J42WindowAdapter(parentWindow, childWindow));
}
}
}
}
Usage example:
Helper_Swing.getInstance().bugFix_transparentJDialog(parentWindow, dialog);
So I have a swing application where a button opens up a window. It is pretty simple, to open it I use:
private static logPicker logWindow;
static boolean logViewerOpen = false;
if (!logViewerOpen) {
logWindow = new logPicker();
logWindow.frmOpenLog.setVisible(true);
logViewerOpen = true;
}
else {
logWindow.frmOpenLog.requestFocus();
}
I also have a window listener to know when the viewer is closed:
frmOpenLog.addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(WindowEvent arg0) {
indexPage.logViewerOpen = false;
frmOpenLog.dispose();
}
});
I do this because I want to keep track on whether or not the window is already open, because if it is then I have to update information. The window I open has a list of logs that a user can double click on to view the information about that log. The problem right now is, when a user double clicks on the list it gets called however many times I have opened and closed that window. example: I open the log picker window, and then close it. I open it again and double click on the log I want to view, and it will open 2 of those. I have the double click simple do a .doClick() on the Open Log button. The weird thing is, when I use the button to open the log, it does not do this. It will only open the log once. Here is the code for the double click event and the Open Log button.
#Override
public void mouseClicked(MouseEvent arg0) {
if (arg0.getClickCount() == 2) {
btnOpenLog.doClick();
}
}
btnOpenLog.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
logViewer window = new logViewer(log.getSelectedValue());
window.frmLogViewer.setVisible(true);
}
});
#LiverpoolFTW: Please provide a SSCCE demonstrating the problem. Absent sufficient code, I speculate you're (re-)adding the MouseListener/MouseAdapter each time your window is opened. The following example works as desired as-is, incrementing the clickCount once per button press or label double-click. But if you uncomment the indicated section, you'll see that the doClick() is executed twice when you double-click the label. If you have, for example, some component to which you're adding a listener each time the window opens, each of those listeners will be executed.
package example.stackoverflow;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
public class ClickCheck extends JFrame
{
private static final long serialVersionUID = -6446528001976145548L;
private static final JButton btnOpenLog = new JButton("Open Log");
public ClickCheck()
{
JLabel label = new JLabel("Double-Click Me");
label.addMouseListener(new MouseAdapter()
{
#Override
public void mouseClicked(MouseEvent arg0) {
if (arg0.getClickCount() == 2) {
btnOpenLog.doClick();
}
}
});
// Uncomment to demonstrate the effect of multiple listeners
// label.addMouseListener(new MouseAdapter()
// {
// #Override
// public void mouseClicked(MouseEvent arg0) {
// if (arg0.getClickCount() == 2) {
// btnOpenLog.doClick();
// }
// }
// });
btnOpenLog.addActionListener(new ActionListener() {
private int clickCount = 0;
public void actionPerformed(ActionEvent e) {
System.out.println(++clickCount + ": Button clicked");
}
});
setSize(200, 200);
setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));
add(btnOpenLog);
add(label);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
ClickCheck c = new ClickCheck();
c.setVisible(true);
}
});
}
}
I have a JFileChooser in a JFrame. I've added an ActionListener to the JFileChooser so that the "Cancel" button works when clicked. I can also tab to the "Cancel" button, but when I then hit the "Enter" key, nothing happens (i.e., the ActionListener isn't called with the event command JFileChooser.CANCEL_SELECTION). What must I do with the JFileChooser so that hitting the "Enter" key when on the "Cancel" button is equivalent to clicking on the "Cancel" button?
Here's a simple example of the (mis)behavior I'm seeing:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
public final class TestApp {
public static void main(final String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
try {
final JFileChooser chooser = new JFileChooser();
chooser.addActionListener(new ActionListener() {
public void actionPerformed(final ActionEvent e) {
System.exit(0);
}
});
final JFrame frame = new JFrame();
frame.add(chooser);
frame.pack();
frame.setVisible(true);
}
catch (final Throwable t) {
t.printStackTrace();
}
}
});
}
}
To see the (mis)behavior, execute the program, tab to "Cancel", and then hit the "Enter" key. The program doesn't terminate on my platform -- although it does when I click on the "Cancel" button.
Extending JFileChooser and overriding cancelSelection() also doesn't work (apparently, that function isn't called when the "Enter" key is hit while on the "Cancel" button).
The (mis)behavior occurs on my Fedora 10 x86_64 system with Java 5, 6, and 7.
ADDENDUM: The following adds a KeyEventPostProcessor to the current KeyboardFocusManager and appears to do what I want:
import java.awt.Component;
import java.awt.KeyEventPostProcessor;
import java.awt.KeyboardFocusManager;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
public final class TestApp {
public static void main(final String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
try {
final JFileChooser chooser = new JFileChooser();
chooser.addActionListener(new ActionListener() {
public void actionPerformed(final ActionEvent e) {
System.out.println(e.paramString());
System.exit(0);
}
});
final KeyboardFocusManager kfm = KeyboardFocusManager
.getCurrentKeyboardFocusManager();
kfm.addKeyEventPostProcessor(new KeyEventPostProcessor() {
#Override
public boolean postProcessKeyEvent(final KeyEvent e) {
if (e.getID() == KeyEvent.KEY_RELEASED
&& e.getKeyCode() == KeyEvent.VK_ENTER) {
final Component comp = e.getComponent();
if (chooser.isAncestorOf(comp)) {
if (!(comp instanceof JButton)) {
chooser.approveSelection();
}
else {
final JButton button = (JButton) comp;
if ("Cancel".equals(button.getText())) {
chooser.cancelSelection();
}
else {
chooser.approveSelection();
}
}
}
}
return false;
}
});
final JFrame frame = new JFrame();
frame.add(chooser);
frame.pack();
frame.setVisible(true);
}
catch (final Throwable t) {
t.printStackTrace();
}
}
});
}
}
It seems like a lot of work, however, just to be able to distinguish between hitting the enter key on the "Cancel" button versus anywhere else.
Do you see any problems with it?
DISCOVERED SOLUTION: Setting the GUI Look and Feel to the native one for my system (Linux) does what I want without the need for anything else. This is what I was ignorant of and what I was looking for. The solution is to have the following
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
as the first executable statement of the main() method. One can then dispense with all focus listeners, key event processors, etc.
I've awarded the 100 points to the most helpful respondent.
The program doesn't terminate on my platform.
I see normal operation on Mac OS X 10.5, Ubuntu 10 and Windows 7 using (variously) Java 5 and 6. I replaced your exit() with println() to see the event:
System.out.println(rootDirChooser.getSelectedFile().getName() + e.paramString());
It may help to specify your platform and version; if possible, verify correct installation as well.
I'm not sure I understand your goal; but, as an alternative, consider overriding approveSelection():
private static class MyChooser extends JFileChooser {
#Override
public void approveSelection() {
super.approveSelection();
System.out.println(this.getSelectedFile().getName());
}
}
Addendum:
The goal is to have the action of hitting the "Enter" key while on the "Cancel" button be identical to clicking on the "Cancel" button.
As discussed in Key Bindings, you can change the action associated with VK_ENTER.
KeyStroke enter = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0);
InputMap map = chooser.getInputMap(JFileChooser.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
map.put(enter, "cancelSelection");
If you want the change to occur only while the "Cancel" button has focus, you'll need to do it in a Focus Listener.
Addendum:
I found a solution that uses KeyboadFocusManager, instead. What do you think?
I can see pros & cons each way, so I've outlined both below. Using KeyboadFocusManager finds all buttons, but offers no locale independent way to distinguish among them; the Focus Listener approach can only see the approve button, and it's UI specific. Still, you might combine the approaches for better results. A second opinion wouldn't be out of order.
Addendum:
I've updated the code below to eliminate the need to know the localized name of the "Cancel" button and use key bindings.
import java.awt.EventQueue;
import java.awt.KeyboardFocusManager;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.KeyStroke;
import javax.swing.plaf.metal.MetalFileChooserUI;
public final class FileChooserKeys
implements ActionListener, FocusListener, PropertyChangeListener {
private final JFileChooser chooser = new JFileChooser();
private final MyChooserUI myUI = new MyChooserUI(chooser);
private final KeyStroke enterKey =
KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0);
private void create() {
final JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
chooser.addActionListener(this);
myUI.installUI(chooser);
myUI.getApproveButton(chooser).addFocusListener(this);
KeyboardFocusManager focusManager =
KeyboardFocusManager.getCurrentKeyboardFocusManager();
focusManager.addPropertyChangeListener(this);
frame.add(chooser);
frame.pack();
frame.setVisible(true);
}
#Override
public void actionPerformed(ActionEvent e) {
System.out.println(e.paramString());
}
#Override
public void focusGained(FocusEvent e) {
System.out.println("ApproveButton gained focus.");
}
#Override
public void focusLost(FocusEvent e) {
System.out.println("ApproveButton lost focus.");
}
#Override
public void propertyChange(PropertyChangeEvent e) {
Object o = e.getNewValue();
InputMap map = chooser.getInputMap(
JFileChooser.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
if (o instanceof JButton) {
if ("focusOwner".equals(e.getPropertyName())) {
JButton b = (JButton) o;
String s = b.getText();
boolean inApproved = b == myUI.getApproveButton(chooser);
if (!(s == null || "".equals(s) || inApproved)) {
map.put(enterKey, "cancelSelection");
} else {
map.put(enterKey, "approveSelection");
}
}
}
}
private static class MyChooserUI extends MetalFileChooserUI {
public MyChooserUI(JFileChooser b) {
super(b);
}
#Override
protected JButton getApproveButton(JFileChooser fc) {
return super.getApproveButton(fc);
}
}
public static void main(final String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new FileChooserKeys().create();
}
});
}
}