1 /* Atomix -- a little puzzle game about atoms and molecules.
2  * Copyright (C) 1999-2001 Jens Finke
3  * Copyright (C) 2005 Guilherme de S. Pastore
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  */
19 
20 #include "theme.h"
21 #include "theme-private.h"
22 
23 
24 /*=================================================================
25 
26   Declaration of internal functions
27 
28   ---------------------------------------------------------------*/
29 
30 static void theme_class_init (GObjectClass *class);
31 static void theme_init (Theme *theme);
32 static void theme_finalize (GObject *object);
33 static void destroy_theme_image (gpointer data);
34 
35 static GObjectClass *parent_class;
36 
theme_get_type(void)37 GType theme_get_type (void)
38 {
39   static GType object_type = 0;
40 
41   if (!object_type)
42     {
43       static const GTypeInfo object_info = {
44 	sizeof (ThemeClass),
45 	(GBaseInitFunc) NULL,
46 	(GBaseFinalizeFunc) NULL,
47 	(GClassInitFunc) theme_class_init,
48 	NULL,			/* clas_finalize */
49 	NULL,			/* class_data */
50 	sizeof (Theme),
51 	0,			/* n_preallocs */
52 	(GInstanceInitFunc) theme_init,
53       };
54 
55       object_type = g_type_register_static (G_TYPE_OBJECT,
56 					    "Theme", &object_info, 0);
57     }
58 
59   return object_type;
60 }
61 
62 
63 /*=================================================================
64 
65   Theme creation, initialisation and clean up
66 
67   ---------------------------------------------------------------*/
68 
theme_class_init(GObjectClass * class)69 static void theme_class_init (GObjectClass *class)
70 {
71   parent_class = g_type_class_ref (G_TYPE_OBJECT);
72   class->finalize = theme_finalize;
73 }
74 
theme_init(Theme * theme)75 static void theme_init (Theme *theme)
76 {
77   ThemePrivate *priv;
78 
79   priv = g_new0 (ThemePrivate, 1);
80 
81   priv->name = NULL;
82   priv->path = NULL;
83   priv->tile_width = 0;
84   priv->tile_height = 0;
85   priv->animstep = 0;
86   priv->bg_color.red = 0;
87   priv->bg_color.green = 0;
88   priv->bg_color.blue = 0;
89   g_datalist_init (&priv->images);
90 
91   theme->priv = priv;
92 }
93 
theme_finalize(GObject * object)94 static void theme_finalize (GObject *object)
95 {
96   ThemePrivate *priv;
97   Theme *theme = THEME (object);
98 
99   g_return_if_fail (theme != NULL);
100 
101 #ifdef DEBUG
102   g_message ("Finalize theme");
103 #endif
104   priv = theme->priv;
105 
106   if (priv->name)
107     g_free (priv->name);
108   priv->name = NULL;
109 
110   if (priv->path)
111     g_free (priv->path);
112   priv->path = NULL;
113 
114   if (priv->images)
115     g_datalist_clear (&priv->images);
116 
117   g_free (theme->priv);
118   theme->priv = NULL;
119 }
120 
theme_new(void)121 Theme *theme_new (void)
122 {
123   Theme *theme;
124   theme = THEME (g_object_new (THEME_TYPE, NULL));
125   return theme;
126 }
127 
128 
129 /*=================================================================
130 
131   Theme functions
132 
133   ---------------------------------------------------------------*/
134 
theme_get_name(Theme * theme)135 gchar *theme_get_name (Theme *theme)
136 {
137   g_return_val_if_fail (IS_THEME (theme), NULL);
138 
139   return theme->priv->name;
140 }
141 
theme_get_animstep(Theme * theme)142 gint theme_get_animstep (Theme *theme)
143 {
144   g_return_val_if_fail (IS_THEME (theme), 0);
145 
146   return theme->priv->animstep;
147 }
148 
destroy_theme_image(gpointer data)149 static void destroy_theme_image (gpointer data)
150 {
151   ThemeImage *ti;
152 
153   ti = (ThemeImage *) data;
154 
155   if (ti == NULL)
156     return;
157 
158   if (ti->file)
159     g_free (ti->file);
160 
161   if (ti->image)
162     g_object_unref (ti->image);
163 
164   if (ti->decorations)
165     g_slist_free (ti->decorations);
166 
167   g_free (ti);
168 }
169 
get_theme_image_pixbuf(ThemeImage * ti)170 static GdkPixbuf *get_theme_image_pixbuf (ThemeImage *ti)
171 {
172   GdkPixbuf *pixbuf;
173 
174   if (ti == NULL)
175     return NULL;
176 
177   if (ti->loading_failed)
178     return NULL;
179 
180   if (ti->image == NULL)
181     {
182       ti->image = gdk_pixbuf_new_from_file (ti->file, NULL);
183       if (ti->image == NULL)
184 	ti->loading_failed = TRUE;
185       else if (ti->alpha < 255)
186 	{
187 	  pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8,
188 				   gdk_pixbuf_get_width (ti->image),
189 				   gdk_pixbuf_get_height (ti->image));
190 	  gdk_pixbuf_fill (pixbuf, 0x00000000);
191 	  gdk_pixbuf_composite (ti->image,
192 				pixbuf,
193 				0, 0,
194 				gdk_pixbuf_get_width (pixbuf),
195 				gdk_pixbuf_get_height (pixbuf),
196 				0.0, 0.0, 1.0, 1.0,
197 				GDK_INTERP_NEAREST, ti->alpha);
198 	  g_object_unref (ti->image);
199 	  ti->image = pixbuf;
200 	}
201     }
202 
203   if (ti->image != NULL)
204     g_object_ref (ti->image);
205 
206   return ti->image;
207 }
208 
create_sub_images(Theme * theme,Tile * tile,TileSubType sub_type)209 static GdkPixbuf *create_sub_images (Theme *theme, Tile *tile,
210 				     TileSubType sub_type)
211 {
212   ThemePrivate *priv;
213   GSList *elem;
214   ThemeImage *ti;
215   GdkPixbuf *pixbuf = NULL;
216   GdkPixbuf *pb;
217 
218   g_return_val_if_fail (IS_THEME (theme), NULL);
219   g_return_val_if_fail (IS_TILE (tile), NULL);
220 
221   priv = theme->priv;
222 
223   elem = tile_get_sub_ids (tile, sub_type);
224 
225   for (; elem != NULL; elem = elem->next)
226     {
227       ti = g_datalist_id_get_data (&priv->images, GPOINTER_TO_QUARK (elem->data));
228       if (ti == NULL)
229 	continue;
230 
231       pb = get_theme_image_pixbuf (ti);
232       if (pb == NULL)
233 	{
234 #ifdef DEBUG
235 	  g_warning ("Couldn't load sub image: %s",
236 		     g_quark_to_string (GPOINTER_TO_QUARK (elem->data)));
237 #endif
238 	  continue;
239 	}
240 
241       if (pixbuf == NULL)
242 	{
243 	  pixbuf = gdk_pixbuf_copy (pb);
244 	}
245       else
246 	{
247 	  gdk_pixbuf_composite (pb,
248 				pixbuf,
249 				0, 0,
250 				gdk_pixbuf_get_width (pixbuf),
251 				gdk_pixbuf_get_height (pixbuf),
252 				0.0, 0.0, 1.0, 1.0, GDK_INTERP_NEAREST, 255);
253 	}
254       g_object_unref (pb);
255     }
256 
257   return pixbuf;
258 }
259 
theme_apply_decoration(Theme * theme,Tile * tile)260 gboolean theme_apply_decoration (Theme *theme, Tile *tile)
261 {
262   static int counter = 0;
263   ThemeImage *ti;
264   gint n_decorations;
265   GQuark decor_id;
266 
267   if (tile == NULL)
268     return FALSE;
269 
270   g_return_val_if_fail (IS_THEME (theme), FALSE);
271   g_return_val_if_fail (IS_TILE (tile), FALSE);
272 
273   ti = g_datalist_id_get_data (&theme->priv->images, tile_get_base_id (tile));
274   if (ti->decorations == NULL)
275     return FALSE;
276 
277   n_decorations = g_slist_length (ti->decorations);
278   decor_id = GPOINTER_TO_QUARK (g_slist_nth_data (ti->decorations,
279 					counter++ % n_decorations));
280   tile_add_sub_id (tile, decor_id, TILE_SUB_OVERLAY);
281 
282   return TRUE;
283 }
284 
theme_get_tile_image(Theme * theme,Tile * tile)285 GdkPixbuf *theme_get_tile_image (Theme *theme, Tile *tile)
286 {
287   ThemePrivate *priv;
288   GQuark image_id;
289   ThemeImage *ti = NULL;
290   GdkPixbuf *underlay_pb = NULL;
291   GdkPixbuf *overlay_pb = NULL;
292   GdkPixbuf *base_pb = NULL;
293   GdkPixbuf *result = NULL;
294   gint tw, th;
295 
296   g_return_val_if_fail (IS_THEME (theme), NULL);
297   g_return_val_if_fail (IS_TILE (tile), NULL);
298 
299   priv = theme->priv;
300 
301   image_id = tile_get_base_id (tile);
302 
303   underlay_pb = create_sub_images (theme, tile, TILE_SUB_UNDERLAY);
304   overlay_pb = create_sub_images (theme, tile, TILE_SUB_OVERLAY);
305 
306   ti = g_datalist_id_get_data (&priv->images, image_id);
307   base_pb = get_theme_image_pixbuf (ti);
308   if (base_pb == NULL)
309     {
310       theme_get_tile_size (theme, &tw, &th);
311       base_pb = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, tw, th);
312       gdk_pixbuf_fill (base_pb, 0);
313     }
314 
315   if (underlay_pb && overlay_pb)
316     {
317       result = gdk_pixbuf_copy (underlay_pb);
318       gdk_pixbuf_composite (base_pb,
319 			    result,
320 			    0, 0,
321 			    gdk_pixbuf_get_width (result),
322 			    gdk_pixbuf_get_height (result),
323 			    0.0, 0.0, 1.0, 1.0, GDK_INTERP_NEAREST, 255);
324       gdk_pixbuf_composite (overlay_pb,
325 			    result,
326 			    0, 0,
327 			    gdk_pixbuf_get_width (result),
328 			    gdk_pixbuf_get_height (result),
329 			    0.0, 0.0, 1.0, 1.0, GDK_INTERP_NEAREST, 255);
330       g_object_unref (overlay_pb);
331       g_object_unref (underlay_pb);
332     }
333   else if (!underlay_pb && overlay_pb)
334     {
335       result = gdk_pixbuf_copy (base_pb);
336       gdk_pixbuf_composite (overlay_pb,
337 			    result,
338 			    0, 0,
339 			    gdk_pixbuf_get_width (result),
340 			    gdk_pixbuf_get_height (result),
341 			    0.0, 0.0, 1.0, 1.0, GDK_INTERP_NEAREST, 255);
342       g_object_unref (overlay_pb);
343     }
344   else if (underlay_pb && !overlay_pb)
345     {
346       result = gdk_pixbuf_copy (underlay_pb);
347       gdk_pixbuf_composite (base_pb,
348 			    result,
349 			    0, 0,
350 			    gdk_pixbuf_get_width (result),
351 			    gdk_pixbuf_get_height (result),
352 			    0.0, 0.0, 1.0, 1.0, GDK_INTERP_NEAREST, 255);
353       g_object_unref (underlay_pb);
354     }
355   else
356     result = g_object_ref (base_pb);
357 
358   g_object_unref (base_pb);
359 
360   return result;
361 }
362 
theme_get_selector_image(Theme * theme)363 GdkPixbuf *theme_get_selector_image (Theme *theme)
364 {
365   static GQuark cursor_id = 0;
366   ThemeImage *ti;
367 
368   if (!cursor_id)
369     cursor_id = g_quark_from_static_string ("cursor");
370 
371   g_return_val_if_fail (IS_THEME (theme), NULL);
372 
373   ti = g_datalist_id_get_data (&theme->priv->images, cursor_id);
374 
375   return get_theme_image_pixbuf (ti);
376 }
377 
theme_get_selector_arrow_images(Theme * theme,GdkPixbuf ** arrows)378 void theme_get_selector_arrow_images (Theme *theme, GdkPixbuf **arrows)
379 {
380   gint i;
381   ThemeImage *ti;
382   static GQuark arrow_id[4] = { 0, 0, 0, 0 };
383 
384   if (!arrow_id[0])
385     {
386       arrow_id[0] = g_quark_from_static_string ("arrow-top");
387       arrow_id[1] = g_quark_from_static_string ("arrow-right");
388       arrow_id[2] = g_quark_from_static_string ("arrow-bottom");
389       arrow_id[3] = g_quark_from_static_string ("arrow-left");
390     }
391 
392   for (i = 0; i < 4; i++)
393     {
394       ti = g_datalist_id_get_data (&theme->priv->images, arrow_id[i]);
395       arrows[i] = get_theme_image_pixbuf (ti);
396     }
397 }
398 
theme_get_tile_size(Theme * theme,gint * width,gint * height)399 void theme_get_tile_size (Theme *theme, gint *width, gint *height)
400 {
401   *width = *height = 0;
402 
403   g_return_if_fail (IS_THEME (theme));
404 
405   *width = theme->priv->tile_width;
406   *height = theme->priv->tile_height;
407 }
408 
theme_get_background_color(Theme * theme)409 GdkRGBA *theme_get_background_color (Theme *theme)
410 {
411   return &(theme->priv->bg_color);
412 }
413 
414 
theme_add_image(Theme * theme,const gchar * src,gint alpha)415 GQuark theme_add_image (Theme *theme, const gchar *src, gint alpha)
416 {
417   ThemeImage *ti;
418   gchar *filename;
419   gchar *suffix;
420 
421   g_return_val_if_fail (IS_THEME (theme), 0);
422   g_return_val_if_fail (src != NULL, 0);
423   g_return_val_if_fail (0 <= alpha && alpha <= 255, 0);
424 
425   ti = g_new0 (ThemeImage, 1);
426   ti->file = g_strdup (src);
427   ti->loading_failed = FALSE;
428   ti->image = NULL;
429   ti->alpha = alpha;
430   ti->decorations = NULL;
431 
432   filename = g_path_get_basename (src);
433   suffix = g_strrstr (filename, ".png");
434   if (suffix != NULL)
435     *suffix = '\0';
436   else
437     {
438       suffix = g_strrstr (filename, ".gif");
439       if (suffix != NULL)
440 	*suffix = '\0';
441     }
442 
443   ti->id = g_quark_from_string (filename);
444 
445   g_datalist_id_set_data_full (&theme->priv->images,
446 			       ti->id, ti, destroy_theme_image);
447 
448   g_free (filename);
449 
450   return ti->id;
451 }
452 
theme_add_image_decoration(Theme * theme,GQuark base,GQuark decor)453 void theme_add_image_decoration (Theme *theme, GQuark base, GQuark decor)
454 {
455   ThemeImage *ti;
456 
457   g_return_if_fail (IS_THEME (theme));
458 
459   ti = g_datalist_id_get_data (&theme->priv->images, base);
460   if (ti == NULL)
461     return;
462 
463   ti->decorations = g_slist_append (ti->decorations, GQUARK_TO_POINTER (decor));
464 }
465