package games.rednblack.editor.utils; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Input; import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.Json; import com.badlogic.gdx.utils.ObjectMap; import games.rednblack.editor.proxy.SettingsManager; import games.rednblack.puremvc.Facade; import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.SystemUtils; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Comparator; import java.util.List; public class KeyBindingsLayout { public static final int NEW_PROJECT = 0; public static final int OPEN_PROJECT = 1; public static final int SAVE_PROJECT = 2; public static final int EXPORT_PROJECT = 3; public static final int IMPORT_TO_LIBRARY = 4; public static final int OPEN_SETTINGS = 5; public static final int EXIT_APP = 6; public static final int SELECTION_TOOL = 7; public static final int TRANSFORM_TOOL = 8; public static final int PAN_TOOL = 9; public static final int ZOOM_PLUS = 10; public static final int ZOOM_MINUS = 11; public static final int Z_INDEX_UP = 12; public static final int Z_INDEX_DOWN = 13; public static final int SELECT_ALL = 14; public static final int COPY = 15; public static final int CUT = 16; public static final int PASTE = 17; public static final int UNDO = 18; public static final int REDO = 19; public static final int RESET_CAMERA = 20; public static final int ALIGN_LEFT = 21; public static final int ALIGN_TOP = 22; public static final int ALIGN_RIGHT = 23; public static final int ALIGN_BOTTOM = 24; public static final int DELETE = 25; public static final int HIDE_GUI = 26; public static final int OPEN_CONSOLE = 27; public static final int SAVE_PROJECT_AS = 28; public static final int TOGGLE_FULL_SCREEN = 29; public static final int SHOW_MINI_MAP = 30; private static final ObjectMap defaultMapper = new ObjectMap<>(); static { defaultMapper.put(NEW_PROJECT, new KeyMapper(NEW_PROJECT, true, false, false, Input.Keys.N)); defaultMapper.put(OPEN_PROJECT, new KeyMapper(OPEN_PROJECT, true, false, false, Input.Keys.O)); defaultMapper.put(SAVE_PROJECT, new KeyMapper(SAVE_PROJECT, true, false, false, Input.Keys.S)); defaultMapper.put(SAVE_PROJECT_AS, new KeyMapper(SAVE_PROJECT_AS, true, false, true, Input.Keys.S)); defaultMapper.put(EXPORT_PROJECT, new KeyMapper(EXPORT_PROJECT, true, false, false, Input.Keys.E)); defaultMapper.put(IMPORT_TO_LIBRARY, new KeyMapper(IMPORT_TO_LIBRARY, true, false, false, Input.Keys.I)); defaultMapper.put(OPEN_SETTINGS, new KeyMapper(OPEN_SETTINGS, true, true, false, Input.Keys.S)); defaultMapper.put(EXIT_APP, new KeyMapper(EXIT_APP, true, false, false, Input.Keys.Q)); defaultMapper.put(SELECTION_TOOL, new KeyMapper(SELECTION_TOOL, false, false, false, Input.Keys.V)); defaultMapper.put(TRANSFORM_TOOL, new KeyMapper(TRANSFORM_TOOL, true, false, false, Input.Keys.T)); defaultMapper.put(PAN_TOOL, new KeyMapper(PAN_TOOL, false, false, false, Input.Keys.SPACE)); defaultMapper.put(ZOOM_PLUS, new KeyMapper(ZOOM_PLUS, true, false, false, Input.Keys.MINUS, Input.Keys.SLASH)); defaultMapper.put(ZOOM_MINUS, new KeyMapper(ZOOM_MINUS, true, false, false, Input.Keys.PLUS, Input.Keys.RIGHT_BRACKET)); defaultMapper.put(Z_INDEX_UP, new KeyMapper(Z_INDEX_UP, true, false, false, Input.Keys.UP)); defaultMapper.put(Z_INDEX_DOWN, new KeyMapper(Z_INDEX_DOWN, true, false, false, Input.Keys.DOWN)); defaultMapper.put(SELECT_ALL, new KeyMapper(SELECT_ALL, true, false, false, Input.Keys.A)); defaultMapper.put(COPY, new KeyMapper(COPY, true, false, false, Input.Keys.C)); defaultMapper.put(CUT, new KeyMapper(CUT, true, false, false, Input.Keys.X)); defaultMapper.put(PASTE, new KeyMapper(PASTE, true, false, false, Input.Keys.V)); defaultMapper.put(UNDO, new KeyMapper(UNDO, true, false, false, Input.Keys.Z)); defaultMapper.put(REDO, new KeyMapper(REDO, true, false, true, Input.Keys.Z)); defaultMapper.put(RESET_CAMERA, new KeyMapper(RESET_CAMERA, true, false, false, Input.Keys.NUM_0, Input.Keys.NUMPAD_0)); defaultMapper.put(ALIGN_TOP, new KeyMapper(ALIGN_TOP, true, false, false, Input.Keys.NUMPAD_8)); defaultMapper.put(ALIGN_LEFT, new KeyMapper(ALIGN_LEFT, true, false, false, Input.Keys.NUMPAD_4)); defaultMapper.put(ALIGN_BOTTOM, new KeyMapper(ALIGN_BOTTOM, true, false, false, Input.Keys.NUMPAD_2)); defaultMapper.put(ALIGN_RIGHT, new KeyMapper(ALIGN_RIGHT, true, false, false, Input.Keys.NUMPAD_6)); defaultMapper.put(DELETE, new KeyMapper(DELETE, false, false, false, Input.Keys.DEL, Input.Keys.FORWARD_DEL)); defaultMapper.put(HIDE_GUI, new KeyMapper(HIDE_GUI, false, false, false, Input.Keys.F12)); defaultMapper.put(OPEN_CONSOLE, new KeyMapper(OPEN_CONSOLE, false, false, false, Input.Keys.F10)); defaultMapper.put(TOGGLE_FULL_SCREEN, new KeyMapper(TOGGLE_FULL_SCREEN, false, false, false, Input.Keys.F11)); defaultMapper.put(SHOW_MINI_MAP, new KeyMapper(SHOW_MINI_MAP, false, false, false, Input.Keys.M)); } private static final Array mapping = new Array<>(); public static void init() { SettingsManager settingsManager = Facade.getInstance().retrieveProxy(SettingsManager.NAME); if (!settingsManager.editorConfigVO.keyBindingLayout.equals("default")) { String mapPath = HyperLap2DUtils.getKeyMapPath() + File.separator + settingsManager.editorConfigVO.keyBindingLayout + ".keymap"; File mapFile = new File(mapPath); if (mapFile.exists()) { Json json = new Json(); try { String mapJson = FileUtils.readFileToString(mapFile, "utf-8"); ObjectMap customBindings = json.fromJson(ObjectMap.class, mapJson); for (String key : new ObjectMap.Keys<>(customBindings)) { int action = Integer.parseInt(key); defaultMapper.put(action, customBindings.get(key)); } } catch (IOException e) { e.printStackTrace(); } } } for (KeyMapper keyMapper : new ObjectMap.Values<>(defaultMapper)) { mapping.add(keyMapper); } //Sort mapping in order to give priority to shortcuts that has more modifiers mapping.sort(new KeyMapperComparator()); } public static int mapAction(int keyCode) { for (KeyMapper keyMapper : new Array.ArrayIterator<>(mapping)) { if (keyMapper.keyCodes.contains(keyCode)) { if (keyMapper.isControl) { if (SystemUtils.IS_OS_MAC || SystemUtils.IS_OS_MAC_OSX) { if (!Gdx.input.isKeyPressed(Input.Keys.SYM)) continue; } else if (!Gdx.input.isKeyPressed(Input.Keys.CONTROL_LEFT) && !Gdx.input.isKeyPressed(Input.Keys.CONTROL_RIGHT)) { continue; } } if (keyMapper.isShift) { if (!Gdx.input.isKeyPressed(Input.Keys.SHIFT_LEFT) && !Gdx.input.isKeyPressed(Input.Keys.SHIFT_RIGHT)) { continue; } } if (keyMapper.isAlt) { if (!Gdx.input.isKeyPressed(Input.Keys.ALT_LEFT) && !Gdx.input.isKeyPressed(Input.Keys.ALT_RIGHT)) { continue; } } return keyMapper.action; } } return -1; } public static int[] getShortcutList(int action) { for (KeyMapper keyMapper : new Array.ArrayIterator<>(mapping)) { if (keyMapper.action == action) { List shortcut = new ArrayList<>(); if (keyMapper.isControl) { shortcut.add(Input.Keys.CONTROL_LEFT); } if (keyMapper.isAlt) { shortcut.add(Input.Keys.ALT_LEFT); } if (keyMapper.isShift) { shortcut.add(Input.Keys.SHIFT_LEFT); } shortcut.add(keyMapper.keyCodes.get(0)); return ArrayUtils.toPrimitive(shortcut.toArray(new Integer[0])); } } return new int[0]; } private static class KeyMapper { boolean isControl, isAlt, isShift; List keyCodes = new ArrayList<>(); int action; public KeyMapper() { } /** * KeyMapper object to map an action to a key combination * * @param action id of the mapped action * @param isControl true if needs CONTROL_LEFT, CONTROL_RIGHT or SYM pressed * @param isAlt true if needs ALT_LEFT or ALT_RIGHT pressed * @param isShift true if needs SHIFT_LEFT or SHIFT_RIGHT * @param keyCodes use first key as main key code and other for variants */ public KeyMapper(int action, boolean isControl, boolean isAlt, boolean isShift, int... keyCodes) { this.isControl = isControl; this.isShift = isShift; this.isAlt = isAlt; for (int keycode : keyCodes) this.keyCodes.add(keycode); this.action = action; } } private static class KeyMapperComparator implements Comparator { @Override public int compare(KeyMapper o1, KeyMapper o2) { int mod1 = 0, mod2 = 0; if (o1.isControl) mod1++; if (o2.isControl) mod2++; if (o1.isShift) mod1++; if (o2.isShift) mod2++; if (o1.isAlt) mod1++; if (o2.isAlt) mod2++; return mod2 - mod1; } } }