1 #include <algorithm>
2 #include <wx/log.h>
3
4 #include "HotkeyTypes.h"
5 #include "HotkeyException.h"
6
7
key_binding()8 key_binding::key_binding() //: m_nextOrderIdx(1)
9 {
10 }
11
setMetaKey(const wxString & key)12 void key_binding::setMetaKey( const wxString& key )
13 {
14 this->m_meta = key;
15 }
16
getMetaKey() const17 const wxString& key_binding::getMetaKey() const
18 {
19 return this->m_meta;
20 }
21
bind(const wxString & cmd,const wxString & keyString)22 void key_binding::bind( const wxString& cmd, const wxString& keyString )
23 {
24 if ( this->exists( cmd, keyString ) )
25 {
26 return;
27 }
28
29 wxString normKey;
30 if ( keyString.StartsWith( wxT("&") ) )
31 {
32 //key set
33 normKey = resolveKeySymSetName( KeynameConverter::normalizeSpringKey( keyString.SubString( 1, keyString.size() + 1 ) ) );
34 }
35 else
36 {
37 //normal key
38 normKey = resolveKeySymName( KeynameConverter::normalizeSpringKey( keyString ) );
39 }
40
41 if ( normKey.StartsWith( wxT("Any+") ) )
42 {
43 m_groupsAny[ normKey ].push_back( cmd );
44 m_keyCmdSetAny.insert( std::make_pair( normKey, cmd ) );
45 }
46 else
47 {
48 m_groups[ normKey ].push_back( cmd );
49 m_keyCmdSet.insert( std::make_pair( normKey, cmd ) );
50 }
51 }
52
addKeySymSet(const wxString & name,const wxString & keyString)53 void key_binding::addKeySymSet( const wxString& name, const wxString& keyString )
54 {
55 const wxString normName = name.Lower();
56 const wxString normKey = this->resolveKeySymName( KeynameConverter::normalizeSpringKey( keyString ) );
57 this->m_keySymsSet[normName] = normKey;
58 this->m_keySymsSetRev[normKey] = normName;
59 }
60
addKeySym(const wxString & name,const wxString & keyString)61 void key_binding::addKeySym( const wxString& name, const wxString& keyString )
62 {
63 const wxString normName = name.Lower();
64 const wxString normKey = KeynameConverter::normalizeSpringKey( keyString );
65 this->m_keySyms[normName] = normKey;
66 this->m_keySymsRev[KeynameConverter::convertHexValueToKey( keyString )] = normName;
67 }
68
resolveKeySymSetName(const wxString & symName) const69 const wxString key_binding::resolveKeySymSetName( const wxString& symName ) const
70 {
71 key_sym_set_map::const_iterator iter = this->m_keySymsSet.find( symName );
72 if ( iter == this->m_keySymsSet.end() )
73 {
74 return symName;
75 }
76
77 return iter->second;
78 }
79
resolveKeySymSetKey(const wxString & key) const80 const wxString key_binding::resolveKeySymSetKey( const wxString& key ) const
81 {
82 key_sym_set_map::const_iterator iter = this->m_keySymsSetRev.find( key );
83 if ( iter == this->m_keySymsSetRev.end() )
84 {
85 return key;
86 }
87
88 return wxT("&") + iter->second;
89 }
90
resolveKeySymName(const wxString & symName) const91 const wxString key_binding::resolveKeySymName( const wxString& symName ) const
92 {
93 key_sym_map::const_iterator iter = this->m_keySyms.find( KeynameConverter::discardModifier( symName ) );
94 if ( iter == this->m_keySyms.end() )
95 {
96 return symName;
97 }
98
99 KeynameConverter::ModifierList modList = KeynameConverter::stringToKeyModifier( symName );
100 return KeynameConverter::modifier2String( modList ) + KeynameConverter::convertHexValueToKey( iter->second );
101 }
102
resolveKeySymKeyAndSet(const wxString & key) const103 wxString key_binding::resolveKeySymKeyAndSet( const wxString& key ) const
104 {
105 wxString res = resolveKeySymSetKey( key );
106
107 return resolveKeySymKey(res);
108 }
109
resolveKeySymKey(const wxString & key) const110 wxString key_binding::resolveKeySymKey( const wxString& key ) const
111 {
112 key_sym_map::const_iterator iter = this->m_keySymsRev.find( KeynameConverter::discardModifier( key ) );
113 if ( iter == this->m_keySymsRev.end() )
114 {
115 return key;
116 }
117
118 KeynameConverter::ModifierList modList = KeynameConverter::stringToKeyModifier( key );
119 return KeynameConverter::modifier2String( modList ) + iter->second;
120 }
121
setKeySymsSet(const key_sym_set_map & keySymsSet)122 void key_binding::setKeySymsSet( const key_sym_set_map& keySymsSet )
123 {
124 this->m_keySymsSet = keySymsSet;
125
126 //update reverse map
127 for( key_sym_set_map::const_iterator iter = m_keySymsSet.begin(); iter != m_keySymsSet.end(); ++iter )
128 {
129 this->m_keySymsSetRev[iter->second] = iter->first;
130 }
131 }
132
setKeySyms(const key_sym_map & keySyms)133 void key_binding::setKeySyms( const key_sym_map& keySyms )
134 {
135 this->m_keySyms = keySyms;
136
137 //update reverse map
138 for( key_sym_map::const_iterator iter = m_keySyms.begin(); iter != m_keySyms.end(); ++iter )
139 {
140 //hex convert is needed here (data could come from wxProfile which stores raw keys (0x62 etc))
141 this->m_keySymsRev[KeynameConverter::convertHexValueToKey( iter->second )] = iter->first;
142 }
143 }
144
getKeySyms() const145 const key_sym_map& key_binding::getKeySyms() const
146 {
147 return this->m_keySyms;
148 }
149
getKeySymsSet() const150 const key_sym_map& key_binding::getKeySymsSet() const
151 {
152 return this->m_keySymsSet;
153 }
154
getBinds() const155 key_commands_sorted key_binding::getBinds() const
156 {
157 key_commands_sorted sortKeys;
158
159 //any keys
160 for( KeyGroupMap::const_iterator iter = m_groupsAny.begin(); iter != m_groupsAny.end(); ++iter )
161 {
162 for( size_t idx = 0; idx < iter->second.size(); ++idx )
163 {
164 sortKeys.push_back( key_command( iter->first, iter->second[ idx ] ) );
165 }
166 }
167
168 //normal keys
169 for( KeyGroupMap::const_iterator iter = m_groups.begin(); iter != m_groups.end(); ++iter )
170 {
171 for( size_t idx = 0; idx < iter->second.size(); ++idx )
172 {
173 sortKeys.push_back( key_command( iter->first, iter->second[ idx ] ) );
174 }
175 }
176
177 return sortKeys;
178 }
179
isEmpty() const180 bool key_binding::isEmpty() const
181 {
182 if ( m_keyCmdSet.empty() && m_keyCmdSetAny.empty() )
183 {
184 return true;
185 }
186 return false;
187 }
188
unbindAllCmds(const wxString & cmd)189 void key_binding::unbindAllCmds( const wxString& cmd )
190 {
191 key_command_set keyCmdSetCopy = m_keyCmdSet;
192 key_command_set keyCmdSetAnyCopy = m_keyCmdSetAny;
193
194 for ( key_command_set::const_iterator iter = keyCmdSetCopy.begin(); iter != keyCmdSetCopy.end(); ++iter )
195 {
196 if ( key_binding::isCmd1MatchingCmd2( cmd, iter->second ) )
197 {
198 this->unbind( iter->second, iter->first );
199 }
200 }
201
202 for ( key_command_set::const_iterator iter = keyCmdSetAnyCopy.begin(); iter != keyCmdSetAnyCopy.end(); ++iter )
203 {
204 if ( key_binding::isCmd1MatchingCmd2( cmd, iter->second ) )
205 {
206 this->unbind( iter->second, iter->first );
207 }
208 }
209 }
210
unbindAllKeys(const wxString & key)211 void key_binding::unbindAllKeys( const wxString& key )
212 {
213 if ( !key.StartsWith( wxT("Any+") ) )
214 {
215 key_command_set keyCmdSetCopy = m_keyCmdSet;
216 for ( key_command_set::const_iterator iter = keyCmdSetCopy.begin(); iter != keyCmdSetCopy.end(); ++iter )
217 {
218 if ( iter->first == key )
219 {
220 this->unbind( iter->second, iter->first );
221 }
222 }
223 }
224 else
225 {
226 key_command_set keyCmdSetAnyCopy = m_keyCmdSetAny;
227 for ( key_command_set::const_iterator iter = keyCmdSetAnyCopy.begin(); iter != keyCmdSetAnyCopy.end(); ++iter )
228 {
229 if ( iter->first == key )
230 {
231 this->unbind( iter->second, iter->first );
232 }
233 }
234 }
235 }
236
unbind(const wxString & cmd,const wxString & keyString)237 void key_binding::unbind( const wxString& cmd, const wxString& keyString )
238 {
239 if ( !this->exists( cmd, keyString ) )
240 {
241 return;
242 }
243
244 const wxString normKey = KeynameConverter::normalizeSpringKey( keyString );
245 if ( normKey.StartsWith( wxT("Any+") ) )
246 {
247 m_keyCmdSetAny.erase( std::make_pair( normKey, cmd ) );
248
249 key_binding::KeyGroupMap::iterator iter = m_groupsAny.find( normKey );
250 assert( iter != m_groupsAny.end() ); //we can assert this, since we checked for exists() at the beginning
251 iter->second.erase( std::find(iter->second.begin(), iter->second.end(), cmd) );
252
253 if ( iter->second.size() == 0 )
254 m_groupsAny.erase( iter->first );
255 }
256 else
257 {
258 m_keyCmdSet.erase( std::make_pair( normKey, cmd ) );
259
260 key_binding::KeyGroupMap::iterator iter = m_groups.find( normKey );
261 assert( iter != m_groups.end() ); //we can assert this, since we checked for exists() at the beginning
262 iter->second.erase( std::find(iter->second.begin(), iter->second.end(), cmd) );
263
264 if ( iter->second.size() == 0 )
265 m_groups.erase( iter->first );
266 }
267 }
268
exists(const wxString & command,const wxString & key)269 bool key_binding::exists( const wxString& command, const wxString& key )
270 {
271 const wxString& normKey = resolveKeySymName( KeynameConverter::normalizeSpringKey( key ) );
272
273 bool found = false;
274 if ( normKey.StartsWith( wxT("Any+") ) )
275 {
276 if ( m_keyCmdSetAny.find( std::make_pair( normKey, command ) ) != m_keyCmdSetAny.end() )
277 {
278 found = true;
279 }
280 }
281 else
282 {
283 if ( m_keyCmdSet.find( std::make_pair( normKey, command ) ) != m_keyCmdSet.end() )
284 {
285 found = true;
286 }
287 }
288
289 return found;
290 }
291
clear()292 void key_binding::clear()
293 {
294 this->m_groups.clear();
295 this->m_groupsAny.clear();
296 this->m_keyCmdSetAny.clear();
297 this->m_keyCmdSet.clear();
298 this->m_keySyms.clear();
299 this->m_keySymsSet.clear();
300 this->m_meta.clear();
301 }
302
operator ==(const key_binding & other) const303 bool key_binding::operator==(const key_binding& other) const
304 {
305 const bool groups = this->m_groups == other.m_groups;
306 const bool groupsAny = this->m_groupsAny == other.m_groupsAny;
307 const bool keySyms = this->m_keySyms == other.m_keySyms;
308 const bool keySymsSet = this->m_keySymsSet == other.m_keySymsSet;
309 const bool meta = this->m_meta == other.m_meta;
310
311 #ifdef __WXDEBUG__
312 if ( !groups )
313 {
314 for( key_binding::KeyGroupMap::const_iterator iter = this->m_groups.begin(); iter != this->m_groups.end(); ++iter )
315 {
316 key_binding::KeyGroupMap::const_iterator fiter = other.m_groups.find( iter->first );
317
318 if ( fiter != other.m_groups.end() )
319 {
320 if ( iter->second != fiter->second )
321 {
322 wxLogWarning( wxT("Difference in hotkey group: ") + iter->first );
323
324 //const std::vector<wxString> cmdsA = iter->second;
325 //const std::vector<wxString> cmdsB = fiter->second;
326
327 int i=0;
328 ++i;
329 }
330 }
331 else
332 wxLogWarning( wxT("Group ") + iter->first + wxT(" not found in other group!") );
333 }
334 }
335
336 if ( !groupsAny )
337 {
338 for( key_binding::KeyGroupMap::const_iterator iter = this->m_groupsAny.begin(); iter != this->m_groupsAny.end(); ++iter )
339 {
340 key_binding::KeyGroupMap::const_iterator fiter = other.m_groupsAny.find( iter->first );
341
342 if ( fiter != other.m_groupsAny.end() )
343 {
344 if ( iter->second != fiter->second )
345 {
346 wxLogWarning( wxT("Difference in hotkey groupAny: ") + iter->first );
347
348 //const std::vector<wxString> cmdsA = iter->second;
349 //const std::vector<wxString> cmdsB = fiter->second;
350
351 int i=0;
352 ++i;
353 }
354 }
355 else
356 wxLogWarning( wxT("GroupAny ") + iter->first + wxT(" not found in other group!") );
357 }
358 }
359 #endif
360
361 return groups && groupsAny && keySyms && keySymsSet && meta;
362 }
363
operator -(const key_binding & other) const364 const key_binding key_binding::operator-(const key_binding& other) const
365 {
366 key_binding resBind = (*this);
367 resBind.m_groups.clear();
368 resBind.m_groupsAny.clear();
369 resBind.m_keyCmdSet.clear();
370 resBind.m_keyCmdSetAny.clear();
371
372 //normal keys
373 for( KeyGroupMap::const_iterator iter = m_groups.begin(); iter != m_groups.end(); ++iter )
374 {
375 bool diffs = false;
376 for( size_t idx = 0; idx < iter->second.size(); ++idx )
377 {
378 KeyGroupMap::const_iterator findIter = other.m_groups.find( iter->first );
379 if ( ( diffs ) ||
380 ( findIter == other.m_groups.end() ) ||
381 ( iter->second.empty() ) ||
382 ( findIter->second.size() < (idx + 1) ) ||
383 ( findIter->second[idx] != iter->second[idx] ) )
384 {
385 resBind.bind( iter->second[ idx ], iter->first );
386 diffs = true;
387 }
388 }
389 }
390
391 //any-keys
392 for( KeyGroupMap::const_iterator iter = m_groupsAny.begin(); iter != m_groupsAny.end(); ++iter )
393 {
394 bool diffs = false;
395 for( size_t idx = 0; idx < iter->second.size(); ++idx )
396 {
397 KeyGroupMap::const_iterator findIter = other.m_groupsAny.find( iter->first );
398 if ( ( diffs ) ||
399 ( findIter == other.m_groupsAny.end() ) ||
400 ( findIter->second.size() < (idx + 1) ) ||
401 ( findIter->second[idx] != iter->second[idx] ) )
402 {
403 resBind.bind( iter->second[ idx ], iter->first );
404 diffs = true;
405 }
406 }
407 }
408
409 return resBind;
410 }
411
isCmd1MatchingCmd2(const wxString & cmd1,const wxString & cmd2)412 bool key_binding::isCmd1MatchingCmd2( const wxString& cmd1, const wxString& cmd2 )
413 {
414 int cmd1SepPos = cmd1.Find( _T(' ') );
415 int cmd2SepPos = cmd2.Find( _T(' ') );
416
417 if ( (cmd1SepPos != wxNOT_FOUND) || ((cmd1SepPos == wxNOT_FOUND) && (cmd2SepPos == wxNOT_FOUND)) )
418 return cmd1 == cmd2; //simple compare
419
420 //cmd1 has no params, cmd2 has params. like "specteam" and "specteam 5"
421 return ( cmd2.substr(0, cmd2SepPos) == cmd1 ); //just compare "specteam" and "specteam"
422 }
423