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