+
+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:
+
+ a) event is delivered to a GtkWindow
+ b) accelerators/mnemonics are activated
+ c) if (b) didn't handle the event, propagate to
+ 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
+ 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
+ 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
+ from acceleration.
+
+ Of course, this can also be problematic: if there
+ is a widget with focus, then it will swallow
+ all "normal text" accelerators.
+ */
+
+#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)) {
+#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];
+}
+
+Glib::ustring
+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
+}
+
+
+
+inline guint8
+convert_color_channel (guint8 src,
+ guint8 alpha)
+{
+ return alpha ? ((guint (src) << 8) - src) / alpha : 0;
+}
+
+void
+convert_bgra_to_rgba (guint8 const* src,
+ guint8* dst,
+ int width,
+ int height)
+{
+ guint8 const* src_pixel = src;
+ guint8* dst_pixel = dst;
+
+ for (int y = 0; y < height; y++)
+ for (int x = 0; x < width; x++)
+ {
+ dst_pixel[0] = convert_color_channel (src_pixel[2],
+ src_pixel[3]);
+ dst_pixel[1] = convert_color_channel (src_pixel[1],
+ src_pixel[3]);
+ dst_pixel[2] = convert_color_channel (src_pixel[0],
+ src_pixel[3]);
+ dst_pixel[3] = src_pixel[3];
+
+ dst_pixel += 4;
+ src_pixel += 4;
+ }
+}
+
+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);
+}
+
+Glib::RefPtr<Gdk::Pixbuf>
+pixbuf_from_ustring(const ustring& name, Pango::FontDescription* font, int clip_width, int clip_height, Gdk::Color fg)
+{
+ static Glib::RefPtr<Gdk::Pixbuf>* empty_pixbuf = 0;
+
+ if (name.empty()) {
+ if (empty_pixbuf == 0) {
+ empty_pixbuf = new Glib::RefPtr<Gdk::Pixbuf>;
+ *empty_pixbuf = Gdk::Pixbuf::create(Gdk::COLORSPACE_RGB, true, 8, clip_width, clip_height);
+ }
+ return *empty_pixbuf;
+ }
+
+ Glib::RefPtr<Gdk::Pixbuf> buf = Gdk::Pixbuf::create(Gdk::COLORSPACE_RGB, true, 8, clip_width, clip_height);
+
+ cairo_surface_t* surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, clip_width, clip_height);
+ cairo_t* cr = cairo_create (surface);
+ cairo_text_extents_t te;
+
+ cairo_set_source_rgba (cr, fg.get_red_p(), fg.get_green_p(), fg.get_blue_p(), 1.0);
+ cairo_select_font_face (cr, font->get_family().c_str(),
+ CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
+ cairo_set_font_size (cr, font->get_size() / Pango::SCALE);
+ cairo_text_extents (cr, name.c_str(), &te);
+
+ cairo_move_to (cr, 0.5, 0.5 - te.height / 2 - te.y_bearing + clip_height / 2);
+ cairo_show_text (cr, name.c_str());
+
+ convert_bgra_to_rgba(cairo_image_surface_get_data (surface), buf->get_pixels(), clip_width, clip_height);
+
+ cairo_destroy(cr);
+ cairo_surface_destroy(surface);
+
+ return buf;
+}
+
+/** 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;
+}
+
+static void
+adjustment_to_controllable (Gtk::Adjustment* adj, boost::weak_ptr<Controllable> wcont)
+{
+ boost::shared_ptr<Controllable> cont = wcont.lock();
+
+ if (cont) {
+ double val = adj->get_value();
+ if (val != cont->get_value()) {
+ cont->set_value (val);
+ }
+ }
+}
+
+static void
+controllable_to_adjustment (Gtk::Adjustment* adj, boost::weak_ptr<Controllable> wcont)
+{
+ boost::shared_ptr<Controllable> cont = wcont.lock();
+
+ if (cont) {
+ float val = cont->get_value();
+
+ if (val != adj->get_value()) {
+ adj->set_value (val);
+ }
+ }
+}
+
+void
+control_link (ScopedConnectionList& scl, boost::shared_ptr<Controllable> c, Gtk::Adjustment& a)
+{
+ boost::weak_ptr<Controllable> wc (c);
+
+ a.signal_value_changed().connect (sigc::bind (sigc::ptr_fun (adjustment_to_controllable), &a, wc));
+ c->Changed.connect (scl, MISSING_INVALIDATOR, boost::bind (controllable_to_adjustment, &a, wc),
+ gui_context());
+}
+