Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

`WindowListener` acting up, perpetual firing

I have an application with an abstract class that extends JDialog. The class as an abstract void onClose(), and, in the class's constructor, the following code is added:

addWindowListener(new WindowAdapter() {
    @Override
    public void windowClosed(WindowEvent e) {
        onClose();
    }
}

The event is fired when expected, but then a strange thing happens. When a concrete extension of this class has code to create a new JDialog in the onClose() method, and this JDialog's defaultCloseOperation is JDialog.DISPOSE_ON_CLOSE, the event is fired continuously, until I force quit the operation.

I have isolated the code to the following SSCCE:

// package removed
// imports removed
public class SSCCE extends JDialog {
    public static void main(String[] args) {
        SSCCE s = new SSCCE();
        s.pack();
        s.setVisible(true);
    }
    public SSCCE() {
        setLayout(new GridLayout(1, 0, 0, 0));
        JButton btn = new JButton("click me");
        btn.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent arg0) {
                dispose();
            }
        });
        addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosed(WindowEvent e) {
                System.out
                        .println("SSCCE.SSCCE().new WindowAdapter() {...}.windowClosed()");
                onClose();
            }
        });
        add(btn);
    }

    public void onClose() {
        JDialog dialog = new JDialog();
        dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
        dialog.setVisible(true);
    }

}

Upon clicking the "click me" button, the blank JDialog appears and SSCCE.SSCCE().new WindowAdapter() {...}.windowClosed() appears in the console window. When I close the blank dialog, it reappears again and the text appears again.

Another really interesting thing is that when I change the initialization line from

JDialog dialog = new JDialog();

to

JDialog dialog = new JDialog() {
        @Override
        public synchronized void addWindowListener(WindowListener l) {
            super.addWindowListener(l);
            System.out
                    .println("SSCCE.onClose().new JDialog() {...}.addWindowListener()");
        }
    };

I get the following output in the console:

When clicking the "click me" button:

SSCCE.SSCCE().new WindowAdapter() {...}.windowClosed()
SSCCE.onClose().new JDialog() {...}.addWindowListener()
SSCCE.onClose().new JDialog() {...}.addWindowListener()

At the first closing of the dialog:

SSCCE.SSCCE().new WindowAdapter() {...}.windowClosed()
SSCCE.onClose().new JDialog() {...}.addWindowListener()
SSCCE.onClose().new JDialog() {...}.addWindowListener()
SSCCE.onClose().new JDialog() {...}.addWindowListener()
SSCCE.onClose().new JDialog() {...}.addWindowListener()

At the second closing of the dialog:

SSCCE.SSCCE().new WindowAdapter() {...}.windowClosed()
SSCCE.onClose().new JDialog() {...}.addWindowListener()
SSCCE.onClose().new JDialog() {...}.addWindowListener()
SSCCE.onClose().new JDialog() {...}.addWindowListener()
SSCCE.onClose().new JDialog() {...}.addWindowListener()
SSCCE.onClose().new JDialog() {...}.addWindowListener()

Each time I close the dialog, addWindowListener(WindowListener l) is called an additional time, even though it I am not intentionally calling it.

I don't really want any WindowListeners to be registered on the dialog, but I think that simply overriding the addWindowListener(...) method and not calling super.addWindowListener(...) would be too sloppy.

I'm running Java 1.6.0_31 on Mac OS X 10.6.8, using Eclipse Indigo (with WindowBuilder, if it matters).

Does anyone have any ideas?

Thanks!

like image 421
wchargin Avatar asked Apr 19 '12 21:04

wchargin


2 Answers

As per the Java Dialog tutorial,

Every dialog is dependent on a Frame component. When that Frame is destroyed, so are its dependent Dialogs.

When you use the JDialog constructor without any arguments, it

Creates a modeless dialog without a title and without a specified Frame owner. A shared, hidden frame will be set as the owner of the dialog.

That shared hidden frame is SwingUtilities$SharedOwnerFrame, and on initialization it registers a WindowListener to all owned windows.

When you close your dialog, the SharedOwnerFrame's windowClosed method is called, which checks for all windows it owns (at this point is the original SSCCE dialog and the new one), it finds none are visible, and so it disposes itself. This has the impact of disposing all of the dialogs it owns, which posts a window close event to each. This calls windowClosed in your listener, opening a new dialog. And round we go again :-). Regarding your last comments, you get additional log lines each time because you get one per dialog the SharedOwnerFrame owns.

If you make the SSCCE dialog own the new dialog (by passing this into its constructor) then you don't end up with shared ownership and it works fine:

 public void onClose() {
     JDialog dialog = new JDialog(this);
     dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
     dialog.setVisible(true);
 }
like image 182
Ian Jones Avatar answered Nov 08 '22 05:11

Ian Jones


import java.awt.GridLayout;
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.JDialog;
import javax.swing.JOptionPane;

public class SSCCE extends JDialog {
    public static void main(String[] args) {
        SSCCE s = new SSCCE();
        s.pack();
        s.setVisible(true);
    }

    private WindowAdapter wa = new WindowAdapter() {
        @Override
        public void windowClosed(WindowEvent e) {
            System.out
                    .println("SSCCE.SSCCE().new WindowAdapter() {...}.windowClosed()");
            onClose();
        } 
    };

    public SSCCE() {
        setLayout(new GridLayout(1, 0, 0, 0));
        JButton btn = new JButton("click me");
        btn.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent arg0) {
                dispose();
            }
        });
        addWindowListener(
        wa);
        add(btn);
    }

    public void onClose() {
        removeWindowListener(wa);
        JDialog dialog = new JDialog();
        dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
        dialog.setVisible(true);

    }

}
like image 25
ControlAltDel Avatar answered Nov 08 '22 05:11

ControlAltDel