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