001 package cpw.mods.fml.client.registry;
002
003 import java.util.ArrayList;
004 import java.util.EnumSet;
005 import java.util.Set;
006
007 import net.minecraft.client.settings.GameSettings;
008 import net.minecraft.client.settings.KeyBinding;
009
010 import org.lwjgl.input.Keyboard;
011 import org.lwjgl.input.Mouse;
012
013 import com.google.common.collect.Lists;
014 import com.google.common.collect.Sets;
015
016 import cpw.mods.fml.common.ITickHandler;
017 import cpw.mods.fml.common.TickType;
018 import cpw.mods.fml.common.registry.TickRegistry;
019 import cpw.mods.fml.relauncher.Side;
020
021 public class KeyBindingRegistry
022 {
023 /**
024 * Register a KeyHandler to the game. This handler will be called on certain tick events
025 * if any of its key is inactive or has recently changed state
026 *
027 * @param handler
028 */
029 public static void registerKeyBinding(KeyHandler handler) {
030 instance().keyHandlers.add(handler);
031 if (!handler.isDummy)
032 {
033 TickRegistry.registerTickHandler(handler, Side.CLIENT);
034 }
035 }
036
037
038 /**
039 * Extend this class to register a KeyBinding and recieve callback
040 * when the key binding is triggered
041 *
042 * @author cpw
043 *
044 */
045 public static abstract class KeyHandler implements ITickHandler
046 {
047 protected KeyBinding[] keyBindings;
048 protected boolean[] keyDown;
049 protected boolean[] repeatings;
050 private boolean isDummy;
051
052 /**
053 * Pass an array of keybindings and a repeat flag for each one
054 *
055 * @param keyBindings
056 * @param repeatings
057 */
058 public KeyHandler(KeyBinding[] keyBindings, boolean[] repeatings)
059 {
060 assert keyBindings.length == repeatings.length : "You need to pass two arrays of identical length";
061 this.keyBindings = keyBindings;
062 this.repeatings = repeatings;
063 this.keyDown = new boolean[keyBindings.length];
064 }
065
066
067 /**
068 * Register the keys into the system. You will do your own keyboard management elsewhere. No events will fire
069 * if you use this method
070 *
071 * @param keyBindings
072 */
073 public KeyHandler(KeyBinding[] keyBindings)
074 {
075 this.keyBindings = keyBindings;
076 this.isDummy = true;
077 }
078
079 public KeyBinding[] getKeyBindings()
080 {
081 return this.keyBindings;
082 }
083
084 /**
085 * Not to be overridden - KeyBindings are tickhandlers under the covers
086 */
087 @Override
088 public final void tickStart(EnumSet<TickType> type, Object... tickData)
089 {
090 keyTick(type, false);
091 }
092
093 /**
094 * Not to be overridden - KeyBindings are tickhandlers under the covers
095 */
096 @Override
097 public final void tickEnd(EnumSet<TickType> type, Object... tickData)
098 {
099 keyTick(type, true);
100 }
101
102 private void keyTick(EnumSet<TickType> type, boolean tickEnd)
103 {
104 for (int i = 0; i < keyBindings.length; i++)
105 {
106 KeyBinding keyBinding = keyBindings[i];
107 int keyCode = keyBinding.keyCode;
108 boolean state = (keyCode < 0 ? Mouse.isButtonDown(keyCode + 100) : Keyboard.isKeyDown(keyCode));
109 if (state != keyDown[i] || (state && repeatings[i]))
110 {
111 if (state)
112 {
113 keyDown(type, keyBinding, tickEnd, state!=keyDown[i]);
114 }
115 else
116 {
117 keyUp(type, keyBinding, tickEnd);
118 }
119 if (tickEnd)
120 {
121 keyDown[i] = state;
122 }
123 }
124
125 }
126 }
127
128 /**
129 * Called when the key is first in the down position on any tick from the {@link #ticks()}
130 * set. Will be called subsequently with isRepeat set to true
131 *
132 * @see #keyUp(EnumSet, KeyBinding, boolean)
133 *
134 * @param types the type(s) of tick that fired when this key was first down
135 * @param tickEnd was it an end or start tick which fired the key
136 * @param isRepeat is it a repeat key event
137 */
138 public abstract void keyDown(EnumSet<TickType> types, KeyBinding kb, boolean tickEnd, boolean isRepeat);
139 /**
140 * Fired once when the key changes state from down to up
141 *
142 * @see #keyDown(EnumSet, KeyBinding, boolean, boolean)
143 *
144 * @param types the type(s) of tick that fired when this key was first down
145 * @param tickEnd was it an end or start tick which fired the key
146 */
147 public abstract void keyUp(EnumSet<TickType> types, KeyBinding kb, boolean tickEnd);
148
149
150 /**
151 * This is the list of ticks for which the key binding should trigger. The only
152 * valid ticks are client side ticks, obviously.
153 *
154 * @see cpw.mods.fml.common.ITickHandler#ticks()
155 */
156 public abstract EnumSet<TickType> ticks();
157 }
158
159 private static final KeyBindingRegistry INSTANCE = new KeyBindingRegistry();
160
161 private Set<KeyHandler> keyHandlers = Sets.newLinkedHashSet();
162
163 @Deprecated
164 public static KeyBindingRegistry instance()
165 {
166 return INSTANCE;
167 }
168
169
170 public void uploadKeyBindingsToGame(GameSettings settings)
171 {
172 ArrayList<KeyBinding> harvestedBindings = Lists.newArrayList();
173 for (KeyHandler key : keyHandlers)
174 {
175 for (KeyBinding kb : key.keyBindings)
176 {
177 harvestedBindings.add(kb);
178 }
179 }
180 KeyBinding[] modKeyBindings = harvestedBindings.toArray(new KeyBinding[harvestedBindings.size()]);
181 KeyBinding[] allKeys = new KeyBinding[settings.keyBindings.length + modKeyBindings.length];
182 System.arraycopy(settings.keyBindings, 0, allKeys, 0, settings.keyBindings.length);
183 System.arraycopy(modKeyBindings, 0, allKeys, settings.keyBindings.length, modKeyBindings.length);
184 settings.keyBindings = allKeys;
185 settings.loadOptions();
186 }
187 }