This document provides a review of the existing key binding facilities in Swing and defines a new set of APIs that unify what we have today and satisfy the requirements listed in the next section.
This is a list of all of the requirements we've tried to satisfy, roughly in priority order.
      There are two mechanisms for creating keyboard bindings in Swing 1.1: 
      the JComponent registerKeyboardAction methods and the 
       Keymap support in the text classes. Both approaches use  
      KeyStroke and Action objects to characterize a 
      binding. On the whole they're more the same than they are different. 
      Here's a quick review of each one.
    
The JComponent class supports managing keyboard bindings with a set of public methods that add and remove entries in a private table:
    void registerKeyboardAction(ActionListener a, String command, KeyStroke k, int condition)
    void registerKeyboardAction(ActionListener a, KeyStroke k, int condition)
    void unregisterKeyboardAction(KeyStroke k)
    void resetKeyboardActions()
                            
    
      The registerKeyboardAction methods add an entry to the 
      table that means: "when KeyStroke k occurs, invoke  
      a.actionPerformed(). The actionPerformed method is passed an  
      ActionEvent whose source is the component and whose  
      actionCommand is the specified command string. The shorter 
      version of registerKeyboardAction just uses null for command
       . The condition allows one to specify when the binding is valid:
    
WHEN_FOCUSED
WHEN_ANCESTOR_OF_FOCUSED_COMPONENT
WHEN_IN_FOCUSED_WINDOW
      The unregisterKeyboardAction and resetKeyboardActions
 methods support removing one binding or all of them. In addition to the 
      methods for managing the binding table, there are a few public 
      JComponent methods for reading the table:
    
    KeyStroke[] getRegisteredKeyStrokes()
    int getConditionForKeyStroke(KeyStroke aKeyStroke)
    ActionListener getActionForKeyStroke(KeyStroke aKeyStroke)
                            
    
      Keystroke processing is driven by JComponent.processKeyEvent
       . If an incoming KeyEvent isn't consumed by the FocusManager or any of 
      the components KeyListeners, the action associated with the KeyEvent is 
      retrieved from the binding table (which is stored as a semi-private 
      client property under "_KeyboardBindings").
    
      All of the ComponentUI subclasses use registerKeyboardAction to enable 
      keyboard navigation, except for the text classes. Conventionally, each 
BasicXXXUI subclass has a pair of methods,  
      installKeyboardActions() and uninstallKeyboardActions()
 that use registerKeyboardAction and unregister KeyboardAction respectively to 
      manage the bindings. The code that does this is largely boilerplate and 
      can be difficult to read in large doses. For example here's just a 
      little of installKeyboardActions() in BasicListUI:
    
	// page up
	list.registerKeyboardAction(new PageUpAction
		        ("SelectPageUp", CHANGE_SELECTION),
			KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_UP, 0),
			JComponent.WHEN_FOCUSED);
	list.registerKeyboardAction(new PageUpAction
		        ("ExtendSelectPageUp", EXTEND_SELECTION),
			KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_UP, InputEvent.
			SHIFT_MASK), JComponent.WHEN_FOCUSED);
	// page down
	list.registerKeyboardAction(new PageDownAction
		        ("SelectPageDown", CHANGE_SELECTION),
			KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN, 0),
			JComponent.WHEN_FOCUSED);
	list.registerKeyboardAction(new PageDownAction
		        ("ExtendSelectPageDown", EXTEND_SELECTION),
			KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN,
			InputEvent.SHIFT_MASK), JComponent.WHEN_FOCUSED);
                            
    [TBD: note the design flaw in the current archicture: menu items, with their dubious parentage, can't really use WHEN_IN_FOCUSED_WINDOW.]
      JTextComponent, the superclass for the Swing text components, uses an 
      ordered list of named Keymaps to define key bindings. Each 
      text component has a default keymap that contains generic key bindings. 
      The TextUI classes insert look and feel specific Keymaps in front of the 
      default one. The default keymap, which is the tail of the keymaps list, 
      is the value of the JTextComponent "keymap" property:
    
    void setKeymap(Keymap map)
    Keymap getKeymap()
                            
    
      The following JTextComponent static utility methods support managing 
      Keymap lists. The addKeymap method inserts an empty keymap 
      named name before parent, removeKeymap 
      removes a keymap from the list, and getKeymap looks one up 
      by name.
    
    static Keymap addKeymap(String name, Keymap parent)
    static Keymap removeKeymap(String name)
    static Keymap getKeymap(String name)
                            
    The Keymap class provides a nice set of methods for managing bindings, e.g. the add, remove, and lookup operations are:
    void addActionForKeyStroke(KeyStroke key, Action a)
    void removeKeyStrokeBinding(KeyStroke key);
    Action getAction(KeyStroke key);
                            
    
      The text components handle pressed/released KeyEvents by finding the 
      first Keymap with a non-null binding for the corresponding KeyStroke. If 
      the bindings Action is enabled, it's actionPerformed method 
      is applied to an ActionEvent whose actionCommand property 
      is the (string value of the) key character that matched the binding.
    
      KeyMaps also have a defaultAction property that's used for 
KeyEvent.KEY_TYPED events that don't have an ordinary binding. [Why is 
      this property called "defaultAction"?]
    
The text components also support a read-only property called "actions" whose value is a complete list of the actions supported by the Component. Both the generic actions and the look and feel specific actions are combined to form this list.
The Text UI classes store a simple version of each Look and Feel specific Keymap in an array of KeyBinding objects in the defaults table. The KeyBindings are combined with the Actions defined by the UI class to create a Keymap. The swing LookAndFeel class provides a utility method for creating KeyBinding arrays from a list of Strings. The result is relatively easy to read.
There are two serious incompatabilities between the JComponent registerKeyboardAction machinery and the Keymap based system in the text package:
          The text package doesn't explicitly allow for creating bindings that 
          apply when a descendant has the focus or when the components 
          ancestor window has the focus. The JComponent  
          registerComponentAction() use a condition of  
          WHEN_ANCESTOR_OF_FOCUSED_COMPONENT or  
          WHEN_IN_FOCUSED_WINDOW for these cases. Most of the bindings 
          in Swing are defined with condition == WHEN_FOCUSED.
        
          The JComponent registerKeyboardAction methods allow one 
          to specify what the ActionEvent command will be. Most of the non 
          text Swing components use an informational name for the command. 
          Dependencies on these names have started to creep in, e.g. in the 
          accessibility code. The text components rely on the ActionEvents 
          "actionCommand" property being string version of the KeyEvents 
          keyChar property. Keymaps don't allow one to specify a overriding 
          value to use for actionCommand. We can work around this 
          incompatibility by allowing Action objects to provide the command 
          string, and then using the keyChar string when the Actions command 
          string is null. See below.
        
    In Kestrel we'll replace the two existing keyboard binding systems
    with a new API that unifies them and satisfies the goals listed in
    the first section.  The unified API is based on two new classes:
    InputMap and ActionMap.  Both of these
    classes are just simple tables or "maps". An InputMap maps a
    KeyStroke to an object and ActionMap maps from an
    object to an Action.  In Kestrel Swing will handle
    incoming key events with a simple three step process:
    
Object actionMapKey = inputMap.get(KeyStroke.getKeyStroke(keyEvent));
if (actionMapKey != null) {
    Action action = actionMap.get(actionMapKey);
    if (action != null) {
	// run the actions actionPerformed() method
    }
}
    
    Incoming KeyEvents are converted to KeyStroke objects, KeyStrokes are mapped by the components InputMap to an object that's used as a key for the ActionMap. If a non-null entry in the ActionMap is found, the actions actionPerformed method is invoked.
The keybinding infrastructure is slightly more complicated than the description above implies because of the need to support component key bindings that apply even when the component itself doesn't have the focus. The existing keybinding infrastructure defines two other important scopes for a key binding: when a descendant of the component has the focus and when the component is a descendant of a top level window that has the focus. Each kind of keybinding gets it's own InputMap.
    All Swing components, i.e. all subclasses of JComponent, will have
    an ActionMap and three InputMaps, one for each keybinding scope.
    A components ActionMap is
    initialized by it's UI (its look and feel implementation) with
    both generic actions and look and feel specific actions.  The
    three InputMaps are initialized similarly and they provide support
    for the three kinds of keyboard bindings the existing
    registerKeyboardAction method supports: WHEN_FOCUSED,
    WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,
    WHEN_IN_FOCUSED_WINDOW.
    
    InputMaps and ActionMaps have a parent property whose value is
    another map of the same type or null.  In both classes the lookup
    method, get(key), recursively searches the parent map
    if a local match isn't found.  This enables sharing, e.g. most
    text components can share a single InputMap that contains basic
    bindings for caret motion, character editing, cut and paste, and
    so on.  To protect developers from inadvertantly changing values
    in shared maps, the value of the JComponent InputMap and ActionMap
    properties is always map whose parent is the potentially shared
    map provided by the UI.  The map itself is initially empty.
    
    The fact that the getInputMap method returns an empty
    component-local map makes adding a new binding for an existing
    action simple.  For example to bind the F10 key to the
    "cut" action in myComponent one would write:
    
myComponent.getInputMap().put(KeyStroke.getKeyStroke("F10"), "cut");
    
    
    There's no need to create a new InputMap and configure its parent and set the inputMap property. To defeat the binding for an existing keystroke we bind the KeyStroke to an action called "none", which is never bound to an action by convention. In the following example we've defeated the binding for he Win32 style cut accelerator "control-C":
myComponent.getInputMap().put(KeyStroke.getKeyStroke("control C"), "none");
    
    New actions can be added to a component equally easily. The conventional key for an action is it's name, here's an example:
Action myAction = new AbstractAction("doSomething") {
    public void actionPerformed() {
	doSomething();
    }
};
myComponent.getActionMap().put(myAction.get(Action.NAME), myAction);
    
    The following sections itemize the Swing API that has been changed or added to support the new key binding infrastructure.
InputMap associates a KeyStroke with an Object (usually the name of the action as a String, but that is up to you). InputMap, is esentially a strongly typed version of the Map interface defined in the collection classes. InputMap also has a parent property of type InputMap. InputMap is implemented such that if a binding is asked for (using the get method) that is not contained in the receiver, the parent InputMap is invoked. The following code creates two InputMaps, one a parent of the other:
    child = new InputMap();
    child.put(KeyStroke.getKeyStroke('a'), "A");
    parent = new InputMap();
    parent.put(InputMap.getKeyStroke('b'), "B");
    child.setParent(parent);
    
      child does not have a binding for 'b', but its parent does. So
      that child.get(KeyStroke.getKeyStroke('b')) will
      return "B".
    
Here's the InputMap API.
/**
 * <code>InputMap</code> provides a binding between an input event
 * (currently only <code>KeyStroke</code>s are used)
 * and an <code>Object</code>. <code>InputMap</code>s
 * are usually used with an <code>ActionMap</code>,
 * to determine an <code>Action</code> to perform
 * when a key is pressed.
 * An <code>InputMap</code> can have a parent
 * that is searched for bindings not defined in the <code>InputMap</code>.
 *
 * @version 1.5 06/03/99
 * @author Scott Violet
 * @since 1.3
 */
public class InputMap implements Serializable {
    /** 
     * Creates an <code>InputMap</code> with no parent and no mappings.
     */
    public InputMap()
    /**
     * Sets this <code>InputMap</code>'s parent.
     *
     * @param map  the <code>InputMap</code> that is the parent of this one
     */
    public void setParent(InputMap map) 
    /**
     * Gets this <code>InputMap</code>'s parent.
     *
     * @return map  the <code>InputMap</code> that is the parent of this one,
     *              or null if this <code>InputMap</code> has no parent
     */
    public InputMap getParent()
    /**
     * Adds a binding for <code>keyStroke</code> to <code>actionMapKey</code>.
     * If <code>actionMapKey</code> is null, this removes the current binding
     * for <code>keyStroke</code>.
     */
    public void put(KeyStroke keyStroke, Object actionMapKey)
    /**
     * Returns the binding for <code>keyStroke</code>, messaging the 
     * parent <code>InputMap</code> if the binding is not locally defined.
     */
    public Object get(KeyStroke keyStroke) 
    /**
     * Removes the binding for <code>key</code> from this
     * <code>InputMap</code>.
     */
    public void remove(KeyStroke key)
    /**
     * Removes all the mappings from this <code>InputMap</code>.
     */
    public void clear() 
    /**
     * Returns the <code>KeyStroke</code>s that are bound in this <code>InputMap</code>.
     */
    public KeyStroke[] keys()
    /**
     * Returns the number of <code>KeyStroke</code> bindings.
     */
    public int size() 
    /**
     * Returns an array of the <code>KeyStroke</code>s defined in this 
     * <code>InputMap</code> and its parent. This differs from <code>keys()</code> in that
     * this method includes the keys defined in the parent.
     */
    public KeyStroke[] allKeys()
</pre>
    The ActionMap Class
    
      ActionMap associates an Object (usually the name of the action
      as a String) to an Action. ActionMap is esentially a InputMap,
      with Object replacing KeyStroke, and Action replacing
      Object. ActionMap also has a parent property that behaves in the 
      same way as the parent property in InputMap.
    
    Here's the ActionMap API:
/**
 * ActionMap provides mappings from
 * Objects
 * (called keys or Action names)
 * to Actions.
 * An ActionMap is usually used with an InputMap
 * to locate a particular action
 * when a key is pressed. As with InputMap,
 * an ActionMap can have a parent 
 * that is searched for keys not defined in the ActionMap.
 *
 * @version 1.4 06/03/99
 * @author Scott Violet
 * @since 1.3
 */
public class ActionMap implements Serializable {
    /**
     * Creates an ActionMap with no parent and no mappings.
     */
    public ActionMap() 
    /**
     * Sets this ActionMap's parent.
     * 
     * @param map  the ActionMap that is the parent of this one
     */
    public void setParent(ActionMap map) 
    /**
     * Returns this ActionMap's parent.
     * 
     * @return the ActionMap that is the parent of this one,
     * 	       or null if this ActionMap has no parent
     */
    public ActionMap getParent() 
    /**
     * Adds a binding for key to action.
     * If action is null, this removes the current binding
     * for key.
     * In most instances, key will be
     * action.getValue(NAME).
     */
    public void put(Object key, Action action) 
    /**
     * Returns the binding for key, messaging the 
     * parent ActionMap if the binding is not locally defined.
     */
    public Action get(Object key) 
    /**
     * Removes the binding for key from this ActionMap.
     */
    public void remove(Object key) 
    /**
     * Removes all the mappings from this ActionMap.
     */
    public void clear()
    /**
     * Returns the Action names that are bound in this ActionMap.
     */
    public Object[] keys() 
    /**
     * Returns the number of KeyStroke bindings.
     */
    public int size() 
    /**
     * Returns an array of the keys defined in this ActionMap and
     * its parent. This method differs from keys() in that
     * this method includes the keys defined in the parent.
     */
    public Object[] allKeys() 
}
    The Implementation and Related Changes
    
      The plan is to move to using InputMap and ActionMap in lieu
      of registerKeyboardAction. registerKeyboardAction, in all
      incarnations, will be deprecated. JComponent will get the
      following methods:
    public final void setInputMap(int, InputMap);
    public final InputMap getInputMap(int);
    public setActionMap(ActionMap);
    public ActionMap getActionMap();
    
      The integer passed to set/getInputMap will be one of:
      WHEN_IN_FOCUSED_WINDOW, WHEN_ANCESTOR_OF_FOCUSED_COMPONENT or
      WHEN_IN_FOCUSED_WINDOW , implying JComponent maintains three
      InputMaps as well as the ActionMap. There is a single ActionMap
      containing all the actions that the InputMaps reference.
    
      Each ComponentUI will have the opportunity to provide an
      ActionMap, as well as three InputMaps for the conditions. The
      InputMaps and ActionMap provided by the UI will implement the
      UIResource interface (use javax.swing.plaf.InputMapUIResource and
      javax.swing.plaf.ActionMapUIResource). The InputMaps
      provided by the UI will be set as the parent of the InputMap
      (actually, as the parent of the first InputMap with a null parent)
      contained in the JComponent, the ActionMap will be set in a
      similiar manner. SwingUtilties will provide the
      convenience methods replaceUIActionMap and replaceUIInputMap to
      manipulate the UI InputMap/ActionMap, as well as the methods
      getUIActionMap and getUIInputMap to obtain the Maps provided by
      the UI. Installing a UI InputMap looks like:
    SwingUtilties.replaceUIInputMap(jcomponent, int, keyMap);
    
      And to remove the UI InputMap use the following:
    SwingUtilties.replaceUIInputMap(component, int, null);
    ComponentInputMap and WHEN_IN_FOCUSED_WINDOW?
    
      WHEN_IN_FOCUSED_WINDOW bindings are handled very differently
      from the rest of the bindings. They are handled differently
      to avoid having to walk the container hierarchy each time a
      KeyEvent goes unconsumed by normaly processing (thank Steve for
      this, originally the container hieararchy was walked each
      time leading to serious performance degredation when
      typing!). To facilitate
      this, ComponentInputMap is used for all WHEN_IN_FOCUSED_WINDOW
      bindings. ComponentInputMap extends InputMap, adding an associated
      JComponent property. When the InputMap is modified, the JComponent
      is notfied so that it can update its internal state. Further,
      ComponentInputMaps only allow parents of type ComponentInputMap.
    
      The astute reader may have surmised that ComponentInputMap's can
      not be shared. Since a ComponentInputMap is associated with a
      single JComponent it should not be shared (in reality it can be
      shared, but changes to the InputMap may not be picked up by the
      associated components). While this may seem nasty, we believe
      that WHEN_IN_FOCUSED_WINDOW bindings are seldomly used.
    
Processing of KeyEvents
    
      There are a number of entry points into the new system allowing
      the developer to override behavior in a number of ways. The
      following is a listing of how KeyEvents are processed (the
      processing stops when the event is consumed):
    
      - 
	The initial entry point for JComponent is processKeyEvent,
	which is invoked as part of the AWT processing of events
	(JComponent overrides this method this method defined in
	Component). processKeyEvent is only invoked if KeyEvents
	  are enabled (JComponent will do this if it has a valid 
	  InputMap) or a KeyListener has been added. If a developer
	  requests focus on a Component that does not have any
	  bindings (or does not descend from JComponent) any
	  WHEN_ANCESTOR_OF_FOCUSED_COMPONENT or WHEN_IN_FOCUSED_WINDOW 
	  bindings will not work!
      
- The FocusManager is handed the event, if the FocusManager
	does something as a result of the action (say, changes focus)
	it will consume the event.
      
- Any registered KeyListeners are notified by invoking
	super.processKeyEvent.
      
- processComponentKeyEvent is invoked to allow any
	customizations in a subclasses to happen.
      
- If the event represents a KEY_RELEASED event, and a
	corresponding KEY_PRESSED event was not received processing
	will stop (it is likely this code is the result of a bug
	  that existed in the AWT, we need to investigate if this is
	  still needed).
      
- The WHEN_FOCUSED InputMap is checked (by invoking the
	protected method processKeyBinding with a condition ==
	WHEN_FOCUSED). If there is a binding, the action is enabled,
	and the component is enabled actionPerformed is invoked on the 
	Action and the event is consumed.
      
- The container hiearchy is walked from the focused component
	to the Window, Applet or JInternalFrame invoking
	processKeyBinding with a condition ==
	WHEN_ANCESTOR_OF_FOCUSED_COMPONENT. Similiar to the previous
	step, if a binding exists, the receiver is enabled and the
	action is enabled the action is notified and the
	event is consumed.
      
- WHEN_IN_FOCUSED_WINDOW bindings are checked by way of
	the KeyboardManager. The KeyboardManager is a package private
	class that maintains a mapping from top level components to a
	Hashtable mapping from KeyStroke to components. If there is a
	component registered for a binding, processKeyBinding is
	invoked on the component with a condition of
	WHEN_IN_FOCUSED_WINDOW. Similiar to the two previous
	steps, if a binding exists, the receiver is enabled and the
	action is enabled the action is notified and the
	event is consumed.
      
- Lastly, the KeyboardManager will give any JMenuBar's a
	chance to consume the event by invoking processKeyBinding on
	the JMenuBar's until one consumes the event.
    
Avoiding Allocation
    
      There are a number of opportunities for sharing with this
      scheme. The InputMaps (with the exception of the ComponentInputMap)
      and ActionMap provided by the UI can be shared among 
      instances of the same class. In order to share the InputMap, the
      bindings must not change based on the state of an instance. For
      example, the mnemonic of a component changes based on developer
      settings implying this InputMap can not be shared (this example
      isn't that good, the mnemonic is a binding of type
      WHEN_IN_FOCUSED_WINDOW, implying it is to be in the
      ComponentInputMap and can not be shared, but in looking at the
      classes I have not come across one that dynamicly changes 
      bindings for WHEN_FOCUSED or WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).
    
      When you wouldn't share ActionMaps:
    
isEnabled() method 
	to conditionaly return a value based on an instance.
	actionPerformed is passed the component the
	action is performed on, which provides a way to determine the
	instance allowing for sharing, on the other hand,
	isEnabled() is not passed the source. Putting the 
	isEnabled() check in the
	actionPerformed() method does not provide the
	same behavior, the event will be consumed and not allow
	another component to see the event. Consider a disabled menu
	item and button with the same mnemonic, if the menu item Action
	where to be shared isEnabled would always return true. This
	would give the effect of the menu item consuming the key event 
	even though the button has the same binding, and is enabled.
      
      The bindings (between KeyStroke and Action name) for a component
      will be registered in the defaults table. The bindings will be
      an Object array containing KeyStrokes as Strings and Action
      names (refer to the javadoc of getKeyStroke(String) 
      for the format of the Strings in the binding
      array). JRadioButton has the following bindings:
    "RadioButton.focusInputMap",
       new UIDefaults.LazyInputMap(new Object[]
        {
                     "SPACE", "pressed",
            "released SPACE", "released"
        }),
    The first time the value "RadioButton.focusInputMap" is asked for from the UIManager LazyInputMap will create a InputMapUIResource from the Object array passed in.
A InputMap entry can be used for InputMaps that will be registered under WHEN_FOCUSED (focusInputMap) and WHEN_ANCESTOR_OF_FOCUSED_COMPONENT (ancestorInputMap) but not for WHEN_IN_FOCUSED_WINDOW. Remember the WHEN_IN_FOCUSED_WINDOW InputMap is of type ComponentInputMap and can not be shared. It is perfectly fine to express the WHEN_IN_FOCUSED_WINDOW bindings in the defaults, but not the InputMap. The following shows the WHEN_IN_FOCUSED_WINDOW bindnigs for JDesktopPane:
    "Desktop.windowBindings", new Object[]
      {  "ctrl F9", "minimize", 
	"ctrl F10", "maximize",
	 "ctrl F4", "close",
	 "ctrl F6", "navigate",
	"ctrl TAB", "navigate"
      },
    BasicDesktopPaneUI creates a InputMap (actually, a ComponentInputMapUIResource) from the above by way of:
    Object[] bindings = (Object[])UIManager.get
		                          ("Desktop.windowBindings");
    if (bindings != null) {
	keymap = LookAndFeel.makeComponentInputMap(desktop, bindings);
    }
    
      Almost all plaf classes have the protected method
      installKeyboardActions to register any keyboard
      actions. Instead of having these methods call
      registerKeyboardActions they will now install the
      necessary InputMaps and ActionMap. In modifying the plaf classes,
      I have added the following methods (all currently package
      private):
    
InputMap getInputMap(int) which is to return the
	InputMap for the sepcified condition. If the component does not
	share the InputMap for the specified condition this method will 
	call createInputMap() each time it is invoked.
      InputMap createInputMap(int) to create a
	InputMap. This is currently only invoked for InputMaps of type
	WHEN_IN_FOCUSED_WINDOW, all others are stored in the defaults
	table so that the component UI never needs to instantiate
	one. This method only exists for components that create a
	InputMap per instance (most don't).
      ActionMap getActionMap() which returns the
	appropriate ActionMap. If the ActionMap is not shared, this
	method will call createActionMap to create the
	ActionMap. If the ActionMap is shared the ActionMap is stored
	in the defaults table under the name XXX.actionMap. So, the
	first time getActionMap is invoked there would
	be no entry, and createActionMap would be
	invoked, the return value would then be placed in the
	defaults table.
      createActionMap to instantiate the ActionMap
	with the appropriate Actions.
    
      A typical installKeyboardActions method now looks
      like:
    protected void installKeyboardActions() {
	InputMap keyMap = getInputMap(JComponent.WHEN_FOCUSED);
	SwingUtilities.replaceUIInputMap(list, JComponent.WHEN_FOCUSED, keyMap);
	ActionMap map = getActionMap();
        SwingUtilities.replaceUIActionMap(list, map);
    }
    
      Notice that currently installKeyboardActions is
      only installing InputMaps for the bindings it expects. That is,
      the above only looks for a InputMap of type WHEN_FOCUSED, since
      currently that is the only binding registered. Similiarly, if a
      component did nothing in its installKeyboardActions method, it
      still does nothing.
    
      And the getInputMap() and getActionMap()
      methods look like:
    InputMap getInputMap(int condition) {
	if (condition == JComponent.WHEN_FOCUSED) {
	    return (InputMap)UIManager.get("List.focusInputMap");
	}
	return null;
    }
    ActionMap getActionMap() {
	ActionMap map = (ActionMap)UIManager.get("List.actionMap");
	if (map == null) {
	    map = createActionMap();
	    if (map != null) {
		SwingUtilities.put("List.actionMap", map);
	    }
	}
	return map;
    }
    ActionMap createActionMap() {
	ActionMap map = new ActionMapUIResource();
        map.put("selectPreviousRow", new ....)
        // register all the Actions JList supports.
        return map;
    }
    
      And the uninstallKeyboardActions looks like:
    protected void uninstallKeyboardActions() {
	SwingUtilities.replaceUIActionMap(list, null);
	SwingUtilities.replaceUIInputMap(list, JComponent.WHEN_FOCUSED, null);
    }
    
    The text components already have the notion of associating KeyStrokes with Actions. The Keymap (note the small m) class exists in the javax.swing.text package and associates a KeyStroke with an Action. We dot not plan on deprecating this, instead we will wrap Keymaps inside of a special ActionMap and InputMap. The plan is to modify the setKeymap method of JTextComponent to wrap the Keymap into a InputMap/ActionMap that is set as the parent of the InputMap/ActionMap provided by the component. This custom InputMap/ActionMap will NOT implement UIResource, so as the UI can provide keybindings, if it wishes. Confused? Take a JTextField, with a focusAccelerator, as an example. There will be the InputMap directly associated with the Comonent, call it cKM, there will be the InputMap that is wrapping the Keymap, calling it kmKM, and there will be the InputMap provided by the UI (call it uiKM). This will have the following structure:
    cKM -> kmKM -> uiKM
    If the Keymap is reset (via JTextComponent.setKeymap()), a new KeymapWrapper will be created, call it kmKM2 that will result in the following structure:
    cKM -> kmKM2 -> uiKM
    Setting the Keymap to null, will result in the usual InputMap structure:
    cKM -> uiKM
    KeymapWrapper and KeymapActionMap are private inner classes of JTextComponent. By wrapping Keymap like this, we do not have to have any special code in JTextComponent to deal with Keymaps, they are essentialy a specialized version of InputMap and ActionMap. Further, builders will not have to specially handle JTextComonent's to discover their bindings.
JButton has two WHEN_FOCUSED bindings, one for when the space bar is pressed and the second for when the space bar is released. BasicLookAndFeel will contain the following entry expressing this:
	    "Button.focusInputMap", new UIDefaults.LazyInputMap(new Object[]
              {
                         "SPACE", "pressed",
                "released SPACE", "released"
              }),
    The focusInputMap is installed in the installKeyboardActions method, via the following:
InputMap km = getInputMap(JComponent.WHEN_FOCUSED, c); SwingUtilities.replaceUIInputMap(c, JComponent.WHEN_FOCUSED, km);
Where getInputMap looks like:
    InputMap getInputMap(int condition, JComponent c) {
	if (condition == JComponent.WHEN_FOCUSED) {
	    ButtonUI ui = ((AbstractButton)c).getUI();
	    if (ui != null && (ui instanceof BasicButtonUI)) {
		return (InputMap)UIManager.get(((BasicButtonUI)ui).
				       getPropertyPrefix() +"focusInputMap");
	    }
	}
	return null;
    }
    JButton (actually, AbstractButton) allows the user to set a mnemonic. Mnemonics are registered using WHEN_IN_FOCUSED_WINDOW, implying ButtonUI needs to supply a ComponentInputMap. When the mnemonic changes (BasicButtonListener is notified via a PropertyChangeListener) it will update the ComponentInputMap. This code looks like:
    int m = b.getMnemonic();
    InputMap keyMap = getWindowInputMap(c);
    keyMap.clear();
    if(m != 0) {
        keyMap.put(KeyStroke.getKeyStroke(m, ActionEvent.ALT_MASK, false),
		       "pressed");
	keyMap.put(KeyStroke.getKeyStroke(m, ActionEvent.ALT_MASK, true),
		       "released");
	keyMap.put(KeyStroke.getKeyStroke(m, 0, true), "released");
    } 
    The last step is to supply the ActionMap containing the implementation for the "pressed" and "released" actions. These actions are conditionally enabled based on the state of the button, implying they can not be shared. BasicButtonUI will provide a new ActionMap for each JButton instance that is created at installUI time. This is created by the following code:
    ActionMap map = new ActionMapUIResource();
    map.put("pressed", new PressedAction((AbstractButton)c));
    map.put("released", new ReleasedAction((AbstractButton)c));
    So, JButton creates an ActionMap, and ComponentInputMap for each instance, and shares a InputMap between all JButton instances. Ideally we could share the ActionMap as well if isEnabled was passed a Component...
Here is the complete set of changes to enable InputMaps/ActionMaps in BasicButtonListener:
    public void installKeyboardActions(JComponent c) {
	AbstractButton b = (AbstractButton)c;	
	// Update the mnemonic binding.
	updateMnemonicBinding(b);
	// Reset the ActionMap.
	ActionMap map = getActionMap(b);
	SwingUtilities.replaceUIActionMap(map, c);
	InputMap km = getInputMap(JComponent.WHEN_FOCUSED, c);
	SwingUtilities.replaceUIInputMap(c, JComponent.WHEN_FOCUSED, km);
    }
    public void uninstallKeyboardActions(JComponent c) {
	if (createdWindowInputMap) {
	    SwingUtilities.replaceUIInputMap(c, JComponent.
					   WHEN_IN_FOCUSED_WINDOW, null);
	    createdWindowInputMap = false;
	}
	SwingUtilities.replaceUIInputMap(c, JComponent.WHEN_FOCUSED, null);
	SwingUtilities.replaceUIActionMap(c, null);
    }
    ActionMap getActionMap(AbstractButton b) {
	return createActionMap(b);
    }
    InputMap getInputMap(int condition, JComponent c) {
	if (condition == JComponent.WHEN_FOCUSED) {
	    ButtonUI ui = ((AbstractButton)c).getUI();
	    if (ui != null && (ui instanceof BasicButtonUI)) {
		return (InputMap)UIManager.get(((BasicButtonUI)ui).
				       getPropertyPrefix() +"focusInputMap");
	    }
	}
	return null;
    }
    ActionMap createActionMap(AbstractButton c) {
	ActionMap retValue = new javax.swing.plaf.ActionMapUIResource();
	retValue.put("pressed", new PressedAction((AbstractButton)c));
	retValue.put("released", new ReleasedAction((AbstractButton)c));
	return retValue;
    }
    void updateMnemonicBinding(AbstractButton b) {
	int m = b.getMnemonic();
	if(m != 0) {
	    InputMap map;
	    if (!createdWindowInputMap) {
		map = new ComponentInputMapUIResource(b);
		SwingUtilities.replaceUIInputMap(b,
			       JComponent.WHEN_IN_FOCUSED_WINDOW, map);
		createdWindowInputMap = true;
	    }
	    else {
		map = SwingUtilities.getUIInputMap(JComponent.
						 WHEN_IN_FOCUSED_WINDOW, b);
	    }
	    if (map != null) {
		map.clear();
		map.put(KeyStroke.getKeyStroke(m, ActionEvent.ALT_MASK, false),
			"pressed");
		map.put(KeyStroke.getKeyStroke(m, ActionEvent.ALT_MASK, true),
		       "released");
		map.put(KeyStroke.getKeyStroke(m, 0, true), "released");
	    }
	} 
	else if (createdWindowInputMap) {
	    InputMap map = SwingUtilities.getUIInputMap(JComponent.
					     WHEN_IN_FOCUSED_WINDOW, b);
	    if (map != null) {
		map.clear();
	    }
	}
    }
    Notice the trickery with getPropertyPrefix(), this is used as BasicButtonListener is used for a number of classes, not just JButton. Also notice the updateMnemonicBinding, this is called when the mnemonic property changes and will update the WHEN_IN_FOCUSED_WINDOW InputMap as a result of the mnemonic changing.
A couple of things should be noted about moving to InputMap/ActionMap:
Hans Muller and Scott Violet Last modified: Fri Jun 4 13:00:54 PDT 1999