Fix warning.
[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 #include "gui_thread.h"
51
52 using namespace std;
53 using namespace Gtk;
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 bool
508 relay_key_press (GdkEventKey* ev, Gtk::Window* win)
509 {
510         if (!key_press_focus_accelerator_handler (*win, ev)) {
511                 return PublicEditor::instance().on_key_press_event(ev);
512         } else {
513                 return true;
514         }
515 }
516
517 bool
518 forward_key_press (GdkEventKey* ev)
519 {
520         return PublicEditor::instance().on_key_press_event(ev);
521 }
522
523 #ifdef GTKOSX
524 static guint
525 osx_keyval_without_alt (guint accent_keyval)
526 {
527         switch (accent_keyval) {
528         case GDK_oe:
529                 return GDK_q;
530         case GDK_registered:
531                 return GDK_r;
532         case GDK_dagger:
533                 return GDK_t;
534         case GDK_yen:
535                 return GDK_y;
536         case GDK_diaeresis:
537                 return GDK_u;
538         case GDK_oslash:
539                 return GDK_o;
540         case GDK_Greek_pi:
541                 return GDK_p;
542         case GDK_leftdoublequotemark:
543                 return GDK_bracketleft;
544         case GDK_leftsinglequotemark:
545                 return GDK_bracketright;
546         case GDK_guillemotleft:
547                 return GDK_backslash;
548         case GDK_aring:
549                 return GDK_a;
550         case GDK_ssharp:
551                 return GDK_s;
552         case GDK_partialderivative:
553                 return GDK_d;
554         case GDK_function:
555                 return GDK_f;
556         case GDK_copyright:
557                 return GDK_g;
558         case GDK_abovedot:
559                 return GDK_h;
560         case GDK_notsign:
561                 return GDK_l;
562         case GDK_ellipsis:
563                 return GDK_semicolon;
564         case GDK_ae:
565                 return GDK_apostrophe;
566         case GDK_Greek_OMEGA:
567                 return GDK_z;
568         case GDK_ccedilla:
569                 return GDK_c;
570         case GDK_radical:
571                 return GDK_v;
572         case GDK_integral:
573                 return GDK_b;
574         case GDK_mu:
575                 return GDK_m;
576         case GDK_lessthanequal:
577                 return GDK_comma;
578         case GDK_greaterthanequal:
579                 return GDK_period;
580         case GDK_division:
581                 return GDK_slash;
582         default:
583                 break;
584         }
585
586         return GDK_VoidSymbol;
587 }
588 #endif
589
590 bool
591 key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev)
592 {
593         GtkWindow* win = window.gobj();
594         GtkWidget* focus = gtk_window_get_focus (win);
595         bool special_handling_of_unmodified_accelerators = false;
596         bool allow_activating = true;
597
598 #undef DEBUG_ACCELERATOR_HANDLING
599 #ifdef  DEBUG_ACCELERATOR_HANDLING
600         //bool debug = (getenv ("ARDOUR_DEBUG_ACCELERATOR_HANDLING") != 0);
601         bool debug=true;
602 #endif
603         if (focus) {
604                 if (GTK_IS_ENTRY(focus) || Keyboard::some_magic_widget_has_focus()) {
605                         special_handling_of_unmodified_accelerators = true;
606                 }
607         }
608
609 #ifdef GTKOSX
610         /* should this be universally true? */
611         if (Keyboard::some_magic_widget_has_focus ()) {
612                 allow_activating = false;
613         }
614 #endif
615
616 #ifdef DEBUG_ACCELERATOR_HANDLING
617         if (debug) {
618                 cerr << "Win = " << win << " Key event: code = " << ev->keyval << " state = " << hex << ev->state << dec << " special handling ? "
619                      << special_handling_of_unmodified_accelerators
620                      << " magic widget focus ? "
621                      << Keyboard::some_magic_widget_has_focus()
622                      << " allow_activation ? "
623                      << allow_activating
624                      << endl;
625         }
626 #endif
627
628         /* This exists to allow us to override the way GTK handles
629            key events. The normal sequence is:
630
631            a) event is delivered to a GtkWindow
632            b) accelerators/mnemonics are activated
633            c) if (b) didn't handle the event, propagate to
634                the focus widget and/or focus chain
635
636            The problem with this is that if the accelerators include
637            keys without modifiers, such as the space bar or the
638            letter "e", then pressing the key while typing into
639            a text entry widget results in the accelerator being
640            activated, instead of the desired letter appearing
641            in the text entry.
642
643            There is no good way of fixing this, but this
644            represents a compromise. The idea is that
645            key events involving modifiers (not Shift)
646            get routed into the activation pathway first, then
647            get propagated to the focus widget if necessary.
648
649            If the key event doesn't involve modifiers,
650            we deliver to the focus widget first, thus allowing
651            it to get "normal text" without interference
652            from acceleration.
653
654            Of course, this can also be problematic: if there
655            is a widget with focus, then it will swallow
656            all "normal text" accelerators.
657         */
658
659 #ifdef GTKOSX
660         if (!special_handling_of_unmodified_accelerators) {
661                 if (ev->state & GDK_MOD1_MASK) {
662                         /* we're not in a text entry or "magic focus" widget so we don't want OS X "special-character" 
663                            text-style handling of alt-<key>. change the keyval back to what it would be without
664                            the alt key. this way, we see <alt>-v rather than <alt>-radical and so on.
665                         */
666                         guint keyval_without_alt = osx_keyval_without_alt (ev->keyval);
667
668                         if (keyval_without_alt != GDK_VoidSymbol) {
669 #ifdef DEBUG_ACCELERATOR_HANDLING
670                                 cerr << "Remapped " << gdk_keyval_name (ev->keyval) << " to " << gdk_keyval_name (keyval_without_alt) << endl;
671
672 #endif                          ev->keyval = keyval_without_alt;
673                         }
674                 }
675         }
676 #endif
677
678         if (!special_handling_of_unmodified_accelerators) {
679
680                 /* pretend that certain key events that GTK does not allow
681                    to be used as accelerators are actually something that
682                    it does allow.
683                 */
684
685                 uint32_t fakekey = ev->keyval;
686
687                 if (possibly_translate_keyval_to_make_legal_accelerator (fakekey)) {
688                         if (allow_activating && gtk_accel_groups_activate(G_OBJECT(win), fakekey, GdkModifierType(ev->state))) {
689                                 return true;
690                         }
691                 }
692         }
693
694         /* consider all relevant modifiers but not LOCK or SHIFT */
695
696         guint mask = (Keyboard::RelevantModifierKeyMask & ~(Gdk::SHIFT_MASK|Gdk::LOCK_MASK));
697
698         if (!special_handling_of_unmodified_accelerators || (ev->state & mask)) {
699
700                 /* no special handling or there are modifiers in effect: accelerate first */
701
702 #ifdef DEBUG_ACCELERATOR_HANDLING
703                 if (debug) {
704                         cerr << "\tactivate, then propagate\n";
705                 }
706 #endif
707
708                 if (allow_activating) {
709                         if (gtk_window_activate_key (win, ev)) {
710                                 return true;
711                         }
712                 }
713
714 #ifdef DEBUG_ACCELERATOR_HANDLING
715                 if (debug) {
716                         cerr << "\tnot accelerated, now propagate\n";
717                 }
718 #endif
719                 return gtk_window_propagate_key_event (win, ev);
720         }
721
722         /* no modifiers, propagate first */
723
724 #ifdef DEBUG_ACCELERATOR_HANDLING
725         if (debug) {
726                 cerr << "\tpropagate, then activate\n";
727         }
728 #endif
729         if (!gtk_window_propagate_key_event (win, ev)) {
730 #ifdef DEBUG_ACCELERATOR_HANDLING
731                 if (debug) {
732                         cerr << "\tpropagation didn't handle, so activate\n";
733                 }
734 #endif
735
736                 if (allow_activating) {
737                         return gtk_window_activate_key (win, ev);
738                 }
739
740         } else {
741 #ifdef DEBUG_ACCELERATOR_HANDLING
742                 if (debug) {
743                         cerr << "\thandled by propagate\n";
744                 }
745 #endif
746                 return true;
747         }
748
749 #ifdef DEBUG_ACCELERATOR_HANDLING
750         if (debug) {
751                 cerr << "\tnot handled\n";
752         }
753 #endif
754         return true;
755 }
756
757 Glib::RefPtr<Gdk::Pixbuf>
758 get_xpm (std::string name)
759 {
760         if (!xpm_map[name]) {
761
762                 SearchPath spath(ARDOUR::ardour_search_path());
763                 spath += ARDOUR::system_data_search_path();
764
765                 spath.add_subdirectory_to_paths("pixmaps");
766
767                 sys::path data_file_path;
768
769                 if(!find_file_in_search_path (spath, name, data_file_path)) {
770                         fatal << string_compose (_("cannot find XPM file for %1"), name) << endmsg;
771                 }
772
773                 try {
774                         xpm_map[name] =  Gdk::Pixbuf::create_from_file (data_file_path.to_string());
775                 } catch(const Glib::Error& e)   {
776                         warning << "Caught Glib::Error: " << e.what() << endmsg;
777                 }
778         }
779
780         return xpm_map[name];
781 }
782
783 Glib::ustring
784 get_icon_path (const char* cname)
785 {
786         string name = cname;
787         name += X_(".png");
788
789         SearchPath spath(ARDOUR::ardour_search_path());
790         spath += ARDOUR::system_data_search_path();
791
792         spath.add_subdirectory_to_paths("icons");
793
794         sys::path data_file_path;
795
796         if (!find_file_in_search_path (spath, name, data_file_path)) {
797                 fatal << string_compose (_("cannot find icon image for %1"), name) << endmsg;
798         }
799
800         return data_file_path.to_string();
801 }
802
803 Glib::RefPtr<Gdk::Pixbuf>
804 get_icon (const char* cname)
805 {
806         Glib::RefPtr<Gdk::Pixbuf> img;
807         try {
808                 img = Gdk::Pixbuf::create_from_file (get_icon_path (cname));
809         } catch (const Gdk::PixbufError &e) {
810                 cerr << "Caught PixbufError: " << e.what() << endl;
811         } catch (...) {
812                 g_message("Caught ... ");
813         }
814
815         return img;
816 }
817
818 string
819 longest (vector<string>& strings)
820 {
821         if (strings.empty()) {
822                 return string ("");
823         }
824
825         vector<string>::iterator longest = strings.begin();
826         string::size_type longest_length = (*longest).length();
827
828         vector<string>::iterator i = longest;
829         ++i;
830
831         while (i != strings.end()) {
832
833                 string::size_type len = (*i).length();
834
835                 if (len > longest_length) {
836                         longest = i;
837                         longest_length = len;
838                 }
839
840                 ++i;
841         }
842
843         return *longest;
844 }
845
846 bool
847 key_is_legal_for_numeric_entry (guint keyval)
848 {
849         switch (keyval) {
850         case GDK_minus:
851         case GDK_plus:
852         case GDK_period:
853         case GDK_comma:
854         case GDK_0:
855         case GDK_1:
856         case GDK_2:
857         case GDK_3:
858         case GDK_4:
859         case GDK_5:
860         case GDK_6:
861         case GDK_7:
862         case GDK_8:
863         case GDK_9:
864         case GDK_KP_Add:
865         case GDK_KP_Subtract:
866         case GDK_KP_Decimal:
867         case GDK_KP_0:
868         case GDK_KP_1:
869         case GDK_KP_2:
870         case GDK_KP_3:
871         case GDK_KP_4:
872         case GDK_KP_5:
873         case GDK_KP_6:
874         case GDK_KP_7:
875         case GDK_KP_8:
876         case GDK_KP_9:
877         case GDK_Return:
878         case GDK_BackSpace:
879         case GDK_Delete:
880         case GDK_KP_Enter:
881         case GDK_Home:
882         case GDK_End:
883         case GDK_Left:
884         case GDK_Right:
885                 return true;
886
887         default:
888                 break;
889         }
890
891         return false;
892 }
893 void
894 set_pango_fontsize ()
895 {
896         long val = ARDOUR::Config->get_font_scale();
897
898         /* FT2 rendering - used by GnomeCanvas, sigh */
899
900         pango_ft2_font_map_set_resolution ((PangoFT2FontMap*) pango_ft2_font_map_for_display(), val/1024, val/1024);
901
902         /* Cairo rendering, in case there is any */
903
904         pango_cairo_font_map_set_resolution ((PangoCairoFontMap*) pango_cairo_font_map_get_default(), val/1024);
905 }
906
907 void
908 reset_dpi ()
909 {
910         long val = ARDOUR::Config->get_font_scale();
911         set_pango_fontsize ();
912         /* Xft rendering */
913
914         gtk_settings_set_long_property (gtk_settings_get_default(),
915                                         "gtk-xft-dpi", val, "ardour");
916         DPIReset();//Emit Signal
917 }
918
919 bool
920 possibly_translate_keyval_to_make_legal_accelerator (uint32_t& keyval)
921 {
922         int fakekey = GDK_VoidSymbol;
923
924         switch (keyval) {
925         case GDK_Tab:
926         case GDK_ISO_Left_Tab:
927                 fakekey = GDK_nabla;
928                 break;
929
930         case GDK_Up:
931                 fakekey = GDK_uparrow;
932                 break;
933
934         case GDK_Down:
935                 fakekey = GDK_downarrow;
936                 break;
937
938         case GDK_Right:
939                 fakekey = GDK_rightarrow;
940                 break;
941
942         case GDK_Left:
943                 fakekey = GDK_leftarrow;
944                 break;
945
946         case GDK_Return:
947                 fakekey = GDK_3270_Enter;
948                 break;
949
950         case GDK_KP_Enter:
951                 fakekey = GDK_F35;
952                 break;
953
954         default:
955                 break;
956         }
957
958         if (fakekey != GDK_VoidSymbol) {
959                 keyval = fakekey;
960                 return true;
961         }
962
963         return false;
964 }
965
966 uint32_t
967 possibly_translate_legal_accelerator_to_real_key (uint32_t keyval)
968 {
969         switch (keyval) {
970         case GDK_nabla:
971                 return GDK_Tab;
972                 break;
973
974         case GDK_uparrow:
975                 return GDK_Up;
976                 break;
977
978         case GDK_downarrow:
979                 return GDK_Down;
980                 break;
981
982         case GDK_rightarrow:
983                 return GDK_Right;
984                 break;
985
986         case GDK_leftarrow:
987                 return GDK_Left;
988                 break;
989
990         case GDK_3270_Enter:
991                 return GDK_Return;
992
993         case GDK_F35:
994                 return GDK_KP_Enter;
995                 break;
996         }
997
998         return keyval;
999 }
1000
1001
1002
1003 inline guint8
1004 convert_color_channel (guint8 src,
1005                        guint8 alpha)
1006 {
1007         return alpha ? ((guint (src) << 8) - src) / alpha : 0;
1008 }
1009
1010 void
1011 convert_bgra_to_rgba (guint8 const* src,
1012                       guint8*       dst,
1013                       int           width,
1014                       int           height)
1015 {
1016         guint8 const* src_pixel = src;
1017         guint8*       dst_pixel = dst;
1018
1019         for (int y = 0; y < height; y++)
1020                 for (int x = 0; x < width; x++)
1021                 {
1022                         dst_pixel[0] = convert_color_channel (src_pixel[2],
1023                                                               src_pixel[3]);
1024                         dst_pixel[1] = convert_color_channel (src_pixel[1],
1025                                                               src_pixel[3]);
1026                         dst_pixel[2] = convert_color_channel (src_pixel[0],
1027                                                               src_pixel[3]);
1028                         dst_pixel[3] = src_pixel[3];
1029
1030                         dst_pixel += 4;
1031                         src_pixel += 4;
1032                 }
1033 }
1034
1035 void
1036 resize_window_to_proportion_of_monitor (Gtk::Window* window, int max_width, int max_height)
1037 {
1038         Glib::RefPtr<Gdk::Screen> screen = window->get_screen ();
1039         Gdk::Rectangle monitor_rect;
1040         screen->get_monitor_geometry (0, monitor_rect);
1041
1042         int const w = std::min (monitor_rect.get_width(), max_width) * 0.8;
1043         int const h = std::min (monitor_rect.get_height(), max_height) * 0.8;
1044
1045         window->resize (w, h);
1046 }
1047
1048 Glib::RefPtr<Gdk::Pixbuf>
1049 pixbuf_from_ustring(const ustring& name, Pango::FontDescription* font, int clip_width, int clip_height, Gdk::Color fg)
1050 {
1051         static Glib::RefPtr<Gdk::Pixbuf>* empty_pixbuf = 0;
1052
1053         if (name.empty()) {
1054                 if (empty_pixbuf == 0) {
1055                         empty_pixbuf = new Glib::RefPtr<Gdk::Pixbuf>;
1056                         *empty_pixbuf = Gdk::Pixbuf::create(Gdk::COLORSPACE_RGB, true, 8, clip_width, clip_height);
1057                 }
1058                 return *empty_pixbuf;
1059         }
1060
1061         Glib::RefPtr<Gdk::Pixbuf> buf = Gdk::Pixbuf::create(Gdk::COLORSPACE_RGB, true, 8, clip_width, clip_height);
1062
1063         cairo_surface_t* surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, clip_width, clip_height);
1064         cairo_t* cr = cairo_create (surface);
1065         cairo_text_extents_t te;
1066         
1067         cairo_set_source_rgba (cr, fg.get_red_p(), fg.get_green_p(), fg.get_blue_p(), 1.0);
1068         cairo_select_font_face (cr, font->get_family().c_str(),
1069                                 CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
1070         cairo_set_font_size (cr,  font->get_size() / Pango::SCALE);
1071         cairo_text_extents (cr, name.c_str(), &te);
1072         
1073         cairo_move_to (cr, 0.5, 0.5 - te.height / 2 - te.y_bearing + clip_height / 2);
1074         cairo_show_text (cr, name.c_str());
1075         
1076         convert_bgra_to_rgba(cairo_image_surface_get_data (surface), buf->get_pixels(), clip_width, clip_height);
1077
1078         cairo_destroy(cr);
1079         cairo_surface_destroy(surface);
1080
1081         return buf;
1082 }
1083
1084 /** Replace _ with __ in a string; for use with menu item text to make underscores displayed correctly */
1085 string
1086 escape_underscores (string const & s)
1087 {
1088         string o;
1089         string::size_type const N = s.length ();
1090
1091         for (string::size_type i = 0; i < N; ++i) {
1092                 if (s[i] == '_') {
1093                         o += "__";
1094                 } else {
1095                         o += s[i];
1096                 }
1097         }
1098
1099         return o;
1100 }
1101
1102 static void
1103 adjustment_to_controllable (Gtk::Adjustment* adj, boost::weak_ptr<Controllable> wcont)
1104 {
1105         boost::shared_ptr<Controllable> cont = wcont.lock();
1106
1107         if (cont) {
1108                 double val = adj->get_value();
1109                 if (val != cont->get_value()) {
1110                         cont->set_value (val);
1111                 }
1112         }
1113 }
1114
1115 static void
1116 controllable_to_adjustment (Gtk::Adjustment* adj, boost::weak_ptr<Controllable> wcont)
1117 {
1118         boost::shared_ptr<Controllable> cont = wcont.lock();
1119
1120         if (cont) {
1121                 float val = cont->get_value();
1122                 
1123                 if (val != adj->get_value()) {
1124                         adj->set_value (val);
1125                 }
1126         }
1127 }
1128
1129 void
1130 control_link (ScopedConnectionList& scl, boost::shared_ptr<Controllable> c, Gtk::Adjustment& a)
1131 {
1132         boost::weak_ptr<Controllable> wc (c);
1133
1134         a.signal_value_changed().connect (sigc::bind (sigc::ptr_fun (adjustment_to_controllable), &a, wc));
1135         c->Changed.connect (scl, MISSING_INVALIDATOR, boost::bind (controllable_to_adjustment, &a, wc),
1136                             gui_context());
1137 }
1138