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