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);
419 Gtkmm2ext::rounded_right_half_rectangle (Cairo::RefPtr<Cairo::Context> context, double x, double y, double w, double h, double r)
421 rounded_right_half_rectangle (context->cobj(), x, y, w, h, r);
425 Gtkmm2ext::rounded_rectangle (cairo_t* cr, double x, double y, double w, double h, double r)
427 double degrees = M_PI / 180.0;
429 cairo_new_sub_path (cr);
430 cairo_arc (cr, x + w - r, y + r, r, -90 * degrees, 0 * degrees); //tr
431 cairo_arc (cr, x + w - r, y + h - r, r, 0 * degrees, 90 * degrees); //br
432 cairo_arc (cr, x + r, y + h - r, r, 90 * degrees, 180 * degrees); //bl
433 cairo_arc (cr, x + r, y + r, r, 180 * degrees, 270 * degrees); //tl
434 cairo_close_path (cr);
438 Gtkmm2ext::rounded_right_half_rectangle (cairo_t* cr, double x, double y, double w, double h, double r)
440 double degrees = M_PI / 180.0;
442 cairo_new_sub_path (cr);
443 cairo_arc (cr, x + w - r, y + r, r, -90 * degrees, 0 * degrees); //tr
444 cairo_arc (cr, x + w - r, y + h - r, r, 0 * degrees, 90 * degrees); //br
445 cairo_line_to (cr, x, y + h); // bl
446 cairo_line_to (cr, x, y); // tl
447 cairo_close_path (cr);
451 Gtkmm2ext::rounded_top_half_rectangle (cairo_t* cr, double x, double y, double w, double h, double r)
453 double degrees = M_PI / 180.0;
455 cairo_new_sub_path (cr);
456 cairo_move_to (cr, x+w, y+h);
457 cairo_line_to (cr, x, y+h);
458 cairo_arc (cr, x + r, y + r, r, 180 * degrees, 270 * degrees); //tl
459 cairo_arc (cr, x + w - r, y + r, r, -90 * degrees, 0 * degrees); //tr
460 cairo_close_path (cr);
464 Gtkmm2ext::rounded_bottom_half_rectangle (cairo_t* cr, double x, double y, double w, double h, double r)
466 double degrees = M_PI / 180.0;
468 cairo_new_sub_path (cr);
469 cairo_move_to (cr, x, y);
470 cairo_line_to (cr, x+w, y);
471 cairo_arc (cr, x + w - r, y + h - r, r, 0 * degrees, 90 * degrees); //br
472 cairo_arc (cr, x + r, y + h - r, r, 90 * degrees, 180 * degrees); //bl
473 cairo_close_path (cr);
478 Gtkmm2ext::rounded_top_rectangle (cairo_t* cr, double x, double y, double w, double h, double r)
480 double degrees = M_PI / 180.0;
482 cairo_new_sub_path (cr);
483 cairo_move_to (cr, x+w, y+h);
484 cairo_line_to (cr, x, y+h);
485 cairo_arc (cr, x + r, y + r, r, 180 * degrees, 270 * degrees); //tl
486 cairo_arc (cr, x + w - r, y + r, r, -90 * degrees, 0 * degrees); //tr
487 cairo_close_path (cr);
491 Gtkmm2ext::rounded_top_left_rectangle (cairo_t* cr, double x, double y, double w, double h, double r)
499 cairo_move_to (cr, x+r,y); // Move to A
500 cairo_line_to (cr, x+w,y); // Straight line to B
501 cairo_line_to (cr, x+w,y+h); // Move to E
502 cairo_line_to (cr, x,y+h); // Line to F
503 cairo_line_to (cr, x,y+r); // Line to H
504 cairo_curve_to (cr, x,y,x,y,x+r,y); // Curve to A
508 Gtkmm2ext::rounded_top_right_rectangle (cairo_t* cr, double x, double y, double w, double h, double r)
516 cairo_move_to (cr, x,y); // Move to A
517 cairo_line_to (cr, x+w-r,y); // Straight line to B
518 cairo_curve_to (cr, x+w,y,x+w,y,x+w,y+r); // Curve to C, Control points are both at Q
519 cairo_line_to (cr, x+w,y+h); // Move to E
520 cairo_line_to (cr, x,y+h); // Line to F
521 cairo_line_to (cr, x,y); // Line to A
524 Glib::RefPtr<Gdk::Window>
525 Gtkmm2ext::window_to_draw_on (Gtk::Widget& w, Gtk::Widget** parent)
527 if (w.get_has_window()) {
528 return w.get_window();
531 (*parent) = w.get_parent();
534 if ((*parent)->get_has_window()) {
535 return (*parent)->get_window ();
537 (*parent) = (*parent)->get_parent ();
540 return Glib::RefPtr<Gdk::Window> ();
544 Gtkmm2ext::pixel_width (const string& str, Pango::FontDescription& font)
547 Glib::RefPtr<Pango::Layout> layout = foo.create_pango_layout ("");
549 layout->set_font_description (font);
550 layout->set_text (str);
553 Gtkmm2ext::get_ink_pixel_size (layout, width, height);
559 Gtkmm2ext::fit_to_pixels (const string& str, int pixel_width, Pango::FontDescription& font, int& actual_width, bool with_ellipses)
561 /* DECEMBER 2011: THIS PROTOTYPE OF fit_to_pixels() IS NOT USED
562 ANYWHERE AND HAS NOT BEEN TESTED.
565 Glib::RefPtr<Pango::Layout> layout = foo.create_pango_layout (str);
566 Glib::RefPtr<const Pango::LayoutLine> line;
568 layout->set_font_description (font);
569 layout->set_width (pixel_width * PANGO_SCALE);
572 layout->set_ellipsize (Pango::ELLIPSIZE_END);
574 layout->set_wrap (Pango::WRAP_CHAR);
577 line = layout->get_line (0);
579 /* XXX: might need special care to get the ellipsis character, not sure
583 string s = string (layout->get_text ().substr(line->get_start_index(), line->get_length()));
585 cerr << "fit to pixels of " << str << " returns " << s << endl;
591 /** Try to fit a string into a given horizontal space by ellipsizing it.
592 * @param cr Cairo context in which the text will be plotted.
594 * @param avail Available horizontal space.
595 * @return (Text, possibly ellipsized) and (horizontal size of text)
598 std::pair<std::string, double>
599 Gtkmm2ext::fit_to_pixels (cairo_t* cr, std::string name, double avail)
601 /* XXX hopefully there exists a more efficient way of doing this */
603 bool abbreviated = false;
607 cairo_text_extents_t ext;
608 cairo_text_extents (cr, name.c_str(), &ext);
610 if (ext.width < avail || name.length() <= 4) {
616 name = name.substr (0, name.length() - 4) + "...";
618 name = name.substr (0, name.length() - 3) + "...";
623 return std::make_pair (name, width);
627 Gtkmm2ext::left_aligned_label (string const & t)
629 Gtk::Label* l = new Gtk::Label (t);
630 l->set_alignment (0, 0.5);
635 make_null_tooltip (int, int, bool, const Glib::RefPtr<Gtk::Tooltip>& t)
637 t->set_tip_area (Gdk::Rectangle (0, 0, 0, 0));
641 /** Hackily arrange for the provided widget to have no tooltip,
642 * and also to stop any other widget from providing one while
643 * the mouse is over w.
646 Gtkmm2ext::set_no_tooltip_whatsoever (Gtk::Widget& w)
648 w.property_has_tooltip() = true;
649 w.signal_query_tooltip().connect (sigc::ptr_fun (make_null_tooltip));
653 Gtkmm2ext::enable_tooltips ()
655 gtk_rc_parse_string ("gtk-enable-tooltips = 1");
659 Gtkmm2ext::disable_tooltips ()
661 gtk_rc_parse_string ("gtk-enable-tooltips = 0");