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);
49 Gtkmm2ext::get_ink_pixel_size (Glib::RefPtr<Pango::Layout> layout,
53 Pango::Rectangle ink_rect = layout->get_ink_extents ();
55 width = (ink_rect.get_width() + PANGO_SCALE / 2) / PANGO_SCALE;
56 height = (ink_rect.get_height() + PANGO_SCALE / 2) / PANGO_SCALE;
60 Gtkmm2ext::get_pixel_size (Glib::RefPtr<Pango::Layout> layout,
64 layout->get_pixel_size (width, height);
68 Gtkmm2ext::set_size_request_to_display_given_text (Gtk::Widget &w, const gchar *text,
69 gint hpadding, gint vpadding)
74 get_pixel_size (w.create_pango_layout (text), width, height);
75 w.set_size_request(width + hpadding, height + vpadding);
79 Gtkmm2ext::set_size_request_to_display_given_text (Gtk::Widget &w,
80 const std::vector<std::string>& strings,
81 gint hpadding, gint vpadding)
88 const vector<string>* to_use;
89 vector<string>::const_iterator i;
91 for (i = strings.begin(); i != strings.end(); ++i) {
92 if ((*i).find_first_of ("gy") != string::npos) {
93 /* contains a descender */
98 if (i == strings.end()) {
99 /* make a copy of the strings then add one that has a descender */
101 copy.push_back ("g");
107 for (vector<string>::const_iterator i = to_use->begin(); i != to_use->end(); ++i) {
108 get_pixel_size (w.create_pango_layout (*i), width, height);
109 width_max = max(width_max,width);
110 height_max = max(height_max, height);
113 w.set_size_request(width_max + hpadding, height_max + vpadding);
117 demultiply_alpha (guint8 src,
120 /* cairo pixel buffer data contains RGB values with the alpha
121 values premultiplied.
123 GdkPixbuf pixel buffer data contains RGB values without the
126 this removes the alpha component from the cairo version and
127 returns the GdkPixbuf version.
129 return alpha ? ((guint (src) << 8) - src) / alpha : 0;
133 Gtkmm2ext::convert_bgra_to_rgba (guint8 const* src,
138 guint8 const* src_pixel = src;
139 guint8* dst_pixel = dst;
141 /* cairo pixel data is endian-dependent ARGB with A in the most significant 8 bits,
142 with premultipled alpha values (see preceding function)
144 GdkPixbuf pixel data is non-endian-dependent RGBA with R in the lowest addressable
145 8 bits, and non-premultiplied alpha values.
147 convert from the cairo values to the GdkPixbuf ones.
150 for (int y = 0; y < height; y++) {
151 for (int x = 0; x < width; x++) {
152 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
153 /* Cairo [ B G R A ] is actually [ B G R A ] in memory SOURCE
155 Pixbuf [ R G B A ] is actually [ R G B A ] in memory DEST
157 dst_pixel[0] = demultiply_alpha (src_pixel[2],
158 src_pixel[3]); // R [0] <= [ 2 ]
159 dst_pixel[1] = demultiply_alpha (src_pixel[1],
160 src_pixel[3]); // G [1] <= [ 1 ]
161 dst_pixel[2] = demultiply_alpha (src_pixel[0],
162 src_pixel[3]); // B [2] <= [ 0 ]
163 dst_pixel[3] = src_pixel[3]; // alpha
165 #elif G_BYTE_ORDER == G_BIG_ENDIAN
166 /* Cairo [ B G R A ] is actually [ A R G B ] in memory SOURCE
168 Pixbuf [ R G B A ] is actually [ R G B A ] in memory DEST
170 dst_pixel[0] = demultiply_alpha (src_pixel[1],
171 src_pixel[0]); // R [0] <= [ 1 ]
172 dst_pixel[1] = demultiply_alpha (src_pixel[2],
173 src_pixel[0]); // G [1] <= [ 2 ]
174 dst_pixel[2] = demultiply_alpha (src_pixel[3],
175 src_pixel[0]); // B [2] <= [ 3 ]
176 dst_pixel[3] = src_pixel[0]; // alpha
179 #error ardour does not currently support PDP-endianess
188 Glib::RefPtr<Gdk::Pixbuf>
189 Gtkmm2ext::pixbuf_from_string(const string& name, const Pango::FontDescription& font, int clip_width, int clip_height, Gdk::Color fg)
191 static Glib::RefPtr<Gdk::Pixbuf>* empty_pixbuf = 0;
194 if (empty_pixbuf == 0) {
195 empty_pixbuf = new Glib::RefPtr<Gdk::Pixbuf>;
196 *empty_pixbuf = Gdk::Pixbuf::create(Gdk::COLORSPACE_RGB, true, 8, clip_width, clip_height);
198 return *empty_pixbuf;
201 Glib::RefPtr<Gdk::Pixbuf> buf = Gdk::Pixbuf::create(Gdk::COLORSPACE_RGB, true, 8, clip_width, clip_height);
203 cairo_surface_t* surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, clip_width, clip_height);
204 cairo_t* cr = cairo_create (surface);
205 cairo_text_extents_t te;
207 cairo_set_source_rgba (cr, fg.get_red_p(), fg.get_green_p(), fg.get_blue_p(), 1.0);
208 cairo_select_font_face (cr, font.get_family().c_str(),
209 CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
210 cairo_set_font_size (cr, font.get_size() / Pango::SCALE);
211 cairo_text_extents (cr, name.c_str(), &te);
213 cairo_move_to (cr, 0.5, int (0.5 - te.height / 2 - te.y_bearing + clip_height / 2));
214 cairo_show_text (cr, name.c_str());
216 convert_bgra_to_rgba(cairo_image_surface_get_data (surface), buf->get_pixels(), clip_width, clip_height);
219 cairo_surface_destroy(surface);
225 Gtkmm2ext::set_popdown_strings (Gtk::ComboBoxText& cr, const vector<string>& strings)
227 vector<string>::const_iterator i;
231 for (i = strings.begin(); i != strings.end(); ++i) {
237 Gtkmm2ext::get_paned_handle (Gtk::Paned& paned)
239 return GTK_PANED(paned.gobj())->handle;
243 Gtkmm2ext::set_decoration (Gtk::Window* win, Gdk::WMDecoration decor)
245 win->get_window()->set_decorations (decor);
248 void Gtkmm2ext::set_treeview_header_as_default_label(Gtk::TreeViewColumn* c)
250 gtk_tree_view_column_set_widget( c->gobj(), GTK_WIDGET(0) );
254 Gtkmm2ext::detach_menu (Gtk::Menu& menu)
256 /* its possible for a Gtk::Menu to have no gobj() because it has
257 not yet been instantiated. Catch this and provide a safe
261 if (menu.get_attach_widget()) {
268 Gtkmm2ext::possibly_translate_keyval_to_make_legal_accelerator (uint32_t& keyval)
270 int fakekey = GDK_VoidSymbol;
274 case GDK_ISO_Left_Tab:
279 fakekey = GDK_uparrow;
283 fakekey = GDK_downarrow;
287 fakekey = GDK_rightarrow;
291 fakekey = GDK_leftarrow;
295 fakekey = GDK_3270_Enter;
306 if (fakekey != GDK_VoidSymbol) {
315 Gtkmm2ext::possibly_translate_legal_accelerator_to_real_key (uint32_t keyval)
350 Gtkmm2ext::physical_screen_height (Glib::RefPtr<Gdk::Window> win)
352 GdkScreen* scr = gdk_screen_get_default();
356 gint monitor = gdk_screen_get_monitor_at_window (scr, win->gobj());
357 gdk_screen_get_monitor_geometry (scr, monitor, &r);
360 return gdk_screen_get_height (scr);
365 Gtkmm2ext::physical_screen_width (Glib::RefPtr<Gdk::Window> win)
367 GdkScreen* scr = gdk_screen_get_default();
371 gint monitor = gdk_screen_get_monitor_at_window (scr, win->gobj());
372 gdk_screen_get_monitor_geometry (scr, monitor, &r);
375 return gdk_screen_get_width (scr);
380 Gtkmm2ext::container_clear (Gtk::Container& c)
382 list<Gtk::Widget*> children = c.get_children();
383 for (list<Gtk::Widget*>::iterator child = children.begin(); child != children.end(); ++child) {
389 Gtkmm2ext::rounded_rectangle (Cairo::RefPtr<Cairo::Context> context, double x, double y, double w, double h, double r)
391 rounded_rectangle (context->cobj(), x, y, w, h, r);
394 Gtkmm2ext::rounded_top_rectangle (Cairo::RefPtr<Cairo::Context> context, double x, double y, double w, double h, double r)
396 rounded_top_rectangle (context->cobj(), x, y, w, h, r);
399 Gtkmm2ext::rounded_top_left_rectangle (Cairo::RefPtr<Cairo::Context> context, double x, double y, double w, double h, double r)
401 rounded_top_left_rectangle (context->cobj(), x, y, w, h, r);
404 Gtkmm2ext::rounded_top_right_rectangle (Cairo::RefPtr<Cairo::Context> context, double x, double y, double w, double h, double r)
406 rounded_top_right_rectangle (context->cobj(), x, y, w, h, r);
409 Gtkmm2ext::rounded_top_half_rectangle (Cairo::RefPtr<Cairo::Context> context, double x, double y, double w, double h, double r)
411 rounded_top_half_rectangle (context->cobj(), x, y, w, h, r);
414 Gtkmm2ext::rounded_bottom_half_rectangle (Cairo::RefPtr<Cairo::Context> context, double x, double y, double w, double h, double r)
416 rounded_bottom_half_rectangle (context->cobj(), x, y, w, h, r);
420 Gtkmm2ext::rounded_left_half_rectangle (Cairo::RefPtr<Cairo::Context> context, double x, double y, double w, double h, double r)
422 rounded_left_half_rectangle (context->cobj(), x, y, w, h, r);
426 Gtkmm2ext::rounded_right_half_rectangle (Cairo::RefPtr<Cairo::Context> context, double x, double y, double w, double h, double r)
428 rounded_right_half_rectangle (context->cobj(), x, y, w, h, r);
432 Gtkmm2ext::rounded_rectangle (cairo_t* cr, double x, double y, double w, double h, double r)
434 double degrees = M_PI / 180.0;
436 cairo_new_sub_path (cr);
437 cairo_arc (cr, x + w - r, y + r, r, -90 * degrees, 0 * degrees); //tr
438 cairo_arc (cr, x + w - r, y + h - r, r, 0 * degrees, 90 * degrees); //br
439 cairo_arc (cr, x + r, y + h - r, r, 90 * degrees, 180 * degrees); //bl
440 cairo_arc (cr, x + r, y + r, r, 180 * degrees, 270 * degrees); //tl
441 cairo_close_path (cr);
445 Gtkmm2ext::rounded_left_half_rectangle (cairo_t* cr, double x, double y, double w, double h, double r)
447 double degrees = M_PI / 180.0;
449 cairo_new_sub_path (cr);
450 cairo_line_to (cr, x+w, y); // tr
451 cairo_line_to (cr, x+w, y + h); // br
452 cairo_arc (cr, x + r, y + h - r, r, 90 * degrees, 180 * degrees); //bl
453 cairo_arc (cr, x + r, y + r, r, 180 * degrees, 270 * degrees); //tl
454 cairo_close_path (cr);
458 Gtkmm2ext::rounded_right_half_rectangle (cairo_t* cr, double x, double y, double w, double h, double r)
460 double degrees = M_PI / 180.0;
462 cairo_new_sub_path (cr);
463 cairo_arc (cr, x + w - r, y + r, r, -90 * degrees, 0 * degrees); //tr
464 cairo_arc (cr, x + w - r, y + h - r, r, 0 * degrees, 90 * degrees); //br
465 cairo_line_to (cr, x, y + h); // bl
466 cairo_line_to (cr, x, y); // tl
467 cairo_close_path (cr);
471 Gtkmm2ext::rounded_top_half_rectangle (cairo_t* cr, double x, double y, double w, double h, double r)
473 double degrees = M_PI / 180.0;
475 cairo_new_sub_path (cr);
476 cairo_move_to (cr, x+w, y+h);
477 cairo_line_to (cr, x, y+h);
478 cairo_arc (cr, x + r, y + r, r, 180 * degrees, 270 * degrees); //tl
479 cairo_arc (cr, x + w - r, y + r, r, -90 * degrees, 0 * degrees); //tr
480 cairo_close_path (cr);
484 Gtkmm2ext::rounded_bottom_half_rectangle (cairo_t* cr, double x, double y, double w, double h, double r)
486 double degrees = M_PI / 180.0;
488 cairo_new_sub_path (cr);
489 cairo_move_to (cr, x, y);
490 cairo_line_to (cr, x+w, y);
491 cairo_arc (cr, x + w - r, y + h - r, r, 0 * degrees, 90 * degrees); //br
492 cairo_arc (cr, x + r, y + h - r, r, 90 * degrees, 180 * degrees); //bl
493 cairo_close_path (cr);
498 Gtkmm2ext::rounded_top_rectangle (cairo_t* cr, double x, double y, double w, double h, double r)
500 double degrees = M_PI / 180.0;
502 cairo_new_sub_path (cr);
503 cairo_move_to (cr, x+w, y+h);
504 cairo_line_to (cr, x, y+h);
505 cairo_arc (cr, x + r, y + r, r, 180 * degrees, 270 * degrees); //tl
506 cairo_arc (cr, x + w - r, y + r, r, -90 * degrees, 0 * degrees); //tr
507 cairo_close_path (cr);
511 Gtkmm2ext::rounded_top_left_rectangle (cairo_t* cr, double x, double y, double w, double h, double r)
519 cairo_move_to (cr, x+r,y); // Move to A
520 cairo_line_to (cr, x+w,y); // Straight line to B
521 cairo_line_to (cr, x+w,y+h); // Move to E
522 cairo_line_to (cr, x,y+h); // Line to F
523 cairo_line_to (cr, x,y+r); // Line to H
524 cairo_curve_to (cr, x,y,x,y,x+r,y); // Curve to A
528 Gtkmm2ext::rounded_top_right_rectangle (cairo_t* cr, double x, double y, double w, double h, double r)
536 cairo_move_to (cr, x,y); // Move to A
537 cairo_line_to (cr, x+w-r,y); // Straight line to B
538 cairo_curve_to (cr, x+w,y,x+w,y,x+w,y+r); // Curve to C, Control points are both at Q
539 cairo_line_to (cr, x+w,y+h); // Move to E
540 cairo_line_to (cr, x,y+h); // Line to F
541 cairo_line_to (cr, x,y); // Line to A
544 Glib::RefPtr<Gdk::Window>
545 Gtkmm2ext::window_to_draw_on (Gtk::Widget& w, Gtk::Widget** parent)
547 if (w.get_has_window()) {
548 return w.get_window();
551 (*parent) = w.get_parent();
554 if ((*parent)->get_has_window()) {
555 return (*parent)->get_window ();
557 (*parent) = (*parent)->get_parent ();
560 return Glib::RefPtr<Gdk::Window> ();
564 Gtkmm2ext::pixel_width (const string& str, Pango::FontDescription& font)
567 Glib::RefPtr<Pango::Layout> layout = foo.create_pango_layout ("");
569 layout->set_font_description (font);
570 layout->set_text (str);
573 Gtkmm2ext::get_ink_pixel_size (layout, width, height);
579 Gtkmm2ext::fit_to_pixels (const string& str, int pixel_width, Pango::FontDescription& font, int& actual_width, bool with_ellipses)
581 /* DECEMBER 2011: THIS PROTOTYPE OF fit_to_pixels() IS NOT USED
582 ANYWHERE AND HAS NOT BEEN TESTED.
585 Glib::RefPtr<Pango::Layout> layout = foo.create_pango_layout (str);
586 Glib::RefPtr<const Pango::LayoutLine> line;
588 layout->set_font_description (font);
589 layout->set_width (pixel_width * PANGO_SCALE);
592 layout->set_ellipsize (Pango::ELLIPSIZE_END);
594 layout->set_wrap (Pango::WRAP_CHAR);
597 line = layout->get_line (0);
599 /* XXX: might need special care to get the ellipsis character, not sure
603 string s = string (layout->get_text ().substr(line->get_start_index(), line->get_length()));
605 cerr << "fit to pixels of " << str << " returns " << s << endl;
611 /** Try to fit a string into a given horizontal space by ellipsizing it.
612 * @param cr Cairo context in which the text will be plotted.
614 * @param avail Available horizontal space.
615 * @return (Text, possibly ellipsized) and (horizontal size of text)
618 std::pair<std::string, double>
619 Gtkmm2ext::fit_to_pixels (cairo_t* cr, std::string name, double avail)
621 /* XXX hopefully there exists a more efficient way of doing this */
623 bool abbreviated = false;
627 cairo_text_extents_t ext;
628 cairo_text_extents (cr, name.c_str(), &ext);
630 if (ext.width < avail || name.length() <= 4) {
636 name = name.substr (0, name.length() - 4) + "...";
638 name = name.substr (0, name.length() - 3) + "...";
643 return std::make_pair (name, width);
647 Gtkmm2ext::left_aligned_label (string const & t)
649 Gtk::Label* l = new Gtk::Label (t);
650 l->set_alignment (0, 0.5);
655 make_null_tooltip (int, int, bool, const Glib::RefPtr<Gtk::Tooltip>& t)
657 t->set_tip_area (Gdk::Rectangle (0, 0, 0, 0));
661 /** Hackily arrange for the provided widget to have no tooltip,
662 * and also to stop any other widget from providing one while
663 * the mouse is over w.
666 Gtkmm2ext::set_no_tooltip_whatsoever (Gtk::Widget& w)
668 w.property_has_tooltip() = true;
669 w.signal_query_tooltip().connect (sigc::ptr_fun (make_null_tooltip));
673 Gtkmm2ext::enable_tooltips ()
675 gtk_rc_parse_string ("gtk-enable-tooltips = 1");
679 Gtkmm2ext::disable_tooltips ()
681 gtk_rc_parse_string ("gtk-enable-tooltips = 0");