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