Radium Engine  1.5.0
KeyMappingManager.cpp
1 #include "KeyMappingManager.hpp"
2 
3 #include <Core/Resources/Resources.hpp>
4 #include <Core/Utils/Log.hpp>
5 
6 #include <QMessageBox>
7 #include <QtGlobal> //QT_VERSION, QT_VERSION_CHECK
8 
9 namespace Ra::Gui {
10 
11 using namespace Core::Utils; // log
12 
13 KeyMappingManager::KeyMappingManager() : m_file( nullptr ) {
14 
15  auto optionalPath { Core::Resources::getRadiumResourcesPath() };
16  auto resourcesRootDir { optionalPath.value_or( "[[Default resrouces path not found]]" ) };
17 
19  m_defaultConfigFile = resourcesRootDir + std::string( "Configs/default.xml" );
20 
21  QSettings settings;
22  QString keyMappingFilename =
23  settings.value( "keymapping/config", QString::fromStdString( m_defaultConfigFile ) )
24  .toString();
25  if ( !keyMappingFilename.contains( QString::fromStdString( m_defaultConfigFile ) ) ) {
26  LOG( logDEBUG ) << "Loading key mapping " << keyMappingFilename.toStdString() << " (from "
27  << settings.fileName().toStdString() << ")";
28  }
29  else { LOG( logDEBUG ) << "Loading default key mapping " << m_defaultConfigFile; }
30  loadConfiguration( keyMappingFilename.toStdString() );
31 }
32 
33 KeyMappingManager::KeyMappingAction
34 KeyMappingManager::getAction( const KeyMappingManager::Context& context,
35  const QEvent* event,
36  int key,
37  bool wheel ) {
38 
39  Qt::MouseButtons buttons;
40  Qt::KeyboardModifiers modifiers;
41  const QMouseEvent* mouseEvent = dynamic_cast<const QMouseEvent*>( event );
42  if ( mouseEvent ) {
43  buttons = mouseEvent->buttons();
44  modifiers = mouseEvent->modifiers();
45  }
46  return getAction( context, buttons, modifiers, key, wheel );
47 }
48 
49 KeyMappingManager::KeyMappingAction
50 KeyMappingManager::getAction( const KeyMappingManager::Context& context,
51  const Qt::MouseButtons& buttons,
52  const Qt::KeyboardModifiers& modifiers,
53  int key,
54  bool wheel ) {
55 
56  if ( context.isInvalid() ) {
57  LOG( logDEBUG ) << "try to get action from an invalid context";
58  return KeyMappingAction();
59  }
60  // skip key as modifiers,
61  if ( ( key == Qt::Key_Shift ) || ( key == Qt::Key_Control ) || ( key == Qt::Key_Alt ) ||
62  ( key == Qt::Key_Meta ) ) {
63  key = -1;
64  }
65 
66  return getAction( context, EventBinding { buttons, modifiers, key, wheel } );
67 }
68 
70 KeyMappingManager::getAction( const KeyMappingManager::Context& context,
71  const KeyMappingManager::EventBinding& binding ) {
72  auto action = m_bindingToAction[context].find( binding );
73  if ( action != m_bindingToAction[context].end() ) { return action->second; }
74 
76 }
77 
78 std::optional<KeyMappingManager::EventBinding>
79 KeyMappingManager::getBinding( const KeyMappingManager::Context& context,
80  KeyMappingAction action ) {
81  for ( const auto& [key, value] : m_bindingToAction[context] )
82  if ( value == action ) return key;
83  return {};
84 }
85 
86 KeyMappingManager::Context KeyMappingManager::addContext( const std::string& contextName ) {
87  Ra::Core::Utils::Index contextIndex;
88 
89  auto contextItr = m_contextNameToIndex.find( contextName );
90  if ( contextItr == m_contextNameToIndex.end() ) {
91  contextIndex = m_contextNameToIndex.size();
92  m_contextNameToIndex[contextName] = contextIndex;
93  m_actionNameToIndex.emplace_back();
94  m_bindingToAction.emplace_back();
95  CORE_ASSERT( m_actionNameToIndex.size() == size_t( contextIndex + 1 ),
96  "Corrupted actionName DB" );
97  CORE_ASSERT( m_bindingToAction.size() == size_t( contextIndex + 1 ),
98  "Corrupted mappingAction DB" );
99  }
100  else
101  contextIndex = contextItr->second;
102 
103  return contextIndex;
104 }
105 
106 KeyMappingManager::Context KeyMappingManager::getContext( const std::string& contextName ) {
107  // use find so that it do not insert invalid context
108  auto itr = m_contextNameToIndex.find( contextName );
109  if ( itr != m_contextNameToIndex.end() ) return itr->second;
110  return Context {};
111 }
112 
113 KeyMappingManager::KeyMappingAction KeyMappingManager::getAction( const Context& context,
114  const std::string& actionName ) {
115  if ( size_t( context ) >= m_actionNameToIndex.size() || context.isInvalid() ) {
116  LOG( logWARNING ) << "try to get action index ( " << actionName
117  << " ) from an invalid context ( " << context << " )";
118 
119  return KeyMappingAction {};
120  }
121  auto itr = m_actionNameToIndex[context].find( actionName );
122  if ( itr != m_actionNameToIndex[context].end() ) return itr->second;
123  LOG( logWARNING ) << "try to get action index from an invalid action name " << actionName
124  << " (context " << getContextName( context ) << " [" << context << "])";
125  LOG( logWARNING ) << "consider add to conf: "
126  << "<keymap context=\"" << getContextName( context )
127  << "\" key=\"\" modifiers=\"\" buttons=\"\" action=\"" << actionName
128  << "\"/>";
129 
130  return KeyMappingAction {};
131 }
132 
133 std::string KeyMappingManager::getActionName( const Context& context,
134  const KeyMappingAction& action ) {
135 
136  if ( size_t( context ) < m_actionNameToIndex.size() && context.isValid() ) {
137 
138  auto actionFindItr = std::find_if(
139  std::begin( m_actionNameToIndex[context] ),
140  std::end( m_actionNameToIndex[context] ),
141  [&]( const ActionNameMap::value_type& pair ) { return pair.second == action; } );
142 
143  if ( actionFindItr != std::end( m_actionNameToIndex[context] ) ) {
144  return actionFindItr->first;
145  }
146  }
147  return "Invalid";
148 }
149 
150 std::string KeyMappingManager::getContextName( const Context& context ) {
151  auto contextFindItr = std::find_if(
152  std::begin( m_contextNameToIndex ),
153  std::end( m_contextNameToIndex ),
154  [&]( const ContextNameMap ::value_type& pair ) { return pair.second == context; } );
155 
156  if ( contextFindItr != std::end( m_contextNameToIndex ) ) { return contextFindItr->first; }
157  return "Invalid";
158 }
159 
160 int KeyMappingManager::addListener( Observer callback ) {
161  auto gid = attach( callback );
162  // call the registered listener directly to have it up to date if the
163  // config is already loaded
164  callback();
165  return gid;
166 }
167 
168 void KeyMappingManager::removeListener( int callbackId ) {
169  detach( callbackId );
170 }
171 
172 void KeyMappingManager::setActionBinding( const Context& contextIndex,
173  const EventBinding& binding,
174  const KeyMappingAction& actionIndex ) {
175 
176  CORE_ASSERT( size_t( contextIndex ) < m_contextNameToIndex.size(),
177  "contextIndex is out of range" );
178 
179  // search if an action already correspond to this binding.
180  auto f = m_bindingToAction[contextIndex].find( binding );
181  if ( f != m_bindingToAction[contextIndex].end() ) {
182 
183  // if yes, search for its name
184  auto findResult = std::find_if(
185  std::begin( m_actionNameToIndex[contextIndex] ),
186  std::end( m_actionNameToIndex[contextIndex] ),
187  [&]( const ActionNameMap::value_type& pair ) { return pair.second == actionIndex; } );
188 
189  // if the name is not present, something bad happens.
190  if ( findResult == std::end( m_actionNameToIndex[contextIndex] ) ) {
191  LOG( logERROR ) << "Corrupted call to bindKeyToAction, index " << actionIndex
192  << " must have been inserted before !\n";
193  return;
194  }
195 
196  // if name is present, find the other action's name
197  auto findResult2 = std::find_if(
198  std::begin( m_actionNameToIndex[contextIndex] ),
199  std::end( m_actionNameToIndex[contextIndex] ),
200  [&]( const ActionNameMap::value_type& pair ) { return pair.second == f->second; } );
201 
202  // if the name is not present, something bad happens.
203  if ( findResult2 == std::end( m_actionNameToIndex[contextIndex] ) ) {
204  LOG( logERROR ) << "Corrupted call to bindKeyToAction, index " << actionIndex
205  << " must have been inserted before !\n";
206  return;
207  }
208 
209  LOG( logWARNING ) << "Binding action " << findResult->first << " to "
210  << "buttons [" << enumNamesFromMouseButtons( binding.m_buttons ) << "] "
211  << "modifiers [" << enumNamesFromKeyboardModifiers( binding.m_modifiers )
212  << "]"
213  << " keycode [" << binding.m_key << "]"
214  << " wheel [" << binding.m_wheel << "]"
215  << ", which is already used for action " << findResult2->first << ".";
216  }
217 
218  LOG( logDEBUG2 ) << "In context " << getContextName( contextIndex ) << " [" << contextIndex
219  << "]"
220  << " binding action " << getActionName( contextIndex, actionIndex ) << " ["
221  << actionIndex << "]"
222  << " buttons [" << enumNamesFromMouseButtons( binding.m_buttons ) << "]"
223  << " modifiers [" << enumNamesFromKeyboardModifiers( binding.m_modifiers )
224  << "]"
225  << " keycode [" << binding.m_key << "]"
226  << " wheel [" << binding.m_wheel << "]";
227 
228  m_bindingToAction[contextIndex][binding] = actionIndex;
229 }
230 
231 void KeyMappingManager::loadConfiguration( const std::string& inFilename ) {
232  // if no filename is given, load default configuration
233  std::string filename = inFilename;
234  if ( filename.empty() ) { filename = m_defaultConfigFile.c_str(); }
235 
236  delete m_file;
237  m_file = new QFile( QString::fromStdString( filename ) );
238 
239  if ( !m_file->open( QIODevice::ReadOnly ) ) {
240  if ( filename != m_defaultConfigFile ) {
241  LOG( logERROR ) << "Failed to open key mapping configuration file ! "
242  << m_file->fileName().toStdString();
243  LOG( logERROR ) << "Trying to load default configuration...";
244  loadConfiguration();
245  return;
246  }
247  else {
248  LOG( logERROR ) << "Failed to open default key mapping configuration file !";
249  return;
250  }
251  }
252  QDomDocument domDocument;
253  if ( !domDocument.setContent( m_file ) ) {
254  LOG( logERROR ) << "Can't associate XML file to QDomDocument !";
255  LOG( logERROR ) << "Trying to load default configuration...";
256  m_file->close();
257  loadConfiguration();
258  return;
259  }
260 
261  // Store setting only if not default
262  if ( filename != m_defaultConfigFile ) {
263  QSettings settings;
264  settings.setValue( "keymapping/config", m_file->fileName() );
265  }
266  m_file->close();
267 
268  loadConfigurationInternal( domDocument );
269 
270  // notify observer that keymapping has changed.
271  notify();
272 }
273 
274 bool KeyMappingManager::saveConfiguration( const std::string& inFilename ) {
275  QString filename { m_file->fileName() };
276  if ( !inFilename.empty() ) { filename = QString::fromStdString( inFilename ); }
277 
278  QFile saveTo( filename );
279  saveTo.open( QIODevice::WriteOnly );
280  QXmlStreamWriter stream( &saveTo );
281  stream.setAutoFormatting( true );
282  stream.setAutoFormattingIndent( 4 );
283  stream.writeStartDocument();
284  stream.writeComment( "\tRadium KeyMappingManager configuration file\t" );
285  stream.writeComment(
286  "\n<keymap context=\"theContext\" action=\"theAction\" buttons=\"QButton\" "
287  "modifier=\"QModifier\" key=\"QKey\" wheel=\"boolean\"/>\n" );
288 
289  saveKeymap( stream );
290 
291  stream.writeEndDocument();
292 
293  if ( stream.hasError() ) {
294  LOG( logERROR ) << "Fail to write Canonical XML.";
295  return false;
296  }
297  return true;
298 }
299 
300 void KeyMappingManager::saveKeymap( QXmlStreamWriter& stream ) {
301 
302  // helper functor to write attrib
303  auto saveAttrib = [&stream]( const QString& attribName,
304  const QString& attribValue,
305  const QString& attribDefault ) {
306  if ( attribValue != attribDefault ) stream.writeAttribute( attribName, attribValue );
307  };
308 
309  if ( stream.hasError() ) { return; }
310 
311  stream.writeStartElement( "keymaps" );
312 
313  for ( const auto& contextPair : m_contextNameToIndex ) {
314  const auto& contextName = contextPair.first;
315  const auto& context = contextPair.second;
316  for ( const auto& actionPair : m_bindingToAction[context] ) {
317  const auto& binding = actionPair.first;
318  const auto& action = actionPair.second;
319  const auto& actionNames = m_actionNameToIndex[context];
320  auto actionItr =
321  std::find_if( actionNames.begin(), actionNames.end(), [&action]( const auto& a ) {
322  return a.second == action;
323  } );
324  if ( actionItr != actionNames.end() ) {
325  const auto& actionName = actionItr->first;
326  stream.writeStartElement( "keymap" );
327  stream.writeAttribute( "context", QString::fromStdString( contextName ) );
328  stream.writeAttribute( "action", QString::fromStdString( actionName ) );
329 
330  saveAttrib(
331  "buttons",
332  QString::fromStdString( enumNamesFromMouseButtons( binding.m_buttons ) ),
333  QString::fromStdString( enumNamesFromMouseButtons( Qt::NoButton ) ) );
334  saveAttrib(
335  "modifiers",
336  QString::fromStdString( enumNamesFromKeyboardModifiers( binding.m_modifiers ) ),
337  QString::fromStdString( enumNamesFromKeyboardModifiers( Qt::NoModifier ) ) );
338  saveAttrib( "key", "Key_" + QKeySequence( binding.m_key ).toString(), "Key_" );
339  saveAttrib( "wheel", binding.m_wheel ? "true" : "false", "false" );
340 
341  stream.writeEndElement();
342  }
343  }
344  }
345 
346  stream.writeEndElement();
347 }
348 
349 void KeyMappingManager::loadConfigurationInternal( const QDomDocument& domDocument ) {
352  m_contextNameToIndex.clear();
353  m_actionNameToIndex.clear();
354  m_bindingToAction.clear();
355 
356  QDomElement domElement = domDocument.documentElement();
357 
358  if ( domElement.tagName() != "keymaps" ) {
359  LOG( logWARNING ) << "No <keymaps> global bounding tag ! Maybe you set a different global "
360  "tag ? (Not a big deal)";
361  }
362 
363  QDomNode node = domElement.firstChild();
364  while ( !node.isNull() ) {
365  if ( !node.isComment() ) {
366  QDomElement nodeElement = node.toElement();
367  loadConfigurationTagsInternal( nodeElement );
368  }
369  node = node.nextSibling();
370  }
371 }
372 
373 void KeyMappingManager::loadConfigurationTagsInternal( QDomElement& node ) {
374  if ( node.tagName() == "keymap" ) {
375 
376  QDomElement e = node.toElement();
377  std::string keyString = e.attribute( "key", "-1" ).toStdString();
378  std::string modifiersString = e.attribute( "modifiers", "NoModifier" ).toStdString();
379  std::string buttonsString = e.attribute( "buttons", "NoButton" ).toStdString();
380  std::string contextName = e.attribute( "context", "AppContext" ).toStdString();
381  std::string wheelString = e.attribute( "wheel", "false" ).toStdString();
382  std::string actionName = e.attribute( "action" ).toStdString();
383 
384  loadConfigurationMappingInternal(
385  contextName, keyString, modifiersString, buttonsString, wheelString, actionName );
386  }
387  else {
388  LOG( logERROR ) << "Unrecognized XML key mapping configuration file tag \""
389  << qPrintable( node.tagName() ) << "\" !";
390  LOG( logERROR ) << "Trying to load default configuration...";
391  loadConfiguration();
392 
393  return;
394  }
395 }
396 
397 KeyMappingManager::KeyMappingAction KeyMappingManager::addAction( const Context& context,
398  const std::string& actionName ) {
399  Ra::Core::Utils::Index actionIndex;
400  auto actionItr = m_actionNameToIndex[context].find( actionName );
401  if ( actionItr == m_actionNameToIndex[context].end() ) {
402  actionIndex = m_actionNameToIndex[context].size();
403  m_actionNameToIndex[context][actionName] = actionIndex;
404  }
405  else { actionIndex = actionItr->second; }
406 
407  return actionIndex;
408 }
409 
410 KeyMappingManager::KeyMappingAction KeyMappingManager::addAction( const Context& context,
411  const EventBinding& binding,
412  const std::string& actionName ) {
413  auto actionIndex = addAction( context, actionName );
414  setActionBinding( context, binding, actionIndex );
415  return actionIndex;
416 }
417 
418 int KeyMappingManager::getKeyCode( const std::string& keyString ) {
419  auto metaEnumKey = QMetaEnum::fromType<Qt::Key>();
420  return metaEnumKey.keyToValue( keyString.c_str() );
421 }
422 
424 KeyMappingManager::loadConfigurationMappingInternal( const std::string& context,
425  const std::string& keyString,
426  const std::string& modifiersString,
427  const std::string& buttonsString,
428  const std::string& wheelString,
429  const std::string& actionName ) {
430  auto contextIndex = addContext( context );
431  auto actionIndex = addAction( contextIndex, actionName );
432 
433  auto binding =
434  createEventBindingFromStrings( buttonsString, modifiersString, keyString, wheelString );
435 
436  if ( binding.m_key == -1 && binding.m_buttons == Qt::NoButton && !binding.m_wheel ) {
437  LOG( logERROR ) << "Invalid binding for action [" << actionName << "] with key ["
438  << keyString << "], buttons [" << buttonsString << "], wheel ["
439  << wheelString << "]";
440  }
441  else { setActionBinding( contextIndex, binding, actionIndex ); }
442  return actionIndex;
443 }
444 
445 Qt::KeyboardModifiers KeyMappingManager::getQtModifiersValue( const std::string& modifierString ) {
446  Qt::KeyboardModifiers modifier = Qt::NoModifier;
447 
448  std::istringstream f( modifierString );
449  std::string s;
450 
451  while ( getline( f, s, ',' ) ) {
452  if ( s == "ShiftModifier" ) { modifier |= Qt::ShiftModifier; }
453  else if ( s == "ControlModifier" ) { modifier |= Qt::ControlModifier; }
454  else if ( s == "AltModifier" ) { modifier |= Qt::AltModifier; }
455  else if ( s == "MetaModifier" ) { modifier |= Qt::MetaModifier; }
456  else if ( s == "KeypadModifier" ) { modifier |= Qt::KeypadModifier; }
457  else if ( s == "GroupSwitchModifier" ) { modifier |= Qt::GroupSwitchModifier; }
458  }
459 
460  return modifier;
461 }
462 
463 Qt::MouseButtons KeyMappingManager::getQtMouseButtonsValue( const std::string& keyString ) {
464  Qt::MouseButtons key = Qt::NoButton;
465 
466  if ( keyString == "LeftButton" ) { key = Qt::LeftButton; }
467  else if ( keyString == "RightButton" ) { key = Qt::RightButton; }
468  else if ( keyString == "MidButton" || keyString == "MiddleButton" ) { key = Qt::MiddleButton; }
469  else if ( keyString == "XButton1" ) { key = Qt::XButton1; }
470  else if ( keyString == "XButton2" ) { key = Qt::XButton2; }
472  return key;
473 }
474 
475 void KeyMappingManager::reloadConfiguration() {
476  QString filename = m_file->fileName();
477  loadConfiguration( filename.toStdString().c_str() );
478 }
479 
480 KeyMappingManager::~KeyMappingManager() {
481  if ( m_file->isOpen() ) { m_file->close(); }
482 }
483 
484 std::string KeyMappingManager::getHelpText() {
485  std::ostringstream text;
486  for ( const auto& context : m_contextNameToIndex ) {
487  std::string contextName { context.first };
488  auto end = contextName.find( "Context" );
489  contextName = contextName.substr( 0, end );
490  text << "<h2>" << contextName << "</h2>\n";
491 
492  for ( const auto& action : m_bindingToAction[context.second] ) {
493  const auto& binding = action.first;
494  const auto& actionIndex = action.second;
495  const auto& actionNames = m_actionNameToIndex[context.second];
496  const auto& actionName =
497  std::find_if( actionNames.begin(),
498  actionNames.end(),
499  [&actionIndex]( const auto& a ) { return a.second == actionIndex; } );
500  if ( actionName != actionNames.end() ) {
501  text << "<tr><td>";
502  text << "<b>" << actionName->first << "</b>";
503  text << "</td><td>";
504  if ( binding.m_buttons != Qt::NoButton )
505  text << "[" << enumNamesFromMouseButtons( binding.m_buttons ) << "] ";
506  if ( binding.m_wheel ) text << " [Wheel] ";
507  if ( binding.m_modifiers != Qt::NoModifier ) {
508 
509  std::string modifierString { "Modifier" };
510  auto modifiers = enumNamesFromKeyboardModifiers( binding.m_modifiers );
511  auto found = modifiers.find( modifierString );
512  while ( found != std::string::npos ) {
513  modifiers.erase( found, modifierString.length() );
514  found = modifiers.find( modifierString, found );
515  }
516  text << "[" << modifiers << "] ";
517  }
518  if ( binding.m_key != -1 )
519  text << "[key: " << QKeySequence( binding.m_key ).toString().toStdString()
520  << "] ";
521  text << "</td></tr>\n";
522  }
523  }
524  text << "</table>\n";
525  }
526  return text.str();
527 }
528 
529 #define TEST_BUTTON_STRING( BUTTON ) \
530  if ( buttons & Qt::BUTTON ) { \
531  returnText += sep + #BUTTON; \
532  sep = ","; \
533  }
534 
535 std::string KeyMappingManager::enumNamesFromMouseButtons( const Qt::MouseButtons& buttons ) {
536  std::string returnText;
537  std::string sep;
538 
539  if ( buttons == Qt::NoButton ) return "NoButton";
540 
541  TEST_BUTTON_STRING( LeftButton );
542  TEST_BUTTON_STRING( RightButton );
543  TEST_BUTTON_STRING( MiddleButton );
544  TEST_BUTTON_STRING( BackButton );
545  TEST_BUTTON_STRING( ForwardButton );
546  TEST_BUTTON_STRING( TaskButton );
547  TEST_BUTTON_STRING( ExtraButton4 );
548  TEST_BUTTON_STRING( ExtraButton5 );
549  TEST_BUTTON_STRING( ExtraButton6 );
550  TEST_BUTTON_STRING( ExtraButton7 );
551  TEST_BUTTON_STRING( ExtraButton8 );
552  TEST_BUTTON_STRING( ExtraButton9 );
553  TEST_BUTTON_STRING( ExtraButton10 );
554  TEST_BUTTON_STRING( ExtraButton11 );
555  TEST_BUTTON_STRING( ExtraButton12 );
556  TEST_BUTTON_STRING( ExtraButton13 );
557  TEST_BUTTON_STRING( ExtraButton14 );
558  TEST_BUTTON_STRING( ExtraButton15 );
559  TEST_BUTTON_STRING( ExtraButton16 );
560  TEST_BUTTON_STRING( ExtraButton17 );
561  TEST_BUTTON_STRING( ExtraButton18 );
562  TEST_BUTTON_STRING( ExtraButton19 );
563  TEST_BUTTON_STRING( ExtraButton20 );
564  TEST_BUTTON_STRING( ExtraButton21 );
565  TEST_BUTTON_STRING( ExtraButton22 );
566  TEST_BUTTON_STRING( ExtraButton23 );
567  TEST_BUTTON_STRING( ExtraButton24 );
568 
569  return returnText;
570 }
571 
572 std::string
573 KeyMappingManager::enumNamesFromKeyboardModifiers( const Qt::KeyboardModifiers& buttons ) {
574  std::string returnText;
575  std::string sep;
576 
577  if ( buttons == Qt::NoModifier ) return "NoModifier";
578 
579  TEST_BUTTON_STRING( ShiftModifier );
580  TEST_BUTTON_STRING( ControlModifier );
581  TEST_BUTTON_STRING( AltModifier );
582  TEST_BUTTON_STRING( MetaModifier );
583  TEST_BUTTON_STRING( KeypadModifier );
584  TEST_BUTTON_STRING( GroupSwitchModifier );
585 
586  return returnText;
587 }
588 
589 #undef TEST_BUTTON_STRING
590 
592 KeyMappingManager::createEventBindingFromStrings( const std::string& buttonsString,
593  const std::string& modifiersString,
594  const std::string& keyString,
595  const std::string& wheelString ) {
596  return { getQtMouseButtonsValue( buttonsString ),
597  getQtModifiersValue( modifiersString ),
598  getKeyCode( keyString ),
599  wheelString.compare( "true" ) == 0 };
600 }
601 
602 RA_SINGLETON_IMPLEMENTATION( KeyMappingManager );
603 } // namespace Ra::Gui
Inner class to store event binding.
An utility class used to map a (combination of) key / modifiers / mouse buttons to a specific action....
Ra::Core::Utils::Index KeyMappingAction
handle to an action
Ra::Core::Utils::Index Context
handle to a Context