use volume controller widget for monitor section, drop some now-unused code
[ardour.git] / gtk2_ardour / utils.cc
index 19c280f6460be100cd75eaa88782d4d0694e30b1..6d0c0a2103ba4b17c480f49ed75f2c0512e64888 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.
     along with this program; if not, write to the Free Software
     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
-    $Id$
 */
 
+#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 <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 "pbd/file_utils.h"
+
 #include <gtkmm2ext/utils.h>
+#include "ardour/configuration.h"
+#include "ardour/rc_configuration.h"
+
+#include "ardour/filesystem_paths.h"
 
 #include "ardour_ui.h"
+#include "public_editor.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 sigc;
 using namespace Glib;
+using namespace PBD;
+using Gtkmm2ext::Keyboard;
 
-string
-short_version (string orig, string::size_type target_length)
+sigc::signal<void>  DPIReset;
+
+int
+pixel_width (const string& str, Pango::FontDescription& font)
 {
-       /* this tries to create a recognizable abbreviation
-          of "orig" by removing characters until we meet
-          a certain target length.
+       Label foo;
+       Glib::RefPtr<Pango::Layout> layout = foo.create_pango_layout ("");
 
-          note that we deliberately leave digits in the result
-          without modification.
-       */
+       layout->set_font_description (font);
+       layout->set_text (str);
 
+       int width, height;
+       Gtkmm2ext::get_ink_pixel_size (layout, width, height);
+       return width;
+}
 
-       string::size_type pos;
+string
+fit_to_pixels (const string& str, int pixel_width, Pango::FontDescription& font, int& actual_width, bool with_ellipses)
+{
+       Label foo;
+       Glib::RefPtr<Pango::Layout> layout = foo.create_pango_layout ("");
+       string::size_type shorter_by = 0;
+       string txt;
 
-       /* remove white-space and punctuation, starting at end */
+       layout->set_font_description (font);
 
-       while (orig.length() > target_length) {
-               if ((pos = orig.find_last_of (_("\"\n\t ,<.>/?:;'[{}]~`!@#$%^&*()_-+="))) == string::npos) {
-                       break;
-               }
-               orig.replace (pos, 1, "");
-       }
+       actual_width = 0;
 
-       /* remove lower-case vowels, starting at end */
+       string ustr = str;
+       string::iterator last = ustr.end();
+       --last; /* now points at final entry */
 
-       while (orig.length() > target_length) {
-               if ((pos = orig.find_last_of (_("aeiou"))) == string::npos) {
-                       break;
-               }
-               orig.replace (pos, 1, "");
-       }
+       txt = ustr;
 
-       /* remove upper-case vowels, starting at end */
+       while (!ustr.empty()) {
 
-       while (orig.length() > target_length) {
-               if ((pos = orig.find_last_of (_("AEIOU"))) == string::npos) {
-                       break;
-               }
-               orig.replace (pos, 1, "");
-       }
+               layout->set_text (txt);
 
-       /* remove lower-case consonants, starting at end */
+               int width, height;
+               Gtkmm2ext::get_ink_pixel_size (layout, width, height);
 
-       while (orig.length() > target_length) {
-               if ((pos = orig.find_last_of (_("bcdfghjklmnpqrtvwxyz"))) == string::npos) {
+               if (width < pixel_width) {
+                       actual_width = width;
                        break;
                }
-               orig.replace (pos, 1, "");
-       }
 
-       /* remove upper-case consonants, starting at end */
+               ustr.erase (last--);
+               shorter_by++;
 
-       while (orig.length() > target_length) {
-               if ((pos = orig.find_last_of (_("BCDFGHJKLMNPQRTVWXYZ"))) == string::npos) {
-                       break;
+               if (with_ellipses && shorter_by > 3) {
+                       txt = ustr;
+                       txt += "...";
+               } else {
+                       txt = ustr;
                }
-               orig.replace (pos, 1, "");
        }
 
-       /* whatever the length is now, use it */
-       
-       return orig;
+       return txt;
 }
 
-string
-fit_to_pixels (const string & str, int pixel_width, const string & font)
+/** Try to fit a string into a given horizontal space by ellipsizing it.
+ *  @param cr Cairo context in which the text will be plotted.
+ *  @param name Text.
+ *  @param avail Available horizontal space.
+ *  @return (Text, possibly ellipsized) and (horizontal size of text)
+ */
+
+std::pair<std::string, double>
+fit_to_pixels (cairo_t* cr, std::string name, double avail)
 {
-       Label foo;
-       int width;
-       int height;
-       Pango::FontDescription fontdesc (font);
-       
-       int namelen = str.length();
-       char cstr[namelen+1];
-       strcpy (cstr, str.c_str());
-       
-       while (namelen) {
-               Glib::RefPtr<Pango::Layout> layout = foo.create_pango_layout (cstr);
-
-               layout->set_font_description (fontdesc);
-               layout->get_pixel_size (width, height);
-
-               if (width < (pixel_width)) {
+       /* XXX hopefully there exists a more efficient way of doing this */
+
+       bool abbreviated = false;
+       uint32_t width = 0;
+
+       while (1) {
+               cairo_text_extents_t ext;
+               cairo_text_extents (cr, name.c_str(), &ext);
+
+               if (ext.width < avail || name.length() <= 4) {
+                       width = ext.width;
                        break;
                }
 
-               --namelen;
-               cstr[namelen] = '\0';
-
+               if (abbreviated) {
+                       name = name.substr (0, name.length() - 4) + "...";
+               } else {
+                       name = name.substr (0, name.length() - 3) + "...";
+                       abbreviated = true;
+               }
        }
 
-       return cstr;
+       return std::make_pair (name, width);
 }
 
-int
-atoi (const string& s)
-{
-       return atoi (s.c_str());
-}
 
-double
-atof (const string& s)
-{
-       return atof (s.c_str());
-}
-
-vector<string>
-internationalize (const char **array)
+/** 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)
 {
-       vector<string> v;
-
-       for (uint32_t i = 0; array[i]; ++i) {
-               v.push_back (_(array[i]));
+       m.push_back (e);
+       if (!s) {
+               m.back().set_sensitive (false);
        }
-
-       return v;
 }
 
+
 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:
@@ -181,23 +192,23 @@ xpm2rgb (const char** xpm, uint32_t& w, uint32_t& h)
        uint32_t t, x, y, colors, cpp;
        unsigned char c;
        unsigned char *savergb, *rgb;
-       
+
        // 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;
                return 0;
        }
 
-       savergb = rgb = (unsigned char*)art_alloc (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;
        }
-       
+
        // COLORMAP -> RGB CONVERSION
        //    Get low 3 bytes from vals[]
        //
@@ -233,8 +244,8 @@ xpm2rgba (const char** xpm, uint32_t& w, uint32_t& h)
                return 0;
        }
 
-       savergb = rgb = (unsigned char*)art_alloc (h * w * 4);
-       
+       savergb = rgb = (unsigned char*) malloc (h * w * 4);
+
        // LOAD XPM COLORMAP LONG ENOUGH TO DO CONVERSION
 
        if (strstr (xpm[1], "None")) {
@@ -249,7 +260,7 @@ xpm2rgba (const char** xpm, uint32_t& w, uint32_t& h)
                sscanf (xpm[t+1], "%c c #%lx", &c, &val);
                vals[c] = val;
        }
-       
+
        // COLORMAP -> RGB CONVERSION
        //    Get low 3 bytes from vals[]
        //
@@ -280,7 +291,7 @@ xpm2rgba (const char** xpm, uint32_t& w, uint32_t& h)
 }
 
 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
@@ -291,152 +302,35 @@ get_canvas_points (string who, uint32_t npoints)
        return new ArdourCanvas::Points (npoints);
 }
 
-static int32_t 
-int_from_hex (char hic, char loc) 
-{
-       int hi;         /* hi byte */
-       int lo;         /* low byte */
-
-       hi = (int) hic;
-
-       if( ('0'<=hi) && (hi<='9') ) {
-               hi -= '0';
-       } else if( ('a'<= hi) && (hi<= 'f') ) {
-               hi -= ('a'-10);
-       } else if( ('A'<=hi) && (hi<='F') ) {
-               hi -= ('A'-10);
-       }
-       
-       lo = (int) loc;
-       
-       if( ('0'<=lo) && (lo<='9') ) {
-               lo -= '0';
-       } else if( ('a'<=lo) && (lo<='f') ) {
-               lo -= ('a'-10);
-       } else if( ('A'<=lo) && (lo<='F') ) {
-               lo -= ('A'-10);
-       }
-
-       return lo + (16 * hi);
-}
-
-void
-url_decode (string& url)
-{
-       string::iterator last;
-       string::iterator next;
-
-       for (string::iterator i = url.begin(); i != url.end(); ++i) {
-               if ((*i) == '+') {
-                       *i = ' ';
-               }
-       }
-
-       if (url.length() <= 3) {
-               return;
-       }
-
-       last = url.end();
-
-       --last; /* points at last char */
-       --last; /* points at last char - 1 */
-
-       for (string::iterator i = url.begin(); i != last; ) {
-
-               if (*i == '%') {
-
-                       next = i;
-
-                       url.erase (i);
-                       
-                       i = next;
-                       ++next;
-                       
-                       if (isxdigit (*i) && isxdigit (*next)) {
-                               /* replace first digit with char */
-                               *i = int_from_hex (*i,*next);
-                               ++i; /* points at 2nd of 2 digits */
-                               url.erase (i);
-                       }
-               } else {
-                       ++i;
-               }
-       }
-}
-
-Pango::FontDescription
+Pango::FontDescription*
 get_font_for_style (string widgetname)
 {
        Gtk::Window window (WINDOW_TOPLEVEL);
        Gtk::Label foobar;
-       Glib::RefPtr<Style> style;
+       Glib::RefPtr<Gtk::Style> style;
 
        window.add (foobar);
        foobar.set_name (widgetname);
        foobar.ensure_style();
 
        style = foobar.get_style ();
-       return style->get_font();
-}
-
-gint
-pane_handler (GdkEventButton* ev, Gtk::Paned* pane)
-{
-       if (ev->window != Gtkmm2ext::get_paned_handle (*pane)) {
-               return FALSE;
-       }
-
-       if (Keyboard::is_delete_event (ev)) {
-
-               gint pos;
-               gint cmp;
-               
-               pos = pane->get_position ();
-
-               if (dynamic_cast<VPaned*>(pane)) {
-                       cmp = pane->get_height();
-               } else {
-                       cmp = pane->get_width();
-               }
-
-               /* we have to use approximations here because we can't predict the
-                  exact position or sizes of the pane (themes, etc)
-               */
-
-               if (pos < 10 || abs (pos - cmp) < 10) {
-
-                       /* already collapsed: restore it (note that this is cast from a pointer value to int, which is tricky on 64bit */
-                       
-                       pane->set_position ((intptr_t) pane->get_data ("rpos"));
 
-               } else {        
+       Glib::RefPtr<const Pango::Layout> layout = foobar.get_layout();
 
-                       int collapse_direction;
+       PangoFontDescription *pfd = (PangoFontDescription *)pango_layout_get_font_description((PangoLayout *)layout->gobj());
 
-                       /* store the current position */
+       if (!pfd) {
 
-                       pane->set_data ("rpos", (gpointer) pos);
+               /* layout inherited its font description from a PangoContext */
 
-                       /* collapse to show the relevant child in full */
-                       
-                       collapse_direction = (intptr_t) pane->get_data ("collapse-direction");
-
-                       if (collapse_direction) {
-                               pane->set_position (1);
-                       } else {
-                               if (dynamic_cast<VPaned*>(pane)) {
-                                       pane->set_position (pane->get_height());
-                               } else {
-                                       pane->set_position (pane->get_width());
-                               }
-                       }
-               }
-
-               return TRUE;
-       } 
+               PangoContext* ctxt = (PangoContext*) pango_layout_get_context ((PangoLayout*) layout->gobj());
+               pfd =  pango_context_get_font_description (ctxt);
+               return new Pango::FontDescription (pfd, true); /* make a copy */
+       }
 
-       return FALSE;
+       return new Pango::FontDescription (pfd, true); /* make a copy */
 }
+
 uint32_t
 rgba_from_style (string style, uint32_t r, uint32_t g, uint32_t b, uint32_t a, string attr, int state, bool rgba)
 {
@@ -451,43 +345,44 @@ rgba_from_style (string style, uint32_t r, uint32_t g, uint32_t b, uint32_t a, s
        }
 
        Gtk::Label foo;
-       
+
        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") {
-                       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) {
-                               a = waverc->fg[GTK_STATE_ACTIVE].red / 257;
+                               a = rc->fg[GTK_STATE_ACTIVE].red / 257;
                        }
                } 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") {
-                       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") {
-                       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 ();
-       
+
        if (state == Gtk::STATE_NORMAL && rgba) {
                return (uint32_t) RGBA_TO_UINT(r,g,b,a);
        } else {
@@ -495,7 +390,113 @@ rgba_from_style (string style, uint32_t r, uint32_t g, uint32_t b, uint32_t a, s
        }
 }
 
-bool 
+
+Gdk::Color
+color_from_style (string widget_style_name, int state, string attr)
+{
+       GtkStyle* style;
+
+       style = gtk_rc_get_style_by_paths (gtk_settings_get_default(),
+                                          widget_style_name.c_str(),
+                                          0, G_TYPE_NONE);
+
+       if (!style) {
+               error << string_compose (_("no style found for %1, using red"), style) << endmsg;
+               return Gdk::Color ("red");
+       }
+
+       if (attr == "fg") {
+               return Gdk::Color (&style->fg[state]);
+       }
+
+       if (attr == "bg") {
+               return Gdk::Color (&style->bg[state]);
+       }
+
+       if (attr == "light") {
+               return Gdk::Color (&style->light[state]);
+       }
+
+       if (attr == "dark") {
+               return Gdk::Color (&style->dark[state]);
+       }
+
+       if (attr == "mid") {
+               return Gdk::Color (&style->mid[state]);
+       }
+
+       if (attr == "text") {
+               return Gdk::Color (&style->text[state]);
+       }
+
+       if (attr == "base") {
+               return Gdk::Color (&style->base[state]);
+       }
+
+       if (attr == "text_aa") {
+               return Gdk::Color (&style->text_aa[state]);
+       }
+
+       error << string_compose (_("unknown style attribute %1 requested for color; using \"red\""), attr) << endmsg;
+       return Gdk::Color ("red");
+}
+
+Glib::RefPtr<Gdk::GC>
+gc_from_style (string widget_style_name, int state, string attr)
+{
+        GtkStyle* style;
+
+        style = gtk_rc_get_style_by_paths (gtk_settings_get_default(),
+                                           widget_style_name.c_str(),
+                                           0, G_TYPE_NONE);
+
+        if (!style) {
+                error << string_compose (_("no style found for %1, using red"), style) << endmsg;
+               Glib::RefPtr<Gdk::GC> ret = Gdk::GC::create();
+               ret->set_rgb_fg_color(Gdk::Color("red"));
+                return ret;
+        }
+
+        if (attr == "fg") {
+                return Glib::wrap(style->fg_gc[state]);
+        }
+
+        if (attr == "bg") {
+                return Glib::wrap(style->bg_gc[state]);
+        }
+
+        if (attr == "light") {
+                return Glib::wrap(style->light_gc[state]);
+        }
+
+        if (attr == "dark") {
+                return Glib::wrap(style->dark_gc[state]);
+        }
+
+        if (attr == "mid") {
+                return Glib::wrap(style->mid_gc[state]);
+        }
+
+        if (attr == "text") {
+                return Glib::wrap(style->text_gc[state]);
+        }
+
+        if (attr == "base") {
+                return Glib::wrap(style->base_gc[state]);
+        }
+
+        if (attr == "text_aa") {
+                return Glib::wrap(style->text_aa_gc[state]);
+        }
+
+        error << string_compose (_("unknown style attribute %1 requested for color; using \"red\""), attr) << endmsg;
+       Glib::RefPtr<Gdk::GC> ret = Gdk::GC::create();
+       ret->set_rgb_fg_color(Gdk::Color("red"));
+        return ret;
+}
+
+
+bool
 canvas_item_visible (ArdourCanvas::Item* item)
 {
        return (item->gobj()->object.flags & GNOME_CANVAS_ITEM_VISIBLE) ? true : false;
@@ -507,10 +508,126 @@ set_color (Gdk::Color& c, int rgb)
        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);
+}
+
+#ifdef GTKOSX
+static guint
+osx_keyval_without_alt (guint accent_keyval)
+{
+       switch (accent_keyval) {
+       case GDK_oe:
+               return GDK_q;
+       case GDK_registered:
+               return GDK_r;
+       case GDK_dagger:
+               return GDK_t;
+       case GDK_yen:
+               return GDK_y;
+       case GDK_diaeresis:
+               return GDK_u;
+       case GDK_oslash:
+               return GDK_o;
+       case GDK_Greek_pi:
+               return GDK_p;
+       case GDK_leftdoublequotemark:
+               return GDK_bracketleft;
+       case GDK_leftsinglequotemark:
+               return GDK_bracketright;
+       case GDK_guillemotleft:
+               return GDK_backslash;
+       case GDK_aring:
+               return GDK_a;
+       case GDK_ssharp:
+               return GDK_s;
+       case GDK_partialderivative:
+               return GDK_d;
+       case GDK_function:
+               return GDK_f;
+       case GDK_copyright:
+               return GDK_g;
+       case GDK_abovedot:
+               return GDK_h;
+       case GDK_notsign:
+               return GDK_l;
+       case GDK_ellipsis:
+               return GDK_semicolon;
+       case GDK_ae:
+               return GDK_apostrophe;
+       case GDK_Greek_OMEGA:
+               return GDK_z;
+       case GDK_ccedilla:
+               return GDK_c;
+       case GDK_radical:
+               return GDK_v;
+       case GDK_integral:
+               return GDK_b;
+       case GDK_mu:
+               return GDK_m;
+       case GDK_lessthanequal:
+               return GDK_comma;
+       case GDK_greaterthanequal:
+               return GDK_period;
+       case GDK_division:
+               return GDK_slash;
+       default:
+               break;
+       }
+
+       return GDK_VoidSymbol;
+}
+#endif
+
 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 allow_activating = true;
+
+#undef DEBUG_ACCELERATOR_HANDLING
+#ifdef  DEBUG_ACCELERATOR_HANDLING
+       //bool debug = (getenv ("ARDOUR_DEBUG_ACCELERATOR_HANDLING") != 0);
+       bool debug=true;
+#endif
+       if (focus) {
+               if (GTK_IS_ENTRY(focus) || Keyboard::some_magic_widget_has_focus()) {
+                       special_handling_of_unmodified_accelerators = true;
+               }
+       }
+
+#ifdef GTKOSX
+       /* should this be universally true? */
+       if (Keyboard::some_magic_widget_has_focus ()) {
+               allow_activating = false;
+       }
+#endif
+
+#ifdef DEBUG_ACCELERATOR_HANDLING
+       if (debug) {
+               cerr << "Win = " << win << " Key event: code = " << ev->keyval << " state = " << hex << ev->state << dec << " special handling ? "
+                    << special_handling_of_unmodified_accelerators
+                    << " magic widget focus ? "
+                    << Keyboard::some_magic_widget_has_focus()
+                    << " allow_activation ? "
+                    << allow_activating
+                    << endl;
+       }
+#endif
 
        /* This exists to allow us to override the way GTK handles
           key events. The normal sequence is:
@@ -521,18 +638,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
-          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
-          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.
-          
+
           If the key event doesn't involve modifiers,
           we deliver to the focus widget first, thus allowing
           it to get "normal text" without interference
@@ -543,23 +660,296 @@ key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev)
           all "normal text" accelerators.
        */
 
-       if (ev->state & ~Gdk::SHIFT_MASK) {
-               
-               /* modifiers in effect, accelerate first */
-               if (!gtk_window_activate_key (win, ev)) {
-                       return gtk_window_propagate_key_event (win, ev);
-               } else {
-                       return true;
-               } 
+#ifdef GTKOSX
+       if (!special_handling_of_unmodified_accelerators) {
+               if (ev->state & GDK_MOD1_MASK) {
+                       /* we're not in a text entry or "magic focus" widget so we don't want OS X "special-character" 
+                          text-style handling of alt-<key>. change the keyval back to what it would be without
+                          the alt key. this way, we see <alt>-v rather than <alt>-radical and so on.
+                       */
+                       guint keyval_without_alt = osx_keyval_without_alt (ev->keyval);
+
+                       if (keyval_without_alt != GDK_VoidSymbol) {
+#ifdef DEBUG_ACCELERATOR_HANDLING
+                               cerr << "Remapped " << gdk_keyval_name (ev->keyval) << " to " << gdk_keyval_name (keyval_without_alt) << endl;
+
+#endif                         ev->keyval = keyval_without_alt;
+                       }
+               }
        }
-       
+#endif
+
+       if (!special_handling_of_unmodified_accelerators) {
+
+               /* pretend that certain key events that GTK does not allow
+                  to be used as accelerators are actually something that
+                  it does allow.
+               */
+
+               uint32_t fakekey = ev->keyval;
+
+               if (Gtkmm2ext::possibly_translate_keyval_to_make_legal_accelerator (fakekey)) {
+                       if (allow_activating && gtk_accel_groups_activate(G_OBJECT(win), fakekey, GdkModifierType(ev->state))) {
+                               return true;
+                       }
+               }
+       }
+
+       /* consider all relevant modifiers but not LOCK or SHIFT */
+
+       guint mask = (Keyboard::RelevantModifierKeyMask & ~(Gdk::SHIFT_MASK|Gdk::LOCK_MASK));
+
+       if (!special_handling_of_unmodified_accelerators || (ev->state & mask)) {
+
+               /* no special handling or there are modifiers in effect: accelerate first */
+
+#ifdef DEBUG_ACCELERATOR_HANDLING
+               if (debug) {
+                       cerr << "\tactivate, then propagate\n";
+               }
+#endif
+
+               if (allow_activating) {
+                       if (gtk_window_activate_key (win, ev)) {
+                               return true;
+                       }
+               }
+
+#ifdef DEBUG_ACCELERATOR_HANDLING
+               if (debug) {
+                       cerr << "\tnot accelerated, now propagate\n";
+               }
+#endif
+               return gtk_window_propagate_key_event (win, ev);
+       }
+
        /* no modifiers, propagate first */
 
+#ifdef DEBUG_ACCELERATOR_HANDLING
+       if (debug) {
+               cerr << "\tpropagate, then activate\n";
+       }
+#endif
        if (!gtk_window_propagate_key_event (win, ev)) {
-               return gtk_window_activate_key (win, ev);
-       } 
+#ifdef DEBUG_ACCELERATOR_HANDLING
+               if (debug) {
+                       cerr << "\tpropagation didn't handle, so activate\n";
+               }
+#endif
+
+               if (allow_activating) {
+                       return gtk_window_activate_key (win, ev);
+               }
 
+       } else {
+#ifdef DEBUG_ACCELERATOR_HANDLING
+               if (debug) {
+                       cerr << "\thandled by propagate\n";
+               }
+#endif
+               return true;
+       }
 
+#ifdef DEBUG_ACCELERATOR_HANDLING
+       if (debug) {
+               cerr << "\tnot handled\n";
+       }
+#endif
        return true;
 }
 
+Glib::RefPtr<Gdk::Pixbuf>
+get_xpm (std::string name)
+{
+       if (!xpm_map[name]) {
+
+               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];
+}
+
+std::string
+get_icon_path (const char* cname)
+{
+       string name = cname;
+       name += X_(".png");
+
+       SearchPath spath(ARDOUR::ardour_search_path());
+       spath += ARDOUR::system_data_search_path();
+
+       spath.add_subdirectory_to_paths("icons");
+
+       sys::path data_file_path;
+
+       if (!find_file_in_search_path (spath, name, data_file_path)) {
+               fatal << string_compose (_("cannot find icon image for %1"), name) << endmsg;
+       }
+
+       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
+longest (vector<string>& strings)
+{
+       if (strings.empty()) {
+               return string ("");
+       }
+
+       vector<string>::iterator longest = strings.begin();
+       string::size_type longest_length = (*longest).length();
+
+       vector<string>::iterator i = longest;
+       ++i;
+
+       while (i != strings.end()) {
+
+               string::size_type len = (*i).length();
+
+               if (len > longest_length) {
+                       longest = i;
+                       longest_length = len;
+               }
+
+               ++i;
+       }
+
+       return *longest;
+}
+
+bool
+key_is_legal_for_numeric_entry (guint keyval)
+{
+       switch (keyval) {
+       case GDK_minus:
+       case GDK_plus:
+       case GDK_period:
+       case GDK_comma:
+       case GDK_0:
+       case GDK_1:
+       case GDK_2:
+       case GDK_3:
+       case GDK_4:
+       case GDK_5:
+       case GDK_6:
+       case GDK_7:
+       case GDK_8:
+       case GDK_9:
+       case GDK_KP_Add:
+       case GDK_KP_Subtract:
+       case GDK_KP_Decimal:
+       case GDK_KP_0:
+       case GDK_KP_1:
+       case GDK_KP_2:
+       case GDK_KP_3:
+       case GDK_KP_4:
+       case GDK_KP_5:
+       case GDK_KP_6:
+       case GDK_KP_7:
+       case GDK_KP_8:
+       case GDK_KP_9:
+       case GDK_Return:
+       case GDK_BackSpace:
+       case GDK_Delete:
+       case GDK_KP_Enter:
+       case GDK_Home:
+       case GDK_End:
+       case GDK_Left:
+       case GDK_Right:
+               return true;
+
+       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 */
+
+       pango_cairo_font_map_set_resolution ((PangoCairoFontMap*) pango_cairo_font_map_get_default(), val/1024);
+}
+
+void
+reset_dpi ()
+{
+       long val = ARDOUR::Config->get_font_scale();
+       set_pango_fontsize ();
+       /* Xft rendering */
+
+       gtk_settings_set_long_property (gtk_settings_get_default(),
+                                       "gtk-xft-dpi", val, "ardour");
+       DPIReset();//Emit Signal
+}
+
+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);
+
+       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 ();
+
+       for (string::size_type i = 0; i < N; ++i) {
+               if (s[i] == '_') {
+                       o += "__";
+               } else {
+                       o += s[i];
+               }
+       }
+
+       return o;
+}
+
+