/*
* This file is part of Spoutcraft.
*
* Copyright (c) 2011 SpoutcraftDev <http://spoutcraft.org/>
* Spoutcraft is licensed under the GNU Lesser General Public License.
*
* Spoutcraft is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Spoutcraft is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.spoutcraft.client.controls;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.introspector.BeanAccess;
import org.lwjgl.input.Keyboard;
import net.minecraft.src.Block;
import net.minecraft.src.GuiChat;
import net.minecraft.src.GuiScreen;
import org.spoutcraft.api.Spoutcraft;
import org.spoutcraft.api.gui.ScreenType;
import org.spoutcraft.api.keyboard.AbstractBinding;
import org.spoutcraft.api.keyboard.KeyBinding;
import org.spoutcraft.api.keyboard.KeyBindingManager;
import org.spoutcraft.api.keyboard.KeyBindingPress;
import org.spoutcraft.client.SpoutClient;
import org.spoutcraft.client.gui.controls.GuiControls;
import org.spoutcraft.client.io.FileUtil;
import org.spoutcraft.client.packet.PacketKeyBinding;
public class SimpleKeyBindingManager implements KeyBindingManager {
private ArrayList<KeyBinding> bindings;
private ArrayList<Shortcut> shortcuts = new ArrayList<Shortcut>();
private HashMap<Integer, ArrayList<AbstractBinding>> bindingsForKey = new HashMap<Integer, ArrayList<AbstractBinding>>();
public static final int MOUSE_OFFSET = -100;
public SimpleKeyBindingManager() {
}
public void registerControl(KeyBinding binding) {
KeyBinding result = null;
for (KeyBinding check:bindings) {
if (check.getId().equals(binding.getId()) && check.getAddonName().equals(binding.getAddonName())) {
result = check;
}
}
if (result != null) {
result.takeChanges(binding);
} else {
bindings.add(binding);
}
updateBindings();
save();
}
public void registerShortcut(Shortcut shortcut) {
shortcuts.add(shortcut);
updateBindings();
save();
}
public void unregisterShortcut(Shortcut shortcut) {
if (shortcut == null) {
return;
}
shortcuts.remove(shortcut);
if (bindingsForKey.get(shortcut.getKey()) == null) {
return;
}
bindingsForKey.get(shortcut.getKey()).remove(shortcut);
save();
}
public void unregisterControl(KeyBinding binding) {
if (binding == null) {
return;
}
bindings.remove(binding);
if (bindingsForKey.get(binding.getKey()) == null) {
return;
}
bindingsForKey.get(binding.getKey()).remove(binding);
save();
}
public void updateBindings() {
if (bindings == null) {
bindings = new ArrayList<KeyBinding>();
}
if (shortcuts == null) {
shortcuts = new ArrayList<Shortcut>();
}
bindingsForKey.clear();
for (KeyBinding binding:bindings) {
ArrayList<AbstractBinding> bindings = bindingsForKey.get(binding.getKey());
if (bindings == null) {
bindings = new ArrayList<AbstractBinding>();
bindingsForKey.put(binding.getKey(), bindings);
}
bindings.add(binding);
}
for (Shortcut binding:shortcuts) {
ArrayList<AbstractBinding> bindings = bindingsForKey.get(binding.getKey());
if (bindings == null) {
bindings = new ArrayList<AbstractBinding>();
bindingsForKey.put(binding.getKey(), bindings);
}
bindings.add(binding);
}
}
public void save() {
Yaml yaml = new Yaml();
yaml.setBeanAccess(BeanAccess.FIELD); // To ignore transient fields
try {
// KeyBindings saving
FileWriter writer = new FileWriter(getBindingsFile());
ArrayList<Object> kbsave = new ArrayList<Object>();
for (KeyBinding binding:bindings) {
HashMap<String, Object> item = new HashMap<String, Object>();
item.put("key", binding.getKey());
item.put("id", binding.getId());
item.put("description", binding.getDescription());
item.put("addonName", binding.getAddonName());
item.put("modifiers", binding.getModifiers());
kbsave.add(item);
}
yaml.dump(kbsave, writer);
// Shortcuts saving
writer = new FileWriter(getShortcutsFile());
ArrayList<Object> shsave = new ArrayList<Object>();
for (Shortcut sh:shortcuts) {
HashMap<String, Object> item = new HashMap<String, Object>();
item.put("title", sh.getTitle());
item.put("key", sh.getKey());
item.put("modifiers", sh.getModifiers());
item.put("commands", sh.getCommands());
item.put("delay", sh.getDelay());
shsave.add(item);
}
yaml.dump(shsave, writer);
} catch (IOException e) {
e.printStackTrace();
}
}
private File getBindingsFile() throws IOException {
File file = new File(FileUtil.getConfigDir(), "bindings.yml");
if (!file.exists()) {
file.createNewFile();
}
return file;
}
private File getShortcutsFile() throws IOException {
File file = new File(FileUtil.getConfigDir(), "shortcuts.yml");
if (!file.exists()) {
file.createNewFile();
}
return file;
}
@SuppressWarnings("unchecked")
public void load() {
Yaml yaml = new Yaml();
try {
bindings = new ArrayList<KeyBinding>();
ArrayList<Object> kbsave = yaml.loadAs(new FileReader(getBindingsFile()), ArrayList.class);
if (kbsave == null) {
kbsave = new ArrayList<Object>();
}
for (Object obj:kbsave) {
HashMap<String, Object> item = (HashMap<String, Object>) obj;
int key = (Integer) item.get("key");
String id, description, addonName;
id = (String) item.get("id");
description = (String) item.get("description");
byte modifiers = 0;
if (item.containsKey("modifiers")) {
modifiers = (byte)(int)(Integer) item.get("modifiers");
}
if (item.containsKey("addonName")) {
addonName = (String) item.get("addonName");
} else if (item.containsKey("plugin")) {
addonName = (String) item.get("plugin");
} else {
continue; // Invalid item
}
KeyBinding binding = new KeyBinding(key, addonName, id, description);
binding.setRawModifiers(modifiers);
bindings.add(binding);
}
} catch (Exception e) {
e.printStackTrace();
bindings = new ArrayList<KeyBinding>();
}
try {
shortcuts.clear();
ArrayList<Object> shsave = yaml.loadAs(new FileReader(getShortcutsFile()), ArrayList.class);
if (shsave == null) {
shsave = new ArrayList<Object>();
}
for (Object obj:shsave) {
HashMap<String, Object> item = (HashMap<String, Object>) obj;
Shortcut sh = new Shortcut();
sh.setTitle((String)item.get("title"));
sh.setKey((Integer)item.get("key"));
sh.setCommands((ArrayList<String>)item.get("commands"));
if (item.containsKey("modifiers")) {
sh.setRawModifiers((byte)(int)(Integer)item.get("modifiers"));
}
if (item.containsKey("delay")) {
sh.setDelay((Integer) item.get("delay"));
}
shortcuts.add(sh);
}
} catch (Exception e) {
e.printStackTrace();
shortcuts = new ArrayList<Shortcut>();
}
updateBindings();
}
public void pressKey(int key, boolean keyPressed, int screen) {
if (SpoutClient.getHandle().currentScreen instanceof GuiAmbigousInput || SpoutClient.getHandle().currentScreen instanceof GuiControls) {
return;
}
if (bindingsForKey.containsKey(key)) {
ArrayList<AbstractBinding> bindings = bindingsForKey.get(key);
ArrayList<AbstractBinding> effective = new ArrayList<AbstractBinding>();
for (AbstractBinding b:bindings) {
if (b.matches(key, getPressedModifiers())) {
effective.add(b);
}
}
if (effective.size() == 0) {
return;
} else if (effective.size() == 1) {
effective.iterator().next().summon(key, !keyPressed, screen);
} else if (screen == 0 || (getPressedModifiers() != 0 && getPressedModifiers() != AbstractBinding.MOD_SHIFT)) {
GuiScreen parent = SpoutClient.getHandle().currentScreen;
SpoutClient.getHandle().displayGuiScreen(new GuiAmbigousInput(effective, parent));
} else {
GuiScreen parent = SpoutClient.getHandle().currentScreen;
if (!(parent instanceof GuiChat)) {
Spoutcraft.getActivePlayer().showAchievement("Multiple Bindings ...", "are assigned to Key " + Keyboard.getKeyName(key), Block.workbench.blockID);
}
}
}
}
public static boolean isModifierKey(int key) {
if (key == Keyboard.KEY_LSHIFT
|| key == Keyboard.KEY_RSHIFT
|| key == Keyboard.KEY_LMENU
|| key == Keyboard.KEY_RMENU
|| key == Keyboard.KEY_LCONTROL
|| key == Keyboard.KEY_RCONTROL
|| key == Keyboard.KEY_LMETA
|| key == Keyboard.KEY_RMETA) {
return true;
}
return false;
}
public List<KeyBinding> getAllBindings() {
return Collections.unmodifiableList(bindings);
}
public List<Shortcut> getAllShortcuts() {
return Collections.unmodifiableList(shortcuts);
}
public static void setModifiersToShortcut(Shortcut sh) {
if (Keyboard.isKeyDown(Keyboard.KEY_LSHIFT) || Keyboard.isKeyDown(Keyboard.KEY_RSHIFT)) {
sh.setModifier(AbstractBinding.MOD_SHIFT, true);
}
if (Keyboard.isKeyDown(Keyboard.KEY_LCONTROL) || Keyboard.isKeyDown(Keyboard.KEY_RCONTROL)) {
sh.setModifier(AbstractBinding.MOD_CTRL, true);
}
if (Keyboard.isKeyDown(Keyboard.KEY_LMENU) || Keyboard.isKeyDown(Keyboard.KEY_RMENU)) {
sh.setModifier(AbstractBinding.MOD_ALT, true);
}
if (Keyboard.isKeyDown(Keyboard.KEY_LMETA) || Keyboard.isKeyDown(Keyboard.KEY_RMETA)) {
sh.setModifier(AbstractBinding.MOD_SUPER, true);
}
}
public static byte getPressedModifiers() {
byte res = 0;
if (Keyboard.isKeyDown(Keyboard.KEY_LSHIFT) || Keyboard.isKeyDown(Keyboard.KEY_RSHIFT)) {
res|=Shortcut.MOD_SHIFT;
}
if (Keyboard.isKeyDown(Keyboard.KEY_LCONTROL) || Keyboard.isKeyDown(Keyboard.KEY_RCONTROL)) {
res|=Shortcut.MOD_CTRL;
}
if (Keyboard.isKeyDown(Keyboard.KEY_LMENU) || Keyboard.isKeyDown(Keyboard.KEY_RMENU)) {
res|=Shortcut.MOD_ALT;
}
if (Keyboard.isKeyDown(Keyboard.KEY_LMETA) || Keyboard.isKeyDown(Keyboard.KEY_RMETA)) {
res|=Shortcut.MOD_SUPER;
}
return res;
}
public void summon(KeyBinding binding, int key, boolean keyReleased, int screen) {
if (binding.getDelegate() == null && binding.getUniqueId() != null) { // Server-side
SpoutClient.getInstance().getPacketManager().sendSpoutPacket(new PacketKeyBinding(binding, key, !keyReleased, screen));
} else if (binding.getDelegate() != null) { // Client-side
KeyBindingPress event = new KeyBindingPress(org.spoutcraft.api.gui.Keyboard.getKey(key), binding, ScreenType.getType(screen));
if (!keyReleased) {
binding.getDelegate().onKeyPress(event);
} else {
binding.getDelegate().onKeyRelease(event);
}
}
}
}