I l@ve RuBoard |
![]() ![]() |
21.3 KeymapsA Keymap contains mappings from KeyStrokes[2] to Actions and provides a variety of methods for accessing and updating these mappings.
21.3.1 The Keymap InterfaceOne last interface you can use to customize your application without implementing your own L&F is Keymap. Normally, an L&F defines a set of meaningful keystrokes. For example, Windows users expect Ctrl-C to copy text and Ctrl-V to paste while the Mac uses the Command key instead. Ctrl-Insert and Shift-Insert perform the same tasks for Motif users. These key sequences work as expected in Swing text components because of the Keymap installed by the L&F. This interface lets you change or augment this behavior.
21.3.1.1 PropertiesThe Keymap interface defines the properties shown in Table 21-8. The boundAction and boundKeyStrokes properties contain the Actions and KeyStrokes (both part of the javax.swing package) local to the Keymap.
The defaultAction property represents the action to be used when there is no action defined for a key. It is typically set to an instance of the DefaultKeyTypedAction inner class from the DefaultEditorKit. However, if you want to catch all keystrokes (perhaps to implement an editor like vi that inserts text only when it is in insert mode), you could define your own default action. defaultAction may be null, but the getDefaultAction( ) method should consult the resolveParent before returning null. name is an arbitrary name given to the map. It may be null, but then the keymap will not be found by JTextComponent methods that refer to keymaps by name. Finally, the resolveParent is another Keymap used to resolve any keys that are not defined locally in the map, thus creating a linear hierarchy of Keymaps. If an appropriate Action is not defined locally, the parent keymap is consulted (unless the parent is null). 21.3.1.2 MethodsSeveral methods defined in the Keymap interface allow mappings from KeyStrokes to Actions to be added, removed, and queried:
21.3.2 Keymap ImplementationUnlike Caret and Highlighter, the Keymap interface does not have a public default implementation. The JTextComponent class defines DefaultKeymap as a package-private inner class. This implementation uses a Hashtable to map from KeyStrokes to Actions. (So each KeyStroke maps to no more than one Action.) There are a few ways to change the Keymap used by your text components. One option is to call getKeymap( ) on the component and add any new actions directly to that map. Doing this may change the mappings for all JTextComponents in the application if the L&F is sharing a single Keymap for every component (which is what the Swing L&Fs do). This is not a big problem if the actions you add work with all types of text components, but it is probably not the best approach. A better approach is to define a new Keymap that uses the default Keymap (installed by the L&F) as its parent. The new Keymap contains only the mappings you define and passes any other keystrokes up to the default map. As the next example shows, this is done by calling JTextComponent.addKeymap( ). This method takes the name of your new map and the parent map to be used and returns a new Keymap for you to add mappings to. Since this is a static method, once you've made this call and added to the new map, you can use the new map for any other JTextComponents by calling: myComponent.setKeymap(JTextComponent.getKeymap("MyKeymapName")) 21.3.3 Adding Keyboard ActionsHere's a quick example showing how easy it is to add keyboard functionality to Swing text components. In this example, we'll enable Ctrl-W to select a word, Ctrl-L to select a line, and Ctrl-U to convert words to uppercase.[3]
JTextArea provides Actions for select-word and select-line via the getActions( ) method, but we create our own upcase-word Action as an inner class. All we have to do is create a Keymap containing the mappings we want, and we'll be able to make selections or upcase words with a simple key press. Here's the code to do this: // KeymapExample.java // import javax.swing.*; import javax.swing.text.*; import java.util.Hashtable; import java.awt.event.*; import java.awt.BorderLayout; // A simple example showing how to add Actions for KeyStrokes public class KeymapExample { public static void main(String[] args) { // Start with a simple JTextArea, get its Keymap to use as our parent, // and create a new map called "KeymapExampleMap". JTextArea area = new JTextArea(6, 32); Keymap parent = area.getKeymap( ); Keymap newmap = JTextComponent.addKeymap("KeymapExampleMap", parent); // Add Ctrl-U: change current word to uppercase (our own action). KeyStroke u = KeyStroke.getKeyStroke(KeyEvent.VK_U, InputEvent.CTRL_MASK); Action actionU = new UpWord( ); // An inner class (defined below) newmap.addActionForKeyStroke(u, actionU); // Get all the actions JTextArea provides for us. Action actionList[] = area.getActions( ); // Put them in a Hashtable so that we can retreive them by Action.NAME. Hashtable lookup = new Hashtable( ); for (int j=0; j < actionList.length; j+=1) lookup.put(actionList[j].getValue(Action.NAME), actionList[j]); // Add Ctrl-L: select current line (action provided for us). KeyStroke L = KeyStroke.getKeyStroke(KeyEvent.VK_L, InputEvent.CTRL_MASK); Action actionL = (Action)lookup.get(DefaultEditorKit.selectLineAction); newmap.addActionForKeyStroke(L, actionL); // Add Ctrl-W: select current word (action provided for us). KeyStroke W = KeyStroke.getKeyStroke(KeyEvent.VK_W, InputEvent.CTRL_MASK); Action actionW = (Action)lookup.get(DefaultEditorKit.selectWordAction); newmap.addActionForKeyStroke(W, actionW); // Set the JTextArea's Keymap to be our new map. area.setKeymap(newmap); // Show the TextPane. JFrame f = new JFrame("KeymapExample"); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.getContentPane( ).add(new JScrollPane(area), BorderLayout.CENTER); area.setText("This is the story\nof the hare who\nlost his spectacles."); f.pack( ); f.setVisible(true); } // Begin inner class. public static class UpWord extends TextAction { public UpWord( ) { super("uppercase-word-action"); } public void actionPerformed(ActionEvent e) { // Change current word (or selected words) to uppercase. JTextComponent comp = getTextComponent(e); if (comp == null) return; Document doc = comp.getDocument( ); int start = comp.getSelectionStart( ); int end = comp.getSelectionEnd( ); try { int left = javax.swing.text.Utilities.getWordStart(comp, start); int right = javax.swing.text.Utilities.getWordEnd(comp, end); String word = doc.getText(left, right-left); doc.remove(left, right-left); doc.insertString(left, word.toUpperCase( ), null); comp.setSelectionStart(start); // Restore previous position/selection. comp.setSelectionEnd(end); } catch (BadLocationException ble) { return; } } } // End inner class. }
Don't worry about understanding everything about the actions we added here. (Text actions are covered in detail in Chapter 23.) The important things to understand are the following basic steps:
![]() |
I l@ve RuBoard |
![]() ![]() |