Expand just entry fields when the dialog is expanded
[ardour.git] / gtk2_ardour / utils.cc
index fbebf0d51afc0a1a888a192ca4292bf8dbb811dc..d061fce35f3963bbd6dbbcdd0e37312a97cb00ef 100644 (file)
@@ -1,7 +1,7 @@
 /*
 /*
-    Copyright (C) 2003 Paul Davis 
+    Copyright (C) 2003 Paul Davis
 
 
-    This program is free software; you can redistribute it and/or modify
+    This program is free software; you an redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
     the Free Software Foundation; either version 2 of the License, or
     (at your option) any later version.
     it under the terms of the GNU General Public License as published by
     the Free Software Foundation; either version 2 of the License, or
     (at your option) any later version.
 
 */
 
 
 */
 
+#ifdef WAF_BUILD
+#include "gtk2ardour-config.h"
+#endif
+
+#include <pango/pangoft2.h> // for fontmap resolution control for GnomeCanvas
+#include <pango/pangocairo.h> // for fontmap resolution control for GnomeCanvas
+
 #include <cstdlib>
 #include <cctype>
 #include <fstream>
 #include <cstdlib>
 #include <cctype>
 #include <fstream>
+#include <list>
 #include <sys/stat.h>
 #include <libart_lgpl/art_misc.h>
 #include <sys/stat.h>
 #include <libart_lgpl/art_misc.h>
+#include <gtkmm/rc.h>
 #include <gtkmm/window.h>
 #include <gtkmm/combo.h>
 #include <gtkmm/label.h>
 #include <gtkmm/paned.h>
 #include <gtk/gtkpaned.h>
 
 #include <gtkmm/window.h>
 #include <gtkmm/combo.h>
 #include <gtkmm/label.h>
 #include <gtkmm/paned.h>
 #include <gtk/gtkpaned.h>
 
+#include "pbd/file_utils.h"
+
 #include <gtkmm2ext/utils.h>
 #include <gtkmm2ext/utils.h>
-#include <ardour/ardour.h>
+#include "ardour/configuration.h"
+#include "ardour/rc_configuration.h"
+
+#include "ardour/filesystem_paths.h"
 
 #include "ardour_ui.h"
 
 #include "ardour_ui.h"
+#include "debug.h"
+#include "public_editor.h"
 #include "keyboard.h"
 #include "utils.h"
 #include "i18n.h"
 #include "rgb_macros.h"
 #include "canvas_impl.h"
 #include "keyboard.h"
 #include "utils.h"
 #include "i18n.h"
 #include "rgb_macros.h"
 #include "canvas_impl.h"
+#include "gui_thread.h"
 
 using namespace std;
 using namespace Gtk;
 
 using namespace std;
 using namespace Gtk;
-using namespace sigc;
 using namespace Glib;
 using namespace PBD;
 using namespace Glib;
 using namespace PBD;
+using Gtkmm2ext::Keyboard;
 
 
-ustring
-fit_to_pixels (const ustring& str, int pixel_width, Pango::FontDescription& font, int& actual_width, bool with_ellipses)
-{
-       Label foo;
-       Glib::RefPtr<Pango::Layout> layout = foo.create_pango_layout ("");
-       ustring::size_type shorter_by = 0;
-       ustring txt;
-
-       layout->set_font_description (font);
-
-       actual_width = 0;
-
-       ustring ustr = str;
-       ustring::iterator last = ustr.end();
-       --last; /* now points at final entry */
+sigc::signal<void>  DPIReset;
 
 
-       txt = ustr;
 
 
-       while (!ustr.empty()) {
-
-               layout->set_text (txt);
-
-               int width, height;
-               Gtkmm2ext::get_ink_pixel_size (layout, width, height);
-
-               if (width < pixel_width) {
-                       actual_width = width;
-                       break;
-               }
-               
-               ustr.erase (last--);
-               shorter_by++;
-
-               if (with_ellipses && shorter_by > 3) {
-                       txt = ustr;
-                       txt += "...";
-               } else {
-                       txt = ustr;
-               }
+/** Add an element to a menu, settings its sensitivity.
+ * @param m Menu to add to.
+ * @param e Element to add.
+ * @param s true to make sensitive, false to make insensitive
+ */
+void
+add_item_with_sensitivity (Menu_Helpers::MenuList& m, Menu_Helpers::MenuElem e, bool s)
+{
+       m.push_back (e);
+       if (!s) {
+               m.back().set_sensitive (false);
        }
        }
-
-       return txt;
 }
 
 }
 
+
 gint
 gint
-just_hide_it (GdkEventAny *ev, Gtk::Window *win)
+just_hide_it (GdkEventAny */*ev*/, Gtk::Window *win)
 {
 {
-       win->hide_all ();
-       return TRUE;
+       win->hide ();
+       return 0;
 }
 
 /* xpm2rgb copied from nixieclock, which bore the legend:
 }
 
 /* xpm2rgb copied from nixieclock, which bore the legend:
@@ -110,9 +101,9 @@ xpm2rgb (const char** xpm, uint32_t& w, uint32_t& h)
        uint32_t t, x, y, colors, cpp;
        unsigned char c;
        unsigned char *savergb, *rgb;
        uint32_t t, x, y, colors, cpp;
        unsigned char c;
        unsigned char *savergb, *rgb;
-       
+
        // PARSE HEADER
        // PARSE HEADER
-       
+
        if ( sscanf(xpm[0], "%u%u%u%u", &w, &h, &colors, &cpp) != 4 ) {
                error << string_compose (_("bad XPM header %1"), xpm[0])
                      << endmsg;
        if ( sscanf(xpm[0], "%u%u%u%u", &w, &h, &colors, &cpp) != 4 ) {
                error << string_compose (_("bad XPM header %1"), xpm[0])
                      << endmsg;
@@ -120,13 +111,13 @@ xpm2rgb (const char** xpm, uint32_t& w, uint32_t& h)
        }
 
        savergb = rgb = (unsigned char*) malloc (h * w * 3);
        }
 
        savergb = rgb = (unsigned char*) malloc (h * w * 3);
-       
+
        // LOAD XPM COLORMAP LONG ENOUGH TO DO CONVERSION
        for (t = 0; t < colors; ++t) {
                sscanf (xpm[t+1], "%c c #%lx", &c, &val);
                vals[c] = val;
        }
        // LOAD XPM COLORMAP LONG ENOUGH TO DO CONVERSION
        for (t = 0; t < colors; ++t) {
                sscanf (xpm[t+1], "%c c #%lx", &c, &val);
                vals[c] = val;
        }
-       
+
        // COLORMAP -> RGB CONVERSION
        //    Get low 3 bytes from vals[]
        //
        // COLORMAP -> RGB CONVERSION
        //    Get low 3 bytes from vals[]
        //
@@ -163,7 +154,7 @@ xpm2rgba (const char** xpm, uint32_t& w, uint32_t& h)
        }
 
        savergb = rgb = (unsigned char*) malloc (h * w * 4);
        }
 
        savergb = rgb = (unsigned char*) malloc (h * w * 4);
-       
+
        // LOAD XPM COLORMAP LONG ENOUGH TO DO CONVERSION
 
        if (strstr (xpm[1], "None")) {
        // LOAD XPM COLORMAP LONG ENOUGH TO DO CONVERSION
 
        if (strstr (xpm[1], "None")) {
@@ -178,7 +169,7 @@ xpm2rgba (const char** xpm, uint32_t& w, uint32_t& h)
                sscanf (xpm[t+1], "%c c #%lx", &c, &val);
                vals[c] = val;
        }
                sscanf (xpm[t+1], "%c c #%lx", &c, &val);
                vals[c] = val;
        }
-       
+
        // COLORMAP -> RGB CONVERSION
        //    Get low 3 bytes from vals[]
        //
        // COLORMAP -> RGB CONVERSION
        //    Get low 3 bytes from vals[]
        //
@@ -209,7 +200,7 @@ xpm2rgba (const char** xpm, uint32_t& w, uint32_t& h)
 }
 
 ArdourCanvas::Points*
 }
 
 ArdourCanvas::Points*
-get_canvas_points (string who, uint32_t npoints)
+get_canvas_points (string /*who*/, uint32_t npoints)
 {
        // cerr << who << ": wants " << npoints << " canvas points" << endl;
 #ifdef TRAP_EXCESSIVE_POINT_REQUESTS
 {
        // cerr << who << ": wants " << npoints << " canvas points" << endl;
 #ifdef TRAP_EXCESSIVE_POINT_REQUESTS
@@ -234,19 +225,19 @@ get_font_for_style (string widgetname)
        style = foobar.get_style ();
 
        Glib::RefPtr<const Pango::Layout> layout = foobar.get_layout();
        style = foobar.get_style ();
 
        Glib::RefPtr<const Pango::Layout> layout = foobar.get_layout();
-       
+
        PangoFontDescription *pfd = (PangoFontDescription *)pango_layout_get_font_description((PangoLayout *)layout->gobj());
        PangoFontDescription *pfd = (PangoFontDescription *)pango_layout_get_font_description((PangoLayout *)layout->gobj());
-       
+
        if (!pfd) {
        if (!pfd) {
-               
+
                /* layout inherited its font description from a PangoContext */
 
                PangoContext* ctxt = (PangoContext*) pango_layout_get_context ((PangoLayout*) layout->gobj());
                pfd =  pango_context_get_font_description (ctxt);
                /* layout inherited its font description from a PangoContext */
 
                PangoContext* ctxt = (PangoContext*) pango_layout_get_context ((PangoLayout*) layout->gobj());
                pfd =  pango_context_get_font_description (ctxt);
-               return Pango::FontDescription (pfd, true); /* make a copy */
-       } 
+               return Pango::FontDescription (pfd); /* make a copy */
+       }
 
 
-       return Pango::FontDescription (pfd, true); /* make a copy */
+       return Pango::FontDescription (pfd); /* make a copy */
 }
 
 uint32_t
 }
 
 uint32_t
@@ -263,44 +254,44 @@ rgba_from_style (string style, uint32_t r, uint32_t g, uint32_t b, uint32_t a, s
        }
 
        Gtk::Label foo;
        }
 
        Gtk::Label foo;
-       
+
        window->add (foo);
 
        foo.set_name (style);
        foo.ensure_style ();
        window->add (foo);
 
        foo.set_name (style);
        foo.ensure_style ();
-       
-       GtkRcStyle* waverc = foo.get_style()->gobj()->rc_style;
 
 
-       if (waverc) {
+       GtkRcStyle* rc = foo.get_style()->gobj()->rc_style;
+
+       if (rc) {
                if (attr == "fg") {
                if (attr == "fg") {
-                       r = waverc->fg[state].red / 257;
-                       g = waverc->fg[state].green / 257;
-                       b = waverc->fg[state].blue / 257;
+                       r = rc->fg[state].red / 257;
+                       g = rc->fg[state].green / 257;
+                       b = rc->fg[state].blue / 257;
+
                        /* what a hack ... "a" is for "active" */
                        if (state == Gtk::STATE_NORMAL && rgba) {
                        /* what a hack ... "a" is for "active" */
                        if (state == Gtk::STATE_NORMAL && rgba) {
-                               a = waverc->fg[GTK_STATE_ACTIVE].red / 257;
+                               a = rc->fg[GTK_STATE_ACTIVE].red / 257;
                        }
                } else if (attr == "bg") {
                        r = g = b = 0;
                        }
                } else if (attr == "bg") {
                        r = g = b = 0;
-                       r = waverc->bg[state].red / 257;
-                       g = waverc->bg[state].green / 257;
-                       b = waverc->bg[state].blue / 257;
+                       r = rc->bg[state].red / 257;
+                       g = rc->bg[state].green / 257;
+                       b = rc->bg[state].blue / 257;
                } else if (attr == "base") {
                } else if (attr == "base") {
-                       r = waverc->base[state].red / 257;
-                       g = waverc->base[state].green / 257;
-                       b = waverc->base[state].blue / 257;
+                       r = rc->base[state].red / 257;
+                       g = rc->base[state].green / 257;
+                       b = rc->base[state].blue / 257;
                } else if (attr == "text") {
                } else if (attr == "text") {
-                       r = waverc->text[state].red / 257;
-                       g = waverc->text[state].green / 257;
-                       b = waverc->text[state].blue / 257;
+                       r = rc->text[state].red / 257;
+                       g = rc->text[state].green / 257;
+                       b = rc->text[state].blue / 257;
                }
        } else {
                warning << string_compose (_("missing RGBA style for \"%1\""), style) << endl;
        }
 
        window->remove ();
                }
        } else {
                warning << string_compose (_("missing RGBA style for \"%1\""), style) << endl;
        }
 
        window->remove ();
-       
+
        if (state == Gtk::STATE_NORMAL && rgba) {
                return (uint32_t) RGBA_TO_UINT(r,g,b,a);
        } else {
        if (state == Gtk::STATE_NORMAL && rgba) {
                return (uint32_t) RGBA_TO_UINT(r,g,b,a);
        } else {
@@ -308,7 +299,7 @@ rgba_from_style (string style, uint32_t r, uint32_t g, uint32_t b, uint32_t a, s
        }
 }
 
        }
 }
 
-bool 
+bool
 canvas_item_visible (ArdourCanvas::Item* item)
 {
        return (item->gobj()->object.flags & GNOME_CANVAS_ITEM_VISIBLE) ? true : false;
 canvas_item_visible (ArdourCanvas::Item* item)
 {
        return (item->gobj()->object.flags & GNOME_CANVAS_ITEM_VISIBLE) ? true : false;
@@ -320,32 +311,55 @@ set_color (Gdk::Color& c, int rgb)
        c.set_rgb((rgb >> 16)*256, ((rgb & 0xff00) >> 8)*256, (rgb & 0xff)*256);
 }
 
        c.set_rgb((rgb >> 16)*256, ((rgb & 0xff00) >> 8)*256, (rgb & 0xff)*256);
 }
 
+bool
+relay_key_press (GdkEventKey* ev, Gtk::Window* win)
+{
+       if (!key_press_focus_accelerator_handler (*win, ev)) {
+               return PublicEditor::instance().on_key_press_event(ev);
+       } else {
+               return true;
+       }
+}
+
+bool
+forward_key_press (GdkEventKey* ev)
+{
+        return PublicEditor::instance().on_key_press_event(ev);
+}
+
 bool
 key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev)
 {
        GtkWindow* win = window.gobj();
        GtkWidget* focus = gtk_window_get_focus (win);
        bool special_handling_of_unmodified_accelerators = false;
 bool
 key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev)
 {
        GtkWindow* win = window.gobj();
        GtkWidget* focus = gtk_window_get_focus (win);
        bool special_handling_of_unmodified_accelerators = false;
-
-#undef  DEBUG_ACCELERATOR_HANDLING
-#ifdef  DEBUG_ACCELERATOR_HANDLING
-       bool debug = (getenv ("ARDOUR_DEBUG_ACCELERATOR_HANDLING") != 0);
-#endif
+       bool allow_activating = true;
+       /* consider all relevant modifiers but not LOCK or SHIFT */
+       const guint mask = (Keyboard::RelevantModifierKeyMask & ~(Gdk::SHIFT_MASK|Gdk::LOCK_MASK));
 
        if (focus) {
 
        if (focus) {
-               if (GTK_IS_ENTRY(focus)) {
+               if (GTK_IS_ENTRY(focus) || Keyboard::some_magic_widget_has_focus()) {
                        special_handling_of_unmodified_accelerators = true;
                        special_handling_of_unmodified_accelerators = true;
-               } 
-       } 
-
-#ifdef DEBUG_ACCELERATOR_HANDLING
-       if (debug) {
-               cerr << "Key event: code = " << ev->keyval << " state = " << hex << ev->state << dec << " focus is an entry ? " 
-                    << special_handling_of_unmodified_accelerators
-                    << endl;
+               }
+       }
+
+#ifdef GTKOSX
+       /* should this be universally true? */
+       if (Keyboard::some_magic_widget_has_focus ()) {
+               allow_activating = false;
        }
 #endif
 
        }
 #endif
 
+
+        DEBUG_TRACE (DEBUG::Accelerators, string_compose ("Win = %1 focus = %7 Key event: code = %2  state = %3 special handling ? %4 magic widget focus ? %5 allow_activation ? %6\n",
+                                                          win,
+                                                          ev->keyval,
+                                                          ev->state,
+                                                          special_handling_of_unmodified_accelerators,
+                                                          Keyboard::some_magic_widget_has_focus(),
+                                                          allow_activating,
+                                                         focus));
+
        /* This exists to allow us to override the way GTK handles
           key events. The normal sequence is:
 
        /* This exists to allow us to override the way GTK handles
           key events. The normal sequence is:
 
@@ -355,18 +369,18 @@ key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev)
               the focus widget and/or focus chain
 
           The problem with this is that if the accelerators include
               the focus widget and/or focus chain
 
           The problem with this is that if the accelerators include
-          keys without modifiers, such as the space bar or the 
+          keys without modifiers, such as the space bar or the
           letter "e", then pressing the key while typing into
           a text entry widget results in the accelerator being
           activated, instead of the desired letter appearing
           in the text entry.
 
           There is no good way of fixing this, but this
           letter "e", then pressing the key while typing into
           a text entry widget results in the accelerator being
           activated, instead of the desired letter appearing
           in the text entry.
 
           There is no good way of fixing this, but this
-          represents a compromise. The idea is that 
+          represents a compromise. The idea is that
           key events involving modifiers (not Shift)
           get routed into the activation pathway first, then
           get propagated to the focus widget if necessary.
           key events involving modifiers (not Shift)
           get routed into the activation pathway first, then
           get propagated to the focus widget if necessary.
-          
+
           If the key event doesn't involve modifiers,
           we deliver to the focus widget first, thus allowing
           it to get "normal text" without interference
           If the key event doesn't involve modifiers,
           we deliver to the focus widget first, thus allowing
           it to get "normal text" without interference
@@ -377,112 +391,163 @@ key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev)
           all "normal text" accelerators.
        */
 
           all "normal text" accelerators.
        */
 
-
        if (!special_handling_of_unmodified_accelerators) {
 
        if (!special_handling_of_unmodified_accelerators) {
 
+               /* XXX note that for a brief moment, the conditional above
+                * included "|| (ev->state & mask)" so as to enforce the
+                * implication of special_handling_of_UNMODIFIED_accelerators.
+                * however, this forces any key that GTK doesn't allow and that
+                * we have an alternative (see next comment) for to be
+                * automatically sent through the accel groups activation
+                * pathway, which prevents individual widgets & canvas items
+                * from ever seeing it if is used by a key binding.
+                * 
+                * specifically, this hid Ctrl-down-arrow from MIDI region
+                * views because it is also bound to an action.
+                *
+                * until we have a robust, clean binding system, this
+                * quirk will have to remain in place.
+                */
+
                /* pretend that certain key events that GTK does not allow
                   to be used as accelerators are actually something that
                /* pretend that certain key events that GTK does not allow
                   to be used as accelerators are actually something that
-                  it does allow.
+                  it does allow. but only where there are no modifiers.
                */
 
                */
 
-               int ret = false;
+               uint32_t fakekey = ev->keyval;
 
 
-               switch (ev->keyval) {
-               case GDK_Up:
-                       ret = gtk_accel_groups_activate(G_OBJECT(win), GDK_uparrow, GdkModifierType(ev->state));
-                       break;
+               if (Gtkmm2ext::possibly_translate_keyval_to_make_legal_accelerator (fakekey)) {
+                       DEBUG_TRACE (DEBUG::Accelerators, string_compose ("\tactivate (was %1 now %2) without special hanlding of unmodified accels\n",
+                                                                         ev->keyval, fakekey));
 
 
-               case GDK_Down:
-                       ret = gtk_accel_groups_activate(G_OBJECT(win), GDK_downarrow, GdkModifierType(ev->state));
-                       break;
+                       GdkModifierType mod = GdkModifierType (ev->state);
 
 
-               case GDK_Right:
-                       ret = gtk_accel_groups_activate(G_OBJECT(win), GDK_rightarrow, GdkModifierType(ev->state));
-                       break;
+                       mod = GdkModifierType (mod & gtk_accelerator_get_default_mod_mask());
+#ifdef GTKOSX
+                       /* GTK on OS X is currently (February 2012) setting both
+                          the Meta and Mod2 bits in the event modifier state if 
+                          the Command key is down.
 
 
-               case GDK_Left:
-                       ret = gtk_accel_groups_activate(G_OBJECT(win), GDK_leftarrow, GdkModifierType(ev->state));
-                       break;
+                          gtk_accel_groups_activate() does not invoke any of the logic
+                          that gtk_window_activate_key() will that sorts out that stupid
+                          state of affairs, and as a result it fails to find a match
+                          for the key event and the current set of accelerators.
 
 
-               default:
-                       break;
-               }
+                          to fix this, if the meta bit is set, remove the mod2 bit
+                          from the modifier. this assumes that our bindings use Primary
+                          which will have set the meta bit in the accelerator entry.
+                       */
+                       if (mod & GDK_META_MASK) {
+                               mod = GdkModifierType (mod & ~GDK_MOD2_MASK);
+                       }
+#endif
 
 
-               if (ret) {
-                       return true;
+                       if (allow_activating && gtk_accel_groups_activate(G_OBJECT(win), fakekey, mod)) {
+                               DEBUG_TRACE (DEBUG::Accelerators, "\taccel group activated by fakekey\n");
+                               return true;
+                       }
                }
        }
                }
        }
-               
-       if (!special_handling_of_unmodified_accelerators ||
-           ev->state & (Gdk::MOD1_MASK|
-                        Gdk::MOD3_MASK|
-                        Gdk::MOD4_MASK|
-                        Gdk::MOD5_MASK|
-                        Gdk::CONTROL_MASK)) {
-
-               /* no special handling or modifiers in effect: accelerate first */
-
-#ifdef DEBUG_ACCELERATOR_HANDLING
-               if (debug) {
-                       cerr << "\tactivate, then propagate\n";
-               }
-#endif
-               if (!gtk_window_activate_key (win, ev)) {
-                       return gtk_window_propagate_key_event (win, ev);
+
+       if (!special_handling_of_unmodified_accelerators || (ev->state & mask)) {
+
+               /* no special handling or there are modifiers in effect: accelerate first */
+
+                DEBUG_TRACE (DEBUG::Accelerators, "\tactivate, then propagate\n");
+
+               if (allow_activating) {
+                       if (gtk_window_activate_key (win, ev)) {
+                               return true;
+                       }
                } else {
                } else {
-#ifdef DEBUG_ACCELERATOR_HANDLING
-               if (debug) {
-                       cerr << "\tnot handled\n";
+                       DEBUG_TRACE (DEBUG::Accelerators, "\tactivation skipped\n");
                }
                }
-#endif
-                       return true;
-               } 
+
+                DEBUG_TRACE (DEBUG::Accelerators, "\tnot accelerated, now propagate\n");
+
+               return gtk_window_propagate_key_event (win, ev);
        }
        }
-       
+
        /* no modifiers, propagate first */
        /* no modifiers, propagate first */
-       
-#ifdef DEBUG_ACCELERATOR_HANDLING
-               if (debug) {
-                       cerr << "\tactivate, then propagate\n";
-               }
-#endif
-       if (!gtk_window_propagate_key_event (win, ev)) {
-               return gtk_window_activate_key (win, ev);
-       } 
 
 
+        DEBUG_TRACE (DEBUG::Accelerators, "\tpropagate, then activate\n");
 
 
-#ifdef DEBUG_ACCELERATOR_HANDLING
-       if (debug) {
-               cerr << "\tnot handled\n";
+       if (!gtk_window_propagate_key_event (win, ev)) {
+                DEBUG_TRACE (DEBUG::Accelerators, "\tpropagation didn't handle, so activate\n");
+               if (allow_activating) {
+                       return gtk_window_activate_key (win, ev);
+               } else {
+                       DEBUG_TRACE (DEBUG::Accelerators, "\tactivation skipped\n");
+               }
+
+       } else {
+                DEBUG_TRACE (DEBUG::Accelerators, "\thandled by propagate\n");
+               return true;
        }
        }
-#endif
+
+        DEBUG_TRACE (DEBUG::Accelerators, "\tnot handled\n");
        return true;
 }
 
        return true;
 }
 
-Glib::RefPtr<Gdk::Pixbuf>      
+Glib::RefPtr<Gdk::Pixbuf>
 get_xpm (std::string name)
 {
        if (!xpm_map[name]) {
 get_xpm (std::string name)
 {
        if (!xpm_map[name]) {
-               xpm_map[name] = Gdk::Pixbuf::create_from_file (ARDOUR::find_data_file(name, "pixmaps"));
+
+               SearchPath spath(ARDOUR::ardour_search_path());
+               spath += ARDOUR::system_data_search_path();
+
+               spath.add_subdirectory_to_paths("pixmaps");
+
+               sys::path data_file_path;
+
+               if(!find_file_in_search_path (spath, name, data_file_path)) {
+                       fatal << string_compose (_("cannot find XPM file for %1"), name) << endmsg;
+               }
+
+               try {
+                       xpm_map[name] =  Gdk::Pixbuf::create_from_file (data_file_path.to_string());
+               } catch(const Glib::Error& e)   {
+                       warning << "Caught Glib::Error: " << e.what() << endmsg;
+               }
        }
        }
-               
-       return (xpm_map[name]);
+
+       return xpm_map[name];
 }
 
 }
 
-Glib::RefPtr<Gdk::Pixbuf>      
-get_icon (const char* cname)
+std::string
+get_icon_path (const char* cname)
 {
        string name = cname;
        name += X_(".png");
 
 {
        string name = cname;
        name += X_(".png");
 
-       string path = ARDOUR::find_data_file (name, "icons");
+       SearchPath spath(ARDOUR::ardour_search_path());
+       spath += ARDOUR::system_data_search_path();
+
+       spath.add_subdirectory_to_paths("icons");
+
+       sys::path data_file_path;
 
 
-       if (path.empty()) {
+       if (!find_file_in_search_path (spath, name, data_file_path)) {
                fatal << string_compose (_("cannot find icon image for %1"), name) << endmsg;
                fatal << string_compose (_("cannot find icon image for %1"), name) << endmsg;
-               /*NOTREACHED*/
        }
 
        }
 
-       return Gdk::Pixbuf::create_from_file (path);
+       return data_file_path.to_string();
+}
+
+Glib::RefPtr<Gdk::Pixbuf>
+get_icon (const char* cname)
+{
+       Glib::RefPtr<Gdk::Pixbuf> img;
+       try {
+               img = Gdk::Pixbuf::create_from_file (get_icon_path (cname));
+       } catch (const Gdk::PixbufError &e) {
+               cerr << "Caught PixbufError: " << e.what() << endl;
+       } catch (...) {
+               g_message("Caught ... ");
+       }
+
+       return img;
 }
 
 string
 }
 
 string
@@ -494,22 +559,22 @@ longest (vector<string>& strings)
 
        vector<string>::iterator longest = strings.begin();
        string::size_type longest_length = (*longest).length();
 
        vector<string>::iterator longest = strings.begin();
        string::size_type longest_length = (*longest).length();
-       
+
        vector<string>::iterator i = longest;
        ++i;
 
        while (i != strings.end()) {
        vector<string>::iterator i = longest;
        ++i;
 
        while (i != strings.end()) {
-               
+
                string::size_type len = (*i).length();
                string::size_type len = (*i).length();
-               
+
                if (len > longest_length) {
                        longest = i;
                        longest_length = len;
                if (len > longest_length) {
                        longest = i;
                        longest_length = len;
-               } 
-               
+               }
+
                ++i;
        }
                ++i;
        }
-       
+
        return *longest;
 }
 
        return *longest;
 }
 
@@ -553,64 +618,107 @@ key_is_legal_for_numeric_entry (guint keyval)
        case GDK_Left:
        case GDK_Right:
                return true;
        case GDK_Left:
        case GDK_Right:
                return true;
-               
+
        default:
                break;
        }
 
        return false;
 }
        default:
                break;
        }
 
        return false;
 }
+void
+set_pango_fontsize ()
+{
+       long val = ARDOUR::Config->get_font_scale();
+
+       /* FT2 rendering - used by GnomeCanvas, sigh */
+
+       pango_ft2_font_map_set_resolution ((PangoFT2FontMap*) pango_ft2_font_map_for_display(), val/1024, val/1024);
 
 
+       /* Cairo rendering, in case there is any */
 
 
-ustring
-short_path (ustring path, uint32_t target_characters)
+       pango_cairo_font_map_set_resolution ((PangoCairoFontMap*) pango_cairo_font_map_get_default(), val/1024);
+}
+
+void
+reset_dpi ()
 {
 {
-       ustring::size_type last_sep;
-       ustring::size_type len = path.length();
-       const char separator = '/';
+       long val = ARDOUR::Config->get_font_scale();
+       set_pango_fontsize ();
+       /* Xft rendering */
 
 
-       if (len <= target_characters) {
-               return path;
-       }
+       gtk_settings_set_long_property (gtk_settings_get_default(),
+                                       "gtk-xft-dpi", val, "ardour");
+       DPIReset();//Emit Signal
+}
 
 
-       if ((last_sep = path.find_last_of (separator)) == ustring::npos) {
+void
+resize_window_to_proportion_of_monitor (Gtk::Window* window, int max_width, int max_height)
+{
+       Glib::RefPtr<Gdk::Screen> screen = window->get_screen ();
+       Gdk::Rectangle monitor_rect;
+       screen->get_monitor_geometry (0, monitor_rect);
 
 
-               /* just a filename, but its too long anyway */
+       int const w = std::min (int (monitor_rect.get_width() * 0.8), max_width);
+       int const h = std::min (int (monitor_rect.get_height() * 0.8), max_height);
+
+       window->resize (w, h);
+}
+
+
+/** Replace _ with __ in a string; for use with menu item text to make underscores displayed correctly */
+string
+escape_underscores (string const & s)
+{
+       string o;
+       string::size_type const N = s.length ();
 
 
-               if (target_characters > 3) {
-                       return path.substr (0, target_characters - 3) + ustring ("...");
+       for (string::size_type i = 0; i < N; ++i) {
+               if (s[i] == '_') {
+                       o += "__";
                } else {
                } else {
-                       /* stupid caller, just hand back the whole thing */
-                       return path;
+                       o += s[i];
                }
        }
 
                }
        }
 
-       if (len - last_sep >= target_characters) {
+       return o;
+}
 
 
-               /* even the filename itself is too long */
+Gdk::Color
+unique_random_color (list<Gdk::Color>& used_colors)
+{
+       Gdk::Color newcolor;
 
 
-               if (target_characters > 3) {
-                       return path.substr (last_sep+1, target_characters - 3) + ustring ("...");
-               } else {
-                       /* stupid caller, just hand back the whole thing */
-                       return path;
+       while (1) {
+
+               /* avoid neon/glowing tones by limiting them to the
+                  "inner section" (paler) of a color wheel/circle.
+               */
+
+               const int32_t max_saturation = 48000; // 65535 would open up the whole color wheel
+
+               newcolor.set_red (random() % max_saturation);
+               newcolor.set_blue (random() % max_saturation);
+               newcolor.set_green (random() % max_saturation);
+
+               if (used_colors.size() == 0) {
+                       used_colors.push_back (newcolor);
+                       return newcolor;
                }
                }
-       }
-       
-       uint32_t so_far = (len - last_sep);
-       uint32_t space_for = target_characters - so_far;
-
-       if (space_for >= 3) {
-               ustring res = "...";
-               res += path.substr (last_sep - space_for);
-               return res;
-       } else {
-               /* remove part of the end */
-               ustring res = "...";
-               res += path.substr (last_sep - space_for, len - last_sep + space_for - 3);
-               res += "...";
-               return res;
-               
+
+               for (list<Gdk::Color>::iterator i = used_colors.begin(); i != used_colors.end(); ++i) {
+                 Gdk::Color c = *i;
+                       float rdelta, bdelta, gdelta;
+
+                       rdelta = newcolor.get_red() - c.get_red();
+                       bdelta = newcolor.get_blue() - c.get_blue();
+                       gdelta = newcolor.get_green() - c.get_green();
+
+                       if (sqrt (rdelta*rdelta + bdelta*bdelta + gdelta*gdelta) > 25.0) {
+                               used_colors.push_back (newcolor);
+                               return newcolor;
+                       }
+               }
+
+               /* XXX need throttle here to make sure we don't spin for ever */
        }
 }
        }
 }
-