Audio plugin host https://kx.studio/carla
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.

283 lines
11KB

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