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 <gtkmm2ext/utils.h>
27 #include <gtkmm/widget.h>
28 #include <gtkmm/button.h>
29 #include <gtkmm/window.h>
30 #include <gtkmm/paned.h>
31 #include <gtkmm/label.h>
32 #include <gtkmm/comboboxtext.h>
33 #include <gtkmm/tooltip.h>
40 Gtkmm2ext::init (const char* localedir)
43 (void) bindtextdomain(PACKAGE, localedir);
48 Gtkmm2ext::get_ink_pixel_size (Glib::RefPtr<Pango::Layout> layout,
52 Pango::Rectangle ink_rect = layout->get_ink_extents ();
54 width = (ink_rect.get_width() + PANGO_SCALE / 2) / PANGO_SCALE;
55 height = (ink_rect.get_height() + PANGO_SCALE / 2) / PANGO_SCALE;
59 get_pixel_size (Glib::RefPtr<Pango::Layout> layout,
63 layout->get_pixel_size (width, height);
67 Gtkmm2ext::set_size_request_to_display_given_text (Gtk::Widget &w, const gchar *text,
68 gint hpadding, gint vpadding)
73 get_pixel_size (w.create_pango_layout (text), width, height);
74 w.set_size_request(width + hpadding, height + vpadding);
78 Gtkmm2ext::set_size_request_to_display_given_text (Gtk::Widget &w,
79 const std::vector<std::string>& strings,
80 gint hpadding, gint vpadding)
87 const vector<string>* to_use;
88 vector<string>::const_iterator i;
90 for (i = strings.begin(); i != strings.end(); ++i) {
91 if ((*i).find_first_of ("gy") != string::npos) {
92 /* contains a descender */
97 if (i == strings.end()) {
98 /* make a copy of the strings then add one that has a descender */
100 copy.push_back ("g");
106 for (vector<string>::const_iterator i = to_use->begin(); i != to_use->end(); ++i) {
107 get_pixel_size (w.create_pango_layout (*i), width, height);
108 width_max = max(width_max,width);
109 height_max = max(height_max, height);
112 w.set_size_request(width_max + hpadding, height_max + vpadding);
116 demultiply_alpha (guint8 src,
119 /* cairo pixel buffer data contains RGB values with the alpha
120 values premultiplied.
122 GdkPixbuf pixel buffer data contains RGB values without the
125 this removes the alpha component from the cairo version and
126 returns the GdkPixbuf version.
128 return alpha ? ((guint (src) << 8) - src) / alpha : 0;
132 convert_bgra_to_rgba (guint8 const* src,
137 guint8 const* src_pixel = src;
138 guint8* dst_pixel = dst;
140 /* cairo pixel data is endian-dependent ARGB with A in the most significant 8 bits,
141 with premultipled alpha values (see preceding function)
143 GdkPixbuf pixel data is non-endian-dependent RGBA with R in the lowest addressable
144 8 bits, and non-premultiplied alpha values.
146 convert from the cairo values to the GdkPixbuf ones.
149 for (int y = 0; y < height; y++) {
150 for (int x = 0; x < width; x++) {
151 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
152 /* Cairo [ B G R A ] is actually [ B G R A ] in memory SOURCE
154 Pixbuf [ R G B A ] is actually [ R G B A ] in memory DEST
156 dst_pixel[0] = demultiply_alpha (src_pixel[2],
157 src_pixel[3]); // R [0] <= [ 2 ]
158 dst_pixel[1] = demultiply_alpha (src_pixel[1],
159 src_pixel[3]); // G [1] <= [ 1 ]
160 dst_pixel[2] = demultiply_alpha (src_pixel[0],
161 src_pixel[3]); // B [2] <= [ 0 ]
162 dst_pixel[3] = src_pixel[3]; // alpha
164 #elif G_BYTE_ORDER == G_BIG_ENDIAN
165 /* Cairo [ B G R A ] is actually [ A R G B ] in memory SOURCE
167 Pixbuf [ R G B A ] is actually [ R G B A ] in memory DEST
169 dst_pixel[0] = demultiply_alpha (src_pixel[1],
170 src_pixel[0]); // R [0] <= [ 1 ]
171 dst_pixel[1] = demultiply_alpha (src_pixel[2],
172 src_pixel[0]); // G [1] <= [ 2 ]
173 dst_pixel[2] = demultiply_alpha (src_pixel[3],
174 src_pixel[0]); // B [2] <= [ 3 ]
175 dst_pixel[3] = src_pixel[0]; // alpha
178 #error ardour does not currently support PDP-endianess
187 Glib::RefPtr<Gdk::Pixbuf>
188 Gtkmm2ext::pixbuf_from_string(const string& name, const Pango::FontDescription& font, int clip_width, int clip_height, Gdk::Color fg)
190 static Glib::RefPtr<Gdk::Pixbuf>* empty_pixbuf = 0;
193 if (empty_pixbuf == 0) {
194 empty_pixbuf = new Glib::RefPtr<Gdk::Pixbuf>;
195 *empty_pixbuf = Gdk::Pixbuf::create(Gdk::COLORSPACE_RGB, true, 8, clip_width, clip_height);
197 return *empty_pixbuf;
200 Glib::RefPtr<Gdk::Pixbuf> buf = Gdk::Pixbuf::create(Gdk::COLORSPACE_RGB, true, 8, clip_width, clip_height);
202 cairo_surface_t* surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, clip_width, clip_height);
203 cairo_t* cr = cairo_create (surface);
204 cairo_text_extents_t te;
206 cairo_set_source_rgba (cr, fg.get_red_p(), fg.get_green_p(), fg.get_blue_p(), 1.0);
207 cairo_select_font_face (cr, font.get_family().c_str(),
208 CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
209 cairo_set_font_size (cr, font.get_size() / Pango::SCALE);
210 cairo_text_extents (cr, name.c_str(), &te);
212 cairo_move_to (cr, 0.5, int (0.5 - te.height / 2 - te.y_bearing + clip_height / 2));
213 cairo_show_text (cr, name.c_str());
215 convert_bgra_to_rgba(cairo_image_surface_get_data (surface), buf->get_pixels(), clip_width, clip_height);
218 cairo_surface_destroy(surface);
224 Gtkmm2ext::set_popdown_strings (Gtk::ComboBoxText& cr, const vector<string>& strings)
226 vector<string>::const_iterator i;
230 for (i = strings.begin(); i != strings.end(); ++i) {
236 Gtkmm2ext::get_paned_handle (Gtk::Paned& paned)
238 return GTK_PANED(paned.gobj())->handle;
242 Gtkmm2ext::set_decoration (Gtk::Window* win, Gdk::WMDecoration decor)
244 win->get_window()->set_decorations (decor);
247 void Gtkmm2ext::set_treeview_header_as_default_label(Gtk::TreeViewColumn* c)
249 gtk_tree_view_column_set_widget( c->gobj(), GTK_WIDGET(0) );
253 Gtkmm2ext::detach_menu (Gtk::Menu& menu)
255 /* its possible for a Gtk::Menu to have no gobj() because it has
256 not yet been instantiated. Catch this and provide a safe
260 if (menu.get_attach_widget()) {
267 Gtkmm2ext::possibly_translate_keyval_to_make_legal_accelerator (uint32_t& keyval)
269 int fakekey = GDK_VoidSymbol;
273 case GDK_ISO_Left_Tab:
278 fakekey = GDK_uparrow;
282 fakekey = GDK_downarrow;
286 fakekey = GDK_rightarrow;
290 fakekey = GDK_leftarrow;
294 fakekey = GDK_3270_Enter;
305 if (fakekey != GDK_VoidSymbol) {
314 Gtkmm2ext::possibly_translate_legal_accelerator_to_real_key (uint32_t keyval)
349 Gtkmm2ext::physical_screen_height (Glib::RefPtr<Gdk::Window> win)
351 GdkScreen* scr = gdk_screen_get_default();
355 gint monitor = gdk_screen_get_monitor_at_window (scr, win->gobj());
356 gdk_screen_get_monitor_geometry (scr, monitor, &r);
359 return gdk_screen_get_height (scr);
364 Gtkmm2ext::physical_screen_width (Glib::RefPtr<Gdk::Window> win)
366 GdkScreen* scr = gdk_screen_get_default();
370 gint monitor = gdk_screen_get_monitor_at_window (scr, win->gobj());
371 gdk_screen_get_monitor_geometry (scr, monitor, &r);
374 return gdk_screen_get_width (scr);
379 Gtkmm2ext::container_clear (Gtk::Container& c)
381 list<Gtk::Widget*> children = c.get_children();
382 for (list<Gtk::Widget*>::iterator child = children.begin(); child != children.end(); ++child) {
388 Gtkmm2ext::rounded_rectangle (Cairo::RefPtr<Cairo::Context> context, double x, double y, double w, double h, double r)
390 rounded_rectangle (context->cobj(), x, y, w, h, r);
393 Gtkmm2ext::rounded_top_rectangle (Cairo::RefPtr<Cairo::Context> context, double x, double y, double w, double h, double r)
395 rounded_top_rectangle (context->cobj(), x, y, w, h, r);
398 Gtkmm2ext::rounded_top_left_rectangle (Cairo::RefPtr<Cairo::Context> context, double x, double y, double w, double h, double r)
400 rounded_top_left_rectangle (context->cobj(), x, y, w, h, r);
403 Gtkmm2ext::rounded_top_right_rectangle (Cairo::RefPtr<Cairo::Context> context, double x, double y, double w, double h, double r)
405 rounded_top_right_rectangle (context->cobj(), x, y, w, h, r);
408 Gtkmm2ext::rounded_top_half_rectangle (Cairo::RefPtr<Cairo::Context> context, double x, double y, double w, double h, double r)
410 rounded_top_half_rectangle (context->cobj(), x, y, w, h, r);
413 Gtkmm2ext::rounded_bottom_half_rectangle (Cairo::RefPtr<Cairo::Context> context, double x, double y, double w, double h, double r)
415 rounded_bottom_half_rectangle (context->cobj(), x, y, w, h, r);
418 Gtkmm2ext::rounded_right_half_rectangle (Cairo::RefPtr<Cairo::Context> context, double x, double y, double w, double h, double r)
420 rounded_right_half_rectangle (context->cobj(), x, y, w, h, r);
424 Gtkmm2ext::rounded_rectangle (cairo_t* cr, double x, double y, double w, double h, double r)
426 double degrees = M_PI / 180.0;
428 cairo_new_sub_path (cr);
429 cairo_arc (cr, x + w - r, y + r, r, -90 * degrees, 0 * degrees); //tr
430 cairo_arc (cr, x + w - r, y + h - r, r, 0 * degrees, 90 * degrees); //br
431 cairo_arc (cr, x + r, y + h - r, r, 90 * degrees, 180 * degrees); //bl
432 cairo_arc (cr, x + r, y + r, r, 180 * degrees, 270 * degrees); //tl
433 cairo_close_path (cr);
437 Gtkmm2ext::rounded_right_half_rectangle (cairo_t* cr, double x, double y, double w, double h, double r)
439 double degrees = M_PI / 180.0;
441 cairo_new_sub_path (cr);
442 cairo_arc (cr, x + w - r, y + r, r, -90 * degrees, 0 * degrees); //tr
443 cairo_arc (cr, x + w - r, y + h - r, r, 0 * degrees, 90 * degrees); //br
444 cairo_line_to (cr, x, y + h); // bl
445 cairo_line_to (cr, x, y); // tl
446 cairo_close_path (cr);
450 Gtkmm2ext::rounded_top_half_rectangle (cairo_t* cr, double x, double y, double w, double h, double r)
452 double degrees = M_PI / 180.0;
454 cairo_new_sub_path (cr);
455 cairo_move_to (cr, x+w, y+h);
456 cairo_line_to (cr, x, y+h);
457 cairo_arc (cr, x + r, y + r, r, 180 * degrees, 270 * degrees); //tl
458 cairo_arc (cr, x + w - r, y + r, r, -90 * degrees, 0 * degrees); //tr
459 cairo_close_path (cr);
463 Gtkmm2ext::rounded_bottom_half_rectangle (cairo_t* cr, double x, double y, double w, double h, double r)
465 double degrees = M_PI / 180.0;
467 cairo_new_sub_path (cr);
468 cairo_move_to (cr, x, y);
469 cairo_line_to (cr, x+w, y);
470 cairo_arc (cr, x + w - r, y + h - r, r, 0 * degrees, 90 * degrees); //br
471 cairo_arc (cr, x + r, y + h - r, r, 90 * degrees, 180 * degrees); //bl
472 cairo_close_path (cr);
477 Gtkmm2ext::rounded_top_rectangle (cairo_t* cr, double x, double y, double w, double h, double r)
479 double degrees = M_PI / 180.0;
481 cairo_new_sub_path (cr);
482 cairo_move_to (cr, x+w, y+h);
483 cairo_line_to (cr, x, y+h);
484 cairo_arc (cr, x + r, y + r, r, 180 * degrees, 270 * degrees); //tl
485 cairo_arc (cr, x + w - r, y + r, r, -90 * degrees, 0 * degrees); //tr
486 cairo_close_path (cr);
490 Gtkmm2ext::rounded_top_left_rectangle (cairo_t* cr, double x, double y, double w, double h, double r)
498 cairo_move_to (cr, x+r,y); // Move to A
499 cairo_line_to (cr, x+w,y); // Straight line to B
500 cairo_line_to (cr, x+w,y+h); // Move to E
501 cairo_line_to (cr, x,y+h); // Line to F
502 cairo_line_to (cr, x,y+r); // Line to H
503 cairo_curve_to (cr, x,y,x,y,x+r,y); // Curve to A
507 Gtkmm2ext::rounded_top_right_rectangle (cairo_t* cr, double x, double y, double w, double h, double r)
515 cairo_move_to (cr, x,y); // Move to A
516 cairo_line_to (cr, x+w-r,y); // Straight line to B
517 cairo_curve_to (cr, x+w,y,x+w,y,x+w,y+r); // Curve to C, Control points are both at Q
518 cairo_line_to (cr, x+w,y+h); // Move to E
519 cairo_line_to (cr, x,y+h); // Line to F
520 cairo_line_to (cr, x,y); // Line to A
523 Glib::RefPtr<Gdk::Window>
524 Gtkmm2ext::window_to_draw_on (Gtk::Widget& w, Gtk::Widget** parent)
526 if (w.get_has_window()) {
527 return w.get_window();
530 (*parent) = w.get_parent();
533 if ((*parent)->get_has_window()) {
534 return (*parent)->get_window ();
536 (*parent) = (*parent)->get_parent ();
539 return Glib::RefPtr<Gdk::Window> ();
543 Gtkmm2ext::pixel_width (const string& str, Pango::FontDescription& font)
546 Glib::RefPtr<Pango::Layout> layout = foo.create_pango_layout ("");
548 layout->set_font_description (font);
549 layout->set_text (str);
552 Gtkmm2ext::get_ink_pixel_size (layout, width, height);
558 Gtkmm2ext::fit_to_pixels (const string& str, int pixel_width, Pango::FontDescription& font, int& actual_width, bool with_ellipses)
560 /* DECEMBER 2011: THIS PROTOTYPE OF fit_to_pixels() IS NOT USED
561 ANYWHERE AND HAS NOT BEEN TESTED.
564 Glib::RefPtr<Pango::Layout> layout = foo.create_pango_layout (str);
565 Glib::RefPtr<const Pango::LayoutLine> line;
567 layout->set_font_description (font);
568 layout->set_width (pixel_width * PANGO_SCALE);
571 layout->set_ellipsize (Pango::ELLIPSIZE_END);
573 layout->set_wrap (Pango::WRAP_CHAR);
576 line = layout->get_line (0);
578 /* XXX: might need special care to get the ellipsis character, not sure
582 string s = string (layout->get_text ().substr(line->get_start_index(), line->get_length()));
584 cerr << "fit to pixels of " << str << " returns " << s << endl;
590 /** Try to fit a string into a given horizontal space by ellipsizing it.
591 * @param cr Cairo context in which the text will be plotted.
593 * @param avail Available horizontal space.
594 * @return (Text, possibly ellipsized) and (horizontal size of text)
597 std::pair<std::string, double>
598 Gtkmm2ext::fit_to_pixels (cairo_t* cr, std::string name, double avail)
600 /* XXX hopefully there exists a more efficient way of doing this */
602 bool abbreviated = false;
606 cairo_text_extents_t ext;
607 cairo_text_extents (cr, name.c_str(), &ext);
609 if (ext.width < avail || name.length() <= 4) {
615 name = name.substr (0, name.length() - 4) + "...";
617 name = name.substr (0, name.length() - 3) + "...";
622 return std::make_pair (name, width);
626 Gtkmm2ext::left_aligned_label (string const & t)
628 Gtk::Label* l = new Gtk::Label (t);
629 l->set_alignment (0, 0.5);
634 make_null_tooltip (int, int, bool, const Glib::RefPtr<Gtk::Tooltip>& t)
636 t->set_tip_area (Gdk::Rectangle (0, 0, 0, 0));
640 /** Hackily arrange for the provided widget to have no tooltip,
641 * and also to stop any other widget from providing one while
642 * the mouse is over w.
645 Gtkmm2ext::set_no_tooltip_whatsoever (Gtk::Widget& w)
647 w.property_has_tooltip() = true;
648 w.signal_query_tooltip().connect (sigc::ptr_fun (make_null_tooltip));
652 Gtkmm2ext::enable_tooltips ()
654 gtk_rc_parse_string ("gtk-enable-tooltips = 1");
658 Gtkmm2ext::disable_tooltips ()
660 gtk_rc_parse_string ("gtk-enable-tooltips = 0");