Menu shortcut keys in another JFrame
Greetings,
This is the scenario: my application uses one JFrame as the 'main'
application frame. It carries all the frillies: a toolbar, a main desktop
area and a menu bar with quite a lot of menu items, most having a
shortcut key.
This all works fine. One of those menu items toggles the visibility of
a second JFrame which doesn't have much functionality, i.e. it just
display two JTextAreas (which aren't editable) that display the redirected
System.out and System.err respectively.
When this second JFrame is the focus owner, those shortcut keys in
the first main application window aren't processed. I attempted to solve
it like this (this is code in the second JFrame initialization method):JRootPane trp= this.getRootPane();
JRootPane prp= ApplicationFrame.getFrame().getRootPane();
trp.getInputMap().setParent(prp.getInputMap());
trp.getActionMap().setParent(prp.getActionMap());
The idea was that by setting the maps of the application JFrame as the
parents of the maps in the second JFrame would solve this little problem.
Not so, it doesn't work. Adding a KeyListener to the second JFrame
doesn't give me much either because I can't figure out how to
'redispatch' the KeyEvent to the first main application JFrame.
Then I got lost in the KeyboardFocusManager documentation for which
I suspect that focus traversal would not be the way to go. I realize that
my little problem isn't unique and that it must've been solved before.
Does anyone want to show me how to solve this? Also, can some kind
soul explain to me why my idea (see above) doesn't work? Obviously
I don't understand keyboard event processing much ...
kind regards,
Jos
[1828 byte] By [
JosAH] at [2007-11-26 12:04:21]

# 1
Have you triedsecondFrame.setFocusableWindowState(false);to prevent the second frame from gaining keyboard focus?Cheers, Jukka
# 2
Or you could transfer the key presses to the main frame
// Define this is KeyListener for second frame
public void keyTyped(KeyEvent ke)
{
keyListenerForMainFrame.keyTyped(ke);
}
//...
Cheers
# 3
> Have you tried> secondFrame.setFocusableWindowState(false);
> to prevent the second frame from gaining keyboard focus?
I thought about that and it will work for this particular output stream
frame. But what should be done when other future frames *do* want to
gain the focus?
I'm trying to find a way to redirect all non-understood keys from a current
frame to the main application frame.
thank you very much for your reply, much appreciated.
kind regards,
Jos
JosAH at 2007-7-7 12:30:37 >

# 4
> Or you could transfer the key presses to the main frame
> // Define this is KeyListener for second frame
> public void keyTyped(KeyEvent ke)
> {
>keyListenerForMainFrame.keyTyped(ke);
>
> //...
> Cheers
But what should that other KeyListener (the one in the main app frame)
do with the event then? It doesn't know where to re-dispatch it to. The
trouble is I got stuck in the documentation again, i.e. suppose I find the
JMenuBar in the main frame. This menu bar is a MenuContainer which
can re-dispatch this event for me but that bloody method is deprecated
and I can't find what the docs are mentioning: dispatchEvent(AWTEvent).
Thank you very much for your reply, I do appreciate it.
kind regards,
Jos
JosAH at 2007-7-7 12:30:37 >

# 5
Try this for ideas
package testing;
import javax.swing.*;
import java.awt.event.*;
public class Test
{
public static void main(String[] args)
{
JFrame frame1 = new JFrame("Main");
JFrame frame2 = new JFrame("Second");
frame1.setLocation(400, 400);
frame2.setLocation(450, 450);
frame1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame2.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JButton button = new JButton("alt + 0");
button.setMnemonic(KeyEvent.VK_0);
final JLabel label = new JLabel("0");
button.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent ae)
{
label.setText(String.valueOf(Integer.parseInt(label.getText()) + 1));
}
});
frame2.addKeyListener(new KeyAdapter()
{
public void keyTyped(KeyEvent ke)
{
if (ke.getKeyChar() == KeyEvent.VK_0)
button.doClick();
}
});
frame1.add(button);
frame2.add(label);
frame1.pack();
frame2.pack();
frame1.setVisible(true);
frame2.setVisible(true);
}
}
I used a button to make it simple but I guess the setMnemonic(...) method works similarly for menuitems?
Jukka
# 6
I'm sorry, that's not what I intend to do: in your scenario the second frame
knows somehow what's supposed to happen when a key is pressed.
I'm trying to figure out how the second frame can redispatch a KeyEvent
to the main frame for any key unknown to any component in the second
frame, i.e. an unconsumed KeyEvent.
I do appreciate your thinking about my nasty little problem though ;-)
kind regards,
Jos
ps. I suspect the answer can be found somewhere in the KeyboardFocusManager,
not sure though ...
JosAH at 2007-7-7 12:30:37 >

# 7
******. =) Well heres another revised version for you to consider. Now frame2 does not have clue as to what the keypresses in frame1 do...
package testing;
import javax.swing.*;
import java.awt.event.*;
public class Test
{
public static void main(String[] args)
{
final JFrame frame1 = new JFrame("Main");
final JFrame frame2 = new JFrame("Second");
frame1.setLocation(400, 400);
frame2.setLocation(450, 450);
frame1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame2.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JButton button = new JButton("alt + 0");
final JLabel label = new JLabel("0");
button.setMnemonic(KeyEvent.VK_0);
button.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent ae)
{
label.setText(String.valueOf(Integer.parseInt(label.getText()) + 1));
}
});
frame1.addKeyListener(new KeyAdapter()
{
public void keyTyped(KeyEvent ke)
{
if (ke.getKeyChar() == KeyEvent.VK_0)
button.doClick();
}
});
frame2.addKeyListener(new KeyAdapter()
{
public void keyTyped(KeyEvent ke)
{
frame1.getKeyListeners()[0].keyTyped(ke);
}
});
frame1.add(button);
frame2.add(label);
frame1.pack();
frame2.pack();
frame1.setVisible(true);
frame2.setVisible(true);
}
}
Jukka
# 8
> ******. =)
My email notification didn't show those stars ... ;-)
> Well heres another revised version for you to consider. Now frame2
> does not have clue as to what the keypresses in frame1 do...
That is not what I want either, sorry; your scenario needs the main
JFrame/window to know what every key press is all about. Consider
this from a user perspective: there's a secondary window somewhere,
either visible or not. A simple key press should toggle its visibility.
Clicking the title bar should bring that secondary window up front.
I just can't do that and I hate it because this little nuisance ruined my
entire Sunday afternoon.
Thank you for you reply though; it makes me realize that such a simple
thing (from a user's perspective) isn't that trivial at all ..
kind regards,
Jos
JosAH at 2007-7-7 12:30:37 >

# 9
maybe(?) something like this might do
click the menu items (or F2/F3) to show the 2nd/3rd frame, then F1/F2/F3
again, to trigger the main frame action.
I have been using KeyboardFocusManager... for something like this, but
courtesy of camickr, this seems to be simpler (although I haven't used it
enough to be sure of no hidden gotchas)
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
class Testing
{
JFrame f1;
JFrame f2;
JFrame f3;
public void buildGUI()
{
JMenu menu = new JMenu("File");
JMenuItem frame1 = new JMenuItem("Frame1 (F1)");
JMenuItem frame2 = new JMenuItem("Frame2 (F2)");
JMenuItem frame3 = new JMenuItem("Frame3 (F3)");
frame1.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent ae){
frame_1_Action();
}
});
frame2.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent ae){
frame_2_Action();
}
});
frame3.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent ae){
frame_3_Action();
}
});
menu.add(frame1);
menu.add(frame2);
menu.add(frame3);
JMenuBar menuBar = new JMenuBar();
menuBar.add(menu);
f1 = new JFrame();
f1.setJMenuBar(menuBar);
f1.setSize(100,100);
f1.setLocationRelativeTo(null);
f1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f1.setVisible(true);
Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener()
{
public void eventDispatched(AWTEvent e)
{
if(e.getID() == KeyEvent.KEY_PRESSED)
{
if(((KeyEvent)e).getKeyCode() == KeyEvent.VK_F1)
{
frame_1_Action();
}
else if(((KeyEvent)e).getKeyCode() == KeyEvent.VK_F2)
{
frame_2_Action();
}
else if(((KeyEvent)e).getKeyCode() == KeyEvent.VK_F3)
{
frame_3_Action();
}
}
}
}, AWTEvent.KEY_EVENT_MASK);
}
public void frame_1_Action()
{
if(f2 != null) f2.dispose();
if(f3 != null) f3.dispose();
}
public void frame_2_Action()
{
if(f2 == null)
{
f2 = new JFrame("F2");
f2.getContentPane().add(new JLabel("F2"));
f2.setSize(100,100);
f2.setLocation(600,200);
f2.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
}
f2.setVisible(true);
}
public void frame_3_Action()
{
if(f3 == null)
{
f3 = new JFrame("F3");
f3.getContentPane().add(new JTextField("F3"));
f3.setSize(100,100);
f3.setLocation(400,500);
f3.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
}
f3.setVisible(true);
}
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable(){
public void run(){
new Testing().buildGUI();
}
});
}
}
# 10
> Consider this from a user perspective:
From the user perspective there should only ever be one active window at a time. Any commands you invoke should be handled by that window.
> there's a secondary window somewhere, either visible or not.
Maybe thats the problem, the secondary window, once created should always be visible when the primary frame is activated. That is, your secondary window should be a dialog with the frame as the owner. Then the secondary window is automatically made visible when the primary window is activated
> A simple key press should toggle its visibility.
If the secondary window has focus then the KeyStroke should deactivate the secondary window and activate the primary window.
If the primary window has focus then the KeyStroke should show and activate the secondary window.
So the same KeStroke would be defined in both windows, but it would have a different meaning depending on which window was active at the time.
Here is a simply example of the primary frame and secondary dialog working together.
import javax.swing.*;
public class FrameDialogTest extends JFrame
{
public static void main(String[] args)
{
JFrame frame = new FrameDialogTest();
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
frame.setSize(200, 200);
frame.setVisible(true);
JDialog dialog = new JDialog(frame, false);
dialog.setDefaultCloseOperation( JDialog.DISPOSE_ON_CLOSE );
dialog.setSize(200, 200);
dialog.setLocationRelativeTo( null );
dialog.setVisible(true);
}
}
# 11
> > Consider this from a user perspective:
>
> From the user perspective there should only ever be one active
> window at a time. Any commands you invoke should be handled by
> that window.
> > there's a secondary window somewhere, either visible or not.
>
> Maybe thats the problem, the secondary window, once created
> should always be visible when the primary frame is activated. That is,
> your secondary window should be a dialog with the frame as the
> owner. Then the secondary window is automatically made visible
> when the primary window is activated
The only reason I made that second window a JFrame instead of a non-
modal dialog is that I wanted it to have its own icon in the top left corner.
I simply want that (output displaying) window to be there or not, depending
on a toggle key, but acually that second window never needs to be the
focus owner. If I make that second window non focusable I can never
put it on top of the first window when its visibility is toggled to 'on'. If I
could do that, my problems are over.
> > A simple key press should toggle its visibility.
>
> If the secondary window has focus then the KeyStroke should
> deactivate the secondary window and activate the primary window.
>
> If the primary window has focus then the KeyStroke should show and
> activate the secondary window.
>
> So the same KeStroke would be defined in both windows, but it
> would have a different meaning depending on which window was
> active at the time.
Yes, that was my previous solution: add a little key listener to that second
window that would also toggle the visibility of the second window. It didn't
'feel right', i.e. the second window would listen to that single key and it
made the user (me) wanting to be able to use the other keys defined
in the first window too.
[ example snipped: functionally equivalent to my previous solution ]
I'm afraid I have to live with that gesture 'paradigm' ... Thank you very
much for your reply; I guess I'm going to revert to that previous solution
(see above) which you also described.
kind regards,
Jos
JosAH at 2007-7-7 12:30:37 >

