themes: Workaround for bug where a background color of RGB 0,0,0 in Black color schem...
[ntk.git] / src / fl_shortcut.cxx
blob84c84f7f2925f49e4ee9906ff524c9182961b638
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.
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
21 // USA.
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)
44 #include <FL/Fl.H>
45 #include <FL/Fl_Widget.H>
46 #include <FL/Fl_Button.H>
47 #include <FL/fl_draw.H>
48 #include <ctype.h>
49 #include "flstring.h"
50 #if !defined(WIN32) && !defined(__APPLE__)
51 #include <FL/x.H>
52 #endif
54 /**
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) {
67 shortcut |= FL_SHIFT;
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
90 return 0;
93 // This table must be in numeric order by fltk (X) keysym number:
94 struct Keyname {unsigned int key; const char* name;};
95 #if defined(WIN32)
96 static Keyname table[] = {
97 {' ', "Space"},
98 {FL_BackSpace, "Backspace"},
99 {FL_Tab, "Tab"},
100 {0xff0b/*XK_Clear*/, "Clear"},
101 {FL_Enter, "Enter"}, // X says "Enter"
102 {FL_Pause, "Pause"},
103 {FL_Scroll_Lock, "Scroll_Lock"},
104 {FL_Escape, "Escape"},
105 {FL_Home, "Home"},
106 {FL_Left, "Left"},
107 {FL_Up, "Up"},
108 {FL_Right, "Right"},
109 {FL_Down, "Down"},
110 {FL_Page_Up, "Page_Up"}, // X says "Prior"
111 {FL_Page_Down,"Page_Down"}, // X says "Next"
112 {FL_End, "End"},
113 {FL_Print, "Print"},
114 {FL_Insert, "Insert"},
115 {FL_Menu, "Menu"},
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"},
125 {FL_Alt_L, "Alt_L"},
126 {FL_Alt_R, "Alt_R"},
127 {FL_Delete, "Delete"}
129 #elif defined(__APPLE__)
130 static Keyname table[] = {
131 // v - this column contains UTF-8 characters
132 {' ', "Space"},
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
137 {FL_Pause, "Pause"},
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
148 {FL_Print, "Print"},
149 {FL_Insert, "Insert"},
150 {FL_Menu, "Menu"},
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"},
160 {FL_Alt_L, "Alt_L"},
161 {FL_Alt_R, "Alt_R"},
162 {FL_Delete, "\xe2\x8c\xa7"} // x in a rectangle box
164 #endif
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);
182 /**
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) {
191 static char buf[20];
192 char *p = buf;
193 if (eom) *eom = p;
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;
200 #ifdef __APPLE__
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
206 #else
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;}
211 #endif // __APPLE__
212 if (eom) *eom = p;
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) {
216 *p++ = 'F';
217 if (key > FL_F+9) *p++ = (key-FL_F)/10+'0';
218 *p++ = (key-FL_F)%10 + '0';
219 } else {
220 // binary search the table for a match:
221 int a = 0;
222 int b = sizeof(table)/sizeof(*table);
223 while (a < b) {
224 int c = (a+b)/2;
225 if (table[c].key == key) {
226 if (p > buf) {
227 strcpy(p,table[c].name);
228 return buf;
229 } else {
230 const char *sp = table[c].name;
231 if (eom) *eom = sp;
232 return sp;
235 if (table[c].key < key) a = c+1;
236 else b = c;
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);
242 } else {
243 // if none found, use the keystroke as a match:
244 p += fl_utf8encode(fl_toupper(key), p);
247 *p = 0;
248 return buf;
249 #else
250 const char* q;
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);
254 if (!q) {
255 p += fl_utf8encode(fl_toupper(key), p);
256 *p = 0;
257 return buf;
259 if (p > buf) {
260 strcpy(p,q);
261 return buf;
262 } else {
263 if (eom) *eom = q;
264 return q;
266 #endif
269 // Emulation of XForms named shortcuts
270 #include <stdlib.h>
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:
278 \verbatim
279 # - Alt
280 + - Shift
281 ^ - Control
282 \endverbatim
284 unsigned int fl_old_shortcut(const char* s) {
285 if (!s || !*s) return 0;
286 unsigned int n = 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
291 return n | *s;
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) {
309 if (!t) return 0;
310 for (;;) {
311 if (*t==0) return 0;
312 if (*t=='&') {
313 unsigned int s = fl_utf8decode(t+1, 0, 0);
314 if (s==0) return 0;
315 else if (s==(unsigned int)'&') t++;
316 else return s;
318 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
331 is true.
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
337 false (0) otherwise.
339 \note Internal use only.
341 int Fl_Widget::test_shortcut(const char *t, const bool require_alt) {
342 if (!t) return 0;
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);
346 #ifdef __APPLE__
347 // this line makes underline shortcuts work the same way they do on MSWindow
348 // and Linux.
349 if (c && Fl::event_state(FL_ALT))
350 c = Fl::event_key();
351 #endif
352 if (!c) return 0;
353 unsigned int ls = label_shortcut(t);
354 if (c == ls)
355 return 1;
356 #ifdef __APPLE__
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)
359 return 1;
360 #endif
361 return 0;
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,
374 false (0) otherwise.
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 $".