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"
19 #include "safeguards.h"
24 * List of all HotkeyLists.
25 * This is a pointer to ensure initialisation order with the various static HotkeyList instances.
27 static SmallVector
<HotkeyList
*, 16> *_hotkey_lists
= NULL
;
29 /** String representation of a keycode */
31 const char *name
; ///< Name of the keycode
32 WindowKeyCodes keycode
; ///< The keycode
35 /** Array of non-standard keycodes that can be used in the hotkeys config file. */
36 static const KeycodeNames _keycode_to_name
[] = {
41 {"GLOBAL", WKC_GLOBAL_HOTKEY
},
44 {"RETURN", WKC_RETURN
},
45 {"BACKQUOTE", WKC_BACKQUOTE
},
60 {"NUM_PLUS", WKC_NUM_PLUS
},
61 {"NUM_MINUS", WKC_NUM_MINUS
},
67 * Try to parse a single part of a keycode.
68 * @param start Start of the string to parse.
69 * @param end End of the string to parse.
70 * @return A keycode if a match is found or 0.
72 static uint16
ParseCode(const char *start
, const char *end
)
75 while (start
< end
&& *start
== ' ') start
++;
76 while (end
> start
&& *end
== ' ') end
--;
77 for (uint i
= 0; i
< lengthof(_keycode_to_name
); i
++) {
78 if (strlen(_keycode_to_name
[i
].name
) == (size_t)(end
- start
) && strncasecmp(start
, _keycode_to_name
[i
].name
, end
- start
) == 0) {
79 return _keycode_to_name
[i
].keycode
;
82 if (end
- start
== 1) {
83 if (*start
>= 'a' && *start
<= 'z') return *start
- ('a'-'A');
84 /* Ignore invalid keycodes */
85 if (*(const uint8
*)start
< 128) return *start
;
91 * Parse a string representation of a keycode.
92 * @param start Start of the input.
93 * @param end End of the input.
94 * @return A valid keycode or 0.
96 static uint16
ParseKeycode(const char *start
, const char *end
)
101 const char *cur
= start
;
102 while (*cur
!= '+' && cur
!= end
) cur
++;
103 uint16 code
= ParseCode(start
, cur
);
104 if (code
== 0) return 0;
105 if (code
& WKC_SPECIAL_KEYS
) {
106 /* Some completely wrong keycode we don't support. */
107 if (code
& ~WKC_SPECIAL_KEYS
) return 0;
110 /* Ignore the code if it has more then 1 letter. */
111 if (keycode
& ~WKC_SPECIAL_KEYS
) return 0;
114 if (cur
== end
) break;
122 * Parse a string to the keycodes it represents
123 * @param hotkey The hotkey object to add the keycodes to
124 * @param value The string to parse
126 static void ParseHotkeys(Hotkey
*hotkey
, const char *value
)
128 const char *start
= value
;
129 while (*start
!= '\0') {
130 const char *end
= start
;
131 while (*end
!= '\0' && *end
!= ',') end
++;
132 uint16 keycode
= ParseKeycode(start
, end
);
133 if (keycode
!= 0) hotkey
->AddKeycode(keycode
);
134 start
= (*end
== ',') ? end
+ 1: end
;
139 * Convert a hotkey to it's string representation so it can be written to the
140 * config file. Separate parts of the keycode (like "CTRL" and "F1" are split
142 * @param keycode The keycode to convert to a string.
143 * @return A string representation of this keycode.
144 * @note The return value is a static buffer, stredup the result before calling
145 * this function again.
147 static const char *KeycodeToString(uint16 keycode
)
152 if (keycode
& WKC_GLOBAL_HOTKEY
) {
153 strecat(buf
, "GLOBAL", lastof(buf
));
156 if (keycode
& WKC_SHIFT
) {
157 if (!first
) strecat(buf
, "+", lastof(buf
));
158 strecat(buf
, "SHIFT", lastof(buf
));
161 if (keycode
& WKC_CTRL
) {
162 if (!first
) strecat(buf
, "+", lastof(buf
));
163 strecat(buf
, "CTRL", lastof(buf
));
166 if (keycode
& WKC_ALT
) {
167 if (!first
) strecat(buf
, "+", lastof(buf
));
168 strecat(buf
, "ALT", lastof(buf
));
171 if (keycode
& WKC_META
) {
172 if (!first
) strecat(buf
, "+", lastof(buf
));
173 strecat(buf
, "META", lastof(buf
));
176 if (!first
) strecat(buf
, "+", lastof(buf
));
177 keycode
= keycode
& ~WKC_SPECIAL_KEYS
;
179 for (uint i
= 0; i
< lengthof(_keycode_to_name
); i
++) {
180 if (_keycode_to_name
[i
].keycode
== keycode
) {
181 strecat(buf
, _keycode_to_name
[i
].name
, lastof(buf
));
185 assert(keycode
< 128);
189 strecat(buf
, key
, lastof(buf
));
194 * Convert all keycodes attached to a hotkey to a single string. If multiple
195 * keycodes are attached to the hotkey they are split by a comma.
196 * @param hotkey The keycodes of this hotkey need to be converted to a string.
197 * @return A string representation of all keycodes.
198 * @note The return value is a static buffer, stredup the result before calling
199 * this function again.
201 const char *SaveKeycodes(const Hotkey
*hotkey
)
203 static char buf
[128];
205 for (uint i
= 0; i
< hotkey
->keycodes
.Length(); i
++) {
206 const char *str
= KeycodeToString(hotkey
->keycodes
[i
]);
207 if (i
> 0) strecat(buf
, ",", lastof(buf
));
208 strecat(buf
, str
, lastof(buf
));
214 * Create a new Hotkey object with a single default keycode.
215 * @param default_keycode The default keycode for this hotkey.
216 * @param name The name of this hotkey.
217 * @param num Number of this hotkey, should be unique within the hotkey list.
219 Hotkey::Hotkey(uint16 default_keycode
, const char *name
, int num
) :
223 if (default_keycode
!= 0) this->AddKeycode(default_keycode
);
227 * Create a new Hotkey object with multiple default keycodes.
228 * @param default_keycodes An array of default keycodes terminated with 0.
229 * @param name The name of this hotkey.
230 * @param num Number of this hotkey, should be unique within the hotkey list.
232 Hotkey::Hotkey(const uint16
*default_keycodes
, const char *name
, int num
) :
236 const uint16
*keycode
= default_keycodes
;
237 while (*keycode
!= 0) {
238 this->AddKeycode(*keycode
);
244 * Add a keycode to this hotkey, from now that keycode will be matched
245 * in addition to any previously added keycodes.
246 * @param keycode The keycode to add.
248 void Hotkey::AddKeycode(uint16 keycode
)
250 this->keycodes
.Include(keycode
);
253 HotkeyList::HotkeyList(const char *ini_group
, Hotkey
*items
, GlobalHotkeyHandlerFunc global_hotkey_handler
) :
254 global_hotkey_handler(global_hotkey_handler
), ini_group(ini_group
), items(items
)
256 if (_hotkey_lists
== NULL
) _hotkey_lists
= new SmallVector
<HotkeyList
*, 16>();
257 *_hotkey_lists
->Append() = this;
260 HotkeyList::~HotkeyList()
262 _hotkey_lists
->Erase(_hotkey_lists
->Find(this));
266 * Load HotkeyList from IniFile.
267 * @param ini IniFile to load from.
269 void HotkeyList::Load(IniFile
*ini
)
271 IniGroup
*group
= ini
->GetGroup(this->ini_group
);
272 for (Hotkey
*hotkey
= this->items
; hotkey
->name
!= NULL
; ++hotkey
) {
273 IniItem
*item
= group
->GetItem(hotkey
->name
, false);
275 hotkey
->keycodes
.Clear();
276 if (item
->value
!= NULL
) ParseHotkeys(hotkey
, item
->value
);
282 * Save HotkeyList to IniFile.
283 * @param ini IniFile to save to.
285 void HotkeyList::Save(IniFile
*ini
) const
287 IniGroup
*group
= ini
->GetGroup(this->ini_group
);
288 for (const Hotkey
*hotkey
= this->items
; hotkey
->name
!= NULL
; ++hotkey
) {
289 IniItem
*item
= group
->GetItem(hotkey
->name
, true);
290 item
->SetValue(SaveKeycodes(hotkey
));
295 * Check if a keycode is bound to something.
296 * @param keycode The keycode that was pressed
297 * @param global_only Limit the search to hotkeys defined as 'global'.
298 * @return The number of the matching hotkey or -1.
300 int HotkeyList::CheckMatch(uint16 keycode
, bool global_only
) const
302 for (const Hotkey
*list
= this->items
; list
->name
!= NULL
; ++list
) {
303 if (list
->keycodes
.Contains(keycode
| WKC_GLOBAL_HOTKEY
) || (!global_only
&& list
->keycodes
.Contains(keycode
))) {
311 static void SaveLoadHotkeys(bool save
)
313 IniFile
*ini
= new IniFile();
314 ini
->LoadFromDisk(_hotkeys_file
, NO_DIRECTORY
);
316 for (HotkeyList
**list
= _hotkey_lists
->Begin(); list
!= _hotkey_lists
->End(); ++list
) {
324 if (save
) ini
->SaveToDisk(_hotkeys_file
);
329 /** Load the hotkeys from the config file */
330 void LoadHotkeysFromConfig()
332 SaveLoadHotkeys(false);
335 /** Save the hotkeys to the config file */
336 void SaveHotkeysToConfig()
338 SaveLoadHotkeys(true);
341 void HandleGlobalHotkeys(WChar key
, uint16 keycode
)
343 for (HotkeyList
**list
= _hotkey_lists
->Begin(); list
!= _hotkey_lists
->End(); ++list
) {
344 if ((*list
)->global_hotkey_handler
== NULL
) continue;
346 int hotkey
= (*list
)->CheckMatch(keycode
, true);
347 if (hotkey
>= 0 && ((*list
)->global_hotkey_handler(hotkey
) == ES_HANDLED
)) return;