factor out shuttle controller to its own class, new design
[ardour2.git] / libs / gtkmm2ext / utils.cc
blob878d5402aff4f2192f2516e8df1a50e54c55db3f
1 /*
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.
18 $Id$
21 #include <map>
23 #include <gtk/gtkpaned.h>
24 #include <gtk/gtk.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/comboboxtext.h>
33 #include "i18n.h"
35 using namespace std;
37 void
38 Gtkmm2ext::init ()
40 // Necessary for gettext
41 (void) bindtextdomain(PACKAGE, LOCALEDIR);
44 void
45 Gtkmm2ext::get_ink_pixel_size (Glib::RefPtr<Pango::Layout> layout,
46 int& width,
47 int& height)
49 Pango::Rectangle ink_rect = layout->get_ink_extents ();
51 width = (ink_rect.get_width() + PANGO_SCALE / 2) / PANGO_SCALE;
52 height = (ink_rect.get_height() + PANGO_SCALE / 2) / PANGO_SCALE;
55 void
56 get_pixel_size (Glib::RefPtr<Pango::Layout> layout,
57 int& width,
58 int& height)
60 layout->get_pixel_size (width, height);
63 void
64 Gtkmm2ext::set_size_request_to_display_given_text (Gtk::Widget &w, const gchar *text,
65 gint hpadding, gint vpadding)
68 int width, height;
69 w.ensure_style ();
71 get_pixel_size (w.create_pango_layout (text), width, height);
72 w.set_size_request(width + hpadding, height + vpadding);
75 void
76 Gtkmm2ext::set_size_request_to_display_given_text (Gtk::Widget &w,
77 const std::vector<std::string>& strings,
78 gint hpadding, gint vpadding)
81 int width, height;
82 int width_max = 0;
83 int height_max = 0;
84 w.ensure_style ();
85 vector<string> copy;
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 */
92 break;
96 if (i == strings.end()) {
97 /* make a copy of the strings then add one that has a descener */
98 copy = strings;
99 copy.push_back ("g");
100 to_use = &copy;
101 } else {
102 to_use = &strings;
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);
114 static inline guint8
115 demultiply_alpha (guint8 src,
116 guint8 alpha)
118 /* cairo pixel buffer data contains RGB values with the alpha
119 values premultiplied.
121 GdkPixbuf pixel buffer data contains RGB values without the
122 alpha value applied.
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;
130 static void
131 convert_bgra_to_rgba (guint8 const* src,
132 guint8* dst,
133 int width,
134 int height)
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
152 0 1 2 3
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
165 0 1 2 3
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
176 #else
177 #error ardour does not currently support PDP-endianess
178 #endif
180 dst_pixel += 4;
181 src_pixel += 4;
186 Glib::RefPtr<Gdk::Pixbuf>
187 Gtkmm2ext::pixbuf_from_string(const string& name, Pango::FontDescription* font, int clip_width, int clip_height, Gdk::Color fg)
189 static Glib::RefPtr<Gdk::Pixbuf>* empty_pixbuf = 0;
191 if (name.empty()) {
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);
216 cairo_destroy(cr);
217 cairo_surface_destroy(surface);
219 return buf;
222 void
223 Gtkmm2ext::set_popdown_strings (Gtk::ComboBoxText& cr, const vector<string>& strings, bool set_size, gint hpadding, gint vpadding)
225 vector<string>::const_iterator i;
227 cr.clear ();
229 if (set_size) {
230 set_size_request_to_display_given_text (cr, strings, COMBO_FUDGE+10+hpadding, 15+vpadding);
233 for (i = strings.begin(); i != strings.end(); ++i) {
234 cr.append_text (*i);
238 GdkWindow*
239 Gtkmm2ext::get_paned_handle (Gtk::Paned& paned)
241 return GTK_PANED(paned.gobj())->handle;
244 void
245 Gtkmm2ext::set_decoration (Gtk::Window* win, Gdk::WMDecoration decor)
247 win->get_window()->set_decorations (decor);
250 void Gtkmm2ext::set_treeview_header_as_default_label(Gtk::TreeViewColumn* c)
252 gtk_tree_view_column_set_widget( c->gobj(), GTK_WIDGET(0) );
255 void
256 Gtkmm2ext::detach_menu (Gtk::Menu& menu)
258 /* its possible for a Gtk::Menu to have no gobj() because it has
259 not yet been instantiated. Catch this and provide a safe
260 detach method.
262 if (menu.gobj()) {
263 if (menu.get_attach_widget()) {
264 menu.detach ();
269 bool
270 Gtkmm2ext::possibly_translate_keyval_to_make_legal_accelerator (uint32_t& keyval)
272 int fakekey = GDK_VoidSymbol;
274 switch (keyval) {
275 case GDK_Tab:
276 case GDK_ISO_Left_Tab:
277 fakekey = GDK_nabla;
278 break;
280 case GDK_Up:
281 fakekey = GDK_uparrow;
282 break;
284 case GDK_Down:
285 fakekey = GDK_downarrow;
286 break;
288 case GDK_Right:
289 fakekey = GDK_rightarrow;
290 break;
292 case GDK_Left:
293 fakekey = GDK_leftarrow;
294 break;
296 case GDK_Return:
297 fakekey = GDK_3270_Enter;
298 break;
300 case GDK_KP_Enter:
301 fakekey = GDK_F35;
302 break;
304 default:
305 break;
308 if (fakekey != GDK_VoidSymbol) {
309 keyval = fakekey;
310 return true;
313 return false;
316 uint32_t
317 Gtkmm2ext::possibly_translate_legal_accelerator_to_real_key (uint32_t keyval)
319 switch (keyval) {
320 case GDK_nabla:
321 return GDK_Tab;
322 break;
324 case GDK_uparrow:
325 return GDK_Up;
326 break;
328 case GDK_downarrow:
329 return GDK_Down;
330 break;
332 case GDK_rightarrow:
333 return GDK_Right;
334 break;
336 case GDK_leftarrow:
337 return GDK_Left;
338 break;
340 case GDK_3270_Enter:
341 return GDK_Return;
343 case GDK_F35:
344 return GDK_KP_Enter;
345 break;
348 return keyval;
352 Gtkmm2ext::physical_screen_height (Glib::RefPtr<Gdk::Window> win)
354 GdkScreen* scr = gdk_screen_get_default();
356 if (win) {
357 GdkRectangle r;
358 gint monitor = gdk_screen_get_monitor_at_window (scr, win->gobj());
359 gdk_screen_get_monitor_geometry (scr, monitor, &r);
360 return r.height;
361 } else {
362 return gdk_screen_get_height (scr);
367 Gtkmm2ext::physical_screen_width (Glib::RefPtr<Gdk::Window> win)
369 GdkScreen* scr = gdk_screen_get_default();
371 if (win) {
372 GdkRectangle r;
373 gint monitor = gdk_screen_get_monitor_at_window (scr, win->gobj());
374 gdk_screen_get_monitor_geometry (scr, monitor, &r);
375 return r.width;
376 } else {
377 return gdk_screen_get_width (scr);
381 void
382 Gtkmm2ext::container_clear (Gtk::Container& c)
384 list<Gtk::Widget*> children = c.get_children();
385 for (list<Gtk::Widget*>::iterator child = children.begin(); child != children.end(); ++child) {
386 c.remove (**child);
390 #if 1
391 void
392 Gtkmm2ext::rounded_rectangle (Cairo::RefPtr<Cairo::Context> context, double x, double y, double w, double h, double r)
394 /* renders small shapes better than most others */
396 /* A****BQ
400 F****E
402 context->move_to(x+r,y); // Move to A
403 context->line_to(x+w-r,y); // Straight line to B
404 context->curve_to(x+w,y,x+w,y,x+w,y+r); // Curve to C, Control points are both at Q
405 context->line_to(x+w,y+h-r); // Move to D
406 context->curve_to(x+w,y+h,x+w,y+h,x+w-r,y+h); // Curve to E
407 context->line_to(x+r,y+h); // Line to F
408 context->curve_to(x,y+h,x,y+h,x,y+h-r); // Curve to G
409 context->line_to(x,y+r); // Line to H
410 context->curve_to(x,y,x,y,x+r,y); // Curve to A
413 #else
415 void
416 Gtkmm2ext::rounded_rectangle (Cairo::RefPtr<Cairo::Context> context, double x, double y, double width, double height, double radius)
418 /* doesn't render small shapes well at all, and does not absolutely honor width & height */
420 double x0 = x+radius/2.0;
421 double y0 = y+radius/2.0;
422 double rect_width = width - radius;
423 double rect_height = height - radius;
425 context->save();
427 double x1=x0+rect_width;
428 double y1=y0+rect_height;
430 if (rect_width/2<radius) {
431 if (rect_height/2<radius) {
432 context->move_to (x0, (y0 + y1)/2);
433 context->curve_to (x0 ,y0, x0, y0, (x0 + x1)/2, y0);
434 context->curve_to (x1, y0, x1, y0, x1, (y0 + y1)/2);
435 context->curve_to (x1, y1, x1, y1, (x1 + x0)/2, y1);
436 context->curve_to (x0, y1, x0, y1, x0, (y0 + y1)/2);
437 } else {
438 context->move_to (x0, y0 + radius);
439 context->curve_to (x0 ,y0, x0, y0, (x0 + x1)/2, y0);
440 context->curve_to (x1, y0, x1, y0, x1, y0 + radius);
441 context->line_to (x1 , y1 - radius);
442 context->curve_to (x1, y1, x1, y1, (x1 + x0)/2, y1);
443 context->curve_to (x0, y1, x0, y1, x0, y1- radius);
445 } else {
446 if (rect_height/2<radius) {
447 context->move_to (x0, (y0 + y1)/2);
448 context->curve_to (x0 , y0, x0 , y0, x0 + radius, y0);
449 context->line_to (x1 - radius, y0);
450 context->curve_to (x1, y0, x1, y0, x1, (y0 + y1)/2);
451 context->curve_to (x1, y1, x1, y1, x1 - radius, y1);
452 context->line_to (x0 + radius, y1);
453 context->curve_to (x0, y1, x0, y1, x0, (y0 + y1)/2);
454 } else {
455 context->move_to (x0, y0 + radius);
456 context->curve_to (x0 , y0, x0 , y0, x0 + radius, y0);
457 context->line_to (x1 - radius, y0);
458 context->curve_to (x1, y0, x1, y0, x1, y0 + radius);
459 context->line_to (x1 , y1 - radius);
460 context->curve_to (x1, y1, x1, y1, x1 - radius, y1);
461 context->line_to (x0 + radius, y1);
462 context->curve_to (x0, y1, x0, y1, x0, y1- radius);
466 context->close_path ();
467 context->restore();
470 #endif