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)
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?
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.
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.