X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=gtk2_ardour%2Futils.cc;h=aa57a3b773cb87a60f3ab79fd6a9542958477e93;hb=e69aca28426dd17a0f82ea01c7c98e217b4fdcc3;hp=396940284b3c273c04a71c592bda634ed328e6bc;hpb=07bf584673579d47dc6e80f86d6e53d354acda5b;p=ardour.git diff --git a/gtk2_ardour/utils.cc b/gtk2_ardour/utils.cc index 396940284b..aa57a3b773 100644 --- a/gtk2_ardour/utils.cc +++ b/gtk2_ardour/utils.cc @@ -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. @@ -17,32 +17,59 @@ */ +#include // for fontmap resolution control for GnomeCanvas +#include // for fontmap resolution control for GnomeCanvas + #include #include #include #include #include +#include #include #include #include #include #include +#include "pbd/file_utils.h" + #include -#include +#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; + +sigc::signal DPIReset; + +int +pixel_width (const ustring& str, Pango::FontDescription& font) +{ + Label foo; + Glib::RefPtr layout = foo.create_pango_layout (""); + + layout->set_font_description (font); + layout->set_text (str); + + int width, height; + Gtkmm2ext::get_ink_pixel_size (layout, width, height); + return width; +} ustring fit_to_pixels (const ustring& str, int pixel_width, Pango::FontDescription& font, int& actual_width, bool with_ellipses) @@ -73,7 +100,7 @@ fit_to_pixels (const ustring& str, int pixel_width, Pango::FontDescription& font actual_width = width; break; } - + ustr.erase (last--); shorter_by++; @@ -88,11 +115,62 @@ fit_to_pixels (const ustring& str, int pixel_width, Pango::FontDescription& font return txt; } +/** 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 +fit_to_pixels (cairo_t* cr, std::string name, double avail) +{ + /* 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; + } + + if (abbreviated) { + name = name.substr (0, name.length() - 4) + "..."; + } else { + name = name.substr (0, name.length() - 3) + "..."; + abbreviated = true; + } + } + + return std::make_pair (name, width); +} + + +/** 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); + } +} + + 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: @@ -110,9 +188,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; - + // 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; @@ -120,13 +198,13 @@ xpm2rgb (const char** xpm, uint32_t& w, uint32_t& h) } 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[] // @@ -163,7 +241,7 @@ xpm2rgba (const char** xpm, uint32_t& w, uint32_t& h) } savergb = rgb = (unsigned char*) malloc (h * w * 4); - + // LOAD XPM COLORMAP LONG ENOUGH TO DO CONVERSION if (strstr (xpm[1], "None")) { @@ -178,7 +256,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[] // @@ -209,7 +287,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 @@ -220,7 +298,7 @@ get_canvas_points (string who, uint32_t npoints) return new ArdourCanvas::Points (npoints); } -Pango::FontDescription +Pango::FontDescription* get_font_for_style (string widgetname) { Gtk::Window window (WINDOW_TOPLEVEL); @@ -232,7 +310,21 @@ get_font_for_style (string widgetname) foobar.ensure_style(); style = foobar.get_style (); - return style->get_font(); + + Glib::RefPtr layout = foobar.get_layout(); + + PangoFontDescription *pfd = (PangoFontDescription *)pango_layout_get_font_description((PangoLayout *)layout->gobj()); + + 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); + return new Pango::FontDescription (pfd, true); /* make a copy */ + } + + return new Pango::FontDescription (pfd, true); /* make a copy */ } uint32_t @@ -249,12 +341,12 @@ 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) { @@ -262,7 +354,7 @@ rgba_from_style (string style, uint32_t r, uint32_t g, uint32_t b, uint32_t a, s r = waverc->fg[state].red / 257; g = waverc->fg[state].green / 257; b = waverc->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; @@ -286,7 +378,7 @@ rgba_from_style (string style, uint32_t r, uint32_t g, uint32_t b, uint32_t a, s } window->remove (); - + if (state == Gtk::STATE_NORMAL && rgba) { return (uint32_t) RGBA_TO_UINT(r,g,b,a); } else { @@ -294,7 +386,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 +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 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 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; @@ -306,28 +504,62 @@ set_color (Gdk::Color& c, int rgb) c.set_rgb((rgb >> 16)*256, ((rgb & 0xff00) >> 8)*256, (rgb & 0xff)*256); } +#ifdef GTKOSX +extern "C" { + gboolean gdk_quartz_possibly_forward (GdkEvent*); +} +#endif + +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 allow_activating = true; -#undef DEBUG_ACCELERATOR_HANDLING +#undef DEBUG_ACCELERATOR_HANDLING #ifdef DEBUG_ACCELERATOR_HANDLING - bool debug = (getenv ("ARDOUR_DEBUG_ACCELERATOR_HANDLING") != 0); + //bool debug = (getenv ("ARDOUR_DEBUG_ACCELERATOR_HANDLING") != 0); + bool debug=true; #endif - if (focus) { - if (GTK_IS_ENTRY(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 << "Key event: code = " << ev->keyval << " state = " << hex << ev->state << dec << " focus is an entry ? " + 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 @@ -341,18 +573,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 @@ -371,71 +603,91 @@ key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev) it does allow. */ - 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; - - case GDK_Down: - ret = gtk_accel_groups_activate(G_OBJECT(win), GDK_downarrow, GdkModifierType(ev->state)); - break; + if (possibly_translate_keyval_to_make_legal_accelerator (fakekey)) { + if (allow_activating && gtk_accel_groups_activate(G_OBJECT(win), fakekey, GdkModifierType(ev->state))) { + return true; + } - case GDK_Right: - ret = gtk_accel_groups_activate(G_OBJECT(win), GDK_rightarrow, GdkModifierType(ev->state)); - break; +#ifdef GTKOSX + if (allow_activating) { + int oldval = ev->keyval; + ev->keyval = fakekey; + if (gdk_quartz_possibly_forward ((GdkEvent*) ev)) { + return true; + } + ev->keyval = oldval; + } +#endif + } + } - case GDK_Left: - ret = gtk_accel_groups_activate(G_OBJECT(win), GDK_leftarrow, GdkModifierType(ev->state)); - break; + /* consider all relevant modifiers but not LOCK or SHIFT */ - default: - break; - } + guint mask = (Keyboard::RelevantModifierKeyMask & ~(Gdk::SHIFT_MASK|Gdk::LOCK_MASK)); - if (ret) { - 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)) { + if (!special_handling_of_unmodified_accelerators || (ev->state & mask)) { - /* no special handling or modifiers in effect: accelerate first */ + /* no special handling or there are 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); - } else { + + if (allow_activating) { +#ifdef GTKOSX + if (gdk_quartz_possibly_forward ((GdkEvent*) ev)) { + return true; + } +#endif + if (gtk_window_activate_key (win, ev)) { + return true; + } + } + #ifdef DEBUG_ACCELERATOR_HANDLING if (debug) { - cerr << "\tnot handled\n"; + cerr << "\tnot accelerated, now propagate\n"; } #endif - return true; - } + 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 << "\tactivate, then propagate\n"; + cerr << "\tpropagation didn't handle, so activate\n"; } #endif - if (!gtk_window_propagate_key_event (win, ev)) { - return gtk_window_activate_key (win, ev); - } + if (allow_activating) { + +#ifdef GTKOSX + if (gdk_quartz_possibly_forward ((GdkEvent*) ev)) { + return true; + } +#endif + 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) { @@ -445,30 +697,65 @@ key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev) return true; } -Glib::RefPtr +Glib::RefPtr 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 -get_icon (const char* cname) +Glib::ustring +get_icon_path (const char* cname) { 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"); - if (path.empty()) { + 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; - /*NOTREACHED*/ } - return Gdk::Pixbuf::create_from_file (path); + return data_file_path.to_string(); +} + +Glib::RefPtr +get_icon (const char* cname) +{ + Glib::RefPtr 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 @@ -480,22 +767,22 @@ longest (vector& strings) vector::iterator longest = strings.begin(); string::size_type longest_length = (*longest).length(); - + vector::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; } @@ -539,64 +826,256 @@ key_is_legal_for_numeric_entry (guint keyval) 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 */ -ustring -short_path (ustring path, uint32_t target_characters) + 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 () { - 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 */ + + gtk_settings_set_long_property (gtk_settings_get_default(), + "gtk-xft-dpi", val, "ardour"); + DPIReset();//Emit Signal +} + +bool +possibly_translate_keyval_to_make_legal_accelerator (uint32_t& keyval) +{ + int fakekey = GDK_VoidSymbol; + + switch (keyval) { + case GDK_Tab: + case GDK_ISO_Left_Tab: + fakekey = GDK_nabla; + break; + + case GDK_Up: + fakekey = GDK_uparrow; + break; + + case GDK_Down: + fakekey = GDK_downarrow; + break; + + case GDK_Right: + fakekey = GDK_rightarrow; + break; + + case GDK_Left: + fakekey = GDK_leftarrow; + break; + + case GDK_Return: + fakekey = GDK_3270_Enter; + break; - if (len <= target_characters) { - return path; + case GDK_KP_Enter: + fakekey = GDK_F35; + break; + + default: + break; } - if ((last_sep = path.find_last_of (separator)) == ustring::npos) { + if (fakekey != GDK_VoidSymbol) { + keyval = fakekey; + return true; + } + + return false; +} + +uint32_t +possibly_translate_legal_accelerator_to_real_key (uint32_t keyval) +{ + switch (keyval) { + case GDK_nabla: + return GDK_Tab; + break; - /* just a filename, but its too long anyway */ + case GDK_uparrow: + return GDK_Up; + break; - if (target_characters > 3) { - return path.substr (0, target_characters - 3) + ustring ("..."); - } else { - /* stupid caller, just hand back the whole thing */ - return path; - } + case GDK_downarrow: + return GDK_Down; + break; + + case GDK_rightarrow: + return GDK_Right; + break; + + case GDK_leftarrow: + return GDK_Left; + break; + + case GDK_3270_Enter: + return GDK_Return; + + case GDK_F35: + return GDK_KP_Enter; + break; } - if (len - last_sep >= target_characters) { + return keyval; +} - /* even the filename itself is too long */ - 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; + +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 screen = window->get_screen (); + Gdk::Rectangle monitor_rect; + screen->get_monitor_geometry (0, monitor_rect); + + int const w = std::min (monitor_rect.get_width(), max_width) * 0.8; + int const h = std::min (monitor_rect.get_height(), max_height) * 0.8; + + window->resize (w, h); +} + +Glib::RefPtr +pixbuf_from_ustring(const ustring& name, Pango::FontDescription* font, int clip_width, int clip_height, Gdk::Color fg) +{ + static Glib::RefPtr* empty_pixbuf = 0; + + if (name.empty()) { + if (empty_pixbuf == 0) { + empty_pixbuf = new Glib::RefPtr; + *empty_pixbuf = Gdk::Pixbuf::create(Gdk::COLORSPACE_RGB, true, 8, clip_width, clip_height); + } + return *empty_pixbuf; } + + Glib::RefPtr 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); - uint32_t so_far = (len - last_sep); - uint32_t space_for = target_characters - so_far; + 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); - 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; - + 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 wcont) +{ + boost::shared_ptr 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 wcont) +{ + boost::shared_ptr 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 c, Gtk::Adjustment& a) +{ + boost::weak_ptr 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()); +} +