/* * Copyright 2017 Laszlo Balazs-Csiki * * This file is part of Pixelitor. Pixelitor is free software: you * can redistribute it and/or modify it under the terms of the GNU * General Public License, version 3 as published by the Free * Software Foundation. * * Pixelitor 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Pixelitor. If not, see <http://www.gnu.org/licenses/>. */ package pixelitor.gui; import pixelitor.menus.view.ShowHideAllAction; import pixelitor.tools.ArrowKey; import pixelitor.tools.KeyboardObserver; import pixelitor.tools.Tools; import javax.swing.*; import java.awt.AWTEvent; import java.awt.KeyboardFocusManager; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; /** * A global listener for keyboard events */ public class GlobalKeyboardWatch { private static boolean spaceDown = false; private static boolean dialogActive = false; private static JComponent alwaysVisibleComponent; private static KeyboardObserver observer; private GlobalKeyboardWatch() { } public static void init() { // tab is the focus traversal key, it must be handled before it gets consumed KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(e -> { int id = e.getID(); if (id == KeyEvent.KEY_PRESSED) { keyPressed(e); } else if (id == KeyEvent.KEY_RELEASED) { keyReleased(e); } return false; }); } private static void keyPressed(KeyEvent e) { int keyCode = e.getKeyCode(); switch (keyCode) { case KeyEvent.VK_TAB: if (!dialogActive) { ShowHideAllAction.INSTANCE.actionPerformed(null); } break; case KeyEvent.VK_SPACE: if (!dialogActive) { observer.spacePressed(); spaceDown = true; e.consume(); } break; case KeyEvent.VK_RIGHT: case KeyEvent.VK_KP_RIGHT: // checking for VK_KP_RIGHT and other KP keys does not seem to be necessary // because at least on windows actually VK_RIGHT is sent by the keypad keys // but let's check them in order to be on the safe side if (!dialogActive && observer.arrowKeyPressed(new ArrowKey.RIGHT(e.isShiftDown()))) { e.consume(); } break; case KeyEvent.VK_LEFT: case KeyEvent.VK_KP_LEFT: if (!dialogActive && observer.arrowKeyPressed(new ArrowKey.LEFT(e.isShiftDown()))) { e.consume(); } break; case KeyEvent.VK_UP: case KeyEvent.VK_KP_UP: if (!dialogActive && observer.arrowKeyPressed(new ArrowKey.UP(e.isShiftDown()))) { e.consume(); } break; case KeyEvent.VK_DOWN: case KeyEvent.VK_KP_DOWN: if (!dialogActive && observer.arrowKeyPressed(new ArrowKey.DOWN(e.isShiftDown()))) { e.consume(); } break; case KeyEvent.VK_ESCAPE: if (!dialogActive) { observer.escPressed(); } break; case KeyEvent.VK_ALT: if (!dialogActive) { observer.altPressed(); } break; } } private static void keyReleased(KeyEvent e) { int keyCode = e.getKeyCode(); switch (keyCode) { case KeyEvent.VK_SPACE: observer.spaceReleased(); spaceDown = false; break; case KeyEvent.VK_ALT: if (!dialogActive) { observer.altReleased(); } break; } } public static boolean isSpaceDown() { return spaceDown; } /** * The idea is that when we are in a dialog, we want to use the Tab * key for navigating the UI, and not for "Hide All" */ public static void setDialogActive(boolean dialogActive) { GlobalKeyboardWatch.dialogActive = dialogActive; } public static void setAlwaysVisibleComponent(JComponent alwaysVisibleComponent) { GlobalKeyboardWatch.alwaysVisibleComponent = alwaysVisibleComponent; } public static void addKeyboardShortCut(char activationChar, boolean caseInsensitive, String actionMapKey, Action action) { InputMap inputMap = alwaysVisibleComponent.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW); if (caseInsensitive) { char activationLC = Character.toLowerCase(activationChar); char activationUC = Character.toUpperCase(activationChar); inputMap.put(KeyStroke.getKeyStroke(activationLC), actionMapKey); inputMap.put(KeyStroke.getKeyStroke(activationUC), actionMapKey); } else { inputMap.put(KeyStroke.getKeyStroke(activationChar), actionMapKey); } alwaysVisibleComponent.getActionMap().put(actionMapKey, action); } public static void addKeyboardShortCut(KeyStroke keyStroke, String actionMapKey, Action action) { InputMap inputMap = alwaysVisibleComponent.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW); inputMap.put(keyStroke, actionMapKey); alwaysVisibleComponent.getActionMap().put(actionMapKey, action); } public static void registerBrushSizeActions() { Action increaseActiveBrushSizeAction = new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { Tools.increaseActiveBrushSize(); } }; Action decreaseActiveBrushSizeAction = new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { Tools.decreaseActiveBrushSize(); } }; GlobalKeyboardWatch.addKeyboardShortCut(']', false, "increment", increaseActiveBrushSizeAction); GlobalKeyboardWatch.addKeyboardShortCut('[', false, "decrement", decreaseActiveBrushSizeAction); } public static void registerDebugMouseWatching() { Toolkit.getDefaultToolkit().addAWTEventListener(event -> { MouseEvent m = (MouseEvent) event; String compClass = m.getComponent().getClass().getName(); if (m.getID() == MouseEvent.MOUSE_CLICKED) { System.out.println("GlobalKeyboardWatch:MOUSE_CLICKED x = " + m.getX() + ", y = " + m.getY() + ", click count = " + m.getClickCount() + ", comp class = " + compClass); } else if (m.getID() == MouseEvent.MOUSE_DRAGGED) { System.out.println("GlobalKeyboardWatch:MOUSE_DRAGGED x = " + m.getX() + ", y = " + m.getY() + ", comp class = " + compClass); } else if (m.getID() == MouseEvent.MOUSE_PRESSED) { System.out.println("GlobalKeyboardWatch:MOUSE_PRESSED x = " + m.getX() + ", y = " + m.getY() + ", comp class = " + compClass); } else if (m.getID() == MouseEvent.MOUSE_RELEASED) { System.out.println("GlobalKeyboardWatch:MOUSE_RELEASED x = " + m.getX() + ", y = " + m.getY() + ", comp class = " + compClass); } }, AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK); } // TODO this kind of global listening might be better // public static void registerMouseWheelWatching() { // Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() { // @Override // public void eventDispatched(AWTEvent e) { // } // }, AWTEvent.MOUSE_WHEEL_EVENT_MASK); // } public static void setObserver(KeyboardObserver observer) { GlobalKeyboardWatch.observer = observer; } }