1 /* 2 * Copyright (c) 1999, 2003, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package javax.swing; 26 27 import java.io.IOException; 28 import java.io.ObjectInputStream; 29 import java.io.ObjectOutputStream; 30 import java.io.Serializable; 31 import java.util.HashMap; 32 import java.util.Set; 33 34 /** 35 * <code>InputMap</code> provides a binding between an input event 36 * (currently only <code>KeyStroke</code>s are used) 37 * and an <code>Object</code>. <code>InputMap</code>s 38 * are usually used with an <code>ActionMap</code>, 39 * to determine an <code>Action</code> to perform 40 * when a key is pressed. 41 * An <code>InputMap</code> can have a parent 42 * that is searched for bindings not defined in the <code>InputMap</code>. 43 * <p>As with <code>ActionMap</code> if you create a cycle, eg: 44 * <pre> 45 * InputMap am = new InputMap(); 46 * InputMap bm = new InputMap(): 47 * am.setParent(bm); 48 * bm.setParent(am); 49 * </pre> 50 * some of the methods will cause a StackOverflowError to be thrown. 51 * 52 * @author Scott Violet 53 * @since 1.3 54 */ 55 public class InputMap implements Serializable { 56 /** Handles the mapping between KeyStroke and Action name. */ 57 private transient ArrayTable arrayTable; 58 /** Parent that handles any bindings we don't contain. */ 59 private InputMap parent; 60 61 62 /** 63 * Creates an <code>InputMap</code> with no parent and no mappings. 64 */ 65 public InputMap() { 66 } 67 68 /** 69 * Sets this <code>InputMap</code>'s parent. 70 * 71 * @param map the <code>InputMap</code> that is the parent of this one 72 */ 73 public void setParent(InputMap map) { 74 this.parent = map; 75 } 76 77 /** 78 * Gets this <code>InputMap</code>'s parent. 79 * 80 * @return map the <code>InputMap</code> that is the parent of this one, 81 * or null if this <code>InputMap</code> has no parent 82 */ 83 public InputMap getParent() { 84 return parent; 85 } 86 87 /** 88 * Adds a binding for <code>keyStroke</code> to <code>actionMapKey</code>. 89 * If <code>actionMapKey</code> is null, this removes the current binding 90 * for <code>keyStroke</code>. 91 */ 92 public void put(KeyStroke keyStroke, Object actionMapKey) { 93 if (keyStroke == null) { 94 return; 95 } 96 if (actionMapKey == null) { 97 remove(keyStroke); 98 } 99 else { 100 if (arrayTable == null) { 101 arrayTable = new ArrayTable(); 102 } 103 arrayTable.put(keyStroke, actionMapKey); 104 } 105 } 106 107 /** 108 * Returns the binding for <code>keyStroke</code>, messaging the 109 * parent <code>InputMap</code> if the binding is not locally defined. 110 */ 111 public Object get(KeyStroke keyStroke) { 112 if (arrayTable == null) { 113 InputMap parent = getParent(); 114 115 if (parent != null) { 116 return parent.get(keyStroke); 117 } 118 return null; 119 } 120 Object value = arrayTable.get(keyStroke); 121 122 if (value == null) { 123 InputMap parent = getParent(); 124 125 if (parent != null) { 126 return parent.get(keyStroke); 127 } 128 } 129 return value; 130 } 131 132 /** 133 * Removes the binding for <code>key</code> from this 134 * <code>InputMap</code>. 135 */ 136 public void remove(KeyStroke key) { 137 if (arrayTable != null) { 138 arrayTable.remove(key); 139 } 140 } 141 142 /** 143 * Removes all the mappings from this <code>InputMap</code>. 144 */ 145 public void clear() { 146 if (arrayTable != null) { 147 arrayTable.clear(); 148 } 149 } 150 151 /** 152 * Returns the <code>KeyStroke</code>s that are bound in this <code>InputMap</code>. 153 */ 154 public KeyStroke[] keys() { 155 if (arrayTable == null) { 156 return null; 157 } 158 KeyStroke[] keys = new KeyStroke[arrayTable.size()]; 159 arrayTable.getKeys(keys); 160 return keys; 161 } 162 163 /** 164 * Returns the number of <code>KeyStroke</code> bindings. 165 */ 166 public int size() { 167 if (arrayTable == null) { 168 return 0; 169 } 170 return arrayTable.size(); 171 } 172 173 /** 174 * Returns an array of the <code>KeyStroke</code>s defined in this 175 * <code>InputMap</code> and its parent. This differs from <code>keys()</code> in that 176 * this method includes the keys defined in the parent. 177 */ 178 public KeyStroke[] allKeys() { 179 int count = size(); 180 InputMap parent = getParent(); 181 182 if (count == 0) { 183 if (parent != null) { 184 return parent.allKeys(); 185 } 186 return keys(); 187 } 188 if (parent == null) { 189 return keys(); 190 } 191 KeyStroke[] keys = keys(); 192 KeyStroke[] pKeys = parent.allKeys(); 193 194 if (pKeys == null) { 195 return keys; 196 } 197 if (keys == null) { 198 // Should only happen if size() != keys.length, which should only 199 // happen if mutated from multiple threads (or a bogus subclass). 200 return pKeys; 201 } 202 203 HashMap keyMap = new HashMap(); 204 int counter; 205 206 for (counter = keys.length - 1; counter >= 0; counter--) { 207 keyMap.put(keys[counter], keys[counter]); 208 } 209 for (counter = pKeys.length - 1; counter >= 0; counter--) { 210 keyMap.put(pKeys[counter], pKeys[counter]); 211 } 212 213 KeyStroke[] allKeys = new KeyStroke[keyMap.size()]; 214 215 return (KeyStroke[])keyMap.keySet().toArray(allKeys); 216 } 217 218 private void writeObject(ObjectOutputStream s) throws IOException { 219 s.defaultWriteObject(); 220 221 ArrayTable.writeArrayTable(s, arrayTable); 222 } 223 224 private void readObject(ObjectInputStream s) throws ClassNotFoundException, 225 IOException { 226 s.defaultReadObject(); 227 for (int counter = s.readInt() - 1; counter >= 0; counter--) { 228 put((KeyStroke)s.readObject(), s.readObject()); 229 } 230 } 231 }