4 * This file is part of OpenTTD.
5 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
6 * OpenTTD 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.
7 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
10 /** @file hotkeys.cpp Implementation of hotkey related functions */
16 #include "string_func.h"
17 #include "window_gui.h"
22 * List of all HotkeyLists.
23 * This is a pointer to ensure initialisation order with the various static HotkeyList instances.
25 static SmallVector
<HotkeyList
*, 16> *_hotkey_lists
= NULL
;
27 /** String representation of a keycode */
29 const char *name
; ///< Name of the keycode
30 WindowKeyCodes keycode
; ///< The keycode
33 /** Array of non-standard keycodes that can be used in the hotkeys config file. */
34 static const KeycodeNames _keycode_to_name
[] = {
39 {"GLOBAL", WKC_GLOBAL_HOTKEY
},
42 {"RETURN", WKC_RETURN
},
43 {"BACKQUOTE", WKC_BACKQUOTE
},
58 {"NUM_PLUS", WKC_NUM_PLUS
},
59 {"NUM_MINUS", WKC_NUM_MINUS
},
65 * Try to parse a single part of a keycode.
66 * @param start Start of the string to parse.
67 * @param end End of the string to parse.
68 * @return A keycode if a match is found or 0.
70 static uint16
ParseCode(const char *start
, const char *end
)
73 while (start
< end
&& *start
== ' ') start
++;
74 while (end
> start
&& *end
== ' ') end
--;
75 for (uint i
= 0; i
< lengthof(_keycode_to_name
); i
++) {
76 if (strlen(_keycode_to_name
[i
].name
) == (size_t)(end
- start
) && strncasecmp(start
, _keycode_to_name
[i
].name
, end
- start
) == 0) {
77 return _keycode_to_name
[i
].keycode
;
80 if (end
- start
== 1) {
81 if (*start
>= 'a' && *start
<= 'z') return *start
- ('a'-'A');
82 /* Ignore invalid keycodes */
83 if (*(const uint8
*)start
< 128) return *start
;
89 * Parse a string representation of a keycode.
90 * @param start Start of the input.
91 * @param end End of the input.
92 * @return A valid keycode or 0.
94 static uint16
ParseKeycode(const char *start
, const char *end
)
99 const char *cur
= start
;
100 while (*cur
!= '+' && cur
!= end
) cur
++;
101 uint16 code
= ParseCode(start
, cur
);
102 if (code
== 0) return 0;
103 if (code
& WKC_SPECIAL_KEYS
) {
104 /* Some completely wrong keycode we don't support. */
105 if (code
& ~WKC_SPECIAL_KEYS
) return 0;
108 /* Ignore the code if it has more then 1 letter. */
109 if (keycode
& ~WKC_SPECIAL_KEYS
) return 0;
112 if (cur
== end
) break;
120 * Parse a string to the keycodes it represents
121 * @param hotkey The hotkey object to add the keycodes to
122 * @param value The string to parse
124 static void ParseHotkeys(Hotkey
*hotkey
, const char *value
)
126 const char *start
= value
;
127 while (*start
!= '\0') {
128 const char *end
= start
;
129 while (*end
!= '\0' && *end
!= ',') end
++;
130 uint16 keycode
= ParseKeycode(start
, end
);
131 if (keycode
!= 0) hotkey
->AddKeycode(keycode
);
132 start
= (*end
== ',') ? end
+ 1: end
;
137 * Convert a hotkey to it's string representation so it can be written to the
138 * config file. Separate parts of the keycode (like "CTRL" and "F1" are split
140 * @param keycode The keycode to convert to a string.
141 * @return A string representation of this keycode.
142 * @note The return value is a static buffer, strdup the result before calling
143 * this function again.
145 static const char *KeycodeToString(uint16 keycode
)
150 if (keycode
& WKC_GLOBAL_HOTKEY
) {
151 strecat(buf
, "GLOBAL", lastof(buf
));
154 if (keycode
& WKC_SHIFT
) {
155 if (!first
) strecat(buf
, "+", lastof(buf
));
156 strecat(buf
, "SHIFT", lastof(buf
));
159 if (keycode
& WKC_CTRL
) {
160 if (!first
) strecat(buf
, "+", lastof(buf
));
161 strecat(buf
, "CTRL", lastof(buf
));
164 if (keycode
& WKC_ALT
) {
165 if (!first
) strecat(buf
, "+", lastof(buf
));
166 strecat(buf
, "ALT", lastof(buf
));
169 if (keycode
& WKC_META
) {
170 if (!first
) strecat(buf
, "+", lastof(buf
));
171 strecat(buf
, "META", lastof(buf
));
174 if (!first
) strecat(buf
, "+", lastof(buf
));
175 keycode
= keycode
& ~WKC_SPECIAL_KEYS
;
177 for (uint i
= 0; i
< lengthof(_keycode_to_name
); i
++) {
178 if (_keycode_to_name
[i
].keycode
== keycode
) {
179 strecat(buf
, _keycode_to_name
[i
].name
, lastof(buf
));
183 assert(keycode
< 128);
187 strecat(buf
, key
, lastof(buf
));
192 * Convert all keycodes attached to a hotkey to a single string. If multiple
193 * keycodes are attached to the hotkey they are split by a comma.
194 * @param hotkey The keycodes of this hotkey need to be converted to a string.
195 * @return A string representation of all keycodes.
196 * @note The return value is a static buffer, strdup the result before calling
197 * this function again.
199 const char *SaveKeycodes(const Hotkey
*hotkey
)
201 static char buf
[128];
203 for (uint i
= 0; i
< hotkey
->keycodes
.Length(); i
++) {
204 const char *str
= KeycodeToString(hotkey
->keycodes
[i
]);
205 if (i
> 0) strecat(buf
, ",", lastof(buf
));
206 strecat(buf
, str
, lastof(buf
));
212 * Create a new Hotkey object with a single default keycode.
213 * @param default_keycode The default keycode for this hotkey.
214 * @param name The name of this hotkey.
215 * @param num Number of this hotkey, should be unique within the hotkey list.
217 Hotkey::Hotkey(uint16 default_keycode
, const char *name
, int num
) :
221 if (default_keycode
!= 0) this->AddKeycode(default_keycode
);
225 * Create a new Hotkey object with multiple default keycodes.
226 * @param default_keycodes An array of default keycodes terminated with 0.
227 * @param name The name of this hotkey.
228 * @param num Number of this hotkey, should be unique within the hotkey list.
230 Hotkey::Hotkey(const uint16
*default_keycodes
, const char *name
, int num
) :
234 const uint16
*keycode
= default_keycodes
;
235 while (*keycode
!= 0) {
236 this->AddKeycode(*keycode
);
242 * Add a keycode to this hotkey, from now that keycode will be matched
243 * in addition to any previously added keycodes.
244 * @param keycode The keycode to add.
246 void Hotkey::AddKeycode(uint16 keycode
)
248 this->keycodes
.Include(keycode
);
251 HotkeyList::HotkeyList(const char *ini_group
, Hotkey
*items
, GlobalHotkeyHandlerFunc global_hotkey_handler
) :
252 global_hotkey_handler(global_hotkey_handler
), ini_group(ini_group
), items(items
)
254 if (_hotkey_lists
== NULL
) _hotkey_lists
= new SmallVector
<HotkeyList
*, 16>();
255 *_hotkey_lists
->Append() = this;
258 HotkeyList::~HotkeyList()
260 _hotkey_lists
->Erase(_hotkey_lists
->Find(this));
264 * Load HotkeyList from IniFile.
265 * @param ini IniFile to load from.
267 void HotkeyList::Load(IniFile
*ini
)
269 IniGroup
*group
= ini
->GetGroup(this->ini_group
);
270 for (Hotkey
*hotkey
= this->items
; hotkey
->name
!= NULL
; ++hotkey
) {
271 IniItem
*item
= group
->GetItem(hotkey
->name
, false);
273 hotkey
->keycodes
.Clear();
274 if (item
->value
!= NULL
) ParseHotkeys(hotkey
, item
->value
);
280 * Save HotkeyList to IniFile.
281 * @param ini IniFile to save to.
283 void HotkeyList::Save(IniFile
*ini
) const
285 IniGroup
*group
= ini
->GetGroup(this->ini_group
);
286 for (const Hotkey
*hotkey
= this->items
; hotkey
->name
!= NULL
; ++hotkey
) {
287 IniItem
*item
= group
->GetItem(hotkey
->name
, true);
288 item
->SetValue(SaveKeycodes(hotkey
));
293 * Check if a keycode is bound to something.
294 * @param keycode The keycode that was pressed
295 * @param global_only Limit the search to hotkeys defined as 'global'.
296 * @return The number of the matching hotkey or -1.
298 int HotkeyList::CheckMatch(uint16 keycode
, bool global_only
) const
300 for (const Hotkey
*list
= this->items
; list
->name
!= NULL
; ++list
) {
301 if (list
->keycodes
.Contains(keycode
| WKC_GLOBAL_HOTKEY
) || (!global_only
&& list
->keycodes
.Contains(keycode
))) {
309 static void SaveLoadHotkeys(bool save
)
311 IniFile
*ini
= new IniFile();
312 ini
->LoadFromDisk(_hotkeys_file
, BASE_DIR
);
314 for (HotkeyList
**list
= _hotkey_lists
->Begin(); list
!= _hotkey_lists
->End(); ++list
) {
322 if (save
) ini
->SaveToDisk(_hotkeys_file
);
327 /** Load the hotkeys from the config file */
328 void LoadHotkeysFromConfig()
330 SaveLoadHotkeys(false);
333 /** Save the hotkeys to the config file */
334 void SaveHotkeysToConfig()
336 SaveLoadHotkeys(true);
339 void HandleGlobalHotkeys(WChar key
, uint16 keycode
)
341 for (HotkeyList
**list
= _hotkey_lists
->Begin(); list
!= _hotkey_lists
->End(); ++list
) {
342 if ((*list
)->global_hotkey_handler
== NULL
) continue;
344 int hotkey
= (*list
)->CheckMatch(keycode
, true);
345 if (hotkey
>= 0 && ((*list
)->global_hotkey_handler(hotkey
) == ES_HANDLED
)) return;