1 /*
2   Copyright © 2004 Callum McKenzie
3   Copyright © 2007, 2008, 2009 Christian Persch
4 
5   This library 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 3 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, see <http://www.gnu.org/licenses/>.
17  */
18 
19 /* Authors:   Callum McKenzie <callum@physics.otago.ac.nz> */
20 
21 #include <config.h>
22 
23 #include <string.h>
24 #include <glib.h>
25 #include <gdk-pixbuf/gdk-pixbuf.h>
26 #include <gtk/gtk.h>
27 
28 #include "ar-debug.h"
29 #include "ar-runtime.h"
30 #include "ar-string-utils.h"
31 
32 #include "ar-card-theme.h"
33 #include "ar-card-theme-private.h"
34 
35 struct _ArCardThemeFixedClass {
36   ArCardThemeClass parent_class;
37 };
38 
39 struct _ArCardThemeFixed {
40   ArCardTheme parent_instance;
41 
42   /* Switched on ArCardThemeFixed.use_scalable */
43   char *themesizepath;
44   CardSize *card_sizes;
45   guint n_card_sizes;
46 
47   CardSize slot_size;
48   CardSize card_size;
49 
50   guint size_available : 1;
51 };
52 
53 /* Class implementation */
54 
55 G_DEFINE_TYPE (ArCardThemeFixed, ar_card_theme_fixed, AR_TYPE_CARD_THEME);
56 
57 static gboolean
ar_card_theme_fixed_load(ArCardTheme * card_theme,GError ** error__)58 ar_card_theme_fixed_load (ArCardTheme *card_theme,
59                              GError **error__)
60 {
61   ArCardThemeFixed *theme = (ArCardThemeFixed *) card_theme;
62   ArCardThemeInfo *theme_info = card_theme->theme_info;
63   GKeyFile *key_file;
64   char *path;
65   GError *error = NULL;
66   int *sizes = NULL;
67   gsize n_sizes, i;
68   gboolean retval = FALSE;
69 
70   path = g_build_filename (theme_info->path, theme_info->filename, NULL);
71 
72   key_file = g_key_file_new ();
73   if (!g_key_file_load_from_file (key_file, path, 0, &error)) {
74     ar_debug_print (AR_DEBUG_CARD_THEME,
75                         "Failed to load prerendered card theme from %s: %s\n", path,
76                         error->message);
77     g_error_free (error);
78     goto loser;
79   }
80 
81   sizes =
82     g_key_file_get_integer_list (key_file, "Card Theme", "Sizes", &n_sizes,
83                                  &error);
84   if (error) {
85     ar_debug_print (AR_DEBUG_CARD_THEME,
86                         "Failed to get card sizes: %s\n", error->message);
87     g_error_free (error);
88     goto loser;
89   }
90 
91   if (n_sizes == 0) {
92     ar_debug_print (AR_DEBUG_CARD_THEME,
93                         "Card theme contains no sizes\n");
94     goto loser;
95   }
96 
97   theme->card_sizes = g_new (CardSize, n_sizes);
98   theme->n_card_sizes = n_sizes;
99 
100   for (i = 0; i < n_sizes; ++i) {
101     char group[32];
102     GError *err = NULL;
103     int width, height;
104 
105     g_snprintf (group, sizeof (group), "%d", sizes[i]);
106 
107     width = g_key_file_get_integer (key_file, group, "Width", &err);
108     if (err) {
109       ar_debug_print (AR_DEBUG_CARD_THEME,
110                           "Error loading width for size %d: %s\n", sizes[i],
111                           err->message);
112       g_error_free (err);
113       goto loser;
114     }
115     height = g_key_file_get_integer (key_file, group, "Height", &err);
116     if (err) {
117       ar_debug_print (AR_DEBUG_CARD_THEME,
118                           "Error loading height for size %d: %s\n", sizes[i],
119                           err->message);
120       g_error_free (err);
121       goto loser;
122     }
123 
124     theme->card_sizes[i].width = width;
125     theme->card_sizes[i].height = height;
126   }
127 
128   retval = TRUE;
129 
130 loser:
131 
132   g_free (sizes);
133 
134   g_key_file_free (key_file);
135   g_free (path);
136 
137   return retval;
138 }
139 
140 static void
ar_card_theme_fixed_init(ArCardThemeFixed * theme)141 ar_card_theme_fixed_init (ArCardThemeFixed *theme)
142 {
143   theme->card_size.width = theme->card_size.height = -1;
144 
145   theme->card_sizes = NULL;
146   theme->n_card_sizes = 0;
147   theme->themesizepath = NULL;
148 
149   theme->size_available = FALSE;
150 
151   theme->card_size.width = theme->card_size.height = theme->slot_size.width =
152     theme->slot_size.height = -1;
153 }
154 
155 static void
ar_card_theme_fixed_finalize(GObject * object)156 ar_card_theme_fixed_finalize (GObject * object)
157 {
158   ArCardThemeFixed *theme = AR_CARD_THEME_FIXED (object);
159 
160   g_free (theme->card_sizes);
161   g_free (theme->themesizepath);
162 
163   G_OBJECT_CLASS (ar_card_theme_fixed_parent_class)->finalize (object);
164 }
165 
166 static gboolean
ar_card_theme_fixed_set_card_size(ArCardTheme * card_theme,int width,int height,double proportion)167 ar_card_theme_fixed_set_card_size (ArCardTheme *card_theme,
168                                       int width,
169                                       int height,
170                                       double proportion)
171 {
172   ArCardThemeFixed *theme = (ArCardThemeFixed *) card_theme;
173   ArCardThemeInfo *theme_info = card_theme->theme_info;
174 
175   if ((width == theme->slot_size.width) &&
176       (height == theme->slot_size.height))
177     return FALSE;
178 
179   theme->slot_size.width = width;
180   theme->slot_size.height = height;
181 
182   {
183     guint i;
184     int twidth, theight;
185     CardSize size = { -1, -1 }, fit_size = { -1, -1};
186 
187     twidth = FLOAT_TO_INT_CEIL (((double) width) * proportion);
188     theight = FLOAT_TO_INT_CEIL (((double) height) * proportion);
189 
190     /* Find the closest prerendered size */
191     for (i = 0; i < theme->n_card_sizes; ++i) {
192       CardSize info = theme->card_sizes[i];
193 
194       if (info.width > width || info.height > height)
195         continue;
196 
197       if (info.width > fit_size.width && info.height > fit_size.height)
198         fit_size = info;
199 
200       /* FIXMEchpe */
201       if (info.width <= twidth && info.height <= theight &&
202           info.width > size.width && info.height > size.height)
203         size = info;
204     }
205 
206     if (size.width < 0 || size.height < 0)
207       size = fit_size;
208 
209     if (size.width > 0 && size.height > 0) {
210       char *theme_basename;
211       char sizestr[16];
212 
213       if (size.width == theme->card_size.width &&
214           size.height == theme->card_size.height)
215         return FALSE;
216 
217       g_free (theme->themesizepath);
218 
219       theme_basename = g_strdup (theme_info->filename);
220       *(strchr (theme_basename, '.')) = '\0';
221       g_snprintf (sizestr, sizeof (sizestr), "%d", size.width);
222       theme->themesizepath = g_build_filename (theme_info->path,
223                                                theme_basename,
224                                                sizestr, NULL);
225       g_free (theme_basename);
226 
227       theme->size_available = TRUE;
228       theme->card_size = size;
229 
230       ar_debug_print (AR_DEBUG_CARD_THEME,
231                           "Found prerendered card size %dx%d as nearest available size to %dx%d\n",
232                           size.width, size.height, twidth, theight);
233 
234     } else {
235       ar_debug_print (AR_DEBUG_CARD_THEME,
236                           "No prerendered size available for %d:%d\n",
237                           width, height);
238       theme->size_available = FALSE;
239 
240       /* FIXMEchpe: at least use the smallest available size here, or
241        * programme will surely crash when trying to render NULL pixbufs
242        * later on!
243        */
244       /* FIXMEchpe: emit changed signal here too!! */
245       return FALSE;
246     }
247   }
248 
249   _ar_card_theme_emit_changed (card_theme);
250 
251   return TRUE;
252 }
253 
254 static void
ar_card_theme_fixed_get_card_size(ArCardTheme * card_theme,CardSize * size)255 ar_card_theme_fixed_get_card_size (ArCardTheme *card_theme,
256                                       CardSize *size)
257 {
258   ArCardThemeFixed *theme = (ArCardThemeFixed *) card_theme;
259 
260   *size = theme->card_size;
261 }
262 
263 static double
ar_card_theme_fixed_get_card_aspect(ArCardTheme * card_theme)264 ar_card_theme_fixed_get_card_aspect (ArCardTheme *card_theme)
265 {
266   ArCardThemeFixed *theme = (ArCardThemeFixed *) card_theme;
267 
268   return ((double) theme->card_size.width) / ((double) theme->card_size.height);
269 }
270 
271 static GdkPixbuf *
ar_card_theme_fixed_get_card_pixbuf(ArCardTheme * card_theme,int card_id)272 ar_card_theme_fixed_get_card_pixbuf (ArCardTheme *card_theme,
273                                         int card_id)
274 {
275   ArCardThemeFixed *theme = (ArCardThemeFixed *) card_theme;
276   GdkPixbuf *pixbuf;
277   GError *error = NULL;
278   char name[32], filename[36];
279   char *path;
280 
281   if (!theme->size_available)
282     return NULL;
283 
284   ar_card_get_name_by_id_snprintf (name, sizeof (name), card_id);
285   g_snprintf (filename, sizeof (filename), "%s.png", name);
286   path = g_build_filename (theme->themesizepath, filename, NULL);
287 
288   pixbuf = gdk_pixbuf_new_from_file (path, &error);
289   if (!pixbuf) {
290     ar_debug_print (AR_DEBUG_CARD_THEME,
291                         "Failed to load card image %s: %s\n",
292                         filename, error->message);
293     g_error_free (error);
294   }
295 
296   g_free (path);
297 
298   return pixbuf;
299 }
300 
301 static ArCardThemeInfo *
ar_card_theme_fixed_class_get_theme_info(ArCardThemeClass * klass,const char * path,const char * filename)302 ar_card_theme_fixed_class_get_theme_info (ArCardThemeClass *klass,
303                                              const char *path,
304                                              const char *filename)
305 {
306   ArCardThemeInfo *info;
307   char *display_name, *pref_name;
308 
309   if (!g_str_has_suffix (filename, ".card-theme"))
310     return NULL;
311 
312   display_name = ar_filename_to_display_name (filename);
313   pref_name = g_strdup_printf ("fixed:%s", filename);
314 
315   info = _ar_card_theme_info_new (G_OBJECT_CLASS_TYPE (klass),
316                                      path,
317                                      filename,
318                                      display_name /* adopts */,
319                                      pref_name,
320                                      FALSE /* not scalable */,
321                                      NULL, NULL);
322 
323   return info;
324 }
325 
326 static gboolean
ar_card_theme_fixed_class_foreach_theme_dir(ArCardThemeClass * klass,ArCardThemeForeachFunc callback,gpointer data)327 ar_card_theme_fixed_class_foreach_theme_dir (ArCardThemeClass *klass,
328                                                 ArCardThemeForeachFunc callback,
329                                                 gpointer data)
330 {
331   if (!_ar_card_theme_class_foreach_env (klass, "AR_CARD_THEME_PATH_FIXED", callback, data))
332     return FALSE;
333 
334   if (!callback (klass, ar_runtime_get_directory (AR_RUNTIME_PRERENDERED_CARDS_DIRECTORY), data))
335     return FALSE;
336 
337   /* If we're installed in a non-system prefix, also load the card themes
338    * from the system prefix.
339    */
340   if (!ar_runtime_is_system_prefix ())
341     return callback (klass, "/usr/share/gnome-games-common/card-themes", data);
342 
343   return TRUE;
344 }
345 
346 static void
ar_card_theme_fixed_class_init(ArCardThemeFixedClass * klass)347 ar_card_theme_fixed_class_init (ArCardThemeFixedClass * klass)
348 {
349   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
350   ArCardThemeClass *theme_class = AR_CARD_THEME_CLASS (klass);
351 
352   gobject_class->finalize = ar_card_theme_fixed_finalize;
353 
354   theme_class->get_theme_info = ar_card_theme_fixed_class_get_theme_info;
355   theme_class->foreach_theme_dir = ar_card_theme_fixed_class_foreach_theme_dir;
356 
357   theme_class->load = ar_card_theme_fixed_load;
358   theme_class->set_card_size = ar_card_theme_fixed_set_card_size;
359   theme_class->get_card_size = ar_card_theme_fixed_get_card_size;
360   theme_class->get_card_aspect = ar_card_theme_fixed_get_card_aspect;
361   theme_class->get_card_pixbuf = ar_card_theme_fixed_get_card_pixbuf;
362 }
363 
364 /* public API */
365 
366 /**
367  * ar_card_theme_fixed_new:
368  *
369  * Returns: a new #ArCardThemeFixed
370  */
371 ArCardTheme *
ar_card_theme_fixed_new(void)372 ar_card_theme_fixed_new (void)
373 {
374   return g_object_new (AR_TYPE_CARD_THEME_FIXED, NULL);
375 }
376