package org.jdesktop.swingx.plaf.basic; import static java.awt.event.KeyEvent.VK_CAPS_LOCK; import java.awt.KeyEventDispatcher; import java.awt.KeyboardFocusManager; import java.awt.Toolkit; import java.awt.event.KeyEvent; import org.jdesktop.beans.AbstractBean; /** * A class for determining the state of the {@link java.awt.event.KeyEvent.VK_CAPS_LOCK CAPS LOCK * key}. It also supports notification when the locking state changes. * <p> * Although it is possible to use {@link Toolkit#getLockingKeyState(int)} to determine the current * state of the CAPS LOCK key, that method is not guaranteed to work on all platforms. This class * attempts to handle those shortfalls and provide an easy mechanism for listening to state changes. * * <pre> * CapsLockSupport cls = CapsLockSupport.getInstance(); * // for get the current state of the caps lock key * boolean currentState = cls.isCapsLockEnabled(); * // for listening to changes in the caps lock state * cls.addPropertyChangeListener("capsLockEnabled", myListener); * </pre> * * There is one special case to be aware of. If {@code CapsLockSupport} is not able to determine the * state of the CAPS LOCK key, then {@link #isInitialized()} will return {@code false} until it is * able to introspect a {@link KeyEvent} and determine the current locking state. If * {@code CapsLockSupport} must use delayed initialization, it will fire a property change to notify * listeners that it is now in an accurate state. * * @author kschaefer */ public final class CapsLockSupport extends AbstractBean implements KeyEventDispatcher { private boolean useToolkit; private boolean capsLockeEnabled; private boolean updateViaKeyEvent; private static class SingletonHolder { private static final CapsLockSupport INSTANCE = new CapsLockSupport(); static { KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(INSTANCE); } } private CapsLockSupport() { try { capsLockeEnabled = Toolkit.getDefaultToolkit().getLockingKeyState(VK_CAPS_LOCK); useToolkit = true; updateViaKeyEvent = false; } catch (UnsupportedOperationException e) { capsLockeEnabled = false; useToolkit = false; updateViaKeyEvent = true; } } /** * Gets the only instance of {@code CapsLockSupport}. * * @return the {@code CapsLockSupport} instance */ public static CapsLockSupport getInstance() { return SingletonHolder.INSTANCE; } /** * Determines if {@code CapsLockSupport} is accurately synchronized with the state of the CAPS * LOCK key. When not initialized, {@link #isCapsLockEnabled()} will always return {@code false} * . {@code CapsLockSupport} will fail to initialize only if * {@code Toolkit#getLockingKeyState(int)} throws an exception; in that case, it will initialize * as soon as it receives a valid key event (that can be used to determine the current locking * state). * * @return {@code true} if {@code CapsLockSupport} accurately knows the state of the CAPS LOCK * key */ public boolean isInitialized() { return useToolkit || (useToolkit ^ updateViaKeyEvent); } /** * Determines the current state of the {@link java.awt.event.KeyEvent.VK_CAPS_LOCK CAPS LOCK key}. * * @return {@code true} if CAPS LOCK is enabled; {@code false} otherwise */ public boolean isCapsLockEnabled() { if (useToolkit) { try { return Toolkit.getDefaultToolkit().getLockingKeyState(VK_CAPS_LOCK); } catch (UnsupportedOperationException shouldNeverHappen) { return capsLockeEnabled; } } return capsLockeEnabled; } void setCapsLockEnabled(boolean capsLockEnabled) { boolean oldValue = this.capsLockeEnabled; this.capsLockeEnabled = capsLockEnabled; firePropertyChange("capsLockEnabled", oldValue, this.capsLockeEnabled); //$NON-NLS-1$ } // updateViaKeyEvent is use to find the initial state of the CAPS LOCK key when the Toolkit does // not support it /** * This is an implementation detail and should not be considered public. */ @Override public boolean dispatchKeyEvent(KeyEvent e) { if (e.getID() == KeyEvent.KEY_PRESSED) { int keyCode = e.getKeyCode(); if (keyCode == VK_CAPS_LOCK) { if (!updateViaKeyEvent) { if (useToolkit) { try { setCapsLockEnabled(Toolkit.getDefaultToolkit().getLockingKeyState(VK_CAPS_LOCK)); } catch (UnsupportedOperationException shouldNeverHappen) { setCapsLockEnabled(!capsLockeEnabled); } } else { setCapsLockEnabled(!capsLockeEnabled); } } } else if (updateViaKeyEvent && Character.isLetter(keyCode)) { if (keyCode == e.getKeyChar()) { capsLockeEnabled = !e.isShiftDown(); } else { capsLockeEnabled = e.isShiftDown(); } updateViaKeyEvent = false; firePropertyChange("initialized", false, true); //$NON-NLS-1$ } } return false; } }