df922c2d629239b380feb5c585fd9c012a5b070f
[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 /** Try to fit a string into a given horizontal space by ellipsizing it.
117  *  @param cr Cairo context in which the text will be plotted.
118  *  @param name Text.
119  *  @param avail Available horizontal space.
120  *  @return (Text, possibly ellipsized) and (horizontal size of text)
121  */
122
123 std::pair<std::string, double>
124 fit_to_pixels (cairo_t* cr, std::string name, double avail)
125 {
126         /* XXX hopefully there exists a more efficient way of doing this */
127
128         bool abbreviated = false;
129         uint32_t width = 0;
130                 
131         while (1) {
132                 cairo_text_extents_t ext;
133                 cairo_text_extents (cr, name.c_str(), &ext);
134
135                 if (ext.width < avail || name.length() <= 4) {
136                         width = ext.width;
137                         break;
138                 }
139
140                 if (abbreviated) {
141                         name = name.substr (0, name.length() - 4) + "...";
142                 } else {
143                         name = name.substr (0, name.length() - 3) + "...";
144                         abbreviated = true;
145                 }
146         }
147
148         return std::make_pair (name, width);
149 }
150
151
152 /** Add an element to a menu, settings its sensitivity.
153  * @param m Menu to add to.
154  * @param e Element to add.
155  * @param s true to make sensitive, false to make insensitive
156  */
157 void
158 add_item_with_sensitivity (Menu_Helpers::MenuList& m, Menu_Helpers::MenuElem e, bool s)
159 {
160         m.push_back (e);
161         if (!s) {
162                 m.back().set_sensitive (false);
163         }
164 }
165
166
167 gint
168 just_hide_it (GdkEventAny */*ev*/, Gtk::Window *win)
169 {
170         win->hide ();
171         return 0;
172 }
173
174 /* xpm2rgb copied from nixieclock, which bore the legend:
175
176     nixieclock - a nixie desktop timepiece
177     Copyright (C) 2000 Greg Ercolano, erco@3dsite.com
178
179     and was released under the GPL.
180 */
181
182 unsigned char*
183 xpm2rgb (const char** xpm, uint32_t& w, uint32_t& h)
184 {
185         static long vals[256], val;
186         uint32_t t, x, y, colors, cpp;
187         unsigned char c;
188         unsigned char *savergb, *rgb;
189         
190         // PARSE HEADER
191         
192         if ( sscanf(xpm[0], "%u%u%u%u", &w, &h, &colors, &cpp) != 4 ) {
193                 error << string_compose (_("bad XPM header %1"), xpm[0])
194                       << endmsg;
195                 return 0;
196         }
197
198         savergb = rgb = (unsigned char*) malloc (h * w * 3);
199         
200         // LOAD XPM COLORMAP LONG ENOUGH TO DO CONVERSION
201         for (t = 0; t < colors; ++t) {
202                 sscanf (xpm[t+1], "%c c #%lx", &c, &val);
203                 vals[c] = val;
204         }
205         
206         // COLORMAP -> RGB CONVERSION
207         //    Get low 3 bytes from vals[]
208         //
209
210         const char *p;
211         for (y = h-1; y > 0; --y) {
212
213                 for (p = xpm[1+colors+(h-y-1)], x = 0; x < w; x++, rgb += 3) {
214                         val = vals[(int)*p++];
215                         *(rgb+2) = val & 0xff; val >>= 8;  // 2:B
216                         *(rgb+1) = val & 0xff; val >>= 8;  // 1:G
217                         *(rgb+0) = val & 0xff;             // 0:R
218                 }
219         }
220
221         return (savergb);
222 }
223
224 unsigned char*
225 xpm2rgba (const char** xpm, uint32_t& w, uint32_t& h)
226 {
227         static long vals[256], val;
228         uint32_t t, x, y, colors, cpp;
229         unsigned char c;
230         unsigned char *savergb, *rgb;
231         char transparent;
232
233         // PARSE HEADER
234
235         if ( sscanf(xpm[0], "%u%u%u%u", &w, &h, &colors, &cpp) != 4 ) {
236                 error << string_compose (_("bad XPM header %1"), xpm[0])
237                       << endmsg;
238                 return 0;
239         }
240
241         savergb = rgb = (unsigned char*) malloc (h * w * 4);
242         
243         // LOAD XPM COLORMAP LONG ENOUGH TO DO CONVERSION
244
245         if (strstr (xpm[1], "None")) {
246                 sscanf (xpm[1], "%c", &transparent);
247                 t = 1;
248         } else {
249                 transparent = 0;
250                 t = 0;
251         }
252
253         for (; t < colors; ++t) {
254                 sscanf (xpm[t+1], "%c c #%lx", &c, &val);
255                 vals[c] = val;
256         }
257         
258         // COLORMAP -> RGB CONVERSION
259         //    Get low 3 bytes from vals[]
260         //
261
262         const char *p;
263         for (y = h-1; y > 0; --y) {
264
265                 char alpha;
266
267                 for (p = xpm[1+colors+(h-y-1)], x = 0; x < w; x++, rgb += 4) {
268
269                         if (transparent && (*p++ == transparent)) {
270                                 alpha = 0;
271                                 val = 0;
272                         } else {
273                                 alpha = 255;
274                                 val = vals[(int)*p];
275                         }
276
277                         *(rgb+3) = alpha;                  // 3: alpha
278                         *(rgb+2) = val & 0xff; val >>= 8;  // 2:B
279                         *(rgb+1) = val & 0xff; val >>= 8;  // 1:G
280                         *(rgb+0) = val & 0xff;             // 0:R
281                 }
282         }
283
284         return (savergb);
285 }
286
287 ArdourCanvas::Points*
288 get_canvas_points (string /*who*/, uint32_t npoints)
289 {
290         // cerr << who << ": wants " << npoints << " canvas points" << endl;
291 #ifdef TRAP_EXCESSIVE_POINT_REQUESTS
292         if (npoints > (uint32_t) gdk_screen_width() + 4) {
293                 abort ();
294         }
295 #endif
296         return new ArdourCanvas::Points (npoints);
297 }
298
299 Pango::FontDescription*
300 get_font_for_style (string widgetname)
301 {
302         Gtk::Window window (WINDOW_TOPLEVEL);
303         Gtk::Label foobar;
304         Glib::RefPtr<Gtk::Style> style;
305
306         window.add (foobar);
307         foobar.set_name (widgetname);
308         foobar.ensure_style();
309
310         style = foobar.get_style ();
311
312         Glib::RefPtr<const Pango::Layout> layout = foobar.get_layout();
313         
314         PangoFontDescription *pfd = (PangoFontDescription *)pango_layout_get_font_description((PangoLayout *)layout->gobj());
315         
316         if (!pfd) {
317                 
318                 /* layout inherited its font description from a PangoContext */
319
320                 PangoContext* ctxt = (PangoContext*) pango_layout_get_context ((PangoLayout*) layout->gobj());
321                 pfd =  pango_context_get_font_description (ctxt);
322                 return new Pango::FontDescription (pfd, true); /* make a copy */
323         } 
324
325         return new Pango::FontDescription (pfd, true); /* make a copy */
326 }
327
328 uint32_t
329 rgba_from_style (string style, uint32_t r, uint32_t g, uint32_t b, uint32_t a, string attr, int state, bool rgba)
330 {
331         /* In GTK+2, styles aren't set up correctly if the widget is not
332            attached to a toplevel window that has a screen pointer.
333         */
334
335         static Gtk::Window* window = 0;
336
337         if (window == 0) {
338                 window = new Window (WINDOW_TOPLEVEL);
339         }
340
341         Gtk::Label foo;
342         
343         window->add (foo);
344
345         foo.set_name (style);
346         foo.ensure_style ();
347         
348         GtkRcStyle* waverc = foo.get_style()->gobj()->rc_style;
349
350         if (waverc) {
351                 if (attr == "fg") {
352                         r = waverc->fg[state].red / 257;
353                         g = waverc->fg[state].green / 257;
354                         b = waverc->fg[state].blue / 257;
355  
356                         /* what a hack ... "a" is for "active" */
357                         if (state == Gtk::STATE_NORMAL && rgba) {
358                                 a = waverc->fg[GTK_STATE_ACTIVE].red / 257;
359                         }
360                 } else if (attr == "bg") {
361                         r = g = b = 0;
362                         r = waverc->bg[state].red / 257;
363                         g = waverc->bg[state].green / 257;
364                         b = waverc->bg[state].blue / 257;
365                 } else if (attr == "base") {
366                         r = waverc->base[state].red / 257;
367                         g = waverc->base[state].green / 257;
368                         b = waverc->base[state].blue / 257;
369                 } else if (attr == "text") {
370                         r = waverc->text[state].red / 257;
371                         g = waverc->text[state].green / 257;
372                         b = waverc->text[state].blue / 257;
373                 }
374         } else {
375                 warning << string_compose (_("missing RGBA style for \"%1\""), style) << endl;
376         }
377
378         window->remove ();
379         
380         if (state == Gtk::STATE_NORMAL && rgba) {
381                 return (uint32_t) RGBA_TO_UINT(r,g,b,a);
382         } else {
383                 return (uint32_t) RGB_TO_UINT(r,g,b);
384         }
385 }
386
387
388 Gdk::Color
389 color_from_style (string widget_style_name, int state, string attr)
390 {
391         GtkStyle* style;
392
393         style = gtk_rc_get_style_by_paths (gtk_settings_get_default(),
394                                            widget_style_name.c_str(),
395                                            0, G_TYPE_NONE);
396
397         if (!style) {
398                 error << string_compose (_("no style found for %1, using red"), style) << endmsg;
399                 return Gdk::Color ("red");
400         }
401
402         if (attr == "fg") {
403                 return Gdk::Color (&style->fg[state]);
404         }
405
406         if (attr == "bg") {
407                 return Gdk::Color (&style->bg[state]);
408         }
409
410         if (attr == "light") {
411                 return Gdk::Color (&style->light[state]);
412         }
413
414         if (attr == "dark") {
415                 return Gdk::Color (&style->dark[state]);
416         }
417
418         if (attr == "mid") {
419                 return Gdk::Color (&style->mid[state]);
420         }
421
422         if (attr == "text") {
423                 return Gdk::Color (&style->text[state]);
424         }
425
426         if (attr == "base") {
427                 return Gdk::Color (&style->base[state]);
428         }
429
430         if (attr == "text_aa") {
431                 return Gdk::Color (&style->text_aa[state]);
432         }
433
434         error << string_compose (_("unknown style attribute %1 requested for color; using \"red\""), attr) << endmsg;
435         return Gdk::Color ("red");
436 }
437
438 Glib::RefPtr<Gdk::GC>
439 gc_from_style (string widget_style_name, int state, string attr)
440 {
441         GtkStyle* style;
442
443         style = gtk_rc_get_style_by_paths (gtk_settings_get_default(),
444                                            widget_style_name.c_str(),
445                                            0, G_TYPE_NONE);
446
447         if (!style) {
448                 error << string_compose (_("no style found for %1, using red"), style) << endmsg;
449                 Glib::RefPtr<Gdk::GC> ret = Gdk::GC::create();
450                 ret->set_rgb_fg_color(Gdk::Color("red"));
451                 return ret;
452         }
453
454         if (attr == "fg") {
455                 return Glib::wrap(style->fg_gc[state]);
456         }
457
458         if (attr == "bg") {
459                 return Glib::wrap(style->bg_gc[state]);
460         }
461
462         if (attr == "light") {
463                 return Glib::wrap(style->light_gc[state]);
464         }
465
466         if (attr == "dark") {
467                 return Glib::wrap(style->dark_gc[state]);
468         }
469
470         if (attr == "mid") {
471                 return Glib::wrap(style->mid_gc[state]);
472         }
473
474         if (attr == "text") {
475                 return Glib::wrap(style->text_gc[state]);
476         }
477
478         if (attr == "base") {
479                 return Glib::wrap(style->base_gc[state]);
480         }
481
482         if (attr == "text_aa") {
483                 return Glib::wrap(style->text_aa_gc[state]);
484         }
485
486         error << string_compose (_("unknown style attribute %1 requested for color; using \"red\""), attr) << endmsg;
487         Glib::RefPtr<Gdk::GC> ret = Gdk::GC::create();
488         ret->set_rgb_fg_color(Gdk::Color("red"));
489         return ret;
490 }
491
492
493 bool 
494 canvas_item_visible (ArdourCanvas::Item* item)
495 {
496         return (item->gobj()->object.flags & GNOME_CANVAS_ITEM_VISIBLE) ? true : false;
497 }
498
499 void
500 set_color (Gdk::Color& c, int rgb)
501 {
502         c.set_rgb((rgb >> 16)*256, ((rgb & 0xff00) >> 8)*256, (rgb & 0xff)*256);
503 }
504
505 #ifdef GTKOSX
506 extern "C" {
507         gboolean gdk_quartz_possibly_forward (GdkEvent*);
508 }
509 #endif
510
511 bool
512 key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev)
513 {
514         GtkWindow* win = window.gobj();
515         GtkWidget* focus = gtk_window_get_focus (win);
516         bool special_handling_of_unmodified_accelerators = false;
517         bool allow_activating = true;
518
519 #undef DEBUG_ACCELERATOR_HANDLING
520 #ifdef  DEBUG_ACCELERATOR_HANDLING
521         //bool debug = (getenv ("ARDOUR_DEBUG_ACCELERATOR_HANDLING") != 0);
522         bool debug=true;
523 #endif
524         if (focus) {
525                 if (GTK_IS_ENTRY(focus) || Keyboard::some_magic_widget_has_focus()) {
526                         special_handling_of_unmodified_accelerators = true;
527                 } 
528         } 
529
530 #ifdef GTKOSX
531         /* should this be universally true? */
532         if (Keyboard::some_magic_widget_has_focus ()) {
533                 allow_activating = false;
534         }
535 #endif
536
537 #ifdef DEBUG_ACCELERATOR_HANDLING
538         if (debug) {
539                 cerr << "Win = " << win << " Key event: code = " << ev->keyval << " state = " << hex << ev->state << dec << " special handling ? " 
540                      << special_handling_of_unmodified_accelerators
541                      << " magic widget focus ? "
542                      << Keyboard::some_magic_widget_has_focus()
543                      << " allow_activation ? "
544                      << allow_activating
545                      << endl;
546         }
547 #endif
548
549         /* This exists to allow us to override the way GTK handles
550            key events. The normal sequence is:
551
552            a) event is delivered to a GtkWindow
553            b) accelerators/mnemonics are activated
554            c) if (b) didn't handle the event, propagate to
555                the focus widget and/or focus chain
556
557            The problem with this is that if the accelerators include
558            keys without modifiers, such as the space bar or the 
559            letter "e", then pressing the key while typing into
560            a text entry widget results in the accelerator being
561            activated, instead of the desired letter appearing
562            in the text entry.
563
564            There is no good way of fixing this, but this
565            represents a compromise. The idea is that 
566            key events involving modifiers (not Shift)
567            get routed into the activation pathway first, then
568            get propagated to the focus widget if necessary.
569            
570            If the key event doesn't involve modifiers,
571            we deliver to the focus widget first, thus allowing
572            it to get "normal text" without interference
573            from acceleration.
574
575            Of course, this can also be problematic: if there
576            is a widget with focus, then it will swallow
577            all "normal text" accelerators.
578         */
579
580
581         if (!special_handling_of_unmodified_accelerators) {
582
583                 /* pretend that certain key events that GTK does not allow
584                    to be used as accelerators are actually something that
585                    it does allow.
586                 */
587
588                 uint32_t fakekey = ev->keyval;
589
590                 if (possibly_translate_keyval_to_make_legal_accelerator (fakekey)) {
591                         if (allow_activating && gtk_accel_groups_activate(G_OBJECT(win), fakekey, GdkModifierType(ev->state))) {
592                                 return true;
593                         }
594
595 #ifdef GTKOSX
596                         if (allow_activating) {
597                                 int oldval = ev->keyval;
598                                 ev->keyval = fakekey;
599                                 if (gdk_quartz_possibly_forward ((GdkEvent*) ev)) {
600                                         return true;
601                                 }
602                                 ev->keyval = oldval;
603                         }
604 #endif
605                 }
606         }
607
608         /* consider all relevant modifiers but not LOCK or SHIFT */
609
610         guint mask = (Keyboard::RelevantModifierKeyMask & ~(Gdk::SHIFT_MASK|Gdk::LOCK_MASK));
611
612         if (!special_handling_of_unmodified_accelerators || (ev->state & mask)) {
613
614                 /* no special handling or there are modifiers in effect: accelerate first */
615
616 #ifdef DEBUG_ACCELERATOR_HANDLING
617                 if (debug) {
618                         cerr << "\tactivate, then propagate\n";
619                 }
620 #endif
621
622                 if (allow_activating) {
623 #ifdef GTKOSX
624                         if (gdk_quartz_possibly_forward ((GdkEvent*) ev)) {
625                                 return true;
626                         }
627 #endif
628                         if (gtk_window_activate_key (win, ev)) {
629                                 return true;
630                         }
631                 }
632
633 #ifdef DEBUG_ACCELERATOR_HANDLING
634                 if (debug) {
635                         cerr << "\tnot accelerated, now propagate\n";
636                 }
637 #endif
638                 return gtk_window_propagate_key_event (win, ev);
639         }
640         
641         /* no modifiers, propagate first */
642         
643 #ifdef DEBUG_ACCELERATOR_HANDLING
644         if (debug) {
645                 cerr << "\tpropagate, then activate\n";
646         }
647 #endif
648         if (!gtk_window_propagate_key_event (win, ev)) {
649 #ifdef DEBUG_ACCELERATOR_HANDLING
650                 if (debug) {
651                         cerr << "\tpropagation didn't handle, so activate\n";
652                 }
653 #endif
654
655                 if (allow_activating) {
656                         
657 #ifdef GTKOSX
658                         if (gdk_quartz_possibly_forward ((GdkEvent*) ev)) {
659                                 return true;
660                         }
661 #endif
662                         return gtk_window_activate_key (win, ev);
663                 } 
664                         
665         } else {
666 #ifdef DEBUG_ACCELERATOR_HANDLING
667                 if (debug) {
668                         cerr << "\thandled by propagate\n";
669                 }
670 #endif
671                 return true;
672         }
673
674 #ifdef DEBUG_ACCELERATOR_HANDLING
675         if (debug) {
676                 cerr << "\tnot handled\n";
677         }
678 #endif
679         return true;
680 }
681
682 Glib::RefPtr<Gdk::Pixbuf>       
683 get_xpm (std::string name)
684 {
685         if (!xpm_map[name]) {
686
687                 SearchPath spath(ARDOUR::ardour_search_path());
688                 spath += ARDOUR::system_data_search_path();
689                 
690                 spath.add_subdirectory_to_paths("pixmaps");
691                 
692                 sys::path data_file_path;
693                 
694                 if(!find_file_in_search_path (spath, name, data_file_path)) {
695                         fatal << string_compose (_("cannot find XPM file for %1"), name) << endmsg;
696                 }
697                 
698                 try {
699                         xpm_map[name] =  Gdk::Pixbuf::create_from_file (data_file_path.to_string());
700                 } catch(const Glib::Error& e)   {
701                         warning << "Caught Glib::Error: " << e.what() << endmsg;
702                 }
703         }
704
705         return xpm_map[name];
706 }
707
708
709 Glib::RefPtr<Gdk::Pixbuf>       
710 get_icon (const char* cname)
711 {
712         string name = cname;
713         name += X_(".png");
714
715         SearchPath spath(ARDOUR::ardour_search_path());
716         spath += ARDOUR::system_data_search_path();
717
718         spath.add_subdirectory_to_paths("icons");
719
720         sys::path data_file_path;
721
722         if (!find_file_in_search_path (spath, name, data_file_path)) {
723                 fatal << string_compose (_("cannot find icon image for %1"), name) << endmsg;
724         }
725
726         Glib::RefPtr<Gdk::Pixbuf> img;
727         try {
728                 img = Gdk::Pixbuf::create_from_file (data_file_path.to_string());
729         } catch (const Gdk::PixbufError &e) {
730                 cerr << "Caught PixbufError: " << e.what() << endl;
731         } catch (...) {
732                 g_message("Caught ... ");
733         }
734
735         return img;
736 }
737
738 string
739 longest (vector<string>& strings)
740 {
741         if (strings.empty()) {
742                 return string ("");
743         }
744
745         vector<string>::iterator longest = strings.begin();
746         string::size_type longest_length = (*longest).length();
747         
748         vector<string>::iterator i = longest;
749         ++i;
750
751         while (i != strings.end()) {
752                 
753                 string::size_type len = (*i).length();
754                 
755                 if (len > longest_length) {
756                         longest = i;
757                         longest_length = len;
758                 } 
759                 
760                 ++i;
761         }
762         
763         return *longest;
764 }
765
766 bool
767 key_is_legal_for_numeric_entry (guint keyval)
768 {
769         switch (keyval) {
770         case GDK_minus:
771         case GDK_plus:
772         case GDK_period:
773         case GDK_comma:
774         case GDK_0:
775         case GDK_1:
776         case GDK_2:
777         case GDK_3:
778         case GDK_4:
779         case GDK_5:
780         case GDK_6:
781         case GDK_7:
782         case GDK_8:
783         case GDK_9:
784         case GDK_KP_Add:
785         case GDK_KP_Subtract:
786         case GDK_KP_Decimal:
787         case GDK_KP_0:
788         case GDK_KP_1:
789         case GDK_KP_2:
790         case GDK_KP_3:
791         case GDK_KP_4:
792         case GDK_KP_5:
793         case GDK_KP_6:
794         case GDK_KP_7:
795         case GDK_KP_8:
796         case GDK_KP_9:
797         case GDK_Return:
798         case GDK_BackSpace:
799         case GDK_Delete:
800         case GDK_KP_Enter:
801         case GDK_Home:
802         case GDK_End:
803         case GDK_Left:
804         case GDK_Right:
805                 return true;
806                 
807         default:
808                 break;
809         }
810
811         return false;
812 }
813 void
814 set_pango_fontsize ()
815 {
816         long val = ARDOUR::Config->get_font_scale();
817
818         /* FT2 rendering - used by GnomeCanvas, sigh */
819
820         pango_ft2_font_map_set_resolution ((PangoFT2FontMap*) pango_ft2_font_map_for_display(), val/1024, val/1024);
821
822         /* Cairo rendering, in case there is any */
823         
824         pango_cairo_font_map_set_resolution ((PangoCairoFontMap*) pango_cairo_font_map_get_default(), val/1024);
825 }
826
827 void
828 reset_dpi ()
829 {
830         long val = ARDOUR::Config->get_font_scale();
831         set_pango_fontsize ();
832         /* Xft rendering */
833
834         gtk_settings_set_long_property (gtk_settings_get_default(),
835                                         "gtk-xft-dpi", val, "ardour");
836         DPIReset();//Emit Signal
837 }
838
839 bool
840 possibly_translate_keyval_to_make_legal_accelerator (uint32_t& keyval)
841 {
842         int fakekey = GDK_VoidSymbol;
843
844         switch (keyval) {
845         case GDK_Tab:
846         case GDK_ISO_Left_Tab:
847                 fakekey = GDK_nabla;
848                 break;
849                 
850         case GDK_Up:
851                 fakekey = GDK_uparrow;
852                 break;
853                 
854         case GDK_Down:
855                 fakekey = GDK_downarrow;
856                 break;
857                 
858         case GDK_Right:
859                 fakekey = GDK_rightarrow;
860                 break;
861                 
862         case GDK_Left:
863                 fakekey = GDK_leftarrow;
864                 break;
865                 
866         default:
867                 break;
868         }
869         
870         if (fakekey != GDK_VoidSymbol) {
871                 keyval = fakekey;
872                 return true;
873         } 
874
875         return false;
876 }
877                 
878
879 inline guint8
880 convert_color_channel (guint8 src,
881                        guint8 alpha)
882 {
883         return alpha ? ((guint (src) << 8) - src) / alpha : 0;
884 }
885
886 void
887 convert_bgra_to_rgba (guint8 const* src,
888                       guint8*       dst,
889                       int           width,
890                       int           height)
891 {
892         guint8 const* src_pixel = src;
893         guint8*       dst_pixel = dst;
894         
895         for (int y = 0; y < height; y++)
896                 for (int x = 0; x < width; x++)
897                 {
898                         dst_pixel[0] = convert_color_channel (src_pixel[2],
899                                                               src_pixel[3]);
900                         dst_pixel[1] = convert_color_channel (src_pixel[1],
901                                                               src_pixel[3]);
902                         dst_pixel[2] = convert_color_channel (src_pixel[0],
903                                                               src_pixel[3]);
904                         dst_pixel[3] = src_pixel[3];
905                         
906                         dst_pixel += 4;
907                         src_pixel += 4;
908                 }
909 }