The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

291 lines
11KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2017 - ROLI Ltd.
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 5 End-User License
  8. Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
  9. 27th April 2017).
  10. End User License Agreement: www.juce.com/juce-5-licence
  11. Privacy Policy: www.juce.com/juce-5-privacy-policy
  12. Or: You may also use this code under the terms of the GPL v3 (see
  13. www.gnu.org/licenses).
  14. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  15. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  16. DISCLAIMED.
  17. ==============================================================================
  18. */
  19. namespace juce
  20. {
  21. KeyPress::KeyPress (int code, ModifierKeys m, juce_wchar textChar) noexcept
  22. : keyCode (code), mods (m), textCharacter (textChar)
  23. {
  24. }
  25. KeyPress::KeyPress (const int code) noexcept : keyCode (code)
  26. {
  27. }
  28. bool KeyPress::operator== (int otherKeyCode) const noexcept
  29. {
  30. return keyCode == otherKeyCode && ! mods.isAnyModifierKeyDown();
  31. }
  32. bool KeyPress::operator== (const KeyPress& other) const noexcept
  33. {
  34. return mods.getRawFlags() == other.mods.getRawFlags()
  35. && (textCharacter == other.textCharacter
  36. || textCharacter == 0
  37. || other.textCharacter == 0)
  38. && (keyCode == other.keyCode
  39. || (keyCode < 256
  40. && other.keyCode < 256
  41. && CharacterFunctions::toLowerCase ((juce_wchar) keyCode)
  42. == CharacterFunctions::toLowerCase ((juce_wchar) other.keyCode)));
  43. }
  44. bool KeyPress::operator!= (const KeyPress& other) const noexcept { return ! operator== (other); }
  45. bool KeyPress::operator!= (int otherKeyCode) const noexcept { return ! operator== (otherKeyCode); }
  46. bool KeyPress::isCurrentlyDown() const
  47. {
  48. return isKeyCurrentlyDown (keyCode)
  49. && (ModifierKeys::currentModifiers.getRawFlags() & ModifierKeys::allKeyboardModifiers)
  50. == (mods.getRawFlags() & ModifierKeys::allKeyboardModifiers);
  51. }
  52. //==============================================================================
  53. namespace KeyPressHelpers
  54. {
  55. struct KeyNameAndCode
  56. {
  57. const char* name;
  58. int code;
  59. };
  60. const KeyNameAndCode translations[] =
  61. {
  62. { "spacebar", KeyPress::spaceKey },
  63. { "return", KeyPress::returnKey },
  64. { "escape", KeyPress::escapeKey },
  65. { "backspace", KeyPress::backspaceKey },
  66. { "cursor left", KeyPress::leftKey },
  67. { "cursor right", KeyPress::rightKey },
  68. { "cursor up", KeyPress::upKey },
  69. { "cursor down", KeyPress::downKey },
  70. { "page up", KeyPress::pageUpKey },
  71. { "page down", KeyPress::pageDownKey },
  72. { "home", KeyPress::homeKey },
  73. { "end", KeyPress::endKey },
  74. { "delete", KeyPress::deleteKey },
  75. { "insert", KeyPress::insertKey },
  76. { "tab", KeyPress::tabKey },
  77. { "play", KeyPress::playKey },
  78. { "stop", KeyPress::stopKey },
  79. { "fast forward", KeyPress::fastForwardKey },
  80. { "rewind", KeyPress::rewindKey }
  81. };
  82. struct ModifierDescription
  83. {
  84. const char* name;
  85. int flag;
  86. };
  87. static const ModifierDescription modifierNames[] =
  88. {
  89. { "ctrl", ModifierKeys::ctrlModifier },
  90. { "control", ModifierKeys::ctrlModifier },
  91. { "ctl", ModifierKeys::ctrlModifier },
  92. { "shift", ModifierKeys::shiftModifier },
  93. { "shft", ModifierKeys::shiftModifier },
  94. { "alt", ModifierKeys::altModifier },
  95. { "option", ModifierKeys::altModifier },
  96. { "command", ModifierKeys::commandModifier },
  97. { "cmd", ModifierKeys::commandModifier }
  98. };
  99. static const char* numberPadPrefix() noexcept { return "numpad "; }
  100. static int getNumpadKeyCode (const String& desc)
  101. {
  102. if (desc.containsIgnoreCase (numberPadPrefix()))
  103. {
  104. auto lastChar = desc.trimEnd().getLastCharacter();
  105. switch (lastChar)
  106. {
  107. case '0': case '1': case '2': case '3': case '4':
  108. case '5': case '6': case '7': case '8': case '9':
  109. return (int) (KeyPress::numberPad0 + (int) lastChar - '0');
  110. case '+': return KeyPress::numberPadAdd;
  111. case '-': return KeyPress::numberPadSubtract;
  112. case '*': return KeyPress::numberPadMultiply;
  113. case '/': return KeyPress::numberPadDivide;
  114. case '.': return KeyPress::numberPadDecimalPoint;
  115. case '=': return KeyPress::numberPadEquals;
  116. default: break;
  117. }
  118. if (desc.endsWith ("separator")) return KeyPress::numberPadSeparator;
  119. if (desc.endsWith ("delete")) return KeyPress::numberPadDelete;
  120. }
  121. return 0;
  122. }
  123. #if JUCE_MAC
  124. struct OSXSymbolReplacement
  125. {
  126. const char* text;
  127. juce_wchar symbol;
  128. };
  129. const OSXSymbolReplacement osxSymbols[] =
  130. {
  131. { "shift + ", 0x21e7 },
  132. { "command + ", 0x2318 },
  133. { "option + ", 0x2325 },
  134. { "ctrl + ", 0x2303 },
  135. { "return", 0x21b5 },
  136. { "cursor left", 0x2190 },
  137. { "cursor right", 0x2192 },
  138. { "cursor up", 0x2191 },
  139. { "cursor down", 0x2193 },
  140. { "backspace", 0x232b },
  141. { "delete", 0x2326 },
  142. { "spacebar", 0x2423 }
  143. };
  144. #endif
  145. }
  146. //==============================================================================
  147. KeyPress KeyPress::createFromDescription (const String& desc)
  148. {
  149. int modifiers = 0;
  150. for (int i = 0; i < numElementsInArray (KeyPressHelpers::modifierNames); ++i)
  151. if (desc.containsWholeWordIgnoreCase (KeyPressHelpers::modifierNames[i].name))
  152. modifiers |= KeyPressHelpers::modifierNames[i].flag;
  153. int key = 0;
  154. for (int i = 0; i < numElementsInArray (KeyPressHelpers::translations); ++i)
  155. {
  156. if (desc.containsWholeWordIgnoreCase (String (KeyPressHelpers::translations[i].name)))
  157. {
  158. key = KeyPressHelpers::translations[i].code;
  159. break;
  160. }
  161. }
  162. if (key == 0)
  163. key = KeyPressHelpers::getNumpadKeyCode (desc);
  164. if (key == 0)
  165. {
  166. // see if it's a function key..
  167. if (! desc.containsChar ('#')) // avoid mistaking hex-codes like "#f1"
  168. {
  169. for (int i = 1; i <= 35; ++i)
  170. {
  171. if (desc.containsWholeWordIgnoreCase ("f" + String (i)))
  172. {
  173. if (i <= 16) key = F1Key + i - 1;
  174. else if (i <= 24) key = F17Key + i - 17;
  175. else if (i <= 35) key = F25Key + i - 25;
  176. }
  177. }
  178. }
  179. if (key == 0)
  180. {
  181. // give up and use the hex code..
  182. auto hexCode = desc.fromFirstOccurrenceOf ("#", false, false)
  183. .retainCharacters ("0123456789abcdefABCDEF")
  184. .getHexValue32();
  185. if (hexCode > 0)
  186. key = hexCode;
  187. else
  188. key = (int) CharacterFunctions::toUpperCase (desc.getLastCharacter());
  189. }
  190. }
  191. return KeyPress (key, ModifierKeys (modifiers), 0);
  192. }
  193. String KeyPress::getTextDescription() const
  194. {
  195. String desc;
  196. if (keyCode > 0)
  197. {
  198. // some keyboard layouts use a shift-key to get the slash, but in those cases, we
  199. // want to store it as being a slash, not shift+whatever.
  200. if (textCharacter == '/' && keyCode != numberPadDivide)
  201. return "/";
  202. if (mods.isCtrlDown()) desc << "ctrl + ";
  203. if (mods.isShiftDown()) desc << "shift + ";
  204. #if JUCE_MAC
  205. if (mods.isAltDown()) desc << "option + ";
  206. if (mods.isCommandDown()) desc << "command + ";
  207. #else
  208. if (mods.isAltDown()) desc << "alt + ";
  209. #endif
  210. for (int i = 0; i < numElementsInArray (KeyPressHelpers::translations); ++i)
  211. if (keyCode == KeyPressHelpers::translations[i].code)
  212. return desc + KeyPressHelpers::translations[i].name;
  213. // not all F keys have consecutive key codes on all platforms
  214. if (keyCode >= F1Key && keyCode <= F16Key) desc << 'F' << (1 + keyCode - F1Key);
  215. else if (keyCode >= F17Key && keyCode <= F24Key) desc << 'F' << (17 + keyCode - F17Key);
  216. else if (keyCode >= F25Key && keyCode <= F35Key) desc << 'F' << (25 + keyCode - F25Key);
  217. else if (keyCode >= numberPad0 && keyCode <= numberPad9) desc << KeyPressHelpers::numberPadPrefix() << (keyCode - numberPad0);
  218. else if (keyCode >= 33 && keyCode < 176) desc += CharacterFunctions::toUpperCase ((juce_wchar) keyCode);
  219. else if (keyCode == numberPadAdd) desc << KeyPressHelpers::numberPadPrefix() << '+';
  220. else if (keyCode == numberPadSubtract) desc << KeyPressHelpers::numberPadPrefix() << '-';
  221. else if (keyCode == numberPadMultiply) desc << KeyPressHelpers::numberPadPrefix() << '*';
  222. else if (keyCode == numberPadDivide) desc << KeyPressHelpers::numberPadPrefix() << '/';
  223. else if (keyCode == numberPadSeparator) desc << KeyPressHelpers::numberPadPrefix() << "separator";
  224. else if (keyCode == numberPadDecimalPoint) desc << KeyPressHelpers::numberPadPrefix() << '.';
  225. else if (keyCode == numberPadEquals) desc << KeyPressHelpers::numberPadPrefix() << '=';
  226. else if (keyCode == numberPadDelete) desc << KeyPressHelpers::numberPadPrefix() << "delete";
  227. else desc << '#' << String::toHexString (keyCode);
  228. }
  229. return desc;
  230. }
  231. String KeyPress::getTextDescriptionWithIcons() const
  232. {
  233. #if JUCE_MAC
  234. auto s = getTextDescription();
  235. for (int i = 0; i < numElementsInArray (KeyPressHelpers::osxSymbols); ++i)
  236. s = s.replace (KeyPressHelpers::osxSymbols[i].text,
  237. String::charToString (KeyPressHelpers::osxSymbols[i].symbol));
  238. return s;
  239. #else
  240. return getTextDescription();
  241. #endif
  242. }
  243. } // namespace juce