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.

387 lines
12KB

  1. //
  2. // "$Id: fl_shortcut.cxx 8621 2011-04-23 15:46:30Z AlbrechtS $"
  3. //
  4. // Shortcut support routines for the Fast Light Tool Kit (FLTK).
  5. //
  6. // Copyright 1998-2011 by Bill Spitzak and others.
  7. //
  8. // This library is free software; you can redistribute it and/or
  9. // modify it under the terms of the GNU Library General Public
  10. // License as published by the Free Software Foundation; either
  11. // version 2 of the License, or (at your option) any later version.
  12. //
  13. // This library is distributed in the hope that it will be useful,
  14. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  16. // Library General Public License for more details.
  17. //
  18. // You should have received a copy of the GNU Library General Public
  19. // License along with this library; if not, write to the Free Software
  20. // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
  21. // USA.
  22. //
  23. // Please report all bugs and problems on the following page:
  24. //
  25. // http://www.fltk.org/str.php
  26. //
  27. // Code to test and parse fltk shortcut numbers.
  28. //
  29. // A shortcut is a keysym or'd with shift flags. In the simplest
  30. // sense a shortcut is matched if the shift state is exactly as
  31. // given and the key returning that keysym is pressed.
  32. //
  33. // To make it easier to match some things it is more complex:
  34. //
  35. // Only FL_META, FL_ALT, FL_SHIFT, and FL_CTRL must be "off". A
  36. // zero in the other shift flags indicates "don't care".
  37. //
  38. // It also checks against the first character of Fl::event_text(),
  39. // and zero for FL_SHIFT means "don't care".
  40. // This allows punctuation shortcuts like "#" to work (rather than
  41. // calling it "shift+3" on a US keyboard)
  42. #include <FL/Fl.H>
  43. #include <FL/Fl_Widget.H>
  44. #include <FL/Fl_Button.H>
  45. #include <FL/fl_draw.H>
  46. #include <ctype.h>
  47. #include "flstring.h"
  48. #if !defined(WIN32) && !defined(__APPLE__)
  49. #include <FL/x.H>
  50. #endif
  51. /**
  52. Tests the current event, which must be an FL_KEYBOARD or
  53. FL_SHORTCUT, against a shortcut value (described in Fl_Button).
  54. Not to be confused with Fl_Widget::test_shortcut().
  55. \return non-zero if there is a match.
  56. */
  57. int Fl::test_shortcut(unsigned int shortcut) {
  58. if (!shortcut) return 0;
  59. unsigned int v = shortcut & FL_KEY_MASK;
  60. if (((unsigned)fl_tolower(v))!=v) {
  61. shortcut |= FL_SHIFT;
  62. }
  63. int shift = Fl::event_state();
  64. // see if any required shift flags are off:
  65. if ((shortcut&shift) != (shortcut&0x7fff0000)) return 0;
  66. // record shift flags that are wrong:
  67. int mismatch = (shortcut^shift)&0x7fff0000;
  68. // these three must always be correct:
  69. if (mismatch&(FL_META|FL_ALT|FL_CTRL)) return 0;
  70. unsigned int key = shortcut & FL_KEY_MASK;
  71. // if shift is also correct, check for exactly equal keysyms:
  72. if (!(mismatch&(FL_SHIFT)) && key == (unsigned)Fl::event_key()) return 1;
  73. // try matching utf8, ignore shift:
  74. unsigned int firstChar = fl_utf8decode(Fl::event_text(), Fl::event_text()+Fl::event_length(), 0);
  75. if ( ! (FL_CAPS_LOCK&shift) && key==firstChar) return 1;
  76. // kludge so that Ctrl+'_' works (as opposed to Ctrl+'^_'):
  77. if ((shift&FL_CTRL) && key >= 0x3f && key <= 0x5F
  78. && firstChar==(key^0x40)) return 1; // firstChar should be within a-z
  79. return 0;
  80. }
  81. // This table must be in numeric order by fltk (X) keysym number:
  82. struct Keyname {unsigned int key; const char* name;};
  83. #if defined(WIN32)
  84. static Keyname table[] = {
  85. {' ', "Space"},
  86. {FL_BackSpace, "Backspace"},
  87. {FL_Tab, "Tab"},
  88. {0xff0b/*XK_Clear*/, "Clear"},
  89. {FL_Enter, "Enter"}, // X says "Enter"
  90. {FL_Pause, "Pause"},
  91. {FL_Scroll_Lock, "Scroll_Lock"},
  92. {FL_Escape, "Escape"},
  93. {FL_Home, "Home"},
  94. {FL_Left, "Left"},
  95. {FL_Up, "Up"},
  96. {FL_Right, "Right"},
  97. {FL_Down, "Down"},
  98. {FL_Page_Up, "Page_Up"}, // X says "Prior"
  99. {FL_Page_Down,"Page_Down"}, // X says "Next"
  100. {FL_End, "End"},
  101. {FL_Print, "Print"},
  102. {FL_Insert, "Insert"},
  103. {FL_Menu, "Menu"},
  104. {FL_Num_Lock, "Num_Lock"},
  105. {FL_KP_Enter, "KP_Enter"},
  106. {FL_Shift_L, "Shift_L"},
  107. {FL_Shift_R, "Shift_R"},
  108. {FL_Control_L,"Control_L"},
  109. {FL_Control_R,"Control_R"},
  110. {FL_Caps_Lock,"Caps_Lock"},
  111. {FL_Meta_L, "Meta_L"},
  112. {FL_Meta_R, "Meta_R"},
  113. {FL_Alt_L, "Alt_L"},
  114. {FL_Alt_R, "Alt_R"},
  115. {FL_Delete, "Delete"}
  116. };
  117. #elif defined(__APPLE__)
  118. static Keyname table[] = {
  119. // v - this column contains UTF-8 characters
  120. {' ', "Space"},
  121. {FL_BackSpace,"\xe2\x8c\xab"}, // erase to the left
  122. {FL_Tab, "\xe2\x87\xa5"}, // rightwards arrow to bar
  123. {0xff0b, "\xe2\x8c\xa6"}, // erase to the right
  124. {FL_Enter, "\xe2\x86\xa9"}, // leftwards arrow with hook
  125. {FL_Pause, "Pause"},
  126. {FL_Scroll_Lock, "Scroll_Lock"},
  127. {FL_Escape, "\xe2\x90\x9b"},
  128. {FL_Home, "\xe2\x86\x96"}, // north west arrow
  129. {FL_Left, "\xe2\x86\x90"}, // leftwards arrow
  130. {FL_Up, "\xe2\x86\x91"}, // upwards arrow
  131. {FL_Right, "\xe2\x86\x92"}, // rightwards arrow
  132. {FL_Down, "\xe2\x86\x93"}, // downwards arrow
  133. {FL_Page_Up, "\xe2\x87\x9e"}, // upwards arrow with double stroke
  134. {FL_Page_Down,"\xe2\x87\x9f"}, // downwards arrow with double stroke
  135. {FL_End, "\xe2\x86\x98"}, // south east arrow
  136. {FL_Print, "Print"},
  137. {FL_Insert, "Insert"},
  138. {FL_Menu, "Menu"},
  139. {FL_Num_Lock, "Num_Lock"},
  140. {FL_KP_Enter, "\xe2\x8c\xa4"}, // up arrow head between two horizontal bars
  141. {FL_Shift_L, "Shift_L"},
  142. {FL_Shift_R, "Shift_R"},
  143. {FL_Control_L,"Control_L"},
  144. {FL_Control_R,"Control_R"},
  145. {FL_Caps_Lock,"\xe2\x87\xaa"}, // upwards white arrow from bar
  146. {FL_Meta_L, "Meta_L"},
  147. {FL_Meta_R, "Meta_R"},
  148. {FL_Alt_L, "Alt_L"},
  149. {FL_Alt_R, "Alt_R"},
  150. {FL_Delete, "\xe2\x8c\xa7"} // x in a rectangle box
  151. };
  152. #endif
  153. /**
  154. Get a human-readable string from a shortcut value.
  155. Unparse a shortcut value as used by Fl_Button or Fl_Menu_Item into
  156. a human-readable string like "Alt+N". This only works if the shortcut
  157. is a character key or a numbered function key. If the shortcut is
  158. zero then an empty string is returned. The return value points at
  159. a static buffer that is overwritten with each call.
  160. \param [in] shortcut the integer value containing the ascii character or extended keystroke plus modifiers
  161. \return a pointer to a static buffer containing human readable text for the shortcut
  162. */
  163. const char* fl_shortcut_label(unsigned int shortcut) {
  164. return fl_shortcut_label(shortcut, 0L);
  165. }
  166. /**
  167. Get a human-readable string from a shortcut value.
  168. \param [in] shortcut the integer value containing the ascii character or extended keystroke plus modifiers
  169. \param [in] eom if this pointer is set, it will receive a pointer to the end of the modifier text
  170. \return a pointer to a static buffer containing human readable text for the shortcut
  171. \see fl_shortcut_label(unsigned int shortcut)
  172. */
  173. const char* fl_shortcut_label(unsigned int shortcut, const char **eom) {
  174. static char buf[20];
  175. char *p = buf;
  176. if (eom) *eom = p;
  177. if (!shortcut) {*p = 0; return buf;}
  178. // fix upper case shortcuts
  179. unsigned int v = shortcut & FL_KEY_MASK;
  180. if (((unsigned)fl_tolower(v))!=v) {
  181. shortcut |= FL_SHIFT;
  182. }
  183. #ifdef __APPLE__
  184. // this column contains utf8 characters - v
  185. if (shortcut & FL_SHIFT) {strcpy(p,"\xe2\x87\xa7"); p += 3;} // upwards white arrow
  186. if (shortcut & FL_CTRL) {strcpy(p,"\xe2\x8c\x83"); p += 3;} // up arrowhead
  187. if (shortcut & FL_ALT) {strcpy(p,"\xe2\x8c\xa5"); p += 3;} // alternative key symbol
  188. if (shortcut & FL_META) {strcpy(p,"\xe2\x8c\x98"); p += 3;} // place of interest sign
  189. #else
  190. if (shortcut & FL_META) {strcpy(p,"Meta+"); p += 5;}
  191. if (shortcut & FL_ALT) {strcpy(p,"Alt+"); p += 4;}
  192. if (shortcut & FL_SHIFT) {strcpy(p,"Shift+"); p += 6;}
  193. if (shortcut & FL_CTRL) {strcpy(p,"Ctrl+"); p += 5;}
  194. #endif // __APPLE__
  195. if (eom) *eom = p;
  196. unsigned int key = shortcut & FL_KEY_MASK;
  197. #if defined(WIN32) || defined(__APPLE__) // if not X
  198. if (key >= FL_F && key <= FL_F_Last) {
  199. *p++ = 'F';
  200. if (key > FL_F+9) *p++ = (key-FL_F)/10+'0';
  201. *p++ = (key-FL_F)%10 + '0';
  202. } else {
  203. // binary search the table for a match:
  204. int a = 0;
  205. int b = sizeof(table)/sizeof(*table);
  206. while (a < b) {
  207. int c = (a+b)/2;
  208. if (table[c].key == key) {
  209. if (p > buf) {
  210. strcpy(p,table[c].name);
  211. return buf;
  212. } else {
  213. const char *sp = table[c].name;
  214. if (eom) *eom = sp;
  215. return sp;
  216. }
  217. }
  218. if (table[c].key < key) a = c+1;
  219. else b = c;
  220. }
  221. if (key >= FL_KP && key <= FL_KP_Last) {
  222. // mark keypad keys with KP_ prefix
  223. strcpy(p,"KP_"); p += 3;
  224. *p++ = uchar(key & 127);
  225. } else {
  226. // if none found, use the keystroke as a match:
  227. p += fl_utf8encode(fl_toupper(key), p);
  228. }
  229. }
  230. *p = 0;
  231. return buf;
  232. #else
  233. const char* q;
  234. if (key == FL_Enter || key == '\r') q="Enter"; // don't use Xlib's "Return":
  235. else if (key > 32 && key < 0x100) q = 0;
  236. else q = XKeysymToString(key);
  237. if (!q) {
  238. p += fl_utf8encode(fl_toupper(key), p);
  239. *p = 0;
  240. return buf;
  241. }
  242. if (p > buf) {
  243. strcpy(p,q);
  244. return buf;
  245. } else {
  246. if (eom) *eom = q;
  247. return q;
  248. }
  249. #endif
  250. }
  251. // Emulation of XForms named shortcuts
  252. #include <stdlib.h>
  253. /**
  254. Emulation of XForms named shortcuts.
  255. Converts ascii shortcut specifications (eg. "^c")
  256. into the FLTK integer equivalent (eg. FL_CTRL+'c')
  257. These ascii characters are used to specify the various keyboard modifier keys:
  258. \verbatim
  259. # - Alt
  260. + - Shift
  261. ^ - Control
  262. \endverbatim
  263. */
  264. unsigned int fl_old_shortcut(const char* s) {
  265. if (!s || !*s) return 0;
  266. unsigned int n = 0;
  267. if (*s == '#') {n |= FL_ALT; s++;}
  268. if (*s == '+') {n |= FL_SHIFT; s++;}
  269. if (*s == '^') {n |= FL_CTRL; s++;}
  270. if (*s && s[1]) return n | (int)strtol(s,0,0); // allow 0xf00 to get any key
  271. return n | *s;
  272. }
  273. // Tests for &x shortcuts in button labels:
  274. /** Returns the Unicode value of the '&x' shortcut in a given text.
  275. The given text \p t (usually a widget's label or a menu text) is
  276. searched for a '&x' shortcut label, and if found, the Unicode
  277. value (code point) of the '&x' shortcut is returned.
  278. \param t text or label to search for '&x' shortcut.
  279. \return Unicode (UCS-4) value of shortcut in \p t or 0.
  280. \note Internal use only.
  281. */
  282. unsigned int Fl_Widget::label_shortcut(const char *t) {
  283. if (!t) return 0;
  284. for (;;) {
  285. if (*t==0) return 0;
  286. if (*t=='&') {
  287. unsigned int s = fl_utf8decode(t+1, 0, 0);
  288. if (s==0) return 0;
  289. else if (s==(unsigned int)'&') t++;
  290. else return s;
  291. }
  292. t++;
  293. }
  294. }
  295. /** Returns true if the given text \p t contains the entered '&x' shortcut.
  296. This method must only be called in handle() methods or callbacks after
  297. a keypress event (usually FL_KEYDOWN or FL_SHORTCUT). The given text
  298. \p t (usually a widget's label or menu text) is searched for a '&x'
  299. shortcut, and if found, this is compared with the entered key value.
  300. Fl::event_text() is used to get the entered key value.
  301. Fl::event_state() is used to get the Alt modifier, if \p require_alt
  302. is true.
  303. \param t text or label to search for '&x' shortcut.
  304. \param require_alt if true: match only if Alt key is pressed.
  305. \return true, if the entered text matches the '&x' shortcut in \p t
  306. false (0) otherwise.
  307. \note Internal use only.
  308. */
  309. int Fl_Widget::test_shortcut(const char *t, const bool require_alt) {
  310. if (!t) return 0;
  311. // for menubars etc. shortcuts must work only if the Alt modifier is pressed
  312. if (require_alt && Fl::event_state(FL_ALT)==0) return 0;
  313. unsigned int c = fl_utf8decode(Fl::event_text(), Fl::event_text()+Fl::event_length(), 0);
  314. #ifdef __APPLE__
  315. // this line makes underline shortcuts work the same way they do on MSWindow
  316. // and Linux.
  317. if (c && Fl::event_state(FL_ALT))
  318. c = Fl::event_key();
  319. #endif
  320. if (!c) return 0;
  321. unsigned int ls = label_shortcut(t);
  322. if (c == ls)
  323. return 1;
  324. #ifdef __APPLE__
  325. // On OS X, we need to simulate the upper case keystroke as well
  326. if (Fl::event_state(FL_ALT) && c<128 && isalpha(c) && (unsigned)toupper(c)==ls)
  327. return 1;
  328. #endif
  329. return 0;
  330. }
  331. /** Returns true if the widget's label contains the entered '&x' shortcut.
  332. This method must only be called in handle() methods or callbacks after
  333. a keypress event (usually FL_KEYDOWN or FL_SHORTCUT).
  334. The widget's label is searched for a '&x'
  335. shortcut, and if found, this is compared with the entered key value.
  336. Fl::event_text() is used to get the entered key value.
  337. \return true, if the entered text matches the widget's'&x' shortcut,
  338. false (0) otherwise.
  339. \note Internal use only.
  340. */
  341. int Fl_Widget::test_shortcut() {
  342. if (!(flags()&SHORTCUT_LABEL)) return 0;
  343. return test_shortcut(label());
  344. }
  345. //
  346. // End of "$Id: fl_shortcut.cxx 8621 2011-04-23 15:46:30Z AlbrechtS $".
  347. //