package com.ts3client.ui; import com.ts3client.hotkey.GlobalInputHook; import com.ts3client.hotkey.Hotkey; import com.ts3client.hotkey.HotkeyAction; import com.ts3client.hotkey.HotkeyCombo; import com.ts3client.hotkey.HotkeyEngine; import com.ts3client.hotkey.HotkeyKey; import com.ts3client.hotkey.Hotkeys; import com.ts3client.hotkey.desktop.DesktopInputHooks; import java.util.List; /** * Owns the hotkey machinery for the UI: the stored bindings, the matching engine and * the platform input hook they are fed from. Everything the dialogs need — recording a * combination, naming keys, telling the user why hotkeys are dead — goes through here. */ final class HotkeyService { private final Hotkeys hotkeys = Hotkeys.load(); private final HotkeyEngine engine; private final GlobalInputHook hook; private final HotkeyArguments arguments; HotkeyService(HotkeyEngine.Handler handler, HotkeyArguments arguments) { this.arguments = arguments; engine = new HotkeyEngine(hotkeys, handler); hook = DesktopInputHooks.start(engine); } /** The values this action's parameter can take right now, for the action tree. */ List argumentChoices(HotkeyAction action) { if (arguments == null || action.argument() == HotkeyAction.Argument.NONE) return List.of(); try { return arguments.choices(action); } catch (RuntimeException e) { return List.of(); } } List all() { return hotkeys.all(); } void replaceAll(List updated) { engine.releaseAll(); hotkeys.replaceAll(updated); hotkeys.save(); } /** The binding for an action with no argument, or {@code null} when unbound. */ Hotkey find(HotkeyAction action) { synchronized (hotkeys.all()) { for (Hotkey h : hotkeys.all()) { if (h.action == action) return h; } } return null; } boolean isRunning() { return hook.isRunning(); } /** One line for the options dialog: either working, or why it is not. */ String status() { return hook.isRunning() ? "Global hotkeys are active." : "Global hotkeys are unavailable (" + hook.unavailableReason() + ")."; } String display(HotkeyCombo combo) { return combo == null ? "No hotkey assigned" : combo.display(this::keyName); } private String keyName(HotkeyKey key) { return hook.keyName(key); } /** Captures the next combination instead of firing bindings; see {@link #stopRecording()}. */ void record(HotkeyEngine.Recorder recorder) { engine.record(recorder); } void stopRecording() { engine.stopRecording(); } void dispose() { engine.releaseAll(); hook.close(); } }