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