]> Pileus Git - ~andy/gtk/blob - gtk/gtkiconcache.c
Remove an accidentally leftover duplicate pixbuf creation. (#314700,
[~andy/gtk] / gtk / gtkiconcache.c
1 /* gtkiconcache.c
2  * Copyright (C) 2004  Anders Carlsson <andersca@gnome.org>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #include <config.h>
21
22 #include "gtkdebug.h"
23 #include "gtkiconcache.h"
24 #include <glib/gstdio.h>
25 #include <gdk-pixbuf/gdk-pixdata.h>
26
27 #ifdef HAVE_UNISTD_H
28 #include <unistd.h>
29 #endif
30 #ifdef G_OS_WIN32
31 #include <io.h>
32 #endif
33 #include <fcntl.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <string.h>
37
38
39 #ifndef _O_BINARY
40 #define _O_BINARY 0
41 #endif
42
43 #define MAJOR_VERSION 1
44 #define MINOR_VERSION 0
45
46 #define GET_UINT16(cache, offset) (GUINT16_FROM_BE (*(guint16 *)((cache) + (offset))))
47 #define GET_UINT32(cache, offset) (GUINT32_FROM_BE (*(guint32 *)((cache) + (offset))))
48
49 struct _GtkIconCache {
50   gint ref_count;
51
52   GMappedFile *map;
53   gchar *buffer;
54 };
55
56 GtkIconCache *
57 _gtk_icon_cache_ref (GtkIconCache *cache)
58 {
59   cache->ref_count ++;
60   return cache;
61 }
62
63 void
64 _gtk_icon_cache_unref (GtkIconCache *cache)
65 {
66   cache->ref_count --;
67
68   if (cache->ref_count == 0)
69     {
70       GTK_NOTE (ICONTHEME, 
71                 g_print ("unmapping icon cache\n"));
72
73       g_mapped_file_free (cache->map);
74       g_free (cache);
75     }
76 }
77
78 GtkIconCache *
79 _gtk_icon_cache_new_for_path (const gchar *path)
80 {
81   GtkIconCache *cache = NULL;
82   GMappedFile *map;
83
84   gchar *cache_filename;
85   gint fd = -1;
86   struct stat st;
87   struct stat path_st;
88   gchar *buffer = NULL;
89
90    /* Check if we have a cache file */
91   cache_filename = g_build_filename (path, "icon-theme.cache", NULL);
92
93   GTK_NOTE (ICONTHEME, 
94             g_print ("look for cache in %s\n", path));
95
96   if (g_stat (path, &path_st) < 0)
97     goto done;
98
99   /* Open the file and map it into memory */
100   fd = g_open (cache_filename, O_RDONLY|_O_BINARY, 0);
101
102   if (fd < 0)
103     goto done;
104   
105   if (fstat (fd, &st) < 0 || st.st_size < 4)
106     goto done;
107
108   /* Verify cache is uptodate */
109   if (st.st_mtime < path_st.st_mtime)
110     {
111       GTK_NOTE (ICONTHEME, 
112                 g_print ("cache outdated\n"));
113       goto done; 
114     }
115
116   map = g_mapped_file_new (cache_filename, FALSE, NULL);
117
118   if (!map)
119     goto done;
120
121   /* Verify version */
122   buffer = g_mapped_file_get_contents (map);
123   if (GET_UINT16 (buffer, 0) != MAJOR_VERSION ||
124       GET_UINT16 (buffer, 2) != MINOR_VERSION)
125     {
126       g_mapped_file_free (map);
127
128       GTK_NOTE (ICONTHEME, 
129                 g_print ("wrong cache version\n"));
130       goto done;
131     }
132   
133   GTK_NOTE (ICONTHEME, 
134             g_print ("found cache for %s\n", path));
135
136   cache = g_new0 (GtkIconCache, 1);
137   cache->ref_count = 1;
138   cache->map = map;
139   cache->buffer = buffer;
140
141  done:
142   g_free (cache_filename);  
143   if (fd >= 0)
144     close (fd);
145
146   return cache;
147 }
148
149 static int
150 get_directory_index (GtkIconCache *cache,
151                      const gchar *directory)
152 {
153   guint32 dir_list_offset;
154   int n_dirs;
155   int i;
156   
157   dir_list_offset = GET_UINT32 (cache->buffer, 8);
158
159   n_dirs = GET_UINT32 (cache->buffer, dir_list_offset);
160
161   for (i = 0; i < n_dirs; i++)
162     {
163       guint32 name_offset = GET_UINT32 (cache->buffer, dir_list_offset + 4 + 4 * i);
164       gchar *name = cache->buffer + name_offset;
165       if (strcmp (name, directory) == 0)
166         return i;
167     }
168   
169   return -1;
170 }
171
172 gboolean
173 _gtk_icon_cache_has_directory (GtkIconCache *cache,
174                                const gchar *directory)
175 {
176   return get_directory_index (cache, directory) != -1;
177 }
178
179 static guint
180 icon_name_hash (gconstpointer key)
181 {
182   const signed char *p = key;
183   guint32 h = *p;
184
185   if (h)
186     for (p += 1; *p != '\0'; p++)
187       h = (h << 5) - h + *p;
188
189   return h;
190 }
191
192 static gint
193 find_image_offset (GtkIconCache *cache,
194                    const gchar  *icon_name,
195                    const gchar  *directory)
196 {
197   guint32 hash_offset;
198   guint32 n_buckets;
199   guint32 chain_offset;
200   int hash, directory_index;
201   guint32 image_list_offset, n_images;
202   gboolean found = FALSE;
203   int i;
204   
205   hash_offset = GET_UINT32 (cache->buffer, 4);
206   n_buckets = GET_UINT32 (cache->buffer, hash_offset);
207
208   hash = icon_name_hash (icon_name) % n_buckets;
209
210   chain_offset = GET_UINT32 (cache->buffer, hash_offset + 4 + 4 * hash);
211   while (chain_offset != 0xffffffff)
212     {
213       guint32 name_offset = GET_UINT32 (cache->buffer, chain_offset + 4);
214       gchar *name = cache->buffer + name_offset;
215
216       if (strcmp (name, icon_name) == 0)
217         {
218           found = TRUE;
219           break;
220         }
221           
222       chain_offset = GET_UINT32 (cache->buffer, chain_offset);
223     }
224
225   if (!found) {
226     return 0;
227   }
228
229   /* We've found an icon list, now check if we have the right icon in it */
230   directory_index = get_directory_index (cache, directory);
231   image_list_offset = GET_UINT32 (cache->buffer, chain_offset + 8);
232   n_images = GET_UINT32 (cache->buffer, image_list_offset);
233   
234   for (i = 0; i < n_images; i++)
235     {
236       if (GET_UINT16 (cache->buffer, image_list_offset + 4 + 8 * i) ==
237           directory_index) 
238         return image_list_offset + 4 + 8 * i;
239     }
240
241   return 0;
242 }
243
244 gint
245 _gtk_icon_cache_get_icon_flags (GtkIconCache *cache,
246                                 const gchar  *icon_name,
247                                 const gchar  *directory)
248 {
249   guint32 image_offset;
250
251   image_offset = find_image_offset (cache, icon_name, directory);
252
253   if (!image_offset)
254     return 0;
255
256   return GET_UINT16 (cache->buffer, image_offset + 2);
257 }
258
259 void
260 _gtk_icon_cache_add_icons (GtkIconCache *cache,
261                            const gchar  *directory,
262                            GHashTable   *hash_table)
263 {
264   int directory_index;
265   guint32 hash_offset, n_buckets;
266   guint32 chain_offset;
267   guint32 image_list_offset, n_images;
268   int i, j;
269   
270   directory_index = get_directory_index (cache, directory);
271
272   if (directory_index == -1)
273     return;
274   
275   hash_offset = GET_UINT32 (cache->buffer, 4);
276   n_buckets = GET_UINT32 (cache->buffer, hash_offset);
277
278   for (i = 0; i < n_buckets; i++)
279     {
280       chain_offset = GET_UINT32 (cache->buffer, hash_offset + 4 + 4 * i);
281       while (chain_offset != 0xffffffff)
282         {
283           guint32 name_offset = GET_UINT32 (cache->buffer, chain_offset + 4);
284           gchar *name = cache->buffer + name_offset;
285           
286           image_list_offset = GET_UINT32 (cache->buffer, chain_offset + 8);
287           n_images = GET_UINT32 (cache->buffer, image_list_offset);
288   
289           for (j = 0; j < n_images; j++)
290             {
291               if (GET_UINT16 (cache->buffer, image_list_offset + 4 + 8 * j) ==
292                   directory_index)
293                 g_hash_table_insert (hash_table, name, NULL);
294             }
295
296           chain_offset = GET_UINT32 (cache->buffer, chain_offset);
297         }
298     }  
299 }
300
301 gboolean
302 _gtk_icon_cache_has_icon (GtkIconCache *cache,
303                           const gchar  *icon_name)
304 {
305   guint32 hash_offset;
306   guint32 n_buckets;
307   guint32 chain_offset;
308   gint hash;
309   
310   hash_offset = GET_UINT32 (cache->buffer, 4);
311   n_buckets = GET_UINT32 (cache->buffer, hash_offset);
312
313   hash = icon_name_hash (icon_name) % n_buckets;
314
315   chain_offset = GET_UINT32 (cache->buffer, hash_offset + 4 + 4 * hash);
316   while (chain_offset != 0xffffffff)
317     {
318       guint32 name_offset = GET_UINT32 (cache->buffer, chain_offset + 4);
319       gchar *name = cache->buffer + name_offset;
320
321       if (strcmp (name, icon_name) == 0)
322         return TRUE;
323           
324       chain_offset = GET_UINT32 (cache->buffer, chain_offset);
325     }
326
327   return FALSE;
328 }
329
330 static void
331 pixbuf_destroy_cb (guchar   *pixels, 
332                    gpointer  data)
333 {
334   GtkIconCache *cache = data;
335
336   _gtk_icon_cache_unref (cache);
337 }
338
339 GdkPixbuf *
340 _gtk_icon_cache_get_icon (GtkIconCache *cache,
341                           const gchar  *icon_name,
342                           const gchar  *directory)
343 {
344   guint32 offset, image_data_offset, pixel_data_offset;
345   guint32 length, type;
346   GdkPixbuf *pixbuf;
347   GdkPixdata pixdata;
348   GError *error = NULL;
349
350   offset = find_image_offset (cache, icon_name, directory);
351   
352   image_data_offset = GET_UINT32 (cache->buffer, offset + 4);
353   
354   if (!image_data_offset)
355     return NULL;
356
357   pixel_data_offset = GET_UINT32 (cache->buffer, image_data_offset);
358
359   type = GET_UINT32 (cache->buffer, pixel_data_offset);
360
361   if (type != 0)
362     {
363       GTK_NOTE (ICONTHEME,
364                 g_print ("invalid pixel data type %d\n", type));
365       return NULL;
366     }
367
368   length = GET_UINT32 (cache->buffer, pixel_data_offset + 4);
369   
370   if (!gdk_pixdata_deserialize (&pixdata, length, 
371                                 cache->buffer + pixel_data_offset + 8,
372                                 &error))
373     {
374       GTK_NOTE (ICONTHEME,
375                 g_print ("could not deserialize data: %s\n", error->message));
376       g_error_free (error);
377
378       return NULL;
379     }
380
381   pixbuf = gdk_pixbuf_new_from_data (pixdata.pixel_data, GDK_COLORSPACE_RGB,
382                                      (pixdata.pixdata_type & GDK_PIXDATA_COLOR_TYPE_MASK) == GDK_PIXDATA_COLOR_TYPE_RGBA,
383                                      8, pixdata.width, pixdata.height, pixdata.rowstride,
384                                      (GdkPixbufDestroyNotify)pixbuf_destroy_cb, 
385                                      cache);
386   if (!pixbuf)
387     {
388       GTK_NOTE (ICONTHEME,
389                 g_print ("could not convert pixdata to pixbuf: %s\n", error->message));
390       g_error_free (error);
391
392       return NULL;
393     }
394
395   _gtk_icon_cache_ref (cache);
396
397   return pixbuf;
398 }
399
400 GtkIconData  *
401 _gtk_icon_cache_get_icon_data  (GtkIconCache *cache,
402                                 const gchar  *icon_name,
403                                 const gchar  *directory)
404 {
405   guint32 offset, image_data_offset, meta_data_offset;
406   GtkIconData *data;
407   int i;
408
409   offset = find_image_offset (cache, icon_name, directory);
410   if (!offset)
411     return NULL;
412
413   image_data_offset = GET_UINT32 (cache->buffer, offset + 4);
414   if (!image_data_offset)
415     return NULL;
416
417   meta_data_offset = GET_UINT32 (cache->buffer, image_data_offset + 4);
418   
419   if (!meta_data_offset)
420     return NULL;
421
422   data = g_new0 (GtkIconData, 1);
423
424   offset = GET_UINT32 (cache->buffer, meta_data_offset);
425   if (offset)
426     {
427       data->has_embedded_rect = TRUE;
428       data->x0 = GET_UINT16 (cache->buffer, offset);
429       data->y0 = GET_UINT16 (cache->buffer, offset + 2);
430       data->x1 = GET_UINT16 (cache->buffer, offset + 4);
431       data->y1 = GET_UINT16 (cache->buffer, offset + 6);
432     }
433
434   offset = GET_UINT32 (cache->buffer, meta_data_offset + 4);
435   if (offset)
436     {
437       data->n_attach_points = GET_UINT32 (cache->buffer, offset);
438       data->attach_points = g_new (GdkPoint, data->n_attach_points);
439       for (i = 0; i < data->n_attach_points; i++)
440         {
441           data->attach_points[i].x = GET_UINT16 (cache->buffer, offset + 4 + 4 * i); 
442           data->attach_points[i].y = GET_UINT16 (cache->buffer, offset + 4 + 4 * i + 2); 
443         }
444     }
445
446   offset = GET_UINT32 (cache->buffer, meta_data_offset + 8);
447   if (offset)
448     {
449       gint n_names;
450       gchar *lang, *name;
451       gchar **langs;
452       GHashTable *table = g_hash_table_new (g_str_hash, g_str_equal);
453
454       n_names = GET_UINT32 (cache->buffer, offset);
455       
456       for (i = 0; i < n_names; i++)
457         {
458           lang = cache->buffer + GET_UINT32 (cache->buffer, offset + 4 + 8 * i);
459           name = cache->buffer + GET_UINT32 (cache->buffer, offset + 4 + 8 * i + 4);
460           
461           g_hash_table_insert (table, lang, name);
462         }
463       
464       langs = (gchar **)g_get_language_names ();
465       for (i = 0; langs[i]; i++)
466         {
467           name = g_hash_table_lookup (table, langs[i]);
468           if (name)
469             {
470               data->display_name = g_strdup (name);
471               break;
472             }
473         }
474
475       g_hash_table_destroy (table);
476     }
477
478   return data;
479 }
480