Move control surface options into RC prefs editor. Remove Options menu.
[ardour.git] / gtk2_ardour / utils.cc
1 /*
2     Copyright (C) 2003 Paul Davis 
3
4     This program is free software; you an redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #include <pango/pangoft2.h> // for fontmap resolution control for GnomeCanvas
21 #include <pango/pangocairo.h> // for fontmap resolution control for GnomeCanvas
22
23 #include <cstdlib>
24 #include <cctype>
25 #include <fstream>
26 #include <sys/stat.h>
27 #include <libart_lgpl/art_misc.h>
28 #include <gtkmm/rc.h>
29 #include <gtkmm/window.h>
30 #include <gtkmm/combo.h>
31 #include <gtkmm/label.h>
32 #include <gtkmm/paned.h>
33 #include <gtk/gtkpaned.h>
34
35 #include "pbd/file_utils.h"
36
37 #include <gtkmm2ext/utils.h>
38 #include "ardour/configuration.h"
39 #include "ardour/configuration.h"
40
41 #include "ardour/filesystem_paths.h"
42
43 #include "ardour_ui.h"
44 #include "keyboard.h"
45 #include "utils.h"
46 #include "i18n.h"
47 #include "rgb_macros.h"
48 #include "canvas_impl.h"
49
50 using namespace std;
51 using namespace Gtk;
52 using namespace sigc;
53 using namespace Glib;
54 using namespace PBD;
55
56 sigc::signal<void>  DPIReset;
57
58 int
59 pixel_width (const ustring& str, Pango::FontDescription& font)
60 {
61         Label foo;
62         Glib::RefPtr<Pango::Layout> layout = foo.create_pango_layout ("");
63
64         layout->set_font_description (font);
65         layout->set_text (str);
66
67         int width, height;
68         Gtkmm2ext::get_ink_pixel_size (layout, width, height);
69         return width;
70 }
71
72 ustring
73 fit_to_pixels (const ustring& str, int pixel_width, Pango::FontDescription& font, int& actual_width, bool with_ellipses)
74 {
75         Label foo;
76         Glib::RefPtr<Pango::Layout> layout = foo.create_pango_layout ("");
77         ustring::size_type shorter_by = 0;
78         ustring txt;
79
80         layout->set_font_description (font);
81
82         actual_width = 0;
83
84         ustring ustr = str;
85         ustring::iterator last = ustr.end();
86         --last; /* now points at final entry */
87
88         txt = ustr;
89
90         while (!ustr.empty()) {
91
92                 layout->set_text (txt);
93
94                 int width, height;
95                 Gtkmm2ext::get_ink_pixel_size (layout, width, height);
96
97                 if (width < pixel_width) {
98                         actual_width = width;
99                         break;
100                 }
101                 
102                 ustr.erase (last--);
103                 shorter_by++;
104
105                 if (with_ellipses && shorter_by > 3) {
106                         txt = ustr;
107                         txt += "...";
108                 } else {
109                         txt = ustr;
110                 }
111         }
112
113         return txt;
114 }
115
116 gint
117 just_hide_it (GdkEventAny *ev, Gtk::Window *win)
118 {
119         win->hide ();
120         return TRUE;
121 }
122
123 /* xpm2rgb copied from nixieclock, which bore the legend:
124
125     nixieclock - a nixie desktop timepiece
126     Copyright (C) 2000 Greg Ercolano, erco@3dsite.com
127
128     and was released under the GPL.
129 */
130
131 unsigned char*
132 xpm2rgb (const char** xpm, uint32_t& w, uint32_t& h)
133 {
134         static long vals[256], val;
135         uint32_t t, x, y, colors, cpp;
136         unsigned char c;
137         unsigned char *savergb, *rgb;
138         
139         // PARSE HEADER
140         
141         if ( sscanf(xpm[0], "%u%u%u%u", &w, &h, &colors, &cpp) != 4 ) {
142                 error << string_compose (_("bad XPM header %1"), xpm[0])
143                       << endmsg;
144                 return 0;
145         }
146
147         savergb = rgb = (unsigned char*) malloc (h * w * 3);
148         
149         // LOAD XPM COLORMAP LONG ENOUGH TO DO CONVERSION
150         for (t = 0; t < colors; ++t) {
151                 sscanf (xpm[t+1], "%c c #%lx", &c, &val);
152                 vals[c] = val;
153         }
154         
155         // COLORMAP -> RGB CONVERSION
156         //    Get low 3 bytes from vals[]
157         //
158
159         const char *p;
160         for (y = h-1; y > 0; --y) {
161
162                 for (p = xpm[1+colors+(h-y-1)], x = 0; x < w; x++, rgb += 3) {
163                         val = vals[(int)*p++];
164                         *(rgb+2) = val & 0xff; val >>= 8;  // 2:B
165                         *(rgb+1) = val & 0xff; val >>= 8;  // 1:G
166                         *(rgb+0) = val & 0xff;             // 0:R
167                 }
168         }
169
170         return (savergb);
171 }
172
173 unsigned char*
174 xpm2rgba (const char** xpm, uint32_t& w, uint32_t& h)
175 {
176         static long vals[256], val;
177         uint32_t t, x, y, colors, cpp;
178         unsigned char c;
179         unsigned char *savergb, *rgb;
180         char transparent;
181
182         // PARSE HEADER
183
184         if ( sscanf(xpm[0], "%u%u%u%u", &w, &h, &colors, &cpp) != 4 ) {
185                 error << string_compose (_("bad XPM header %1"), xpm[0])
186                       << endmsg;
187                 return 0;
188         }
189
190         savergb = rgb = (unsigned char*) malloc (h * w * 4);
191         
192         // LOAD XPM COLORMAP LONG ENOUGH TO DO CONVERSION
193
194         if (strstr (xpm[1], "None")) {
195                 sscanf (xpm[1], "%c", &transparent);
196                 t = 1;
197         } else {
198                 transparent = 0;
199                 t = 0;
200         }
201
202         for (; t < colors; ++t) {
203                 sscanf (xpm[t+1], "%c c #%lx", &c, &val);
204                 vals[c] = val;
205         }
206         
207         // COLORMAP -> RGB CONVERSION
208         //    Get low 3 bytes from vals[]
209         //
210
211         const char *p;
212         for (y = h-1; y > 0; --y) {
213
214                 char alpha;
215
216                 for (p = xpm[1+colors+(h-y-1)], x = 0; x < w; x++, rgb += 4) {
217
218                         if (transparent && (*p++ == transparent)) {
219                                 alpha = 0;
220                                 val = 0;
221                         } else {
222                                 alpha = 255;
223                                 val = vals[(int)*p];
224                         }
225
226                         *(rgb+3) = alpha;                  // 3: alpha
227                         *(rgb+2) = val & 0xff; val >>= 8;  // 2:B
228                         *(rgb+1) = val & 0xff; val >>= 8;  // 1:G
229                         *(rgb+0) = val & 0xff;             // 0:R
230                 }
231         }
232
233         return (savergb);
234 }
235
236 ArdourCanvas::Points*
237 get_canvas_points (string who, uint32_t npoints)
238 {
239         // cerr << who << ": wants " << npoints << " canvas points" << endl;
240 #ifdef TRAP_EXCESSIVE_POINT_REQUESTS
241         if (npoints > (uint32_t) gdk_screen_width() + 4) {
242                 abort ();
243         }
244 #endif
245         return new ArdourCanvas::Points (npoints);
246 }
247
248 Pango::FontDescription*
249 get_font_for_style (string widgetname)
250 {
251         Gtk::Window window (WINDOW_TOPLEVEL);
252         Gtk::Label foobar;
253         Glib::RefPtr<Gtk::Style> style;
254
255         window.add (foobar);
256         foobar.set_name (widgetname);
257         foobar.ensure_style();
258
259         style = foobar.get_style ();
260
261         Glib::RefPtr<const Pango::Layout> layout = foobar.get_layout();
262         
263         PangoFontDescription *pfd = (PangoFontDescription *)pango_layout_get_font_description((PangoLayout *)layout->gobj());
264         
265         if (!pfd) {
266                 
267                 /* layout inherited its font description from a PangoContext */
268
269                 PangoContext* ctxt = (PangoContext*) pango_layout_get_context ((PangoLayout*) layout->gobj());
270                 pfd =  pango_context_get_font_description (ctxt);
271                 return new Pango::FontDescription (pfd, true); /* make a copy */
272         } 
273
274         return new Pango::FontDescription (pfd, true); /* make a copy */
275 }
276
277 uint32_t
278 rgba_from_style (string style, uint32_t r, uint32_t g, uint32_t b, uint32_t a, string attr, int state, bool rgba)
279 {
280         /* In GTK+2, styles aren't set up correctly if the widget is not
281            attached to a toplevel window that has a screen pointer.
282         */
283
284         static Gtk::Window* window = 0;
285
286         if (window == 0) {
287                 window = new Window (WINDOW_TOPLEVEL);
288         }
289
290         Gtk::Label foo;
291         
292         window->add (foo);
293
294         foo.set_name (style);
295         foo.ensure_style ();
296         
297         GtkRcStyle* waverc = foo.get_style()->gobj()->rc_style;
298
299         if (waverc) {
300                 if (attr == "fg") {
301                         r = waverc->fg[state].red / 257;
302                         g = waverc->fg[state].green / 257;
303                         b = waverc->fg[state].blue / 257;
304  
305                         /* what a hack ... "a" is for "active" */
306                         if (state == Gtk::STATE_NORMAL && rgba) {
307                                 a = waverc->fg[GTK_STATE_ACTIVE].red / 257;
308                         }
309                 } else if (attr == "bg") {
310                         r = g = b = 0;
311                         r = waverc->bg[state].red / 257;
312                         g = waverc->bg[state].green / 257;
313                         b = waverc->bg[state].blue / 257;
314                 } else if (attr == "base") {
315                         r = waverc->base[state].red / 257;
316                         g = waverc->base[state].green / 257;
317                         b = waverc->base[state].blue / 257;
318                 } else if (attr == "text") {
319                         r = waverc->text[state].red / 257;
320                         g = waverc->text[state].green / 257;
321                         b = waverc->text[state].blue / 257;
322                 }
323         } else {
324                 warning << string_compose (_("missing RGBA style for \"%1\""), style) << endl;
325         }
326
327         window->remove ();
328         
329         if (state == Gtk::STATE_NORMAL && rgba) {
330                 return (uint32_t) RGBA_TO_UINT(r,g,b,a);
331         } else {
332                 return (uint32_t) RGB_TO_UINT(r,g,b);
333         }
334 }
335
336
337 Gdk::Color
338 color_from_style (string widget_style_name, int state, string attr)
339 {
340         GtkStyle* style;
341
342         style = gtk_rc_get_style_by_paths (gtk_settings_get_default(),
343                                            widget_style_name.c_str(),
344                                            0, G_TYPE_NONE);
345
346         if (!style) {
347                 error << string_compose (_("no style found for %1, using red"), style) << endmsg;
348                 return Gdk::Color ("red");
349         }
350
351         if (attr == "fg") {
352                 return Gdk::Color (&style->fg[state]);
353         }
354
355         if (attr == "bg") {
356                 return Gdk::Color (&style->bg[state]);
357         }
358
359         if (attr == "light") {
360                 return Gdk::Color (&style->light[state]);
361         }
362
363         if (attr == "dark") {
364                 return Gdk::Color (&style->dark[state]);
365         }
366
367         if (attr == "mid") {
368                 return Gdk::Color (&style->mid[state]);
369         }
370
371         if (attr == "text") {
372                 return Gdk::Color (&style->text[state]);
373         }
374
375         if (attr == "base") {
376                 return Gdk::Color (&style->base[state]);
377         }
378
379         if (attr == "text_aa") {
380                 return Gdk::Color (&style->text_aa[state]);
381         }
382
383         error << string_compose (_("unknown style attribute %1 requested for color; using \"red\""), attr) << endmsg;
384         return Gdk::Color ("red");
385 }
386
387 Glib::RefPtr<Gdk::GC>
388 gc_from_style (string widget_style_name, int state, string attr)
389 {
390         GtkStyle* style;
391
392         style = gtk_rc_get_style_by_paths (gtk_settings_get_default(),
393                                            widget_style_name.c_str(),
394                                            0, G_TYPE_NONE);
395
396         if (!style) {
397                 error << string_compose (_("no style found for %1, using red"), style) << endmsg;
398                 Glib::RefPtr<Gdk::GC> ret = Gdk::GC::create();
399                 ret->set_rgb_fg_color(Gdk::Color("red"));
400                 return ret;
401         }
402
403         if (attr == "fg") {
404                 return Glib::wrap(style->fg_gc[state]);
405         }
406
407         if (attr == "bg") {
408                 return Glib::wrap(style->bg_gc[state]);
409         }
410
411         if (attr == "light") {
412                 return Glib::wrap(style->light_gc[state]);
413         }
414
415         if (attr == "dark") {
416                 return Glib::wrap(style->dark_gc[state]);
417         }
418
419         if (attr == "mid") {
420                 return Glib::wrap(style->mid_gc[state]);
421         }
422
423         if (attr == "text") {
424                 return Glib::wrap(style->text_gc[state]);
425         }
426
427         if (attr == "base") {
428                 return Glib::wrap(style->base_gc[state]);
429         }
430
431         if (attr == "text_aa") {
432                 return Glib::wrap(style->text_aa_gc[state]);
433         }
434
435         error << string_compose (_("unknown style attribute %1 requested for color; using \"red\""), attr) << endmsg;
436         Glib::RefPtr<Gdk::GC> ret = Gdk::GC::create();
437         ret->set_rgb_fg_color(Gdk::Color("red"));
438         return ret;
439 }
440
441
442 bool 
443 canvas_item_visible (ArdourCanvas::Item* item)
444 {
445         return (item->gobj()->object.flags & GNOME_CANVAS_ITEM_VISIBLE) ? true : false;
446 }
447
448 void
449 set_color (Gdk::Color& c, int rgb)
450 {
451         c.set_rgb((rgb >> 16)*256, ((rgb & 0xff00) >> 8)*256, (rgb & 0xff)*256);
452 }
453
454 #ifdef GTKOSX
455 extern "C" {
456         gboolean gdk_quartz_possibly_forward (GdkEvent*);
457 }
458 #endif
459
460 bool
461 key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev)
462 {
463         GtkWindow* win = window.gobj();
464         GtkWidget* focus = gtk_window_get_focus (win);
465         bool special_handling_of_unmodified_accelerators = false;
466         bool allow_activating = true;
467
468 #undef DEBUG_ACCELERATOR_HANDLING
469 #ifdef  DEBUG_ACCELERATOR_HANDLING
470         //bool debug = (getenv ("ARDOUR_DEBUG_ACCELERATOR_HANDLING") != 0);
471         bool debug=true;
472 #endif
473         if (focus) {
474                 if (GTK_IS_ENTRY(focus) || Keyboard::some_magic_widget_has_focus()) {
475                         special_handling_of_unmodified_accelerators = true;
476                 } 
477         } 
478
479 #ifdef GTKOSX
480         /* should this be universally true? */
481         if (Keyboard::some_magic_widget_has_focus ()) {
482                 allow_activating = false;
483         }
484 #endif
485
486 #ifdef DEBUG_ACCELERATOR_HANDLING
487         if (debug) {
488                 cerr << "Win = " << win << " Key event: code = " << ev->keyval << " state = " << hex << ev->state << dec << " special handling ? " 
489                      << special_handling_of_unmodified_accelerators
490                      << " magic widget focus ? "
491                      << Keyboard::some_magic_widget_has_focus()
492                      << " allow_activation ? "
493                      << allow_activating
494                      << endl;
495         }
496 #endif
497
498         /* This exists to allow us to override the way GTK handles
499            key events. The normal sequence is:
500
501            a) event is delivered to a GtkWindow
502            b) accelerators/mnemonics are activated
503            c) if (b) didn't handle the event, propagate to
504                the focus widget and/or focus chain
505
506            The problem with this is that if the accelerators include
507            keys without modifiers, such as the space bar or the 
508            letter "e", then pressing the key while typing into
509            a text entry widget results in the accelerator being
510            activated, instead of the desired letter appearing
511            in the text entry.
512
513            There is no good way of fixing this, but this
514            represents a compromise. The idea is that 
515            key events involving modifiers (not Shift)
516            get routed into the activation pathway first, then
517            get propagated to the focus widget if necessary.
518            
519            If the key event doesn't involve modifiers,
520            we deliver to the focus widget first, thus allowing
521            it to get "normal text" without interference
522            from acceleration.
523
524            Of course, this can also be problematic: if there
525            is a widget with focus, then it will swallow
526            all "normal text" accelerators.
527         */
528
529
530         if (!special_handling_of_unmodified_accelerators) {
531
532                 /* pretend that certain key events that GTK does not allow
533                    to be used as accelerators are actually something that
534                    it does allow.
535                 */
536
537                 uint32_t fakekey = ev->keyval;
538
539                 if (possibly_translate_keyval_to_make_legal_accelerator (fakekey)) {
540                         if (allow_activating && gtk_accel_groups_activate(G_OBJECT(win), fakekey, GdkModifierType(ev->state))) {
541                                 return true;
542                         }
543
544 #ifdef GTKOSX
545                         if (allow_activating) {
546                                 int oldval = ev->keyval;
547                                 ev->keyval = fakekey;
548                                 if (gdk_quartz_possibly_forward ((GdkEvent*) ev)) {
549                                         return true;
550                                 }
551                                 ev->keyval = oldval;
552                         }
553 #endif
554                 }
555         }
556
557         /* consider all relevant modifiers but not LOCK or SHIFT */
558
559         guint mask = (Keyboard::RelevantModifierKeyMask & ~(Gdk::SHIFT_MASK|Gdk::LOCK_MASK));
560
561         if (!special_handling_of_unmodified_accelerators || (ev->state & mask)) {
562
563                 /* no special handling or there are modifiers in effect: accelerate first */
564
565 #ifdef DEBUG_ACCELERATOR_HANDLING
566                 if (debug) {
567                         cerr << "\tactivate, then propagate\n";
568                 }
569 #endif
570
571                 if (allow_activating) {
572 #ifdef GTKOSX
573                         if (gdk_quartz_possibly_forward ((GdkEvent*) ev)) {
574                                 return true;
575                         }
576 #endif
577                         if (gtk_window_activate_key (win, ev)) {
578                                 return true;
579                         }
580                 }
581
582 #ifdef DEBUG_ACCELERATOR_HANDLING
583                 if (debug) {
584                         cerr << "\tnot accelerated, now propagate\n";
585                 }
586 #endif
587                 return gtk_window_propagate_key_event (win, ev);
588         }
589         
590         /* no modifiers, propagate first */
591         
592 #ifdef DEBUG_ACCELERATOR_HANDLING
593         if (debug) {
594                 cerr << "\tpropagate, then activate\n";
595         }
596 #endif
597         if (!gtk_window_propagate_key_event (win, ev)) {
598 #ifdef DEBUG_ACCELERATOR_HANDLING
599                 if (debug) {
600                         cerr << "\tpropagation didn't handle, so activate\n";
601                 }
602 #endif
603
604                 if (allow_activating) {
605                         
606 #ifdef GTKOSX
607                         if (gdk_quartz_possibly_forward ((GdkEvent*) ev)) {
608                                 return true;
609                         }
610 #endif
611                         return gtk_window_activate_key (win, ev);
612                 } 
613                         
614         } else {
615 #ifdef DEBUG_ACCELERATOR_HANDLING
616                 if (debug) {
617                         cerr << "\thandled by propagate\n";
618                 }
619 #endif
620                 return true;
621         }
622
623 #ifdef DEBUG_ACCELERATOR_HANDLING
624         if (debug) {
625                 cerr << "\tnot handled\n";
626         }
627 #endif
628         return true;
629 }
630
631 Glib::RefPtr<Gdk::Pixbuf>       
632 get_xpm (std::string name)
633 {
634         if (!xpm_map[name]) {
635
636                 SearchPath spath(ARDOUR::ardour_search_path());
637                 spath += ARDOUR::system_data_search_path();
638                 
639                 spath.add_subdirectory_to_paths("pixmaps");
640                 
641                 sys::path data_file_path;
642                 
643                 if(!find_file_in_search_path (spath, name, data_file_path)) {
644                         fatal << string_compose (_("cannot find XPM file for %1"), name) << endmsg;
645                 }
646                 
647                 try {
648                         xpm_map[name] =  Gdk::Pixbuf::create_from_file (data_file_path.to_string());
649                 } catch(const Glib::Error& e)   {
650                         warning << "Caught Glib::Error: " << e.what() << endmsg;
651                 }
652         }
653
654         return xpm_map[name];
655 }
656
657
658 Glib::RefPtr<Gdk::Pixbuf>       
659 get_icon (const char* cname)
660 {
661         string name = cname;
662         name += X_(".png");
663
664         SearchPath spath(ARDOUR::ardour_search_path());
665         spath += ARDOUR::system_data_search_path();
666
667         spath.add_subdirectory_to_paths("icons");
668
669         sys::path data_file_path;
670
671         if (!find_file_in_search_path (spath, name, data_file_path)) {
672                 fatal << string_compose (_("cannot find icon image for %1"), name) << endmsg;
673         }
674
675         Glib::RefPtr<Gdk::Pixbuf> img;
676         try {
677                 img = Gdk::Pixbuf::create_from_file (data_file_path.to_string());
678         } catch (const Gdk::PixbufError &e) {
679                 cerr << "Caught PixbufError: " << e.what() << endl;
680         } catch (...) {
681                 g_message("Caught ... ");
682         }
683
684         return img;
685 }
686
687 string
688 longest (vector<string>& strings)
689 {
690         if (strings.empty()) {
691                 return string ("");
692         }
693
694         vector<string>::iterator longest = strings.begin();
695         string::size_type longest_length = (*longest).length();
696         
697         vector<string>::iterator i = longest;
698         ++i;
699
700         while (i != strings.end()) {
701                 
702                 string::size_type len = (*i).length();
703                 
704                 if (len > longest_length) {
705                         longest = i;
706                         longest_length = len;
707                 } 
708                 
709                 ++i;
710         }
711         
712         return *longest;
713 }
714
715 bool
716 key_is_legal_for_numeric_entry (guint keyval)
717 {
718         switch (keyval) {
719         case GDK_minus:
720         case GDK_plus:
721         case GDK_period:
722         case GDK_comma:
723         case GDK_0:
724         case GDK_1:
725         case GDK_2:
726         case GDK_3:
727         case GDK_4:
728         case GDK_5:
729         case GDK_6:
730         case GDK_7:
731         case GDK_8:
732         case GDK_9:
733         case GDK_KP_Add:
734         case GDK_KP_Subtract:
735         case GDK_KP_Decimal:
736         case GDK_KP_0:
737         case GDK_KP_1:
738         case GDK_KP_2:
739         case GDK_KP_3:
740         case GDK_KP_4:
741         case GDK_KP_5:
742         case GDK_KP_6:
743         case GDK_KP_7:
744         case GDK_KP_8:
745         case GDK_KP_9:
746         case GDK_Return:
747         case GDK_BackSpace:
748         case GDK_Delete:
749         case GDK_KP_Enter:
750         case GDK_Home:
751         case GDK_End:
752         case GDK_Left:
753         case GDK_Right:
754                 return true;
755                 
756         default:
757                 break;
758         }
759
760         return false;
761 }
762 void
763 set_pango_fontsize ()
764 {
765         long val = ARDOUR::Config->get_font_scale();
766
767         /* FT2 rendering - used by GnomeCanvas, sigh */
768
769         pango_ft2_font_map_set_resolution ((PangoFT2FontMap*) pango_ft2_font_map_for_display(), val/1024, val/1024);
770
771         /* Cairo rendering, in case there is any */
772         
773         pango_cairo_font_map_set_resolution ((PangoCairoFontMap*) pango_cairo_font_map_get_default(), val/1024);
774 }
775
776 void
777 reset_dpi ()
778 {
779         long val = ARDOUR::Config->get_font_scale();
780         set_pango_fontsize ();
781         /* Xft rendering */
782
783         gtk_settings_set_long_property (gtk_settings_get_default(),
784                                         "gtk-xft-dpi", val, "ardour");
785         DPIReset();//Emit Signal
786 }
787
788 bool
789 possibly_translate_keyval_to_make_legal_accelerator (uint32_t& keyval)
790 {
791         int fakekey = GDK_VoidSymbol;
792
793         switch (keyval) {
794         case GDK_Tab:
795         case GDK_ISO_Left_Tab:
796                 fakekey = GDK_nabla;
797                 break;
798                 
799         case GDK_Up:
800                 fakekey = GDK_uparrow;
801                 break;
802                 
803         case GDK_Down:
804                 fakekey = GDK_downarrow;
805                 break;
806                 
807         case GDK_Right:
808                 fakekey = GDK_rightarrow;
809                 break;
810                 
811         case GDK_Left:
812                 fakekey = GDK_leftarrow;
813                 break;
814                 
815         default:
816                 break;
817         }
818         
819         if (fakekey != GDK_VoidSymbol) {
820                 keyval = fakekey;
821                 return true;
822         } 
823
824         return false;
825 }
826