2 // "$Id: fl_shortcut.cxx 8621 2011-04-23 15:46:30Z AlbrechtS $"
4 // Shortcut support routines for the Fast Light Tool Kit (FLTK).
6 // Copyright 1998-2011 by Bill Spitzak and others.
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.
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.
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
23 // Please report all bugs and problems on the following page:
25 // http://www.fltk.org/str.php
28 // Code to test and parse fltk shortcut numbers.
30 // A shortcut is a keysym or'd with shift flags. In the simplest
31 // sense a shortcut is matched if the shift state is exactly as
32 // given and the key returning that keysym is pressed.
34 // To make it easier to match some things it is more complex:
36 // Only FL_META, FL_ALT, FL_SHIFT, and FL_CTRL must be "off". A
37 // zero in the other shift flags indicates "don't care".
39 // It also checks against the first character of Fl::event_text(),
40 // and zero for FL_SHIFT means "don't care".
41 // This allows punctuation shortcuts like "#" to work (rather than
42 // calling it "shift+3" on a US keyboard)
45 #include <FL/Fl_Widget.H>
46 #include <FL/Fl_Button.H>
47 #include <FL/fl_draw.H>
50 #if !defined(WIN32) && !defined(__APPLE__)
55 Tests the current event, which must be an FL_KEYBOARD or
56 FL_SHORTCUT, against a shortcut value (described in Fl_Button).
58 Not to be confused with Fl_Widget::test_shortcut().
60 \return non-zero if there is a match.
62 int Fl::test_shortcut(unsigned int shortcut
) {
63 if (!shortcut
) return 0;
65 unsigned int v
= shortcut
& FL_KEY_MASK
;
66 if (((unsigned)fl_tolower(v
))!=v
) {
70 int shift
= Fl::event_state();
71 // see if any required shift flags are off:
72 if ((shortcut
&shift
) != (shortcut
&0x7fff0000)) return 0;
73 // record shift flags that are wrong:
74 int mismatch
= (shortcut
^shift
)&0x7fff0000;
75 // these three must always be correct:
76 if (mismatch
&(FL_META
|FL_ALT
|FL_CTRL
)) return 0;
78 unsigned int key
= shortcut
& FL_KEY_MASK
;
80 // if shift is also correct, check for exactly equal keysyms:
81 if (!(mismatch
&(FL_SHIFT
)) && key
== (unsigned)Fl::event_key()) return 1;
83 // try matching utf8, ignore shift:
84 unsigned int firstChar
= fl_utf8decode(Fl::event_text(), Fl::event_text()+Fl::event_length(), 0);
85 if ( ! (FL_CAPS_LOCK
&shift
) && key
==firstChar
) return 1;
87 // kludge so that Ctrl+'_' works (as opposed to Ctrl+'^_'):
88 if ((shift
&FL_CTRL
) && key
>= 0x3f && key
<= 0x5F
89 && firstChar
==(key
^0x40)) return 1; // firstChar should be within a-z
93 // This table must be in numeric order by fltk (X) keysym number:
94 struct Keyname
{unsigned int key
; const char* name
;};
96 static Keyname table
[] = {
98 {FL_BackSpace
, "Backspace"},
100 {0xff0b/*XK_Clear*/, "Clear"},
101 {FL_Enter
, "Enter"}, // X says "Enter"
103 {FL_Scroll_Lock
, "Scroll_Lock"},
104 {FL_Escape
, "Escape"},
110 {FL_Page_Up
, "Page_Up"}, // X says "Prior"
111 {FL_Page_Down
,"Page_Down"}, // X says "Next"
114 {FL_Insert
, "Insert"},
116 {FL_Num_Lock
, "Num_Lock"},
117 {FL_KP_Enter
, "KP_Enter"},
118 {FL_Shift_L
, "Shift_L"},
119 {FL_Shift_R
, "Shift_R"},
120 {FL_Control_L
,"Control_L"},
121 {FL_Control_R
,"Control_R"},
122 {FL_Caps_Lock
,"Caps_Lock"},
123 {FL_Meta_L
, "Meta_L"},
124 {FL_Meta_R
, "Meta_R"},
127 {FL_Delete
, "Delete"}
129 #elif defined(__APPLE__)
130 static Keyname table
[] = {
131 // v - this column contains UTF-8 characters
133 {FL_BackSpace
,"\xe2\x8c\xab"}, // erase to the left
134 {FL_Tab
, "\xe2\x87\xa5"}, // rightwards arrow to bar
135 {0xff0b, "\xe2\x8c\xa6"}, // erase to the right
136 {FL_Enter
, "\xe2\x86\xa9"}, // leftwards arrow with hook
138 {FL_Scroll_Lock
, "Scroll_Lock"},
139 {FL_Escape
, "\xe2\x90\x9b"},
140 {FL_Home
, "\xe2\x86\x96"}, // north west arrow
141 {FL_Left
, "\xe2\x86\x90"}, // leftwards arrow
142 {FL_Up
, "\xe2\x86\x91"}, // upwards arrow
143 {FL_Right
, "\xe2\x86\x92"}, // rightwards arrow
144 {FL_Down
, "\xe2\x86\x93"}, // downwards arrow
145 {FL_Page_Up
, "\xe2\x87\x9e"}, // upwards arrow with double stroke
146 {FL_Page_Down
,"\xe2\x87\x9f"}, // downwards arrow with double stroke
147 {FL_End
, "\xe2\x86\x98"}, // south east arrow
149 {FL_Insert
, "Insert"},
151 {FL_Num_Lock
, "Num_Lock"},
152 {FL_KP_Enter
, "\xe2\x8c\xa4"}, // up arrow head between two horizontal bars
153 {FL_Shift_L
, "Shift_L"},
154 {FL_Shift_R
, "Shift_R"},
155 {FL_Control_L
,"Control_L"},
156 {FL_Control_R
,"Control_R"},
157 {FL_Caps_Lock
,"\xe2\x87\xaa"}, // upwards white arrow from bar
158 {FL_Meta_L
, "Meta_L"},
159 {FL_Meta_R
, "Meta_R"},
162 {FL_Delete
, "\xe2\x8c\xa7"} // x in a rectangle box
167 Get a human-readable string from a shortcut value.
169 Unparse a shortcut value as used by Fl_Button or Fl_Menu_Item into
170 a human-readable string like "Alt+N". This only works if the shortcut
171 is a character key or a numbered function key. If the shortcut is
172 zero then an empty string is returned. The return value points at
173 a static buffer that is overwritten with each call.
175 \param [in] shortcut the integer value containing the ascii character or extended keystroke plus modifiers
176 \return a pointer to a static buffer containing human readable text for the shortcut
178 const char* fl_shortcut_label(unsigned int shortcut
) {
179 return fl_shortcut_label(shortcut
, 0L);
183 Get a human-readable string from a shortcut value.
185 \param [in] shortcut the integer value containing the ascii character or extended keystroke plus modifiers
186 \param [in] eom if this pointer is set, it will receive a pointer to the end of the modifier text
187 \return a pointer to a static buffer containing human readable text for the shortcut
188 \see fl_shortcut_label(unsigned int shortcut)
190 const char* fl_shortcut_label(unsigned int shortcut
, const char **eom
) {
194 if (!shortcut
) {*p
= 0; return buf
;}
195 // fix upper case shortcuts
196 unsigned int v
= shortcut
& FL_KEY_MASK
;
197 if (((unsigned)fl_tolower(v
))!=v
) {
198 shortcut
|= FL_SHIFT
;
201 // this column contains utf8 characters - v
202 if (shortcut
& FL_SHIFT
) {strcpy(p
,"\xe2\x87\xa7"); p
+= 3;} // upwards white arrow
203 if (shortcut
& FL_CTRL
) {strcpy(p
,"\xe2\x8c\x83"); p
+= 3;} // up arrowhead
204 if (shortcut
& FL_ALT
) {strcpy(p
,"\xe2\x8c\xa5"); p
+= 3;} // alternative key symbol
205 if (shortcut
& FL_META
) {strcpy(p
,"\xe2\x8c\x98"); p
+= 3;} // place of interest sign
207 if (shortcut
& FL_META
) {strcpy(p
,"Meta+"); p
+= 5;}
208 if (shortcut
& FL_ALT
) {strcpy(p
,"Alt+"); p
+= 4;}
209 if (shortcut
& FL_SHIFT
) {strcpy(p
,"Shift+"); p
+= 6;}
210 if (shortcut
& FL_CTRL
) {strcpy(p
,"Ctrl+"); p
+= 5;}
213 unsigned int key
= shortcut
& FL_KEY_MASK
;
214 #if defined(WIN32) || defined(__APPLE__) // if not X
215 if (key
>= FL_F
&& key
<= FL_F_Last
) {
217 if (key
> FL_F
+9) *p
++ = (key
-FL_F
)/10+'0';
218 *p
++ = (key
-FL_F
)%10 + '0';
220 // binary search the table for a match:
222 int b
= sizeof(table
)/sizeof(*table
);
225 if (table
[c
].key
== key
) {
227 strcpy(p
,table
[c
].name
);
230 const char *sp
= table
[c
].name
;
235 if (table
[c
].key
< key
) a
= c
+1;
238 if (key
>= FL_KP
&& key
<= FL_KP_Last
) {
239 // mark keypad keys with KP_ prefix
240 strcpy(p
,"KP_"); p
+= 3;
241 *p
++ = uchar(key
& 127);
243 // if none found, use the keystroke as a match:
244 p
+= fl_utf8encode(fl_toupper(key
), p
);
251 if (key
== FL_Enter
|| key
== '\r') q
="Enter"; // don't use Xlib's "Return":
252 else if (key
> 32 && key
< 0x100) q
= 0;
253 else q
= XKeysymToString(key
);
255 p
+= fl_utf8encode(fl_toupper(key
), p
);
269 // Emulation of XForms named shortcuts
272 Emulation of XForms named shortcuts.
274 Converts ascii shortcut specifications (eg. "^c")
275 into the FLTK integer equivalent (eg. FL_CTRL+'c')
277 These ascii characters are used to specify the various keyboard modifier keys:
284 unsigned int fl_old_shortcut(const char* s
) {
285 if (!s
|| !*s
) return 0;
287 if (*s
== '#') {n
|= FL_ALT
; s
++;}
288 if (*s
== '+') {n
|= FL_SHIFT
; s
++;}
289 if (*s
== '^') {n
|= FL_CTRL
; s
++;}
290 if (*s
&& s
[1]) return n
| (int)strtol(s
,0,0); // allow 0xf00 to get any key
294 // Tests for &x shortcuts in button labels:
296 /** Returns the Unicode value of the '&x' shortcut in a given text.
298 The given text \p t (usually a widget's label or a menu text) is
299 searched for a '&x' shortcut label, and if found, the Unicode
300 value (code point) of the '&x' shortcut is returned.
302 \param t text or label to search for '&x' shortcut.
304 \return Unicode (UCS-4) value of shortcut in \p t or 0.
306 \note Internal use only.
308 unsigned int Fl_Widget::label_shortcut(const char *t
) {
313 unsigned int s
= fl_utf8decode(t
+1, 0, 0);
315 else if (s
==(unsigned int)'&') t
++;
322 /** Returns true if the given text \p t contains the entered '&x' shortcut.
324 This method must only be called in handle() methods or callbacks after
325 a keypress event (usually FL_KEYDOWN or FL_SHORTCUT). The given text
326 \p t (usually a widget's label or menu text) is searched for a '&x'
327 shortcut, and if found, this is compared with the entered key value.
329 Fl::event_text() is used to get the entered key value.
330 Fl::event_state() is used to get the Alt modifier, if \p require_alt
333 \param t text or label to search for '&x' shortcut.
334 \param require_alt if true: match only if Alt key is pressed.
336 \return true, if the entered text matches the '&x' shortcut in \p t
339 \note Internal use only.
341 int Fl_Widget::test_shortcut(const char *t
, const bool require_alt
) {
343 // for menubars etc. shortcuts must work only if the Alt modifier is pressed
344 if (require_alt
&& Fl::event_state(FL_ALT
)==0) return 0;
345 unsigned int c
= fl_utf8decode(Fl::event_text(), Fl::event_text()+Fl::event_length(), 0);
347 // this line makes underline shortcuts work the same way they do on MSWindow
349 if (c
&& Fl::event_state(FL_ALT
))
353 unsigned int ls
= label_shortcut(t
);
357 // On OS X, we need to simulate the upper case keystroke as well
358 if (Fl::event_state(FL_ALT
) && c
<128 && isalpha(c
) && (unsigned)toupper(c
)==ls
)
364 /** Returns true if the widget's label contains the entered '&x' shortcut.
366 This method must only be called in handle() methods or callbacks after
367 a keypress event (usually FL_KEYDOWN or FL_SHORTCUT).
368 The widget's label is searched for a '&x'
369 shortcut, and if found, this is compared with the entered key value.
371 Fl::event_text() is used to get the entered key value.
373 \return true, if the entered text matches the widget's'&x' shortcut,
376 \note Internal use only.
379 int Fl_Widget::test_shortcut() {
380 if (!(flags()&SHORTCUT_LABEL
)) return 0;
381 return test_shortcut(label());
385 // End of "$Id: fl_shortcut.cxx 8621 2011-04-23 15:46:30Z AlbrechtS $".