I have created a class that extends from WindowAdapter so that every time I want to close a window, it asks you if you really want to close the window. The problem comes when I click "No". How can I handle it so that the window event doesn't "remain" there and the frame keeps trying to dispatch it?
I only do a return, and I can't come up with anything. Here's the code:
public class ExitController extends WindowAdapter{
#Override
public void windowClosing(WindowEvent windowEvent) {
if(JOptionPane.showConfirmDialog(null,"Are you sure to close this window?",
"Really Closing?", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE)
== JOptionPane.YES_OPTION){
System.exit(0);
} else {
return;
}
}
}
Check out Closing an Application.
It gives some basic code for this. The basic code would set the default close operation of the frame to DO_NOTHING_ON_CLOSE.
Then in the WindowListener when the user confirms the close it will reset the default close operation to EXIT_ON_CLOSE instead of using System.exit(0);
You can also use the CloseListener class which is a more complex version (because it provides more functionality) of youe ExitController class.
The problem is in JFrame.processWindowEvent:
protected void processWindowEvent(WindowEvent e) {
super.processWindowEvent(e); // --> this will call your listener
if (e.getID() == WindowEvent.WINDOW_CLOSING) {
switch(defaultCloseOperation) {
case HIDE_ON_CLOSE:
setVisible(false);
break;
case DISPOSE_ON_CLOSE:
dispose();
break;
case DO_NOTHING_ON_CLOSE:
default:
break;
case EXIT_ON_CLOSE:
// This needs to match the checkExit call in
// setDefaultCloseOperation
System.exit(0);
break;
}
}
}
Independently of what your listener does, the JFrame evaluates its defaultCloseOperation and closes or hides itself.
Therefore you need also to initialize the right default close operation of the frame on which you install your listener to prevent the default operation:
frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
frame.addWindowListener(new ExitController ());
You could provide a method in ExitListenerto facilitate this:
public class ExitController extends WindowAdapter {
public void installOn(JFrame frame) {
frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
frame.addWindowListener(this);
}
Related
I'm programming a JToggleButton to load to/discard from memory the configuration of an element (a telescope config), so I've added a JComboBox in a JFrame and near it the button to load the selected item. When the JToggleButton is selected, an hard disk icon is displayed, another icon if otherwise. I'm using the IntelliJ IDEA GUI editor for that. Of course, I've added an ItemListener (as suggested from the web) to that button:
loadTelescopeButton.setSelected(true);
System.out.println(loadTelescopeButton.isSelected());
loadTelescopeButton.addItemListener(new ItemListener() {
#Override
public void itemStateChanged(ItemEvent e) {
System.out.println("LAODACTION " + loadTelescopeButton.isSelected());
try {
if (e.getStateChange() == ItemEvent.SELECTED) {
String selected = telescopesList.getSelectedItem().toString();
if ((selected != null) && (!selected.equals("")) && (ObjUtils.isAlphaNumeric(selected))) {
//...
} else {
showErrorMessage("Invalid id selected!");
}
} else if (e.getStateChange() == ItemEvent.DESELECTED) {
if ((configurationActivity != null) && (configurationActivity.getManager() != null) &&
(configurationActivity.getTelescope() != null) && (configurationActivity.getTelescope().isConnected())) {
//...
} else {
//...
}
}
} catch (Exception e1) {
e1.printStackTrace();
}
}
});
Output:
true
-> When the window is displayed
LAOD_ACTION false
-> When I click the button
I've made some tests with some new toggle buttons and they gave me same error: the code inside itemStateChanged(ItemEvent e) {...} is repeated forever, without stopping! In that piece of code there are no for and while loops! The result is a great number of message dialogs (only one dialog should be displayed), and if I focus another window in my desktop the screen behind the dialogs becomes black (the area of the parent window). I changed the listener to ActionListener and now everything is executed one time/click.
Why this error? I've copied that code from https://stackoverflow.com/a/7524627/6267019, as you can see.
Full code on GitHub Here, I've highlighted the code for that toggle button. The same error happens with other JToggleButtons in my MainActivity.java file, and also when debugging IntelliJ lets me see that the code in the listener is repeated forever. After some thousand of dialogs Windows shows me a message and closes Java Platform Binary with an error.
EDIT:
The same problem in a new class:
import javax.swing.*;
import java.awt.*;
public class ErrorGUI extends JFrame {
public ErrorGUI() throws HeadlessException {
super("ciao");
JPanel panel1 = new JPanel();
setContentPane(panel1);
JToggleButton ciaoToggleButton = new JToggleButton("cajs");
ciaoToggleButton.setSelected(true);
ciaoToggleButton.addItemListener(e -> {
System.out.println("caiooasfsdvn");
try {
JOptionPane.showMessageDialog(panel1, "skjngksfnb");
} catch (Exception e2) {
e2.printStackTrace();
}
});
panel1.add(ciaoToggleButton);
pack();
setVisible(true);
}
public static void main(String[] args) {
new ErrorGUI();
}
}
Whenever you open a modal dialog, the opening method call will return only after the dialog has been closed. This is crucial for the dialogs that return an entered value or choice.
This implies that while the dialog is open, a new event handling loop has to be started to react on the input in the dialog.
So when you open a modal dialog from a listener, you are stopping the handling of the current event and start processing of subsequent events, which can disturb the handling of the current event significantly. Most notably, the button will suddenly loose the focus when the new dialog is opened.
The nested event handling can be easily demonstrated by changing the listener to
ciaoToggleButton.addItemListener(e -> {
System.out.println("entering");
JOptionPane.showMessageDialog(panel1,
e.getStateChange()==ItemEvent.SELECTED? "selected": "deselected");
System.out.println("leaving");
});
which will print sequences of
entering
entering
leaving
leaving
showing how the contradicting event is generated while the processing of the old one hasn’t completed.
As said by others, you can fix this by opening the dialog after the completion of the even handling, like
ciaoToggleButton.addItemListener(e -> {
System.out.println("entering");
EventQueue.invokeLater(() -> JOptionPane.showMessageDialog(panel1,
e.getStateChange()==ItemEvent.SELECTED? "selected": "deselected"));
System.out.println("leaving");
});
or you enforce a non-modal dialog:
ciaoToggleButton.addItemListener(e -> {
System.out.println("entering");
JDialog d = new JOptionPane(
e.getStateChange()==ItemEvent.SELECTED? "selected": "deselected",
JOptionPane.INFORMATION_MESSAGE)
.createDialog(panel1, UIManager.getString("OptionPane.messageDialogTitle"));
d.setModal(false);
d.setVisible(true);
System.out.println("leaving");
});
(in a real application you would either keep the dialog for later reuse or call dispose after use)
Unfortunately, the danger of opening modal dialogs (or doing anything else that creates a secondary event loop) hasn’t been emphasized enough in the documentation. You can read everywhere that accessing Swing components from other threads can create inconsistencies, but starting new event handling loop while there are incompletely processed events can have a similar impact.
I can't say that I understand why your code is misbehaving, but I agree that what you're seeing doesn't quite make sense, and is likely due to the JOptionPane call somehow affecting the JToggleButton's state change. One way to get around this is by wrapping the JOptionPane call in a Runnable and queuing it on the Swing event queue via SwingUtilities.invokeLater(...). For example:
import javax.swing.*;
import java.awt.*;
#SuppressWarnings("serial")
public class ErrorGUI extends JFrame {
public ErrorGUI() throws HeadlessException {
super("ciao");
JPanel panel1 = new JPanel();
setContentPane(panel1);
JToggleButton ciaoToggleButton = new JToggleButton("cajs");
ciaoToggleButton.setSelected(true);
ciaoToggleButton.addItemListener(e -> {
System.out.println("caiooasfsdvn");
SwingUtilities.invokeLater(() -> {
JOptionPane.showMessageDialog(panel1, "skjngksfnb");
});
// JOptionPane.showMessageDialog(panel1, "skjngksfnb");
});
panel1.add(ciaoToggleButton);
pack();
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLocationRelativeTo(null);
setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
new ErrorGUI();
});
}
}
An interesting variation:
ciaoToggleButton.setSelected(true);
System.out.println("0:" + ciaoToggleButton.isSelected());
ciaoToggleButton.addItemListener(e -> {
System.out.println("1: " + ciaoToggleButton.isSelected());
if (e.getStateChange() == ItemEvent.SELECTED) {
JOptionPane.showMessageDialog(panel1, "skjngksfnb");
}
System.out.println("2: " + ciaoToggleButton.isSelected());
});
prints out:
0:true
1: false
2: false
1: true
1: false
2: false
2: false
1: true
1: false
2: false
2: false
I've already this project, but I'm having more problems. The dialog for SetMusicDialog opens, but it won't close when I try to exit out. I have a System.exit, but I'm not sure why the window won't close.
import java.awt.*;
import java.io.*;
public class SetMusicDialog extends Dialog
{
public static String sng;
public SetMusicDialog()
{
super ((Dialog)null, "Set Music");
Panel mpanel;
Font l = new Font("Helvetica", Font.ITALIC, 12);
setFont(l);//sets font
setBackground(Color.cyan);
Panel f = new Panel();
f.add("West", new Button("Death Grips"));
f.add("East", new Button("Siren"));
add("South",f);
pack(); // make it just fit
resize(preferredSize());
move(200,200);
}
public boolean handleEvent1 (Event evt)
{
switch (evt.id)
{
case Event.WINDOW_DESTROY:
System.exit(0);
dispose();
return true;
case Event.ACTION_EVENT:
if("Death Grips".equals(evt.arg))
{
sng= "breakmirrors.wav";
}
else if("Siren".equals(evt.arg))
{
sng= "bip.wav";
}
dispose();
}
return false;
}
}
You can add this:
addWindowListener( new WindowAdapter() {
public void windowClosing(WindowEvent e){
dispose();
System.exit(0);
}
});
windowClosed won't detect if the user tries to close the window. It will only run if the window has been closed. So use windowClosing.
Also, by using WindowAdapter you do not need to write all the methods of WindowListener.
I added this code in your constructor, and it works properly.
If you are using AWT, you should create a WindowListener as MadProgrammer stated. Basically, a WindowListener is a class that has methods that are run when certain window-related actions occur. To write code that will run when a Dialog (which extends Window) is closed:
//d is a dialog
d.addWindowListener(new WindowListener() {
//You'll need to implement all the abstract methods. leave them empty.
#Override
public void windowClosed(WindowEvent e) {
//Your code
}
});
Basically, you're anonymously implementing the abstract class WindowEvent. Make sure you implement all the other methods too, or you will get compiler errors. Your IDE should automatically implement all the methods.
I have a very basic question.
I have the following WIndowClosing() event in java. I want to keep the application open if the user clicks "Cancel". How do I do this?
The application keeps closing when "Cancel" is selected.
This is what I have:
public void windowClosing(WindowEvent evt)
{
SAVE_MODE event = m_model.saveChanges();
if(event == SAVE_MODE.YES)
{
saveChanges();
System.exit(0);
}
else if(event == Common.SAVE_MODE.NO)
{
System.exit(0);
}
else
{
//Keep the application open -- how do I say that in code?
}
}
Note the following:
The application still closes when the ELSE clause is empty. I don't think that should be happening.
This should have taken me two minutes to solve. Any help would be appreciated. Thanks.
(if you want to credit me with the correct answer)
make sure you're calling [yourJFrame].setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
I think probably a different approach is all that's needed. Set your default close operation for the main frame (during your initial setup) to:
frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
Then add your windowClosing code to exit on OK, and do nothing on Cancel.
frame.addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent evt) {
SAVE_MODE event = m_model.saveChanges();
switch (event)
{
case YES:
saveChanges();
System.exit(0);
break;
case NO:
System.exit(0);
break;
default:
break;
}
}
});
I've searched similar threads but I can't seem the find the solution that fits. (I'm new to java)
I have made an texteditor and i need to make the option to save before exit. A possible window that pops up and asks the user if they want to save or not...
The main frame has set this:
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
And this is my inner class that handles the event that is generated when the user selects Exit from the file menu.
private class ExitListener implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
System.exit(0);
}
}
Thanks!
So in your frame class you should override the method processWindowEvent.
#Override
protected void processWindowEvent(WindowEvent e) {
if(e.getID() == WindowEvent.WINDOW_CLOSING){
System.out.println("Windows is closing");
}
super.processWindowEvent(e);
}
What is the proper way to terminate a Swing application from the code, and what are the pitfalls?
I'd tried to close my application automatically after a timer fires. But just calling dispose() on the JFrame didn't do the trick - the window vanished but the application did not terminate. However when closing the window with the close button, the application does terminate. What should I do?
Your JFrame default close action can be set to "DISPOSE_ON_CLOSE" instead of EXIT_ON_CLOSE (why people keep using EXIT_ON_CLOSE is beyond me).
If you have any undisposed windows or non-daemon threads, your application will not terminate. This should be considered a error (and solving it with System.exit is a very bad idea).
The most common culprits are java.util.Timer and a custom Thread you've created. Both should be set to daemon or must be explicitly killed.
If you want to check for all active frames, you can use Frame.getFrames(). If all Windows/Frames are disposed of, then use a debugger to check for any non-daemon threads that are still running.
I guess a EXIT_ON_CLOSE
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
before System.exit(0) is better since you can write a Window Listener to make some cleaning operations before actually leaving the app.
That window listener allows you to defined:
public void windowClosing(WindowEvent e) {
displayMessage("WindowListener method called: windowClosing.");
//A pause so user can see the message before
//the window actually closes.
ActionListener task = new ActionListener() {
boolean alreadyDisposed = false;
public void actionPerformed(ActionEvent e) {
if (frame.isDisplayable()) {
alreadyDisposed = true;
frame.dispose();
}
}
};
Timer timer = new Timer(500, task); //fire every half second
timer.setInitialDelay(2000); //first delay 2 seconds
timer.setRepeats(false);
timer.start();
}
public void windowClosed(WindowEvent e) {
//This will only be seen on standard output.
displayMessage("WindowListener method called: windowClosed.");
}
Try:
System.exit(0);
Crude, but effective.
May be the safe way is something like:
private JButton btnExit;
...
btnExit = new JButton("Quit");
btnExit.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e){
Container frame = btnExit.getParent();
do
frame = frame.getParent();
while (!(frame instanceof JFrame));
((JFrame) frame).dispose();
}
});
The following program includes code that will terminate a program lacking extraneous threads without explicitly calling System.exit(). In order to apply this example to applications using threads/listeners/timers/etc, one need only insert cleanup code requesting (and, if applicable, awaiting) their termination before the WindowEvent is manually initiated within actionPerformed().
For those who wish to copy/paste code capable of running exactly as shown, a slightly-ugly but otherwise irrelevant main method is included at the end.
public class CloseExample extends JFrame implements ActionListener {
private JButton turnOffButton;
private void addStuff() {
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
turnOffButton = new JButton("Exit");
turnOffButton.addActionListener(this);
this.add(turnOffButton);
}
public void actionPerformed(ActionEvent quitEvent) {
/* Iterate through and close all timers, threads, etc here */
this.processWindowEvent(
new WindowEvent(
this, WindowEvent.WINDOW_CLOSING));
}
public CloseExample() {
super("Close Me!");
addStuff();
}
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
CloseExample cTW = new CloseExample();
cTW.setSize(200, 100);
cTW.setLocation(300,300);
cTW.setVisible(true);
}
});
}
}
If I understand you correctly you want to close the application even if the user did not click on the close button. You will need to register WindowEvents maybe with addWindowListener() or enableEvents() whichever suits your needs better.
You can then invoke the event with a call to processWindowEvent(). Here is a sample code that will create a JFrame, wait 5 seconds and close the JFrame without user interaction.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class ClosingFrame extends JFrame implements WindowListener{
public ClosingFrame(){
super("A Frame");
setSize(400, 400);
//in case the user closes the window
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
//enables Window Events on this Component
this.addWindowListener(this);
//start a timer
Thread t = new Timer();
t.start();
}
public void windowOpened(WindowEvent e){}
public void windowClosing(WindowEvent e){}
//the event that we are interested in
public void windowClosed(WindowEvent e){
System.exit(0);
}
public void windowIconified(WindowEvent e){}
public void windowDeiconified(WindowEvent e){}
public void windowActivated(WindowEvent e){}
public void windowDeactivated(WindowEvent e){}
//a simple timer
class Timer extends Thread{
int time = 10;
public void run(){
while(time-- > 0){
System.out.println("Still Waiting:" + time);
try{
sleep(500);
}catch(InterruptedException e){}
}
System.out.println("About to close");
//close the frame
ClosingFrame.this.processWindowEvent(
new WindowEvent(
ClosingFrame.this, WindowEvent.WINDOW_CLOSED));
}
}
//instantiate the Frame
public static void main(String args[]){
new ClosingFrame();
}
}
As you can see, the processWindowEvent() method causes the WindowClosed event to be fired where you have an oportunity to do some clean up code if you require before closing the application.
Take a look at the Oracle Documentation.
Starting from JDK 1.4 an Application terminates if:
There are no displayable AWT or Swing components.
There are no native events in the native event queue.
There are no AWT events in java EventQueues.
Cornercases:
The document states that some packages create displayable components without releasing them.A program which calls Toolkit.getDefaultToolkit() won't terminate. is among others given as an example.
Also other Processes can keep AWT alive when they, for what ever reason, are sending events into the native event queue.
Also I noticed that on some Systems it takes a coupple of seconds before the Application actually terminates.
I think, the idea is here the WindowListener - you can add any code there that you'd like to run before the thing shuts down
In response to other comments, DISPOSE_ON_CLOSE does not seem to properly exit the application - it only destroys the window, but the application will continue running. If you want to terminate the application use EXIT_ON_CLOSE.