Change the GtkClutter macro namespace
[clutter-gtk] / clutter-gtk / gtk-clutter-util.c
1 #ifdef HAVE_CONFIG_H
2 #include "config.h"
3 #endif
4
5 #include <gdk-pixbuf/gdk-pixbuf.h>
6 #include <gtk/gtk.h>
7 #include <clutter/clutter.h>
8
9 #include "gtk-clutter-util.h"
10
11 /**
12  * SECTION:gtk-clutter-util
13  * @short_description: Utility functions for integrating Clutter in GTK+
14  *
15  * In order to properly integrate a Clutter scene into a GTK+ applications
16  * a certain degree of state must be retrieved from GTK+ itself.
17  *
18  * Clutter-GTK provides API for easing the process of synchronizing colors
19  * with the current GTK+ theme and for loading image sources from #GdkPixbuf,
20  * GTK+ stock items and icon themes.
21  */
22
23 enum
24 {
25   /* base symbols from GtkRcStyle */
26   FG_COMPONENT = GTK_RC_FG,
27   BG_COMPONENT = GTK_RC_BG,
28   TEXT_COMPONENT = GTK_RC_TEXT,
29   BASE_COMPONENT = GTK_RC_BASE,
30
31   /* symbols used by GtkStyle */
32   LIGHT_COMPONENT,
33   MID_COMPONENT,
34   DARK_COMPONENT,
35   TEXT_AA_COMPONENT
36 };
37
38 static inline void
39 gtk_clutter_get_component (GtkWidget    *widget,
40                            gint          component,
41                            GtkStateType  state,
42                            ClutterColor *color)
43 {
44   GtkStyle *style = gtk_widget_get_style (widget);
45   GdkColor gtk_color = { 0, };
46
47   switch (component)
48     {
49     case FG_COMPONENT:
50       gtk_color = style->fg[state];
51       break;
52
53     case BG_COMPONENT:
54       gtk_color = style->bg[state];
55       break;
56
57     case TEXT_COMPONENT:
58       gtk_color = style->text[state];
59       break;
60
61     case BASE_COMPONENT:
62       gtk_color = style->base[state];
63       break;
64
65     case LIGHT_COMPONENT:
66       gtk_color = style->light[state];
67       break;
68
69     case MID_COMPONENT:
70       gtk_color = style->mid[state];
71       break;
72
73     case DARK_COMPONENT:
74       gtk_color = style->dark[state];
75       break;
76
77     case TEXT_AA_COMPONENT:
78       gtk_color = style->text_aa[state];
79       break;
80
81     default:
82       g_assert_not_reached ();
83       break;
84     }
85
86   color->red   = CLAMP (((gtk_color.red   / 65535.0) * 255), 0, 255);
87   color->green = CLAMP (((gtk_color.green / 65535.0) * 255), 0, 255);
88   color->blue  = CLAMP (((gtk_color.blue  / 65535.0) * 255), 0, 255);
89   color->alpha = 255;
90 }
91
92 /**
93  * gtk_clutter_get_fg_color:
94  * @widget: a #GtkWidget
95  * @state: a state
96  * @color: return location for a #ClutterColor
97  *
98  * Retrieves the foreground color of @widget for the given @state and copies
99  * it into @color.
100  *
101  * Since: 0.8
102  */
103 void
104 gtk_clutter_get_fg_color (GtkWidget    *widget,
105                           GtkStateType  state,
106                           ClutterColor *color)
107 {
108   g_return_if_fail (GTK_IS_WIDGET (widget));
109   g_return_if_fail (state >= GTK_STATE_NORMAL &&
110                     state <= GTK_STATE_INSENSITIVE);
111   g_return_if_fail (color != NULL);
112
113   gtk_clutter_get_component (widget, FG_COMPONENT, state, color);
114 }
115
116 /**
117  * gtk_clutter_get_bg_color:
118  * @widget: a #GtkWidget
119  * @state: a state
120  * @color: return location for a #ClutterColor
121  *
122  * Retrieves the background color of @widget for the given @state and copies
123  * it into @color.
124  *
125  * Since: 0.8
126  */
127 void
128 gtk_clutter_get_bg_color (GtkWidget    *widget,
129                           GtkStateType  state,
130                           ClutterColor *color)
131 {
132   g_return_if_fail (GTK_IS_WIDGET (widget));
133   g_return_if_fail (state >= GTK_STATE_NORMAL &&
134                     state <= GTK_STATE_INSENSITIVE);
135   g_return_if_fail (color != NULL);
136
137   gtk_clutter_get_component (widget, BG_COMPONENT, state, color);
138 }
139
140 /**
141  * gtk_clutter_get_text_color:
142  * @widget: a #GtkWidget
143  * @state: a state
144  * @color: return location for a #ClutterColor
145  *
146  * Retrieves the text color of @widget for the given @state and copies it
147  * into @color.
148  *
149  * Since: 0.8
150  */
151 void
152 gtk_clutter_get_text_color (GtkWidget    *widget,
153                             GtkStateType  state,
154                             ClutterColor *color)
155 {
156   g_return_if_fail (GTK_IS_WIDGET (widget));
157   g_return_if_fail (state >= GTK_STATE_NORMAL &&
158                     state <= GTK_STATE_INSENSITIVE);
159   g_return_if_fail (color != NULL);
160
161   gtk_clutter_get_component (widget, TEXT_COMPONENT, state, color);
162 }
163
164 /**
165  * gtk_clutter_get_base_color:
166  * @widget: a #GtkWidget
167  * @state: a state
168  * @color: return location for a #ClutterColor
169  *
170  * Retrieves the base color of @widget for the given @state and copies it
171  * into @color.
172  *
173  * Since: 0.8
174  */
175 void
176 gtk_clutter_get_base_color (GtkWidget    *widget,
177                             GtkStateType  state,
178                             ClutterColor *color)
179 {
180   g_return_if_fail (GTK_IS_WIDGET (widget));
181   g_return_if_fail (state >= GTK_STATE_NORMAL &&
182                     state <= GTK_STATE_INSENSITIVE);
183   g_return_if_fail (color != NULL);
184
185   gtk_clutter_get_component (widget, BASE_COMPONENT, state, color);
186 }
187
188 /**
189  * gtk_clutter_get_light_color:
190  * @widget: a #GtkWidget
191  * @state: a state
192  * @color: return location for a #ClutterColor
193  *
194  * Retrieves the light color of @widget for the given @state and copies it
195  * into @color.
196  *
197  * Since: 0.8
198  */
199 void
200 gtk_clutter_get_light_color (GtkWidget    *widget,
201                              GtkStateType  state,
202                              ClutterColor *color)
203 {
204   g_return_if_fail (GTK_IS_WIDGET (widget));
205   g_return_if_fail (state >= GTK_STATE_NORMAL &&
206                     state <= GTK_STATE_INSENSITIVE);
207   g_return_if_fail (color != NULL);
208
209   gtk_clutter_get_component (widget, LIGHT_COMPONENT, state, color);
210 }
211
212 /**
213  * gtk_clutter_get_mid_color:
214  * @widget: a #GtkWidget
215  * @state: a state
216  * @color: return location for a #ClutterColor
217  *
218  * Retrieves the mid color of @widget for the given @state and copies it
219  * into @color.
220  *
221  * Since: 0.8
222  */
223 void
224 gtk_clutter_get_mid_color (GtkWidget    *widget,
225                            GtkStateType  state,
226                            ClutterColor *color)
227 {
228   g_return_if_fail (GTK_IS_WIDGET (widget));
229   g_return_if_fail (state >= GTK_STATE_NORMAL &&
230                     state <= GTK_STATE_INSENSITIVE);
231   g_return_if_fail (color != NULL);
232
233   gtk_clutter_get_component (widget, MID_COMPONENT, state, color);
234 }
235
236 /**
237  * gtk_clutter_get_dark_color:
238  * @widget: a #GtkWidget
239  * @state: a state
240  * @color: return location for a #ClutterColor
241  *
242  * Retrieves the dark color of @widget for the given @state and copies it
243  * into @color.
244  *
245  * Since: 0.8
246  */
247 void
248 gtk_clutter_get_dark_color (GtkWidget    *widget,
249                             GtkStateType  state,
250                             ClutterColor *color)
251 {
252   g_return_if_fail (GTK_IS_WIDGET (widget));
253   g_return_if_fail (state >= GTK_STATE_NORMAL &&
254                     state <= GTK_STATE_INSENSITIVE);
255   g_return_if_fail (color != NULL);
256
257   gtk_clutter_get_component (widget, DARK_COMPONENT, state, color);
258 }
259
260 /**
261  * gtk_clutter_get_text_aa_color:
262  * @widget: a #GtkWidget
263  * @state: a state
264  * @color: return location for a #ClutterColor
265  *
266  * Retrieves the text-aa color of @widget for the given @state and copies it
267  * into @color.
268  *
269  * Since: 0.8
270  */
271 void
272 gtk_clutter_get_text_aa_color (GtkWidget    *widget,
273                                GtkStateType  state,
274                                ClutterColor *color)
275 {
276   g_return_if_fail (GTK_IS_WIDGET (widget));
277   g_return_if_fail (state >= GTK_STATE_NORMAL &&
278                     state <= GTK_STATE_INSENSITIVE);
279   g_return_if_fail (color != NULL);
280
281   gtk_clutter_get_component (widget, TEXT_AA_COMPONENT, state, color);
282 }
283
284 /**
285  * gtk_clutter_texture_new_from_pixbuf:
286  * @pixbuf: a #GdkPixbuf
287  *
288  * Creates a new #ClutterTexture and sets its contents with a copy
289  * of @pixbuf.
290  *
291  * Return value: the newly created #ClutterTexture
292  *
293  * Since: 0.8
294  */
295 ClutterActor *
296 gtk_clutter_texture_new_from_pixbuf (GdkPixbuf *pixbuf)
297 {
298   ClutterActor *retval;
299   GError *error;
300
301   g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL);
302
303   retval = clutter_texture_new ();
304
305   error = NULL;
306   clutter_texture_set_from_rgb_data (CLUTTER_TEXTURE (retval),
307                                      gdk_pixbuf_get_pixels (pixbuf),
308                                      gdk_pixbuf_get_has_alpha (pixbuf),
309                                      gdk_pixbuf_get_width (pixbuf),
310                                      gdk_pixbuf_get_height (pixbuf),
311                                      gdk_pixbuf_get_rowstride (pixbuf),
312                                      gdk_pixbuf_get_has_alpha (pixbuf) ? 4 : 3,
313                                      0,
314                                      &error);
315   if (error)
316     {
317       g_warning ("Unable to set the pixbuf: %s", error->message);
318       g_error_free (error);
319     }
320
321   return retval; 
322 }
323
324 /**
325  * gtk_clutter_texture_set_from_pixbuf:
326  * @texture: a #ClutterTexture
327  * @pixbuf: a #GdkPixbuf
328  *
329  * Sets the contents of @texture with a copy of @pixbuf.
330  *
331  * Since: 0.8
332  */
333 void
334 gtk_clutter_texture_set_from_pixbuf (ClutterTexture *texture,
335                                      GdkPixbuf      *pixbuf)
336 {
337   GError *error;
338
339   g_return_if_fail (CLUTTER_IS_TEXTURE (texture));
340   g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
341
342   error = NULL;
343   clutter_texture_set_from_rgb_data (texture,
344                                      gdk_pixbuf_get_pixels (pixbuf),
345                                      gdk_pixbuf_get_has_alpha (pixbuf),
346                                      gdk_pixbuf_get_width (pixbuf),
347                                      gdk_pixbuf_get_height (pixbuf),
348                                      gdk_pixbuf_get_rowstride (pixbuf),
349                                      gdk_pixbuf_get_has_alpha (pixbuf) ? 4 : 3,
350                                      0,
351                                      &error);
352   if (error)
353     {
354       g_warning ("Unable to set the pixbuf: %s", error->message);
355       g_error_free (error);
356     }
357 }
358
359 /**
360  * gtk_clutter_texture_new_from_stock:
361  * @widget: a #GtkWidget
362  * @stock_id: the stock id of the icon
363  * @size: the size of the icon, or -1
364  *
365  * Creates a new #ClutterTexture and sets its contents using the stock
366  * icon @stock_id as rendered by @widget.
367  *
368  * Return value: the newly created #ClutterTexture
369  *
370  * Since: 0.8
371  */
372 ClutterActor *
373 gtk_clutter_texture_new_from_stock (GtkWidget   *widget,
374                                     const gchar *stock_id,
375                                     GtkIconSize  size)
376 {
377   GdkPixbuf *pixbuf;
378   ClutterActor *retval;
379
380   g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
381   g_return_val_if_fail (stock_id != NULL, NULL);
382   g_return_val_if_fail (size > GTK_ICON_SIZE_INVALID || size == -1, NULL);
383
384   pixbuf = gtk_widget_render_icon (widget, stock_id, size, NULL);
385   if (!pixbuf)
386     pixbuf = gtk_widget_render_icon (widget,
387                                      GTK_STOCK_MISSING_IMAGE, size,
388                                      NULL);
389
390   retval = gtk_clutter_texture_new_from_pixbuf (pixbuf);
391   g_object_unref (pixbuf);
392
393   return retval;
394 }
395
396 /**
397  * gtk_clutter_texture_set_from_stock:
398  * @texture: a #ClutterTexture
399  * @widget: a #GtkWidget
400  * @stock_id: the stock id of the icon
401  * @size: the size of the icon, or -1
402  *
403  * Sets the contents of @texture using the stock icon @stock_id, as
404  * rendered by @widget.
405  *
406  * Since: 0.8
407  */
408 void
409 gtk_clutter_texture_set_from_stock (ClutterTexture *texture,
410                                     GtkWidget      *widget,
411                                     const gchar    *stock_id,
412                                     GtkIconSize     size)
413 {
414   GdkPixbuf *pixbuf;
415
416   g_return_if_fail (CLUTTER_IS_TEXTURE (texture));
417   g_return_if_fail (GTK_IS_WIDGET (widget));
418   g_return_if_fail (stock_id != NULL);
419   g_return_if_fail (size > GTK_ICON_SIZE_INVALID || size == -1);
420
421   pixbuf = gtk_widget_render_icon (widget, stock_id, size, NULL);
422   if (!pixbuf)
423     pixbuf = gtk_widget_render_icon (widget,
424                                      GTK_STOCK_MISSING_IMAGE, size,
425                                      NULL);
426
427   gtk_clutter_texture_set_from_pixbuf (texture, pixbuf);
428   g_object_unref (pixbuf);
429 }
430
431 /**
432  * gtk_clutter_texture_new_from_icon_name:
433  * @widget: a #GtkWidget or %NULL
434  * @icon_name: the name of the icon
435  * @size: the size of the icon, or -1
436  *
437  * Creates a new #ClutterTexture and sets its contents to be
438  * the @icon_name from the current icon theme.
439  *
440  * Return value: the newly created texture, or %NULL if @widget
441  *   was %NULL and @icon_name was not found.
442  *
443  * Since: 0.8
444  */
445 ClutterActor *
446 gtk_clutter_texture_new_from_icon_name (GtkWidget   *widget,
447                                         const gchar *icon_name,
448                                         GtkIconSize  size)
449 {
450   GtkSettings *settings;
451   GtkIconTheme *icon_theme;
452   gint width, height;
453   GdkPixbuf *pixbuf;
454   GError *error;
455   ClutterActor *retval;
456
457   g_return_val_if_fail (widget == NULL || GTK_IS_WIDGET (widget), NULL);
458   g_return_val_if_fail (icon_name != NULL, NULL);
459   g_return_val_if_fail (size > GTK_ICON_SIZE_INVALID || size == -1, NULL);
460
461   if (widget && gtk_widget_has_screen (widget))
462     {
463       GdkScreen *screen;
464
465       screen = gtk_widget_get_screen (widget);
466       settings = gtk_settings_get_for_screen (screen);
467       icon_theme = gtk_icon_theme_get_for_screen (screen);
468     }
469   else
470     {
471       settings = gtk_settings_get_default ();
472       icon_theme = gtk_icon_theme_get_default ();
473     }
474
475   if (size == -1 ||
476       !gtk_icon_size_lookup_for_settings (settings, size, &width, &height))
477     {
478       width = height = 48;
479     }
480
481   error = NULL;
482   pixbuf = gtk_icon_theme_load_icon (icon_theme,
483                                      icon_name,
484                                      MIN (width, height), 0,
485                                      &error);
486   if (error)
487     {
488       g_warning ("Unable to load the icon `%s' from the theme: %s",
489                  icon_name,
490                  error->message);
491
492       g_error_free (error);
493
494       if (widget)
495         return gtk_clutter_texture_new_from_stock (widget,
496                                              GTK_STOCK_MISSING_IMAGE,
497                                              size);
498       else
499         return NULL;
500     }
501
502   retval = gtk_clutter_texture_new_from_pixbuf (pixbuf);
503   g_object_unref (pixbuf);
504
505   return retval; 
506 }
507
508 /**
509  * gtk_clutter_texture_set_from_icon_name:
510  * @texture: a #ClutterTexture
511  * @widget: a #GtkWidget or %NULL
512  * @icon_name: the name of the icon
513  * @size: the icon size or -1
514  *
515  * Sets the contents of @texture using the @icon_name from the
516  * current icon theme.
517  *
518  * Since: 0.8
519  */
520 void
521 gtk_clutter_texture_set_from_icon_name (ClutterTexture *texture,
522                                         GtkWidget      *widget,
523                                         const gchar    *icon_name,
524                                         GtkIconSize     size)
525 {
526   GtkSettings *settings;
527   GtkIconTheme *icon_theme;
528   gint width, height;
529   GdkPixbuf *pixbuf;
530   GError *error;
531
532   g_return_if_fail (CLUTTER_IS_TEXTURE (texture));
533   g_return_if_fail (widget == NULL || GTK_IS_WIDGET (widget));
534   g_return_if_fail (icon_name != NULL);
535   g_return_if_fail (size > GTK_ICON_SIZE_INVALID || size == -1);
536
537   if (widget && gtk_widget_has_screen (widget))
538     {
539       GdkScreen *screen;
540
541       screen = gtk_widget_get_screen (widget);
542       settings = gtk_settings_get_for_screen (screen);
543       icon_theme = gtk_icon_theme_get_for_screen (screen);
544     }
545   else
546     {
547       settings = gtk_settings_get_default ();
548       icon_theme = gtk_icon_theme_get_default ();
549     }
550
551   if (size == -1 ||
552       !gtk_icon_size_lookup_for_settings (settings, size, &width, &height))
553     {
554       width = height = 48;
555     }
556
557   error = NULL;
558   pixbuf = gtk_icon_theme_load_icon (icon_theme,
559                                      icon_name,
560                                      MIN (width, height), 0,
561                                      &error);
562   if (error)
563     {
564       g_warning ("Unable to load the icon `%s' from the theme: %s",
565                  icon_name,
566                  error->message);
567
568       g_error_free (error);
569
570       if (widget)
571         gtk_clutter_texture_set_from_stock (texture,
572                                       widget,
573                                       GTK_STOCK_MISSING_IMAGE,
574                                       size);
575       else
576         return;
577     }
578
579   gtk_clutter_texture_set_from_pixbuf (texture, pixbuf);
580   g_object_unref (pixbuf);
581 }