import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

// Process key events at the window level alone, while having other objects
// that might steal focus (buttons) have setFocusable(false) called on them.
//
// If no command line arguments are given, focus is not lost. If a command
// line argument is given, focus is kept by the window and no key events
// are missed.
public class KeyEventsWindowHandler extends JFrame {
   private static final int DIST = 10;
   private static final int WIDTH = 3;
   private static final int PANEL_WIDTH = 300;
   private KeyboardPanel keyboardPanel = new KeyboardPanel();

  /* Initialize UI */
  public KeyEventsWindowHandler( boolean disableFocus ) {
    // Set up a panel of buttons.
    JPanel newPanel = new JPanel(new GridLayout(WIDTH,WIDTH));

    for (int i = 0; i < WIDTH; i++ ) {
        for (int j = 0; j < WIDTH; j++ ) {
            int buttonNumber = (i * WIDTH) + j;
            JButton nextButton = new JButton( new Integer(buttonNumber).toString() );

            // Next command is critical; otherwise, the buttons can "steal focus"
            // (where key events are sent) from the outer window, and key events won't be processed.
            nextButton.setFocusable( disableFocus );

            nextButton.addActionListener( new ActionListener() {
                public void actionPerformed(ActionEvent a) {
                    System.out.println("Button pressed: " + 
                        a.getActionCommand());
                }
            });
            newPanel.add( nextButton );
        }
    }

    // Add panels and pack() them into the window.
    add(keyboardPanel, BorderLayout.CENTER);
    add(newPanel, BorderLayout.NORTH);
    setSize( PANEL_WIDTH, PANEL_WIDTH);

    setTitle("KeyEventWindowHandler");
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    addKeyListener(new KeyAdapter() {
        public void keyPressed(KeyEvent e) {
          switch (e.getKeyCode()) {
            case KeyEvent.VK_DOWN: keyboardPanel.moveY(DIST); break;
            case KeyEvent.VK_UP: keyboardPanel.moveY(-DIST); break;
            case KeyEvent.VK_LEFT: keyboardPanel.moveX(-DIST); break;
            case KeyEvent.VK_RIGHT: keyboardPanel.moveX(DIST); break;
            default: keyboardPanel.setChar(e.getKeyChar());
          }
        }});
  }

  public static void main(String[] args) {
    KeyEventsWindowHandler frame = null;
    if ( args.length > 0 )
        frame = new KeyEventsWindowHandler( true );
    else
        frame = new KeyEventsWindowHandler( false );
    frame.setVisible(true);
  }
}

// Panel that draws the character.
class KeyboardPanel extends JPanel {
    private int x = 100;
    private int y = 100;
    private char keyChar = 'A'; // Default key

    // Key events will be processed correctly without this,
    // including for consistency/to demonstrate concept.
    public KeyboardPanel() {
        setFocusable(false);
    }

    public void moveY(int change) { y += change; repaint(); }
    public void moveX(int change) { x += change; repaint();  }
    public void setChar(char value) { keyChar = value; repaint(); }

    /** Draw the character */
    protected void paintComponent(Graphics g) {
      super.paintComponent(g);

      g.setFont(new Font("TimesRoman", Font.PLAIN, 24));
      g.drawString(String.valueOf(keyChar), x, y);
    }
}

