X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fgtkmm2ext%2Futils.cc;h=b4926ac43b0177761e1293c94fa74f4123fb7a92;hb=9301ffafab4163e71b1b9fcc9d5f817cae606563;hp=d9e1d15e9ff9914e6f4ae9b887c00bdd13548bb1;hpb=e70249ab658ef1b02da01d84bf2d9b46847e5194;p=ardour.git diff --git a/libs/gtkmm2ext/utils.cc b/libs/gtkmm2ext/utils.cc index d9e1d15e9f..b4926ac43b 100644 --- a/libs/gtkmm2ext/utils.cc +++ b/libs/gtkmm2ext/utils.cc @@ -23,22 +23,27 @@ #include #include -#include #include #include #include #include +#include #include +#include + +#include "gtkmm2ext/utils.h" #include "i18n.h" using namespace std; void -Gtkmm2ext::init () +Gtkmm2ext::init (const char* localedir) { - // Necessary for gettext - (void) bindtextdomain(PACKAGE, LOCALEDIR); +#ifdef ENABLE_NLS + (void) bindtextdomain(PACKAGE, localedir); + (void) bind_textdomain_codeset (PACKAGE, "UTF-8"); +#endif } void @@ -53,9 +58,9 @@ Gtkmm2ext::get_ink_pixel_size (Glib::RefPtr layout, } void -get_pixel_size (Glib::RefPtr layout, - int& width, - int& height) +Gtkmm2ext::get_pixel_size (Glib::RefPtr layout, + int& width, + int& height) { layout->get_pixel_size (width, height); } @@ -71,6 +76,51 @@ Gtkmm2ext::set_size_request_to_display_given_text (Gtk::Widget &w, const gchar * w.set_size_request(width + hpadding, height + vpadding); } +/** Set width request to display given text, and height to display anything. + This is useful for setting many widgets to the same height for consistency. */ +void +Gtkmm2ext::set_size_request_to_display_given_text_width (Gtk::Widget& w, + const gchar* htext, + gint hpadding, + gint vpadding) +{ + static const gchar* vtext = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + + w.ensure_style (); + + int hwidth, hheight; + get_pixel_size (w.create_pango_layout (htext), hwidth, hheight); + + int vwidth, vheight; + get_pixel_size (w.create_pango_layout (vtext), vwidth, vheight); + + w.set_size_request(hwidth + hpadding, vheight + vpadding); +} + +void +Gtkmm2ext::set_height_request_to_display_any_text (Gtk::Widget& w, gint vpadding) +{ + static const gchar* vtext = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + + w.ensure_style (); + + int width, height; + get_pixel_size (w.create_pango_layout (vtext), width, height); + + w.set_size_request(-1, height + vpadding); +} + +void +Gtkmm2ext::set_size_request_to_display_given_text (Gtk::Widget &w, std::string const & text, + gint hpadding, gint vpadding) +{ + int width, height; + w.ensure_style (); + + get_pixel_size (w.create_pango_layout (text), width, height); + w.set_size_request(width + hpadding, height + vpadding); +} + void Gtkmm2ext::set_size_request_to_display_given_text (Gtk::Widget &w, const std::vector& strings, @@ -109,6 +159,33 @@ Gtkmm2ext::set_size_request_to_display_given_text (Gtk::Widget &w, w.set_size_request(width_max + hpadding, height_max + vpadding); } +/** This version specifies horizontal padding in text to avoid assumptions + about font size. Should be used anywhere padding is used to avoid text, + like combo boxes. */ +void +Gtkmm2ext::set_size_request_to_display_given_text (Gtk::Widget& w, + const std::vector& strings, + const std::string& hpadding, + gint vpadding) +{ + int width_max = 0; + int height_max = 0; + w.ensure_style (); + + for (vector::const_iterator i = strings.begin(); i != strings.end(); ++i) { + int width, height; + get_pixel_size (w.create_pango_layout (*i), width, height); + width_max = max(width_max,width); + height_max = max(height_max, height); + } + + int pad_width; + int pad_height; + get_pixel_size (w.create_pango_layout (hpadding), pad_width, pad_height); + + w.set_size_request(width_max + pad_width, height_max + vpadding); +} + static inline guint8 demultiply_alpha (guint8 src, guint8 alpha) @@ -125,11 +202,11 @@ demultiply_alpha (guint8 src, return alpha ? ((guint (src) << 8) - src) / alpha : 0; } -static void -convert_bgra_to_rgba (guint8 const* src, - guint8* dst, - int width, - int height) +void +Gtkmm2ext::convert_bgra_to_rgba (guint8 const* src, + guint8* dst, + int width, + int height) { guint8 const* src_pixel = src; guint8* dst_pixel = dst; @@ -381,93 +458,161 @@ Gtkmm2ext::container_clear (Gtk::Container& c) } } -#if 1 void Gtkmm2ext::rounded_rectangle (Cairo::RefPtr context, double x, double y, double w, double h, double r) { rounded_rectangle (context->cobj(), x, y, w, h, r); } +void +Gtkmm2ext::rounded_top_rectangle (Cairo::RefPtr context, double x, double y, double w, double h, double r) +{ + rounded_top_rectangle (context->cobj(), x, y, w, h, r); +} +void +Gtkmm2ext::rounded_top_left_rectangle (Cairo::RefPtr context, double x, double y, double w, double h, double r) +{ + rounded_top_left_rectangle (context->cobj(), x, y, w, h, r); +} +void +Gtkmm2ext::rounded_top_right_rectangle (Cairo::RefPtr context, double x, double y, double w, double h, double r) +{ + rounded_top_right_rectangle (context->cobj(), x, y, w, h, r); +} +void +Gtkmm2ext::rounded_top_half_rectangle (Cairo::RefPtr context, double x, double y, double w, double h, double r) +{ + rounded_top_half_rectangle (context->cobj(), x, y, w, h, r); +} +void +Gtkmm2ext::rounded_bottom_half_rectangle (Cairo::RefPtr context, double x, double y, double w, double h, double r) +{ + rounded_bottom_half_rectangle (context->cobj(), x, y, w, h, r); +} void -Gtkmm2ext::rounded_rectangle (cairo_t* cr, double x, double y, double w, double h, double r) +Gtkmm2ext::rounded_left_half_rectangle (Cairo::RefPtr context, double x, double y, double w, double h, double r) { - /* renders small shapes better than most others */ + rounded_left_half_rectangle (context->cobj(), x, y, w, h, r); +} -/* A****BQ - H C - * * - G D - F****E -*/ - cairo_move_to (cr, x+r,y); // Move to A - cairo_line_to (cr, x+w-r,y); // Straight line to B - cairo_curve_to (cr, x+w,y,x+w,y,x+w,y+r); // Curve to C, Control points are both at Q - cairo_line_to (cr, x+w,y+h-r); // Move to D - cairo_curve_to (cr, x+w,y+h,x+w,y+h,x+w-r,y+h); // Curve to E - cairo_line_to (cr, x+r,y+h); // Line to F - cairo_curve_to (cr, x,y+h,x,y+h,x,y+h-r); // Curve to G - cairo_line_to (cr, x,y+r); // Line to H - cairo_curve_to (cr, x,y,x,y,x+r,y); // Curve to A +void +Gtkmm2ext::rounded_right_half_rectangle (Cairo::RefPtr context, double x, double y, double w, double h, double r) +{ + rounded_right_half_rectangle (context->cobj(), x, y, w, h, r); } -#else +void +Gtkmm2ext::rounded_rectangle (cairo_t* cr, double x, double y, double w, double h, double r) +{ + double degrees = M_PI / 180.0; + + cairo_new_sub_path (cr); + cairo_arc (cr, x + w - r, y + r, r, -90 * degrees, 0 * degrees); //tr + cairo_arc (cr, x + w - r, y + h - r, r, 0 * degrees, 90 * degrees); //br + cairo_arc (cr, x + r, y + h - r, r, 90 * degrees, 180 * degrees); //bl + cairo_arc (cr, x + r, y + r, r, 180 * degrees, 270 * degrees); //tl + cairo_close_path (cr); +} void -Gtkmm2ext::rounded_rectangle (Cairo::RefPtr context, double x, double y, double width, double height, double radius) +Gtkmm2ext::rounded_left_half_rectangle (cairo_t* cr, double x, double y, double w, double h, double r) { - /* doesn't render small shapes well at all, and does not absolutely honor width & height */ + double degrees = M_PI / 180.0; + + cairo_new_sub_path (cr); + cairo_line_to (cr, x+w, y); // tr + cairo_line_to (cr, x+w, y + h); // br + cairo_arc (cr, x + r, y + h - r, r, 90 * degrees, 180 * degrees); //bl + cairo_arc (cr, x + r, y + r, r, 180 * degrees, 270 * degrees); //tl + cairo_close_path (cr); +} - double x0 = x+radius/2.0; - double y0 = y+radius/2.0; - double rect_width = width - radius; - double rect_height = height - radius; +void +Gtkmm2ext::rounded_right_half_rectangle (cairo_t* cr, double x, double y, double w, double h, double r) +{ + double degrees = M_PI / 180.0; + + cairo_new_sub_path (cr); + cairo_arc (cr, x + w - r, y + r, r, -90 * degrees, 0 * degrees); //tr + cairo_arc (cr, x + w - r, y + h - r, r, 0 * degrees, 90 * degrees); //br + cairo_line_to (cr, x, y + h); // bl + cairo_line_to (cr, x, y); // tl + cairo_close_path (cr); +} - context->save(); +void +Gtkmm2ext::rounded_top_half_rectangle (cairo_t* cr, double x, double y, double w, double h, double r) +{ + double degrees = M_PI / 180.0; + + cairo_new_sub_path (cr); + cairo_move_to (cr, x+w, y+h); + cairo_line_to (cr, x, y+h); + cairo_arc (cr, x + r, y + r, r, 180 * degrees, 270 * degrees); //tl + cairo_arc (cr, x + w - r, y + r, r, -90 * degrees, 0 * degrees); //tr + cairo_close_path (cr); +} - double x1=x0+rect_width; - double y1=y0+rect_height; +void +Gtkmm2ext::rounded_bottom_half_rectangle (cairo_t* cr, double x, double y, double w, double h, double r) +{ + double degrees = M_PI / 180.0; + + cairo_new_sub_path (cr); + cairo_move_to (cr, x, y); + cairo_line_to (cr, x+w, y); + cairo_arc (cr, x + w - r, y + h - r, r, 0 * degrees, 90 * degrees); //br + cairo_arc (cr, x + r, y + h - r, r, 90 * degrees, 180 * degrees); //bl + cairo_close_path (cr); +} - if (rect_width/2move_to (x0, (y0 + y1)/2); - context->curve_to (x0 ,y0, x0, y0, (x0 + x1)/2, y0); - context->curve_to (x1, y0, x1, y0, x1, (y0 + y1)/2); - context->curve_to (x1, y1, x1, y1, (x1 + x0)/2, y1); - context->curve_to (x0, y1, x0, y1, x0, (y0 + y1)/2); - } else { - context->move_to (x0, y0 + radius); - context->curve_to (x0 ,y0, x0, y0, (x0 + x1)/2, y0); - context->curve_to (x1, y0, x1, y0, x1, y0 + radius); - context->line_to (x1 , y1 - radius); - context->curve_to (x1, y1, x1, y1, (x1 + x0)/2, y1); - context->curve_to (x0, y1, x0, y1, x0, y1- radius); - } - } else { - if (rect_height/2move_to (x0, (y0 + y1)/2); - context->curve_to (x0 , y0, x0 , y0, x0 + radius, y0); - context->line_to (x1 - radius, y0); - context->curve_to (x1, y0, x1, y0, x1, (y0 + y1)/2); - context->curve_to (x1, y1, x1, y1, x1 - radius, y1); - context->line_to (x0 + radius, y1); - context->curve_to (x0, y1, x0, y1, x0, (y0 + y1)/2); - } else { - context->move_to (x0, y0 + radius); - context->curve_to (x0 , y0, x0 , y0, x0 + radius, y0); - context->line_to (x1 - radius, y0); - context->curve_to (x1, y0, x1, y0, x1, y0 + radius); - context->line_to (x1 , y1 - radius); - context->curve_to (x1, y1, x1, y1, x1 - radius, y1); - context->line_to (x0 + radius, y1); - context->curve_to (x0, y1, x0, y1, x0, y1- radius); - } - } - context->close_path (); - context->restore(); +void +Gtkmm2ext::rounded_top_rectangle (cairo_t* cr, double x, double y, double w, double h, double r) +{ + double degrees = M_PI / 180.0; + + cairo_new_sub_path (cr); + cairo_move_to (cr, x+w, y+h); + cairo_line_to (cr, x, y+h); + cairo_arc (cr, x + r, y + r, r, 180 * degrees, 270 * degrees); //tl + cairo_arc (cr, x + w - r, y + r, r, -90 * degrees, 0 * degrees); //tr + cairo_close_path (cr); } -#endif +void +Gtkmm2ext::rounded_top_left_rectangle (cairo_t* cr, double x, double y, double w, double h, double r) +{ +/* A****B + H * + * * + * * + F****E +*/ + cairo_move_to (cr, x+r,y); // Move to A + cairo_line_to (cr, x+w,y); // Straight line to B + cairo_line_to (cr, x+w,y+h); // Move to E + cairo_line_to (cr, x,y+h); // Line to F + cairo_line_to (cr, x,y+r); // Line to H + cairo_curve_to (cr, x,y,x,y,x+r,y); // Curve to A +} + +void +Gtkmm2ext::rounded_top_right_rectangle (cairo_t* cr, double x, double y, double w, double h, double r) +{ +/* A****BQ + * C + * * + * * + F****E +*/ + cairo_move_to (cr, x,y); // Move to A + cairo_line_to (cr, x+w-r,y); // Straight line to B + cairo_curve_to (cr, x+w,y,x+w,y,x+w,y+r); // Curve to C, Control points are both at Q + cairo_line_to (cr, x+w,y+h); // Move to E + cairo_line_to (cr, x,y+h); // Line to F + cairo_line_to (cr, x,y); // Line to A +} Glib::RefPtr Gtkmm2ext::window_to_draw_on (Gtk::Widget& w, Gtk::Widget** parent) @@ -488,26 +633,235 @@ Gtkmm2ext::window_to_draw_on (Gtk::Widget& w, Gtk::Widget** parent) return Glib::RefPtr (); } +int +Gtkmm2ext::pixel_width (const string& str, Pango::FontDescription& font) +{ + Gtk::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; +} + #if 0 string -fit_to_pixels (const string& str, int pixel_width, Pango::FontDescription& font, int& actual_width, bool with_ellipses) +Gtkmm2ext::fit_to_pixels (const string& str, int pixel_width, Pango::FontDescription& font, int& actual_width, bool with_ellipses) { - Label foo; + /* DECEMBER 2011: THIS PROTOTYPE OF fit_to_pixels() IS NOT USED + ANYWHERE AND HAS NOT BEEN TESTED. + */ + Gtk::Label foo; Glib::RefPtr layout = foo.create_pango_layout (str); - Glib::RefPtr line; + Glib::RefPtr line; layout->set_font_description (font); layout->set_width (pixel_width * PANGO_SCALE); - if (with_ellipsis) - layout->set_ellipsize (PANGO_ELLIPSIZE_END); - else - layout->set_wrap_mode (PANGO_WRAP_CHAR); + if (with_ellipses) { + layout->set_ellipsize (Pango::ELLIPSIZE_END); + } else { + layout->set_wrap (Pango::WRAP_CHAR); + } - line = layout->get_line_readonly (0); + line = layout->get_line (0); /* XXX: might need special care to get the ellipsis character, not sure - how that works */ - return strdup (layout->get_text () + line->start_index, line->length); + how that works + */ + + string s = string (layout->get_text ().substr(line->get_start_index(), line->get_length())); + + cerr << "fit to pixels of " << str << " returns " << s << endl; + + return s; } #endif + +/** 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 +Gtkmm2ext::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); +} + +Gtk::Label * +Gtkmm2ext::left_aligned_label (string const & t) +{ + Gtk::Label* l = new Gtk::Label (t); + l->set_alignment (0, 0.5); + return l; +} + +static bool +make_null_tooltip (int, int, bool, const Glib::RefPtr& t) +{ + t->set_tip_area (Gdk::Rectangle (0, 0, 0, 0)); + return true; +} + +/** Hackily arrange for the provided widget to have no tooltip, + * and also to stop any other widget from providing one while + * the mouse is over w. + */ +void +Gtkmm2ext::set_no_tooltip_whatsoever (Gtk::Widget& w) +{ + w.property_has_tooltip() = true; + w.signal_query_tooltip().connect (sigc::ptr_fun (make_null_tooltip)); +} + +void +Gtkmm2ext::enable_tooltips () +{ + gtk_rc_parse_string ("gtk-enable-tooltips = 1"); +} + +void +Gtkmm2ext::disable_tooltips () +{ + gtk_rc_parse_string ("gtk-enable-tooltips = 0"); +} + +bool +Gtkmm2ext::event_inside_widget_window (Gtk::Widget& widget, GdkEvent* ev) +{ + gdouble evx, evy; + + if (!gdk_event_get_root_coords (ev, &evx, &evy)) { + return false; + } + + gint wx; + gint wy; + gint width, height, depth; + gint x, y; + + Glib::RefPtr widget_window = widget.get_window(); + + widget_window->get_geometry (x, y, width, height, depth); + widget_window->get_root_origin (wx, wy); + + if ((evx >= wx && evx < wx + width) && + (evy >= wy && evy < wy + height)) { + return true; + } + + return false; +} + +const char* +Gtkmm2ext::event_type_string (int event_type) +{ + switch (event_type) { + case GDK_NOTHING: + return "nothing"; + case GDK_DELETE: + return "delete"; + case GDK_DESTROY: + return "destroy"; + case GDK_EXPOSE: + return "expose"; + case GDK_MOTION_NOTIFY: + return "motion_notify"; + case GDK_BUTTON_PRESS: + return "button_press"; + case GDK_2BUTTON_PRESS: + return "2button_press"; + case GDK_3BUTTON_PRESS: + return "3button_press"; + case GDK_BUTTON_RELEASE: + return "button_release"; + case GDK_KEY_PRESS: + return "key_press"; + case GDK_KEY_RELEASE: + return "key_release"; + case GDK_ENTER_NOTIFY: + return "enter_notify"; + case GDK_LEAVE_NOTIFY: + return "leave_notify"; + case GDK_FOCUS_CHANGE: + return "focus_change"; + case GDK_CONFIGURE: + return "configure"; + case GDK_MAP: + return "map"; + case GDK_UNMAP: + return "unmap"; + case GDK_PROPERTY_NOTIFY: + return "property_notify"; + case GDK_SELECTION_CLEAR: + return "selection_clear"; + case GDK_SELECTION_REQUEST: + return "selection_request"; + case GDK_SELECTION_NOTIFY: + return "selection_notify"; + case GDK_PROXIMITY_IN: + return "proximity_in"; + case GDK_PROXIMITY_OUT: + return "proximity_out"; + case GDK_DRAG_ENTER: + return "drag_enter"; + case GDK_DRAG_LEAVE: + return "drag_leave"; + case GDK_DRAG_MOTION: + return "drag_motion"; + case GDK_DRAG_STATUS: + return "drag_status"; + case GDK_DROP_START: + return "drop_start"; + case GDK_DROP_FINISHED: + return "drop_finished"; + case GDK_CLIENT_EVENT: + return "client_event"; + case GDK_VISIBILITY_NOTIFY: + return "visibility_notify"; + case GDK_NO_EXPOSE: + return "no_expose"; + case GDK_SCROLL: + return "scroll"; + case GDK_WINDOW_STATE: + return "window_state"; + case GDK_SETTING: + return "setting"; + case GDK_OWNER_CHANGE: + return "owner_change"; + case GDK_GRAB_BROKEN: + return "grab_broken"; + case GDK_DAMAGE: + return "damage"; + } + + return "unknown"; +}