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);
80 Gtkmm2ext::set_size_request_to_display_given_text (Gtk::Widget &w,
81 const std::vector<std::string>& strings,
82 gint hpadding, gint vpadding)
89 const vector<string>* to_use;
90 vector<string>::const_iterator i;
92 for (i = strings.begin(); i != strings.end(); ++i) {
93 if ((*i).find_first_of ("gy") != string::npos) {
94 /* contains a descender */
99 if (i == strings.end()) {
100 /* make a copy of the strings then add one that has a descender */
102 copy.push_back ("g");
108 for (vector<string>::const_iterator i = to_use->begin(); i != to_use->end(); ++i) {
109 get_pixel_size (w.create_pango_layout (*i), width, height);
110 width_max = max(width_max,width);
111 height_max = max(height_max, height);
114 w.set_size_request(width_max + hpadding, height_max + vpadding);
118 demultiply_alpha (guint8 src,
121 /* cairo pixel buffer data contains RGB values with the alpha
122 values premultiplied.
124 GdkPixbuf pixel buffer data contains RGB values without the
127 this removes the alpha component from the cairo version and
128 returns the GdkPixbuf version.
130 return alpha ? ((guint (src) << 8) - src) / alpha : 0;
134 Gtkmm2ext::convert_bgra_to_rgba (guint8 const* src,
139 guint8 const* src_pixel = src;
140 guint8* dst_pixel = dst;
142 /* cairo pixel data is endian-dependent ARGB with A in the most significant 8 bits,
143 with premultipled alpha values (see preceding function)
145 GdkPixbuf pixel data is non-endian-dependent RGBA with R in the lowest addressable
146 8 bits, and non-premultiplied alpha values.
148 convert from the cairo values to the GdkPixbuf ones.
151 for (int y = 0; y < height; y++) {
152 for (int x = 0; x < width; x++) {
153 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
154 /* Cairo [ B G R A ] is actually [ B G R A ] in memory SOURCE
156 Pixbuf [ R G B A ] is actually [ R G B A ] in memory DEST
158 dst_pixel[0] = demultiply_alpha (src_pixel[2],
159 src_pixel[3]); // R [0] <= [ 2 ]
160 dst_pixel[1] = demultiply_alpha (src_pixel[1],
161 src_pixel[3]); // G [1] <= [ 1 ]
162 dst_pixel[2] = demultiply_alpha (src_pixel[0],
163 src_pixel[3]); // B [2] <= [ 0 ]
164 dst_pixel[3] = src_pixel[3]; // alpha
166 #elif G_BYTE_ORDER == G_BIG_ENDIAN
167 /* Cairo [ B G R A ] is actually [ A R G B ] in memory SOURCE
169 Pixbuf [ R G B A ] is actually [ R G B A ] in memory DEST
171 dst_pixel[0] = demultiply_alpha (src_pixel[1],
172 src_pixel[0]); // R [0] <= [ 1 ]
173 dst_pixel[1] = demultiply_alpha (src_pixel[2],
174 src_pixel[0]); // G [1] <= [ 2 ]
175 dst_pixel[2] = demultiply_alpha (src_pixel[3],
176 src_pixel[0]); // B [2] <= [ 3 ]
177 dst_pixel[3] = src_pixel[0]; // alpha
180 #error ardour does not currently support PDP-endianess
189 Glib::RefPtr<Gdk::Pixbuf>
190 Gtkmm2ext::pixbuf_from_string(const string& name, const Pango::FontDescription& font, int clip_width, int clip_height, Gdk::Color fg)
192 static Glib::RefPtr<Gdk::Pixbuf>* empty_pixbuf = 0;
195 if (empty_pixbuf == 0) {
196 empty_pixbuf = new Glib::RefPtr<Gdk::Pixbuf>;
197 *empty_pixbuf = Gdk::Pixbuf::create(Gdk::COLORSPACE_RGB, true, 8, clip_width, clip_height);
199 return *empty_pixbuf;
202 Glib::RefPtr<Gdk::Pixbuf> buf = Gdk::Pixbuf::create(Gdk::COLORSPACE_RGB, true, 8, clip_width, clip_height);
204 cairo_surface_t* surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, clip_width, clip_height);
205 cairo_t* cr = cairo_create (surface);
206 cairo_text_extents_t te;
208 cairo_set_source_rgba (cr, fg.get_red_p(), fg.get_green_p(), fg.get_blue_p(), 1.0);
209 cairo_select_font_face (cr, font.get_family().c_str(),
210 CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
211 cairo_set_font_size (cr, font.get_size() / Pango::SCALE);
212 cairo_text_extents (cr, name.c_str(), &te);
214 cairo_move_to (cr, 0.5, int (0.5 - te.height / 2 - te.y_bearing + clip_height / 2));
215 cairo_show_text (cr, name.c_str());
217 convert_bgra_to_rgba(cairo_image_surface_get_data (surface), buf->get_pixels(), clip_width, clip_height);
220 cairo_surface_destroy(surface);
226 Gtkmm2ext::set_popdown_strings (Gtk::ComboBoxText& cr, const vector<string>& strings)
228 vector<string>::const_iterator i;
232 for (i = strings.begin(); i != strings.end(); ++i) {
238 Gtkmm2ext::get_paned_handle (Gtk::Paned& paned)
240 return GTK_PANED(paned.gobj())->handle;
244 Gtkmm2ext::set_decoration (Gtk::Window* win, Gdk::WMDecoration decor)
246 win->get_window()->set_decorations (decor);
249 void Gtkmm2ext::set_treeview_header_as_default_label(Gtk::TreeViewColumn* c)
251 gtk_tree_view_column_set_widget( c->gobj(), GTK_WIDGET(0) );
255 Gtkmm2ext::detach_menu (Gtk::Menu& menu)
257 /* its possible for a Gtk::Menu to have no gobj() because it has
258 not yet been instantiated. Catch this and provide a safe
262 if (menu.get_attach_widget()) {
269 Gtkmm2ext::possibly_translate_keyval_to_make_legal_accelerator (uint32_t& keyval)
271 int fakekey = GDK_VoidSymbol;
275 case GDK_ISO_Left_Tab:
280 fakekey = GDK_uparrow;
284 fakekey = GDK_downarrow;
288 fakekey = GDK_rightarrow;
292 fakekey = GDK_leftarrow;
296 fakekey = GDK_3270_Enter;
307 if (fakekey != GDK_VoidSymbol) {
316 Gtkmm2ext::possibly_translate_legal_accelerator_to_real_key (uint32_t keyval)
351 Gtkmm2ext::physical_screen_height (Glib::RefPtr<Gdk::Window> win)
353 GdkScreen* scr = gdk_screen_get_default();
357 gint monitor = gdk_screen_get_monitor_at_window (scr, win->gobj());
358 gdk_screen_get_monitor_geometry (scr, monitor, &r);
361 return gdk_screen_get_height (scr);
366 Gtkmm2ext::physical_screen_width (Glib::RefPtr<Gdk::Window> win)
368 GdkScreen* scr = gdk_screen_get_default();
372 gint monitor = gdk_screen_get_monitor_at_window (scr, win->gobj());
373 gdk_screen_get_monitor_geometry (scr, monitor, &r);
376 return gdk_screen_get_width (scr);
381 Gtkmm2ext::container_clear (Gtk::Container& c)
383 list<Gtk::Widget*> children = c.get_children();
384 for (list<Gtk::Widget*>::iterator child = children.begin(); child != children.end(); ++child) {
390 Gtkmm2ext::rounded_rectangle (Cairo::RefPtr<Cairo::Context> context, double x, double y, double w, double h, double r)
392 rounded_rectangle (context->cobj(), x, y, w, h, r);
395 Gtkmm2ext::rounded_top_rectangle (Cairo::RefPtr<Cairo::Context> context, double x, double y, double w, double h, double r)
397 rounded_top_rectangle (context->cobj(), x, y, w, h, r);
400 Gtkmm2ext::rounded_top_left_rectangle (Cairo::RefPtr<Cairo::Context> context, double x, double y, double w, double h, double r)
402 rounded_top_left_rectangle (context->cobj(), x, y, w, h, r);
405 Gtkmm2ext::rounded_top_right_rectangle (Cairo::RefPtr<Cairo::Context> context, double x, double y, double w, double h, double r)
407 rounded_top_right_rectangle (context->cobj(), x, y, w, h, r);
410 Gtkmm2ext::rounded_top_half_rectangle (Cairo::RefPtr<Cairo::Context> context, double x, double y, double w, double h, double r)
412 rounded_top_half_rectangle (context->cobj(), x, y, w, h, r);
415 Gtkmm2ext::rounded_bottom_half_rectangle (Cairo::RefPtr<Cairo::Context> context, double x, double y, double w, double h, double r)
417 rounded_bottom_half_rectangle (context->cobj(), x, y, w, h, r);
421 Gtkmm2ext::rounded_left_half_rectangle (Cairo::RefPtr<Cairo::Context> context, double x, double y, double w, double h, double r)
423 rounded_left_half_rectangle (context->cobj(), x, y, w, h, r);
427 Gtkmm2ext::rounded_right_half_rectangle (Cairo::RefPtr<Cairo::Context> context, double x, double y, double w, double h, double r)
429 rounded_right_half_rectangle (context->cobj(), x, y, w, h, r);
433 Gtkmm2ext::rounded_rectangle (cairo_t* cr, double x, double y, double w, double h, double r)
435 double degrees = M_PI / 180.0;
437 cairo_new_sub_path (cr);
438 cairo_arc (cr, x + w - r, y + r, r, -90 * degrees, 0 * degrees); //tr
439 cairo_arc (cr, x + w - r, y + h - r, r, 0 * degrees, 90 * degrees); //br
440 cairo_arc (cr, x + r, y + h - r, r, 90 * degrees, 180 * degrees); //bl
441 cairo_arc (cr, x + r, y + r, r, 180 * degrees, 270 * degrees); //tl
442 cairo_close_path (cr);
446 Gtkmm2ext::rounded_left_half_rectangle (cairo_t* cr, double x, double y, double w, double h, double r)
448 double degrees = M_PI / 180.0;
450 cairo_new_sub_path (cr);
451 cairo_line_to (cr, x+w, y); // tr
452 cairo_line_to (cr, x+w, y + h); // br
453 cairo_arc (cr, x + r, y + h - r, r, 90 * degrees, 180 * degrees); //bl
454 cairo_arc (cr, x + r, y + r, r, 180 * degrees, 270 * degrees); //tl
455 cairo_close_path (cr);
459 Gtkmm2ext::rounded_right_half_rectangle (cairo_t* cr, double x, double y, double w, double h, double r)
461 double degrees = M_PI / 180.0;
463 cairo_new_sub_path (cr);
464 cairo_arc (cr, x + w - r, y + r, r, -90 * degrees, 0 * degrees); //tr
465 cairo_arc (cr, x + w - r, y + h - r, r, 0 * degrees, 90 * degrees); //br
466 cairo_line_to (cr, x, y + h); // bl
467 cairo_line_to (cr, x, y); // tl
468 cairo_close_path (cr);
472 Gtkmm2ext::rounded_top_half_rectangle (cairo_t* cr, double x, double y, double w, double h, double r)
474 double degrees = M_PI / 180.0;
476 cairo_new_sub_path (cr);
477 cairo_move_to (cr, x+w, y+h);
478 cairo_line_to (cr, x, y+h);
479 cairo_arc (cr, x + r, y + r, r, 180 * degrees, 270 * degrees); //tl
480 cairo_arc (cr, x + w - r, y + r, r, -90 * degrees, 0 * degrees); //tr
481 cairo_close_path (cr);
485 Gtkmm2ext::rounded_bottom_half_rectangle (cairo_t* cr, double x, double y, double w, double h, double r)
487 double degrees = M_PI / 180.0;
489 cairo_new_sub_path (cr);
490 cairo_move_to (cr, x, y);
491 cairo_line_to (cr, x+w, y);
492 cairo_arc (cr, x + w - r, y + h - r, r, 0 * degrees, 90 * degrees); //br
493 cairo_arc (cr, x + r, y + h - r, r, 90 * degrees, 180 * degrees); //bl
494 cairo_close_path (cr);
499 Gtkmm2ext::rounded_top_rectangle (cairo_t* cr, double x, double y, double w, double h, double r)
501 double degrees = M_PI / 180.0;
503 cairo_new_sub_path (cr);
504 cairo_move_to (cr, x+w, y+h);
505 cairo_line_to (cr, x, y+h);
506 cairo_arc (cr, x + r, y + r, r, 180 * degrees, 270 * degrees); //tl
507 cairo_arc (cr, x + w - r, y + r, r, -90 * degrees, 0 * degrees); //tr
508 cairo_close_path (cr);
512 Gtkmm2ext::rounded_top_left_rectangle (cairo_t* cr, double x, double y, double w, double h, double r)
520 cairo_move_to (cr, x+r,y); // Move to A
521 cairo_line_to (cr, x+w,y); // Straight line to B
522 cairo_line_to (cr, x+w,y+h); // Move to E
523 cairo_line_to (cr, x,y+h); // Line to F
524 cairo_line_to (cr, x,y+r); // Line to H
525 cairo_curve_to (cr, x,y,x,y,x+r,y); // Curve to A
529 Gtkmm2ext::rounded_top_right_rectangle (cairo_t* cr, double x, double y, double w, double h, double r)
537 cairo_move_to (cr, x,y); // Move to A
538 cairo_line_to (cr, x+w-r,y); // Straight line to B
539 cairo_curve_to (cr, x+w,y,x+w,y,x+w,y+r); // Curve to C, Control points are both at Q
540 cairo_line_to (cr, x+w,y+h); // Move to E
541 cairo_line_to (cr, x,y+h); // Line to F
542 cairo_line_to (cr, x,y); // Line to A
545 Glib::RefPtr<Gdk::Window>
546 Gtkmm2ext::window_to_draw_on (Gtk::Widget& w, Gtk::Widget** parent)
548 if (w.get_has_window()) {
549 return w.get_window();
552 (*parent) = w.get_parent();
555 if ((*parent)->get_has_window()) {
556 return (*parent)->get_window ();
558 (*parent) = (*parent)->get_parent ();
561 return Glib::RefPtr<Gdk::Window> ();
565 Gtkmm2ext::pixel_width (const string& str, Pango::FontDescription& font)
568 Glib::RefPtr<Pango::Layout> layout = foo.create_pango_layout ("");
570 layout->set_font_description (font);
571 layout->set_text (str);
574 Gtkmm2ext::get_ink_pixel_size (layout, width, height);
580 Gtkmm2ext::fit_to_pixels (const string& str, int pixel_width, Pango::FontDescription& font, int& actual_width, bool with_ellipses)
582 /* DECEMBER 2011: THIS PROTOTYPE OF fit_to_pixels() IS NOT USED
583 ANYWHERE AND HAS NOT BEEN TESTED.
586 Glib::RefPtr<Pango::Layout> layout = foo.create_pango_layout (str);
587 Glib::RefPtr<const Pango::LayoutLine> line;
589 layout->set_font_description (font);
590 layout->set_width (pixel_width * PANGO_SCALE);
593 layout->set_ellipsize (Pango::ELLIPSIZE_END);
595 layout->set_wrap (Pango::WRAP_CHAR);
598 line = layout->get_line (0);
600 /* XXX: might need special care to get the ellipsis character, not sure
604 string s = string (layout->get_text ().substr(line->get_start_index(), line->get_length()));
606 cerr << "fit to pixels of " << str << " returns " << s << endl;
612 /** Try to fit a string into a given horizontal space by ellipsizing it.
613 * @param cr Cairo context in which the text will be plotted.
615 * @param avail Available horizontal space.
616 * @return (Text, possibly ellipsized) and (horizontal size of text)
619 std::pair<std::string, double>
620 Gtkmm2ext::fit_to_pixels (cairo_t* cr, std::string name, double avail)
622 /* XXX hopefully there exists a more efficient way of doing this */
624 bool abbreviated = false;
628 cairo_text_extents_t ext;
629 cairo_text_extents (cr, name.c_str(), &ext);
631 if (ext.width < avail || name.length() <= 4) {
637 name = name.substr (0, name.length() - 4) + "...";
639 name = name.substr (0, name.length() - 3) + "...";
644 return std::make_pair (name, width);
648 Gtkmm2ext::left_aligned_label (string const & t)
650 Gtk::Label* l = new Gtk::Label (t);
651 l->set_alignment (0, 0.5);
656 make_null_tooltip (int, int, bool, const Glib::RefPtr<Gtk::Tooltip>& t)
658 t->set_tip_area (Gdk::Rectangle (0, 0, 0, 0));
662 /** Hackily arrange for the provided widget to have no tooltip,
663 * and also to stop any other widget from providing one while
664 * the mouse is over w.
667 Gtkmm2ext::set_no_tooltip_whatsoever (Gtk::Widget& w)
669 w.property_has_tooltip() = true;
670 w.signal_query_tooltip().connect (sigc::ptr_fun (make_null_tooltip));
674 Gtkmm2ext::enable_tooltips ()
676 gtk_rc_parse_string ("gtk-enable-tooltips = 1");
680 Gtkmm2ext::disable_tooltips ()
682 gtk_rc_parse_string ("gtk-enable-tooltips = 0");
686 Gtkmm2ext::event_type_string (int event_type)
688 switch (event_type) {
697 case GDK_MOTION_NOTIFY:
698 return "motion_notify";
699 case GDK_BUTTON_PRESS:
700 return "button_press";
701 case GDK_2BUTTON_PRESS:
702 return "2button_press";
703 case GDK_3BUTTON_PRESS:
704 return "3button_press";
705 case GDK_BUTTON_RELEASE:
706 return "button_release";
709 case GDK_KEY_RELEASE:
710 return "key_release";
711 case GDK_ENTER_NOTIFY:
712 return "enter_notify";
713 case GDK_LEAVE_NOTIFY:
714 return "leave_notify";
715 case GDK_FOCUS_CHANGE:
716 return "focus_change";
723 case GDK_PROPERTY_NOTIFY:
724 return "property_notify";
725 case GDK_SELECTION_CLEAR:
726 return "selection_clear";
727 case GDK_SELECTION_REQUEST:
728 return "selection_request";
729 case GDK_SELECTION_NOTIFY:
730 return "selection_notify";
731 case GDK_PROXIMITY_IN:
732 return "proximity_in";
733 case GDK_PROXIMITY_OUT:
734 return "proximity_out";
739 case GDK_DRAG_MOTION:
740 return "drag_motion";
741 case GDK_DRAG_STATUS:
742 return "drag_status";
745 case GDK_DROP_FINISHED:
746 return "drop_finished";
747 case GDK_CLIENT_EVENT:
748 return "client_event";
749 case GDK_VISIBILITY_NOTIFY:
750 return "visibility_notify";
755 case GDK_WINDOW_STATE:
756 return "window_state";
759 case GDK_OWNER_CHANGE:
760 return "owner_change";
761 case GDK_GRAB_BROKEN:
762 return "grab_broken";