[util] Add error arguments to texture functions
[clutter-gtk] / clutter-gtk / gtk-clutter-util.c
1 #ifdef HAVE_CONFIG_H
2 #include "config.h"
3 #endif
4
5 #include "gtk-clutter-util.h"
6
7 #include <gdk-pixbuf/gdk-pixbuf.h>
8 #include <gdk/gdk.h>
9 #include <gtk/gtk.h>
10 #include <clutter/clutter.h>
11
12 #if defined(HAVE_CLUTTER_GTK_X11)
13
14 #include <clutter/x11/clutter-x11.h>
15 #include <gdk/gdkx.h>
16
17 #elif defined(HAVE_CLUTTER_GTK_WIN32)
18
19 #include <clutter/clutter-win32.h>
20 #include <gdk/gdkwin32.h>
21
22 #endif /* HAVE_CLUTTER_GTK_{X11,WIN32} */
23
24 /**
25  * SECTION:gtk-clutter-util
26  * @short_description: Utility functions for integrating Clutter in GTK+
27  *
28  * In order to properly integrate a Clutter scene into a GTK+ applications
29  * a certain degree of state must be retrieved from GTK+ itself.
30  *
31  * Clutter-GTK provides API for easing the process of synchronizing colors
32  * with the current GTK+ theme and for loading image sources from #GdkPixbuf,
33  * GTK+ stock items and icon themes.
34  */
35
36 enum
37 {
38   /* base symbols from GtkRcStyle */
39   FG_COMPONENT = GTK_RC_FG,
40   BG_COMPONENT = GTK_RC_BG,
41   TEXT_COMPONENT = GTK_RC_TEXT,
42   BASE_COMPONENT = GTK_RC_BASE,
43
44   /* symbols used by GtkStyle */
45   LIGHT_COMPONENT,
46   MID_COMPONENT,
47   DARK_COMPONENT,
48   TEXT_AA_COMPONENT
49 };
50
51 static inline void
52 gtk_clutter_get_component (GtkWidget    *widget,
53                            gint          component,
54                            GtkStateType  state,
55                            ClutterColor *color)
56 {
57   GtkStyle *style = gtk_widget_get_style (widget);
58   GdkColor gtk_color = { 0, };
59
60   switch (component)
61     {
62     case FG_COMPONENT:
63       gtk_color = style->fg[state];
64       break;
65
66     case BG_COMPONENT:
67       gtk_color = style->bg[state];
68       break;
69
70     case TEXT_COMPONENT:
71       gtk_color = style->text[state];
72       break;
73
74     case BASE_COMPONENT:
75       gtk_color = style->base[state];
76       break;
77
78     case LIGHT_COMPONENT:
79       gtk_color = style->light[state];
80       break;
81
82     case MID_COMPONENT:
83       gtk_color = style->mid[state];
84       break;
85
86     case DARK_COMPONENT:
87       gtk_color = style->dark[state];
88       break;
89
90     case TEXT_AA_COMPONENT:
91       gtk_color = style->text_aa[state];
92       break;
93
94     default:
95       g_assert_not_reached ();
96       break;
97     }
98
99   color->red   = CLAMP (((gtk_color.red   / 65535.0) * 255), 0, 255);
100   color->green = CLAMP (((gtk_color.green / 65535.0) * 255), 0, 255);
101   color->blue  = CLAMP (((gtk_color.blue  / 65535.0) * 255), 0, 255);
102   color->alpha = 255;
103 }
104
105 /**
106  * gtk_clutter_get_fg_color:
107  * @widget: a #GtkWidget
108  * @state: a state
109  * @color: return location for a #ClutterColor
110  *
111  * Retrieves the foreground color of @widget for the given @state and copies
112  * it into @color.
113  *
114  * Since: 0.8
115  */
116 void
117 gtk_clutter_get_fg_color (GtkWidget    *widget,
118                           GtkStateType  state,
119                           ClutterColor *color)
120 {
121   g_return_if_fail (GTK_IS_WIDGET (widget));
122   g_return_if_fail (state >= GTK_STATE_NORMAL &&
123                     state <= GTK_STATE_INSENSITIVE);
124   g_return_if_fail (color != NULL);
125
126   gtk_clutter_get_component (widget, FG_COMPONENT, state, color);
127 }
128
129 /**
130  * gtk_clutter_get_bg_color:
131  * @widget: a #GtkWidget
132  * @state: a state
133  * @color: return location for a #ClutterColor
134  *
135  * Retrieves the background color of @widget for the given @state and copies
136  * it into @color.
137  *
138  * Since: 0.8
139  */
140 void
141 gtk_clutter_get_bg_color (GtkWidget    *widget,
142                           GtkStateType  state,
143                           ClutterColor *color)
144 {
145   g_return_if_fail (GTK_IS_WIDGET (widget));
146   g_return_if_fail (state >= GTK_STATE_NORMAL &&
147                     state <= GTK_STATE_INSENSITIVE);
148   g_return_if_fail (color != NULL);
149
150   gtk_clutter_get_component (widget, BG_COMPONENT, state, color);
151 }
152
153 /**
154  * gtk_clutter_get_text_color:
155  * @widget: a #GtkWidget
156  * @state: a state
157  * @color: return location for a #ClutterColor
158  *
159  * Retrieves the text color of @widget for the given @state and copies it
160  * into @color.
161  *
162  * Since: 0.8
163  */
164 void
165 gtk_clutter_get_text_color (GtkWidget    *widget,
166                             GtkStateType  state,
167                             ClutterColor *color)
168 {
169   g_return_if_fail (GTK_IS_WIDGET (widget));
170   g_return_if_fail (state >= GTK_STATE_NORMAL &&
171                     state <= GTK_STATE_INSENSITIVE);
172   g_return_if_fail (color != NULL);
173
174   gtk_clutter_get_component (widget, TEXT_COMPONENT, state, color);
175 }
176
177 /**
178  * gtk_clutter_get_base_color:
179  * @widget: a #GtkWidget
180  * @state: a state
181  * @color: return location for a #ClutterColor
182  *
183  * Retrieves the base color of @widget for the given @state and copies it
184  * into @color.
185  *
186  * Since: 0.8
187  */
188 void
189 gtk_clutter_get_base_color (GtkWidget    *widget,
190                             GtkStateType  state,
191                             ClutterColor *color)
192 {
193   g_return_if_fail (GTK_IS_WIDGET (widget));
194   g_return_if_fail (state >= GTK_STATE_NORMAL &&
195                     state <= GTK_STATE_INSENSITIVE);
196   g_return_if_fail (color != NULL);
197
198   gtk_clutter_get_component (widget, BASE_COMPONENT, state, color);
199 }
200
201 /**
202  * gtk_clutter_get_light_color:
203  * @widget: a #GtkWidget
204  * @state: a state
205  * @color: return location for a #ClutterColor
206  *
207  * Retrieves the light color of @widget for the given @state and copies it
208  * into @color.
209  *
210  * Since: 0.8
211  */
212 void
213 gtk_clutter_get_light_color (GtkWidget    *widget,
214                              GtkStateType  state,
215                              ClutterColor *color)
216 {
217   g_return_if_fail (GTK_IS_WIDGET (widget));
218   g_return_if_fail (state >= GTK_STATE_NORMAL &&
219                     state <= GTK_STATE_INSENSITIVE);
220   g_return_if_fail (color != NULL);
221
222   gtk_clutter_get_component (widget, LIGHT_COMPONENT, state, color);
223 }
224
225 /**
226  * gtk_clutter_get_mid_color:
227  * @widget: a #GtkWidget
228  * @state: a state
229  * @color: return location for a #ClutterColor
230  *
231  * Retrieves the mid color of @widget for the given @state and copies it
232  * into @color.
233  *
234  * Since: 0.8
235  */
236 void
237 gtk_clutter_get_mid_color (GtkWidget    *widget,
238                            GtkStateType  state,
239                            ClutterColor *color)
240 {
241   g_return_if_fail (GTK_IS_WIDGET (widget));
242   g_return_if_fail (state >= GTK_STATE_NORMAL &&
243                     state <= GTK_STATE_INSENSITIVE);
244   g_return_if_fail (color != NULL);
245
246   gtk_clutter_get_component (widget, MID_COMPONENT, state, color);
247 }
248
249 /**
250  * gtk_clutter_get_dark_color:
251  * @widget: a #GtkWidget
252  * @state: a state
253  * @color: return location for a #ClutterColor
254  *
255  * Retrieves the dark color of @widget for the given @state and copies it
256  * into @color.
257  *
258  * Since: 0.8
259  */
260 void
261 gtk_clutter_get_dark_color (GtkWidget    *widget,
262                             GtkStateType  state,
263                             ClutterColor *color)
264 {
265   g_return_if_fail (GTK_IS_WIDGET (widget));
266   g_return_if_fail (state >= GTK_STATE_NORMAL &&
267                     state <= GTK_STATE_INSENSITIVE);
268   g_return_if_fail (color != NULL);
269
270   gtk_clutter_get_component (widget, DARK_COMPONENT, state, color);
271 }
272
273 /**
274  * gtk_clutter_get_text_aa_color:
275  * @widget: a #GtkWidget
276  * @state: a state
277  * @color: return location for a #ClutterColor
278  *
279  * Retrieves the text-aa color of @widget for the given @state and copies it
280  * into @color.
281  *
282  * Since: 0.8
283  */
284 void
285 gtk_clutter_get_text_aa_color (GtkWidget    *widget,
286                                GtkStateType  state,
287                                ClutterColor *color)
288 {
289   g_return_if_fail (GTK_IS_WIDGET (widget));
290   g_return_if_fail (state >= GTK_STATE_NORMAL &&
291                     state <= GTK_STATE_INSENSITIVE);
292   g_return_if_fail (color != NULL);
293
294   gtk_clutter_get_component (widget, TEXT_AA_COMPONENT, state, color);
295 }
296
297 /**
298  * gtk_clutter_texture_new_from_pixbuf:
299  * @pixbuf: a #GdkPixbuf
300  *
301  * Creates a new #ClutterTexture and sets its contents with a copy
302  * of @pixbuf.
303  *
304  * Return value: the newly created #ClutterTexture
305  *
306  * Since: 0.8
307  */
308 ClutterActor *
309 gtk_clutter_texture_new_from_pixbuf (GdkPixbuf *pixbuf)
310 {
311   ClutterActor *retval;
312   GError *error;
313
314   g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL);
315
316   retval = clutter_texture_new ();
317
318   error = NULL;
319   clutter_texture_set_from_rgb_data (CLUTTER_TEXTURE (retval),
320                                      gdk_pixbuf_get_pixels (pixbuf),
321                                      gdk_pixbuf_get_has_alpha (pixbuf),
322                                      gdk_pixbuf_get_width (pixbuf),
323                                      gdk_pixbuf_get_height (pixbuf),
324                                      gdk_pixbuf_get_rowstride (pixbuf),
325                                      gdk_pixbuf_get_has_alpha (pixbuf) ? 4 : 3,
326                                      0,
327                                      &error);
328   if (error)
329     {
330       g_warning ("Unable to set the pixbuf: %s", error->message);
331       g_error_free (error);
332     }
333
334   return retval; 
335 }
336
337 GQuark
338 gtk_clutter_texture_error_quark (void)
339 {
340   return g_quark_from_static_string ("clutter-gtk-texture-error-quark");
341 }
342
343 /**
344  * gtk_clutter_texture_set_from_pixbuf:
345  * @texture: a #ClutterTexture
346  * @pixbuf: a #GdkPixbuf
347  * @error: a return location for errors
348  *
349  * Sets the contents of @texture with a copy of @pixbuf.
350  *
351  * Return value: %TRUE on success, %FALSE on failure.
352  *
353  * Since: 0.8
354  */
355 gboolean
356 gtk_clutter_texture_set_from_pixbuf (ClutterTexture *texture,
357                                      GdkPixbuf      *pixbuf,
358                                      GError        **error)
359 {
360   g_return_val_if_fail (CLUTTER_IS_TEXTURE (texture), FALSE);
361   g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), FALSE);
362
363   return clutter_texture_set_from_rgb_data (texture,
364                                      gdk_pixbuf_get_pixels (pixbuf),
365                                      gdk_pixbuf_get_has_alpha (pixbuf),
366                                      gdk_pixbuf_get_width (pixbuf),
367                                      gdk_pixbuf_get_height (pixbuf),
368                                      gdk_pixbuf_get_rowstride (pixbuf),
369                                      gdk_pixbuf_get_has_alpha (pixbuf) ? 4 : 3,
370                                      0,
371                                      error);
372 }
373
374 /**
375  * gtk_clutter_texture_new_from_stock:
376  * @widget: a #GtkWidget
377  * @stock_id: the stock id of the icon
378  * @size: the size of the icon, or -1
379  *
380  * Creates a new #ClutterTexture and sets its contents using the stock
381  * icon @stock_id as rendered by @widget.
382  *
383  * Return value: the newly created #ClutterTexture
384  *
385  * Since: 0.8
386  */
387 ClutterActor *
388 gtk_clutter_texture_new_from_stock (GtkWidget   *widget,
389                                     const gchar *stock_id,
390                                     GtkIconSize  size)
391 {
392   GdkPixbuf *pixbuf;
393   ClutterActor *retval;
394
395   g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
396   g_return_val_if_fail (stock_id != NULL, NULL);
397   g_return_val_if_fail (size > GTK_ICON_SIZE_INVALID || size == -1, NULL);
398
399   pixbuf = gtk_widget_render_icon (widget, stock_id, size, NULL);
400   if (!pixbuf)
401     pixbuf = gtk_widget_render_icon (widget,
402                                      GTK_STOCK_MISSING_IMAGE, size,
403                                      NULL);
404
405   retval = gtk_clutter_texture_new_from_pixbuf (pixbuf);
406   g_object_unref (pixbuf);
407
408   return retval;
409 }
410
411 /**
412  * gtk_clutter_texture_set_from_stock:
413  * @texture: a #ClutterTexture
414  * @widget: a #GtkWidget
415  * @stock_id: the stock id of the icon
416  * @size: the size of the icon, or -1
417  * @error: a return location for errors
418  *
419  * Sets the contents of @texture using the stock icon @stock_id, as
420  * rendered by @widget.
421  *
422  * Return value: %TRUE on success, %FALSE on failure.
423  *
424  * Since: 0.8
425  */
426 gboolean
427 gtk_clutter_texture_set_from_stock (ClutterTexture *texture,
428                                     GtkWidget      *widget,
429                                     const gchar    *stock_id,
430                                     GtkIconSize     size,
431                                     GError        **error)
432 {
433   GdkPixbuf *pixbuf;
434   gboolean returnval;
435
436   g_return_val_if_fail (CLUTTER_IS_TEXTURE (texture), FALSE);
437   g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
438   g_return_val_if_fail (stock_id != NULL, FALSE);
439   g_return_val_if_fail ((size > GTK_ICON_SIZE_INVALID) || (size == -1), FALSE);
440
441   pixbuf = gtk_widget_render_icon (widget, stock_id, size, NULL);
442   if (!pixbuf)
443     {
444       g_set_error (error,
445                    CLUTTER_GTK_TEXTURE_ERROR,
446                    CLUTTER_GTK_TEXTURE_INVALID_STOCK_ID,
447                    "Stock ID '%s' not found", stock_id);
448       return FALSE;
449     }
450
451   returnval = gtk_clutter_texture_set_from_pixbuf (texture, pixbuf, error);
452   g_object_unref (pixbuf);
453
454   return returnval;
455 }
456
457 /**
458  * gtk_clutter_texture_new_from_icon_name:
459  * @widget: a #GtkWidget or %NULL
460  * @icon_name: the name of the icon
461  * @size: the size of the icon, or -1
462  *
463  * Creates a new #ClutterTexture and sets its contents to be
464  * the @icon_name from the current icon theme.
465  *
466  * Return value: the newly created texture, or %NULL if @widget
467  *   was %NULL and @icon_name was not found.
468  *
469  * Since: 0.8
470  */
471 ClutterActor *
472 gtk_clutter_texture_new_from_icon_name (GtkWidget   *widget,
473                                         const gchar *icon_name,
474                                         GtkIconSize  size)
475 {
476   GtkSettings *settings;
477   GtkIconTheme *icon_theme;
478   gint width, height;
479   GdkPixbuf *pixbuf;
480   GError *error;
481   ClutterActor *retval;
482
483   g_return_val_if_fail (widget == NULL || GTK_IS_WIDGET (widget), NULL);
484   g_return_val_if_fail (icon_name != NULL, NULL);
485   g_return_val_if_fail (size > GTK_ICON_SIZE_INVALID || size == -1, NULL);
486
487   if (widget && gtk_widget_has_screen (widget))
488     {
489       GdkScreen *screen;
490
491       screen = gtk_widget_get_screen (widget);
492       settings = gtk_settings_get_for_screen (screen);
493       icon_theme = gtk_icon_theme_get_for_screen (screen);
494     }
495   else
496     {
497       settings = gtk_settings_get_default ();
498       icon_theme = gtk_icon_theme_get_default ();
499     }
500
501   if (size == -1 ||
502       !gtk_icon_size_lookup_for_settings (settings, size, &width, &height))
503     {
504       width = height = 48;
505     }
506
507   error = NULL;
508   pixbuf = gtk_icon_theme_load_icon (icon_theme,
509                                      icon_name,
510                                      MIN (width, height), 0,
511                                      &error);
512   if (error)
513     {
514       g_warning ("Unable to load the icon `%s' from the theme: %s",
515                  icon_name,
516                  error->message);
517
518       g_error_free (error);
519
520       if (widget)
521         return gtk_clutter_texture_new_from_stock (widget,
522                                              GTK_STOCK_MISSING_IMAGE,
523                                              size);
524       else
525         return NULL;
526     }
527
528   retval = gtk_clutter_texture_new_from_pixbuf (pixbuf);
529   g_object_unref (pixbuf);
530
531   return retval; 
532 }
533
534 /**
535  * gtk_clutter_texture_set_from_icon_name:
536  * @texture: a #ClutterTexture
537  * @widget: a #GtkWidget or %NULL
538  * @icon_name: the name of the icon
539  * @size: the icon size or -1
540  * @error: a return location for errors
541  *
542  * Sets the contents of @texture using the @icon_name from the
543  * current icon theme.
544  *
545  * Return value: %TRUE on success, %FALSE on failure.
546  *
547  * Since: 0.8
548  */
549 gboolean
550 gtk_clutter_texture_set_from_icon_name (ClutterTexture *texture,
551                                         GtkWidget      *widget,
552                                         const gchar    *icon_name,
553                                         GtkIconSize     size,
554                                         GError        **error)
555 {
556   GError *local_error = NULL;
557   GtkSettings *settings;
558   GtkIconTheme *icon_theme;
559   gboolean returnval;
560   gint width, height;
561   GdkPixbuf *pixbuf;
562
563   g_return_val_if_fail (CLUTTER_IS_TEXTURE (texture), FALSE);
564   g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
565   g_return_val_if_fail (icon_name != NULL, FALSE);
566   g_return_val_if_fail ((size > GTK_ICON_SIZE_INVALID) || (size == -1), FALSE);
567
568   if (widget && gtk_widget_has_screen (widget))
569     {
570       GdkScreen *screen;
571
572       screen = gtk_widget_get_screen (widget);
573       settings = gtk_settings_get_for_screen (screen);
574       icon_theme = gtk_icon_theme_get_for_screen (screen);
575     }
576   else
577     {
578       settings = gtk_settings_get_default ();
579       icon_theme = gtk_icon_theme_get_default ();
580     }
581
582   if (size == -1 ||
583       !gtk_icon_size_lookup_for_settings (settings, size, &width, &height))
584     {
585       width = height = 48;
586     }
587
588   pixbuf = gtk_icon_theme_load_icon (icon_theme,
589                                      icon_name,
590                                      MIN (width, height), 0,
591                                      &local_error);
592   if (local_error)
593     {
594       g_propagate_error (error, local_error);
595       return FALSE;
596     }
597
598   returnval = gtk_clutter_texture_set_from_pixbuf (texture, pixbuf, error);
599   g_object_unref (pixbuf);
600
601   return returnval;
602 }
603
604 /**
605  * gtk_clutter_init:
606  * @argc: pointer to the arguments count, or %NULL
607  * @argv: pointer to the arguments vector, or %NULL
608  *
609  * This function should be called instead of clutter_init() and
610  * gtk_init().
611  *
612  * Return value: %CLUTTER_INIT_SUCCESS on success, a negative integer
613  *   on failure.
614  *
615  * Since: 0.8
616  */
617 ClutterInitError
618 gtk_clutter_init (int    *argc,
619                   char ***argv)
620 {
621   if (!gtk_init_check (argc, argv))
622     return CLUTTER_INIT_ERROR_GTK;
623
624 #if defined(HAVE_CLUTTER_GTK_X11)
625   clutter_x11_set_display (GDK_DISPLAY());
626   clutter_x11_disable_event_retrieval ();
627 #elif defined(HAVE_CLUTTER_GTK_WIN32)
628   clutter_win32_disable_event_retrieval ();
629 #endif /* HAVE_CLUTTER_GTK_{X11,WIN32} */
630
631   return clutter_init (argc, argv);
632 }
633
634 /**
635  * gtk_clutter_init_with_args:
636  * @argc: a pointer to the number of command line arguments.
637  * @argv: a pointer to the array of command line arguments.
638  * @parameter_string: a string which is displayed in
639  *    the first line of <option>--help</option> output, after
640  *    <literal><replaceable>programname</replaceable> [OPTION...]</literal>
641  * @entries: a %NULL-terminated array of #GOptionEntry<!-- -->s
642  *    describing the options of your program
643  * @translation_domain: a translation domain to use for translating
644  *    the <option>--help</option> output for the options in @entries
645  *    with gettext(), or %NULL
646  * @error: a return location for errors
647  *
648  * This function should be called instead of clutter_init() and
649  * gtk_init_with_args().
650  *
651  * Return value: %CLUTTER_INIT_SUCCESS on success, a negative integer
652  *   on failure.
653  *
654  * Since: 1.0
655  */
656 ClutterInitError
657 gtk_clutter_init_with_args (int            *argc,
658                             char         ***argv,
659                             const char     *parameter_string,
660                             GOptionEntry   *entries,
661                             const char     *translation_domain,
662                             GError        **error)
663 {
664   gboolean res;
665
666   res = gtk_init_with_args (argc, argv,
667                             (char*) parameter_string,
668                             entries,
669                             (char*) translation_domain,
670                             error);
671
672   if (!res)
673     return CLUTTER_INIT_ERROR_GTK;
674
675 #if defined(GDK_WINDOWING_X11)
676   clutter_x11_set_display (GDK_DISPLAY());
677   clutter_x11_disable_event_retrieval ();
678 #elif defined(GDK_WINDOWING_WIN32)
679   clutter_win32_disable_event_retrieval ();
680 #endif /* GDK_WINDOWING_{X11,WIN32} */
681
682   return clutter_init_with_args (argc, argv,
683                                  NULL,
684                                  NULL,
685                                  NULL,
686                                  error);
687 }