2 Copyright (C) 1999 Paul Barton-Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 #include <gtk/gtkpaned.h>
26 #include <gtkmm/widget.h>
27 #include <gtkmm/button.h>
28 #include <gtkmm/window.h>
29 #include <gtkmm/paned.h>
30 #include <gtkmm/label.h>
31 #include <gtkmm/comboboxtext.h>
32 #include <gtkmm/tooltip.h>
34 #include "gtkmm2ext/utils.h"
41 Gtkmm2ext::init (const char* localedir)
44 (void) bindtextdomain(PACKAGE, localedir);
45 (void) bind_textdomain_codeset (PACKAGE, "UTF-8");
50 Gtkmm2ext::get_ink_pixel_size (Glib::RefPtr<Pango::Layout> layout,
54 Pango::Rectangle ink_rect = layout->get_ink_extents ();
56 width = (ink_rect.get_width() + PANGO_SCALE / 2) / PANGO_SCALE;
57 height = (ink_rect.get_height() + PANGO_SCALE / 2) / PANGO_SCALE;
61 Gtkmm2ext::get_pixel_size (Glib::RefPtr<Pango::Layout> layout,
65 layout->get_pixel_size (width, height);
69 Gtkmm2ext::set_size_request_to_display_given_text (Gtk::Widget &w, const gchar *text,
70 gint hpadding, gint vpadding)
75 get_pixel_size (w.create_pango_layout (text), width, height);
76 w.set_size_request(width + hpadding, height + vpadding);
79 /** Set width request to display given text, and height to display anything.
80 This is useful for setting many widgets to the same height for consistency. */
82 Gtkmm2ext::set_size_request_to_display_given_text_width (Gtk::Widget& w,
87 static const gchar* vtext = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
92 get_pixel_size (w.create_pango_layout (htext), hwidth, hheight);
95 get_pixel_size (w.create_pango_layout (vtext), vwidth, vheight);
97 w.set_size_request(hwidth + hpadding, vheight + vpadding);
101 Gtkmm2ext::set_height_request_to_display_any_text (Gtk::Widget& w, gint vpadding)
103 static const gchar* vtext = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
108 get_pixel_size (w.create_pango_layout (vtext), width, height);
110 w.set_size_request(-1, height + vpadding);
114 Gtkmm2ext::set_size_request_to_display_given_text (Gtk::Widget &w, std::string const & text,
115 gint hpadding, gint vpadding)
120 get_pixel_size (w.create_pango_layout (text), width, height);
121 w.set_size_request(width + hpadding, height + vpadding);
125 Gtkmm2ext::set_size_request_to_display_given_text (Gtk::Widget &w,
126 const std::vector<std::string>& strings,
127 gint hpadding, gint vpadding)
134 const vector<string>* to_use;
135 vector<string>::const_iterator i;
137 for (i = strings.begin(); i != strings.end(); ++i) {
138 if ((*i).find_first_of ("gy") != string::npos) {
139 /* contains a descender */
144 if (i == strings.end()) {
145 /* make a copy of the strings then add one that has a descender */
147 copy.push_back ("g");
153 for (vector<string>::const_iterator i = to_use->begin(); i != to_use->end(); ++i) {
154 get_pixel_size (w.create_pango_layout (*i), width, height);
155 width_max = max(width_max,width);
156 height_max = max(height_max, height);
159 w.set_size_request(width_max + hpadding, height_max + vpadding);
162 /** This version specifies horizontal padding in text to avoid assumptions
163 about font size. Should be used anywhere padding is used to avoid text,
166 Gtkmm2ext::set_size_request_to_display_given_text (Gtk::Widget& w,
167 const std::vector<std::string>& strings,
168 const std::string& hpadding,
175 for (vector<string>::const_iterator i = strings.begin(); i != strings.end(); ++i) {
177 get_pixel_size (w.create_pango_layout (*i), width, height);
178 width_max = max(width_max,width);
179 height_max = max(height_max, height);
184 get_pixel_size (w.create_pango_layout (hpadding), pad_width, pad_height);
186 w.set_size_request(width_max + pad_width, height_max + vpadding);
190 demultiply_alpha (guint8 src,
193 /* cairo pixel buffer data contains RGB values with the alpha
194 values premultiplied.
196 GdkPixbuf pixel buffer data contains RGB values without the
199 this removes the alpha component from the cairo version and
200 returns the GdkPixbuf version.
202 return alpha ? ((guint (src) << 8) - src) / alpha : 0;
206 Gtkmm2ext::convert_bgra_to_rgba (guint8 const* src,
211 guint8 const* src_pixel = src;
212 guint8* dst_pixel = dst;
214 /* cairo pixel data is endian-dependent ARGB with A in the most significant 8 bits,
215 with premultipled alpha values (see preceding function)
217 GdkPixbuf pixel data is non-endian-dependent RGBA with R in the lowest addressable
218 8 bits, and non-premultiplied alpha values.
220 convert from the cairo values to the GdkPixbuf ones.
223 for (int y = 0; y < height; y++) {
224 for (int x = 0; x < width; x++) {
225 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
226 /* Cairo [ B G R A ] is actually [ B G R A ] in memory SOURCE
228 Pixbuf [ R G B A ] is actually [ R G B A ] in memory DEST
230 dst_pixel[0] = demultiply_alpha (src_pixel[2],
231 src_pixel[3]); // R [0] <= [ 2 ]
232 dst_pixel[1] = demultiply_alpha (src_pixel[1],
233 src_pixel[3]); // G [1] <= [ 1 ]
234 dst_pixel[2] = demultiply_alpha (src_pixel[0],
235 src_pixel[3]); // B [2] <= [ 0 ]
236 dst_pixel[3] = src_pixel[3]; // alpha
238 #elif G_BYTE_ORDER == G_BIG_ENDIAN
239 /* Cairo [ B G R A ] is actually [ A R G B ] in memory SOURCE
241 Pixbuf [ R G B A ] is actually [ R G B A ] in memory DEST
243 dst_pixel[0] = demultiply_alpha (src_pixel[1],
244 src_pixel[0]); // R [0] <= [ 1 ]
245 dst_pixel[1] = demultiply_alpha (src_pixel[2],
246 src_pixel[0]); // G [1] <= [ 2 ]
247 dst_pixel[2] = demultiply_alpha (src_pixel[3],
248 src_pixel[0]); // B [2] <= [ 3 ]
249 dst_pixel[3] = src_pixel[0]; // alpha
252 #error ardour does not currently support PDP-endianess
261 Glib::RefPtr<Gdk::Pixbuf>
262 Gtkmm2ext::pixbuf_from_string(const string& name, const Pango::FontDescription& font, int clip_width, int clip_height, Gdk::Color fg)
264 static Glib::RefPtr<Gdk::Pixbuf>* empty_pixbuf = 0;
267 if (empty_pixbuf == 0) {
268 empty_pixbuf = new Glib::RefPtr<Gdk::Pixbuf>;
269 *empty_pixbuf = Gdk::Pixbuf::create(Gdk::COLORSPACE_RGB, true, 8, clip_width, clip_height);
271 return *empty_pixbuf;
274 Glib::RefPtr<Gdk::Pixbuf> buf = Gdk::Pixbuf::create(Gdk::COLORSPACE_RGB, true, 8, clip_width, clip_height);
276 cairo_surface_t* surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, clip_width, clip_height);
277 cairo_t* cr = cairo_create (surface);
278 cairo_text_extents_t te;
280 cairo_set_source_rgba (cr, fg.get_red_p(), fg.get_green_p(), fg.get_blue_p(), 1.0);
281 cairo_select_font_face (cr, font.get_family().c_str(),
282 CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
283 cairo_set_font_size (cr, font.get_size() / Pango::SCALE);
284 cairo_text_extents (cr, name.c_str(), &te);
286 cairo_move_to (cr, 0.5, int (0.5 - te.height / 2 - te.y_bearing + clip_height / 2));
287 cairo_show_text (cr, name.c_str());
289 convert_bgra_to_rgba(cairo_image_surface_get_data (surface), buf->get_pixels(), clip_width, clip_height);
292 cairo_surface_destroy(surface);
298 Gtkmm2ext::set_popdown_strings (Gtk::ComboBoxText& cr, const vector<string>& strings)
300 vector<string>::const_iterator i;
304 for (i = strings.begin(); i != strings.end(); ++i) {
310 Gtkmm2ext::get_paned_handle (Gtk::Paned& paned)
312 return GTK_PANED(paned.gobj())->handle;
316 Gtkmm2ext::set_decoration (Gtk::Window* win, Gdk::WMDecoration decor)
318 win->get_window()->set_decorations (decor);
321 void Gtkmm2ext::set_treeview_header_as_default_label(Gtk::TreeViewColumn* c)
323 gtk_tree_view_column_set_widget( c->gobj(), GTK_WIDGET(0) );
327 Gtkmm2ext::detach_menu (Gtk::Menu& menu)
329 /* its possible for a Gtk::Menu to have no gobj() because it has
330 not yet been instantiated. Catch this and provide a safe
334 if (menu.get_attach_widget()) {
341 Gtkmm2ext::possibly_translate_keyval_to_make_legal_accelerator (uint32_t& keyval)
343 int fakekey = GDK_VoidSymbol;
347 case GDK_ISO_Left_Tab:
352 fakekey = GDK_uparrow;
356 fakekey = GDK_downarrow;
360 fakekey = GDK_rightarrow;
364 fakekey = GDK_leftarrow;
368 fakekey = GDK_3270_Enter;
379 if (fakekey != GDK_VoidSymbol) {
388 Gtkmm2ext::possibly_translate_legal_accelerator_to_real_key (uint32_t keyval)
423 Gtkmm2ext::physical_screen_height (Glib::RefPtr<Gdk::Window> win)
425 GdkScreen* scr = gdk_screen_get_default();
429 gint monitor = gdk_screen_get_monitor_at_window (scr, win->gobj());
430 gdk_screen_get_monitor_geometry (scr, monitor, &r);
433 return gdk_screen_get_height (scr);
438 Gtkmm2ext::physical_screen_width (Glib::RefPtr<Gdk::Window> win)
440 GdkScreen* scr = gdk_screen_get_default();
444 gint monitor = gdk_screen_get_monitor_at_window (scr, win->gobj());
445 gdk_screen_get_monitor_geometry (scr, monitor, &r);
448 return gdk_screen_get_width (scr);
453 Gtkmm2ext::container_clear (Gtk::Container& c)
455 list<Gtk::Widget*> children = c.get_children();
456 for (list<Gtk::Widget*>::iterator child = children.begin(); child != children.end(); ++child) {
462 Gtkmm2ext::rounded_rectangle (Cairo::RefPtr<Cairo::Context> context, double x, double y, double w, double h, double r)
464 rounded_rectangle (context->cobj(), x, y, w, h, r);
467 Gtkmm2ext::rounded_top_rectangle (Cairo::RefPtr<Cairo::Context> context, double x, double y, double w, double h, double r)
469 rounded_top_rectangle (context->cobj(), x, y, w, h, r);
472 Gtkmm2ext::rounded_top_left_rectangle (Cairo::RefPtr<Cairo::Context> context, double x, double y, double w, double h, double r)
474 rounded_top_left_rectangle (context->cobj(), x, y, w, h, r);
477 Gtkmm2ext::rounded_top_right_rectangle (Cairo::RefPtr<Cairo::Context> context, double x, double y, double w, double h, double r)
479 rounded_top_right_rectangle (context->cobj(), x, y, w, h, r);
482 Gtkmm2ext::rounded_top_half_rectangle (Cairo::RefPtr<Cairo::Context> context, double x, double y, double w, double h, double r)
484 rounded_top_half_rectangle (context->cobj(), x, y, w, h, r);
487 Gtkmm2ext::rounded_bottom_half_rectangle (Cairo::RefPtr<Cairo::Context> context, double x, double y, double w, double h, double r)
489 rounded_bottom_half_rectangle (context->cobj(), x, y, w, h, r);
493 Gtkmm2ext::rounded_left_half_rectangle (Cairo::RefPtr<Cairo::Context> context, double x, double y, double w, double h, double r)
495 rounded_left_half_rectangle (context->cobj(), x, y, w, h, r);
499 Gtkmm2ext::rounded_right_half_rectangle (Cairo::RefPtr<Cairo::Context> context, double x, double y, double w, double h, double r)
501 rounded_right_half_rectangle (context->cobj(), x, y, w, h, r);
505 Gtkmm2ext::rounded_rectangle (cairo_t* cr, double x, double y, double w, double h, double r)
507 double degrees = M_PI / 180.0;
509 cairo_new_sub_path (cr);
510 cairo_arc (cr, x + w - r, y + r, r, -90 * degrees, 0 * degrees); //tr
511 cairo_arc (cr, x + w - r, y + h - r, r, 0 * degrees, 90 * degrees); //br
512 cairo_arc (cr, x + r, y + h - r, r, 90 * degrees, 180 * degrees); //bl
513 cairo_arc (cr, x + r, y + r, r, 180 * degrees, 270 * degrees); //tl
514 cairo_close_path (cr);
518 Gtkmm2ext::rounded_left_half_rectangle (cairo_t* cr, double x, double y, double w, double h, double r)
520 double degrees = M_PI / 180.0;
522 cairo_new_sub_path (cr);
523 cairo_line_to (cr, x+w, y); // tr
524 cairo_line_to (cr, x+w, y + h); // br
525 cairo_arc (cr, x + r, y + h - r, r, 90 * degrees, 180 * degrees); //bl
526 cairo_arc (cr, x + r, y + r, r, 180 * degrees, 270 * degrees); //tl
527 cairo_close_path (cr);
531 Gtkmm2ext::rounded_right_half_rectangle (cairo_t* cr, double x, double y, double w, double h, double r)
533 double degrees = M_PI / 180.0;
535 cairo_new_sub_path (cr);
536 cairo_arc (cr, x + w - r, y + r, r, -90 * degrees, 0 * degrees); //tr
537 cairo_arc (cr, x + w - r, y + h - r, r, 0 * degrees, 90 * degrees); //br
538 cairo_line_to (cr, x, y + h); // bl
539 cairo_line_to (cr, x, y); // tl
540 cairo_close_path (cr);
544 Gtkmm2ext::rounded_top_half_rectangle (cairo_t* cr, double x, double y, double w, double h, double r)
546 double degrees = M_PI / 180.0;
548 cairo_new_sub_path (cr);
549 cairo_move_to (cr, x+w, y+h);
550 cairo_line_to (cr, x, y+h);
551 cairo_arc (cr, x + r, y + r, r, 180 * degrees, 270 * degrees); //tl
552 cairo_arc (cr, x + w - r, y + r, r, -90 * degrees, 0 * degrees); //tr
553 cairo_close_path (cr);
557 Gtkmm2ext::rounded_bottom_half_rectangle (cairo_t* cr, double x, double y, double w, double h, double r)
559 double degrees = M_PI / 180.0;
561 cairo_new_sub_path (cr);
562 cairo_move_to (cr, x, y);
563 cairo_line_to (cr, x+w, y);
564 cairo_arc (cr, x + w - r, y + h - r, r, 0 * degrees, 90 * degrees); //br
565 cairo_arc (cr, x + r, y + h - r, r, 90 * degrees, 180 * degrees); //bl
566 cairo_close_path (cr);
571 Gtkmm2ext::rounded_top_rectangle (cairo_t* cr, double x, double y, double w, double h, double r)
573 double degrees = M_PI / 180.0;
575 cairo_new_sub_path (cr);
576 cairo_move_to (cr, x+w, y+h);
577 cairo_line_to (cr, x, y+h);
578 cairo_arc (cr, x + r, y + r, r, 180 * degrees, 270 * degrees); //tl
579 cairo_arc (cr, x + w - r, y + r, r, -90 * degrees, 0 * degrees); //tr
580 cairo_close_path (cr);
584 Gtkmm2ext::rounded_top_left_rectangle (cairo_t* cr, double x, double y, double w, double h, double r)
592 cairo_move_to (cr, x+r,y); // Move to A
593 cairo_line_to (cr, x+w,y); // Straight line to B
594 cairo_line_to (cr, x+w,y+h); // Move to E
595 cairo_line_to (cr, x,y+h); // Line to F
596 cairo_line_to (cr, x,y+r); // Line to H
597 cairo_curve_to (cr, x,y,x,y,x+r,y); // Curve to A
601 Gtkmm2ext::rounded_top_right_rectangle (cairo_t* cr, double x, double y, double w, double h, double r)
609 cairo_move_to (cr, x,y); // Move to A
610 cairo_line_to (cr, x+w-r,y); // Straight line to B
611 cairo_curve_to (cr, x+w,y,x+w,y,x+w,y+r); // Curve to C, Control points are both at Q
612 cairo_line_to (cr, x+w,y+h); // Move to E
613 cairo_line_to (cr, x,y+h); // Line to F
614 cairo_line_to (cr, x,y); // Line to A
617 Glib::RefPtr<Gdk::Window>
618 Gtkmm2ext::window_to_draw_on (Gtk::Widget& w, Gtk::Widget** parent)
620 if (w.get_has_window()) {
621 return w.get_window();
624 (*parent) = w.get_parent();
627 if ((*parent)->get_has_window()) {
628 return (*parent)->get_window ();
630 (*parent) = (*parent)->get_parent ();
633 return Glib::RefPtr<Gdk::Window> ();
637 Gtkmm2ext::pixel_width (const string& str, Pango::FontDescription& font)
640 Glib::RefPtr<Pango::Layout> layout = foo.create_pango_layout ("");
642 layout->set_font_description (font);
643 layout->set_text (str);
646 Gtkmm2ext::get_ink_pixel_size (layout, width, height);
652 Gtkmm2ext::fit_to_pixels (const string& str, int pixel_width, Pango::FontDescription& font, int& actual_width, bool with_ellipses)
654 /* DECEMBER 2011: THIS PROTOTYPE OF fit_to_pixels() IS NOT USED
655 ANYWHERE AND HAS NOT BEEN TESTED.
658 Glib::RefPtr<Pango::Layout> layout = foo.create_pango_layout (str);
659 Glib::RefPtr<const Pango::LayoutLine> line;
661 layout->set_font_description (font);
662 layout->set_width (pixel_width * PANGO_SCALE);
665 layout->set_ellipsize (Pango::ELLIPSIZE_END);
667 layout->set_wrap (Pango::WRAP_CHAR);
670 line = layout->get_line (0);
672 /* XXX: might need special care to get the ellipsis character, not sure
676 string s = string (layout->get_text ().substr(line->get_start_index(), line->get_length()));
678 cerr << "fit to pixels of " << str << " returns " << s << endl;
684 /** Try to fit a string into a given horizontal space by ellipsizing it.
685 * @param cr Cairo context in which the text will be plotted.
687 * @param avail Available horizontal space.
688 * @return (Text, possibly ellipsized) and (horizontal size of text)
691 std::pair<std::string, double>
692 Gtkmm2ext::fit_to_pixels (cairo_t* cr, std::string name, double avail)
694 /* XXX hopefully there exists a more efficient way of doing this */
696 bool abbreviated = false;
700 cairo_text_extents_t ext;
701 cairo_text_extents (cr, name.c_str(), &ext);
703 if (ext.width < avail || name.length() <= 4) {
709 name = name.substr (0, name.length() - 4) + "...";
711 name = name.substr (0, name.length() - 3) + "...";
716 return std::make_pair (name, width);
720 Gtkmm2ext::left_aligned_label (string const & t)
722 Gtk::Label* l = new Gtk::Label (t);
723 l->set_alignment (0, 0.5);
728 make_null_tooltip (int, int, bool, const Glib::RefPtr<Gtk::Tooltip>& t)
730 t->set_tip_area (Gdk::Rectangle (0, 0, 0, 0));
734 /** Hackily arrange for the provided widget to have no tooltip,
735 * and also to stop any other widget from providing one while
736 * the mouse is over w.
739 Gtkmm2ext::set_no_tooltip_whatsoever (Gtk::Widget& w)
741 w.property_has_tooltip() = true;
742 w.signal_query_tooltip().connect (sigc::ptr_fun (make_null_tooltip));
746 Gtkmm2ext::enable_tooltips ()
748 gtk_rc_parse_string ("gtk-enable-tooltips = 1");
752 Gtkmm2ext::disable_tooltips ()
754 gtk_rc_parse_string ("gtk-enable-tooltips = 0");
758 Gtkmm2ext::event_type_string (int event_type)
760 switch (event_type) {
769 case GDK_MOTION_NOTIFY:
770 return "motion_notify";
771 case GDK_BUTTON_PRESS:
772 return "button_press";
773 case GDK_2BUTTON_PRESS:
774 return "2button_press";
775 case GDK_3BUTTON_PRESS:
776 return "3button_press";
777 case GDK_BUTTON_RELEASE:
778 return "button_release";
781 case GDK_KEY_RELEASE:
782 return "key_release";
783 case GDK_ENTER_NOTIFY:
784 return "enter_notify";
785 case GDK_LEAVE_NOTIFY:
786 return "leave_notify";
787 case GDK_FOCUS_CHANGE:
788 return "focus_change";
795 case GDK_PROPERTY_NOTIFY:
796 return "property_notify";
797 case GDK_SELECTION_CLEAR:
798 return "selection_clear";
799 case GDK_SELECTION_REQUEST:
800 return "selection_request";
801 case GDK_SELECTION_NOTIFY:
802 return "selection_notify";
803 case GDK_PROXIMITY_IN:
804 return "proximity_in";
805 case GDK_PROXIMITY_OUT:
806 return "proximity_out";
811 case GDK_DRAG_MOTION:
812 return "drag_motion";
813 case GDK_DRAG_STATUS:
814 return "drag_status";
817 case GDK_DROP_FINISHED:
818 return "drop_finished";
819 case GDK_CLIENT_EVENT:
820 return "client_event";
821 case GDK_VISIBILITY_NOTIFY:
822 return "visibility_notify";
827 case GDK_WINDOW_STATE:
828 return "window_state";
831 case GDK_OWNER_CHANGE:
832 return "owner_change";
833 case GDK_GRAB_BROKEN:
834 return "grab_broken";