tweak search path for export profiles
[ardour.git] / libs / gtkmm2ext / utils.cc
1 /*
2     Copyright (C) 1999 Paul Barton-Davis 
3
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.
8
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.
13
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.
17
18     $Id$
19 */
20
21 #include <map>
22
23 #include <gtk/gtkpaned.h>
24 #include <gtk/gtk.h>
25
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>
32
33 #include "i18n.h"
34
35 using namespace std;
36
37 void
38 Gtkmm2ext::init ()
39 {
40         // Necessary for gettext
41         (void) bindtextdomain(PACKAGE, LOCALEDIR);
42 }
43
44 void
45 Gtkmm2ext::get_ink_pixel_size (Glib::RefPtr<Pango::Layout> layout,
46                                int& width,
47                                int& height)
48 {
49         Pango::Rectangle ink_rect = layout->get_ink_extents ();
50         
51         width = (ink_rect.get_width() + PANGO_SCALE / 2) / PANGO_SCALE;
52         height = (ink_rect.get_height() + PANGO_SCALE / 2) / PANGO_SCALE;
53 }
54
55 void
56 get_pixel_size (Glib::RefPtr<Pango::Layout> layout,
57                                int& width,
58                                int& height)
59 {
60         layout->get_pixel_size (width, height);
61 }
62
63 void
64 Gtkmm2ext::set_size_request_to_display_given_text (Gtk::Widget &w, const gchar *text,
65                                                    gint hpadding, gint vpadding)
66         
67 {
68         int width, height;
69         w.ensure_style ();
70         
71         get_pixel_size (w.create_pango_layout (text), width, height);
72         w.set_size_request(width + hpadding, height + vpadding);
73 }
74
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)
79         
80 {
81         int width, height;
82         int width_max = 0;
83         int height_max = 0;
84         w.ensure_style ();
85         
86         for (vector<string>::const_iterator i = strings.begin(); i != strings.end(); ++i) {
87                 get_pixel_size (w.create_pango_layout (*i), width, height);
88                 width_max = max(width_max,width);
89                 height_max = max(height_max, height);
90         }
91         w.set_size_request(width_max + hpadding, height_max + vpadding);
92 }
93
94 static inline guint8
95 demultiply_alpha (guint8 src,
96                   guint8 alpha)
97 {
98         /* cairo pixel buffer data contains RGB values with the alpha
99            values premultiplied.
100
101            GdkPixbuf pixel buffer data contains RGB values without the
102            alpha value applied.
103
104            this removes the alpha component from the cairo version and
105            returns the GdkPixbuf version.
106         */
107         return alpha ? ((guint (src) << 8) - src) / alpha : 0;
108 }
109
110 static void
111 convert_bgra_to_rgba (guint8 const* src,
112                       guint8*       dst,
113                       int           width,
114                       int           height)
115 {
116         guint8 const* src_pixel = src;
117         guint8*       dst_pixel = dst;
118         
119         /* cairo pixel data is endian-dependent ARGB with A in the most significant 8 bits,
120            with premultipled alpha values (see preceding function)
121
122            GdkPixbuf pixel data is non-endian-dependent RGBA with R in the lowest addressable
123            8 bits, and non-premultiplied alpha values.
124
125            convert from the cairo values to the GdkPixbuf ones.
126         */
127
128         for (int y = 0; y < height; y++) {
129                 for (int x = 0; x < width; x++) {
130 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
131                         /* Cairo [ B G R A ] is actually  [ B G R A ] in memory SOURCE
132                                                             0 1 2 3
133                            Pixbuf [ R G B A ] is actually [ R G B A ] in memory DEST
134                         */
135                         dst_pixel[0] = demultiply_alpha (src_pixel[2],
136                                                          src_pixel[3]); // R [0] <= [ 2 ]
137                         dst_pixel[1] = demultiply_alpha (src_pixel[1],
138                                                          src_pixel[3]); // G [1] <= [ 1 ]
139                         dst_pixel[2] = demultiply_alpha (src_pixel[0],  
140                                                          src_pixel[3]); // B [2] <= [ 0 ]
141                         dst_pixel[3] = src_pixel[3]; // alpha
142                         
143 #elif G_BYTE_ORDER == G_BIG_ENDIAN
144                         /* Cairo [ B G R A ] is actually  [ A R G B ] in memory SOURCE
145                                                             0 1 2 3
146                            Pixbuf [ R G B A ] is actually [ R G B A ] in memory DEST
147                         */
148                         dst_pixel[0] = demultiply_alpha (src_pixel[1],
149                                                          src_pixel[0]); // R [0] <= [ 1 ]
150                         dst_pixel[1] = demultiply_alpha (src_pixel[2],
151                                                          src_pixel[0]); // G [1] <= [ 2 ]
152                         dst_pixel[2] = demultiply_alpha (src_pixel[3],
153                                                          src_pixel[0]); // B [2] <= [ 3 ]
154                         dst_pixel[3] = src_pixel[0]; // alpha
155                         
156 #else
157 #error ardour does not currently support PDP-endianess
158 #endif                  
159                         
160                         dst_pixel += 4;
161                         src_pixel += 4;
162                 }
163         }
164 }
165
166 Glib::RefPtr<Gdk::Pixbuf>
167 Gtkmm2ext::pixbuf_from_string(const string& name, Pango::FontDescription* font, int clip_width, int clip_height, Gdk::Color fg)
168 {
169         static Glib::RefPtr<Gdk::Pixbuf>* empty_pixbuf = 0;
170
171         if (name.empty()) {
172                 if (empty_pixbuf == 0) {
173                         empty_pixbuf = new Glib::RefPtr<Gdk::Pixbuf>;
174                         *empty_pixbuf = Gdk::Pixbuf::create(Gdk::COLORSPACE_RGB, true, 8, clip_width, clip_height);
175                 }
176                 return *empty_pixbuf;
177         }
178
179         Glib::RefPtr<Gdk::Pixbuf> buf = Gdk::Pixbuf::create(Gdk::COLORSPACE_RGB, true, 8, clip_width, clip_height);
180
181         cairo_surface_t* surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, clip_width, clip_height);
182         cairo_t* cr = cairo_create (surface);
183         cairo_text_extents_t te;
184         
185         cairo_set_source_rgba (cr, fg.get_red_p(), fg.get_green_p(), fg.get_blue_p(), 1.0);
186         cairo_select_font_face (cr, font->get_family().c_str(),
187                                 CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
188         cairo_set_font_size (cr,  font->get_size() / Pango::SCALE);
189         cairo_text_extents (cr, name.c_str(), &te);
190         
191         cairo_move_to (cr, 0.5, int (0.5 - te.height / 2 - te.y_bearing + clip_height / 2));
192         cairo_show_text (cr, name.c_str());
193         
194         convert_bgra_to_rgba(cairo_image_surface_get_data (surface), buf->get_pixels(), clip_width, clip_height);
195
196         cairo_destroy(cr);
197         cairo_surface_destroy(surface);
198
199         return buf;
200 }
201
202 void
203 Gtkmm2ext::set_popdown_strings (Gtk::ComboBoxText& cr, const vector<string>& strings, bool set_size, gint hpadding, gint vpadding)
204 {
205         vector<string>::const_iterator i;
206
207         cr.clear ();
208
209         if (set_size) {
210                 vector<string> copy;
211
212                 for (i = strings.begin(); i != strings.end(); ++i) {
213                         if ((*i).find_first_of ("gy") != string::npos) {
214                                 /* contains a descender */
215                                 break;
216                         }
217                 }
218                 
219                 if (i == strings.end()) {
220                         
221                         /* make a copy of the strings then add one that has a descener */
222                         
223                         copy = strings;
224                         copy.push_back ("g");
225                         set_size_request_to_display_given_text (cr, copy, COMBO_FUDGE+10+hpadding, 15+vpadding); 
226
227                 } else {
228                         set_size_request_to_display_given_text (cr, strings, COMBO_FUDGE+10+hpadding, 15+vpadding); 
229                 }
230         }
231
232         for (i = strings.begin(); i != strings.end(); ++i) {
233                 cr.append_text (*i);
234         }
235 }
236
237 GdkWindow*
238 Gtkmm2ext::get_paned_handle (Gtk::Paned& paned)
239 {
240         return GTK_PANED(paned.gobj())->handle;
241 }
242
243 void
244 Gtkmm2ext::set_decoration (Gtk::Window* win, Gdk::WMDecoration decor)
245 {
246         win->get_window()->set_decorations (decor);
247 }
248
249 void Gtkmm2ext::set_treeview_header_as_default_label(Gtk::TreeViewColumn* c)
250 {
251         gtk_tree_view_column_set_widget( c->gobj(), GTK_WIDGET(0) );
252 }
253
254 void
255 Gtkmm2ext::detach_menu (Gtk::Menu& menu)
256 {
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
259            detach method.
260         */
261         if (menu.gobj()) {
262                 if (menu.get_attach_widget()) {
263                         menu.detach ();
264                 }
265         }
266 }
267
268 bool
269 Gtkmm2ext::possibly_translate_keyval_to_make_legal_accelerator (uint32_t& keyval)
270 {
271         int fakekey = GDK_VoidSymbol;
272
273         switch (keyval) {
274         case GDK_Tab:
275         case GDK_ISO_Left_Tab:
276                 fakekey = GDK_nabla;
277                 break;
278
279         case GDK_Up:
280                 fakekey = GDK_uparrow;
281                 break;
282
283         case GDK_Down:
284                 fakekey = GDK_downarrow;
285                 break;
286
287         case GDK_Right:
288                 fakekey = GDK_rightarrow;
289                 break;
290
291         case GDK_Left:
292                 fakekey = GDK_leftarrow;
293                 break;
294
295         case GDK_Return:
296                 fakekey = GDK_3270_Enter;
297                 break;
298
299         case GDK_KP_Enter:
300                 fakekey = GDK_F35;
301                 break;
302
303         default:
304                 break;
305         }
306
307         if (fakekey != GDK_VoidSymbol) {
308                 keyval = fakekey;
309                 return true;
310         }
311
312         return false;
313 }
314
315 uint32_t
316 Gtkmm2ext::possibly_translate_legal_accelerator_to_real_key (uint32_t keyval)
317 {
318         switch (keyval) {
319         case GDK_nabla:
320                 return GDK_Tab;
321                 break;
322
323         case GDK_uparrow:
324                 return GDK_Up;
325                 break;
326
327         case GDK_downarrow:
328                 return GDK_Down;
329                 break;
330
331         case GDK_rightarrow:
332                 return GDK_Right;
333                 break;
334
335         case GDK_leftarrow:
336                 return GDK_Left;
337                 break;
338
339         case GDK_3270_Enter:
340                 return GDK_Return;
341
342         case GDK_F35:
343                 return GDK_KP_Enter;
344                 break;
345         }
346
347         return keyval;
348 }
349
350 int
351 Gtkmm2ext::physical_screen_height (Glib::RefPtr<Gdk::Window> win)
352 {
353         GdkScreen* scr = gdk_screen_get_default();
354
355         if (win) {
356                 GdkRectangle r;
357                 gint monitor = gdk_screen_get_monitor_at_window (scr, win->gobj());
358                 gdk_screen_get_monitor_geometry (scr, monitor, &r);
359                 return r.height;
360         } else {
361                 return gdk_screen_get_height (scr);
362         }
363 }
364
365 int
366 Gtkmm2ext::physical_screen_width (Glib::RefPtr<Gdk::Window> win)
367 {
368         GdkScreen* scr = gdk_screen_get_default();
369         
370         if (win) {
371                 GdkRectangle r;
372                 gint monitor = gdk_screen_get_monitor_at_window (scr, win->gobj());
373                 gdk_screen_get_monitor_geometry (scr, monitor, &r);
374                 return r.width;
375         } else {
376                 return gdk_screen_get_width (scr);
377         }
378 }