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