Trapping <Ctrl-F4> to prevent accidental loss of data

Writing an MDI program using a DesktopFrame and multiple JInternalFrames in the recommended way. I've created ActionListeners to trap File, Close and File, Exit and the Close button on the internal frames, but the user can still lose all their work by pressing <Ctrl-F4>! How can I bring <Ctrl-F4> under the control of my exit routine?

Incidentally <Ctrl-F4> crashes the GUI if used on the very last doc left open, here's a stack trace ...

Exception occurred during event dispatching:

java.lang.ArrayIndexOutOfBoundsException: 0 >= 0

at java.util.Vector.elementAt(Unknown Source)

at javax.swing.plaf.basic.BasicDesktopPaneUI$NavigateAction.actionPerformed(Unknown Source)

at javax.swing.plaf.basic.BasicDesktopPaneUI$CloseAction.actionPerformed(Unknown Source)

at javax.swing.SwingUtilities.notifyAction(Unknown Source)

at javax.swing.JComponent.processKeyBinding(Unknown Source)

at javax.swing.JComponent.processKeyBindings(Unknown Source)

at javax.swing.JComponent.processKeyEvent(Unknown Source)

at javax.swing.JTextArea.processKeyEvent(Unknown Source)

at java.awt.Component.processEvent(Unknown Source)

at java.awt.Container.processEvent(Unknown Source)

at java.awt.Component.dispatchEventImpl(Unknown Source)

at java.awt.Container.dispatchEventImpl(Unknown Source)

at java.awt.Component.dispatchEvent(Unknown Source)

at java.awt.LightweightDispatcher.processKeyEvent(Unknown Source)

at java.awt.LightweightDispatcher.dispatchEvent(Unknown Source)

at java.awt.Container.dispatchEventImpl(Unknown Source)

at java.awt.Window.dispatchEventImpl(Unknown Source)

at java.awt.Component.dispatchEvent(Unknown Source)

at java.awt.EventQueue.dispatchEvent(Unknown Source)

at java.awt.EventDispatchThread.pumpOneEventForHierarchy(Unknown Source)

at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)

at java.awt.EventDispatchThread.pumpEvents(Unknown Source)

at java.awt.EventDispatchThread.run(Unknown Source)

[2113 byte] By [CharlesHarrison] at [2007-9-26 1:14:23]
# 1
Btw: Same problem with <Alt-F4>! Quits program without calling close routines ... Rrrrr!
CharlesHarrison at 2007-6-29 0:31:33 > top of Java-index,Archived Forums,Java Programming...
# 2

I've now sorted <Alt-F4>, it actually does come under the control of setDefaultCloseOperation() and my exit routine on a JFrame, I just hadn't prog'ed it quite right.

However, there does seem to be a genuine problem with <Ctrl-F4>, it completely ignores setDefaultCloseOperation() on JInternalFrames and closes the internal frame regardless. On closing the last doc it crashes the GUI as described.

I tried inserting a line to consume the IntenalFrameEvent as it passes through my exit routine, but consume() is protected access so it won't compile.

Pls, has anyone any ideas?

CharlesHarrison at 2007-6-29 0:31:33 > top of Java-index,Archived Forums,Java Programming...
# 3

Hi,

You'll have to implement InternalFrameListener and catch the closing event in internalFrameClosing(InternalFrameEvent e) or internalFrameClosed(InternalFrameEvent e)

For a complete working example that catches the ctrl+F4 event, take a look at http://java.sun.com/docs/books/tutorial/uiswing/events/internalframelistener.html

Hope this helps,

Kurt.

leukbr at 2007-6-29 0:31:33 > top of Java-index,Archived Forums,Java Programming...
# 4
Kurt,Thanks, but I've done this already. the <Ctrl-F4> does my IFClosing routine, but no matter what closes the internal frame anyway when this returns.Regards Charles.
CharlesHarrison at 2007-6-29 0:31:33 > top of Java-index,Archived Forums,Java Programming...
# 5

Kurt,

In fact ...

>> For a complete working example that catches the ctrl+F4 event, take a look at

>> http://java.sun.com/docs/books/tutorial/uiswing/events/internalframelistener.html

... take this demo and change the parameter in line 58 to true so that the 'Event Watcher' internal frame will now be closable, recompile, and launch.

Press the button to create an internal frame to watch, then close the 'Event Watcher' internal frame.

Press <Ctrl-F4> on the remaining internal frame just created ... same crash as above.

A bug, I think?

Regards Charles.

CharlesHarrison at 2007-6-29 0:31:33 > top of Java-index,Archived Forums,Java Programming...
# 6

Hi,

I've been able to reproduce the bug on jdk 1.3. I've also tested it in jdk 1.4 and it seems to be fixed. Then I've looked in the bug database and found the following:

http://developer.java.sun.com/developer/bugParade/bugs/4322726.html

Too bad that no workaround was found.

I suppose using jdk 1.4 (in beta until Q4) is not an option for you?

Regards,

Kurt.

leukbr at 2007-6-29 0:31:33 > top of Java-index,Archived Forums,Java Programming...
# 7

Yes, I did a bug report and was told it was fixed in 1.4beta, so I've tried downloading that. However, ...

1The i.f. is still hidden by <Ctrl-F4> and can't be made visible again.

2<Alt-F4> is now ignored, so you can't use it to request closure of the app window.

I'm giving up on this, but thanks for your help

CharlesHarrison at 2007-6-29 0:31:33 > top of Java-index,Archived Forums,Java Programming...
# 8

The following solution consumes the keyevent for the ctrl/f4 keystroke only. Because this event is consumed no exception is created. The internalFrameClosing() interface method of my InternalFrameListener is then called so I can chose whether to close the JIF or not. If the keystroke is not ctrl/f4 then it is important to allow the JDesktopPane to handle the other keyboard bindings for the JIFs!

Use this code in your own JInternalFrame class to override processKeyBinding():

protected boolean processKeyBinding(KeyStroke ks, KeyEvent ke, int condition, boolean pressed) {

if (ks == KeyStroke.getKeyStroke(KeyEvent.VK_F4,java.awt.Event.CTRL_MASK)) {

ke.consume();

yourObjectWithListener.internalFrameClosing(new InternalFrameEvent (this,InternalFrameEvent.INTERNAL_FRAME_CLOSING));

return true;

}

else {

InputMap map = (super.getDesktopPane()).getInputMap(condition);

ActionMap am = (super.getDesktopPane()).getActionMap();

if (map !=null && am !=null && (super.getDesktopPane()).isEnabled()) {

Object binding = map.get(ks);

Action action = (binding == null) ? null : am.get(binding);

if (action != null) {

return SwingUtilities.notifyAction(action,ks,ke,this,ke.getModifiers());

}

}

return false;

}

}

This works fine using 1.3.0_02.

Sam.

samwoodcock at 2007-6-29 0:31:33 > top of Java-index,Archived Forums,Java Programming...