1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2
3 /*
4 * Caja
5 *
6 * Copyright (C) 2000 Eazel, Inc.
7 *
8 * Caja is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * Caja is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21 *
22 * Author: Rebecca Schulman <rebecka@eazel.com>
23 */
24
25 /* caja-customization-data.c - functions to collect and load customization
26 names and images */
27
28 #include <config.h>
29 #include <gdk-pixbuf/gdk-pixbuf.h>
30 #include <glib.h>
31 #include <gtk/gtk.h>
32 #include <glib/gi18n.h>
33 #include <libxml/parser.h>
34 #include <stdlib.h>
35 #include <string.h>
36
37 #include <eel/eel-gdk-extensions.h>
38 #include <eel/eel-gdk-extensions.h>
39 #include <eel/eel-gdk-pixbuf-extensions.h>
40 #include <eel/eel-gtk-extensions.h>
41 #include <eel/eel-string.h>
42 #include <eel/eel-vfs-extensions.h>
43 #include <eel/eel-xml-extensions.h>
44
45 #include "caja-customization-data.h"
46 #include "caja-file-utilities.h"
47
48 typedef enum
49 {
50 READ_PUBLIC_CUSTOMIZATIONS,
51 READ_PRIVATE_CUSTOMIZATIONS
52 } CustomizationReadingMode;
53
54 struct CajaCustomizationData
55 {
56 char *customization_name;
57
58 GList *public_file_list;
59 GList *private_file_list;
60 GList *current_file_list;
61
62 GHashTable *name_map_hash;
63
64 GdkPixbuf *pattern_frame;
65
66 int maximum_icon_height;
67 int maximum_icon_width;
68
69 guint private_data_was_displayed : 1;
70 guint reading_mode : 2; /* enough bits for CustomizationReadingMode */
71 };
72
73
74 /* The Property here should be one of "emblems", "colors" or "patterns" */
75 static char * get_global_customization_path (const char *customization_name);
76 static char * get_private_customization_path (const char *customization_name);
77 static char * get_file_path_for_mode (const CajaCustomizationData *data,
78 const char *file_name);
79 static char* format_name_for_display (CajaCustomizationData *data, const char *name);
80 static void load_name_map_hash_table (CajaCustomizationData *data);
81
82
83 static gboolean
read_all_children(char * filename,const char * attributes,GList ** list_out)84 read_all_children (char *filename,
85 const char *attributes,
86 GList **list_out)
87 {
88 GFileEnumerator *enumerator;
89 GList *list;
90 GFile *file;
91 GFileInfo *info;
92
93 file = g_file_new_for_path (filename);
94
95 enumerator = g_file_enumerate_children (file, attributes, 0, NULL, NULL);
96 if (enumerator == NULL)
97 {
98 return FALSE;
99 }
100
101 list = NULL;
102 do
103 {
104 info = g_file_enumerator_next_file (enumerator, NULL, NULL);
105 if (info)
106 {
107 list = g_list_prepend (list, info);
108 }
109 }
110 while (info != NULL);
111
112 g_object_unref (enumerator);
113 g_object_unref (file);
114
115 *list_out = g_list_reverse (list);
116 return TRUE;
117 }
118
119
120 CajaCustomizationData*
caja_customization_data_new(const char * customization_name,gboolean show_public_customizations,int maximum_icon_height,int maximum_icon_width)121 caja_customization_data_new (const char *customization_name,
122 gboolean show_public_customizations,
123 int maximum_icon_height,
124 int maximum_icon_width)
125 {
126 CajaCustomizationData *data;
127 char *private_directory_path;
128 gboolean public_result, private_result;
129
130 data = g_new0 (CajaCustomizationData, 1);
131
132 public_result = TRUE;
133
134 if (show_public_customizations)
135 {
136 char *public_directory_path;
137
138 public_directory_path = get_global_customization_path (customization_name);
139
140 public_result = read_all_children (public_directory_path,
141 G_FILE_ATTRIBUTE_STANDARD_NAME ","
142 G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
143 &data->public_file_list);
144 g_free (public_directory_path);
145 }
146
147 private_directory_path = get_private_customization_path (customization_name);
148 private_result = read_all_children (private_directory_path,
149 G_FILE_ATTRIBUTE_STANDARD_NAME ","
150 G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
151 &data->private_file_list);
152 g_free (private_directory_path);
153 if (!public_result && !private_result)
154 {
155 g_warning ("Couldn't read any of the emblem directories\n");
156 g_free (data);
157 return NULL;
158 }
159 if (private_result)
160 {
161 data->reading_mode = READ_PRIVATE_CUSTOMIZATIONS;
162 data->current_file_list = data->private_file_list;
163 }
164 if (show_public_customizations && public_result)
165 {
166 data->reading_mode = READ_PUBLIC_CUSTOMIZATIONS;
167 data->current_file_list = data->public_file_list;
168 }
169
170 data->pattern_frame = NULL;
171
172 /* load the frame if necessary */
173 if (strcmp (customization_name, "patterns") == 0)
174 {
175 char *temp_str;
176
177 temp_str = caja_pixmap_file ("chit_frame.png");
178 if (temp_str != NULL)
179 {
180 data->pattern_frame = gdk_pixbuf_new_from_file (temp_str, NULL);
181 }
182 g_free (temp_str);
183 }
184
185 data->private_data_was_displayed = FALSE;
186 data->customization_name = g_strdup (customization_name);
187
188 data->maximum_icon_height = maximum_icon_height;
189 data->maximum_icon_width = maximum_icon_width;
190
191 load_name_map_hash_table (data);
192
193 return data;
194 }
195
196 gboolean
caja_customization_data_get_next_element_for_display(CajaCustomizationData * data,char ** emblem_name,GdkPixbuf ** pixbuf_out,char ** label_out)197 caja_customization_data_get_next_element_for_display (CajaCustomizationData *data,
198 char **emblem_name,
199 GdkPixbuf **pixbuf_out,
200 char **label_out)
201 {
202 GFileInfo *current_file_info;
203 char *image_file_name;
204 GdkPixbuf *pixbuf;
205 GdkPixbuf *orig_pixbuf;
206 gboolean is_reset_image;
207
208 g_return_val_if_fail (data != NULL, FALSE);
209 g_return_val_if_fail (emblem_name != NULL, FALSE);
210 g_return_val_if_fail (pixbuf_out != NULL, FALSE);
211 g_return_val_if_fail (label_out != NULL, FALSE);
212
213 if (data->current_file_list == NULL)
214 {
215 if (data->reading_mode == READ_PUBLIC_CUSTOMIZATIONS)
216 {
217 if (data->private_file_list == NULL)
218 {
219 return FALSE;
220 }
221 data->reading_mode = READ_PRIVATE_CUSTOMIZATIONS;
222 data->current_file_list = data->private_file_list;
223 return caja_customization_data_get_next_element_for_display (data,
224 emblem_name,
225 pixbuf_out,
226 label_out);
227 }
228 else
229 {
230 return FALSE;
231 }
232 }
233
234
235 current_file_info = data->current_file_list->data;
236 data->current_file_list = data->current_file_list->next;
237
238 g_assert (current_file_info != NULL);
239
240 if (!eel_istr_has_prefix (g_file_info_get_content_type (current_file_info), "image/")
241 || eel_istr_has_prefix (g_file_info_get_name (current_file_info), "."))
242 {
243 return caja_customization_data_get_next_element_for_display (data,
244 emblem_name,
245 pixbuf_out,
246 label_out);
247 }
248
249 image_file_name = get_file_path_for_mode (data,
250 g_file_info_get_name (current_file_info));
251 orig_pixbuf = gdk_pixbuf_new_from_file_at_scale (image_file_name,
252 data->maximum_icon_width,
253 data->maximum_icon_height,
254 TRUE,
255 NULL);
256 g_free (image_file_name);
257
258 if (orig_pixbuf == NULL)
259 {
260 return caja_customization_data_get_next_element_for_display (data,
261 emblem_name,
262 pixbuf_out,
263 label_out);
264 }
265
266 is_reset_image = g_strcmp0(g_file_info_get_name (current_file_info), RESET_IMAGE_NAME) == 0;
267
268 *emblem_name = g_strdup (g_file_info_get_name (current_file_info));
269
270 if (strcmp (data->customization_name, "patterns") == 0 &&
271 data->pattern_frame != NULL)
272 {
273 pixbuf = caja_customization_make_pattern_chit (orig_pixbuf, data->pattern_frame, FALSE, is_reset_image);
274 }
275 else
276 {
277 pixbuf = eel_gdk_pixbuf_scale_down_to_fit (orig_pixbuf,
278 data->maximum_icon_width,
279 data->maximum_icon_height);
280 }
281
282 g_object_unref (orig_pixbuf);
283
284 *pixbuf_out = pixbuf;
285
286 *label_out = format_name_for_display (data, g_file_info_get_name (current_file_info));
287
288 if (data->reading_mode == READ_PRIVATE_CUSTOMIZATIONS)
289 {
290 data->private_data_was_displayed = TRUE;
291 }
292 return TRUE;
293 }
294
295 gboolean
caja_customization_data_private_data_was_displayed(CajaCustomizationData * data)296 caja_customization_data_private_data_was_displayed (CajaCustomizationData *data)
297 {
298 return data->private_data_was_displayed;
299 }
300
301 void
caja_customization_data_destroy(CajaCustomizationData * data)302 caja_customization_data_destroy (CajaCustomizationData *data)
303 {
304 g_assert (data->public_file_list != NULL ||
305 data->private_file_list != NULL);
306
307 if (data->pattern_frame != NULL)
308 {
309 g_object_unref (data->pattern_frame);
310 }
311
312 g_list_free_full (data->public_file_list, g_object_unref);
313 g_list_free_full (data->private_file_list, g_object_unref);
314
315 if (data->name_map_hash != NULL)
316 {
317 g_hash_table_destroy (data->name_map_hash);
318 }
319
320 g_free (data->customization_name);
321 g_free (data);
322 }
323
324
325 /* get_global_customization_directory
326 Get the path where a property's pixmaps are stored
327 @customization_name : the name of the customization to get.
328 Should be one of "emblems", "colors", or "paterns"
329
330 Return value: The directory name where the customization's
331 public pixmaps are stored */
332 static char *
get_global_customization_path(const char * customization_name)333 get_global_customization_path (const char *customization_name)
334 {
335 return g_build_filename (CAJA_DATADIR,
336 customization_name,
337 NULL);
338 }
339
340
341 /* get_private_customization_directory
342 Get the path where a customization's pixmaps are stored
343 @customization_name : the name of the customization to get.
344 Should be one of "emblems", "colors", or "patterns"
345
346 Return value: The directory name where the customization's
347 user-specific pixmaps are stored */
348 static char *
get_private_customization_path(const char * customization_name)349 get_private_customization_path (const char *customization_name)
350 {
351 char *user_directory;
352 char *directory_path;
353
354 user_directory = caja_get_user_directory ();
355 directory_path = g_build_filename (user_directory,
356 customization_name,
357 NULL);
358 g_free (user_directory);
359
360 return directory_path;
361 }
362
363
364 static char *
get_file_path_for_mode(const CajaCustomizationData * data,const char * file_name)365 get_file_path_for_mode (const CajaCustomizationData *data,
366 const char *file_name)
367 {
368 char *directory_path, *file;
369 if (data->reading_mode == READ_PUBLIC_CUSTOMIZATIONS)
370 {
371 directory_path = get_global_customization_path (data->customization_name);
372 }
373 else
374 {
375 directory_path = get_private_customization_path (data->customization_name);
376 }
377
378 file = g_build_filename (directory_path, file_name, NULL);
379 g_free (directory_path);
380
381 return file;
382 }
383
384
385 /* utility to make an attractive pattern image by compositing with a frame */
386 GdkPixbuf*
caja_customization_make_pattern_chit(GdkPixbuf * pattern_tile,GdkPixbuf * frame,gboolean dragging,gboolean is_reset)387 caja_customization_make_pattern_chit (GdkPixbuf *pattern_tile, GdkPixbuf *frame, gboolean dragging, gboolean is_reset)
388 {
389 GdkPixbuf *pixbuf, *temp_pixbuf;
390 int frame_width, frame_height;
391 int pattern_width, pattern_height;
392
393 g_assert (pattern_tile != NULL);
394 g_assert (frame != NULL);
395
396 frame_width = gdk_pixbuf_get_width (frame);
397 frame_height = gdk_pixbuf_get_height (frame);
398 pattern_width = gdk_pixbuf_get_width (pattern_tile);
399 pattern_height = gdk_pixbuf_get_height (pattern_tile);
400
401 pixbuf = gdk_pixbuf_copy (frame);
402
403 /* scale the pattern tile to the proper size */
404 gdk_pixbuf_scale (pattern_tile,
405 pixbuf,
406 2, 2, frame_width - 8, frame_height - 8,
407 0, 0,
408 (double)(frame_width - 8 + 1)/pattern_width,
409 (double)(frame_height - 8 + 1)/pattern_height,
410 GDK_INTERP_BILINEAR);
411
412 /* if we're dragging, get rid of the light-colored halo */
413 if (dragging)
414 {
415 temp_pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, frame_width - 8, frame_height - 8);
416 gdk_pixbuf_copy_area (pixbuf, 2, 2, frame_width - 8, frame_height - 8, temp_pixbuf, 0, 0);
417 g_object_unref (pixbuf);
418 pixbuf = temp_pixbuf;
419 }
420
421 return pixbuf;
422 }
423
424
425 /* utility to format the passed-in name for display by stripping the extension, mapping underscore
426 and capitalizing as necessary */
427
428 static char*
format_name_for_display(CajaCustomizationData * data,const char * name)429 format_name_for_display (CajaCustomizationData *data, const char* name)
430 {
431 char *formatted_str;
432
433 if (!g_strcmp0(name, RESET_IMAGE_NAME))
434 {
435 return g_strdup (_("Reset"));
436 }
437
438 /* map file names to display names using the mappings defined in the hash table */
439
440 formatted_str = eel_filename_strip_extension (name);
441 if (data->name_map_hash != NULL)
442 {
443 char *mapped_name;
444
445 mapped_name = g_hash_table_lookup (data->name_map_hash, formatted_str);
446 if (mapped_name)
447 {
448 g_free (formatted_str);
449 formatted_str = g_strdup (mapped_name);
450 }
451 }
452
453 return formatted_str;
454 }
455
456 /* utility routine to allocate a hash table and load it with the appropriate
457 * name mapping data from the browser xml file
458 */
459 static void
load_name_map_hash_table(CajaCustomizationData * data)460 load_name_map_hash_table (CajaCustomizationData *data)
461 {
462 xmlDocPtr browser_data;
463 xmlNodePtr category_node, current_node;
464
465 /* allocate the hash table */
466 data->name_map_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
467
468 /* load the browser.xml file */
469 if ((browser_data = xmlParseFile (CAJA_DATADIR "/browser.xml")) != NULL)
470 {
471 /* get the category node */
472 category_node = eel_xml_get_root_child_by_name_and_property (browser_data,
473 (const xmlChar *) "category",
474 (const xmlChar *) "name",
475 (const xmlChar *) data->customization_name);
476 current_node = category_node->children;
477
478 /* loop through the entries, adding a mapping to the hash table */
479 while (current_node != NULL)
480 {
481 xmlChar *filename, *display_name;
482
483 display_name = xmlGetProp (current_node, (const xmlChar *) "display_name");
484 filename = xmlGetProp (current_node, (const xmlChar *) "filename");
485 if (display_name && filename)
486 {
487 g_hash_table_replace (data->name_map_hash,
488 g_strdup ((const char *) filename),
489 g_strdup (_((const char *) display_name)));
490 }
491 xmlFree (filename);
492 xmlFree (display_name);
493 current_node = current_node->next;
494 }
495
496 /* close the xml file */
497 xmlFreeDoc (browser_data);
498 }
499 }
500