Customizing JComboBox: "infinite loops event" when LAF is system LAF - java

I customize my JComboBox as follow.
The program ran ok with default LAF, but whenever i changed the LAF to System LAF (another LAF, Nimbus, is ok), there was an infinite loop after the button was clicked. I saw that the actionPerformed method was called infinitely.
Please help me solving this problem. I use jdk 1.6.0_33
I'm so sorry if there is any unclear mean. My English is not good
Thanks in advance.
package sig.dw.ui;
import java.awt.Component;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.ComboBoxEditor;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
//import javax.swing.event.EventListenerList;
/**
*
* #author congnh
*/
public class ButtonableComboBox extends JComboBox{
private ButtonableComboBoxEditor comboBoxEditor;
public ButtonableComboBox(){
super();
comboBoxEditor = new ButtonableComboBoxEditor();
// eventListenerList = new EventListenerList();
setEditable(true);
setEditor(comboBoxEditor);
}
public ButtonableComboBox(Object[] items){
this();
setModel(new DefaultComboBoxModel(items));
}
public void addButtonListener(ActionListener listener){
comboBoxEditor.addActionListener(listener);
}
public void removeButtonListener(ActionListener listener){
comboBoxEditor.removeActionListener(listener);
}
class ButtonableComboBoxEditor implements ComboBoxEditor{
private JButton button;
public ButtonableComboBoxEditor(){
button = new JButton();
}
#Override
public Component getEditorComponent() {
return button;
}
#Override
public void setItem(Object anObject) {
if(anObject!=null){
button.setText(anObject.toString());
}
}
#Override
public Object getItem() {
return button.getText();
}
#Override
public void selectAll() {
throw new UnsupportedOperationException("Not supported yet.");
}
#Override
public void addActionListener(ActionListener l) {
System.out.println("add new listener");
button.addActionListener(l);
}
#Override
public void removeActionListener(ActionListener l) {
button.removeActionListener(l);
}
}
public static void main(String args[]){
javax.swing.SwingUtilities.invokeLater(new Runnable(){
#Override
public void run(){
String[] comboBoxItems = {"Browse","Explorer","Firefox","IE","Chrome","Opera"};
JFrame frame = new JFrame();
JPanel panel = new JPanel();
panel.setLayout(new FlowLayout());
ButtonableComboBox bcb = new ButtonableComboBox(comboBoxItems);
bcb.addButtonListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println(e.getActionCommand());
}
});
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
panel.add(bcb);
frame.add(panel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}

For reference, I see StackOverflowError for MotifLookAndFeel on Mac OS X / Java 1.6.0_37, but not MetalLookAndFeel, NimbusLookAndFeel, or AquaLookAndFeel. Using the example below and the L&F selector seen here, I get the follwing stack trace as the UI delegate recursively invokes doClick(). I don't see an obvious workaround.
Addendum: I see a similar StackOverflowError for MotifLookAndFeel on Ubuntu 12 / Java 1.6.0_24, but not MetalLookAndFeel, NimbusLookAndFeel, or GTKLookAndFeel.
Exception in thread "AWT-EventQueue-0" java.lang.StackOverflowError
at sun.font.FontManager.getFont2D(Native Method)
at sun.java2d.SunGraphics2D.checkFontInfo(SunGraphics2D.java:780)
at sun.java2d.SunGraphics2D.getFontInfo(SunGraphics2D.java:941)
at sun.java2d.pipe.GlyphListPipe.drawString(GlyphListPipe.java:32)
at sun.java2d.SunGraphics2D.drawString(SunGraphics2D.java:3054)
at sun.swing.SwingUtilities2.drawString(SwingUtilities2.java:517)
at sun.swing.SwingUtilities2.drawStringUnderlineCharAt(SwingUtilities2.java:538)
at javax.swing.plaf.basic.BasicButtonUI.paintText(BasicButtonUI.java:294)
at javax.swing.plaf.basic.BasicButtonUI.paintText(BasicButtonUI.java:319)
at javax.swing.plaf.basic.BasicButtonUI.paint(BasicButtonUI.java:207)
at com.sun.java.swing.plaf.motif.MotifButtonUI.paint(MotifButtonUI.java:91)
at javax.swing.plaf.ComponentUI.update(ComponentUI.java:153)
at javax.swing.JComponent.paintComponent(JComponent.java:760)
at javax.swing.JComponent.paint(JComponent.java:1037)
at javax.swing.JComponent.paintChildren(JComponent.java:870)
at javax.swing.JComponent.paint(JComponent.java:1046)
at javax.swing.JComponent.paintChildren(JComponent.java:870)
at javax.swing.JComponent.paint(JComponent.java:1046)
at javax.swing.JComponent._paintImmediately(JComponent.java:5106)
at javax.swing.JComponent.paintImmediately(JComponent.java:4890)
at javax.swing.JComponent.paintImmediately(JComponent.java:4902)
at javax.swing.AbstractButton.doClick(AbstractButton.java:352)
at javax.swing.plaf.basic.BasicRootPaneUI$Actions.actionPerformed(BasicRootPaneUI.java:191)
at javax.swing.plaf.basic.BasicComboBoxUI$Actions.actionPerformed(BasicComboBoxUI.java:1575)
at javax.swing.plaf.basic.BasicComboBoxUI$Handler.actionPerformed(BasicComboBoxUI.java:1904)
at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:2028)
at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2351)
at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:387)
at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:242)
at javax.swing.AbstractButton.doClick(AbstractButton.java:389)
...
at javax.swing.plaf.basic.BasicRootPaneUI$Actions.actionPerformed(BasicRootPaneUI.java:191)
at javax.swing.plaf.basic.BasicComboBoxUI$Actions.actionPerformed(BasicComboBoxUI.java:1575)
at javax.swing.plaf.basic.BasicComboBoxUI$Handler.actionPerformed(BasicComboBoxUI.java:1904)
at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:2028)
at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2351)
at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:387)
at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:242)
at javax.swing.AbstractButton.doClick(AbstractButton.java:389)
at javax.swing.plaf.basic.BasicRootPaneUI$Actions.actionPerformed(BasicRootPaneUI.java:191)
at javax.swing.plaf.basic.BasicComboBoxUI$Actions.actionPerformed(BasicComboBoxUI.java:1575)
SSCCE:
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.ComboBoxEditor;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JToolBar;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
public class ButtonableComboBox extends JComboBox {
private ButtonableComboBoxEditor comboBoxEditor;
public ButtonableComboBox() {
comboBoxEditor = new ButtonableComboBoxEditor();
setEditor(comboBoxEditor);
setEditable(true);
}
public ButtonableComboBox(Object[] items) {
this();
setModel(new DefaultComboBoxModel(items));
}
public void addButtonListener(ActionListener listener) {
comboBoxEditor.addActionListener(listener);
}
public void removeButtonListener(ActionListener listener) {
comboBoxEditor.removeActionListener(listener);
}
class ButtonableComboBoxEditor implements ComboBoxEditor {
private JButton button = new JButton();
#Override
public Component getEditorComponent() {
return button;
}
#Override
public void setItem(Object anObject) {
if (anObject != null) {
button.setText(anObject.toString());
}
}
#Override
public Object getItem() {
return button.getText();
}
#Override
public void selectAll() {
System.out.println("select all");
button.requestFocus();
}
#Override
public void addActionListener(ActionListener l) {
System.out.println("add listener");
button.addActionListener(l);
}
#Override
public void removeActionListener(ActionListener l) {
System.out.println("remove listener");
button.removeActionListener(l);
}
}
public static void main(String args[]) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
String[] comboBoxItems = {
"Browse", "Explorer", "Firefox", "IE", "Chrome", "Opera"};
JFrame frame = new JFrame();
JPanel panel = new JPanel();
ButtonableComboBox bcb = new ButtonableComboBox(comboBoxItems);
bcb.addButtonListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println(e.getActionCommand());
}
});
panel.add(bcb);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// https://stackoverflow.com/a/11949899/230513
frame.add(createToolBar(frame), BorderLayout.NORTH);
frame.add(panel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}

Related

Can I call method from KeyListener

I have a simple JFrame class with KeyListener and some method.
public class MyClass extends JFrame{
MyClass(){
//build window
addKeyListener(new KeyAdapter() {
#Override
public void keyReleased(KeyEvent e) {
if(e.getKeyCode() == 32){
myMethod();
}
}
});
}
private void myMethod(){
//do something
}
}
MyMethod works correctly if I call it from main(). But from Listener it does nothing. Can I call methods from KeyListener at all? And if answer is no, how can I solve this problem?
It depends how do you expect to use your JFrame. You need to create window with it by instantiating it from the main() method:
public class Main {
public static void main(String[] args) {
// this is your frame instance
MyClass frame = new MyClass();
}
}
and you must implement all of the methods that KeyListener provides:
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JFrame;
public class MyClass extends JFrame {
public MyClass() {
// "this." can be omitted, it is just for better understanding
// that each method applies to instance of JFrame
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setBounds(50, 50, 300, 300);
this.setVisible(true);
// add key listener
this.addKeyListener(new KeyListener() {
#Override
public void keyPressed(KeyEvent e) {
}
#Override
public void keyReleased(KeyEvent e) {
// call this method on key release
myMethod(e.getKeyCode());
}
#Override
public void keyTyped(KeyEvent e) {
}
});
}
private void myMethod(int key) {
this.setTitle("Key released: " + key);
}
}
KeyListener is a pain in the ... code. It relies on the component it is registered to be focusable AND have keyboard focus.
In general, a better solution is to use the key bindings API. It gives you finer control over when the binding should be triggered.
For example
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
public class MyClass extends JFrame {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
MyClass myClass = new MyClass();
myClass.pack();
myClass.setLocationRelativeTo(null);
myClass.setVisible(true);
}
});
}
public MyClass() {
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// This is here because I don't like setSize or setPreferredSize
JPanel panel = new JPanel() {
#Override
public Dimension getPreferredSize() {
return new Dimension(300, 300);
}
};
setContentPane(panel);
InputMap im = panel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
ActionMap am = panel.getActionMap();
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0), "spaced");
am.put("spaced", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
myMethod();
}
});
}
private void myMethod() {
System.out.println("Pressed");
}
}

Java : Timer get paused by PopupMenu

Hi,
I'm working on a small program in Java that have 2 things :
An always-on-top JWindow with a refresh Timer (there can be more than one JWindow)
A TrayIcon with a PopupMenu on left click
The PopupMenu is opened with a MouseEventListener and with a dummy Frame as parent (needed).
Runnable example :
import java.awt.AWTException;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Frame;
import java.awt.Image;
import java.awt.MenuItem;
import java.awt.PopupMenu;
import java.awt.SystemTray;
import java.awt.TrayIcon;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.net.URL;
import javax.swing.ImageIcon;
import javax.swing.JWindow;
import javax.swing.Timer;
public class TestWindow extends JWindow {
private int color;
public static Image createImage(String path) {
URL imageURL = TestWindow.class.getResource(path);
if (imageURL == null) {
System.err.println("Resource not found: " + path);
return null;
} else {
return (new ImageIcon(imageURL)).getImage();
}
}
public static void main(String[] args) {
addTrayIcon();
new TestWindow();
}
public static void addTrayIcon(){
if (!SystemTray.isSupported()) {
System.out.println("SystemTray is not supported");
System.exit(0);
}
TrayIcon trayIcon = new TrayIcon(createImage("/icon.png"));
trayIcon.setImageAutoSize(true);
PopupMenu popup = new PopupMenu();
popup.add(new MenuItem("test"));
popup.add(new MenuItem("test"));
popup.add(new MenuItem("test"));
Frame frame = new Frame("MiniMario");
frame.setResizable(false);
frame.setUndecorated(true);
frame.setType(Frame.Type.UTILITY);
frame.setAlwaysOnTop(true);
frame.setAutoRequestFocus(true);
frame.add(popup);
frame.setVisible(false);
frame.addFocusListener(new FocusListener(){
#Override
public void focusGained(FocusEvent e) {
frame.setVisible(false);
}
#Override
public void focusLost(FocusEvent e) {
frame.setVisible(false);
}
});
trayIcon.addMouseListener(new MouseListener(){
#Override
public void mouseClicked(MouseEvent e) {
if(e.getButton() == MouseEvent.BUTTON3){
EventQueue.invokeLater(new Runnable(){
#Override
public void run() {
frame.setVisible(true);
popup.show(frame, e.getXOnScreen(), e.getYOnScreen());
}
});
}
}
#Override
public void mousePressed(MouseEvent e) {}
#Override
public void mouseReleased(MouseEvent e) {}
#Override
public void mouseEntered(MouseEvent e) {}
#Override
public void mouseExited(MouseEvent e) {}
});
final SystemTray tray = SystemTray.getSystemTray();
try {
tray.add(trayIcon);
} catch (AWTException e) {
System.out.println("TrayIcon could not be added.");
System.exit(0);
}
}
public TestWindow(){
this.setBackground(new Color(0,0,0));
this.setLocationRelativeTo(null);
this.setSize(100, 100);
color = 0;
Timer refresh = new Timer(10,new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
color+=10;
setAlwaysOnTop(true);
getContentPane().setBackground(new Color(color%256,color%256,color%256));
}
});
this.setVisible(true);
refresh.start();
}
}
Here is the thing : when I open the PopupMenu, it freeze the JWindow. I figured out that it was the refresh timer not running anymore. I already tried the following :
Giving up on the always-on-top
Showing the PopupMenu in a runnable (new Thread(...).start / SwingUtilities.invokeLater / EventQueue.invokeLater)
Replacing the JWindow Swing Timer by a Thread with an infinite loop and Thread.sleep
Using the native TrayIcon.setPopup (it was originally the case)
Use the JWindow or the Frame setModalExclusionType (see below)
After searching for this kind of problem, few were linked to swing modality but I don't get how to use modality on this setup because neither Frame nor PopupMenu have the setModal I saw on another post. Do I have to make my own PopupMenu out of a JDialog ?
(Sorry for my bad english)
Thank you for your time
EDIT : Runnable example

Observer pattern to print value from timer in JPanel

I've created a series of classes to try and figure out Observer patterns and am having some trouble.
The two classes in the observer/observed relationship are ClockPanel, and TheTimer. TheTimer is a (swing) timer which keeps track of time from start in seconds. ClockPanel is a GUI (swing) which has a button to start the timer and a JLabel which I want to display the time.
The goal of my observer pattern: take the value being created in TheTimer and print it on my GUI.
The current problem: The timer is updating the time just fine, but I do not seem to understand how to update the value in my GUI.
I found a question similar to this one in a C# discussion, but the problem was more nuanced and way over my head.
Here are the five classes which comprise the program:
1. The GUI-ClockPanel
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class ClockPanel implements Observer {
JFrame frame;
JPanel panel;
JButton sbutton;
JLabel label;
#Override
public void update(int counter) {
String clockval = String.valueOf(counter);
label.setText(clockval);
}
public ClockPanel() {
frame = new JFrame();
frame.setSize(100, 100);
panel = new JPanel();
label = new JLabel();
TheTimer myTimer = new TheTimer();
sbutton = new JButton("start");
sbutton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
myTimer.StartTimer();
}
});
frame.setLayout(new FlowLayout());
frame.add(panel);
frame.add(sbutton);
frame.add(label);
frame.setVisible(true);
}
}
2. The Swing Timer-TheTimer
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import javax.swing.JPanel;
import javax.swing.Timer;
public class TheTimer extends JPanel implements Subject {
private ActionListener action;
private Timer Time;
private int delay = 1000;
private ArrayList<Observer> observers = new ArrayList<Observer>();
private int counter = 0;
public TheTimer() {
action = new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.out.println(counter);
counter++;
setCounter(counter);
}
};
}
public void StartTimer() {
Time = new Timer(delay, action);
Time.setInitialDelay(0);
Time.start();
}
public int getCounter() {
return counter;
}
public void setCounter(int counter) {
this.counter = counter;
notifyObservers();
}
#Override
public void registerObserver(Observer observer) {
observers.add(observer);
}
#Override
public void removeObserver(Observer observer) {
observers.remove(observer);
}
#Override
public void notifyObservers() {
for (Observer ob : observers) {
System.out.println("Notifying ClockPanel on change in counter value");
ob.update(this.counter);
}
}
}
3. The Observer-Observer
public interface Observer {
public void update(int counter);
}
4. The Observer-related methods-Subject
public interface Subject {
public void registerObserver(Observer observer);
public void removeObserver(Observer observer);
public void notifyObservers();
}
5. The Main-TestMain
import javax.swing.SwingUtilities;
public class TestMain {
public static void main(String args[]) {
ClockPanel panel = new ClockPanel();
TheTimer timer = new TheTimer();
timer.registerObserver(panel);
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new ClockPanel();
}
});
}
}
You have two TheTimer objects: one in ClockPanel, the other in TestMain#main().
You need to remove the timer from (say) main() and add:
myTimer.registerObserver(this);
to your ClockPanel constructor.

KeyListener not working on my object [duplicate]

I am trying to make a game engine. I have made the Game class but the error resides in the KeyBoard class. Here I leave some code.
Class:: Game
package transfer2pc.co.cc.game.tileengine;
import java.awt.Graphics;
import java.util.HashMap;
import javax.swing.JPanel;
import transfer2pc.co.cc.game.tileengine.input.KeyBoard;
public abstract class Game extends JPanel implements Runnable {
/**
*
*/
private static final long serialVersionUID = 640206679500196209L;
HashMap<String, ?> maps = null;
KeyBoard keyBoard = null;
public Game(){
super();
keyBoard = new KeyBoard(this);
setKeyBoard(keyBoard);
Thread th = new Thread(this);
th.start();
}
public void run(){
while(true){
repaint();
try {
Thread.sleep(30);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
#Override
public void paint(Graphics g){
}
public void addMap(){
}
public void setMap(){
}
public abstract void keyPressed(int code);
public abstract void keyReleased(int code);
public abstract void keyTyped(int code);
public void setKeyBoard(KeyBoard key){
addKeyListener(key);
}
}
Class:: KeyBoard
package transfer2pc.co.cc.game.tileengine.input;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import transfer2pc.co.cc.game.tileengine.Game;
public class KeyBoard extends KeyAdapter implements KeyListener {
Game game = null;
public KeyBoard(Game gm){
this.game = gm;
}
#Override
public void keyPressed(KeyEvent e) {
System.out.println("KeyPressed");
game.keyPressed(e.getKeyCode());
}
#Override
public void keyReleased(KeyEvent e) {
game.keyReleased(e.getKeyCode());
}
#Override
public void keyTyped(KeyEvent e) {
game.keyTyped(e.getKeyCode());
}
public static char getChar(int key){
return (char)key;
}
}
Class:: KeyTest
package transfer2pc.co.cc.game.tileengine.test;
import java.awt.Graphics;
import javax.swing.JFrame;
import transfer2pc.co.cc.game.tileengine.Game;
import transfer2pc.co.cc.game.tileengine.input.KeyBoard;
public class KeyTest extends Game {
/**
*
*/
private static final long serialVersionUID = 8557676950779023327L;
char pressed;
public KeyTest(){
super();
addKeyListener(new KeyBoard(this));
}
#Override
public void keyPressed(int code) {
pressed = KeyBoard.getChar(code);
}
#Override
public void keyReleased(int code) {
}
#Override
public void keyTyped(int code) {
}
#Override
public void paint(Graphics g){
g.drawString("You pressed: "+pressed, 20, 20);
}
public static void main(String[] args){
JFrame frame = new JFrame("KeyTest");
frame.setSize(640, 480);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.add(new KeyTest());
frame.setVisible(true);
}
}
But the error was there was no exception thrown and the input isn't being read. Could anybody say me the correct way of doing this..
Simply, your panel needs to be focusable. Add in wherever you create the panel:
panel.setFocusable(true);
panel.requestFocusInWindow();
Here's a SSCCE (I suggest asking questions with one of these in the future):
import java.awt.Dimension;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class SimpleKeyTest {
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
JPanel panel = new JPanel();
frame.getContentPane().add(panel);
panel.addKeyListener(new KeyListener() {
#Override
public void keyTyped(KeyEvent e) {}
#Override
public void keyReleased(KeyEvent e) {}
#Override
public void keyPressed(KeyEvent e) {
System.out.println("Pressed " + e.getKeyChar());
}
});
panel.setFocusable(true);
panel.requestFocusInWindow();
frame.setSize(new Dimension(300, 300));
frame.setVisible(true);
}
};
SwingUtilities.invokeLater(r);
}
}
Also, https://www.google.com/search?q=jpanel+keylistener
You can add the key listener to the JFrame, that's something I've done in the past.
It's probably not a good idea however if you have other components in the JFrame.

Fixing JPopupMenu separators (GTK LaF) and items highlighting

yesterday I've been using for the first time Swing for a quick desktop application (I'm a fan of swt indeed...).
BTW I came across a couple of problems with JPopupMenu:
1) With GTK LaF, separators are not showing due to a bug.
2) While moving the mouse over menu items, they do not highlight (seen on linux and win)
Here's a variation using MouseAdapter, as well as an sscce for future reference.
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.*;
/** #see http://stackoverflow.com/questions/7254488 */
public class JPopupMenuEx extends JPopupMenu {
private MouseAdapter mouseListener = new MouseAdapter() {
#Override
public void mouseEntered(MouseEvent e) {
((JMenuItem) e.getSource()).setArmed(true);
}
#Override
public void mouseExited(MouseEvent e) {
((JMenuItem) e.getSource()).setArmed(false);
}
};
#Override
public void addSeparator() {
add(new JSeparatorEx());
}
#Override
public JMenuItem add(JMenuItem menuItem) {
menuItem.addMouseListener(mouseListener);
return super.add(menuItem);
}
private static class JSeparatorEx extends JSeparator {
#Override
public Dimension getPreferredSize() {
Dimension d = super.getPreferredSize();
if (d.height == 0) {
d.height = 4;
}
return d;
}
}
public static void main(String args[]) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JPopupMenuEx popup = new JPopupMenuEx();
popup.add(new JCheckBoxMenuItem("Item 1"));
popup.addSeparator();
popup.add(new JMenuItem("Item 2"));
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel p = new JPanel();
p.add(new JLabel("Right click for context menu."));
p.setComponentPopupMenu(popup);
f.add(p);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
});
}
}
I decided to extend JPopupMenu class in order to fix the two issues above and now I just want to share the code, just in case someone faces the same problem.
import java.awt.Dimension;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JSeparator;
public class JPopupMenuEx
extends JPopupMenu
implements MouseListener {
/**
*
*/
private static final long serialVersionUID = -5352058505305990803L;
#Override
public void addSeparator() {
add(new JSeparatorEx());
}
#Override
public JMenuItem add(JMenuItem menuItem) {
menuItem.addMouseListener(this);
return super.add(menuItem);
}
#Override
public void mouseEntered(MouseEvent e) {
((JMenuItem)e.getSource()).setArmed(true);
}
#Override
public void mouseExited(MouseEvent e) {
((JMenuItem)e.getSource()).setArmed(false);
}
#Override
public void mouseClicked(MouseEvent e) {}
#Override
public void mousePressed(MouseEvent e) {}
#Override
public void mouseReleased(MouseEvent e) {}
public class JSeparatorEx extends JSeparator{
/**
*
*/
private static final long serialVersionUID = 3477309905456341629L;
public Dimension getPreferredSize() {
Dimension d = super.getPreferredSize();
if (d.height==0)
d.height = 4;
return d;
}
}
}
So you can use it just like using JPopupMenu, like this:
JPopupMenuEx popup = new JPopupMenuEx();
popup.add(new JCheckBoxMenuItem("Item 1"));
popup.addSeparator();
popup.add(new JMenuItem("Item 2"));

Categories