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
Related
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");
}
}
I have a desktop application in which I am showing one Frame as notification but I want to stop that thread when mouse hover to the notification frame. Then how could I can do that?
Here current code:
final Notification not = new Notification();
Notification.notification_name.setText(msg[1]);
final ScheduledExecutorService s = Executors.newSingleThreadScheduledExecutor();
s.schedule(new Runnable() {
public void run() {
not.setVisible(false); //should be invoked on the EDT
not.dispose();
}
}, 6, TimeUnit.SECONDS);
It's showing and Exit in 6 sec.
The code that I am trying.
final Notification not = new Notification();
not.addMouseListener(new MouseAdapter() {
#Override
public void mouseEntered(MouseEvent e) {
try {
super.mouseEntered(e);
System.out.println("Mouse Entered");
//s.wait();
new Thread(new Runnable() {
#Override
public void run() {
try {
Thread.sleep(10000);
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
} catch (Exception x) {
x.printStackTrace();
}
}
#Override
public void mouseExited(MouseEvent e) {
try {
super.mouseExited(e);
} catch (Exception ex) {
ex.printStackTrace();
}
}
});
final ScheduledExecutorService s = Executors.newSingleThreadScheduledExecutor();
s.schedule(new Runnable() {
public void run() {
not.setVisible(false); //should be invoked on the EDT
not.dispose();
}
}, 6, TimeUnit.SECONDS);
In this code Notification frame is showing till 6 sec but if user hover that frame then it should be Show till user mouse exit from that Frame.
Whenever you deal with anything that might affect the UI in some way, you need to be careful and ensure that everything is done within the context of the EDT.
javax.swing.Timer allows you to setup a callback at some time in the future that when triggered, will be called within the context of the EDT
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.LineBorder;
public class TimeExample {
public static void main(String[] args) {
new TimeExample();
}
public TimeExample() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new GridBagLayout());
frame.add(new TestPane());
frame.setSize(200, 200);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private Timer timer;
public TestPane() {
setBorder(new LineBorder(Color.BLACK));
timer = new Timer(6000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
setBackground(Color.RED);
}
});
timer.setRepeats(false);
MouseAdapter ma = new MouseAdapter() {
#Override
public void mouseEntered(MouseEvent e) {
if (timer.isRunning()) {
timer.stop();
setBackground(Color.BLUE);
}
}
#Override
public void mouseExited(MouseEvent e) {
if (!timer.isRunning()) {
timer.restart();
setBackground(UIManager.getColor("Panel.background"));
}
}
/**
* Testing purposes only!!
*
* #param e
*/
#Override
public void mouseClicked(MouseEvent e) {
setBackground(UIManager.getColor("Panel.background"));
timer.restart();
}
};
addMouseListener(ma);
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(100, 100);
}
}
}
Have a look at How to Use Swing Timers for more details
I have a JMenu of 16 JMenuItems, of which I want 3 items to be displayed upfront and the rest 13 items to fade in with a 500 ms delay. Is there a way to do this animation in Java?
This is not as easy as it sounds.
Basically I originally thought "I'll attach a popup listener to the popup menu that the menu items are added to"...but apparently this doesn't work so well. The menu popup is built dynamically on demand. Makes sense, but it's still a pain.
So instead, I've found that if I wait for addNotify I can simply start the animation engine.
The animation engine is a simple concept. It has a javax.swing.Timer that ticks at a regular interval. Coupled with a start time and a duration, we can calculate the progress of the animation and generate the alpha value as required.
The only thing left is then to notify all the interested parties that the animation has changed and voila...
import java.awt.AlphaComposite;
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
public class FadeMenu {
private AnimationEngine engine;
public static void main(String[] args) {
new FadeMenu();
}
public FadeMenu() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
engine = new AnimationEngine();
JMenuBar mb = new JMenuBar();
JMenu flip = new JMenu("Flip");
flip.add("Static 1");
flip.add("Static 2");
flip.add("Static 3");
flip.add(new FadeMenuItem("Fade 1"));
flip.add(new FadeMenuItem("Fade 2"));
flip.add(new FadeMenuItem("Fade 3"));
flip.add(new FadeMenuItem("Fade 4"));
mb.add(flip);
JFrame frame = new JFrame("Testing");
frame.setJMenuBar(mb);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}
public class FadeMenuItem extends JMenuItem {
public FadeMenuItem(String text) {
super(text);
engine.addTimingListener(new TimingListener() {
#Override
public void timingEvent() {
repaint();
}
});
}
#Override
protected void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g.create();
g2d.setComposite(AlphaComposite.SrcOver.derive(engine.getAlpha()));
super.paintComponent(g2d);
g2d.dispose();
}
#Override
public void removeNotify() {
Container parent = getParent();
if (parent instanceof JPopupMenu) {
JPopupMenu menu = (JPopupMenu) parent;
engine.stop();
}
super.removeNotify();
}
#Override
public void addNotify() {
super.addNotify();
Container parent = getParent();
if (parent instanceof JPopupMenu) {
JPopupMenu menu = (JPopupMenu) parent;
engine.restart();
}
}
}
public interface TimingListener {
public void timingEvent();
}
public class AnimationEngine {
private Timer fade;
private float alpha;
private long startTime;
private long duration = 1000;
private List<TimingListener> listeners;
public AnimationEngine() {
listeners = new ArrayList<>(5);
fade = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
long elapsed = System.currentTimeMillis() - startTime;
if (elapsed >= duration) {
((Timer) e.getSource()).stop();
alpha = 1f;
} else {
alpha = (float) elapsed / (float) duration;
}
fireTimingEvent();
}
});
fade.setRepeats(true);
fade.setCoalesce(true);
fade.setInitialDelay(500);
}
public void addTimingListener(TimingListener listener) {
listeners.add(listener);
}
public void removeTimingListener(TimingListener listener) {
listeners.add(listener);
}
protected void fireTimingEvent() {
for (TimingListener listener : listeners) {
listener.timingEvent();
}
}
public void restart() {
fade.stop();
alpha = 0;
fireTimingEvent();
startTime = System.currentTimeMillis();
fade.start();
}
public float getAlpha() {
return alpha;
}
public void stop() {
fade.stop();
}
}
}
While this works on Windows, I'd be concerned that it might not work on other platforms, as the means by which the menus are generated are controlled (in part) by the UI delegate. This could become very messy, very quickly
start a timer to fire an event to fade in
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);
}
});
}
}
I want to have the user press a button to kick off a background thread.
While the thread is processing, I want two things to happen:
1) A WAIT_CURSOR should be displayed.
2) The application should not respond to mouse events.
As per the setCursor documentation "This cursor image is displayed when the contains method for this component returns true for the current cursor location, and this Component is visible, displayable, and enabled. ".
I want my application to be disabled while this background thread is processing.
Any ideas how to get the functionality I want?
import java.awt.Component;
import java.awt.Cursor;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class WaitCursor extends JFrame
{
private static final long serialVersionUID = 1L;
public WaitCursor()
{
setResizable(false);
setName(getClass().getSimpleName());
setTitle("My Frame");
setSize(300, 300);
getContentPane().add(new MyButtonPanel());
}
private class MyButtonPanel extends JPanel
{
private static final long serialVersionUID = 1L;
public MyButtonPanel()
{
JButton btnStart = new JButton("Start");
btnStart.addActionListener(new BtnStartActionListener());
add(btnStart);
}
private class BtnStartActionListener implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
// Change to WAIT_CURSOR
Component root = SwingUtilities.getRoot((JButton) e.getSource());
JOptionPane.showMessageDialog(root, "Wait 10 seconds");
root.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
// TODO: Disabling the root component prevents the WAIT_CURSOR from being displayed
root.setEnabled(false);
new Thread(new TimeKiller(root)).start();
}
}
}
private class TimeKiller implements Runnable
{
Component _root;
public TimeKiller(Component root)
{
_root = root;
}
public void run()
{
try
{
Thread.sleep(10 * 1000);
}
catch (InterruptedException e)
{
// Ignore it
}
// Change back to DEFAULT CURSOR
JOptionPane.showMessageDialog(_root, "Done waiting");
_root.setCursor(Cursor.getDefaultCursor());
_root.setEnabled(true);
}
}
private static void createAndShowGUI()
{
// Create and set up the window.
WaitCursor frame = new WaitCursor();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
try
{
createAndShowGUI();
}
catch (Exception e)
{
e.printStackTrace();
System.exit(0);
}
}
});
}
}
One way to disable it is to use the glass pane to block mouse input.
For example:
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import javax.swing.*;
#SuppressWarnings("serial")
public class WaitCursor2 extends JPanel {
private static final int PREF_W = 400;
private static final int PREF_H = PREF_W;
private JComponent glassPane;
private JButton runBackgroundProcBtn;
private JTextArea textarea = new JTextArea(15, 30);
public WaitCursor2(JComponent glassPane) {
this.glassPane = glassPane;
glassPane.setFocusable(true);
glassPane.addMouseListener(new MouseAdapter() {
}); // so it will trap mouse events.
add(new JTextField(10));
add(runBackgroundProcBtn = new JButton(new AbstractAction(
"Run Background Process") {
#Override
public void actionPerformed(ActionEvent arg0) {
runBackgroundProcessAction();
}
}));
add(new JScrollPane(textarea));
}
private void runBackgroundProcessAction() {
disableSystem(true);
glassPane.setVisible(true);
new SwingWorker<Void, Void>() {
#Override
protected Void doInBackground() throws Exception {
long sleepTime = 5000;
Thread.sleep(sleepTime);
return null;
}
#Override
protected void done() {
disableSystem(false);
}
}.execute();
}
public void disableSystem(boolean disable) {
glassPane.setVisible(disable);
runBackgroundProcBtn.setEnabled(!disable);
if (disable) {
System.out.println("started");
glassPane.requestFocusInWindow(); // so can't add text to text components
glassPane.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
} else {
System.out.println("done");
glassPane.setCursor(Cursor.getDefaultCursor());
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
private static void createAndShowGui() {
JFrame frame = new JFrame("WaitCursor2");
WaitCursor2 mainPanel = new WaitCursor2((JComponent) frame.getGlassPane());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
The glass pane will trap mouse events if it set visible and given a MouseListener. It will lose t his ability if it is set invisible. Likewise it will pull the caret from text components if you make it focusable and give it focus.
added a field current_active and at method actionPerformed, do a simple check. Albeit it is not perfect but for simple app, i think this do the trick. A crude way of solving your two requirement. :-) Hope it works for you too.
import java.awt.Component;
import java.awt.Cursor;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class WaitCursor extends JFrame
{
private static boolean current_active = false;
public WaitCursor()
{
setResizable(false);
setName(getClass().getSimpleName());
setTitle("My Frame");
setSize(300, 300);
getContentPane().add(new MyButtonPanel());
}
private class MyButtonPanel extends JPanel
{
public MyButtonPanel()
{
JButton btnStart = new JButton("Start");
btnStart.addActionListener(new BtnStartActionListener());
add(btnStart);
}
private class BtnStartActionListener implements ActionListener
{
// change to wait_cursor
public void actionPerformed(ActionEvent e)
{
if (!current_active)
{
Component root = SwingUtilities.getRoot((JButton) e.getSource());
JOptionPane.showMessageDialog(root, "Wait 10 seconds");
root.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
// TODO: Disabling the root component prevents the WAIT_CURSOR from being displayed
//root.setEnabled(false);
current_active = true;
new Thread(new TimeKiller(root)).start();
}
}
}
}
private class TimeKiller implements Runnable
{
Component m_root;
public TimeKiller(Component p_root)
{
m_root = p_root;
}
#Override
public void run()
{
try
{
Thread.sleep(10 * 1000);
}
catch (InterruptedException e)
{
//Ignore it
}
// Change back to DEFAULT CURSOR
JOptionPane.showMessageDialog(m_root, "Done waiting");
m_root.setCursor(Cursor.getDefaultCursor());
current_active = false;
}
}
// create and setup the window.
public static void createAndShowGUI()
{
WaitCursor frame = new WaitCursor();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
try
{
createAndShowGUI();
}
catch (Exception e)
{
e.printStackTrace();
System.exit(0);
}
}
});
}
}