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 */
17 #include "window_gui.h"
22 /** String representation of a keycode */
24 WindowKeyCodes keycode
; ///< Keycode
25 uint namelen
; ///< Length of keycode name
26 const char *name
; ///< Name of keycode
29 /** Array of non-standard keys that can be used in the hotkeys config file. */
30 static const KeycodeName special_keys
[] = {
31 #define DEFINE_HOTKEY(keycode,name) { keycode, sizeof name - 1, name }
32 DEFINE_HOTKEY (WKC_SHIFT
, "SHIFT"),
33 DEFINE_HOTKEY (WKC_CTRL
, "CTRL"),
34 DEFINE_HOTKEY (WKC_ALT
, "ALT"),
35 DEFINE_HOTKEY (WKC_META
, "META"),
36 DEFINE_HOTKEY (WKC_GLOBAL_HOTKEY
, "GLOBAL"),
37 DEFINE_HOTKEY (WKC_ESC
, "ESC"),
38 DEFINE_HOTKEY (WKC_DELETE
, "DEL"),
39 DEFINE_HOTKEY (WKC_RETURN
, "RETURN"),
40 DEFINE_HOTKEY (WKC_BACKQUOTE
, "BACKQUOTE"),
41 DEFINE_HOTKEY (WKC_F1
, "F1"),
42 DEFINE_HOTKEY (WKC_F2
, "F2"),
43 DEFINE_HOTKEY (WKC_F3
, "F3"),
44 DEFINE_HOTKEY (WKC_F4
, "F4"),
45 DEFINE_HOTKEY (WKC_F5
, "F5"),
46 DEFINE_HOTKEY (WKC_F6
, "F6"),
47 DEFINE_HOTKEY (WKC_F7
, "F7"),
48 DEFINE_HOTKEY (WKC_F8
, "F8"),
49 DEFINE_HOTKEY (WKC_F9
, "F9"),
50 DEFINE_HOTKEY (WKC_F10
, "F10"),
51 DEFINE_HOTKEY (WKC_F11
, "F11"),
52 DEFINE_HOTKEY (WKC_F12
, "F12"),
53 DEFINE_HOTKEY (WKC_PAUSE
, "PAUSE"),
54 DEFINE_HOTKEY (WKC_COMMA
, "COMMA"),
55 DEFINE_HOTKEY (WKC_NUM_PLUS
, "NUM_PLUS"),
56 DEFINE_HOTKEY (WKC_NUM_MINUS
, "NUM_MINUS"),
57 DEFINE_HOTKEY (WKC_EQUALS
, "="),
58 DEFINE_HOTKEY (WKC_MINUS
, "-"),
62 static const KeycodeName
*find_special_key_by_keycode (uint16 keycode
)
64 for (uint i
= 0; i
< lengthof(special_keys
); i
++) {
65 if (special_keys
[i
].keycode
== keycode
) {
66 return &special_keys
[i
];
73 static const KeycodeName
*find_special_key_by_name (const char *name
, size_t len
)
75 for (uint i
= 0; i
< lengthof(special_keys
); i
++) {
76 if (special_keys
[i
].namelen
== len
&& strncasecmp (name
, special_keys
[i
].name
, len
) == 0) {
77 return &special_keys
[i
];
86 * Try to parse a single part of a keycode.
87 * @param start Start of the string to parse.
88 * @param end End of the string to parse.
89 * @return A keycode if a match is found or 0.
91 static uint16
ParseCode(const char *start
, const char *end
)
94 while (start
< end
&& *start
== ' ') start
++;
95 while (end
> start
&& *end
== ' ') end
--;
96 size_t len
= end
- start
;
98 const KeycodeName
*key
= find_special_key_by_name (start
, len
);
99 if (key
!= NULL
) return key
->keycode
;
102 if (*start
>= 'a' && *start
<= 'z') return *start
- ('a'-'A');
103 /* Ignore invalid keycodes */
104 if (*(const uint8
*)start
< 128) return *start
;
110 * Parse a string representation of a keycode.
111 * @param start Start of the input.
112 * @param end End of the input.
113 * @return A valid keycode or 0.
115 static uint16
ParseKeycode(const char *start
, const char *end
)
117 assert(start
<= end
);
120 const char *cur
= start
;
121 while (*cur
!= '+' && cur
!= end
) cur
++;
122 uint16 code
= ParseCode(start
, cur
);
123 if (code
== 0) return 0;
124 if (code
& WKC_SPECIAL_KEYS
) {
125 /* Some completely wrong keycode we don't support. */
126 if (code
& ~WKC_SPECIAL_KEYS
) return 0;
129 /* Ignore the code if it has more then 1 letter. */
130 if (keycode
& ~WKC_SPECIAL_KEYS
) return 0;
133 if (cur
== end
) break;
141 * Append the description of a keycode to a string.
142 * @param buf The buffer to append the description to.
143 * @param keycode The keycode whose description to append
145 static void AppendKeycodeDescription (stringb
*buf
, uint16 keycode
)
147 if (keycode
& WKC_GLOBAL_HOTKEY
) buf
->append ("GLOBAL+");
148 if (keycode
& WKC_SHIFT
) buf
->append ("SHIFT+");
149 if (keycode
& WKC_CTRL
) buf
->append ("CTRL+");
150 if (keycode
& WKC_ALT
) buf
->append ("ALT+");
151 if (keycode
& WKC_META
) buf
->append ("META+");
153 keycode
&= ~WKC_SPECIAL_KEYS
;
154 const KeycodeName
*key
= find_special_key_by_keycode (keycode
);
156 buf
->append (key
->name
);
158 assert (keycode
< 128);
159 buf
->append (keycode
);
165 * List of all HotkeyLists.
166 * This is a pointer to ensure initialisation order with the various static HotkeyList instances.
168 static std::vector
<HotkeyList
*> *_hotkey_lists
= NULL
;
170 void HotkeyList::init (void)
172 if (_hotkey_lists
== NULL
) _hotkey_lists
= new std::vector
<HotkeyList
*>;
173 _hotkey_lists
->push_back (this);
176 HotkeyList::~HotkeyList()
178 std::vector
<HotkeyList
*>::iterator it
= _hotkey_lists
->begin();
180 assert (it
!= _hotkey_lists
->end());
181 if (*it
== this) break;
184 _hotkey_lists
->erase (it
);
188 * Load HotkeyList from IniFile.
189 * @param ini IniFile to load from.
191 void HotkeyList::Load(IniFile
*ini
)
193 const IniGroup
*group
= ini
->get_group (this->ini_group
);
195 this->mappings
.clear();
197 for (uint i
= 0; i
< this->ndescs
; i
++) {
198 const Hotkey
*hotkey
= &this->descs
[i
];
200 const IniItem
*item
= group
->find (hotkey
->name
);
202 static const uint16
Hotkey::* const defaults
[] = { &Hotkey::default0
, &Hotkey::default1
, &Hotkey::default2
, &Hotkey::default3
};
203 for (uint i
= 0; i
< lengthof(defaults
); i
++) {
204 uint16 keycode
= hotkey
->*defaults
[i
];
205 if (keycode
== 0) break;
206 this->push_mapping (keycode
, hotkey
->num
);
208 } else if (item
->value
!= NULL
) {
209 const char *start
= item
->value
;
210 while (*start
!= '\0') {
211 const char *end
= start
;
212 while (*end
!= '\0' && *end
!= ',') end
++;
213 uint16 keycode
= ParseKeycode (start
, end
);
214 if (keycode
!= 0) this->push_mapping (keycode
, hotkey
->num
);
215 start
= (*end
== ',') ? end
+ 1: end
;
222 * Save HotkeyList to IniFile.
223 * @param ini IniFile to save to.
225 void HotkeyList::Save(IniFile
*ini
) const
227 IniGroup
*group
= ini
->get_group (this->ini_group
);
229 for (uint i
= 0; i
< this->ndescs
; i
++) {
230 const Hotkey
*hotkey
= &this->descs
[i
];
233 std::vector
<Mapping
>::const_iterator it
= this->mappings
.begin();
234 for (; it
!= this->mappings
.end(); it
++) {
235 if (it
->value
== hotkey
->num
) {
236 if (!buf
.empty()) buf
.append (',');
237 AppendKeycodeDescription (&buf
, it
->keycode
);
241 group
->get_item(hotkey
->name
)->SetValue (buf
.c_str());
246 * Check if a keycode is bound to something.
247 * @param keycode The keycode that was pressed
248 * @param global_only Limit the search to hotkeys defined as 'global'.
249 * @return The number of the matching hotkey or -1.
251 int HotkeyList::CheckMatch(uint16 keycode
, bool global_only
) const
253 std::vector
<Mapping
>::const_iterator it
= this->mappings
.begin();
254 for (; it
!= this->mappings
.end(); it
++) {
255 if ((it
->keycode
== (keycode
| WKC_GLOBAL_HOTKEY
)) || (!global_only
&& (it
->keycode
== keycode
))) {
263 /** Load the hotkeys from the config file */
264 void LoadHotkeysFromConfig()
266 IniFile
ini (_hotkeys_file
, NO_DIRECTORY
);
268 std::vector
<HotkeyList
*>::iterator it
= _hotkey_lists
->begin();
269 for (; it
!= _hotkey_lists
->end(); it
++) {
274 /** Save the hotkeys to the config file */
275 void SaveHotkeysToConfig()
277 IniFile
ini (_hotkeys_file
, NO_DIRECTORY
);
279 std::vector
<HotkeyList
*>::const_iterator it
= _hotkey_lists
->begin();
280 for (; it
!= _hotkey_lists
->end(); it
++) {
284 ini
.SaveToDisk (_hotkeys_file
);
287 void HandleGlobalHotkeys(WChar key
, uint16 keycode
)
289 std::vector
<HotkeyList
*>::const_iterator it
= _hotkey_lists
->begin();
290 for (; it
!= _hotkey_lists
->end(); it
++) {
291 const HotkeyList
*list
= *it
;
292 if (list
->global_hotkey_handler
== NULL
) continue;
294 int hotkey
= list
->CheckMatch (keycode
, true);
295 if (hotkey
>= 0 && (list
->global_hotkey_handler(hotkey
) == ES_HANDLED
)) return;