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.

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