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>
42 // Necessary for gettext
43 (void) bindtextdomain(PACKAGE, LOCALEDIR);
47 Gtkmm2ext::get_ink_pixel_size (Glib::RefPtr<Pango::Layout> layout,
51 Pango::Rectangle ink_rect = layout->get_ink_extents ();
53 width = (ink_rect.get_width() + PANGO_SCALE / 2) / PANGO_SCALE;
54 height = (ink_rect.get_height() + PANGO_SCALE / 2) / PANGO_SCALE;
58 get_pixel_size (Glib::RefPtr<Pango::Layout> layout,
62 layout->get_pixel_size (width, height);
66 Gtkmm2ext::set_size_request_to_display_given_text (Gtk::Widget &w, const gchar *text,
67 gint hpadding, gint vpadding)
72 get_pixel_size (w.create_pango_layout (text), width, height);
73 w.set_size_request(width + hpadding, height + vpadding);
77 Gtkmm2ext::set_size_request_to_display_given_text (Gtk::Widget &w,
78 const std::vector<std::string>& strings,
79 gint hpadding, gint vpadding)
86 const vector<string>* to_use;
87 vector<string>::const_iterator i;
89 for (i = strings.begin(); i != strings.end(); ++i) {
90 if ((*i).find_first_of ("gy") != string::npos) {
91 /* contains a descender */
96 if (i == strings.end()) {
97 /* make a copy of the strings then add one that has a descender */
105 for (vector<string>::const_iterator i = to_use->begin(); i != to_use->end(); ++i) {
106 get_pixel_size (w.create_pango_layout (*i), width, height);
107 width_max = max(width_max,width);
108 height_max = max(height_max, height);
111 w.set_size_request(width_max + hpadding, height_max + vpadding);
115 demultiply_alpha (guint8 src,
118 /* cairo pixel buffer data contains RGB values with the alpha
119 values premultiplied.
121 GdkPixbuf pixel buffer data contains RGB values without the
124 this removes the alpha component from the cairo version and
125 returns the GdkPixbuf version.
127 return alpha ? ((guint (src) << 8) - src) / alpha : 0;
131 convert_bgra_to_rgba (guint8 const* src,
136 guint8 const* src_pixel = src;
137 guint8* dst_pixel = dst;
139 /* cairo pixel data is endian-dependent ARGB with A in the most significant 8 bits,
140 with premultipled alpha values (see preceding function)
142 GdkPixbuf pixel data is non-endian-dependent RGBA with R in the lowest addressable
143 8 bits, and non-premultiplied alpha values.
145 convert from the cairo values to the GdkPixbuf ones.
148 for (int y = 0; y < height; y++) {
149 for (int x = 0; x < width; x++) {
150 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
151 /* Cairo [ B G R A ] is actually [ B G R A ] in memory SOURCE
153 Pixbuf [ R G B A ] is actually [ R G B A ] in memory DEST
155 dst_pixel[0] = demultiply_alpha (src_pixel[2],
156 src_pixel[3]); // R [0] <= [ 2 ]
157 dst_pixel[1] = demultiply_alpha (src_pixel[1],
158 src_pixel[3]); // G [1] <= [ 1 ]
159 dst_pixel[2] = demultiply_alpha (src_pixel[0],
160 src_pixel[3]); // B [2] <= [ 0 ]
161 dst_pixel[3] = src_pixel[3]; // alpha
163 #elif G_BYTE_ORDER == G_BIG_ENDIAN
164 /* Cairo [ B G R A ] is actually [ A R G B ] in memory SOURCE
166 Pixbuf [ R G B A ] is actually [ R G B A ] in memory DEST
168 dst_pixel[0] = demultiply_alpha (src_pixel[1],
169 src_pixel[0]); // R [0] <= [ 1 ]
170 dst_pixel[1] = demultiply_alpha (src_pixel[2],
171 src_pixel[0]); // G [1] <= [ 2 ]
172 dst_pixel[2] = demultiply_alpha (src_pixel[3],
173 src_pixel[0]); // B [2] <= [ 3 ]
174 dst_pixel[3] = src_pixel[0]; // alpha
177 #error ardour does not currently support PDP-endianess
186 Glib::RefPtr<Gdk::Pixbuf>
187 Gtkmm2ext::pixbuf_from_string(const string& name, const Pango::FontDescription& font, int clip_width, int clip_height, Gdk::Color fg)
189 static Glib::RefPtr<Gdk::Pixbuf>* empty_pixbuf = 0;
192 if (empty_pixbuf == 0) {
193 empty_pixbuf = new Glib::RefPtr<Gdk::Pixbuf>;
194 *empty_pixbuf = Gdk::Pixbuf::create(Gdk::COLORSPACE_RGB, true, 8, clip_width, clip_height);
196 return *empty_pixbuf;
199 Glib::RefPtr<Gdk::Pixbuf> buf = Gdk::Pixbuf::create(Gdk::COLORSPACE_RGB, true, 8, clip_width, clip_height);
201 cairo_surface_t* surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, clip_width, clip_height);
202 cairo_t* cr = cairo_create (surface);
203 cairo_text_extents_t te;
205 cairo_set_source_rgba (cr, fg.get_red_p(), fg.get_green_p(), fg.get_blue_p(), 1.0);
206 cairo_select_font_face (cr, font.get_family().c_str(),
207 CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
208 cairo_set_font_size (cr, font.get_size() / Pango::SCALE);
209 cairo_text_extents (cr, name.c_str(), &te);
211 cairo_move_to (cr, 0.5, int (0.5 - te.height / 2 - te.y_bearing + clip_height / 2));
212 cairo_show_text (cr, name.c_str());
214 convert_bgra_to_rgba(cairo_image_surface_get_data (surface), buf->get_pixels(), clip_width, clip_height);
217 cairo_surface_destroy(surface);
223 Gtkmm2ext::set_popdown_strings (Gtk::ComboBoxText& cr, const vector<string>& strings)
225 vector<string>::const_iterator i;
229 for (i = strings.begin(); i != strings.end(); ++i) {
235 Gtkmm2ext::get_paned_handle (Gtk::Paned& paned)
237 return GTK_PANED(paned.gobj())->handle;
241 Gtkmm2ext::set_decoration (Gtk::Window* win, Gdk::WMDecoration decor)
243 win->get_window()->set_decorations (decor);
246 void Gtkmm2ext::set_treeview_header_as_default_label(Gtk::TreeViewColumn* c)
248 gtk_tree_view_column_set_widget( c->gobj(), GTK_WIDGET(0) );
252 Gtkmm2ext::detach_menu (Gtk::Menu& menu)
254 /* its possible for a Gtk::Menu to have no gobj() because it has
255 not yet been instantiated. Catch this and provide a safe
259 if (menu.get_attach_widget()) {
266 Gtkmm2ext::possibly_translate_keyval_to_make_legal_accelerator (uint32_t& keyval)
268 int fakekey = GDK_VoidSymbol;
272 case GDK_ISO_Left_Tab:
277 fakekey = GDK_uparrow;
281 fakekey = GDK_downarrow;
285 fakekey = GDK_rightarrow;
289 fakekey = GDK_leftarrow;
293 fakekey = GDK_3270_Enter;
304 if (fakekey != GDK_VoidSymbol) {
313 Gtkmm2ext::possibly_translate_legal_accelerator_to_real_key (uint32_t keyval)
348 Gtkmm2ext::physical_screen_height (Glib::RefPtr<Gdk::Window> win)
350 GdkScreen* scr = gdk_screen_get_default();
354 gint monitor = gdk_screen_get_monitor_at_window (scr, win->gobj());
355 gdk_screen_get_monitor_geometry (scr, monitor, &r);
358 return gdk_screen_get_height (scr);
363 Gtkmm2ext::physical_screen_width (Glib::RefPtr<Gdk::Window> win)
365 GdkScreen* scr = gdk_screen_get_default();
369 gint monitor = gdk_screen_get_monitor_at_window (scr, win->gobj());
370 gdk_screen_get_monitor_geometry (scr, monitor, &r);
373 return gdk_screen_get_width (scr);
378 Gtkmm2ext::container_clear (Gtk::Container& c)
380 list<Gtk::Widget*> children = c.get_children();
381 for (list<Gtk::Widget*>::iterator child = children.begin(); child != children.end(); ++child) {
387 Gtkmm2ext::rounded_rectangle (Cairo::RefPtr<Cairo::Context> context, double x, double y, double w, double h, double r)
389 rounded_rectangle (context->cobj(), x, y, w, h, r);
392 Gtkmm2ext::rounded_top_rectangle (Cairo::RefPtr<Cairo::Context> context, double x, double y, double w, double h, double r)
394 rounded_top_rectangle (context->cobj(), x, y, w, h, r);
397 Gtkmm2ext::rounded_top_left_rectangle (Cairo::RefPtr<Cairo::Context> context, double x, double y, double w, double h, double r)
399 rounded_top_left_rectangle (context->cobj(), x, y, w, h, r);
402 Gtkmm2ext::rounded_top_right_rectangle (Cairo::RefPtr<Cairo::Context> context, double x, double y, double w, double h, double r)
404 rounded_top_right_rectangle (context->cobj(), x, y, w, h, r);
407 Gtkmm2ext::rounded_top_half_rectangle (Cairo::RefPtr<Cairo::Context> context, double x, double y, double w, double h, double r)
409 rounded_top_half_rectangle (context->cobj(), x, y, w, h, r);
412 Gtkmm2ext::rounded_bottom_half_rectangle (Cairo::RefPtr<Cairo::Context> context, double x, double y, double w, double h, double r)
414 rounded_bottom_half_rectangle (context->cobj(), x, y, w, h, r);
417 Gtkmm2ext::rounded_right_half_rectangle (Cairo::RefPtr<Cairo::Context> context, double x, double y, double w, double h, double r)
419 rounded_right_half_rectangle (context->cobj(), x, y, w, h, r);
423 Gtkmm2ext::rounded_rectangle (cairo_t* cr, double x, double y, double w, double h, double r)
425 double degrees = M_PI / 180.0;
427 cairo_new_sub_path (cr);
428 cairo_arc (cr, x + w - r, y + r, r, -90 * degrees, 0 * degrees); //tr
429 cairo_arc (cr, x + w - r, y + h - r, r, 0 * degrees, 90 * degrees); //br
430 cairo_arc (cr, x + r, y + h - r, r, 90 * degrees, 180 * degrees); //bl
431 cairo_arc (cr, x + r, y + r, r, 180 * degrees, 270 * degrees); //tl
432 cairo_close_path (cr);
436 Gtkmm2ext::rounded_right_half_rectangle (cairo_t* cr, double x, double y, double w, double h, double r)
438 double degrees = M_PI / 180.0;
440 cairo_new_sub_path (cr);
441 cairo_arc (cr, x + w - r, y + r, r, -90 * degrees, 0 * degrees); //tr
442 cairo_arc (cr, x + w - r, y + h - r, r, 0 * degrees, 90 * degrees); //br
443 cairo_line_to (cr, x, y + h); // bl
444 cairo_line_to (cr, x, y); // tl
445 cairo_close_path (cr);
449 Gtkmm2ext::rounded_top_half_rectangle (cairo_t* cr, double x, double y, double w, double h, double r)
451 double degrees = M_PI / 180.0;
453 cairo_new_sub_path (cr);
454 cairo_move_to (cr, x+w, y+h);
455 cairo_line_to (cr, x, y+h);
456 cairo_arc (cr, x + r, y + r, r, 180 * degrees, 270 * degrees); //tl
457 cairo_arc (cr, x + w - r, y + r, r, -90 * degrees, 0 * degrees); //tr
458 cairo_close_path (cr);
462 Gtkmm2ext::rounded_bottom_half_rectangle (cairo_t* cr, double x, double y, double w, double h, double r)
464 double degrees = M_PI / 180.0;
466 cairo_new_sub_path (cr);
467 cairo_move_to (cr, x, y);
468 cairo_line_to (cr, x+w, y);
469 cairo_arc (cr, x + w - r, y + h - r, r, 0 * degrees, 90 * degrees); //br
470 cairo_arc (cr, x + r, y + h - r, r, 90 * degrees, 180 * degrees); //bl
471 cairo_close_path (cr);
476 Gtkmm2ext::rounded_top_rectangle (cairo_t* cr, double x, double y, double w, double h, double r)
478 double degrees = M_PI / 180.0;
480 cairo_new_sub_path (cr);
481 cairo_move_to (cr, x+w, y+h);
482 cairo_line_to (cr, x, y+h);
483 cairo_arc (cr, x + r, y + r, r, 180 * degrees, 270 * degrees); //tl
484 cairo_arc (cr, x + w - r, y + r, r, -90 * degrees, 0 * degrees); //tr
485 cairo_close_path (cr);
489 Gtkmm2ext::rounded_top_left_rectangle (cairo_t* cr, double x, double y, double w, double h, double r)
497 cairo_move_to (cr, x+r,y); // Move to A
498 cairo_line_to (cr, x+w,y); // Straight line to B
499 cairo_line_to (cr, x+w,y+h); // Move to E
500 cairo_line_to (cr, x,y+h); // Line to F
501 cairo_line_to (cr, x,y+r); // Line to H
502 cairo_curve_to (cr, x,y,x,y,x+r,y); // Curve to A
506 Gtkmm2ext::rounded_top_right_rectangle (cairo_t* cr, double x, double y, double w, double h, double r)
514 cairo_move_to (cr, x,y); // Move to A
515 cairo_line_to (cr, x+w-r,y); // Straight line to B
516 cairo_curve_to (cr, x+w,y,x+w,y,x+w,y+r); // Curve to C, Control points are both at Q
517 cairo_line_to (cr, x+w,y+h); // Move to E
518 cairo_line_to (cr, x,y+h); // Line to F
519 cairo_line_to (cr, x,y); // Line to A
522 Glib::RefPtr<Gdk::Window>
523 Gtkmm2ext::window_to_draw_on (Gtk::Widget& w, Gtk::Widget** parent)
525 if (w.get_has_window()) {
526 return w.get_window();
529 (*parent) = w.get_parent();
532 if ((*parent)->get_has_window()) {
533 return (*parent)->get_window ();
535 (*parent) = (*parent)->get_parent ();
538 return Glib::RefPtr<Gdk::Window> ();
542 Gtkmm2ext::pixel_width (const string& str, Pango::FontDescription& font)
545 Glib::RefPtr<Pango::Layout> layout = foo.create_pango_layout ("");
547 layout->set_font_description (font);
548 layout->set_text (str);
551 Gtkmm2ext::get_ink_pixel_size (layout, width, height);
557 Gtkmm2ext::fit_to_pixels (const string& str, int pixel_width, Pango::FontDescription& font, int& actual_width, bool with_ellipses)
559 /* DECEMBER 2011: THIS PROTOTYPE OF fit_to_pixels() IS NOT USED
560 ANYWHERE AND HAS NOT BEEN TESTED.
563 Glib::RefPtr<Pango::Layout> layout = foo.create_pango_layout (str);
564 Glib::RefPtr<const Pango::LayoutLine> line;
566 layout->set_font_description (font);
567 layout->set_width (pixel_width * PANGO_SCALE);
570 layout->set_ellipsize (Pango::ELLIPSIZE_END);
572 layout->set_wrap (Pango::WRAP_CHAR);
575 line = layout->get_line (0);
577 /* XXX: might need special care to get the ellipsis character, not sure
581 string s = string (layout->get_text ().substr(line->get_start_index(), line->get_length()));
583 cerr << "fit to pixels of " << str << " returns " << s << endl;
589 /** Try to fit a string into a given horizontal space by ellipsizing it.
590 * @param cr Cairo context in which the text will be plotted.
592 * @param avail Available horizontal space.
593 * @return (Text, possibly ellipsized) and (horizontal size of text)
596 std::pair<std::string, double>
597 Gtkmm2ext::fit_to_pixels (cairo_t* cr, std::string name, double avail)
599 /* XXX hopefully there exists a more efficient way of doing this */
601 bool abbreviated = false;
605 cairo_text_extents_t ext;
606 cairo_text_extents (cr, name.c_str(), &ext);
608 if (ext.width < avail || name.length() <= 4) {
614 name = name.substr (0, name.length() - 4) + "...";
616 name = name.substr (0, name.length() - 3) + "...";
621 return std::make_pair (name, width);
625 Gtkmm2ext::left_aligned_label (string const & t)
627 Gtk::Label* l = new Gtk::Label (t);
628 l->set_alignment (0, 0.5);
633 make_null_tooltip (int, int, bool, const Glib::RefPtr<Gtk::Tooltip>& t)
635 t->set_tip_area (Gdk::Rectangle (0, 0, 0, 0));
639 /** Hackily arrange for the provided widget to have no tooltip,
640 * and also to stop any other widget from providing one while
641 * the mouse is over w.
644 Gtkmm2ext::set_no_tooltip_whatsoever (Gtk::Widget& w)
646 w.property_has_tooltip() = true;
647 w.signal_query_tooltip().connect (sigc::ptr_fun (make_null_tooltip));
651 Gtkmm2ext::enable_tooltips ()
653 gtk_rc_parse_string ("gtk-enable-tooltips = 1");
657 Gtkmm2ext::disable_tooltips ()
659 gtk_rc_parse_string ("gtk-enable-tooltips = 0");