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    }