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