The Battle for Wesnoth  1.15.0+dev
hotkey_command.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2018 by David White <dave@whitevine.net>
3  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 #define GETTEXT_DOMAIN "wesnoth-lib"
16 
18 
19 #include "config.hpp"
20 #include "gettext.hpp"
21 #include "hotkey/hotkey_item.hpp"
22 #include "log.hpp"
23 #include "preferences/general.hpp"
24 
25 #include <array>
26 
27 static lg::log_domain log_config("config");
28 #define ERR_G LOG_STREAM(err, lg::general())
29 #define LOG_G LOG_STREAM(info, lg::general())
30 #define DBG_G LOG_STREAM(debug, lg::general())
31 #define ERR_CF LOG_STREAM(err, log_config)
32 
33 namespace hotkey
34 {
35 namespace
36 {
37 const category_name_map_t category_names {
38  { HKCAT_GENERAL, N_("General") },
39  { HKCAT_SAVING, N_("Saved Games") },
40  { HKCAT_MAP, N_("Map Commands") },
41  { HKCAT_UNITS, N_("Unit Commands") },
42  { HKCAT_CHAT, N_("Player Chat") },
43  { HKCAT_REPLAY, N_("Replay Control") },
44  { HKCAT_WHITEBOARD, N_("Planning Mode") },
45  { HKCAT_SCENARIO, N_("Scenario Editor") },
46  { HKCAT_PALETTE, N_("Editor Palettes") },
47  { HKCAT_TOOLS, N_("Editor Tools") },
48  { HKCAT_CLIPBOARD, N_("Editor Clipboard") },
49  { HKCAT_DEBUG, N_("Debug Commands") },
50  { HKCAT_CUSTOM, N_("Custom WML Commands") },
51 };
52 
53 std::map<HOTKEY_CATEGORY, std::list<HOTKEY_COMMAND>> hotkeys_by_category;
54 
55 // Make them global ?
56 hk_scopes scope_game(1 << SCOPE_GAME);
57 hk_scopes scope_editor(1 << SCOPE_EDITOR);
58 hk_scopes scope_main(1 << SCOPE_MAIN_MENU);
59 
60 //
61 // All static hotkeys.
62 //
63 // This array should always have the same number of entries as the HOTKEY_COMMAND enum.
64 // Since HOTKEY_NULL is the last entry in said enum, we can use its index to dynamically
65 // size the array.
66 //
67 std::array<hotkey_command_temp, HOTKEY_NULL - 1> master_hotkey_list {{
68  { HOTKEY_SCROLL_UP, "scroll-up", N_("Scroll Up"), false, scope_game | scope_editor, HKCAT_GENERAL, "" },
69  { HOTKEY_SCROLL_DOWN, "scroll-down", N_("Scroll Down"), false, scope_game | scope_editor, HKCAT_GENERAL, "" },
70  { HOTKEY_SCROLL_LEFT, "scroll-left", N_("Scroll Left"), false, scope_game | scope_editor, HKCAT_GENERAL, "" },
71  { HOTKEY_SCROLL_RIGHT, "scroll-right", N_("Scroll Right"), false, scope_game | scope_editor, HKCAT_GENERAL, "" },
72 
73  { HOTKEY_CANCEL, N_("cancel"), N_("Cancel"), false, scope_game | scope_editor | scope_main, HKCAT_GENERAL, "" },
74  { HOTKEY_SELECT_HEX, "selecthex", N_("Select Hex"), false, scope_game, HKCAT_MAP, "" },
75  { HOTKEY_DESELECT_HEX, "deselecthex", N_("Deselect Hex"), false, scope_game, HKCAT_MAP, "" },
76  { HOTKEY_MOVE_ACTION, "moveaction", N_("Move/Attack"), false, scope_game, HKCAT_UNITS, "" },
77  { HOTKEY_SELECT_AND_ACTION, "selectmoveaction", N_("Select/Move/Attack"), false, scope_game, HKCAT_UNITS, "" },
78  { HOTKEY_TOUCH_HEX, "touchhex", N_("Touch"), false, scope_game, HKCAT_UNITS, "" },
79  { HOTKEY_ANIMATE_MAP, "animatemap", N_("Animate Map"), false, scope_game | scope_editor, HKCAT_MAP, "" },
80  { HOTKEY_CYCLE_UNITS, "cycle", N_("Next Unit"), false, scope_game, HKCAT_UNITS, "" },
81  { HOTKEY_CYCLE_BACK_UNITS, "cycleback", N_("Previous Unit"), false, scope_game, HKCAT_UNITS, "" },
82  { HOTKEY_UNIT_HOLD_POSITION, "holdposition", N_("Hold Position"), false, scope_game, HKCAT_UNITS, "" },
83  { HOTKEY_END_UNIT_TURN, "endunitturn", N_("End Unit Turn"), false, scope_game, HKCAT_UNITS, "" },
84  { HOTKEY_LEADER, "leader", N_("Leader"), false, scope_game, HKCAT_UNITS, "" },
85  { HOTKEY_UNDO, "undo", N_("Undo"), false, scope_game | scope_editor, HKCAT_GENERAL, "" },
86  { HOTKEY_REDO, "redo", N_("Redo"), false, scope_game | scope_editor, HKCAT_GENERAL, "" },
87  { HOTKEY_ZOOM_IN, "zoomin", N_("Zoom In"), false, scope_game | scope_editor, HKCAT_GENERAL, "" },
88  { HOTKEY_ZOOM_OUT, "zoomout", N_("Zoom Out"), false, scope_game | scope_editor, HKCAT_GENERAL, "" },
89  { HOTKEY_ZOOM_DEFAULT, "zoomdefault", N_("Default Zoom"), false, scope_game | scope_editor, HKCAT_GENERAL, "" },
90  { HOTKEY_FULLSCREEN, "fullscreen", N_("Toggle Full Screen"), false, scope_game | scope_editor | scope_main, HKCAT_GENERAL, "" },
91  { HOTKEY_SCREENSHOT, "screenshot", N_("Screenshot"), false, scope_game | scope_editor | scope_main, HKCAT_GENERAL, "" },
92  { HOTKEY_MAP_SCREENSHOT, "mapscreenshot", N_("Map Screenshot"), false, scope_game | scope_editor, HKCAT_GENERAL, "" },
93  { HOTKEY_ACCELERATED, "accelerated", N_("Accelerated"), false, scope_game, HKCAT_GENERAL, "" },
94  { HOTKEY_TERRAIN_DESCRIPTION, "describeterrain", N_("Terrain Description"), false, scope_game | scope_editor, HKCAT_MAP, "" },
95  { HOTKEY_UNIT_DESCRIPTION, "describeunit", N_("Unit Description"), false, scope_game | scope_editor, HKCAT_UNITS, "" },
96  { HOTKEY_RENAME_UNIT, "renameunit", N_("Rename Unit"), false, scope_game | scope_editor, HKCAT_UNITS, "" },
97  { HOTKEY_DELETE_UNIT, "editor-deleteunit", N_("Delete Unit"), false, scope_game | scope_editor, HKCAT_TOOLS, "" },
98 
99  { HOTKEY_SAVE_GAME, "save", N_("Save Game"), false, scope_game, HKCAT_SAVING, "" },
100  { HOTKEY_SAVE_REPLAY, "savereplay", N_("Save Replay"), false, scope_game, HKCAT_SAVING, "" },
101  { HOTKEY_SAVE_MAP, "savemap", N_("Save Map"), false, scope_game, HKCAT_SAVING, "" },
102  { HOTKEY_LOAD_GAME, "load", N_("Load Game"), false, scope_game | scope_main, HKCAT_SAVING, "" },
103  { HOTKEY_RECRUIT, "recruit", N_("Recruit"), false, scope_game, HKCAT_UNITS, "" },
104  { HOTKEY_REPEAT_RECRUIT, "repeatrecruit", N_("Repeat Recruit"), false, scope_game, HKCAT_UNITS, "" },
105  { HOTKEY_RECALL, "recall", N_("Recall"), false, scope_game, HKCAT_UNITS, "" },
106  { HOTKEY_LABEL_SETTINGS, "label_settings", N_("Show/Hide Labels"), false, scope_game, HKCAT_MAP, "" },
107  { HOTKEY_ENDTURN, "endturn", N_("End Turn"), false, scope_game, HKCAT_GENERAL, "" },
108  //TODO: why has HOTKEY_TOGGLE_ELLIPSES more than scope_game ?
109  { HOTKEY_TOGGLE_ELLIPSES, "toggleellipses", N_("Toggle Ellipses"), false, scope_game | scope_editor, HKCAT_GENERAL, "" },
110  { HOTKEY_TOGGLE_GRID, "togglegrid", N_("Toggle Grid"), false, scope_game | scope_editor, HKCAT_GENERAL, "" },
111  { HOTKEY_MOUSE_SCROLL, "mousescroll", N_("Mouse Scrolling"), false, scope_game | scope_editor, HKCAT_GENERAL, "" },
112  { HOTKEY_STATUS_TABLE, "statustable", N_("Status Table"), false, scope_game, HKCAT_GENERAL, "" },
113  { HOTKEY_MUTE, "mute", N_("Mute"), false, scope_game | scope_editor | scope_main, HKCAT_GENERAL, "" },
114  { HOTKEY_SPEAK, "speak", N_("Speak"), false, scope_game, HKCAT_CHAT, "" },
115  { HOTKEY_CREATE_UNIT, "createunit", N_("Create Unit (Debug!)"), false, scope_game, HKCAT_DEBUG, "" },
116  { HOTKEY_CHANGE_SIDE, "changeside", N_("Change Side (Debug!)"), false, scope_game, HKCAT_DEBUG, "" },
117  { HOTKEY_KILL_UNIT, "killunit", N_("Kill Unit (Debug!)"), false, scope_game, HKCAT_DEBUG, "" },
118  { HOTKEY_PREFERENCES, "preferences", N_("Preferences"), false, scope_game | scope_editor | scope_main, HKCAT_GENERAL, "" },
119  { HOTKEY_OBJECTIVES, "objectives", N_("Scenario Objectives"), false, scope_game, HKCAT_MAP, "" },
120  { HOTKEY_UNIT_LIST, "unitlist", N_("Unit List"), false, scope_game | scope_editor, HKCAT_UNITS, "" },
121  { HOTKEY_STATISTICS, "statistics", N_("Statistics"), false, scope_game, HKCAT_GENERAL, "" },
122  { HOTKEY_STOP_NETWORK, "stopnetwork", N_("Pause Network Game"), false, scope_game, HKCAT_GENERAL, "" },
123  { HOTKEY_START_NETWORK, "startnetwork", N_("Continue Network Game"), false, scope_game, HKCAT_GENERAL, "" },
124  { HOTKEY_SURRENDER, "surrender", N_("Surrender Game"), false, scope_game, HKCAT_SCENARIO, "" },
125  { HOTKEY_QUIT_GAME, "quit", N_("Quit to Main Menu"), false, scope_game | scope_editor, HKCAT_GENERAL, "" },
126  { HOTKEY_LABEL_TEAM_TERRAIN, "labelteamterrain", N_("Set Team Label"), false, scope_game, HKCAT_MAP, "" },
127  { HOTKEY_LABEL_TERRAIN, "labelterrain", N_("Set Label"), false, scope_game, HKCAT_MAP, "" },
128  { HOTKEY_CLEAR_LABELS, "clearlabels", N_("Clear Labels"), false, scope_game, HKCAT_MAP, "" },
129  { HOTKEY_SHOW_ENEMY_MOVES, "showenemymoves", N_("Show Enemy Moves"), false, scope_game, HKCAT_UNITS, "" },
130  { HOTKEY_BEST_ENEMY_MOVES, "bestenemymoves", N_("Best Possible Enemy Moves"), false, scope_game, HKCAT_UNITS, "" },
131 
132  { HOTKEY_REPLAY_PLAY, "playreplay", N_("Play Replay"), false, scope_game, HKCAT_REPLAY, "" },
133  { HOTKEY_REPLAY_RESET, "resetreplay", N_("Reset Replay"), false, scope_game, HKCAT_REPLAY, "" },
134  { HOTKEY_REPLAY_STOP, "stopreplay", N_("Stop Replay"), false, scope_game, HKCAT_REPLAY, "" },
135  { HOTKEY_REPLAY_NEXT_TURN, "replaynextturn", N_("Next Turn"), false, scope_game, HKCAT_REPLAY, "" },
136  { HOTKEY_REPLAY_NEXT_SIDE, "replaynextside", N_("Next Side"), false, scope_game, HKCAT_REPLAY, "" },
137  { HOTKEY_REPLAY_NEXT_MOVE, "replaynextmove", N_("Next Move"), false, scope_game, HKCAT_REPLAY, "" },
138  { HOTKEY_REPLAY_SHOW_EVERYTHING, "replayshoweverything", N_("Full Map"), false, scope_game, HKCAT_REPLAY, "" },
139  { HOTKEY_REPLAY_SHOW_EACH, "replayshoweach", N_("Each Team"), false, scope_game, HKCAT_REPLAY, "" },
140  { HOTKEY_REPLAY_SHOW_TEAM1, "replayshowteam1", N_("Human Team"), false, scope_game, HKCAT_REPLAY, "" },
141  { HOTKEY_REPLAY_SKIP_ANIMATION, "replayskipanimation", N_("Skip Animation"), false, scope_game, HKCAT_REPLAY, "" },
142  { HOTKEY_REPLAY_EXIT, "replayexit", N_("End Replay"), false, scope_game, HKCAT_REPLAY, "" },
143  // Whiteboard commands
144  // TRANSLATORS: whiteboard menu entry: toggle planning mode
145  { HOTKEY_WB_TOGGLE, "wbtoggle", N_("whiteboard^Planning Mode"), false, scope_game, HKCAT_WHITEBOARD, "" },
146  // TRANSLATORS: whiteboard menu entry: execute planned action
147  { HOTKEY_WB_EXECUTE_ACTION, "wbexecuteaction", N_("whiteboard^Execute Action"), false, scope_game, HKCAT_WHITEBOARD, "" },
148  // TRANSLATORS: whiteboard menu entry: execute all planned actions
149  { HOTKEY_WB_EXECUTE_ALL_ACTIONS, "wbexecuteallactions", N_("whiteboard^Execute All Actions"), false, scope_game, HKCAT_WHITEBOARD, "" },
150  // TRANSLATORS: whiteboard menu entry: delete planned action
151  { HOTKEY_WB_DELETE_ACTION, "wbdeleteaction", N_("whiteboard^Delete Action"), false, scope_game, HKCAT_WHITEBOARD, "" },
152  // TRANSLATORS: whiteboard menu entry: move planned action up queue
153  { HOTKEY_WB_BUMP_UP_ACTION, "wbbumpupaction", N_("whiteboard^Move Action Up"), false, scope_game, HKCAT_WHITEBOARD, "" },
154  // TRANSLATORS: whiteboard menu entry: move planned action down queue
155  { HOTKEY_WB_BUMP_DOWN_ACTION, "wbbumpdownaction", N_("whiteboard^Move Action Down"), false, scope_game, HKCAT_WHITEBOARD, "" },
156  // TRANSLATORS: whiteboard menu entry: plan as though the chosen unit were dead
157  { HOTKEY_WB_SUPPOSE_DEAD, "wbsupposedead", N_("whiteboard^Suppose Dead"), false, scope_game, HKCAT_WHITEBOARD, "" },
158 
159  { HOTKEY_QUIT_TO_DESKTOP, "quit-to-desktop", N_("Quit to Desktop"), false, scope_game | scope_editor | scope_main, HKCAT_GENERAL, "" },
160  { HOTKEY_EDITOR_MAP_CLOSE, "editor-close-map", N_("Close Map"), false, scope_editor, HKCAT_GENERAL, "" },
161 
162  // These are not really hotkey items but menu entries to get expanded.
163  // They need to have their own hotkey to control their active state.
164  { HOTKEY_EDITOR_PLAYLIST, "editor-playlist", N_("Switch Time of Day"), true, scope_editor, HKCAT_PLACEHOLDER, "" },
165  { HOTKEY_EDITOR_SCHEDULE, "menu-editor-schedule", "", true, scope_editor, HKCAT_PLACEHOLDER, "" },
166  { HOTKEY_EDITOR_MAP_SWITCH, "editor-switch-map", N_("Switch Map"), true, scope_editor, HKCAT_PLACEHOLDER, "" },
167  { HOTKEY_EDITOR_LOCAL_TIME, "menu-editor-local-time", N_("Assign Local Time"), true, scope_editor, HKCAT_PLACEHOLDER, "" },
168 
169  { HOTKEY_EDITOR_CUSTOM_TODS, "editor-custom-tods", N_("Time Schedule Editor"), false, scope_editor, HKCAT_SCENARIO, "" },
170  { HOTKEY_EDITOR_PARTIAL_UNDO, "editor-partial-undo", N_("Partial Undo"), false, scope_editor, HKCAT_SCENARIO, "" },
171  { HOTKEY_EDITOR_MAP_NEW, "editor-map-new", N_("New Map"), false, scope_editor, HKCAT_SCENARIO, "" },
172  { HOTKEY_EDITOR_SCENARIO_NEW, "editor-scenario-new", N_("New Scenario"), false, scope_editor, HKCAT_SCENARIO, "" },
173  { HOTKEY_EDITOR_MAP_LOAD, "editor-map-load", N_("Load Map"), false, scope_editor, HKCAT_MAP, "" },
174  { HOTKEY_EDITOR_MAP_SAVE, "editor-map-save", N_("Save Map"), false, scope_editor, HKCAT_MAP, "" },
175  { HOTKEY_EDITOR_MAP_SAVE_AS, "editor-map-save-as", N_("Save Map As"), false, scope_editor, HKCAT_MAP, "" },
176  { HOTKEY_EDITOR_SCENARIO_SAVE_AS, "editor-scenario-save-as", N_("Save Scenario As"), false, scope_editor, HKCAT_SCENARIO, "" },
177  { HOTKEY_EDITOR_MAP_SAVE_ALL, "editor-map-save-all", N_("Save All Maps"), false, scope_editor, HKCAT_MAP, "" },
178  { HOTKEY_EDITOR_MAP_REVERT, "editor-map-revert", N_("Revert All Changes"), false, scope_editor, HKCAT_MAP, "" },
179  { HOTKEY_EDITOR_MAP_INFO, "editor-map-info", N_("Map Information"), false, scope_editor, HKCAT_MAP, "" },
180 
181  { HOTKEY_EDITOR_PALETTE_ITEMS_CLEAR, "editor-palette-items-clear", N_("Clear Selected Item Set"), false, scope_editor, HKCAT_PALETTE, "" },
182  { HOTKEY_EDITOR_PALETTE_ITEM_SWAP, "editor-terrain-palette-swap", N_("Swap Foreground/Background Palette Item"), false, scope_editor, HKCAT_PALETTE, "" },
183  { HOTKEY_EDITOR_PALETTE_GROUPS, "editor-palette-groups", N_("Change Palette Group"), false, scope_editor, HKCAT_PALETTE, "" },
184  { HOTKEY_EDITOR_PALETTE_UPSCROLL, "editor-palette-upscroll", N_("Scroll Palette Left"), false, scope_editor, HKCAT_PALETTE, "" },
185  { HOTKEY_EDITOR_PALETTE_DOWNSCROLL, "editor-palette-downscroll", N_("Scroll Palette Right"), false, scope_editor, HKCAT_PALETTE, "" },
186  { HOTKEY_EDITOR_REMOVE_LOCATION, "editor-remove-location", N_("Remove Location"), false, scope_editor, HKCAT_PALETTE, "" },
187 
188  { HOTKEY_EDITOR_SIDE_NEW, "editor-side-new", N_("Add New Side"), false, scope_editor, HKCAT_SCENARIO, "" },
189 
190  { HOTKEY_EDITOR_TOOL_NEXT, "editor-tool-next", N_("Next Tool"), false, scope_editor, HKCAT_TOOLS, "" },
191 
192  { HOTKEY_EDITOR_TOOL_PAINT, "editor-tool-paint", N_("Paint Tool"), false, scope_editor, HKCAT_TOOLS, N_("Use left/right mouse button to draw fore-/background terrain. Hold Shift to paint base layer only. Ctrl+click to sample terrain under cursor.") },
193  { HOTKEY_EDITOR_TOOL_FILL, "editor-tool-fill", N_("Fill Tool"), false, scope_editor, HKCAT_TOOLS, N_("Use left/right mouse button to draw fore-/background terrain. Hold Shift to paint base layer only. Ctrl+click to sample terrain under cursor.") },
194  { HOTKEY_EDITOR_TOOL_SELECT, "editor-tool-select", N_("Selection Tool"), false, scope_editor, HKCAT_TOOLS, N_("Left mouse button selects or deselects with Ctrl, right brings up a context menu. Hold Shift for magic-wand selection of tiles with same terrain.") },
195  { HOTKEY_EDITOR_TOOL_STARTING_POSITION, "editor-tool-starting-position", N_("Starting Positions Tool"), false, scope_editor, HKCAT_TOOLS, N_("Left mouse button displays player selection, right clears. Number keys scroll to the starting position, alt+number sets respective starting position under cursor, delete clears.") },
196  { HOTKEY_EDITOR_TOOL_LABEL, "editor-tool-label", N_("Label Tool"), false, scope_editor, HKCAT_TOOLS, N_("Left mouse button sets or drags a label, right clears.") },
197  { HOTKEY_EDITOR_TOOL_UNIT, "editor-tool-unit", N_("Unit Tool"), false, scope_editor, HKCAT_TOOLS, N_("Left mouse button sets a new unit or moves a unit via drag and drop, right brings up a context menu. Needs a defined side.") },
198  { HOTKEY_EDITOR_TOOL_ITEM, "editor-tool-item", N_("Item Tool"), false, scope_editor, HKCAT_TOOLS, N_("Left mouse button sets a new item or moves it via drag and drop, right clears. Not implemented yet.") },
199  { HOTKEY_EDITOR_TOOL_SOUNDSOURCE, "editor-tool-soundsource", N_("Sound Source Tool"), false, scope_editor, HKCAT_TOOLS, N_("Left mouse button sets or drags a sound source, right clears. Not implemented yet.") },
200  { HOTKEY_EDITOR_TOOL_VILLAGE, "editor-tool-village", N_("Village Tool"), false, scope_editor, HKCAT_TOOLS, N_("Left mouse button sets the village ownership to the current side, right clears. Needs a defined side.") },
201 
202  { HOTKEY_EDITOR_UNIT_TOGGLE_CANRECRUIT, "editor-toggle-canrecruit", N_("Can Recruit"), false, scope_editor, HKCAT_TOOLS, N_("Toggle the recruit attribute of a unit.") },
203  { HOTKEY_EDITOR_UNIT_TOGGLE_RENAMEABLE, "editor-toggle-renameable", N_("Can be Renamed"), false, scope_editor, HKCAT_TOOLS, N_("Toggle the unit being renameable.") },
204  { HOTKEY_EDITOR_UNIT_RECRUIT_ASSIGN, "editor-unit-recruit", N_("Assign Recruit List"), false, scope_editor, HKCAT_TOOLS, N_("Assign the selected unit set as recruit list to the unit.") },
205 
206  { HOTKEY_EDITOR_UNIT_CHANGE_ID, "editor-change-unitid", N_("Change Unit ID"), false, scope_editor, HKCAT_TOOLS, "" },
207  { HOTKEY_EDITOR_UNIT_TOGGLE_LOYAL, "editor-unit-toggle-loyal", N_("Loyal"), false, scope_editor, HKCAT_TOOLS, "" },
208  { HOTKEY_EDITOR_UNIT_FACING, "menu-unit-facing", "", true, scope_editor, HKCAT_PLACEHOLDER, "" },
209 
210  { HOTKEY_MINIMAP_CODING_UNIT, "minimap-unit-coding", N_("Toggle Minimap Unit Coding"), false, scope_game | scope_editor, HKCAT_MAP, "" },
211  { HOTKEY_MINIMAP_CODING_TERRAIN, "minimap-terrain-coding", N_("Toggle Minimap Terrain Coding"), false, scope_game | scope_editor, HKCAT_MAP, "" },
212 
213  { HOTKEY_MINIMAP_DRAW_UNITS, "minimap-draw-units", N_("Toggle Minimap Unit Drawing"), false, scope_game | scope_editor, HKCAT_MAP, "" },
214  { HOTKEY_MINIMAP_DRAW_VILLAGES, "minimap-draw-villages", N_("Toggle Minimap Village Drawing"), false, scope_game | scope_editor, HKCAT_MAP, "" },
215  { HOTKEY_MINIMAP_DRAW_TERRAIN, "minimap-draw-terrain", N_("Toggle Minimap Terrain Drawing"), false, scope_game | scope_editor, HKCAT_MAP, "" },
216 
217  { HOTKEY_EDITOR_BRUSH_NEXT, "editor-brush-next", N_("Next Brush"), false, scope_editor, HKCAT_TOOLS, "" },
218  { HOTKEY_EDITOR_BRUSH_DEFAULT, "editor-brush-default", N_("Default Brush"), false, scope_editor, HKCAT_TOOLS, "" },
219  { HOTKEY_EDITOR_BRUSH_1, "editor-brush-1", N_("Single Tile"), false, scope_editor, HKCAT_TOOLS, "" },
220  { HOTKEY_EDITOR_BRUSH_2, "editor-brush-2", N_("Radius One"), false, scope_editor, HKCAT_TOOLS, "" },
221  { HOTKEY_EDITOR_BRUSH_3, "editor-brush-3", N_("Radius Two"), false, scope_editor, HKCAT_TOOLS, "" },
222  { HOTKEY_EDITOR_BRUSH_NW_SE, "editor-brush-nw-se", N_("Brush NW-SE"), false, scope_editor, HKCAT_TOOLS, "" },
223  { HOTKEY_EDITOR_BRUSH_SW_NE, "editor-brush-sw-ne", N_("Brush SW-NE"), false, scope_editor, HKCAT_TOOLS, "" },
224 
225  { HOTKEY_EDITOR_SELECTION_CUT, "editor-cut", N_("Cut"), false, scope_editor, HKCAT_CLIPBOARD, "" },
226  { HOTKEY_EDITOR_SELECTION_COPY, "editor-copy", N_("Copy"), false, scope_editor, HKCAT_CLIPBOARD, "" },
227  { HOTKEY_EDITOR_CLIPBOARD_PASTE, "editor-paste", N_("Paste"), false, scope_editor, HKCAT_CLIPBOARD, N_("Left mouse button pastes from the clipboard, right brings up a context menu.") },
228  { HOTKEY_EDITOR_SELECTION_EXPORT, "editor-export-selection-coords", N_("Export Selected Coordinates to System Clipboard"), true, scope_editor, HKCAT_PLACEHOLDER, "" },
229  { HOTKEY_EDITOR_SELECT_ALL, "editor-select-all", N_("Select All"), false, scope_editor, HKCAT_CLIPBOARD, "" },
230  { HOTKEY_EDITOR_SELECT_INVERSE, "editor-select-inverse", N_("Select Inverse"), false, scope_editor, HKCAT_CLIPBOARD, "" },
231  { HOTKEY_EDITOR_SELECT_NONE, "editor-select-none", N_("Select None"), false, scope_editor, HKCAT_CLIPBOARD, "" },
232  { HOTKEY_EDITOR_CLIPBOARD_ROTATE_CW, "editor-clipboard-rotate-cw", N_("Rotate Clipboard Clockwise"), false, scope_editor, HKCAT_CLIPBOARD, "" },
233  { HOTKEY_EDITOR_CLIPBOARD_ROTATE_CCW, "editor-clipboard-rotate-ccw", N_("Rotate Clipboard Counter-Clockwise"), false, scope_editor, HKCAT_CLIPBOARD, "" },
234  { HOTKEY_EDITOR_CLIPBOARD_FLIP_HORIZONTAL, "editor-clipboard-flip-horizontal", N_("Flip Clipboard Horizontally"), false, scope_editor, HKCAT_CLIPBOARD, "" },
235  { HOTKEY_EDITOR_CLIPBOARD_FLIP_VERTICAL, "editor-clipboard-flip-vertical", N_("Flip Clipboard Vertically"), false, scope_editor, HKCAT_CLIPBOARD, "" },
236  { HOTKEY_EDITOR_SELECTION_ROTATE, "editor-selection-rotate", N_("Rotate Selection"), false, scope_editor, HKCAT_TOOLS, "" },
237  { HOTKEY_EDITOR_SELECTION_FLIP, "editor-selection-flip", N_("Flip Selection"), false, scope_editor, HKCAT_TOOLS, "" },
238  { HOTKEY_EDITOR_SELECTION_FILL, "editor-selection-fill", N_("Fill Selection"), false, scope_editor, HKCAT_TOOLS, "" },
239  { HOTKEY_EDITOR_SELECTION_GENERATE, "editor-selection-generate", N_("Generate Tiles in Selection"), false, scope_editor, HKCAT_TOOLS, "" },
240  { HOTKEY_EDITOR_SELECTION_RANDOMIZE, "editor-selection-randomize", N_("Randomize Tiles in Selection"), false, scope_editor, HKCAT_TOOLS, "" },
241  { HOTKEY_EDITOR_MAP_RESIZE, "editor-map-resize", N_("Resize Map"), false, scope_editor, HKCAT_MAP, "" },
242  { HOTKEY_EDITOR_MAP_ROTATE, "editor-map-rotate", N_("Rotate Map"), false, scope_editor, HKCAT_MAP, "" },
243  { HOTKEY_EDITOR_MAP_GENERATE, "editor-map-generate", N_("Generate Map"), false, scope_editor, HKCAT_MAP, "" },
244  { HOTKEY_EDITOR_MAP_APPLY_MASK, "editor-map-apply-mask", N_("Apply a Mask"), false, scope_editor, HKCAT_MAP, "" },
245  { HOTKEY_EDITOR_MAP_CREATE_MASK_TO, "editor-map-create-mask-to", N_("Create Mask"), false, scope_editor, HKCAT_MAP, "" },
246  { HOTKEY_EDITOR_REFRESH, "editor-refresh", N_("Refresh Display"), false, scope_editor, HKCAT_GENERAL, "" },
247 
248  { HOTKEY_EDITOR_UPDATE_TRANSITIONS, "editor-update-transitions", N_("Update Terrain Transitions"), false, scope_editor, HKCAT_MAP, "" },
249 
250  // This item is for binding in the preferences
251  { HOTKEY_EDITOR_TOGGLE_TRANSITIONS, "editor-toggle-transitions", N_("Toggle Terrain Transition Update"), false, scope_editor, HKCAT_MAP, "" },
252  // The next three are for displaying the different states in the menu
253  { HOTKEY_EDITOR_AUTO_UPDATE_TRANSITIONS, "editor-auto-update-transitions", N_("Auto-update Terrain Transitions"), true, scope_editor, HKCAT_PLACEHOLDER, "" },
254  { HOTKEY_EDITOR_NO_UPDATE_TRANSITIONS, "editor-no-update-transitions", N_("Auto-update Terrain Transitions: No"), true, scope_editor, HKCAT_PLACEHOLDER, "" },
255  { HOTKEY_EDITOR_PARTIAL_UPDATE_TRANSITIONS, "editor-partial-update-transitions", N_("Auto-update Terrain Transitions: Partial"), true, scope_editor, HKCAT_PLACEHOLDER, "" },
256 
257  { HOTKEY_EDITOR_REFRESH_IMAGE_CACHE, "editor-refresh-image-cache", N_("Refresh Image Cache"), false, scope_editor, HKCAT_GENERAL, "" },
258  { HOTKEY_EDITOR_DRAW_COORDINATES, "editor-draw-coordinates", N_("Draw Hex Coordinates"), false, scope_editor, HKCAT_MAP, "" },
259  { HOTKEY_EDITOR_DRAW_TERRAIN_CODES, "editor-draw-terrain-codes", N_("Draw Terrain Codes"), false, scope_editor, HKCAT_MAP, "" },
260  { HOTKEY_EDITOR_DRAW_NUM_OF_BITMAPS, "editor-draw-num-of-bitmaps", N_("Draw Number of Bitmaps"), false, scope_editor, HKCAT_MAP, "" },
261 
262  { HOTKEY_EDITOR_AREA_SAVE, "editor-save-area", N_("Save Selection to Area"), false, scope_editor, HKCAT_SCENARIO, "" },
263  { HOTKEY_EDITOR_AREA_RENAME, "editor-rename-area", N_("Rename Selected Area"), false, scope_editor, HKCAT_SCENARIO, "" },
264  { HOTKEY_EDITOR_AREA_REMOVE, "editor-remove-area", N_("Remove Selected Area"), false, scope_editor, HKCAT_SCENARIO, "" },
265  { HOTKEY_EDITOR_AREA_ADD, "editor-add-area", N_("Add New Area"), false, scope_editor, HKCAT_SCENARIO, "" },
266 
267  { HOTKEY_EDITOR_SCENARIO_EDIT, "editor-scenario-edit", N_("Edit Scenario"), false, scope_editor, HKCAT_SCENARIO, "" },
268  { HOTKEY_EDITOR_SIDE_EDIT, "editor-side-edit", N_("Edit Side"), false, scope_editor, HKCAT_SCENARIO, "" },
269  { HOTKEY_EDITOR_SIDE_REMOVE, "editor-side-remove", N_("Remove Side"), false, scope_editor, HKCAT_SCENARIO, "" },
270 
271  { HOTKEY_DELAY_SHROUD, "delayshroud", N_("Delay Shroud Updates"), false, scope_game, HKCAT_MAP, "" },
272  { HOTKEY_UPDATE_SHROUD, "updateshroud", N_("Update Shroud Now"), false, scope_game, HKCAT_MAP, "" },
273  { HOTKEY_CONTINUE_MOVE, "continue", N_("Continue Move"), false, scope_game, HKCAT_UNITS, "" },
274  { HOTKEY_SEARCH, "search", N_("Find Label or Unit"), false, scope_game, HKCAT_MAP, "" },
275  { HOTKEY_SPEAK_ALLY, "speaktoally", N_("Speak to Ally"), false, scope_game, HKCAT_CHAT, "" },
276  { HOTKEY_SPEAK_ALL, "speaktoall", N_("Speak to All"), false, scope_game, HKCAT_CHAT, "" },
277  { HOTKEY_HELP, "help", N_("Help"), false, scope_game | scope_editor | scope_main, HKCAT_GENERAL, "" },
278  { HOTKEY_HELP_ABOUT_SAVELOAD, "help-about-saveload", N_("Help about save-loading"), false, scope_game, HKCAT_GENERAL, N_("Hint: save-loading is unnecessary")},
279  { HOTKEY_CHAT_LOG, "chatlog", N_("View Chat Log"), false, scope_game, HKCAT_CHAT, "" },
280  { HOTKEY_USER_CMD, "command", N_("Enter User Command"), false, scope_game, HKCAT_CHAT, "" },
281  { HOTKEY_CUSTOM_CMD, "customcommand", N_("Custom Command"), false, scope_game, HKCAT_CHAT, "" },
282  { HOTKEY_AI_FORMULA, "aiformula", N_("Run Formula"), false, scope_game, HKCAT_DEBUG, "" },
283  { HOTKEY_CLEAR_MSG, "clearmessages", N_("Clear Messages"), false, scope_game, HKCAT_CHAT, "" },
284 
285  { HOTKEY_LANGUAGE, "changelanguage", N_("Change Language"), false, scope_main, HKCAT_GENERAL, "" },
286  { TITLE_SCREEN__RELOAD_WML, "title_screen__reload_wml", N_("Refresh WML"), true , scope_editor | scope_main, HKCAT_PLACEHOLDER, "" },
287  { TITLE_SCREEN__NEXT_TIP, "title_screen__next_tip", N_("Next Tip of the Day"), false, scope_main, HKCAT_GENERAL, "" },
288  { TITLE_SCREEN__PREVIOUS_TIP, "title_screen__previous_tip", N_("Previous Tip of the Day"), false, scope_main, HKCAT_GENERAL, "" },
289  { TITLE_SCREEN__TUTORIAL, "title_screen__tutorial", N_("Start Tutorial"), false , scope_main, HKCAT_GENERAL, "" },
290  { TITLE_SCREEN__CAMPAIGN, "title_screen__campaign", N_("Start Campaign"), false , scope_main, HKCAT_GENERAL, "" },
291  { TITLE_SCREEN__MULTIPLAYER, "title_screen__multiplayer", N_("Start Multiplayer Game"), false, scope_main, HKCAT_GENERAL, "" },
292  { TITLE_SCREEN__ADDONS, "title_screen__addons", N_("Manage Add-ons"), false , scope_main, HKCAT_GENERAL, "" },
293  { TITLE_SCREEN__CORES, "title_screen__cores", N_("Manage Cores"), false , scope_main, HKCAT_GENERAL, "" },
294  { TITLE_SCREEN__EDITOR, "title_screen__editor", N_("Start Editor"), false, scope_main, HKCAT_GENERAL, "" },
295  { TITLE_SCREEN__CREDITS, "title_screen__credits", N_("Show Credits"), false , scope_main, HKCAT_GENERAL, "" },
296  { TITLE_SCREEN__TEST, "title_screen__test", N_("Start Test Scenario"), false , scope_main, HKCAT_GENERAL, "" },
297 
298  { GLOBAL__HELPTIP, "global__helptip", N_("Show Helptip"), false, scope_game | scope_editor | scope_main, HKCAT_GENERAL, "" },
299 
300  { LUA_CONSOLE, "global__lua__console", N_("Show Lua Console"), false, scope_game | scope_editor | scope_main, HKCAT_DEBUG, ""},
301 
302  //This list item must stay at the end since it is used as terminator for iterating.
303  { HOTKEY_NULL, "null", N_("Unrecognized Command"), true, SCOPE_COUNT, HKCAT_PLACEHOLDER, "" }
304 }};
305 
306 std::set<HOTKEY_COMMAND> toggle_commands {
311 };
312 
313 // Contains copies of master_hotkey_list and all current active wml menu hotkeys
314 // TODO: Maybe known_hotkeys is not a fitting name anymore.
315 std::vector<hotkey::hotkey_command> known_hotkeys;
316 
317 // Index map for known_hotkeys. Since known_hotkeys begins with master_hotkey_list, they are also indexes for master_hotkey_list.
318 std::map<std::string, std::size_t> command_map_;
319 
320 hk_scopes scope_active_(0);
321 } // end anon namespace
322 
324  : prev_scope_active_(scope_active_)
325 {
326 }
327 
329 {
330  scope_active_ = prev_scope_active_;
331 }
332 
334 {
335  for(int i = 0; i < hotkey::SCOPE_COUNT; ++i) {
336  scope_active_[i] = false;
337  }
338 }
339 
340 void set_scope_active(scope s, bool set)
341 {
342  scope_active_[s] = set;
343 }
344 
346 {
347  scope_active_ = s;
348 }
349 
351 {
352  assert(s < SCOPE_COUNT);
353  return scope_active_[s];
354 }
355 
357 {
358  // s is a copy because we need one
359  s &= scope_active_;
360  return s.any();
361 }
362 
363 const hotkey_command& get_hotkey_command(const std::string& command)
364 {
365  if(command_map_.find(command) == command_map_.end()) {
366  return get_hotkey_null();
367  }
368 
369  return known_hotkeys[command_map_[command]];
370 }
371 
372 const std::vector<hotkey_command>& get_hotkey_commands()
373 {
374  return known_hotkeys;
375 }
376 
377 // Returns whether a hotkey was deleted.
378 bool remove_wml_hotkey(const std::string& id)
379 {
380  const hotkey::hotkey_command& command = get_hotkey_command(id);
381 
382  if(command.id == hotkey::HOTKEY_NULL) {
383  LOG_G << "remove_wml_hotkey: command with id=" + id + " doesn't exist\n";
384  return false;
385  } else if(command.id != hotkey::HOTKEY_WML) {
386  LOG_G << "remove_wml_hotkey: command with id=" + id + " cannot be removed because it is no wml menu hotkey\n";
387  return false;
388  } else {
389  LOG_G << "removing wml hotkey with id=" + id + "\n";
390 
391  // Iterate in reverse since it's almost certain the appropriate hotkey will be near
392  // the end, since this function is used to removed WML hotkeys, which are added after
393  // the built-ins.
394  for(auto itor = known_hotkeys.rbegin(); itor != known_hotkeys.rend(); ++itor) {
395  if(itor->command == id) {
396  known_hotkeys.erase(std::next(itor).base());
397  break;
398  }
399  }
400 
401  // command_map_ might be all wrong now, so we need to rebuild.
402  command_map_.clear();
403 
404  for(std::size_t index = 0; index < known_hotkeys.size(); ++index) {
405  command_map_[known_hotkeys[index].command] = index;
406  }
407 
408  return true;
409  }
410 }
411 
412 bool has_hotkey_command(const std::string& id)
413 {
415 }
416 
417 void add_wml_hotkey(const std::string& id, const t_string& description, const config& default_hotkey)
418 {
419  if(id == "null") {
420  LOG_G << "Couldn't add wml hotkey with null id and description = '" << description << "'.\n";
421  return;
422  }
423 
424  if(has_hotkey_command(id)) {
425  LOG_G << "Hotkey with id '" << id << "' already exists. Deleting the old hotkey_command.\n";
426  remove_wml_hotkey(id);
427  }
428 
429  DBG_G << "Added wml hotkey with id = '" << id << "' and description = '" << description << "'.\n";
430 
431  known_hotkeys.emplace_back(hotkey::HOTKEY_WML, id, description, false, false, scope_game, HKCAT_CUSTOM, t_string(""));
432 
433  command_map_[id] = known_hotkeys.size() - 1;
434 
435  if(!default_hotkey.empty() && !has_hotkey_item(id)) {
436  hotkey::hotkey_ptr new_item = hotkey::load_from_config(default_hotkey);
437  new_item->set_command(id);
438 
439  if(new_item->valid()) {
440  DBG_G << "added default description for the wml hotkey with id=" + id;
441  add_hotkey(new_item);
442  } else {
443  ERR_CF << "failed to add default hotkey with id=" + id;
444  }
445  }
446 }
447 
449  : id(temp_command.id)
450  , command(temp_command.command)
451  , description(temp_command.description, "wesnoth-lib")
452  , hidden(temp_command.hidden)
453  , toggle(toggle_commands.count(temp_command.id) > 0)
454  , scope(temp_command.scope)
455  , category(temp_command.category)
456  , tooltip(temp_command.tooltip, "wesnoth-lib")
457 {
458 }
459 
461  const std::string& id_,
462  const t_string& desc,
463  bool hid,
464  bool tog,
465  hotkey::hk_scopes scop,
467  const t_string& toolt)
468  : id(cmd)
469  , command(id_)
470  , description(desc)
471  , hidden(hid)
472  , toggle(tog)
473  , scope(scop)
474  , category(cat)
475  , tooltip(toolt)
476 {
477 }
478 
480 {
481  return get_hotkey_null();
482 }
483 
485 {
486  if(id == HOTKEY_NULL || command == "null") {
487  const hotkey_command& null_cmd = null_command();
488 
489  if(id == null_cmd.id && command == null_cmd.command && scope == null_cmd.scope && description == null_cmd.description) {
490  return true;
491  } else {
492  ERR_G << "the hotkey command seems to be the null command but it is not 100% sure. This shouldn't happen";
493  return true;
494  }
495  }
496 
497  return false;
498 }
499 
501 {
502  for(hotkey_command& cmd : known_hotkeys) {
503  if(cmd.id == command) {
504  return cmd;
505  }
506  }
507 
508  ERR_G << " \"get_command_by_command\" returned get_hotkey_null() because no hotkey_command had the requested "
509  "number:"
510  << command;
511 
512  return get_hotkey_null();
513 }
514 
516 {
517  // It is the last entry in that array, and the indexes in master_hotkey_list and known_hotkeys are the same.
518  return known_hotkeys[master_hotkey_list.size() - 1];
519 }
520 
522 {
523  while(known_hotkeys.back().id == hotkey::HOTKEY_WML) {
524  command_map_.erase(known_hotkeys.back().command);
525 
526  known_hotkeys.pop_back();
527  }
528 
529  hotkeys_by_category[HKCAT_CUSTOM].clear();
530 }
531 
532 const std::string& get_description(const std::string& command)
533 {
534  return get_hotkey_command(command).description;
535 }
536 
537 const std::string& get_tooltip(const std::string& command)
538 {
539  // The null hotkey_command has the "" tooltip
540  return get_hotkey_command(command).tooltip;
541 }
542 
544 {
545  known_hotkeys.clear();
546  hotkeys_by_category.clear();
547 
548  // Reserve enough space for the built-in hotkeys and 20 extra spaces for WML hotkeys. This is
549  // to avoid reallocation of this huge vector when any of the latter are added. 20 is honestly
550  // overkill, since there's really no reason to ever have near that many hotkey-enabled WML menu
551  // items, but it doesn't cost us anything to have extra.
552  known_hotkeys.reserve(master_hotkey_list.size() + 20);
553 
554  std::size_t i = 0;
555  for(const hotkey_command_temp& cmd : master_hotkey_list) {
556  // Initialize the full hotkey from the temp data.
557  known_hotkeys.emplace_back(cmd);
558 
559  // Note the known_hotkeys index associated with this command.
560  command_map_[cmd.command] = i++;
561 
562  // Record this hotkey's id in the appropriate category list.
563  hotkeys_by_category[cmd.category].push_back(cmd.id);
564  }
565 }
566 
568 {
569  command_map_.clear();
570 }
571 
572 HOTKEY_COMMAND get_id(const std::string& command)
573 {
574  return get_hotkey_command(command).id;
575 }
576 
578 {
579  return category_names;
580 }
581 
583 {
584  return hotkeys_by_category[category];
585 }
586 
587 } // namespace hotkey
HOTKEY_COMMAND id
the names are strange: the "hotkey::HOTKEY_COMMAND" is named id, and the string to identify the objec...
static lg::log_domain log_config("config")
hotkey_ptr load_from_config(const config &cfg)
Create and instantiate a hotkey from a config element.
bool is_scope_active(scope s)
scope
Available hotkey scopes.
void set_scope_active(scope s, bool set)
void add_wml_hotkey(const std::string &id, const t_string &description, const config &default_hotkey)
adds a new wml hotkey to the list, but only if there is no hotkey with that id yet on the list...
static const hotkey_command & null_command()
returns the command that is treated as null
Stores all information related to functions that can be bound to hotkeys.
#define ERR_G
bool has_hotkey_item(const std::string &command)
bool null() const
checks weather this is the null hotkey_command
std::list< HOTKEY_COMMAND > get_hotkeys_by_category(HOTKEY_CATEGORY category)
Returns a list of all the hotkeys belonging to the given category.
Definitions for the interface to Wesnoth Markup Language (WML).
void clear_hotkey_commands()
void set_active_scopes(hk_scopes s)
Keyboard shortcuts for game actions.
std::string::size_type size() const
Definition: tstring.hpp:183
const std::string & get_tooltip(const std::string &command)
#define LOG_G
std::bitset< SCOPE_COUNT > hk_scopes
void init_hotkey_commands()
void add_hotkey(const hotkey_ptr item)
Add a hotkey to the list of hotkeys.
hk_scopes scope
The visibility scope of the command.
void deactivate_all_scopes()
Do not use this outside hotkeys.cpp.
std::map< HOTKEY_CATEGORY, std::string > category_name_map_t
std::size_t i
Definition: function.cpp:933
const category_name_map_t & get_category_names()
Returns the map of hotkey categories and their display names.
const std::vector< hotkey_command > & get_hotkey_commands()
returns a container that contains all currently active hotkey_commands.
std::string command
The command is unique.
bool toggle
Toggle hotkeys have some restrictions on what can be bound to them.
static map_location::DIRECTION s
std::shared_ptr< hotkey_base > hotkey_ptr
Definition: hotkey_item.hpp:30
#define N_(String)
Definition: gettext.hpp:99
std::size_t index(const std::string &str, const std::size_t index)
Codepoint index corresponding to the nth character in a UTF-8 string.
Definition: unicode.cpp:71
#define ERR_CF
const std::string & get_description(const std::string &command)
const hotkey_command & get_hotkey_null()
returns the hotkey_command that is treated as null.
#define next(ls)
Definition: llex.cpp:32
bool has_hotkey_command(const std::string &id)
Standard logging facilities (interface).
bool hidden
If hidden then don&#39;t show the command in the hotkey preferences.
#define DBG_G
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:92
const hotkey_command & get_hotkey_command(const std::string &command)
returns the hotkey_command with the given name
void delete_all_wml_hotkeys()
deletes all wml hotkeys, should be called after a game has ended
bool remove_wml_hotkey(const std::string &id)
removes a wml hotkey with the given id, returns true if the deletion was successful ...
bool empty() const
Definition: config.cpp:884
HOTKEY_CATEGORY category
The category of the command.
HOTKEY_COMMAND get_id(const std::string &command)
returns get_hotkey_command(command).id
static const hotkey_command & get_command_by_command(HOTKEY_COMMAND command)
the execute_command argument was changed from HOTKEY_COMMAND to hotkey_command, to be able to call it...