]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtkiconcache.c
GtkBubbleWindow: use OSD style class
[~andy/gtk] / gtk / gtkiconcache.c
index 20166d8a64797adf3180da59a72737db38dbac3e..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;
 }
 
@@ -74,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);
     }
 }
@@ -89,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
+  GStatBuf st;
+  GStatBuf path_st;
 
-  if (g_getenv ("GTK_NO_ICON_CACHE"))
-    return NULL;
-
-  /* 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;
 
@@ -119,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;
 
@@ -135,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)
-    goto done;
-#else
-  handle = CreateFileMapping (_get_osfhandle (fd), NULL, PAGE_READONLY,
-                             0, 0, NULL);
-  if (handle == NULL)
+  if (!map)
     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);
 
@@ -213,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++)
@@ -236,19 +226,27 @@ icon_name_hash (gconstpointer key)
 static gint
 find_image_offset (GtkIconCache *cache,
                   const gchar  *icon_name,
-                  const gchar  *directory)
+                  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);
@@ -258,20 +256,19 @@ find_image_offset (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);
   
@@ -288,11 +285,11 @@ find_image_offset (GtkIconCache *cache,
 gint
 _gtk_icon_cache_get_icon_flags (GtkIconCache *cache,
                                const gchar  *icon_name,
-                               const gchar  *directory)
+                               gint          directory_index)
 {
   guint32 image_offset;
 
-  image_offset = find_image_offset (cache, icon_name, directory);
+  image_offset = find_image_offset (cache, icon_name, directory_index);
 
   if (!image_offset)
     return 0;
@@ -370,11 +367,76 @@ _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,
-                         const gchar  *directory)
+                         gint          directory_index)
 {
   guint32 offset, image_data_offset, pixel_data_offset;
   guint32 length, type;
@@ -382,8 +444,11 @@ _gtk_icon_cache_get_icon (GtkIconCache *cache,
   GdkPixdata pixdata;
   GError *error = NULL;
 
-  offset = find_image_offset (cache, icon_name, directory);
+  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)
@@ -396,14 +461,14 @@ _gtk_icon_cache_get_icon (GtkIconCache *cache,
   if (type != 0)
     {
       GTK_NOTE (ICONTHEME,
-               g_print ("invalid pixel data type %d\n", type));
+               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, 
-                               cache->buffer + pixel_data_offset + 8,
+                               (guchar *)(cache->buffer + pixel_data_offset + 8),
                                &error))
     {
       GTK_NOTE (ICONTHEME,
@@ -413,8 +478,11 @@ _gtk_icon_cache_get_icon (GtkIconCache *cache,
       return NULL;
     }
 
-  pixbuf = gdk_pixbuf_from_pixdata (&pixdata, FALSE, &error);
-
+  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,
@@ -424,19 +492,21 @@ _gtk_icon_cache_get_icon (GtkIconCache *cache,
       return NULL;
     }
 
+  _gtk_icon_cache_ref (cache);
+
   return pixbuf;
 }
 
 GtkIconData  *
 _gtk_icon_cache_get_icon_data  (GtkIconCache *cache,
                                const gchar  *icon_name,
-                               const gchar  *directory)
+                               gint          directory_index)
 {
   guint32 offset, image_data_offset, meta_data_offset;
   GtkIconData *data;
   int i;
 
-  offset = find_image_offset (cache, icon_name, directory);
+  offset = find_image_offset (cache, icon_name, directory_index);
   if (!offset)
     return NULL;
 
@@ -449,7 +519,7 @@ _gtk_icon_cache_get_icon_data  (GtkIconCache *cache,
   if (!meta_data_offset)
     return NULL;
 
-  data = g_new0 (GtkIconData, 1);
+  data = g_slice_new0 (GtkIconData);
 
   offset = GET_UINT32 (cache->buffer, meta_data_offset);
   if (offset)