import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

// Have object manipulated by key events be the only listener for key events,
// and then pass this object as the KeyListener for all components (e.g. buttons, panels)
public class SingleObjectKeyEvents 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( DIST );

  public SingleObjectKeyEvents() {
    // 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() );
            nextButton.addActionListener( new ActionListener() {
                public void actionPerformed( ActionEvent a ) {
                    System.out.println( a.getActionCommand() );
                }});

            // We must add the key listener to the buttons as well, if we
            // want to handle key events when buttons have focus.
            nextButton.addKeyListener(keyboardPanel);
            newPanel.add( nextButton );
        }
    }

    // Add the keyboard panel as the listener for key events.
    addKeyListener( keyboardPanel );

    // 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);
  }

  public static void main(String[] args) {
    SingleObjectKeyEvents frame = new SingleObjectKeyEvents();
    frame.setVisible(true);
  }
}

// Panel that draws the character AND listens for key events that
// update the panel appearance.
class KeyboardPanel extends JPanel implements KeyListener {
    private int x = 100;
    private int y = 100;
    private int offset;
    private char keyChar = 'A'; // Default key

    public KeyboardPanel( int distance ) {
        offset = distance;

        // Defining the KeyListener interface is not enough; we must add ourselves to the
        // 'mailing list' (registered objects) for events from this panel.
        addKeyListener(this);
    }

    // Modify panel properties ('mutators')
    public void moveY(int change) { y += change; repaint(); }
    public void moveX(int change) { x += change; repaint();  }
    public void setChar(char value) { keyChar = value; repaint(); }

    // KeyListener interface methods
    public void keyReleased(KeyEvent e) {};
    public void keyTyped(KeyEvent e) {};
    public void keyPressed(KeyEvent e) {
          switch (e.getKeyCode()) {
            case KeyEvent.VK_DOWN: moveY( offset ); break;
            case KeyEvent.VK_UP: moveY( -offset ); break;
            case KeyEvent.VK_LEFT: moveX( -offset ); break;
            case KeyEvent.VK_RIGHT: moveX( offset ); break;
            default: setChar(e.getKeyChar());
          }
    }

    /** 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);
    }
}

