I have a Swing application that deals with date and time, so a lot of tests are done changing the system's date and time settings.
During the tests, we noticed that after decreasing the clock, the first click is ignored by the application.
Is it a bug of Swing/Java/Windows? Is there a workaround to this?
Interestingly, this issue only happens when decreasing the date/time settings. If I increase it, the application behaves normally.
Situation:
Swing application running.
Decrease Windows date and time settings (e.g. change time from 15:00 to 14:00).
Notice that the first click in the Swing application does not fire any action.
Code example (you can use it to testify the situation):
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JButton;
import javax.swing.JFrame;
public class Main {
public static void main(String[] args) {
final JFrame frame = new JFrame("frame");
final JButton button = new JButton("button");
button.addActionListener(new ActionListener() {
public void actionPerformed(final ActionEvent e) {
System.out.println("Button Pressed!");
}
});
frame.add(button);
frame.setSize(200, 200);
frame.setVisible(true);
frame.addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(final WindowEvent e) {
System.exit(0);
}
});
}
}
As seen here the Swing uses a date to check when the event occoured. So, in some way, probally a handler is acting here, by discarting your action, since it happened "before" the last action. I can't confirm you this, but probally some Layout Manager or another handler is messing with something here to prevent delayed events to mess up with the current flow.
I've debugged it via Eclipse and found out what is happening.
Clock at 15:00h.
Click at the button. Swing record last event time to 15:00.
Change the clock to 14:00h.
Click at the button. Swing ignores the event because it looks like a multi-click.
The problem here is that the comparison made by Swing checking for multi-click is this:
if (lastTime != -1 && currentTime - lastTime < multiClickThreshhold) {
shouldDiscardRelease = true;
Here, currentTime - lastTime yields a negative value. It's less than 0 (my multiClickThreshhold), so it does not fire the action event:
public void mouseReleased(MouseEvent e) {
if (SwingUtilities.isLeftMouseButton(e)) {
// Support for multiClickThreshhold
if (shouldDiscardRelease) {
shouldDiscardRelease = false;
return;
}
AbstractButton b = (AbstractButton) e.getSource();
ButtonModel model = b.getModel();
model.setPressed(false);
model.setArmed(false);
}
}
All the source listed above is in javax.swing.plaf.basic.BasicButtonListener.
The Button class does have a setMultiClickThreshhold, but it throws IllegalArgumentException in case the threshhold is less than 0.
So, as a workaround, I did this:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.lang.reflect.Field;
import javax.swing.AbstractButton;
import javax.swing.JButton;
import javax.swing.JFrame;
public class Main {
public static void main(String[] args) throws Exception {
final JFrame frame = new JFrame("frame");
final JButton button = new JButton("button");
removeMulticlickThreshold(button);
button.addActionListener(new ActionListener() {
public void actionPerformed(final ActionEvent e) {
System.out.println("Button Pressed!");
}
});
frame.add(button);
frame.setSize(200, 200);
frame.setVisible(true);
frame.addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(final WindowEvent e) {
System.exit(0);
}
});
}
private static void removeMulticlickThreshold(final JButton button) throws Exception {
final Field multiClickThreshhold = AbstractButton.class.getDeclaredField("multiClickThreshhold");
multiClickThreshhold.setAccessible(true);
multiClickThreshhold.set(button, Long.MIN_VALUE);
}
}
Related
I am using a scripting API for making a script for a bot for a game I play, however whenever I override a bot manager, a JOptionPane appears that blocks execution until I close it, however I would like to run this script without human intervention, so I would like to bypass this pane.
I have tried to overwrite the JOptionPane class at runtime to no avail, and I have tried contacting the developer of the bot's API to add an override however he doesn't want to add an override.
I am open to any methods of resolving this (reflection, etc.), I was thinking of detecting the pane's title and force-closing it however that it way beyond my skill level.
Pane title: Override Warning!
I would add the pane text however it reveals personal information.
Here is the possibility to suppress all JOptionPane dialogs. If you want to do it with some of them you probably need to provide additional check for dialog title.
import java.awt.AWTEvent;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.FlowLayout;
import java.awt.Toolkit;
import java.awt.event.AWTEventListener;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowEvent;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
public class CloseDialog implements Runnable {
private final AWTEventListener hideOptionPaneListener = new AWTEventListener() {
#Override
public void eventDispatched(AWTEvent event) {
if (event instanceof WindowEvent) {
processWindowEvent((WindowEvent) event);
}
}
};
private void processWindowEvent(WindowEvent we) {
if (we.getID() == WindowEvent.WINDOW_OPENED && we.getWindow() instanceof JDialog) {
final JDialog dlg = (JDialog) we.getWindow();
Component[] comps = dlg.getContentPane().getComponents();
if (comps.length > 0 && comps[0] instanceof JOptionPane) { // probably also check dialog title
dlg.setVisible(false);
}
}
}
#Override
public void run() {
final JFrame frm = new JFrame("Test frame");
JButton button = new JButton("Show option pane");
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(frm, "Simple message");
}
});
final JCheckBox checkBox = new JCheckBox("Suppress option pane");
checkBox.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (checkBox.isSelected()) {
Toolkit.getDefaultToolkit().addAWTEventListener(hideOptionPaneListener, AWTEvent.WINDOW_EVENT_MASK);
} else {
Toolkit.getDefaultToolkit().removeAWTEventListener(hideOptionPaneListener);
}
}
});
JPanel top = new JPanel(new FlowLayout());
top.add(button);
frm.add(top, BorderLayout.NORTH);
frm.add(checkBox, BorderLayout.SOUTH);
frm.setSize(400, 200);
frm.setLocationRelativeTo(null);
frm.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frm.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new CloseDialog());
}
}
Here is my code:
else if (roomNumber == 3) {
txtAreaDisplay.append("Oh no, it looks like you have run into a monster!\n"
+ "The monster runs straight at you, looking for a fight.\n"
+ "Are you going to fight, or run?\n");
//fight
if (btnFight.getModel().isPressed()) {
txtAreaDisplay.append("You have chosen to fight the monster!\n");
}
//run
else if (btnRun.getModel().isPressed()) {
txtAreaDisplay.append("You have chosen to attempt to run away from the monster!\n");
}
}
My question is, how do I go about registering the button click in the if statement (if that makes sense)? I have the line: (btnFight.getModel().isPressed()) but that does not seem to work because when I press the button, the next line is not outputted.
Any help is greatly appreciated!
I believe the problem you have here is more fundimental. The button press is an event, as such it is asychronous to your prompt. By the time the player has read your prompting message and started to respond the if then else has already been processed and neither event has happened...so you get no message.
the place you want to put the txtAreaDisplay appends is in the button press event code. That way the prompt happens and the player makes their choice. the action happens when the player moves... Now the user should only have a short time to respond before the monster makes the choice for them.
You can do it the following way.
Example with Anonymous ActionListener.
import java.awt.BorderLayout;
import java.awt.Container;
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.JPanel;
import javax.swing.JTextArea;
public class ButtonE extends JFrame {
JButton btnFight = new JButton("Figth");
JButton btnRun = new JButton("Run");
JTextArea txtAreaDisplay = new JTextArea();
JPanel pnl = new JPanel(new FlowLayout());
public ButtonE() {
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setSize(400, 400);
this.setLayout(new BorderLayout());
this.setVisible(true);
pnl.add(btnFight);
pnl.add(btnRun);
Container cntnr = this.getContentPane();
cntnr.add(txtAreaDisplay, BorderLayout.CENTER);
cntnr.add(pnl, BorderLayout.SOUTH);
btnFight.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae) {
txtAreaDisplay.append("Oh no, it looks like you have run into a monster!\n"
+ "The monster runs straight at you, looking for a fight.\n"
+ "Are you going to fight, or run?\n");
txtAreaDisplay.append("You have chosen to fight the monster!\n");
}
});
btnRun.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae) {
txtAreaDisplay.append("Oh no, it looks like you have run into a monster!\n"
+ "The monster runs straight at you, looking for a fight.\n"
+ "Are you going to fight, or run?\n");
txtAreaDisplay.append("You have chosen to attempt to run away from the monster!\n");
}
});
}
public static void main(String[] args) {
new ButtonE();
}
}
Or You can do it the Following way by using an Interface.
import java.awt.BorderLayout;
import java.awt.Container;
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.JPanel;
import javax.swing.JTextArea;
public class ButtonE extends JFrame implements ActionListener{
JButton btnFight = new JButton("Figth");
JButton btnRun = new JButton("Run");
JTextArea txtAreaDisplay = new JTextArea();
JPanel pnl = new JPanel(new FlowLayout());
public ButtonE() {
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setSize(400, 400);
this.setLayout(new BorderLayout());
this.setVisible(true);
pnl.add(btnFight);
pnl.add(btnRun);
Container cntnr = this.getContentPane();
cntnr.add(txtAreaDisplay, BorderLayout.CENTER);
cntnr.add(pnl, BorderLayout.SOUTH);
btnFight.addActionListener(this);
btnRun.addActionListener(this);
}
public static void main(String[] args) {
new ButtonE();
}
#Override
public void actionPerformed(ActionEvent ae) {
txtAreaDisplay.append("Oh no, it looks like you have run into a monster!\n"
+ "The monster runs straight at you, looking for a fight.\n"
+ "Are you going to fight, or run?\n");
//fight
if (ae.getSource() == btnFight) {
txtAreaDisplay.append("You have chosen to fight the monster!\n");
} //run
else if (ae.getSource() == btnRun) {
txtAreaDisplay.append("You have chosen to attempt to run away from the monster!\n");
}
}
}
You should use a JToggleButton and then use an ActionListener to help perform the action you want to run. This should help and will perform whatever has been clicked it is being used as a different version of an if-statement.
tglFight.addActionListener(new ActionListener() {
#Override public void actionPerformed(ActionEvent e) {
tglFight.setBackground(Color.GREEN);
txtAreaDisplay.append("You have chosen to fight the monster!\n");
}
});
tglRun.addActionListener(new ActionListener() {
#Override public void actionPerformed(ActionEvent e) {
tglRun.setBackground(Color.RED);
txtAreaDisplay.append("You have chosen to attempt to run away from the monster!\n");;
}
});
My application has a JFrame and checks every x seconds if something changed. So I would like to hide my JFrame via setVisible(false) on a click on the close button and redisplay it when the icon in the dock (I'm using Mac OS, but it should work the same way with the Windows task bar) is clicked. You know: many applications do this temporary hiding.
Have you got any ideas how to do this? How to listen on these click events?
Here is a little sample, how to hide/open window in the tray.
import java.awt.Image;
import java.awt.SystemTray;
import java.awt.Toolkit;
import java.awt.TrayIcon;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
public class Test {
public static void main(String[] args) throws Exception {
final JFrame frm = new JFrame("Test");
Image im = Toolkit.getDefaultToolkit().getImage("c:\\icons\\icon1.png");
final TrayIcon tri = new TrayIcon(im);
tri.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
frm.setVisible(true);
try {
SystemTray.getSystemTray().remove(tri);
} catch (Exception ex) {
ex.printStackTrace();
}
}
});
frm.addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(WindowEvent e) {
try {
SystemTray.getSystemTray().add(tri);
} catch (Exception ex) {
ex.printStackTrace();
}
frm.setVisible(false);
}
});
frm.setSize(100, 100);
frm.setVisible(true);
}
}
Use the com.apple.eawt or java.awt.Desktop packages to listen to Events that occur when the application is closed, hidden or reactivated.
Particularly com.apple.eawt.AppReOpenedEvent is cast when the Dock Icon is clicked. When you handle the event with com.apple.eawt.AppReOpenedListener, set the frame visible again:
#Override
public void appReOpened(AppReOpenedEvent arg0) {
invalidate(); // Suppose these are optional, but make sure the layout is up to date
pack();
validate();
setVisible(true);
}
I want to set setDissmissDelay() method multiple times, but I could not set it for a specific value again and again. I also tried to use an infinite loop, and tried to override (ToolTipManager Constructor is on default modifier).
I know for sure the code is working properly as I can see it's printing the e.getsource() in console. I was trying to solve this issue asked by someone (This Question) and while solving that I became stumped at this point. What is the reason behind that? And if I can set the value how can it be? Is there any other way to achieve this?
Here is my code snippet:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.*;
public class Hello {
static JButton button;
private static void createAndShowGUI() {
JFrame frame = new JFrame("HelloWorldSwing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
button = new JButton("Hello World");
button.setToolTipText("Its a tool tip Experiment!");
frame.getContentPane().add(button);
button.addMouseListener(new MouseAdapter() {
#Override
public void mouseEntered(MouseEvent e) {
if (e.getSource() == button) {
ActionListener tt = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
ToolTipManager.sharedInstance().setDismissDelay(
1000);
System.out.println(e.getSource());
}
};
new Timer(100, tt).start();
}
}
});
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
Please guide me through this.
Calling setDismissDelay() is a global setting to indicate how long should a Tooltip remain on the display before it is removed. It does not reset the time until when the current tooltip is removed. As suggested in your linked question setting the dismiss delay, once and for all, to Integer.MAX_VALUE should do the trick.
I have a game that uses a JFrame that displays the game info. The window updates whenever a player sends a move object to the server. It works perfectly fine for any number of move objects. However once the 3nd turn starts it hits a wall and here is what happens:
The Jframe completely stops responding to left and right mouse clicks (it makes a windows ding sound when you try to click)
The JFrame still responds to mouse scrolls and keyboard inputs
The JFrame vanishes from the alt-tab program list.
NO error message or stack trace.
Using souts it appears that the code reaches all points of necessary code properly
I can't even click the "X" Window button or right-click close on the task bar
The 3rd turn object is structurally identical to previous turn objects
what on earth can cause a program to do this??
The event dispatch thread has thrown an exception. It is automatically restarted, but your program remains in the state you describe. See also How can I catch Event Dispatch Thread (EDT) exceptions and this answer.
Addendum: How uncaught exceptions are handled and Uncaught exceptions in GUI applications may be helpful. Also check for empty exception handlers.
Addendum: Here's an example.
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;
/** #see https://stackoverflow.com/a/9935287/230513 */
public class Fail extends JPanel {
private static final JLabel label = new JLabel(
"12345678901234567890", JLabel.CENTER);
public Fail() {
this.setLayout(new GridLayout(0, 1));
this.add(label);
this.add(new JButton(new AbstractAction("Kill me, now!") {
#Override
public void actionPerformed(ActionEvent e) {
JButton b = (JButton) e.getSource();
b.setText(String.valueOf(1 / 0));
}
}));
new Timer(100, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
label.setText(String.valueOf(System.nanoTime()));
}
}).start();
}
private void display() {
JFrame f = new JFrame("Example");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(this);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new Fail().display();
}
});
}
}
Check if your frame class do not overrides isEnabled() method.
I spent couple of hours searching for exception but the responce was pretty trivial: I have implemented interface with such method.