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