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.

300 lines
11KB

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