]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtkiconcache.c
docs: Typo fix
[~andy/gtk] / gtk / gtkiconcache.c
index fc44d036de527d5805305338e66a666799c228ea..7d8b3fc7395815f5020726ab1f4a46035f87e535 100644 (file)
  * Library General Public License for more details.
  *
  * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include <config.h>
+#include "config.h"
+
 #include "gtkdebug.h"
 #include "gtkiconcache.h"
+#include "gtkiconcachevalidator.h"
+
 #include <glib/gstdio.h>
+#include <gdk-pixbuf/gdk-pixdata.h>
 
-#ifdef HAVE_MMAP
-#include <sys/mman.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
 #endif
 #ifdef G_OS_WIN32
-#include <windows.h>
 #include <io.h>
 #endif
+#include <fcntl.h>
 #include <sys/types.h>
 #include <sys/stat.h>
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#include <fcntl.h>
 #include <string.h>
 
+
 #ifndef _O_BINARY
 #define _O_BINARY 0
 #endif
 #define GET_UINT16(cache, offset) (GUINT16_FROM_BE (*(guint16 *)((cache) + (offset))))
 #define GET_UINT32(cache, offset) (GUINT32_FROM_BE (*(guint32 *)((cache) + (offset))))
 
+
 struct _GtkIconCache {
   gint ref_count;
 
-  gsize size;
+  GMappedFile *map;
   gchar *buffer;
-#ifdef G_OS_WIN32
-  HANDLE handle;
-#endif
+
+  guint32 last_chain_offset;
 };
 
 GtkIconCache *
 _gtk_icon_cache_ref (GtkIconCache *cache)
 {
-  cache->ref_count ++;
+  cache->ref_count++;
   return cache;
 }
 
@@ -73,13 +72,9 @@ _gtk_icon_cache_unref (GtkIconCache *cache)
     {
       GTK_NOTE (ICONTHEME, 
                g_print ("unmapping icon cache\n"));
-#ifdef HAVE_MMAP
-      munmap (cache->buffer, cache->size);
-#endif
-#ifdef G_OS_WIN32
-      UnmapViewOfFile (cache->buffer);
-      CloseHandle (cache->handle);
-#endif
+
+      if (cache->map)
+       g_mapped_file_unref (cache->map);
       g_free (cache);
     }
 }
@@ -88,29 +83,19 @@ GtkIconCache *
 _gtk_icon_cache_new_for_path (const gchar *path)
 {
   GtkIconCache *cache = NULL;
+  GMappedFile *map;
 
-#if defined(HAVE_MMAP) || defined(G_OS_WIN32)
   gchar *cache_filename;
   gint fd = -1;
-  struct stat st;
-  struct stat path_st;
-  gchar *buffer = NULL;
-#ifdef G_OS_WIN32
-  HANDLE handle = NULL;
-#endif
-
-  if (g_getenv ("GTK_NO_ICON_CACHE"))
-    return NULL;
+  GStatBuf st;
+  GStatBuf path_st;
 
-  /* Check if we have a cache file */
+   /* Check if we have a cache file */
   cache_filename = g_build_filename (path, "icon-theme.cache", NULL);
 
   GTK_NOTE (ICONTHEME, 
            g_print ("look for cache in %s\n", path));
 
-  if (!g_file_test (cache_filename, G_FILE_TEST_IS_REGULAR))
-    goto done;
-
   if (g_stat (path, &path_st) < 0)
     goto done;
 
@@ -118,11 +103,19 @@ _gtk_icon_cache_new_for_path (const gchar *path)
   fd = g_open (cache_filename, O_RDONLY|_O_BINARY, 0);
 
   if (fd < 0)
-    {
-      g_free (cache_filename);
-      return NULL;
-    }
-  
+    goto done;
+
+#ifdef G_OS_WIN32
+
+/* Bug 660730: _fstat32 is only defined in msvcrt80.dll+/VS 2005+ */
+/*             or possibly in the msvcrt.dll linked to by the Windows DDK */
+/*             (will need to check on the Windows DDK part later) */
+#if (_MSC_VER >= 1400 || __MSVCRT_VERSION__ >= 0x0800)
+#undef fstat /* Just in case */
+#define fstat _fstat32  
+#endif
+#endif
+
   if (fstat (fd, &st) < 0 || st.st_size < 4)
     goto done;
 
@@ -134,68 +127,66 @@ _gtk_icon_cache_new_for_path (const gchar *path)
       goto done; 
     }
 
-#ifndef G_OS_WIN32
-  buffer = (gchar *) mmap (NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
+  map = g_mapped_file_new (cache_filename, FALSE, NULL);
 
-  if (buffer == MAP_FAILED)
+  if (!map)
     goto done;
-#else
-  handle = CreateFileMapping (_get_osfhandle (fd), NULL, PAGE_READONLY,
-                             0, 0, NULL);
-  if (handle == NULL)
-    goto done;
-
-  buffer = MapViewOfFile (handle, FILE_MAP_READ, 0, 0, 0);
 
-  if (buffer == NULL)
+#ifdef G_ENABLE_DEBUG
+  if (gtk_get_debug_flags () & GTK_DEBUG_ICONTHEME)
     {
-      CloseHandle (handle);
-      goto done;
-    }
-#endif
+      CacheInfo info;
 
-  /* Verify version */
-  if (GET_UINT16 (buffer, 0) != MAJOR_VERSION ||
-      GET_UINT16 (buffer, 2) != MINOR_VERSION)
-    {
-#ifndef G_OS_WIN32
-      munmap (buffer, st.st_size);
-#else
-      UnmapViewOfFile (buffer);
-      CloseHandle (handle);
-#endif
-      GTK_NOTE (ICONTHEME, 
-               g_print ("wrong cache version\n"));
-      goto done;
+      info.cache = g_mapped_file_get_contents (map);
+      info.cache_size = g_mapped_file_get_length (map);
+      info.n_directories = 0;
+      info.flags = CHECK_OFFSETS|CHECK_STRINGS;
+
+      if (!_gtk_icon_cache_validate (&info))
+        {
+          g_mapped_file_unref (map);
+          g_warning ("Icon cache '%s' is invalid\n", cache_filename);
+
+          goto done;
+        }
     }
-  
-  GTK_NOTE (ICONTHEME, 
-           g_print ("found cache for %s\n", path));
+#endif 
+
+  GTK_NOTE (ICONTHEME, g_print ("found cache for %s\n", path));
 
   cache = g_new0 (GtkIconCache, 1);
   cache->ref_count = 1;
-  cache->buffer = buffer;
-#ifdef G_OS_WIN32
-  cache->handle = handle;
-#endif
-  cache->size = st.st_size;
+  cache->map = map;
+  cache->buffer = g_mapped_file_get_contents (map);
+
  done:
   g_free (cache_filename);  
-  if (fd != -1)
+  if (fd >= 0)
     close (fd);
 
-#endif  /* HAVE_MMAP || G_OS_WIN32 */
+  return cache;
+}
 
+GtkIconCache *
+_gtk_icon_cache_new (const gchar *data)
+{
+  GtkIconCache *cache;
+
+  cache = g_new0 (GtkIconCache, 1);
+  cache->ref_count = 1;
+  cache->map = NULL;
+  cache->buffer = (gchar *)data;
+  
   return cache;
 }
 
-static int
+static gint
 get_directory_index (GtkIconCache *cache,
                     const gchar *directory)
 {
   guint32 dir_list_offset;
-  int n_dirs;
-  int i;
+  gint n_dirs;
+  gint i;
   
   dir_list_offset = GET_UINT32 (cache->buffer, 8);
 
@@ -212,18 +203,18 @@ get_directory_index (GtkIconCache *cache,
   return -1;
 }
 
-gboolean
-_gtk_icon_cache_has_directory (GtkIconCache *cache,
-                              const gchar *directory)
+gint
+_gtk_icon_cache_get_directory_index (GtkIconCache *cache,
+                                    const gchar *directory)
 {
-  return get_directory_index (cache, directory) != -1;
+  return get_directory_index (cache, directory);
 }
 
 static guint
 icon_name_hash (gconstpointer key)
 {
-  const char *p = key;
-  guint h = *p;
+  const signed char *p = key;
+  guint32 h = *p;
 
   if (h)
     for (p += 1; *p != '\0'; p++)
@@ -232,22 +223,30 @@ icon_name_hash (gconstpointer key)
   return h;
 }
 
-gint
-_gtk_icon_cache_get_icon_flags (GtkIconCache *cache,
-                               const gchar  *icon_name,
-                               const gchar  *directory)
+static gint
+find_image_offset (GtkIconCache *cache,
+                  const gchar  *icon_name,
+                  gint          directory_index)
 {
   guint32 hash_offset;
   guint32 n_buckets;
   guint32 chain_offset;
-  int hash, directory_index;
+  int hash;
   guint32 image_list_offset, n_images;
-  gboolean found = FALSE;
   int i;
-  
+
+  chain_offset = cache->last_chain_offset;
+  if (chain_offset)
+    {
+      guint32 name_offset = GET_UINT32 (cache->buffer, chain_offset + 4);
+      gchar *name = cache->buffer + name_offset;
+
+      if (strcmp (name, icon_name) == 0)
+        goto find_dir;
+    }
+
   hash_offset = GET_UINT32 (cache->buffer, 4);
   n_buckets = GET_UINT32 (cache->buffer, hash_offset);
-
   hash = icon_name_hash (icon_name) % n_buckets;
 
   chain_offset = GET_UINT32 (cache->buffer, hash_offset + 4 + 4 * hash);
@@ -257,32 +256,47 @@ _gtk_icon_cache_get_icon_flags (GtkIconCache *cache,
       gchar *name = cache->buffer + name_offset;
 
       if (strcmp (name, icon_name) == 0)
-       {
-         found = TRUE;
-         break;
+        {
+          cache->last_chain_offset = chain_offset;
+          goto find_dir;
        }
-         
+  
       chain_offset = GET_UINT32 (cache->buffer, chain_offset);
     }
 
-  if (!found)
-    return 0;
+  cache->last_chain_offset = 0;
+  return 0;
 
+find_dir:
   /* We've found an icon list, now check if we have the right icon in it */
-  directory_index = get_directory_index (cache, directory);
   image_list_offset = GET_UINT32 (cache->buffer, chain_offset + 8);
   n_images = GET_UINT32 (cache->buffer, image_list_offset);
   
   for (i = 0; i < n_images; i++)
     {
       if (GET_UINT16 (cache->buffer, image_list_offset + 4 + 8 * i) ==
-         directory_index)
-       return GET_UINT16 (cache->buffer, image_list_offset + 4 + 8 * i + 2);
+         directory_index) 
+       return image_list_offset + 4 + 8 * i;
     }
-  
+
   return 0;
 }
 
+gint
+_gtk_icon_cache_get_icon_flags (GtkIconCache *cache,
+                               const gchar  *icon_name,
+                               gint          directory_index)
+{
+  guint32 image_offset;
+
+  image_offset = find_image_offset (cache, icon_name, directory_index);
+
+  if (!image_offset)
+    return 0;
+
+  return GET_UINT16 (cache->buffer, image_offset + 2);
+}
+
 void
 _gtk_icon_cache_add_icons (GtkIconCache *cache,
                           const gchar  *directory,
@@ -301,7 +315,7 @@ _gtk_icon_cache_add_icons (GtkIconCache *cache,
   
   hash_offset = GET_UINT32 (cache->buffer, 4);
   n_buckets = GET_UINT32 (cache->buffer, hash_offset);
-  
+
   for (i = 0; i < n_buckets; i++)
     {
       chain_offset = GET_UINT32 (cache->buffer, hash_offset + 4 + 4 * i);
@@ -353,5 +367,214 @@ _gtk_icon_cache_has_icon (GtkIconCache *cache,
 
   return FALSE;
 }
-                         
+
+gboolean
+_gtk_icon_cache_has_icon_in_directory (GtkIconCache *cache,
+                                      const gchar  *icon_name,
+                                      const gchar  *directory)
+{
+  guint32 hash_offset;
+  guint32 n_buckets;
+  guint32 chain_offset;
+  gint hash;
+  gboolean found_icon = FALSE;
+  gint directory_index;
+
+  directory_index = get_directory_index (cache, directory);
+
+  if (directory_index == -1)
+    return FALSE;
+  
+  hash_offset = GET_UINT32 (cache->buffer, 4);
+  n_buckets = GET_UINT32 (cache->buffer, hash_offset);
+
+  hash = icon_name_hash (icon_name) % n_buckets;
+
+  chain_offset = GET_UINT32 (cache->buffer, hash_offset + 4 + 4 * hash);
+  while (chain_offset != 0xffffffff)
+    {
+      guint32 name_offset = GET_UINT32 (cache->buffer, chain_offset + 4);
+      gchar *name = cache->buffer + name_offset;
+
+      if (strcmp (name, icon_name) == 0)
+       {
+         found_icon = TRUE;
+         break;
+       }
+         
+      chain_offset = GET_UINT32 (cache->buffer, chain_offset);
+    }
+
+  if (found_icon)
+    {
+      guint32 image_list_offset = GET_UINT32 (cache->buffer, chain_offset + 8);
+      guint32 n_images =  GET_UINT32 (cache->buffer, image_list_offset);
+      guint32 image_offset = image_list_offset + 4;
+      gint i;
+      for (i = 0; i < n_images; i++)
+       {
+         guint16 index = GET_UINT16 (cache->buffer, image_offset);
+         
+         if (index == directory_index)
+           return TRUE;
+         image_offset += 8;
+       }
+    }
+
+  return FALSE;
+}
+
+static void
+pixbuf_destroy_cb (guchar   *pixels, 
+                  gpointer  data)
+{
+  GtkIconCache *cache = data;
+
+  _gtk_icon_cache_unref (cache);
+}
+
+GdkPixbuf *
+_gtk_icon_cache_get_icon (GtkIconCache *cache,
+                         const gchar  *icon_name,
+                         gint          directory_index)
+{
+  guint32 offset, image_data_offset, pixel_data_offset;
+  guint32 length, type;
+  GdkPixbuf *pixbuf;
+  GdkPixdata pixdata;
+  GError *error = NULL;
+
+  offset = find_image_offset (cache, icon_name, directory_index);
+  
+  if (!offset)
+    return NULL;
+
+  image_data_offset = GET_UINT32 (cache->buffer, offset + 4);
+  
+  if (!image_data_offset)
+    return NULL;
+
+  pixel_data_offset = GET_UINT32 (cache->buffer, image_data_offset);
+
+  type = GET_UINT32 (cache->buffer, pixel_data_offset);
+
+  if (type != 0)
+    {
+      GTK_NOTE (ICONTHEME,
+               g_print ("invalid pixel data type %u\n", type));
+      return NULL;
+    }
+
+  length = GET_UINT32 (cache->buffer, pixel_data_offset + 4);
+  
+  if (!gdk_pixdata_deserialize (&pixdata, length, 
+                               (guchar *)(cache->buffer + pixel_data_offset + 8),
+                               &error))
+    {
+      GTK_NOTE (ICONTHEME,
+               g_print ("could not deserialize data: %s\n", error->message));
+      g_error_free (error);
+
+      return NULL;
+    }
+
+  pixbuf = gdk_pixbuf_new_from_data (pixdata.pixel_data, GDK_COLORSPACE_RGB,
+                                    (pixdata.pixdata_type & GDK_PIXDATA_COLOR_TYPE_MASK) == GDK_PIXDATA_COLOR_TYPE_RGBA,
+                                    8, pixdata.width, pixdata.height, pixdata.rowstride,
+                                    (GdkPixbufDestroyNotify)pixbuf_destroy_cb, 
+                                    cache);
+  if (!pixbuf)
+    {
+      GTK_NOTE (ICONTHEME,
+               g_print ("could not convert pixdata to pixbuf: %s\n", error->message));
+      g_error_free (error);
+
+      return NULL;
+    }
+
+  _gtk_icon_cache_ref (cache);
+
+  return pixbuf;
+}
+
+GtkIconData  *
+_gtk_icon_cache_get_icon_data  (GtkIconCache *cache,
+                               const gchar  *icon_name,
+                               gint          directory_index)
+{
+  guint32 offset, image_data_offset, meta_data_offset;
+  GtkIconData *data;
+  int i;
+
+  offset = find_image_offset (cache, icon_name, directory_index);
+  if (!offset)
+    return NULL;
+
+  image_data_offset = GET_UINT32 (cache->buffer, offset + 4);
+  if (!image_data_offset)
+    return NULL;
+
+  meta_data_offset = GET_UINT32 (cache->buffer, image_data_offset + 4);
+  
+  if (!meta_data_offset)
+    return NULL;
+
+  data = g_slice_new0 (GtkIconData);
+
+  offset = GET_UINT32 (cache->buffer, meta_data_offset);
+  if (offset)
+    {
+      data->has_embedded_rect = TRUE;
+      data->x0 = GET_UINT16 (cache->buffer, offset);
+      data->y0 = GET_UINT16 (cache->buffer, offset + 2);
+      data->x1 = GET_UINT16 (cache->buffer, offset + 4);
+      data->y1 = GET_UINT16 (cache->buffer, offset + 6);
+    }
+
+  offset = GET_UINT32 (cache->buffer, meta_data_offset + 4);
+  if (offset)
+    {
+      data->n_attach_points = GET_UINT32 (cache->buffer, offset);
+      data->attach_points = g_new (GdkPoint, data->n_attach_points);
+      for (i = 0; i < data->n_attach_points; i++)
+       {
+         data->attach_points[i].x = GET_UINT16 (cache->buffer, offset + 4 + 4 * i); 
+         data->attach_points[i].y = GET_UINT16 (cache->buffer, offset + 4 + 4 * i + 2); 
+       }
+    }
+
+  offset = GET_UINT32 (cache->buffer, meta_data_offset + 8);
+  if (offset)
+    {
+      gint n_names;
+      gchar *lang, *name;
+      gchar **langs;
+      GHashTable *table = g_hash_table_new (g_str_hash, g_str_equal);
+
+      n_names = GET_UINT32 (cache->buffer, offset);
+      
+      for (i = 0; i < n_names; i++)
+       {
+         lang = cache->buffer + GET_UINT32 (cache->buffer, offset + 4 + 8 * i);
+         name = cache->buffer + GET_UINT32 (cache->buffer, offset + 4 + 8 * i + 4);
+         
+         g_hash_table_insert (table, lang, name);
+       }
+      
+      langs = (gchar **)g_get_language_names ();
+      for (i = 0; langs[i]; i++)
+       {
+         name = g_hash_table_lookup (table, langs[i]);
+         if (name)
+           {
+             data->display_name = g_strdup (name);
+             break;
+           }
+       }
+
+      g_hash_table_destroy (table);
+    }
+
+  return data;
+}