001: /*
002: * Sun Public License Notice
003: *
004: * The contents of this file are subject to the Sun Public License
005: * Version 1.0 (the "License"). You may not use this file except in
006: * compliance with the License. A copy of the License is available at
007: * http://www.sun.com/
008: *
009: * The Original Code is NetBeans. The Initial Developer of the Original
010: * Code is Sun Microsystems, Inc. Portions Copyright 1997-2000 Sun
011: * Microsystems, Inc. All Rights Reserved.
012: */
013:
014: package org.netbeans.editor;
015:
016: import java.util.ArrayList;
017: import java.util.Arrays;
018:
019: import javax.swing.KeyStroke;
020: import javax.swing.text.JTextComponent;
021:
022: /**
023: * Extension of JTextComponent.KeyBinding to hold several successive keystrokes.
024: * The binding containing null key(s) is assumed to assign the default action.
025: *
026: * @author Miloslav Metelka
027: * @version 1.00
028: */
029:
030: public class MultiKeyBinding extends JTextComponent.KeyBinding
031: implements java.io.Externalizable {
032:
033: /**
034: * Successive keystroke. They must be pressed in the order they are stored
035: * in the array in order to invoke the associated action.
036: */
037: public KeyStroke[] keys;
038:
039: static final long serialVersionUID = -8602816556604003688L;
040:
041: /** Constructor for serialization */
042: public MultiKeyBinding() {
043: super (null, null);
044: }
045:
046: /**
047: * Constructor for assigning keystroke sequence to action
048: *
049: * @param keys
050: * successive keystroke that must be pressed in order to invoke
051: * action
052: * @param actionName
053: * action that will be invoked. Action is resolved from name by
054: * calling kit.getActions() after the kit is constructed
055: */
056: public MultiKeyBinding(KeyStroke[] keys, String actionName) {
057: super (null, actionName);
058: this .keys = keys;
059: }
060:
061: /** Compatibility constructor */
062: public MultiKeyBinding(KeyStroke key, String actionName) {
063: super (key, actionName);
064: }
065:
066: /** Constructor for existing KeyBinding */
067: public MultiKeyBinding(JTextComponent.KeyBinding kb) {
068: this (kb.key, kb.actionName);
069: }
070:
071: public boolean equals(Object o) {
072: if (o instanceof MultiKeyBinding) {
073: MultiKeyBinding kb = (MultiKeyBinding) o;
074:
075: // Compare action names
076: if (actionName == null) {
077: if (kb.actionName != null) {
078: return false;
079: }
080: } else {
081: if (!actionName.equals(kb.actionName)) {
082: return false;
083: }
084: }
085:
086: // Action names match, now compare action keys
087: if (keys == null) {
088: if (kb.keys == null) {
089: return (key == null && kb.key == null)
090: || (key != null && key.equals(kb.key));
091: } else {
092: return (kb.keys.length == 1 && ((key == null && kb.keys[0] == null) || (key != null && key
093: .equals(kb.keys[0]))));
094: }
095: } else { // keys != null
096: if (kb.keys != null) {
097: return Arrays.equals(keys, kb.keys);
098: } else { // kb.keys == null
099: return (keys.length == 1 && ((kb.key == null && keys[0] == null) || (kb.key != null && kb.key
100: .equals(keys[0]))));
101: }
102: }
103: }
104: return false;
105: }
106:
107: /**
108: * Add or replace key bindings array by changes given in the second bindings
109: * array
110: *
111: * @param target
112: * target list of bindings
113: * @param changes
114: * list of changes to apply: binding containing the non-null
115: * keystroke(s) and non-null action will add the binding or
116: * replace the old binding with the same keystroke(s) in the
117: * target array, binding of the non-null keystroke(s) and null
118: * action removes the binding for that keystroke from the target
119: * array (if it existed) binding containing null keystroke and
120: * non-null action adds or replaces default action
121: */
122: public static void updateKeyBindings(
123: JTextComponent.KeyBinding[] target,
124: JTextComponent.KeyBinding[] changes) {
125: ArrayList tgt = new ArrayList(Arrays.asList(target));
126: MultiKeyBinding tmp = new MultiKeyBinding(new KeyStroke[1],
127: null);
128: MultiKeyBinding cur;
129: for (int i = 0; i < changes.length; i++) {
130: if (changes[i] instanceof MultiKeyBinding) {
131: cur = (MultiKeyBinding) changes[i];
132: if (cur.keys == null) { // single key multi binding
133: tmp.keys[0] = cur.key;
134: tmp.actionName = cur.actionName;
135: cur = tmp;
136: }
137: } else { // simulate multi binding
138: tmp.keys[0] = changes[i].key;
139: tmp.actionName = changes[i].actionName;
140: cur = tmp;
141: }
142: // cycle through all bindings
143: boolean matched = false;
144: for (int j = 0; j < tgt.size(); j++) {
145: JTextComponent.KeyBinding kb = (JTextComponent.KeyBinding) tgt
146: .get(j);
147: if (kb instanceof MultiKeyBinding) {
148: MultiKeyBinding mkb = (MultiKeyBinding) kb;
149: if (mkb.keys == null) { // single key multi binding
150: if (cur.keys.length == 1
151: && cur.keys[0].equals(mkb.key)) { // found
152: if (mkb.actionName == null) { // remove
153: tgt.remove(i);
154: } else { // replace
155: tgt.set(i, mkb);
156: }
157: matched = true;
158: break;
159: }
160: } else { // multi binding
161: if (cur.keys.length == mkb.keys.length) {
162: matched = true;
163: for (int k = 0; k < cur.keys.length; k++) {
164: if (!cur.keys[k].equals(mkb.keys[k])) {
165: matched = false;
166: break;
167: }
168: }
169: if (matched) {
170: if (mkb.actionName == null) { // remove
171: tgt.remove(i);
172: } else { // replace
173: tgt.set(i, mkb);
174: }
175: break;
176: }
177: }
178: }
179: } else { // single key binding
180: if (cur.keys.length == 1
181: && cur.keys[0].equals(kb.key)) { // found
182: if (kb.actionName == null) { // remove
183: tgt.remove(i);
184: } else { // replace
185: tgt.set(i, kb);
186: }
187: matched = true;
188: break;
189: }
190: }
191: }
192: if (!matched) {
193: tgt.add(changes[tgt.size()]);
194: }
195: }
196: }
197:
198: public void readExternal(java.io.ObjectInput in)
199: throws java.io.IOException, ClassNotFoundException {
200: Object obj = in.readObject();
201:
202: if (obj instanceof Integer) { // new settings format
203: int len = ((Integer) obj).intValue();
204: if (len >= 0) {
205: keys = new KeyStroke[len];
206: for (int i = 0; i < len; i++) {
207: keys[i] = KeyStroke.getKeyStroke(in.readInt(), in
208: .readInt(), in.readBoolean());
209: }
210: } else {
211: keys = null;
212: }
213:
214: if (in.readBoolean()) {
215: key = KeyStroke.getKeyStroke(in.readInt(),
216: in.readInt(), in.readBoolean());
217: } else {
218: key = null;
219: }
220:
221: actionName = (String) in.readObject();
222:
223: } else { // compatibility mode, settings in old format
224: keys = (KeyStroke[]) obj;
225: key = (KeyStroke) in.readObject();
226: actionName = (String) in.readObject();
227: }
228: }
229:
230: public void writeExternal(java.io.ObjectOutput out)
231: throws java.io.IOException {
232:
233: if (keys != null) {
234: out.writeObject(new Integer(keys.length));
235: for (int i = 0; i < keys.length; i++) {
236: out.writeInt(keys[i].getKeyCode());
237: out.writeInt(keys[i].getModifiers());
238: out.writeBoolean(keys[i].isOnKeyRelease());
239: }
240: } else {
241: out.writeObject(new Integer(-1));
242: }
243:
244: if (key != null) {
245: out.writeBoolean(true);
246: out.writeInt(key.getKeyCode());
247: out.writeInt(key.getModifiers());
248: out.writeBoolean(key.isOnKeyRelease());
249: } else {
250: out.writeBoolean(false);
251: }
252: out.writeObject(actionName);
253: }
254:
255: public String toString() {
256: if (keys == null) {
257: return "key=" + key + ", actionName=" + actionName; // NOI18N
258: } else {
259: StringBuffer sb = new StringBuffer();
260: for (int i = 0; i < keys.length; i++) {
261: sb.append("key"); // NOI18N
262: sb.append(i);
263: sb.append('=');
264: sb.append(keys[i]);
265: sb.append(", "); // NOI18N
266: }
267: sb.append("actionName="); // NOI18N
268: sb.append(actionName);
269: return sb.toString();
270: }
271: }
272:
273: }
|