    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    }