blob: cba5a5d8bb3838d6981d5810319087dd47dbdda7 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1999-2003 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25package javax.swing;
26
27import java.io.IOException;
28import java.io.ObjectInputStream;
29import java.io.ObjectOutputStream;
30import java.io.Serializable;
31import java.util.HashMap;
32import 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 */
55public 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}