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.
24 #include <gtk/gtkpaned.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>
34 #include <gtkmm/menuitem.h>
36 #include "gtkmm2ext/utils.h"
37 #include "gtkmm2ext/persistent_tooltip.h"
44 Gtkmm2ext::init (const char* localedir)
47 (void) bindtextdomain(PACKAGE, localedir);
48 (void) bind_textdomain_codeset (PACKAGE, "UTF-8");
53 Gtkmm2ext::get_ink_pixel_size (Glib::RefPtr<Pango::Layout> layout,
57 Pango::Rectangle ink_rect = layout->get_ink_extents ();
59 width = PANGO_PIXELS(ink_rect.get_width());
60 height = PANGO_PIXELS(ink_rect.get_height());
64 Gtkmm2ext::get_pixel_size (Glib::RefPtr<Pango::Layout> layout,
68 layout->get_pixel_size (width, height);
72 Gtkmm2ext::set_size_request_to_display_given_text (Gtk::Widget &w, const gchar *text,
73 gint hpadding, gint vpadding)
78 get_pixel_size (w.create_pango_layout (text), width, height);
79 w.set_size_request(width + hpadding, height + vpadding);
82 /** Set width request to display given text, and height to display anything.
83 This is useful for setting many widgets to the same height for consistency. */
85 Gtkmm2ext::set_size_request_to_display_given_text_width (Gtk::Widget& w,
90 static const gchar* vtext = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
95 get_pixel_size (w.create_pango_layout (htext), hwidth, hheight);
98 get_pixel_size (w.create_pango_layout (vtext), vwidth, vheight);
100 w.set_size_request(hwidth + hpadding, vheight + vpadding);
104 Gtkmm2ext::set_height_request_to_display_any_text (Gtk::Widget& w, gint vpadding)
106 static const gchar* vtext = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
111 get_pixel_size (w.create_pango_layout (vtext), width, height);
113 w.set_size_request(-1, height + vpadding);
117 Gtkmm2ext::set_size_request_to_display_given_text (Gtk::Widget &w, std::string const & text,
118 gint hpadding, gint vpadding)
123 get_pixel_size (w.create_pango_layout (text), width, height);
124 w.set_size_request(width + hpadding, height + vpadding);
128 Gtkmm2ext::set_size_request_to_display_given_text (Gtk::Widget &w,
129 const std::vector<std::string>& strings,
130 gint hpadding, gint vpadding)
137 const vector<string>* to_use;
138 vector<string>::const_iterator i;
140 for (i = strings.begin(); i != strings.end(); ++i) {
141 if ((*i).find_first_of ("gy") != string::npos) {
142 /* contains a descender */
147 if (i == strings.end()) {
148 /* make a copy of the strings then add one that has a descender */
150 copy.push_back ("g");
156 for (vector<string>::const_iterator i = to_use->begin(); i != to_use->end(); ++i) {
157 get_pixel_size (w.create_pango_layout (*i), width, height);
158 width_max = max(width_max,width);
159 height_max = max(height_max, height);
162 w.set_size_request(width_max + hpadding, height_max + vpadding);
165 /** This version specifies horizontal padding in text to avoid assumptions
166 about font size. Should be used anywhere padding is used to avoid text,
169 Gtkmm2ext::set_size_request_to_display_given_text (Gtk::Widget& w,
170 const std::vector<std::string>& strings,
171 const std::string& hpadding,
178 for (vector<string>::const_iterator i = strings.begin(); i != strings.end(); ++i) {
180 get_pixel_size (w.create_pango_layout (*i), width, height);
181 width_max = max(width_max,width);
182 height_max = max(height_max, height);
187 get_pixel_size (w.create_pango_layout (hpadding), pad_width, pad_height);
189 w.set_size_request(width_max + pad_width, height_max + vpadding);
193 demultiply_alpha (guint8 src,
196 /* cairo pixel buffer data contains RGB values with the alpha
197 values premultiplied.
199 GdkPixbuf pixel buffer data contains RGB values without the
202 this removes the alpha component from the cairo version and
203 returns the GdkPixbuf version.
205 return alpha ? ((guint (src) << 8) - src) / alpha : 0;
209 Gtkmm2ext::convert_bgra_to_rgba (guint8 const* src,
214 guint8 const* src_pixel = src;
215 guint8* dst_pixel = dst;
217 /* cairo pixel data is endian-dependent ARGB with A in the most significant 8 bits,
218 with premultipled alpha values (see preceding function)
220 GdkPixbuf pixel data is non-endian-dependent RGBA with R in the lowest addressable
221 8 bits, and non-premultiplied alpha values.
223 convert from the cairo values to the GdkPixbuf ones.
226 for (int y = 0; y < height; y++) {
227 for (int x = 0; x < width; x++) {
228 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
229 /* Cairo [ B G R A ] is actually [ B G R A ] in memory SOURCE
231 Pixbuf [ R G B A ] is actually [ R G B A ] in memory DEST
233 dst_pixel[0] = demultiply_alpha (src_pixel[2],
234 src_pixel[3]); // R [0] <= [ 2 ]
235 dst_pixel[1] = demultiply_alpha (src_pixel[1],
236 src_pixel[3]); // G [1] <= [ 1 ]
237 dst_pixel[2] = demultiply_alpha (src_pixel[0],
238 src_pixel[3]); // B [2] <= [ 0 ]
239 dst_pixel[3] = src_pixel[3]; // alpha
241 #elif G_BYTE_ORDER == G_BIG_ENDIAN
242 /* Cairo [ B G R A ] is actually [ A R G B ] in memory SOURCE
244 Pixbuf [ R G B A ] is actually [ R G B A ] in memory DEST
246 dst_pixel[0] = demultiply_alpha (src_pixel[1],
247 src_pixel[0]); // R [0] <= [ 1 ]
248 dst_pixel[1] = demultiply_alpha (src_pixel[2],
249 src_pixel[0]); // G [1] <= [ 2 ]
250 dst_pixel[2] = demultiply_alpha (src_pixel[3],
251 src_pixel[0]); // B [2] <= [ 3 ]
252 dst_pixel[3] = src_pixel[0]; // alpha
255 #error ardour does not currently support PDP-endianess
264 Glib::RefPtr<Gdk::Pixbuf>
265 Gtkmm2ext::pixbuf_from_string(const string& name, const Pango::FontDescription& font, int clip_width, int clip_height, Gdk::Color fg)
267 static Glib::RefPtr<Gdk::Pixbuf>* empty_pixbuf = 0;
270 if (empty_pixbuf == 0) {
271 empty_pixbuf = new Glib::RefPtr<Gdk::Pixbuf>;
272 *empty_pixbuf = Gdk::Pixbuf::create(Gdk::COLORSPACE_RGB, true, 8, clip_width, clip_height);
274 return *empty_pixbuf;
277 if (clip_width <= 0 || clip_height <= 0) {
278 /* negative values mean padding around natural size */
280 pixel_size (name, font, width, height);
281 if (clip_width <= 0) {
282 clip_width = width - clip_width;
284 if (clip_height <= 0) {
285 clip_height = height - clip_height;
289 Glib::RefPtr<Gdk::Pixbuf> buf = Gdk::Pixbuf::create(Gdk::COLORSPACE_RGB, true, 8, clip_width, clip_height);
291 cairo_surface_t* surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, clip_width, clip_height);
292 cairo_t* cr = cairo_create (surface);
293 cairo_text_extents_t te;
295 cairo_set_source_rgba (cr, fg.get_red_p(), fg.get_green_p(), fg.get_blue_p(), 1.0);
296 cairo_select_font_face (cr, font.get_family().c_str(),
297 CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
298 cairo_set_font_size (cr, font.get_size() / Pango::SCALE);
299 cairo_text_extents (cr, name.c_str(), &te);
301 cairo_move_to (cr, 0.5, int (0.5 - te.height / 2 - te.y_bearing + clip_height / 2));
302 cairo_show_text (cr, name.c_str());
304 convert_bgra_to_rgba(cairo_image_surface_get_data (surface), buf->get_pixels(), clip_width, clip_height);
307 cairo_surface_destroy(surface);
313 _position_menu_anchored (int& x, int& y, bool& push_in,
314 const Gtk::Menu* const menu,
315 Gtk::Widget* const anchor,
316 const std::string& selected) {
318 using namespace Gtk::Menu_Helpers;
320 /* TODO: lacks support for rotated dropdown buttons */
322 if (!anchor->has_screen () || !anchor->get_has_window ()) {
326 Gdk::Rectangle monitor;
328 const int monitor_num = anchor->get_screen ()->get_monitor_at_window (
329 anchor->get_window ());
330 anchor->get_screen ()->get_monitor_geometry (
331 (monitor_num < 0) ? 0 : monitor_num, monitor);
334 const Requisition menu_req = menu->size_request();
335 const Gdk::Rectangle allocation = anchor->get_allocation();
337 /* The x and y position are handled separately.
339 * For the x position if the direction is LTR (or RTL), then we try in order:
340 * a) align the left (right) of the menu with the left (right) of the button
341 * if there's enough room until the right (left) border of the screen;
342 * b) align the right (left) of the menu with the right (left) of the button
343 * if there's enough room until the left (right) border of the screen;
344 * c) align the right (left) border of the menu with the right (left) border
345 * of the screen if there's enough space;
346 * d) align the left (right) border of the menu with the left (right) border
347 * of the screen, with the rightmost (leftmost) part of the menu that
348 * overflows the screen.
349 * XXX We always align left regardless of the direction because if x is
350 * left of the current monitor, the menu popup code after us notices it
351 * and enforces that the menu stays in the monitor that's at the left...*/
353 anchor->get_window ()->get_origin (x, y);
355 if (anchor->get_direction() == TEXT_DIR_RTL) {
356 if (monitor.get_x() <= x + allocation.get_width() - menu_req.width) {
357 /* a) align menu right and button right */
358 x += allocation.get_width() - menu_req.width;
359 } else if (x + menu_req.width <= monitor.get_x() + monitor.get_width()) {
360 /* b) align menu left and button left: nothing to do*/
361 } else if (menu_req.width > monitor.get_width()) {
362 /* c) align menu left and screen left, guaranteed to fit */
365 /* d) XXX align left or the menu might change monitors */
369 if (x + menu_req.width <= monitor.get_x() + monitor.get_width()) {
370 /* a) align menu left and button left: nothing to do*/
371 } else if (monitor.get_x() <= x + allocation.get_width() - menu_req.width) {
372 /* b) align menu right and button right */
373 x += allocation.get_width() - menu_req.width;
374 } else if (menu_req.width > monitor.get_width()) {
375 /* c) align menu right and screen right, guaranteed to fit */
376 x = monitor.get_x() + monitor.get_width() - menu_req.width;
383 /* For the y position, try in order:
384 * a) if there is a menu item with the same text as the button, align it
385 * with the button, unless that makes the menu overflow the monitor.
386 * b) align the top of the menu with the bottom of the button if there is
387 * enough room below the button;
388 * c) align the bottom of the menu with the top of the button if there is
389 * enough room above the button;
390 * d) align the bottom of the menu with the bottom of the monitor if there
391 * is enough room, but avoid moving the menu to another monitor */
393 const MenuList& items = menu->items ();
396 MenuList::const_iterator i = items.begin();
397 for ( ; i != items.end(); ++i) {
398 const Label* label_widget = dynamic_cast<const Label*>(i->get_child());
399 if (label_widget && selected == ((std::string) label_widget->get_label())) {
402 offset += i->size_request().height;
404 if (i != items.end() &&
405 y - offset >= monitor.get_y() &&
406 y - offset + menu_req.height <= monitor.get_y() + monitor.get_height()) {
408 } else if (y + allocation.get_height() + menu_req.height <= monitor.get_y() + monitor.get_height()) {
409 y += allocation.get_height(); /* a) */
410 } else if ((y - menu_req.height) >= monitor.get_y()) {
411 y -= menu_req.height; /* b) */
413 y = monitor.get_y() + max(0, monitor.get_height() - menu_req.height);
420 Gtkmm2ext::anchored_menu_popup (Gtk::Menu* const menu,
421 Gtk::Widget* const anchor,
422 const std::string& selected,
423 guint button, guint32 time) {
425 sigc::bind (sigc::ptr_fun(&_position_menu_anchored),
426 menu, anchor, selected),
432 Gtkmm2ext::set_popdown_strings (Gtk::ComboBoxText& cr, const vector<string>& strings)
434 vector<string>::const_iterator i;
438 for (i = strings.begin(); i != strings.end(); ++i) {
444 Gtkmm2ext::get_popdown_strings (Gtk::ComboBoxText& cr, std::vector<std::string>& strings)
447 Glib::RefPtr<const Gtk::TreeModel> m = cr.get_model();
451 for(Gtk::TreeModel::iterator i = m->children().begin(); i != m->children().end(); ++i) {
453 (*i)->get_value(0, txt);
454 strings.push_back (txt);
459 Gtkmm2ext::get_popdown_string_count (Gtk::ComboBoxText& cr)
461 Glib::RefPtr<const Gtk::TreeModel> m = cr.get_model();
465 return m->children().size();
469 Gtkmm2ext::contains_value (Gtk::ComboBoxText& cr, const std::string text)
471 std::vector<std::string> s;
472 get_popdown_strings (cr, s);
473 return (std::find (s.begin(), s.end(), text) != s.end());
477 Gtkmm2ext::set_active_text_if_present (Gtk::ComboBoxText& cr, const std::string text)
479 if (contains_value(cr, text)) {
480 cr.set_active_text (text);
487 Gtkmm2ext::get_paned_handle (Gtk::Paned& paned)
489 return GTK_PANED(paned.gobj())->handle;
493 Gtkmm2ext::set_decoration (Gtk::Window* win, Gdk::WMDecoration decor)
495 win->get_window()->set_decorations (decor);
498 void Gtkmm2ext::set_treeview_header_as_default_label(Gtk::TreeViewColumn* c)
500 gtk_tree_view_column_set_widget( c->gobj(), GTK_WIDGET(0) );
504 Gtkmm2ext::detach_menu (Gtk::Menu& menu)
506 /* its possible for a Gtk::Menu to have no gobj() because it has
507 not yet been instantiated. Catch this and provide a safe
511 if (menu.get_attach_widget()) {
518 Gtkmm2ext::possibly_translate_keyval_to_make_legal_accelerator (uint32_t& keyval)
520 int fakekey = GDK_VoidSymbol;
524 case GDK_ISO_Left_Tab:
529 fakekey = GDK_uparrow;
533 fakekey = GDK_downarrow;
537 fakekey = GDK_rightarrow;
541 fakekey = GDK_leftarrow;
545 fakekey = GDK_3270_Enter;
556 if (fakekey != GDK_VoidSymbol) {
565 Gtkmm2ext::possibly_translate_legal_accelerator_to_real_key (uint32_t keyval)
600 Gtkmm2ext::physical_screen_height (Glib::RefPtr<Gdk::Window> win)
602 GdkScreen* scr = gdk_screen_get_default();
606 gint monitor = gdk_screen_get_monitor_at_window (scr, win->gobj());
607 gdk_screen_get_monitor_geometry (scr, monitor, &r);
610 return gdk_screen_get_height (scr);
615 Gtkmm2ext::physical_screen_width (Glib::RefPtr<Gdk::Window> win)
617 GdkScreen* scr = gdk_screen_get_default();
621 gint monitor = gdk_screen_get_monitor_at_window (scr, win->gobj());
622 gdk_screen_get_monitor_geometry (scr, monitor, &r);
625 return gdk_screen_get_width (scr);
630 Gtkmm2ext::container_clear (Gtk::Container& c)
632 list<Gtk::Widget*> children = c.get_children();
633 for (list<Gtk::Widget*>::iterator child = children.begin(); child != children.end(); ++child) {
639 Gtkmm2ext::rounded_rectangle (Cairo::RefPtr<Cairo::Context> context, double x, double y, double w, double h, double r)
641 rounded_rectangle (context->cobj(), x, y, w, h, r);
644 Gtkmm2ext::rounded_top_rectangle (Cairo::RefPtr<Cairo::Context> context, double x, double y, double w, double h, double r)
646 rounded_top_rectangle (context->cobj(), x, y, w, h, r);
649 Gtkmm2ext::rounded_top_left_rectangle (Cairo::RefPtr<Cairo::Context> context, double x, double y, double w, double h, double r)
651 rounded_top_left_rectangle (context->cobj(), x, y, w, h, r);
654 Gtkmm2ext::rounded_top_right_rectangle (Cairo::RefPtr<Cairo::Context> context, double x, double y, double w, double h, double r)
656 rounded_top_right_rectangle (context->cobj(), x, y, w, h, r);
659 Gtkmm2ext::rounded_top_half_rectangle (Cairo::RefPtr<Cairo::Context> context, double x, double y, double w, double h, double r)
661 rounded_top_half_rectangle (context->cobj(), x, y, w, h, r);
664 Gtkmm2ext::rounded_bottom_half_rectangle (Cairo::RefPtr<Cairo::Context> context, double x, double y, double w, double h, double r)
666 rounded_bottom_half_rectangle (context->cobj(), x, y, w, h, r);
670 Gtkmm2ext::rounded_left_half_rectangle (Cairo::RefPtr<Cairo::Context> context, double x, double y, double w, double h, double r)
672 rounded_left_half_rectangle (context->cobj(), x, y, w, h, r);
676 Gtkmm2ext::rounded_right_half_rectangle (Cairo::RefPtr<Cairo::Context> context, double x, double y, double w, double h, double r)
678 rounded_right_half_rectangle (context->cobj(), x, y, w, h, r);
682 Gtkmm2ext::rounded_rectangle (cairo_t* cr, double x, double y, double w, double h, double r)
684 double degrees = M_PI / 180.0;
686 cairo_new_sub_path (cr);
687 cairo_arc (cr, x + w - r, y + r, r, -90 * degrees, 0 * degrees); //tr
688 cairo_arc (cr, x + w - r, y + h - r, r, 0 * degrees, 90 * degrees); //br
689 cairo_arc (cr, x + r, y + h - r, r, 90 * degrees, 180 * degrees); //bl
690 cairo_arc (cr, x + r, y + r, r, 180 * degrees, 270 * degrees); //tl
691 cairo_close_path (cr);
695 Gtkmm2ext::rounded_left_half_rectangle (cairo_t* cr, double x, double y, double w, double h, double r)
697 double degrees = M_PI / 180.0;
699 cairo_new_sub_path (cr);
700 cairo_line_to (cr, x+w, y); // tr
701 cairo_line_to (cr, x+w, y + h); // br
702 cairo_arc (cr, x + r, y + h - r, r, 90 * degrees, 180 * degrees); //bl
703 cairo_arc (cr, x + r, y + r, r, 180 * degrees, 270 * degrees); //tl
704 cairo_close_path (cr);
708 Gtkmm2ext::rounded_right_half_rectangle (cairo_t* cr, double x, double y, double w, double h, double r)
710 double degrees = M_PI / 180.0;
712 cairo_new_sub_path (cr);
713 cairo_arc (cr, x + w - r, y + r, r, -90 * degrees, 0 * degrees); //tr
714 cairo_arc (cr, x + w - r, y + h - r, r, 0 * degrees, 90 * degrees); //br
715 cairo_line_to (cr, x, y + h); // bl
716 cairo_line_to (cr, x, y); // tl
717 cairo_close_path (cr);
721 Gtkmm2ext::rounded_top_half_rectangle (cairo_t* cr, double x, double y, double w, double h, double r)
723 double degrees = M_PI / 180.0;
725 cairo_new_sub_path (cr);
726 cairo_move_to (cr, x+w, y+h);
727 cairo_line_to (cr, x, y+h);
728 cairo_arc (cr, x + r, y + r, r, 180 * degrees, 270 * degrees); //tl
729 cairo_arc (cr, x + w - r, y + r, r, -90 * degrees, 0 * degrees); //tr
730 cairo_close_path (cr);
734 Gtkmm2ext::rounded_bottom_half_rectangle (cairo_t* cr, double x, double y, double w, double h, double r)
736 double degrees = M_PI / 180.0;
738 cairo_new_sub_path (cr);
739 cairo_move_to (cr, x, y);
740 cairo_line_to (cr, x+w, y);
741 cairo_arc (cr, x + w - r, y + h - r, r, 0 * degrees, 90 * degrees); //br
742 cairo_arc (cr, x + r, y + h - r, r, 90 * degrees, 180 * degrees); //bl
743 cairo_close_path (cr);
748 Gtkmm2ext::rounded_top_rectangle (cairo_t* cr, double x, double y, double w, double h, double r)
750 double degrees = M_PI / 180.0;
752 cairo_new_sub_path (cr);
753 cairo_move_to (cr, x+w, y+h);
754 cairo_line_to (cr, x, y+h);
755 cairo_arc (cr, x + r, y + r, r, 180 * degrees, 270 * degrees); //tl
756 cairo_arc (cr, x + w - r, y + r, r, -90 * degrees, 0 * degrees); //tr
757 cairo_close_path (cr);
761 Gtkmm2ext::rounded_top_left_rectangle (cairo_t* cr, double x, double y, double w, double h, double r)
769 cairo_move_to (cr, x+r,y); // Move to A
770 cairo_line_to (cr, x+w,y); // Straight line to B
771 cairo_line_to (cr, x+w,y+h); // Move to E
772 cairo_line_to (cr, x,y+h); // Line to F
773 cairo_line_to (cr, x,y+r); // Line to H
774 cairo_curve_to (cr, x,y,x,y,x+r,y); // Curve to A
778 Gtkmm2ext::rounded_top_right_rectangle (cairo_t* cr, double x, double y, double w, double h, double r)
786 cairo_move_to (cr, x,y); // Move to A
787 cairo_line_to (cr, x+w-r,y); // Straight line to B
788 cairo_curve_to (cr, x+w,y,x+w,y,x+w,y+r); // Curve to C, Control points are both at Q
789 cairo_line_to (cr, x+w,y+h); // Move to E
790 cairo_line_to (cr, x,y+h); // Line to F
791 cairo_line_to (cr, x,y); // Line to A
794 Glib::RefPtr<Gdk::Window>
795 Gtkmm2ext::window_to_draw_on (Gtk::Widget& w, Gtk::Widget** parent)
797 if (w.get_has_window()) {
798 return w.get_window();
801 (*parent) = w.get_parent();
804 if ((*parent)->get_has_window()) {
805 return (*parent)->get_window ();
807 (*parent) = (*parent)->get_parent ();
810 return Glib::RefPtr<Gdk::Window> ();
814 Gtkmm2ext::pixel_width (const string& str, const Pango::FontDescription& font)
816 Glib::RefPtr<Pango::Context> context = Glib::wrap (gdk_pango_context_get());
817 Glib::RefPtr<Pango::Layout> layout = Pango::Layout::create (context);
819 layout->set_font_description (font);
820 layout->set_text (str);
823 Gtkmm2ext::get_ink_pixel_size (layout, width, height);
826 // Pango returns incorrect text width on some OS X
827 // So we have to make a correction
828 // To determine the correct indent take the largest symbol for which the width is correct
829 // and make the calculation
831 // see also libs/canvas/text.cc
833 layout->set_text ("H");
834 layout->get_pixel_size (cor_width, height);
835 width += cor_width * 1.5;
842 Gtkmm2ext::pixel_size (const string& str, const Pango::FontDescription& font, int& width, int& height)
845 Glib::RefPtr<Pango::Layout> layout = foo.create_pango_layout ("");
847 layout->set_font_description (font);
848 layout->set_text (str);
850 Gtkmm2ext::get_ink_pixel_size (layout, width, height);
855 Gtkmm2ext::fit_to_pixels (const string& str, int pixel_width, Pango::FontDescription& font, int& actual_width, bool with_ellipses)
857 /* DECEMBER 2011: THIS PROTOTYPE OF fit_to_pixels() IS NOT USED
858 ANYWHERE AND HAS NOT BEEN TESTED.
861 Glib::RefPtr<Pango::Layout> layout = foo.create_pango_layout (str);
862 Glib::RefPtr<const Pango::LayoutLine> line;
864 layout->set_font_description (font);
865 layout->set_width (pixel_width * PANGO_SCALE);
868 layout->set_ellipsize (Pango::ELLIPSIZE_END);
870 layout->set_wrap (Pango::WRAP_CHAR);
873 line = layout->get_line (0);
875 /* XXX: might need special care to get the ellipsis character, not sure
879 string s = string (layout->get_text ().substr(line->get_start_index(), line->get_length()));
881 cerr << "fit to pixels of " << str << " returns " << s << endl;
887 /** Try to fit a string into a given horizontal space by ellipsizing it.
888 * @param cr Cairo context in which the text will be plotted.
890 * @param avail Available horizontal space.
891 * @return (Text, possibly ellipsized) and (horizontal size of text)
894 std::pair<std::string, double>
895 Gtkmm2ext::fit_to_pixels (cairo_t* cr, std::string name, double avail)
897 /* XXX hopefully there exists a more efficient way of doing this */
899 bool abbreviated = false;
903 cairo_text_extents_t ext;
904 cairo_text_extents (cr, name.c_str(), &ext);
906 if (ext.width < avail || name.length() <= 4) {
912 name = name.substr (0, name.length() - 4) + "...";
914 name = name.substr (0, name.length() - 3) + "...";
919 return std::make_pair (name, width);
923 Gtkmm2ext::left_aligned_label (string const & t)
925 Gtk::Label* l = new Gtk::Label (t);
926 l->set_alignment (0, 0.5);
931 Gtkmm2ext::right_aligned_label (string const & t)
933 Gtk::Label* l = new Gtk::Label (t);
934 l->set_alignment (1, 0.5);
939 make_null_tooltip (int, int, bool, const Glib::RefPtr<Gtk::Tooltip>& t)
941 t->set_tip_area (Gdk::Rectangle (0, 0, 0, 0));
945 /** Hackily arrange for the provided widget to have no tooltip,
946 * and also to stop any other widget from providing one while
947 * the mouse is over w.
950 Gtkmm2ext::set_no_tooltip_whatsoever (Gtk::Widget& w)
952 w.property_has_tooltip() = true;
953 w.signal_query_tooltip().connect (sigc::ptr_fun (make_null_tooltip));
957 Gtkmm2ext::enable_tooltips ()
959 gtk_rc_parse_string ("gtk-enable-tooltips = 1");
960 PersistentTooltip::set_tooltips_enabled (true);
964 Gtkmm2ext::disable_tooltips ()
966 gtk_rc_parse_string ("gtk-enable-tooltips = 0");
967 PersistentTooltip::set_tooltips_enabled (false);
971 Gtkmm2ext::event_inside_widget_window (Gtk::Widget& widget, GdkEvent* ev)
975 if (!gdk_event_get_root_coords (ev, &evx, &evy)) {
981 gint width, height, depth;
984 Glib::RefPtr<Gdk::Window> widget_window = widget.get_window();
986 widget_window->get_geometry (x, y, width, height, depth);
987 widget_window->get_root_origin (wx, wy);
989 if ((evx >= wx && evx < wx + width) &&
990 (evy >= wy && evy < wy + height)) {
998 Gtkmm2ext::event_type_string (int event_type)
1000 switch (event_type) {
1009 case GDK_MOTION_NOTIFY:
1010 return "motion_notify";
1011 case GDK_BUTTON_PRESS:
1012 return "button_press";
1013 case GDK_2BUTTON_PRESS:
1014 return "2button_press";
1015 case GDK_3BUTTON_PRESS:
1016 return "3button_press";
1017 case GDK_BUTTON_RELEASE:
1018 return "button_release";
1021 case GDK_KEY_RELEASE:
1022 return "key_release";
1023 case GDK_ENTER_NOTIFY:
1024 return "enter_notify";
1025 case GDK_LEAVE_NOTIFY:
1026 return "leave_notify";
1027 case GDK_FOCUS_CHANGE:
1028 return "focus_change";
1035 case GDK_PROPERTY_NOTIFY:
1036 return "property_notify";
1037 case GDK_SELECTION_CLEAR:
1038 return "selection_clear";
1039 case GDK_SELECTION_REQUEST:
1040 return "selection_request";
1041 case GDK_SELECTION_NOTIFY:
1042 return "selection_notify";
1043 case GDK_PROXIMITY_IN:
1044 return "proximity_in";
1045 case GDK_PROXIMITY_OUT:
1046 return "proximity_out";
1047 case GDK_DRAG_ENTER:
1048 return "drag_enter";
1049 case GDK_DRAG_LEAVE:
1050 return "drag_leave";
1051 case GDK_DRAG_MOTION:
1052 return "drag_motion";
1053 case GDK_DRAG_STATUS:
1054 return "drag_status";
1055 case GDK_DROP_START:
1056 return "drop_start";
1057 case GDK_DROP_FINISHED:
1058 return "drop_finished";
1059 case GDK_CLIENT_EVENT:
1060 return "client_event";
1061 case GDK_VISIBILITY_NOTIFY:
1062 return "visibility_notify";
1067 case GDK_WINDOW_STATE:
1068 return "window_state";
1071 case GDK_OWNER_CHANGE:
1072 return "owner_change";
1073 case GDK_GRAB_BROKEN:
1074 return "grab_broken";
1083 Gtkmm2ext::markup_escape_text (std::string const& s)
1085 return Glib::Markup::escape_text (s);
1089 Gtkmm2ext::add_volume_shortcuts (Gtk::FileChooser& c)
1093 /* This is a first order approach, listing all mounted volumes (incl network).
1094 * One could use `diskutil` or `mount` to query local disks only, or
1095 * something even fancier if deemed appropriate.
1097 Glib::Dir dir("/Volumes");
1098 for (Glib::DirIterator di = dir.begin(); di != dir.end(); di++) {
1099 string fullpath = Glib::build_filename ("/Volumes", *di);
1100 if (!Glib::file_test (fullpath, Glib::FILE_TEST_IS_DIR)) continue;
1102 try { /* add_shortcut_folder throws an exception if the folder being added already has a shortcut */
1103 c.add_shortcut_folder (fullpath);
1105 catch (Glib::Error& e) {
1106 std::cerr << "add_shortcut_folder() threw Glib::Error: " << e.what() << std::endl;
1110 catch (Glib::FileError& e) {
1111 std::cerr << "listing /Volumnes failed: " << e.what() << std::endl;
1117 Gtkmm2ext::paned_position_as_fraction (Gtk::Paned& paned, bool h)
1119 const guint pos = gtk_paned_get_position (const_cast<GtkPaned*>(static_cast<const Gtk::Paned*>(&paned)->gobj()));
1120 return (double) pos / (h ? paned.get_allocation().get_height() : paned.get_allocation().get_width());
1124 Gtkmm2ext::paned_set_position_as_fraction (Gtk::Paned& paned, float fraction, bool h)
1126 gint v = (h ? paned.get_allocation().get_height() : paned.get_allocation().get_width());
1132 paned.set_position ((guint) floor (fraction * v));
1136 Gtkmm2ext::show_gdk_event_state (int state)
1139 if (state & GDK_SHIFT_MASK) {
1142 if (state & GDK_LOCK_MASK) {
1145 if (state & GDK_CONTROL_MASK) {
1148 if (state & GDK_MOD1_MASK) {
1151 if (state & GDK_MOD2_MASK) {
1154 if (state & GDK_MOD3_MASK) {
1157 if (state & GDK_MOD4_MASK) {
1160 if (state & GDK_MOD5_MASK) {
1163 if (state & GDK_BUTTON1_MASK) {
1166 if (state & GDK_BUTTON2_MASK) {
1169 if (state & GDK_BUTTON3_MASK) {
1172 if (state & GDK_BUTTON4_MASK) {
1175 if (state & GDK_BUTTON5_MASK) {
1178 if (state & GDK_SUPER_MASK) {
1181 if (state & GDK_HYPER_MASK) {
1184 if (state & GDK_META_MASK) {
1187 if (state & GDK_RELEASE_MASK) {